summaryrefslogtreecommitdiff
path: root/spb
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /spb
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'spb')
-rw-r--r--spb/SkeletonI2C/ReadMe.md117
-rw-r--r--spb/SkeletonI2C/SkeletonI2C.sln28
-rw-r--r--spb/SkeletonI2C/controller.cpp851
-rw-r--r--spb/SkeletonI2C/controller.h76
-rw-r--r--spb/SkeletonI2C/device.cpp2268
-rw-r--r--spb/SkeletonI2C/device.h368
-rw-r--r--spb/SkeletonI2C/driver.cpp454
-rw-r--r--spb/SkeletonI2C/driver.h36
-rw-r--r--spb/SkeletonI2C/hw.cpp83
-rw-r--r--spb/SkeletonI2C/hw.h88
-rw-r--r--spb/SkeletonI2C/i2ctrace.h57
-rw-r--r--spb/SkeletonI2C/internal.h293
-rw-r--r--spb/SkeletonI2C/resource.rc11
-rw-r--r--spb/SkeletonI2C/skeletoni2c.asl14
-rw-r--r--spb/SkeletonI2C/skeletoni2c.h96
-rw-r--r--spb/SkeletonI2C/skeletoni2c.inxbin0 -> 3856 bytes
-rw-r--r--spb/SkeletonI2C/skeletoni2c.vcxproj198
-rw-r--r--spb/SkeletonI2C/skeletoni2c.vcxproj.Filters48
-rw-r--r--spb/SpbTestTool/ReadMe.md203
-rw-r--r--spb/SpbTestTool/SpbTestTool.sln46
-rw-r--r--spb/SpbTestTool/exe/SpbTestTool.vcxproj205
-rw-r--r--spb/SpbTestTool/exe/SpbTestTool.vcxproj.Filters28
-rw-r--r--spb/SpbTestTool/exe/command.cpp660
-rw-r--r--spb/SpbTestTool/exe/command.h638
-rw-r--r--spb/SpbTestTool/exe/internal.h99
-rw-r--r--spb/SpbTestTool/exe/main.cpp633
-rw-r--r--spb/SpbTestTool/exe/util.cpp250
-rw-r--r--spb/SpbTestTool/sys/SpbTestTool.vcxproj198
-rw-r--r--spb/SpbTestTool/sys/SpbTestTool.vcxproj.Filters45
-rw-r--r--spb/SpbTestTool/sys/device.cpp848
-rw-r--r--spb/SpbTestTool/sys/device.h43
-rw-r--r--spb/SpbTestTool/sys/driver.cpp414
-rw-r--r--spb/SpbTestTool/sys/driver.h36
-rw-r--r--spb/SpbTestTool/sys/internal.h144
-rw-r--r--spb/SpbTestTool/sys/peripheral.cpp1683
-rw-r--r--spb/SpbTestTool/sys/peripheral.h104
-rw-r--r--spb/SpbTestTool/sys/resource.rc11
-rw-r--r--spb/SpbTestTool/sys/spbtestioctl.h50
-rw-r--r--spb/SpbTestTool/sys/spbtesttool.asl26
-rw-r--r--spb/SpbTestTool/sys/spbtesttool.inxbin0 -> 4682 bytes
-rw-r--r--spb/SpbTestTool/sys/trace.h54
41 files changed, 11504 insertions, 0 deletions
diff --git a/spb/SkeletonI2C/ReadMe.md b/spb/SkeletonI2C/ReadMe.md
new file mode 100644
index 00000000..4522af7c
--- /dev/null
+++ b/spb/SkeletonI2C/ReadMe.md
@@ -0,0 +1,117 @@
+Skeleton I2C Sample Driver
+=========================
+
+The SkeletonI2C sample demonstrates how to design a KMDF controller driver for Windows that conforms to the [simple peripheral bus](http://msdn.microsoft.com/en-us/library/windows/hardware/hh450903) (SPB) device driver interface (DDI). SPB is an abstraction for low-speed serial buses (for example, I<sup>2</sup>C and SPI) that allows peripheral drivers to be developed for cross-platform use without any knowledge of the underlying bus hardware or device connections. While this sample implements an empty I<sup>2</sup>C driver, it could just as easily be the starting point for an SPI driver with only minor modifications.
+
+Note that the SkeletonI2C sample is simplified to show the overall structure of an SPB controller, but contains only the code that the driver requires to communicate with the [SPB framework extension (SpbCx)](http://msdn.microsoft.com/en-us/library/windows/hardware/hh406203) and KMDF. The SkeletonI2C sample driver omits all hardware-specific code. It does not simulate data transfers or implement request completion asynchronously. Pay close attention to code comments marked with "TODO" that refer to blocks of code that must be removed or updated.
+
+The simplified structure of the SkeletonI2C sample driver makes it a convenient starting point for development of a real SPB controller driver that manages the hardware functions in an SPB controller.
+
+Modifying the sample
+--------------------
+
+Here are some high-level points to consider when modifying the SkeletonI2C sample for use on real hardware:
+
+- Edit (and likely rename) Skeletoni2c.h to describe your hardware's register set.
+- Modify Controller.cpp and Device.cpp to translate the SPB DDI and primitives into I<sup>2</sup>C or SPI protocol for your hardware. This includes initialization, I/O configuration, and interrupt processing.
+- Address any comments marked with "TODO" in the sample, especially those that short circuit the I/O path to complete requests synchronously.
+- Modify the HWID (`ACPI\skeletoni2c`) in Skeletoni2c.inf to match the device node in your firmware.
+- Generate and specify a unique trace GUID in I2ctrace.h.
+- Refactor the driver name, functions, comments, etc., to better describe your implementation.
+
+Code tour
+---------
+
+The following are relevant functions in the SkeletonI2C driver for implementing the SPB DDI.
+
+Function
+
+Description
+
+INITIALIZATION
+
+`OnDeviceAdd`
+
+Within `OnDeviceAdd`, the driver makes several configuration calls for SPB.
+
+[**SpbDeviceInitConfig**](http://msdn.microsoft.com/en-us/library/windows/hardware/hh450918) must be called before creating the WDFDEVICE. Note that SpbCx sets a default security descriptor on the device object, but the controller driver can override it by calling [**WdfDeviceInitAssignSDDLString**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff546035) after **SpbDeviceInitConfig**.
+
+After creating the WDFDEVICE, the driver configures it appropriately for SPB by calling [**SpbDeviceInitialize**](http://msdn.microsoft.com/en-us/library/windows/hardware/hh450919). Here the driver also sets the target and request attributes.
+
+Finally the driver configures a WDF system-managed idle time-out.
+
+TARGET CONNECTION
+
+`OnTargetConnect`
+
+Invoked when a client opens a handle to the specified SPB target. Queries the I<sup>2</sup>C connection parameters from the resource hub (via SPB) and initializes the target context.
+
+SPB I/O CALLBACKS
+
+`OnRead`
+
+SPB read callback. Invokes the `PbcConfigureForNonSequence` function to set up the transfer.
+
+`OnWrite`
+
+SPB write callback. Invokes the `PbcConfigureForNonSequence` function to set up the transfer.
+
+`OnSequence`
+
+SPB sequence callback. Configures the controller for an atomic transfer\*.
+
+`OnControllerLock`
+
+SPB lock controller callback. Configures to handle subsequent I/O as an atomic transfer\*. For I<sup>2</sup>C the controller should place a start bit on the bus. For SPI the controller should assert the chip-select line. The driver may choose to carry this out as part of this callback or defer until the first I/O operation is received (the next call to `OnRead` or `OnWrite`).
+
+`OnControllerUnlock`
+
+SPB unlock controller callback. Marks the end of an atomic transfer\*. For I<sup>2</sup>C, the controller should place a stop bit on the bus. For SPI, the controller should de-assert the chip-select line.
+
+SPB HELPER METHODS
+
+`PbcConfigureForIndex`
+
+Configures the request context for the specified transfer index. This could be a single I/O or part of a sequence.
+
+`PbcRequestComplete`
+
+Sets the number of bytes completed for a request and invokes the [**SpbRequestComplete**](http://msdn.microsoft.com/en-us/library/windows/hardware/hh450920) method.
+
+\*An atomic transfer in SPB is implemented using Sequence or a Lock/Unlock pair. For I<sup>2</sup>C, this means a set of reads and writes with restarts in between. For SPI, this means a set of reads and writes with the chip select-line asserted throughout.
+
+The following are relevant functions in the SkeletonI2C driver for implementing controller-specific I2C protocol. For the most part, these are placeholders and must be filled in appropriately.
+
+Function
+
+Description
+
+INITIALIZATION
+
+`ControllerInitialize`
+
+One-time controller initialization. Prepare FIFOs, clocks, interrupts, etc.
+
+`ControllerConfigureForTransfer`
+
+Per-I/O controller configuration. Depending on the type of I/O (and whether its part of an ongoing atomic transfer), the driver may need to configure direction, set interrupts, etc.
+
+Additionally, for I<sup>2</sup>C, the driver may need to insert a start, restart, or stop bit as necessary, and for SPI the driver may need to assert or de-assert the chip select line.
+
+I/O PROCESSING
+
+`OnInterruptIsr`
+
+Interrupt callback. Acknowledges interrupts and saves state as necessary. Queues a DPC for processing.
+
+`OnInterruptDpc`
+
+DPC callback. Processes saved interrupts. If necessary the request is completed.
+
+`ControllerProcessInterrupts`
+
+Handles processing for both normal and error condition interrupts. Invokes `ControllerCompleteTransfer`() as appropriate.
+
+`ControllerCompleteTransfer`
+
+Invoked when an I/O completes or an error is detected. If this I/O is part of a sequence, `PbcRequestConfigureForIndex`() is called to prepare the next I/O; otherwise, the request is marked for completion.
diff --git a/spb/SkeletonI2C/SkeletonI2C.sln b/spb/SkeletonI2C/SkeletonI2C.sln
new file mode 100644
index 00000000..308053bb
--- /dev/null
+++ b/spb/SkeletonI2C/SkeletonI2C.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0
+MinimumVisualStudioVersion = 12.0
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "skeletoni2c", "skeletoni2c.vcxproj", "{B890FE48-579C-41BF-9D89-064D4C8291D3}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B890FE48-579C-41BF-9D89-064D4C8291D3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {B890FE48-579C-41BF-9D89-064D4C8291D3}.Debug|Win32.Build.0 = Debug|Win32
+ {B890FE48-579C-41BF-9D89-064D4C8291D3}.Release|Win32.ActiveCfg = Release|Win32
+ {B890FE48-579C-41BF-9D89-064D4C8291D3}.Release|Win32.Build.0 = Release|Win32
+ {B890FE48-579C-41BF-9D89-064D4C8291D3}.Debug|x64.ActiveCfg = Debug|x64
+ {B890FE48-579C-41BF-9D89-064D4C8291D3}.Debug|x64.Build.0 = Debug|x64
+ {B890FE48-579C-41BF-9D89-064D4C8291D3}.Release|x64.ActiveCfg = Release|x64
+ {B890FE48-579C-41BF-9D89-064D4C8291D3}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/spb/SkeletonI2C/controller.cpp b/spb/SkeletonI2C/controller.cpp
new file mode 100644
index 00000000..f867fb62
--- /dev/null
+++ b/spb/SkeletonI2C/controller.cpp
@@ -0,0 +1,851 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ controller.cpp
+
+Abstract:
+
+ This module contains the controller-specific functions
+ for handling transfers and implementing interrupts.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+#include "controller.h"
+#include "device.h"
+
+#include "controller.tmh"
+
+const PBC_TRANSFER_SETTINGS g_TransferSettings[] =
+{
+ // TODO: Update this array to reflect changes
+ // made to the PBC_TRANSFER_SETTINGS
+ // structure in internal.h.
+
+ // Bus condition IsStart IsEnd
+ {BusConditionDontCare, FALSE, FALSE}, // SpbRequestTypeInvalid
+ {BusConditionFree, TRUE, TRUE}, // SpbRequestTypeSingle
+ {BusConditionFree, TRUE, FALSE}, // SpbRequestTypeFirst
+ {BusConditionBusy, FALSE, FALSE}, // SpbRequestTypeContinue
+ {BusConditionBusy, FALSE, TRUE} // SpbRequestTypeLast
+};
+
+VOID
+ControllerInitialize(
+ _In_ PPBC_DEVICE pDevice
+ )
+/*++
+
+ Routine Description:
+
+ This routine initializes the controller hardware.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_PBCLOADING);
+
+ NT_ASSERT(pDevice != NULL);
+
+ // TODO: Initialize controller hardware via the
+ // pDevice->pRegisters->* register interface.
+ // Work may include configuring operating modes,
+ // FIFOs, clock, interrupts, etc.
+
+ UNREFERENCED_PARAMETER(pDevice);
+
+ FuncExit(TRACE_FLAG_PBCLOADING);
+}
+
+VOID
+ControllerUninitialize(
+ _In_ PPBC_DEVICE pDevice
+ )
+/*++
+
+ Routine Description:
+
+ This routine uninitializes the controller hardware.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_PBCLOADING);
+
+ NT_ASSERT(pDevice != NULL);
+
+ // TODO: Uninitialize controller hardware via the
+ // pDevice->pRegisters->* register interface
+ // if necessary. Work may include disabling
+ // interrupts, etc.
+
+ UNREFERENCED_PARAMETER(pDevice);
+
+ FuncExit(TRACE_FLAG_PBCLOADING);
+}
+
+VOID
+ControllerConfigureForTransfer(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ PPBC_REQUEST pRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine configures and starts the controller
+ for a transfer.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+ pRequest - a pointer to the PBC request context
+
+ Return Value:
+
+ None. The request is completed asynchronously.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ NT_ASSERT(pDevice != NULL);
+ NT_ASSERT(pRequest != NULL);
+
+ //
+ // Initialize request context for transfer.
+ //
+
+ pRequest->Settings = g_TransferSettings[pRequest->SequencePosition];
+ pRequest->Status = STATUS_SUCCESS;
+
+ //
+ // Configure hardware for transfer.
+ //
+
+ // TODO: Initialize controller hardware for a general
+ // transfer via the pDevice->pRegisters->* register
+ // interface. Work may include setting up transfer,
+ // configuring FIFOs, selecting address, etc.
+
+ if (pRequest->Settings.IsStart)
+ {
+ // TODO: Perform any action to program a start bit.
+ }
+ else if (pRequest->Settings.IsEnd)
+ {
+ // TODO: Perform any action to program a stop bit.
+ }
+
+ if (pRequest->Direction == SpbTransferDirectionToDevice)
+ {
+ // TODO: Perform write-specific configuration,
+ // i.e. pRequest->DataReadyFlag = ...
+ }
+ else if (pRequest->Direction == SpbTransferDirectionFromDevice)
+ {
+ // TODO: Perform read-specific configuration,
+ // i.e. pRequest->DataReadyFlag = ...
+ }
+
+ //
+ // Synchronize access to device context with ISR.
+ //
+
+ // TODO: Uncomment when using interrupts.
+ //WdfInterruptAcquireLock(pDevice->InterruptObject);
+
+ //
+ // Set interrupt mask and clear current status.
+ //
+
+ // TODO: Save desired interrupt mask.
+ // PbcDeviceSetInterruptMask(pDevice, mask)
+
+ pDevice->InterruptStatus = 0;
+
+ Trace(
+ TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_TRANSFER,
+ "Controller configured for %s of %Iu bytes to address 0x%lx "
+ "(SPBREQUEST %p, WDFDEVICE %p)",
+ pRequest->Direction == SpbTransferDirectionFromDevice ? "read" : "write",
+ pRequest->Length,
+ pDevice->pCurrentTarget->Settings.Address,
+ pRequest->SpbRequest,
+ pDevice->FxDevice);
+
+ // TODO: Perform necessary action to begin transfer.
+
+ ControllerEnableInterrupts(
+ pDevice,
+ PbcDeviceGetInterruptMask(pDevice));
+
+ // TODO: Uncomment when using interrupts.
+ //WdfInterruptReleaseLock(pDevice->InterruptObject);
+
+ // TODO: For the purpose of this skeleton sample,
+ // simply complete the request synchronously.
+
+ ControllerCompleteTransfer(pDevice, pRequest, FALSE);
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+}
+
+VOID
+ControllerProcessInterrupts(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ PPBC_REQUEST pRequest,
+ _In_ ULONG InterruptStatus
+ )
+/*++
+
+ Routine Description:
+
+ This routine processes a hardware interrupt. Activities
+ include checking for errors and transferring data.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+ pRequest - a pointer to the PBC request context
+ InterruptStatus - saved interrupt status bits from the ISR.
+ These have already been acknowledged and disabled
+
+ Return Value:
+
+ None. The request is completed asynchronously.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ NTSTATUS status;
+
+ NT_ASSERT(pDevice != NULL);
+ NT_ASSERT(pRequest != NULL);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Ready to process interrupts with status 0x%lx for WDFDEVICE %p",
+ InterruptStatus,
+ pDevice->FxDevice);
+
+ //
+ // Check for address NACK.
+ //
+
+ if (TestAnyBits(InterruptStatus, SI2C_STATUS_ADDRESS_NACK /*update with nack flag*/))
+ {
+ //
+ // An address NACK indicates that a device is
+ // not present at that address or is not responding.
+ // Set the error status accordingly.
+ //
+
+ pRequest->Status = STATUS_NO_SUCH_DEVICE;
+ pRequest->Information = 0;
+
+ // TODO: Perform any additional action needed to handle NACK.
+
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_TRANSFER,
+ "NACK on address 0x%lx (WDFDEVICE %p) - %!STATUS!",
+ pDevice->pCurrentTarget->Settings.Address,
+ pDevice->FxDevice,
+ pRequest->Status);
+
+ //
+ // Complete the transfer and stop processing
+ // interrupts.
+ //
+
+ ControllerCompleteTransfer(pDevice, pRequest, TRUE);
+ goto exit;
+ }
+
+ //
+ // Check for data NACK.
+ //
+
+ if (TestAnyBits(InterruptStatus, SI2C_STATUS_DATA_NACK /*update with nack flag*/))
+ {
+ //
+ // A data NACK is not necessarily an error.
+ // Set the error status to STATUS_SUCCESS and
+ // indicate the number of bytes successfully
+ // transferred in the information field. The
+ // client will determine success or failure of
+ // the IO based on this length.
+ //
+
+ pRequest->Status = STATUS_SUCCESS;
+
+ // TODO: Assuming this info is available, set
+ // information to the actual number of
+ // bytes successfully transferred.
+ //pRequest->Information = 0;
+
+ // TODO: Perform any additional action needed to handle NACK.
+
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_TRANSFER,
+ "NACK after %Iu bytes transferred for address 0x%lx "
+ "(WDFDEVICE %p)- %!STATUS!",
+ pRequest->Information,
+ pDevice->pCurrentTarget->Settings.Address,
+ pDevice->FxDevice,
+ pRequest->Status);
+
+ //
+ // Complete the transfer and stop processing
+ // interrupts.
+ //
+
+ ControllerCompleteTransfer(pDevice, pRequest, TRUE);
+ goto exit;
+ }
+
+ // TODO: Check for other errors.
+
+ if (TestAnyBits(InterruptStatus, SI2C_STATUS_GENERIC_ERROR /*update with error flag*/))
+ {
+ // TODO: Perform any action needed to handle error,
+ // i.e. set status or bytes transferred accordingly.
+
+ pRequest->Status = STATUS_UNSUCCESSFUL;
+ pRequest->Information = 0;
+
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_TRANSFER,
+ "Error after %Iu bytes transferred for address 0x%lx "
+ "(WDFDEVICE %p)- %!STATUS!",
+ pRequest->Information,
+ pDevice->pCurrentTarget->Settings.Address,
+ pDevice->FxDevice,
+ pRequest->Status);
+
+ //
+ // Complete the transfer and stop processing
+ // interrupts.
+ //
+
+ ControllerCompleteTransfer(pDevice, pRequest, TRUE);
+ goto exit;
+ }
+
+ //
+ // Check if controller is ready to transfer more data.
+ //
+
+ if (TestAnyBits(InterruptStatus, pRequest->DataReadyFlag))
+ {
+ //
+ // Transfer data.
+ //
+
+ status = ControllerTransferData(pDevice, pRequest);
+
+ if (!NT_SUCCESS(status))
+ {
+ pRequest->Status = status;
+
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_TRANSFER,
+ "Unexpected error while transferring data for address 0x%lx, "
+ "completing transfer and resetting controller - %!STATUS!",
+ pDevice->pCurrentTarget->Settings.Address,
+ pRequest->Status);
+
+ //
+ // Complete the transfer and stop processing
+ // interrupts.
+ //
+
+ ControllerCompleteTransfer(pDevice, pRequest, TRUE);
+ goto exit;
+ }
+
+ //
+ // If finished transferring data, stop listening for
+ // data ready interrupt. Do not complete transfer
+ // until transfer complete interrupt occurs.
+ //
+
+ if (PbcRequestGetInfoRemaining(pRequest) == 0)
+ {
+ Trace(
+ TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_TRANSFER,
+ "No bytes remaining in transfer for address 0x%lx, wait for "
+ "transfer complete interrupt",
+ pDevice->pCurrentTarget->Settings.Address);
+
+ PbcDeviceAndInterruptMask(pDevice, ~pRequest->DataReadyFlag);
+ }
+ }
+
+ //
+ // Check if transfer is complete.
+ //
+
+ if (TestAnyBits(InterruptStatus, 0 /*update with transfer complete flag*/))
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Transfer complete for address 0x%lx with %Iu bytes remaining",
+ pDevice->pCurrentTarget->Settings.Address,
+ PbcRequestGetInfoRemaining(pRequest));
+
+ //
+ // If transfer complete interrupt occured and there
+ // are still bytes remaining, transfer data. This occurs
+ // when the number of bytes remaining is less than
+ // the FIFO transfer level to trigger a data ready interrupt.
+ //
+
+ if (PbcRequestGetInfoRemaining(pRequest) > 0)
+ {
+ status = ControllerTransferData(pDevice, pRequest);
+
+ if (!NT_SUCCESS(status))
+ {
+ pRequest->Status = status;
+
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_TRANSFER,
+ "Unexpected error while transferring data for address 0x%lx, "
+ "completing transfer and resetting controller "
+ "(WDFDEVICE %p) - %!STATUS!",
+ pDevice->pCurrentTarget->Settings.Address,
+ pDevice->FxDevice,
+ pRequest->Status);
+
+ //
+ // Complete the transfer and stop processing
+ // interrupts.
+ //
+
+ ControllerCompleteTransfer(pDevice, pRequest, TRUE);
+ goto exit;
+ }
+ }
+
+ //
+ // Complete the transfer.
+ //
+
+ ControllerCompleteTransfer(pDevice, pRequest, FALSE);
+ }
+
+exit:
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+}
+
+NTSTATUS
+ControllerTransferData(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ PPBC_REQUEST pRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine transfers data to or from the device.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+ pRequest - a pointer to the PBC request context
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ UNREFERENCED_PARAMETER(pDevice);
+
+ size_t bytesToTransfer = 0;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ //
+ // Write
+ //
+
+ if (pRequest->Direction == SpbTransferDirectionToDevice)
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Ready to write %Iu byte(s) for address 0x%lx",
+ bytesToTransfer,
+ pDevice->pCurrentTarget->Settings.Address);
+
+ // TODO: Perform write. May need to use
+ // PbcRequestGetByte() or some variation.
+ }
+
+ //
+ // Read
+ //
+
+ else
+ {
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Ready to read %Iu byte(s) for address 0x%lx",
+ bytesToTransfer,
+ pDevice->pCurrentTarget->Settings.Address);
+
+ // TODO: Perform read. May need to use
+ // PbcRequestSetByte() or some variation.
+ }
+
+ //
+ // Update request context with bytes transferred.
+ //
+
+ pRequest->Information += bytesToTransfer;
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+
+ return status;
+}
+
+VOID
+ControllerCompleteTransfer(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ PPBC_REQUEST pRequest,
+ _In_ BOOLEAN AbortSequence
+ )
+/*++
+
+ Routine Description:
+
+ This routine completes a data transfer. Unless there are
+ more transfers remaining in the sequence, the request is
+ completed.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+ pRequest - a pointer to the PBC request context
+ AbortSequence - specifies whether the driver should abort the
+ ongoing sequence or begin the next transfer
+
+ Return Value:
+
+ None. The request is completed asynchronously.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ NT_ASSERT(pDevice != NULL);
+ NT_ASSERT(pRequest != NULL);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Transfer (index %lu) %s with %Iu bytes for address 0x%lx "
+ "(SPBREQUEST %p)",
+ pRequest->TransferIndex,
+ NT_SUCCESS(pRequest->Status) ? "complete" : "error",
+ pRequest->Information,
+ pDevice->pCurrentTarget->Settings.Address,
+ pRequest->SpbRequest);
+
+ //
+ // Update request context with information from this transfer.
+ //
+
+ pRequest->TotalInformation += pRequest->Information;
+ pRequest->Information = 0;
+
+ //
+ // Check if there are more transfers
+ // in the sequence.
+ //
+
+ if (!AbortSequence)
+ {
+ pRequest->TransferIndex++;
+
+ if (pRequest->TransferIndex < pRequest->TransferCount)
+ {
+ //
+ // Configure the request for the next transfer.
+ //
+
+ pRequest->Status = PbcRequestConfigureForIndex(
+ pRequest,
+ pRequest->TransferIndex);
+
+ if (NT_SUCCESS(pRequest->Status))
+ {
+ //
+ // Configure controller and kick-off read.
+ // Request will be completed asynchronously.
+ //
+
+ PbcRequestDoTransfer(pDevice,pRequest);
+ goto exit;
+ }
+ }
+ }
+
+ //
+ // If not already cancelled, unmark request cancellable.
+ //
+
+ if (pRequest->Status != STATUS_CANCELLED)
+ {
+ NTSTATUS cancelStatus;
+ cancelStatus = WdfRequestUnmarkCancelable(pRequest->SpbRequest);
+
+ if (!NT_SUCCESS(cancelStatus))
+ {
+ //
+ // WdfRequestUnmarkCancelable should only fail if the request
+ // has already been or is about to be cancelled. If it does fail
+ // the request must NOT be completed - the cancel callback will do
+ // this.
+ //
+
+ NT_ASSERTMSG("WdfRequestUnmarkCancelable should only fail if the request has already been or is about to be cancelled",
+ cancelStatus == STATUS_CANCELLED);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Failed to unmark SPBREQUEST %p as cancelable - %!STATUS!",
+ pRequest->SpbRequest,
+ cancelStatus);
+
+ goto exit;
+ }
+ }
+
+ //
+ // Done or error occurred. Set interrupt mask to 0.
+ // Doing this keeps the DPC from re-enabling interrupts.
+ //
+
+ PbcDeviceSetInterruptMask(pDevice, 0);
+
+ //
+ // Clear the target's current request. This will prevent
+ // the request context from being accessed once the request
+ // is completed (and the context is invalid).
+ //
+
+ pDevice->pCurrentTarget->pCurrentRequest = NULL;
+
+ //
+ // Clear the controller's current target if any of
+ // 1. request is type sequence
+ // 2. request position is single
+ // (did not come between lock/unlock)
+ // Otherwise wait until unlock.
+ //
+
+ if ((pRequest->Type == SpbRequestTypeSequence) ||
+ (pRequest->SequencePosition == SpbRequestSequencePositionSingle))
+ {
+ pDevice->pCurrentTarget = NULL;
+ }
+
+ //
+ // Mark the IO complete. Request not
+ // completed here.
+ //
+
+ pRequest->bIoComplete = TRUE;
+
+exit:
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+}
+
+VOID
+ControllerEnableInterrupts(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ ULONG InterruptMask
+ )
+/*++
+
+ Routine Description:
+
+ This routine enables the hardware interrupts for the
+ specificed mask.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+ InterruptMask - interrupt bits to enable
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ NT_ASSERT(pDevice != NULL);
+
+ Trace(
+ TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_TRANSFER,
+ "Enable interrupts with mask 0x%lx (WDFDEVICE %p)",
+ InterruptMask,
+ pDevice->FxDevice);
+
+ // TODO: Enable interrupts as requested.
+
+ UNREFERENCED_PARAMETER(pDevice);
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+}
+
+VOID
+ControllerDisableInterrupts(
+ _In_ PPBC_DEVICE pDevice
+ )
+/*++
+
+ Routine Description:
+
+ This routine disables all controller interrupts.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ NT_ASSERT(pDevice != NULL);
+
+ // TODO: Disable all interrupts.
+
+ UNREFERENCED_PARAMETER(pDevice);
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+}
+
+ULONG
+ControllerGetInterruptStatus(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ ULONG InterruptMask
+ )
+/*++
+
+ Routine Description:
+
+ This routine gets the interrupt status of the
+ specificed interrupt bits.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+ InterruptMask - interrupt bits to check
+
+ Return Value:
+
+ A bitmap indicating which interrupts are set.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ ULONG interruptStatus = 0;
+
+ NT_ASSERT(pDevice != NULL);
+
+ // TODO: Check if any of the interrupt mask
+ // bits have triggered an interrupt.
+
+ UNREFERENCED_PARAMETER(pDevice);
+ UNREFERENCED_PARAMETER(InterruptMask);
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+
+ return interruptStatus;
+}
+
+VOID
+ControllerAcknowledgeInterrupts(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ ULONG InterruptMask
+ )
+/*++
+
+ Routine Description:
+
+ This routine acknowledges the
+ specificed interrupt bits.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+ InterruptMask - interrupt bits to acknowledge
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ NT_ASSERT(pDevice != NULL);
+
+ // TODO: Acknowledge requested interrupts.
+
+ UNREFERENCED_PARAMETER(pDevice);
+ UNREFERENCED_PARAMETER(InterruptMask);
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+}
diff --git a/spb/SkeletonI2C/controller.h b/spb/SkeletonI2C/controller.h
new file mode 100644
index 00000000..40bcdf67
--- /dev/null
+++ b/spb/SkeletonI2C/controller.h
@@ -0,0 +1,76 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ controller.h
+
+Abstract:
+
+ This module contains the controller-specific function
+ definitions.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _CONTROLLER_H_
+#define _CONTROLLER_H_
+
+//
+// Controller specific function prototypes.
+//
+
+VOID ControllerInitialize(
+ _In_ PPBC_DEVICE pDevice);
+
+VOID ControllerUninitialize(
+ _In_ PPBC_DEVICE pDevice);
+
+VOID
+ControllerConfigureForTransfer(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ PPBC_REQUEST pRequest);
+
+NTSTATUS
+ControllerTransferData(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ PPBC_REQUEST pRequest);
+
+VOID
+ControllerCompleteTransfer(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ PPBC_REQUEST pRequest,
+ _In_ BOOLEAN AbortSequence);
+
+VOID
+ControllerEnableInterrupts(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ ULONG InterruptMask);
+
+VOID
+ControllerDisableInterrupts(
+ _In_ PPBC_DEVICE pDevice);
+
+ULONG
+ControllerGetInterruptStatus(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ ULONG InterruptMask);
+
+VOID
+ControllerAcknowledgeInterrupts(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ ULONG InterruptMask);
+
+VOID
+ControllerProcessInterrupts(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ PPBC_REQUEST pRequest,
+ _In_ ULONG InterruptStatus);
+
+#endif
diff --git a/spb/SkeletonI2C/device.cpp b/spb/SkeletonI2C/device.cpp
new file mode 100644
index 00000000..5666465f
--- /dev/null
+++ b/spb/SkeletonI2C/device.cpp
@@ -0,0 +1,2268 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ device.cpp
+
+Abstract:
+
+ This module contains WDF device initialization
+ and SPB callback functions for the controller driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+#include "device.h"
+#include "controller.h"
+
+#include "device.tmh"
+
+
+/////////////////////////////////////////////////
+//
+// WDF and SPB DDI callbacks.
+//
+/////////////////////////////////////////////////
+
+NTSTATUS
+OnPrepareHardware(
+ _In_ WDFDEVICE FxDevice,
+ _In_ WDFCMRESLIST FxResourcesRaw,
+ _In_ WDFCMRESLIST FxResourcesTranslated
+ )
+/*++
+
+ Routine Description:
+
+ This routine maps the hardware resources to the SPB
+ controller register structure.
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+ FxResourcesRaw - list of translated hardware resources that
+ the PnP manager has assigned to the device
+ FxResourcesTranslated - list of raw hardware resources that
+ the PnP manager has assigned to the device
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ PPBC_DEVICE pDevice = GetDeviceContext(FxDevice);
+ NT_ASSERT(pDevice != NULL);
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ UNREFERENCED_PARAMETER(FxResourcesRaw);
+
+ //
+ // Get the register base for the I2C controller.
+ //
+
+ {
+ ULONG resourceCount = WdfCmResourceListGetCount(FxResourcesTranslated);
+
+ for(ULONG i = 0; i < resourceCount; i++)
+ {
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR res;
+
+ res = WdfCmResourceListGetDescriptor(FxResourcesTranslated, i);
+
+ if (res->Type == CmResourceTypeMemory)
+ {
+ pDevice->pRegisters =
+ (PSKELETONI2C_REGISTERS) MmMapIoSpace(
+ res->u.Memory.Start,
+ res->u.Memory.Length,
+ MmNonCached);
+
+ pDevice->RegistersCb = res->u.Memory.Length;
+
+ if (pDevice->pRegisters == NULL)
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error mapping controller registers (PA:%I64x, length:%d) "
+ "for WDFDEVICE %p - %!STATUS!",
+ res->u.Memory.Start.QuadPart,
+ res->u.Memory.Length,
+ pDevice->FxDevice,
+ status);
+
+ NT_ASSERT(pDevice->pRegisters != NULL);
+
+ goto exit;
+ }
+
+ //
+ // Save the physical address to help identify
+ // the underlying controller while debugging.
+ //
+
+ pDevice->pRegistersPhysicalAddress = res->u.Memory.Start;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_WDFLOADING,
+ "I2C controller @ paddr %I64x vaddr @ %p for WDFDEVICE %p",
+ pDevice->pRegistersPhysicalAddress.QuadPart,
+ pDevice->pRegisters,
+ pDevice->FxDevice);
+ }
+ }
+ }
+
+exit:
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+NTSTATUS
+OnReleaseHardware(
+ _In_ WDFDEVICE FxDevice,
+ _In_ WDFCMRESLIST FxResourcesTranslated
+ )
+/*++
+
+ Routine Description:
+
+ This routine unmaps the SPB controller register structure.
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+ FxResourcesRaw - list of translated hardware resources that
+ the PnP manager has assigned to the device
+ FxResourcesTranslated - list of raw hardware resources that
+ the PnP manager has assigned to the device
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ PPBC_DEVICE pDevice = GetDeviceContext(FxDevice);
+ NT_ASSERT(pDevice != NULL);
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ UNREFERENCED_PARAMETER(FxResourcesTranslated);
+
+ if (pDevice->pRegisters != NULL)
+ {
+ MmUnmapIoSpace(pDevice->pRegisters, pDevice->RegistersCb);
+
+ pDevice->pRegisters = NULL;
+ pDevice->RegistersCb = 0;
+ }
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+NTSTATUS
+OnD0Entry(
+ _In_ WDFDEVICE FxDevice,
+ _In_ WDF_POWER_DEVICE_STATE FxPreviousState
+ )
+/*++
+
+ Routine Description:
+
+ This routine allocates objects needed by the driver
+ and initializes the controller hardware.
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+ FxPreviousState - previous power state
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ PPBC_DEVICE pDevice = GetDeviceContext(FxDevice);
+ NT_ASSERT(pDevice != NULL);
+
+ UNREFERENCED_PARAMETER(FxPreviousState);
+
+ //
+ // Initialize controller.
+ //
+
+ pDevice->pCurrentTarget = NULL;
+
+ ControllerInitialize(pDevice);
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+OnD0Exit(
+ _In_ WDFDEVICE FxDevice,
+ _In_ WDF_POWER_DEVICE_STATE FxPreviousState
+ )
+/*++
+
+ Routine Description:
+
+ This routine destroys objects needed by the driver
+ and uninitializes the controller hardware.
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+ FxPreviousState - previous power state
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ PPBC_DEVICE pDevice = GetDeviceContext(FxDevice);
+ NT_ASSERT(pDevice != NULL);
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ UNREFERENCED_PARAMETER(FxPreviousState);
+
+ //
+ // Uninitialize controller.
+ //
+
+ ControllerUninitialize(pDevice);
+
+ pDevice->pCurrentTarget = NULL;
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+NTSTATUS
+OnSelfManagedIoInit(
+ _In_ WDFDEVICE FxDevice
+ )
+/*++
+
+ Routine Description:
+
+ Initializes and starts the device's self-managed I/O operations.
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ PPBC_DEVICE pDevice = GetDeviceContext(FxDevice);
+ NTSTATUS status;
+
+ //
+ // Register for monitor power setting callback. This will be
+ // used to dynamically set the idle timeout delay according
+ // to the monitor power state.
+ //
+
+ NT_ASSERT(pDevice->pMonitorPowerSettingHandle == NULL);
+
+ status = PoRegisterPowerSettingCallback(
+ WdfDeviceWdmGetDeviceObject(pDevice->FxDevice),
+ &GUID_MONITOR_POWER_ON,
+ OnMonitorPowerSettingCallback,
+ (PVOID)pDevice->FxDevice,
+ &pDevice->pMonitorPowerSettingHandle);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed to register monitor power setting callback - %!STATUS!",
+ status);
+
+ goto exit;
+ }
+
+exit:
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+VOID
+OnSelfManagedIoCleanup(
+ _In_ WDFDEVICE FxDevice
+ )
+/*++
+
+ Routine Description:
+
+ Cleanup for the device's self-managed I/O operations.
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ PPBC_DEVICE pDevice = GetDeviceContext(FxDevice);
+
+ //
+ // Unregister for monitor power setting callback.
+ //
+
+ if (pDevice->pMonitorPowerSettingHandle != NULL)
+ {
+ PoUnregisterPowerSettingCallback(pDevice->pMonitorPowerSettingHandle);
+ pDevice->pMonitorPowerSettingHandle = NULL;
+ }
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+}
+
+__drv_functionClass(POWER_SETTING_CALLBACK)
+_IRQL_requires_same_
+NTSTATUS
+OnMonitorPowerSettingCallback(
+ _In_ LPCGUID SettingGuid,
+ _In_reads_bytes_(ValueLength) PVOID Value,
+ _In_ ULONG ValueLength,
+ _Inout_opt_ PVOID Context
+ )
+/*++
+
+ Routine Description:
+
+ This routine updates the idle timeout delay according
+ to the current monitor power setting.
+
+ Arguments:
+
+ SettingGuid - the setting GUID
+ Value - pointer to the new value of the power setting that changed
+ ValueLength - value of type ULONG that specifies the size, in bytes,
+ of the new power setting value
+ Context - the WDFDEVICE pointer context
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ UNREFERENCED_PARAMETER(ValueLength);
+
+ WDFDEVICE Device;
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
+ BOOLEAN isMonitorOff;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ if (Context == NULL)
+ {
+ status = STATUS_INVALID_PARAMETER;
+
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "%!FUNC! parameter Context is NULL - %!STATUS!",
+ status);
+
+ goto exit;
+ }
+
+ Device = (WDFDEVICE)Context;
+
+ //
+ // We only expect GUID_MONITOR_POWER_ON notifications
+ // in this callback, but let's check just to be sure.
+ //
+
+ if (IsEqualGUID(*SettingGuid, GUID_MONITOR_POWER_ON))
+ {
+ NT_ASSERT(Value != NULL);
+ NT_ASSERT(ValueLength == sizeof(ULONG));
+
+ //
+ // Determine power setting.
+ //
+
+ isMonitorOff = ((*(PULONG)Value) == MONITOR_POWER_OFF);
+
+ //
+ // Update the idle timeout delay.
+ //
+
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(
+ &idleSettings,
+ IdleCannotWakeFromS0);
+
+ idleSettings.IdleTimeoutType = SystemManagedIdleTimeoutWithHint;
+
+ if (isMonitorOff)
+ {
+ idleSettings.IdleTimeout = IDLE_TIMEOUT_MONITOR_OFF;
+ }
+ else
+ {
+ idleSettings.IdleTimeout = IDLE_TIMEOUT_MONITOR_ON;
+
+ }
+
+ status = WdfDeviceAssignS0IdleSettings(
+ Device,
+ &idleSettings);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed to assign S0 idle settings - %!STATUS!",
+ status);
+
+ goto exit;
+ }
+ }
+
+exit:
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+NTSTATUS
+OnTargetConnect(
+ _In_ WDFDEVICE SpbController,
+ _In_ SPBTARGET SpbTarget
+ )
+/*++
+
+ Routine Description:
+
+ This routine is invoked whenever a peripheral driver opens
+ a target. It retrieves target-specific settings from the
+ Resource Hub and saves them in the target's context.
+
+ Arguments:
+
+ SpbController - a handle to the framework device object
+ representing an SPB controller
+ SpbTarget - a handle to the SPBTARGET object
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBDDI);
+
+ PPBC_DEVICE pDevice = GetDeviceContext(SpbController);
+ PPBC_TARGET pTarget = GetTargetContext(SpbTarget);
+
+ NT_ASSERT(pDevice != NULL);
+ NT_ASSERT(pTarget != NULL);
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ //
+ // Get target connection parameters.
+ //
+
+ SPB_CONNECTION_PARAMETERS params;
+ SPB_CONNECTION_PARAMETERS_INIT(&params);
+
+ SpbTargetGetConnectionParameters(SpbTarget, &params);
+
+ //
+ // Retrieve target settings.
+ //
+
+ status = PbcTargetGetSettings(pDevice,
+ params.ConnectionParameters,
+ &pTarget->Settings
+ );
+
+ //
+ // Initialize target context.
+ //
+
+ if (NT_SUCCESS(status))
+ {
+ pTarget->SpbTarget = SpbTarget;
+ pTarget->pCurrentRequest = NULL;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBDDI,
+ "Connected to SPBTARGET %p at address 0x%lx from WDFDEVICE %p",
+ pTarget->SpbTarget,
+ pTarget->Settings.Address,
+ pDevice->FxDevice);
+ }
+
+ FuncExit(TRACE_FLAG_SPBDDI);
+
+ return status;
+}
+
+VOID
+OnControllerLock(
+ _In_ WDFDEVICE SpbController,
+ _In_ SPBTARGET SpbTarget,
+ _In_ SPBREQUEST SpbRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine is invoked whenever the controller is to
+ be locked for a single target. The request is only completed
+ if there is an error configuring the transfer.
+
+ Arguments:
+
+ SpbController - a handle to the framework device object
+ representing an SPB controller
+ SpbTarget - a handle to the SPBTARGET object
+ SpbRequest - a handle to the SPBREQUEST object
+
+ Return Value:
+
+ None. The request is completed synchronously.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBDDI);
+
+ PPBC_DEVICE pDevice = GetDeviceContext(SpbController);
+ PPBC_TARGET pTarget = GetTargetContext(SpbTarget);
+
+ NT_ASSERT(pDevice != NULL);
+ NT_ASSERT(pTarget != NULL);
+
+ //
+ // Acquire the device lock.
+ //
+
+ WdfSpinLockAcquire(pDevice->Lock);
+
+ //
+ // Assign current target.
+ //
+
+ NT_ASSERT(pDevice->pCurrentTarget == NULL);
+
+ pDevice->pCurrentTarget = pTarget;
+
+ WdfSpinLockRelease(pDevice->Lock);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBDDI,
+ "Controller locked for SPBTARGET %p at address 0x%lx (WDFDEVICE %p)",
+ pTarget->SpbTarget,
+ pTarget->Settings.Address,
+ pDevice->FxDevice);
+
+ //
+ // Complete lock request.
+ //
+
+ SpbRequestComplete(SpbRequest, STATUS_SUCCESS);
+
+ FuncExit(TRACE_FLAG_SPBDDI);
+}
+
+VOID
+OnControllerUnlock(
+ _In_ WDFDEVICE SpbController,
+ _In_ SPBTARGET SpbTarget,
+ _In_ SPBREQUEST SpbRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine is invoked whenever the controller is to
+ be unlocked for a single target. The request is only completed
+ if there is an error configuring the transfer.
+
+ Arguments:
+
+ SpbController - a handle to the framework device object
+ representing an SPB controller
+ SpbTarget - a handle to the SPBTARGET object
+ SpbRequest - a handle to the SPBREQUEST object
+
+ Return Value:
+
+ None. The request is completed asynchronously.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBDDI);
+
+ PPBC_DEVICE pDevice = GetDeviceContext(SpbController);
+ PPBC_TARGET pTarget = GetTargetContext(SpbTarget);
+
+ NT_ASSERT(pDevice != NULL);
+ NT_ASSERT(pTarget != NULL);
+
+ //
+ // Acquire the device lock.
+ //
+
+ WdfSpinLockAcquire(pDevice->Lock);
+
+ // TODO: Check if there is an active sequence
+ // and if so perform any action necessary
+ // to stop the transfer in process.
+
+ //
+ // Remove current target.
+ //
+
+ NT_ASSERT(pDevice->pCurrentTarget == pTarget);
+
+ pDevice->pCurrentTarget = NULL;
+
+ WdfSpinLockRelease(pDevice->Lock);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBDDI,
+ "Controller unlocked for SPBTARGET %p at address 0x%lx (WDFDEVICE %p)",
+ pTarget->SpbTarget,
+ pTarget->Settings.Address,
+ pDevice->FxDevice);
+
+ //
+ // Complete lock request.
+ //
+
+ SpbRequestComplete(SpbRequest, STATUS_SUCCESS);
+
+ FuncExit(TRACE_FLAG_SPBDDI);
+}
+
+VOID
+OnRead(
+ _In_ WDFDEVICE SpbController,
+ _In_ SPBTARGET SpbTarget,
+ _In_ SPBREQUEST SpbRequest,
+ _In_ size_t Length
+ )
+/*++
+
+ Routine Description:
+
+ This routine sets up a read from the target device using
+ the supplied buffers. The request is only completed
+ if there is an error configuring the transfer.
+
+ Arguments:
+
+ SpbController - a handle to the framework device object
+ representing an SPB controller
+ SpbTarget - a handle to the SPBTARGET object
+ SpbRequest - a handle to the SPBREQUEST object
+ Length - the number of bytes to read from the target
+
+ Return Value:
+
+ None. The request is completed asynchronously.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBDDI);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBDDI,
+ "Received read request %p of length %Iu for SPBTARGET %p "
+ "(WDFDEVICE %p)",
+ SpbRequest,
+ Length,
+ SpbTarget,
+ SpbController);
+
+ PbcRequestConfigureForNonSequence(
+ SpbController,
+ SpbTarget,
+ SpbRequest,
+ Length);
+
+ FuncExit(TRACE_FLAG_SPBDDI);
+}
+
+VOID
+OnWrite(
+ _In_ WDFDEVICE SpbController,
+ _In_ SPBTARGET SpbTarget,
+ _In_ SPBREQUEST SpbRequest,
+ _In_ size_t Length
+ )
+/*++
+
+ Routine Description:
+
+ This routine sets up a write to the target device using
+ the supplied buffers. The request is only completed
+ if there is an error configuring the transfer.
+
+ Arguments:
+
+ SpbController - a handle to the framework device object
+ representing an SPB controller
+ SpbTarget - a handle to the SPBTARGET object
+ SpbRequest - a handle to the SPBREQUEST object
+ Length - the number of bytes to write to the target
+
+ Return Value:
+
+ None. The request is completed asynchronously.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBDDI);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBDDI,
+ "Received write request %p of length %Iu for SPBTARGET %p "
+ "(WDFDEVICE %p)",
+ SpbRequest,
+ Length,
+ SpbTarget,
+ SpbController);
+
+ PbcRequestConfigureForNonSequence(
+ SpbController,
+ SpbTarget,
+ SpbRequest,
+ Length);
+
+ FuncExit(TRACE_FLAG_SPBDDI);
+}
+
+VOID
+OnSequence(
+ _In_ WDFDEVICE SpbController,
+ _In_ SPBTARGET SpbTarget,
+ _In_ SPBREQUEST SpbRequest,
+ _In_ ULONG TransferCount
+ )
+/*++
+
+ Routine Description:
+
+ This routine sets up a sequence of reads and writes. It
+ validates parameters as necessary. The request is only
+ completed if there is an error configuring the transfer.
+
+ Arguments:
+
+ SpbController - a handle to the framework device object
+ representing an SPB controller
+ SpbTarget - a handle to the SPBTARGET object
+ SpbRequest - a handle to the SPBREQUEST object
+ TransferCount - number of individual transfers in the sequence
+
+ Return Value:
+
+ None. The request is completed asynchronously.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBDDI);
+
+ PPBC_DEVICE pDevice = GetDeviceContext(SpbController);
+ PPBC_TARGET pTarget = GetTargetContext(SpbTarget);
+ PPBC_REQUEST pRequest = GetRequestContext(SpbRequest);
+ BOOLEAN completeRequest = FALSE;
+
+ NT_ASSERT(pDevice != NULL);
+ NT_ASSERT(pTarget != NULL);
+ NT_ASSERT(pRequest != NULL);
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ //
+ // Get request parameters.
+ //
+
+ SPB_REQUEST_PARAMETERS params;
+ SPB_REQUEST_PARAMETERS_INIT(&params);
+ SpbRequestGetParameters(SpbRequest, &params);
+
+ NT_ASSERT(params.Position == SpbRequestSequencePositionSingle);
+ NT_ASSERT(params.Type == SpbRequestTypeSequence);
+
+ //
+ // Initialize request context.
+ //
+
+ pRequest->SpbRequest = SpbRequest;
+ pRequest->Type = params.Type;
+ pRequest->TotalInformation = 0;
+ pRequest->TransferCount = TransferCount;
+ pRequest->TransferIndex = 0;
+ pRequest->bIoComplete = FALSE;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBDDI,
+ "Received sequence request %p with transfer count %d for SPBTARGET %p "
+ "(WDFDEVICE %p)",
+ pRequest->SpbRequest,
+ pRequest->TransferCount,
+ SpbTarget,
+ SpbController);
+
+ //
+ // Validate the request before beginning the transfer.
+ //
+
+ status = PbcRequestValidate(pRequest);
+
+ if (!NT_SUCCESS(status))
+ {
+ goto exit;
+ }
+
+ //
+ // Configure the request.
+ //
+
+ status = PbcRequestConfigureForIndex(pRequest, 0);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBDDI,
+ "Error configuring request context for SPBREQUEST %p "
+ "(SPBTARGET %p) - %!STATUS!",
+ pRequest->SpbRequest,
+ SpbTarget,
+ status);
+
+ goto exit;
+ }
+
+ //
+ // Acquire the device lock.
+ //
+
+ WdfSpinLockAcquire(pDevice->Lock);
+
+ //
+ // Mark request cancellable (if cancellation supported).
+ //
+
+ status = WdfRequestMarkCancelableEx(
+ pRequest->SpbRequest, OnCancel);
+
+ if (!NT_SUCCESS(status))
+ {
+ //
+ // WdfRequestMarkCancelableEx should only fail if the request
+ // has already been cancelled. If it does fail the request
+ // must be completed with the corresponding status.
+ //
+
+ NT_ASSERTMSG("WdfRequestMarkCancelableEx should only fail if the request has already been cancelled",
+ status == STATUS_CANCELLED);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Failed to mark SPBREQUEST %p cancellable - %!STATUS!",
+ pRequest->SpbRequest,
+ status);
+
+ WdfSpinLockRelease(pDevice->Lock);
+ goto exit;
+ }
+
+ //
+ // Update device and target contexts.
+ //
+
+ NT_ASSERT(pDevice->pCurrentTarget == NULL);
+ NT_ASSERT(pTarget->pCurrentRequest == NULL);
+
+ pDevice->pCurrentTarget = pTarget;
+ pTarget->pCurrentRequest = pRequest;
+
+ //
+ // Configure controller and kick-off read.
+ // Request will be completed asynchronously.
+ //
+
+ PbcRequestDoTransfer(pDevice, pRequest);
+
+ // TODO: Remove this block. For the purpose of this
+ // skeleton sample, simply complete the request
+ // synchronously. This must be done outside of
+ // the locked code.
+ if (pRequest->bIoComplete)
+ {
+ completeRequest = TRUE;
+ }
+
+ WdfSpinLockRelease(pDevice->Lock);
+
+ // TODO: Remove this block. For the purpose of this
+ // skeleton sample, simply complete the request
+ // synchronously. This must be done outside of
+ // the locked code.
+ if (completeRequest)
+ {
+ PbcRequestComplete(pRequest);
+ }
+
+exit:
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBDDI,
+ "Error configuring sequence, completing "
+ "SPBREQUEST %p synchronously - %!STATUS!",
+ pRequest->SpbRequest,
+ status);
+
+ SpbRequestComplete(SpbRequest, status);
+ }
+
+ FuncExit(TRACE_FLAG_SPBDDI);
+}
+
+VOID
+OnOtherInCallerContext(
+ _In_ WDFDEVICE SpbController,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine preprocesses custom IO requests before the framework
+ places them in an IO queue. For requests using the SPB transfer list
+ format, it calls SpbRequestCaptureIoOtherTransferList to capture the
+ client's buffers.
+
+ Arguments:
+
+ SpbController - a handle to the framework device object
+ representing an SPB controller
+ SpbRequest - a handle to the SPBREQUEST object
+
+ Return Value:
+
+ None. The request is either completed or enqueued asynchronously.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBDDI);
+
+ NTSTATUS status;
+
+ //
+ // Check for custom IOCTLs that this driver handles. If
+ // unrecognized mark as STATUS_NOT_SUPPORTED and complete.
+ //
+
+ WDF_REQUEST_PARAMETERS fxParams;
+ WDF_REQUEST_PARAMETERS_INIT(&fxParams);
+
+ WdfRequestGetParameters(FxRequest, &fxParams);
+
+ if ((fxParams.Type != WdfRequestTypeDeviceControl) &&
+ (fxParams.Type != WdfRequestTypeDeviceControlInternal))
+ {
+ status = STATUS_NOT_SUPPORTED;
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBDDI,
+ "FxRequest %p is of unsupported request type - %!STATUS!",
+ FxRequest,
+ status
+ );
+ goto exit;
+ }
+
+ //
+ // TODO: verify the driver supports this DeviceIoContol code,
+ // otherwise mark as STATUS_NOT_SUPPORTED and complete.
+ //
+
+ //
+ // For custom IOCTLs that use the SPB transfer list format
+ // (i.e. sequence formatting), call SpbRequestCaptureIoOtherTransferList
+ // so that the driver can leverage other SPB DDIs for this request.
+ //
+
+ status = SpbRequestCaptureIoOtherTransferList((SPBREQUEST)FxRequest);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBDDI,
+ "Failed to capture transfer list for custom SpbRequest %p"
+ " - %!STATUS!",
+ FxRequest,
+ status
+ );
+ goto exit;
+ }
+
+ //
+ // Preprocessing has succeeded, enqueue the request.
+ //
+
+ status = WdfDeviceEnqueueRequest(SpbController, FxRequest);
+
+ if (!NT_SUCCESS(status))
+ {
+ goto exit;
+ }
+
+exit:
+
+ if (!NT_SUCCESS(status))
+ {
+ WdfRequestComplete(FxRequest, status);
+ }
+
+ FuncExit(TRACE_FLAG_SPBDDI);
+}
+
+VOID
+OnOther(
+ _In_ WDFDEVICE SpbController,
+ _In_ SPBTARGET SpbTarget,
+ _In_ SPBREQUEST SpbRequest,
+ _In_ size_t OutputBufferLength,
+ _In_ size_t InputBufferLength,
+ _In_ ULONG IoControlCode
+ )
+/*++
+
+ Routine Description:
+
+ This routine processes custom IO requests that are not natively
+ supported by the SPB framework extension. For requests using the
+ SPB transfer list format, SpbRequestCaptureIoOtherTransferList
+ must have been called in the driver's OnOtherInCallerContext routine.
+
+ Arguments:
+
+ SpbController - a handle to the framework device object
+ representing an SPB controller
+ SpbTarget - a handle to the SPBTARGET object
+ SpbRequest - a handle to the SPBREQUEST object
+ OutputBufferLength - the request's output buffer length
+ InputBufferLength - the requests input buffer length
+ IoControlCode - the device IO control code
+
+ Return Value:
+
+ None. The request is completed asynchronously.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBDDI);
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ UNREFERENCED_PARAMETER(SpbController);
+ UNREFERENCED_PARAMETER(SpbTarget);
+ UNREFERENCED_PARAMETER(SpbRequest);
+ UNREFERENCED_PARAMETER(OutputBufferLength);
+ UNREFERENCED_PARAMETER(InputBufferLength);
+ UNREFERENCED_PARAMETER(IoControlCode);
+
+ //
+ // TODO: the driver should take the following steps
+ //
+ // 1. Verify this specific DeviceIoContol code is supported,
+ // otherwise mark as STATUS_NOT_SUPPORTED and complete.
+ //
+ // 2. If this IOCTL uses SPB_TRANSFER_LIST and the driver has
+ // called SpbRequestCaptureIoOtherTransferList previously,
+ // validate the request format. The driver can make use of
+ // SpbRequestGetTransferParameters to retrieve each transfer
+ // descriptor.
+ //
+ // If this IOCTL uses some proprietary buffer formating
+ // instead of SPB_TRANSFER_LIST, validate appropriately.
+ //
+ // 3. Setup the device, target, and request contexts as necessary,
+ // and program the hardware for the transfer.
+ //
+
+
+ // TODO: Remove this block. For the purpose of this
+ // skeleton sample, simply complete the request
+ // synchronously. Note this must be done outside
+ // of any locked code.
+ SpbRequestComplete(SpbRequest, status);
+
+ FuncExit(TRACE_FLAG_SPBDDI);
+}
+
+VOID
+OnCancel(
+ _In_ WDFREQUEST FxRequest
+)
+
+/*++
+
+ Routine Description:
+
+ This routine cancels an outstanding request. It
+ must synchronize with other driver callbacks.
+
+ Arguments:
+
+ wdfRequest - a handle to the WDFREQUEST object
+
+ Return Value:
+
+ None. The request is completed with status.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ SPBREQUEST spbRequest = (SPBREQUEST) FxRequest;
+ PPBC_DEVICE pDevice;
+ PPBC_TARGET pTarget;
+ PPBC_REQUEST pRequest;
+ BOOLEAN bTransferCompleted = FALSE;
+
+ //
+ // Get the contexts.
+ //
+
+ pDevice = GetDeviceContext(SpbRequestGetController(spbRequest));
+ pTarget = GetTargetContext(SpbRequestGetTarget(spbRequest));
+ pRequest = GetRequestContext(spbRequest);
+
+ NT_ASSERT(pDevice != NULL);
+ NT_ASSERT(pTarget != NULL);
+ NT_ASSERT(pRequest != NULL);
+
+ //
+ // Acquire the device lock.
+ //
+
+ WdfSpinLockAcquire(pDevice->Lock);
+
+ //
+ // Make sure the current target and request
+ // are valid.
+ //
+
+ if (pTarget != pDevice->pCurrentTarget)
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_TRANSFER,
+ "Cancel callback without a valid current target for WDFDEVICE %p, "
+ "this should only occur if SPBREQUEST %p was already completed",
+ pDevice->FxDevice,
+ spbRequest
+ );
+
+ goto exit;
+ }
+
+ if (pRequest != pTarget->pCurrentRequest)
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_TRANSFER,
+ "Cancel callback without a valid current request for SPBTARGET %p, "
+ "this should only occur if SPBREQUEST %p was already completed",
+ pTarget->SpbTarget,
+ spbRequest);
+
+ goto exit;
+ }
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Cancel callback with outstanding SPBREQUEST %p, "
+ "stop IO and complete it",
+ spbRequest);
+
+ //
+ // Stop delay timer.
+ //
+
+ if(WdfTimerStop(pDevice->DelayTimer, FALSE))
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Delay timer previously schedule, now stopped");
+ }
+
+ //
+ // Disable interrupts and clear saved stat for DPC.
+ // Must synchronize with ISR.
+ //
+
+ NT_ASSERT(pDevice->InterruptObject != NULL);
+
+ // TODO: Uncomment when using interrupts.
+ //WdfInterruptAcquireLock(pDevice->InterruptObject);
+
+ ControllerDisableInterrupts(pDevice);
+ pDevice->InterruptStatus = 0;
+
+ //
+ // TODO: Implement any necessary logic to abort the
+ // current IO operation. For I2C this requires
+ // driving a stop bit on the bus.
+ //
+
+ // TODO: Uncomment when using interrupts.
+ //WdfInterruptReleaseLock(pDevice->InterruptObject);
+
+ //
+ // Mark request as cancelled and complete.
+ //
+
+ pRequest->Status = STATUS_CANCELLED;
+
+ ControllerCompleteTransfer(pDevice, pRequest, TRUE);
+ NT_ASSERT(pRequest->bIoComplete == TRUE);
+ bTransferCompleted = TRUE;
+
+exit:
+
+ //
+ // Release the device lock.
+ //
+
+ WdfSpinLockRelease(pDevice->Lock);
+
+ //
+ // Complete the request. There shouldn't be more IO.
+ // This must be done outside of the locked code.
+ //
+
+ if (bTransferCompleted)
+ {
+ PbcRequestComplete(pRequest);
+ }
+
+ FuncExit(TRACE_FLAG_SPBDDI);
+}
+
+
+/////////////////////////////////////////////////
+//
+// Interrupt handling functions.
+//
+/////////////////////////////////////////////////
+
+BOOLEAN
+OnInterruptIsr(
+ _In_ WDFINTERRUPT Interrupt,
+ _In_ ULONG MessageID
+ )
+/*++
+
+ Routine Description:
+
+ This routine responds to interrupts generated by the
+ controller. If one is recognized, it queues a DPC for
+ processing. The interrupt is acknowledged and subsequent
+ interrupts are temporarily disabled.
+
+ Arguments:
+
+ Interrupt - a handle to a framework interrupt object
+ MessageID - message number identifying the device's
+ hardware interrupt message (if using MSI)
+
+ Return Value:
+
+ TRUE if interrupt recognized.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ BOOLEAN interruptRecognized = FALSE;
+ ULONG stat;
+ PPBC_DEVICE pDevice = GetDeviceContext(
+ WdfInterruptGetDevice(Interrupt));
+
+ UNREFERENCED_PARAMETER(MessageID);
+
+ NT_ASSERT(pDevice != NULL);
+
+ //
+ // Queue a DPC if the device's interrupt
+ // is enabled and active.
+ //
+
+ stat = ControllerGetInterruptStatus(
+ pDevice,
+ PbcDeviceGetInterruptMask(pDevice));
+
+ if (stat > 0)
+ {
+ Trace(
+ TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_TRANSFER,
+ "Interrupt with status 0x%lx for WDFDEVICE %p",
+ stat,
+ pDevice->FxDevice);
+
+ //
+ // Save the interrupt status and disable all other
+ // interrupts for now. They will be re-enabled
+ // in OnInterruptDpc. Queue the DPC.
+ //
+
+ interruptRecognized = TRUE;
+
+ pDevice->InterruptStatus |= (stat);
+ ControllerDisableInterrupts(pDevice);
+
+ if(!WdfInterruptQueueDpcForIsr(Interrupt))
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Interrupt with status 0x%lx occurred with "
+ "DPC already queued for WDFDEVICE %p",
+ stat,
+ pDevice->FxDevice);
+ }
+ }
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+
+ return interruptRecognized;
+}
+
+VOID
+OnInterruptDpc(
+ _In_ WDFINTERRUPT Interrupt,
+ _In_ WDFOBJECT WdfDevice
+ )
+/*++
+
+ Routine Description:
+
+ This routine processes interrupts from the controller.
+ When finished it reenables interrupts as appropriate.
+
+ Arguments:
+
+ Interrupt - a handle to a framework interrupt object
+ WdfDevice - a handle to the framework device object
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ PPBC_DEVICE pDevice;
+ PPBC_TARGET pTarget;
+ PPBC_REQUEST pRequest = NULL;
+ ULONG stat;
+ BOOLEAN bInterruptsProcessed = FALSE;
+ BOOLEAN completeRequest = FALSE;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+
+ pDevice = GetDeviceContext(WdfDevice);
+ NT_ASSERT(pDevice != NULL);
+
+ //
+ // Acquire the device lock.
+ //
+
+ WdfSpinLockAcquire(pDevice->Lock);
+
+ //
+ // Make sure the target and request are
+ // still valid.
+ //
+
+ pTarget = pDevice->pCurrentTarget;
+
+ if (pTarget == NULL)
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_TRANSFER,
+ "DPC scheduled without a valid current target for WDFDEVICE %p, "
+ "this should only occur if the request was already cancelled",
+ pDevice->FxDevice);
+
+ goto exit;
+ }
+
+ pRequest = pTarget->pCurrentRequest;
+
+ if (pRequest == NULL)
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_TRANSFER,
+ "DPC scheduled without a valid current request for SPBTARGET %p, "
+ "this should only occur if the request was already cancelled",
+ pTarget->SpbTarget);
+
+ goto exit;
+ }
+
+ NT_ASSERT(pRequest->SpbRequest != NULL);
+
+ //
+ // Synchronize shared data buffers with ISR.
+ // Copy interrupt status and clear shared buffer.
+ // If there is a current target and request,
+ // a DPC should never occur with interrupt status 0.
+ //
+
+ // TODO: Uncomment when using interrupts.
+ //WdfInterruptAcquireLock(Interrupt);
+
+ stat = pDevice->InterruptStatus;
+ pDevice->InterruptStatus = 0;
+
+ // TODO: Uncomment when using interrupts.
+ //WdfInterruptReleaseLock(Interrupt);
+
+ if (stat == 0)
+ {
+ goto exit;
+ }
+
+ Trace(
+ TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_TRANSFER,
+ "DPC for interrupt with status 0x%lx for WDFDEVICE %p",
+ stat,
+ pDevice->FxDevice);
+
+ //
+ // Acknowledge and process interrupts.
+ //
+
+ ControllerAcknowledgeInterrupts(pDevice, stat);
+
+ ControllerProcessInterrupts(pDevice, pRequest, stat);
+ bInterruptsProcessed = TRUE;
+ if (pRequest->bIoComplete)
+ {
+ completeRequest = TRUE;
+ }
+
+ //
+ // Re-enable interrupts if necessary. Synchronize with ISR.
+ //
+
+ // TODO: Uncomment when using interrupts.
+ //WdfInterruptAcquireLock(Interrupt);
+
+ ULONG mask = PbcDeviceGetInterruptMask(pDevice);
+
+ if (mask > 0)
+ {
+ Trace(
+ TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_TRANSFER,
+ "Re-enable interrupts with mask 0x%lx for WDFDEVICE %p",
+ mask,
+ pDevice->FxDevice);
+
+ ControllerEnableInterrupts(pDevice, mask);
+ }
+
+ // TODO: Uncomment when using interrupts.
+ //WdfInterruptReleaseLock(Interrupt);
+
+exit:
+
+ //
+ // Release the device lock.
+ //
+
+ WdfSpinLockRelease(pDevice->Lock);
+
+ //
+ // Complete the request if necessary.
+ // This must be done outside of the locked code.
+ //
+
+ if (bInterruptsProcessed)
+ {
+ if (completeRequest)
+ {
+ PbcRequestComplete(pRequest);
+ }
+ }
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+}
+
+
+/////////////////////////////////////////////////
+//
+// PBC functions.
+//
+/////////////////////////////////////////////////
+
+NTSTATUS
+PbcTargetGetSettings(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ PVOID ConnectionParameters,
+ _Out_ PPBC_TARGET_SETTINGS pSettings
+ )
+/*++
+
+ Routine Description:
+
+ This routine populates the target's settings.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+ ConnectionParameters - a pointer to a blob containing the
+ connection parameters
+ Settings - a pointer the the target's settings
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_PBCLOADING);
+
+ UNREFERENCED_PARAMETER(pDevice);
+
+ NT_ASSERT(ConnectionParameters != nullptr);
+ NT_ASSERT(pSettings != nullptr);
+
+ PRH_QUERY_CONNECTION_PROPERTIES_OUTPUT_BUFFER connection;
+ PPNP_SERIAL_BUS_DESCRIPTOR descriptor;
+ PPNP_I2C_SERIAL_BUS_DESCRIPTOR i2cDescriptor;
+
+ connection = (PRH_QUERY_CONNECTION_PROPERTIES_OUTPUT_BUFFER)
+ ConnectionParameters;
+
+ if (connection->PropertiesLength < sizeof(PNP_SERIAL_BUS_DESCRIPTOR))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_PBCLOADING,
+ "Invalid connection properties (length = %lu, "
+ "expected = %Iu)",
+ connection->PropertiesLength,
+ sizeof(PNP_SERIAL_BUS_DESCRIPTOR));
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ descriptor = (PPNP_SERIAL_BUS_DESCRIPTOR)
+ connection->ConnectionProperties;
+
+ if (descriptor->SerialBusType != I2C_SERIAL_BUS_TYPE)
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_PBCLOADING,
+ "Bus type %c not supported, only I2C",
+ descriptor->SerialBusType);
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ i2cDescriptor = (PPNP_I2C_SERIAL_BUS_DESCRIPTOR)
+ connection->ConnectionProperties;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_PBCLOADING,
+ "I2C Connection Descriptor %p "
+ "ConnectionSpeed:%lu "
+ "Address:0x%hx",
+ i2cDescriptor,
+ i2cDescriptor->ConnectionSpeed,
+ i2cDescriptor->SlaveAddress);
+
+ // Target address
+ pSettings->Address = (ULONG)i2cDescriptor->SlaveAddress;
+
+ // Address mode
+ USHORT i2cFlags = i2cDescriptor->SerialBusDescriptor.TypeSpecificFlags;
+ pSettings->AddressMode =
+ ((i2cFlags & I2C_SERIAL_BUS_SPECIFIC_FLAG_10BIT_ADDRESS) == 0) ?
+ AddressMode7Bit : AddressMode10Bit;
+
+ // Clock speed
+ pSettings->ConnectionSpeed = i2cDescriptor->ConnectionSpeed;
+
+ FuncExit(TRACE_FLAG_PBCLOADING);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+PbcRequestValidate(
+ _In_ PPBC_REQUEST pRequest)
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ SPB_TRANSFER_DESCRIPTOR descriptor;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ //
+ // Validate each transfer descriptor.
+ //
+
+ for (ULONG i = 0; i < pRequest->TransferCount; i++)
+ {
+ //
+ // Get transfer parameters for index.
+ //
+
+ SPB_TRANSFER_DESCRIPTOR_INIT(&descriptor);
+
+ SpbRequestGetTransferParameters(
+ pRequest->SpbRequest,
+ i,
+ &descriptor,
+ nullptr);
+
+ //
+ // Validate the transfer length.
+ //
+
+ if (descriptor.TransferLength > SI2C_MAX_TRANSFER_LENGTH)
+ {
+ status = STATUS_INVALID_PARAMETER;
+
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_TRANSFER,
+ "Transfer length %Iu is too large for controller driver, "
+ "max supported is %d (SPBREQUEST %p, index %lu) - %!STATUS!",
+ descriptor.TransferLength,
+ SI2C_MAX_TRANSFER_LENGTH,
+ pRequest->SpbRequest,
+ i,
+ status);
+
+ goto exit;
+ }
+ }
+
+exit:
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+
+ return status;
+}
+
+VOID
+PbcRequestConfigureForNonSequence(
+ _In_ WDFDEVICE SpbController,
+ _In_ SPBTARGET SpbTarget,
+ _In_ SPBREQUEST SpbRequest,
+ _In_ size_t Length
+ )
+/*++
+
+ Routine Description:
+
+ This is a generic helper routine used to configure
+ the request context and controller hardware for a non-
+ sequence SPB request. It validates parameters and retrieves
+ the transfer buffer as necessary.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+ pTarget - a pointer to the PBC target context
+ pRequest - a pointer to the PBC request context
+ Length - the number of bytes to read from the target
+ Direction - direction of the transfer
+
+ Return Value:
+
+ STATUS
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ PPBC_DEVICE pDevice = GetDeviceContext(SpbController);
+ PPBC_TARGET pTarget = GetTargetContext(SpbTarget);
+ PPBC_REQUEST pRequest = GetRequestContext(SpbRequest);
+ BOOLEAN completeRequest = FALSE;
+
+ NT_ASSERT(pDevice != NULL);
+ NT_ASSERT(pTarget != NULL);
+ NT_ASSERT(pRequest != NULL);
+
+ UNREFERENCED_PARAMETER(Length);
+
+ NTSTATUS status;
+
+ //
+ // Get the request parameters.
+ //
+
+ SPB_REQUEST_PARAMETERS params;
+ SPB_REQUEST_PARAMETERS_INIT(&params);
+ SpbRequestGetParameters(SpbRequest, &params);
+
+ //
+ // Initialize request context.
+ //
+
+ pRequest->SpbRequest = SpbRequest;
+ pRequest->Type = params.Type;
+ pRequest->SequencePosition = params.Position;
+ pRequest->TotalInformation = 0;
+ pRequest->TransferCount = 1;
+ pRequest->TransferIndex = 0;
+ pRequest->bIoComplete = FALSE;
+
+ //
+ // Validate the request before beginning the transfer.
+ //
+
+ status = PbcRequestValidate(pRequest);
+
+ if (!NT_SUCCESS(status))
+ {
+ goto exit;
+ }
+
+ //
+ // Configure the request.
+ //
+
+ status = PbcRequestConfigureForIndex(
+ pRequest,
+ pRequest->TransferIndex);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBDDI,
+ "Error configuring request context for SPBREQUEST %p (SPBTARGET %p)"
+ "- %!STATUS!",
+ pRequest->SpbRequest,
+ SpbTarget,
+ status);
+
+ goto exit;
+ }
+
+ //
+ // Acquire the device lock.
+ //
+
+ WdfSpinLockAcquire(pDevice->Lock);
+
+ //
+ // Mark request cancellable (if cancellation supported).
+ //
+
+ status = WdfRequestMarkCancelableEx(
+ pRequest->SpbRequest, OnCancel);
+
+ if (!NT_SUCCESS(status))
+ {
+ //
+ // WdfRequestMarkCancelableEx should only fail if the request
+ // has already been cancelled. If it does fail the request
+ // must be completed with the corresponding status.
+ //
+
+ NT_ASSERTMSG("WdfRequestMarkCancelableEx should only fail if the request has already been cancelled",
+ status == STATUS_CANCELLED);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Failed to mark SPBREQUEST %p cancellable - %!STATUS!",
+ pRequest->SpbRequest,
+ status);
+
+ WdfSpinLockRelease(pDevice->Lock);
+ goto exit;
+ }
+
+ //
+ // If sequence position is...
+ // - single: ensure there is not a current target
+ // - not single: ensure that the current target is the
+ // same as this target
+ //
+
+ if (params.Position == SpbRequestSequencePositionSingle)
+ {
+ NT_ASSERT(pDevice->pCurrentTarget == NULL);
+ }
+ else
+ {
+ NT_ASSERT(pDevice->pCurrentTarget == pTarget);
+ }
+
+ //
+ // Ensure there is not a current request.
+ //
+
+ NT_ASSERT(pTarget->pCurrentRequest == NULL);
+
+ //
+ // Update the device and target contexts.
+ //
+
+ if (pRequest->SequencePosition == SpbRequestSequencePositionSingle)
+ {
+ pDevice->pCurrentTarget = pTarget;
+ }
+
+ pTarget->pCurrentRequest = pRequest;
+
+ //
+ // Configure controller and kick-off read.
+ // Request will be completed asynchronously.
+ //
+
+ PbcRequestDoTransfer(pDevice, pRequest);
+
+ // TODO: Remove this block. For the purpose of this
+ // skeleton sample, simply complete the request
+ // synchronously. This must be done outside of
+ // the locked code.
+ if (pRequest->bIoComplete)
+ {
+ completeRequest = TRUE;
+ }
+
+ WdfSpinLockRelease(pDevice->Lock);
+
+ // TODO: Remove this block. For the purpose of this
+ // skeleton sample, simply complete the request
+ // synchronously. This must be done outside of
+ // the locked code.
+ if (completeRequest)
+ {
+ PbcRequestComplete(pRequest);
+ }
+
+exit:
+
+ if (!NT_SUCCESS(status))
+ {
+ SpbRequestComplete(SpbRequest, status);
+ }
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+}
+
+NTSTATUS
+PbcRequestConfigureForIndex(
+ _Inout_ PPBC_REQUEST pRequest,
+ _In_ ULONG Index
+ )
+/*++
+
+ Routine Description:
+
+ This is a helper routine used to configure the request
+ context and controller hardware for a transfer within a
+ sequence. It validates parameters and retrieves
+ the transfer buffer as necessary.
+
+ Arguments:
+
+ pRequest - a pointer to the PBC request context
+ Index - index of the transfer within the sequence
+
+ Return Value:
+
+ STATUS
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ NT_ASSERT(pRequest != NULL);
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ //
+ // Get transfer parameters for index.
+ //
+
+ SPB_TRANSFER_DESCRIPTOR descriptor;
+ PMDL pMdl;
+
+ SPB_TRANSFER_DESCRIPTOR_INIT(&descriptor);
+
+ SpbRequestGetTransferParameters(
+ pRequest->SpbRequest,
+ Index,
+ &descriptor,
+ &pMdl);
+
+ NT_ASSERT(pMdl != NULL);
+
+ //
+ // Configure request context.
+ //
+
+ pRequest->pMdlChain = pMdl;
+ pRequest->Length = descriptor.TransferLength;
+ pRequest->Information = 0;
+ pRequest->Direction = descriptor.Direction;
+ pRequest->DelayInUs = descriptor.DelayInUs;
+
+ //
+ // Update sequence position if request is type sequence.
+ //
+
+ if (pRequest->Type == SpbRequestTypeSequence)
+ {
+ if (pRequest->TransferCount == 1)
+ {
+ pRequest->SequencePosition = SpbRequestSequencePositionSingle;
+ }
+ else if (Index == 0)
+ {
+ pRequest->SequencePosition = SpbRequestSequencePositionFirst;
+ }
+ else if (Index == (pRequest->TransferCount - 1))
+ {
+ pRequest->SequencePosition = SpbRequestSequencePositionLast;
+ }
+ else
+ {
+ pRequest->SequencePosition = SpbRequestSequencePositionContinue;
+ }
+ }
+
+ PPBC_TARGET pTarget = GetTargetContext(SpbRequestGetTarget(pRequest->SpbRequest));
+ NT_ASSERT(pTarget != NULL);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Request context configured for %s (index %lu) "
+ "to address 0x%lx (SPBTARGET %p)",
+ pRequest->Direction == SpbTransferDirectionFromDevice ? "read" : "write",
+ Index,
+ pTarget->Settings.Address,
+ pTarget->SpbTarget);
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+
+ return status;
+}
+
+VOID
+PbcRequestDoTransfer(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ PPBC_REQUEST pRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine either starts the delay timer or
+ kicks off the transfer depending on the request
+ parameters.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+ pRequest - a pointer to the PBC request context
+
+ Return Value:
+
+ None. The request is completed asynchronously.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ NT_ASSERT(pDevice != NULL);
+ NT_ASSERT(pRequest != NULL);
+
+ //
+ // Start delay timer if necessary for this request,
+ // otherwise continue transfer.
+ //
+ // NOTE: Note using a timer to implement IO delay is only
+ // applicable for sufficiently long delays (> 15ms).
+ // For shorter delays, especially on the order of
+ // microseconds, consider using a different mechanism.
+ //
+
+ if (pRequest->DelayInUs > 0)
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Delaying %lu us before configuring transfer for WDFDEVICE %p",
+ pRequest->DelayInUs,
+ pDevice->FxDevice);
+
+ BOOLEAN bTimerAlreadyStarted;
+
+ bTimerAlreadyStarted = WdfTimerStart(
+ pDevice->DelayTimer,
+ WDF_REL_TIMEOUT_IN_US(pRequest->DelayInUs));
+
+ //
+ // There should never be another request
+ // scheduled for delay.
+ //
+
+ if (bTimerAlreadyStarted == TRUE)
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_TRANSFER,
+ "The delay timer should not be started");
+ }
+ }
+ else
+ {
+ ControllerConfigureForTransfer(pDevice, pRequest);
+ }
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+}
+
+VOID
+OnDelayTimerExpired(
+ _In_ WDFTIMER Timer
+ )
+/*++
+
+ Routine Description:
+
+ This routine is invoked whenever the driver's delay
+ timer expires. It kicks off the transfer for the request.
+
+ Arguments:
+
+ Timer - a handle to a framework timer object
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ WDFDEVICE fxDevice;
+ PPBC_DEVICE pDevice;
+ PPBC_TARGET pTarget = NULL;
+ PPBC_REQUEST pRequest = NULL;
+ BOOLEAN completeRequest = FALSE;
+
+ fxDevice = (WDFDEVICE) WdfTimerGetParentObject(Timer);
+ pDevice = GetDeviceContext(fxDevice);
+
+ NT_ASSERT(pDevice != NULL);
+
+ //
+ // Acquire the device lock.
+ //
+
+ WdfSpinLockAcquire(pDevice->Lock);
+
+ //
+ // Make sure the target and request are
+ // still valid.
+ //
+
+ pTarget = pDevice->pCurrentTarget;
+
+ if (pTarget == NULL)
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_TRANSFER,
+ "Delay timer expired without a valid current target for WDFDEVICE %p, "
+ "this should only occur if the request was already completed",
+ pDevice->FxDevice);
+
+ goto exit;
+ }
+
+ pRequest = pTarget->pCurrentRequest;
+
+ if (pRequest == NULL)
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_TRANSFER,
+ "Delay timer expired without a valid current request for SPBTARGET %p, "
+ "this should only occur if the request was already cancelled",
+ pTarget->SpbTarget);
+
+ goto exit;
+ }
+
+ NT_ASSERT(pRequest->SpbRequest != NULL);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Delay timer expired, ready to configure transfer for WDFDEVICE %p",
+ pDevice->FxDevice);
+
+ ControllerConfigureForTransfer(pDevice, pRequest);
+
+ // TODO: Remove this block. For the purpose of this
+ // skeleton sample, simply complete the request
+ // synchronously. This must be done outside of
+ // the locked code.
+ if (pRequest->bIoComplete)
+ {
+ completeRequest = TRUE;
+ }
+
+exit:
+
+ //
+ // Release the device lock.
+ //
+
+ WdfSpinLockRelease(pDevice->Lock);
+
+ // TODO: Remove this block. For the purpose of this
+ // skeleton sample, simply complete the request
+ // synchronously. This must be done outside of
+ // the locked code.
+ if (completeRequest)
+ {
+ PbcRequestComplete(pRequest);
+ }
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+}
+
+VOID
+PbcRequestComplete(
+ _In_ PPBC_REQUEST pRequest
+ )
+/*++
+
+ Routine Description:
+
+ This routine completes the SpbRequest associated with
+ the PBC_REQUEST context.
+
+ Arguments:
+
+ pRequest - a pointer to the PBC request context
+
+ Return Value:
+
+ None. The request is completed asynchronously.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_TRANSFER);
+
+ NT_ASSERT(pRequest != NULL);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_TRANSFER,
+ "Completing SPBREQUEST %p with %!STATUS!, transferred %Iu bytes",
+ pRequest->SpbRequest,
+ pRequest->Status,
+ pRequest->TotalInformation);
+
+ WdfRequestSetInformation(
+ pRequest->SpbRequest,
+ pRequest->TotalInformation);
+
+ SpbRequestComplete(
+ pRequest->SpbRequest,
+ pRequest->Status);
+
+ FuncExit(TRACE_FLAG_TRANSFER);
+}
diff --git a/spb/SkeletonI2C/device.h b/spb/SkeletonI2C/device.h
new file mode 100644
index 00000000..b23a4ade
--- /dev/null
+++ b/spb/SkeletonI2C/device.h
@@ -0,0 +1,368 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ device.h
+
+Abstract:
+
+ This module contains the function definitions for the
+ WDF device.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _DEVICE_H_
+#define _DEVICE_H_
+
+//
+// WDF event callbacks.
+//
+
+EVT_WDF_DEVICE_PREPARE_HARDWARE OnPrepareHardware;
+EVT_WDF_DEVICE_RELEASE_HARDWARE OnReleaseHardware;
+EVT_WDF_DEVICE_D0_ENTRY OnD0Entry;
+EVT_WDF_DEVICE_D0_EXIT OnD0Exit;
+EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT OnSelfManagedIoInit;
+EVT_WDF_DEVICE_SELF_MANAGED_IO_CLEANUP OnSelfManagedIoCleanup;
+
+EVT_WDF_INTERRUPT_ISR OnInterruptIsr;
+EVT_WDF_INTERRUPT_DPC OnInterruptDpc;
+
+EVT_WDF_REQUEST_CANCEL OnCancel;
+
+//
+// Power framework event callbacks.
+//
+
+__drv_functionClass(POWER_SETTING_CALLBACK)
+_IRQL_requires_same_
+NTSTATUS
+OnMonitorPowerSettingCallback(
+ _In_ LPCGUID SettingGuid,
+ _In_reads_bytes_(ValueLength) PVOID Value,
+ _In_ ULONG ValueLength,
+ _Inout_opt_ PVOID Context
+ );
+
+//
+// SPBCx event callbacks.
+//
+
+EVT_SPB_TARGET_CONNECT OnTargetConnect;
+EVT_SPB_CONTROLLER_LOCK OnControllerLock;
+EVT_SPB_CONTROLLER_UNLOCK OnControllerUnlock;
+EVT_SPB_CONTROLLER_READ OnRead;
+EVT_SPB_CONTROLLER_WRITE OnWrite;
+EVT_SPB_CONTROLLER_SEQUENCE OnSequence;
+
+EVT_WDF_IO_IN_CALLER_CONTEXT OnOtherInCallerContext;
+EVT_SPB_CONTROLLER_OTHER OnOther;
+
+//
+// PBC function prototypes.
+//
+
+NTSTATUS
+PbcTargetGetSettings(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ PVOID ConnectionParameters,
+ _Out_ PPBC_TARGET_SETTINGS pSettings);
+
+NTSTATUS
+PbcRequestValidate(
+ _In_ PPBC_REQUEST pRequest);
+
+VOID
+PbcRequestConfigureForNonSequence(
+ _In_ WDFDEVICE SpbController,
+ _In_ SPBTARGET SpbTarget,
+ _In_ SPBREQUEST SpbRequest,
+ _In_ size_t Length);
+
+NTSTATUS
+PbcRequestConfigureForIndex(
+ _Inout_ PPBC_REQUEST pRequest,
+ _In_ ULONG Index);
+
+VOID
+PbcRequestDoTransfer(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ PPBC_REQUEST pRequest);
+
+VOID
+PbcRequestComplete(
+ _In_ PPBC_REQUEST pRequest);
+
+EVT_WDF_TIMER OnDelayTimerExpired;
+
+ULONG
+FORCEINLINE
+PbcDeviceGetInterruptMask(
+ _In_ PPBC_DEVICE pDevice
+ )
+/*++
+
+ Routine Description:
+
+ This routine returns the device context's current
+ interrupt mask.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+
+ Return Value:
+
+ Interrupt mask
+
+--*/
+{
+ return (ULONG)InterlockedOr((PLONG)&pDevice->InterruptMask, 0);
+}
+
+VOID
+FORCEINLINE
+PbcDeviceSetInterruptMask(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ ULONG InterruptMask
+ )
+/*++
+
+ Routine Description:
+
+ This routine sets the device context's current
+ interrupt mask.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+ InterruptMask - new interrupt mask value
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ InterlockedExchange(
+ (PLONG)&pDevice->InterruptMask,
+ (LONG)InterruptMask);
+}
+
+VOID
+FORCEINLINE
+PbcDeviceAndInterruptMask(
+ _In_ PPBC_DEVICE pDevice,
+ _In_ ULONG InterruptMask
+ )
+/*++
+
+ Routine Description:
+
+ This routine performs a logical and between the device
+ context's current interrupt mask and the input parameter.
+
+ Arguments:
+
+ pDevice - a pointer to the PBC device context
+ InterruptMask - new interrupt mask value to and
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ InterlockedAnd(
+ (PLONG)&pDevice->InterruptMask,
+ (LONG)InterruptMask);
+}
+
+size_t
+FORCEINLINE
+PbcRequestGetInfoRemaining(
+ _In_ PPBC_REQUEST pRequest
+ )
+/*++
+
+ Routine Description:
+
+ This is a helper routine used to retrieve the
+ number of bytes remaining in the current transfer.
+
+ Arguments:
+
+ pRequest - a pointer to the PBC request context
+
+ Return Value:
+
+ Bytes remaining in request
+
+--*/
+{
+ return (pRequest->Length - pRequest->Information);
+}
+
+NTSTATUS
+FORCEINLINE
+PbcRequestGetByte(
+ _In_ PPBC_REQUEST pRequest,
+ _In_ size_t Index,
+ _Out_ UCHAR* pByte
+ )
+/*++
+
+ Routine Description:
+
+ This is a helper routine used to retrieve the
+ specified byte of the current transfer descriptor buffer.
+
+ Arguments:
+
+ pRequest - a pointer to the PBC request context
+
+ Index - index of desired byte in current transfer descriptor buffer
+
+ pByte - pointer to the location for the specified byte
+
+ Return Value:
+
+ STATUS_INFO_LENGTH_MISMATCH if invalid index,
+ otherwise STATUS_SUCCESS
+
+--*/
+{
+ PMDL mdl = pRequest->pMdlChain;
+ size_t mdlByteCount;
+ size_t currentOffset = Index;
+ PUCHAR pBuffer;
+ NTSTATUS status = STATUS_INFO_LENGTH_MISMATCH;
+
+ //
+ // Check for out-of-bounds index
+ //
+
+ if (Index < pRequest->Length)
+ {
+ while (mdl != NULL)
+ {
+ mdlByteCount = MmGetMdlByteCount(mdl);
+
+ if (currentOffset < mdlByteCount)
+ {
+ pBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe(
+ mdl,
+ NormalPagePriority);
+
+ if (pBuffer != NULL)
+ {
+ //
+ // Byte found, mark successful
+ //
+
+ *pByte = pBuffer[currentOffset];
+ status = STATUS_SUCCESS;
+ }
+
+ break;
+ }
+
+ currentOffset -= mdlByteCount;
+ mdl = mdl->Next;
+ }
+
+ //
+ // If after walking the MDL the byte hasn't been found,
+ // status will still be STATUS_INFO_LENGTH_MISMATCH
+ //
+ }
+
+ return status;
+}
+
+NTSTATUS
+FORCEINLINE
+PbcRequestSetByte(
+ _In_ PPBC_REQUEST pRequest,
+ _In_ size_t Index,
+ _In_ UCHAR Byte
+ )
+/*++
+
+ Routine Description:
+
+ This is a helper routine used to set the
+ specified byte of the current transfer descriptor buffer.
+
+ Arguments:
+
+ pRequest - a pointer to the PBC request context
+
+ Index - index of desired byte in current transfer descriptor buffer
+
+ Byte - the byte
+
+ Return Value:
+
+ STATUS_INFO_LENGTH_MISMATCH if invalid index,
+ otherwise STATUS_SUCCESS
+
+--*/
+{
+ PMDL mdl = pRequest->pMdlChain;
+ size_t mdlByteCount;
+ size_t currentOffset = Index;
+ PUCHAR pBuffer;
+ NTSTATUS status = STATUS_INFO_LENGTH_MISMATCH;
+
+ //
+ // Check for out-of-bounds index
+ //
+
+ if (Index < pRequest->Length)
+ {
+ while (mdl != NULL)
+ {
+ mdlByteCount = MmGetMdlByteCount(mdl);
+
+ if (currentOffset < mdlByteCount)
+ {
+ pBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe(
+ mdl,
+ NormalPagePriority);
+
+ if (pBuffer != NULL)
+ {
+ //
+ // Byte found, mark successful
+ //
+
+ pBuffer[currentOffset] = Byte;
+ status = STATUS_SUCCESS;
+ }
+
+ break;
+ }
+
+ currentOffset -= mdlByteCount;
+ mdl = mdl->Next;
+ }
+
+ //
+ // If after walking the MDL the byte hasn't been found,
+ // status will still be STATUS_INFO_LENGTH_MISMATCH
+ //
+ }
+
+ return status;
+}
+
+#endif
diff --git a/spb/SkeletonI2C/driver.cpp b/spb/SkeletonI2C/driver.cpp
new file mode 100644
index 00000000..e6c58771
--- /dev/null
+++ b/spb/SkeletonI2C/driver.cpp
@@ -0,0 +1,454 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ driver.cpp
+
+Abstract:
+
+ This module contains the WDF driver initialization
+ functions for the controller driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+#include "driver.h"
+#include "device.h"
+#include "ntstrsafe.h"
+
+#include "driver.tmh"
+
+NTSTATUS
+#pragma prefast(suppress:__WARNING_DRIVER_FUNCTION_TYPE, "thanks, i know this already")
+DriverEntry(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_ PUNICODE_STRING RegistryPath
+ )
+{
+ WDF_DRIVER_CONFIG driverConfig;
+ WDF_OBJECT_ATTRIBUTES driverAttributes;
+
+ WDFDRIVER fxDriver;
+
+ NTSTATUS status;
+
+ WPP_INIT_TRACING(DriverObject, RegistryPath);
+
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ WDF_DRIVER_CONFIG_INIT(&driverConfig, OnDeviceAdd);
+ driverConfig.DriverPoolTag = SI2C_POOL_TAG;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&driverAttributes);
+ driverAttributes.EvtCleanupCallback = OnDriverCleanup;
+
+ status = WdfDriverCreate(
+ DriverObject,
+ RegistryPath,
+ &driverAttributes,
+ &driverConfig,
+ &fxDriver);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error creating WDF driver object - %!STATUS!",
+ status);
+
+ goto exit;
+ }
+
+ Trace(
+ TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_WDFLOADING,
+ "Created WDFDRIVER %p",
+ fxDriver);
+
+exit:
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+VOID
+OnDriverCleanup(
+ _In_ WDFOBJECT Object
+ )
+{
+ UNREFERENCED_PARAMETER(Object);
+
+ WPP_CLEANUP(NULL);
+}
+
+NTSTATUS
+OnDeviceAdd(
+ _In_ WDFDRIVER FxDriver,
+ _Inout_ PWDFDEVICE_INIT FxDeviceInit
+ )
+/*++
+
+ Routine Description:
+
+ This routine creates the device object for an SPB
+ controller and the device's child objects.
+
+ Arguments:
+
+ FxDriver - the WDF driver object handle
+ FxDeviceInit - information about the PDO that we are loading on
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ PPBC_DEVICE pDevice;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(FxDriver);
+
+ //
+ // Configure DeviceInit structure
+ //
+
+ status = SpbDeviceInitConfig(FxDeviceInit);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed SpbDeviceInitConfig() for WDFDEVICE_INIT %p - %!STATUS!",
+ FxDeviceInit,
+ status);
+
+ goto exit;
+ }
+
+ //
+ // Setup PNP/Power callbacks.
+ //
+
+ {
+ WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks;
+ WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks);
+
+ pnpCallbacks.EvtDevicePrepareHardware = OnPrepareHardware;
+ pnpCallbacks.EvtDeviceReleaseHardware = OnReleaseHardware;
+ pnpCallbacks.EvtDeviceD0Entry = OnD0Entry;
+ pnpCallbacks.EvtDeviceD0Exit = OnD0Exit;
+ pnpCallbacks.EvtDeviceSelfManagedIoInit = OnSelfManagedIoInit;
+ pnpCallbacks.EvtDeviceSelfManagedIoCleanup = OnSelfManagedIoCleanup;
+
+ WdfDeviceInitSetPnpPowerEventCallbacks(FxDeviceInit, &pnpCallbacks);
+ }
+
+ //
+ // Note: The SPB class extension sets a default
+ // security descriptor to allow access to
+ // user-mode drivers. This can be overridden
+ // by calling WdfDeviceInitAssignSDDLString()
+ // with the desired setting. This must be done
+ // after calling SpbDeviceInitConfig() but
+ // before WdfDeviceCreate().
+ //
+
+
+ //
+ // Create the device.
+ //
+
+ {
+ WDF_OBJECT_ATTRIBUTES deviceAttributes;
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, PBC_DEVICE);
+ WDFDEVICE fxDevice;
+
+ status = WdfDeviceCreate(
+ &FxDeviceInit,
+ &deviceAttributes,
+ &fxDevice);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed to create WDFDEVICE from WDFDEVICE_INIT %p - %!STATUS!",
+ FxDeviceInit,
+ status);
+
+ goto exit;
+ }
+
+ pDevice = GetDeviceContext(fxDevice);
+ NT_ASSERT(pDevice != NULL);
+
+ pDevice->FxDevice = fxDevice;
+ }
+
+ //
+ // Ensure device is disable-able
+ //
+
+ {
+ WDF_DEVICE_STATE deviceState;
+ WDF_DEVICE_STATE_INIT(&deviceState);
+
+ deviceState.NotDisableable = WdfFalse;
+ WdfDeviceSetDeviceState(pDevice->FxDevice, &deviceState);
+ }
+
+ //
+ // Bind a SPB controller object to the device.
+ //
+
+ {
+ SPB_CONTROLLER_CONFIG spbConfig;
+ SPB_CONTROLLER_CONFIG_INIT(&spbConfig);
+
+ //
+ // Register for target connect callback. The driver
+ // does not need to respond to target disconnect.
+ //
+
+ spbConfig.EvtSpbTargetConnect = OnTargetConnect;
+
+ //
+ // Register for IO callbacks.
+ //
+
+ spbConfig.ControllerDispatchType = WdfIoQueueDispatchSequential;
+ spbConfig.PowerManaged = WdfTrue;
+ spbConfig.EvtSpbIoRead = OnRead;
+ spbConfig.EvtSpbIoWrite = OnWrite;
+ spbConfig.EvtSpbIoSequence = OnSequence;
+ spbConfig.EvtSpbControllerLock = OnControllerLock;
+ spbConfig.EvtSpbControllerUnlock = OnControllerUnlock;
+
+ status = SpbDeviceInitialize(pDevice->FxDevice, &spbConfig);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed SpbDeviceInitialize() for WDFDEVICE %p - %!STATUS!",
+ pDevice->FxDevice,
+ status);
+
+ goto exit;
+ }
+
+ //
+ // Register for IO other callbacks.
+ //
+
+ SpbControllerSetIoOtherCallback(
+ pDevice->FxDevice,
+ OnOther,
+ OnOtherInCallerContext);
+ }
+
+ //
+ // Set target object attributes.
+ //
+
+ {
+ WDF_OBJECT_ATTRIBUTES targetAttributes;
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&targetAttributes, PBC_TARGET);
+
+ SpbControllerSetTargetAttributes(pDevice->FxDevice, &targetAttributes);
+ }
+
+ //
+ // Set request object attributes.
+ //
+
+ {
+ WDF_OBJECT_ATTRIBUTES requestAttributes;
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&requestAttributes, PBC_REQUEST);
+
+ //
+ // NOTE: Be mindful when registering for EvtCleanupCallback or
+ // EvtDestroyCallback. IO requests arriving in the class
+ // extension, but not presented to the driver (due to
+ // cancellation), will still have their cleanup and destroy
+ // callbacks invoked.
+ //
+
+ SpbControllerSetRequestAttributes(pDevice->FxDevice, &requestAttributes);
+ }
+
+ //
+ // Create an interrupt object, interrupt spinlock,
+ // and register callbacks.
+ //
+
+ {
+ //
+ // Create the interrupt spinlock.
+ //
+
+ WDF_OBJECT_ATTRIBUTES attributes;
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.ParentObject = pDevice->FxDevice;
+
+ WDFSPINLOCK interruptLock;
+
+ status = WdfSpinLockCreate(
+ &attributes,
+ &interruptLock);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed to create interrupt spinlock for WDFDEVICE %p - %!STATUS!",
+ pDevice->FxDevice,
+ status);
+
+ goto exit;
+ }
+
+ //
+ // Create the interrupt object.
+ //
+
+ WDF_INTERRUPT_CONFIG interruptConfig;
+
+ WDF_INTERRUPT_CONFIG_INIT(
+ &interruptConfig,
+ OnInterruptIsr,
+ OnInterruptDpc);
+
+ interruptConfig.SpinLock = interruptLock;
+
+ status = WdfInterruptCreate(
+ pDevice->FxDevice,
+ &interruptConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &pDevice->InterruptObject);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed to create interrupt object for WDFDEVICE %p - %!STATUS!",
+ pDevice->FxDevice,
+ status);
+
+ goto exit;
+ }
+ }
+
+ //
+ // Create the delay timer to stall between transfers.
+ //
+ {
+ WDF_TIMER_CONFIG wdfTimerConfig;
+ WDF_OBJECT_ATTRIBUTES timerAttributes;
+
+ WDF_TIMER_CONFIG_INIT(&wdfTimerConfig, OnDelayTimerExpired);
+ WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
+ timerAttributes.ParentObject = pDevice->FxDevice;
+
+ status = WdfTimerCreate(
+ &wdfTimerConfig,
+ &timerAttributes,
+ &(pDevice->DelayTimer)
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed to create delay timer for WDFDEVICE %p - %!STATUS!",
+ pDevice->FxDevice,
+ status);
+
+ goto exit;
+ }
+ }
+
+ //
+ // Create the spin lock to synchronize access
+ // to the controller driver.
+ //
+
+ WDF_OBJECT_ATTRIBUTES attributes;
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.ParentObject = pDevice->FxDevice;
+
+ status = WdfSpinLockCreate(
+ &attributes,
+ &pDevice->Lock);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed to create device spinlock for WDFDEVICE %p - %!STATUS!",
+ pDevice->FxDevice,
+ status);
+
+ goto exit;
+ }
+
+ //
+ // Configure idle settings to use system
+ // managed idle timeout.
+ //
+ {
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(
+ &idleSettings,
+ IdleCannotWakeFromS0);
+
+ //
+ // Explicitly set initial idle timeout delay.
+ //
+
+ idleSettings.IdleTimeoutType = SystemManagedIdleTimeoutWithHint;
+ idleSettings.IdleTimeout = IDLE_TIMEOUT_MONITOR_ON;
+
+ status = WdfDeviceAssignS0IdleSettings(
+ pDevice->FxDevice,
+ &idleSettings);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed to initalize S0 idle settings for WDFDEVICE %p- %!STATUS!",
+ pDevice->FxDevice,
+ status);
+
+ goto exit;
+ }
+ }
+
+exit:
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
diff --git a/spb/SkeletonI2C/driver.h b/spb/SkeletonI2C/driver.h
new file mode 100644
index 00000000..1029377c
--- /dev/null
+++ b/spb/SkeletonI2C/driver.h
@@ -0,0 +1,36 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ driver.h
+
+Abstract:
+
+ This module contains the function definitions for
+ the WDF driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _DRIVER_H_
+#define _DRIVER_H_
+
+extern "C"
+
+NTSTATUS
+DriverEntry(
+ _In_ PDRIVER_OBJECT pDriverObject,
+ _In_ PUNICODE_STRING pRegistryPath
+ );
+
+EVT_WDF_DRIVER_DEVICE_ADD OnDeviceAdd;
+EVT_WDF_OBJECT_CONTEXT_CLEANUP OnDriverCleanup;
+
+#endif
diff --git a/spb/SkeletonI2C/hw.cpp b/spb/SkeletonI2C/hw.cpp
new file mode 100644
index 00000000..497cfe5a
--- /dev/null
+++ b/spb/SkeletonI2C/hw.cpp
@@ -0,0 +1,83 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ hw.cpp
+
+Abstract:
+
+ This module contains the functions for accessing
+ the hardware registers.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+#include "hw.tmh"
+
+ULONG
+HWREG<ULONG>::Read(
+ VOID
+ )
+{
+ volatile ULONG *addr = &m_Value;
+ ULONG v = READ_REGISTER_ULONG((PULONG)addr);
+ return v;
+}
+
+ULONG
+HWREG<ULONG>::Write(
+ _In_ ULONG Value
+ )
+{
+ volatile ULONG *addr = &m_Value;
+ WRITE_REGISTER_ULONG((PULONG)addr, Value);
+ return Value;
+}
+
+USHORT
+HWREG<USHORT>::Read(
+ VOID
+ )
+{
+ volatile USHORT *addr = &m_Value;
+ USHORT v = READ_REGISTER_USHORT((PUSHORT)addr);
+ return v;
+}
+
+USHORT
+HWREG<USHORT>::Write(
+ _In_ USHORT Value
+ )
+{
+ volatile USHORT *addr = &m_Value;
+ WRITE_REGISTER_USHORT((PUSHORT)addr, Value);
+ return Value;
+}
+
+UCHAR
+HWREG<UCHAR>::Read(
+ VOID
+ )
+{
+ volatile UCHAR *addr = &m_Value;
+ UCHAR v = READ_REGISTER_UCHAR((PUCHAR)addr);
+ return v;
+}
+
+UCHAR
+HWREG<UCHAR>::Write(
+ _In_ UCHAR Value
+ )
+{
+ volatile UCHAR *addr = &m_Value;
+ WRITE_REGISTER_UCHAR((PUCHAR)addr, Value);
+ return Value;
+}
diff --git a/spb/SkeletonI2C/hw.h b/spb/SkeletonI2C/hw.h
new file mode 100644
index 00000000..1290c8ef
--- /dev/null
+++ b/spb/SkeletonI2C/hw.h
@@ -0,0 +1,88 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ hw.h
+
+Abstract:
+
+ This module contains the function definitions for
+ the hardware registers.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _HW_H_
+#define _HW_H_
+
+template<typename T> struct HWREG
+{
+private:
+
+ //
+ // Only one data member - this has to fit in the same space as the underlying type.
+ //
+
+ T m_Value;
+
+public:
+
+ T Read(void);
+ T Write(_In_ T value);
+
+ VOID
+ ReadBuffer(
+ _In_ ULONG BufferCe,
+ _Out_writes_(BufferCe) T Buffer[]
+ )
+ {
+ for(ULONG i = 0; i < BufferCe; i++)
+ {
+ Buffer[i] = Read();
+ }
+ }
+
+ VOID
+ WriteBuffer(
+ _In_ ULONG BufferCe,
+ _Out_writes_(BufferCe) T Buffer[]
+ )
+ {
+ for(ULONG i = 0; i < BufferCe; i++)
+ {
+ Write(Buffer[i]);
+ }
+ }
+
+ //
+ // Operators with standard meanings.
+ //
+
+ T operator= (_In_ T value) {return Write(value);}
+ T operator|=(_In_ T value) {return Write(Read() | value);}
+ T operator&=(_In_ T value) {return Write(value | Read());}
+ operator T() {return Read();}
+
+ //
+ // Override the meaning of exclusive OR to mean clear
+ //
+ // Added this because x &= ~foo requires a cast of ~foo from signed int
+ // back to the underlying (typically unsigned) type. I would prefer x ~= foo
+ // but that's not a real C++ operator.
+ //
+
+ T operator^=(_In_ T value) {return Write(((T) ~value) & Read());}
+
+ T SetBits (_In_ T Flags) {return (*this |= Flags);}
+ T ClearBits(_In_ T Flags) {return (*this &= ~Flags);}
+ bool TestBits (_In_ T Flags) {return ((Read() & Flags) != 0)};
+};
+
+#endif
diff --git a/spb/SkeletonI2C/i2ctrace.h b/spb/SkeletonI2C/i2ctrace.h
new file mode 100644
index 00000000..85017628
--- /dev/null
+++ b/spb/SkeletonI2C/i2ctrace.h
@@ -0,0 +1,57 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ i2ctrace.h
+
+Abstract:
+
+ This module contains the trace definitions for the PBC
+ controller driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _I2CTRACE_H_
+#define _I2CTRACE_H_
+
+extern "C"
+{
+//
+// Tracing Definitions:
+//
+// TODO: Define a unique tracing guid.
+//
+// Control GUID:
+// {3AD0F092-64C8-4e69-B93D-7FB64933FFDD}
+
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID( \
+ PbcTraceGuid, \
+ (3AD0F092,64C8,4e69,B93D,7FB64933FFDD), \
+ WPP_DEFINE_BIT(TRACE_FLAG_WDFLOADING) \
+ WPP_DEFINE_BIT(TRACE_FLAG_SPBDDI) \
+ WPP_DEFINE_BIT(TRACE_FLAG_PBCLOADING) \
+ WPP_DEFINE_BIT(TRACE_FLAG_TRANSFER) \
+ WPP_DEFINE_BIT(TRACE_FLAG_OTHER) \
+ )
+}
+
+#define WPP_LEVEL_FLAGS_LOGGER(level,flags) WPP_LEVEL_LOGGER(flags)
+#define WPP_LEVEL_FLAGS_ENABLED(level, flags) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= level)
+
+// begin_wpp config
+// FUNC FuncEntry{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS);
+// FUNC FuncExit{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS);
+// USEPREFIX(FuncEntry, "%!STDPREFIX! [%!FUNC!] --> entry");
+// USEPREFIX(FuncExit, "%!STDPREFIX! [%!FUNC!] <--");
+// end_wpp
+
+#endif // _I2CTRACE_H_
diff --git a/spb/SkeletonI2C/internal.h b/spb/SkeletonI2C/internal.h
new file mode 100644
index 00000000..3d102ba4
--- /dev/null
+++ b/spb/SkeletonI2C/internal.h
@@ -0,0 +1,293 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ internal.h
+
+Abstract:
+
+ This module contains the common internal type and function
+ definitions for the SPB controller driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _INTERNAL_H_
+#define _INTERNAL_H_
+
+#pragma warning(push)
+#pragma warning(disable:4512)
+#pragma warning(disable:4480)
+
+#define SI2C_POOL_TAG ((ULONG) 'C2IS')
+
+/////////////////////////////////////////////////
+//
+// Common includes.
+//
+/////////////////////////////////////////////////
+
+#include <initguid.h>
+#include <ntddk.h>
+#include <wdm.h>
+#include <wdf.h>
+#include <ntstrsafe.h>
+
+#include "SPBCx.h"
+#include "i2ctrace.h"
+
+
+/////////////////////////////////////////////////
+//
+// Hardware definitions.
+//
+/////////////////////////////////////////////////
+
+#include "skeletoni2c.h"
+
+/////////////////////////////////////////////////
+//
+// Resource and descriptor definitions.
+//
+/////////////////////////////////////////////////
+
+#include "reshub.h"
+
+//
+// I2C Serial peripheral bus descriptor
+//
+
+#include "pshpack1.h"
+
+typedef struct _PNP_I2C_SERIAL_BUS_DESCRIPTOR {
+ PNP_SERIAL_BUS_DESCRIPTOR SerialBusDescriptor;
+ ULONG ConnectionSpeed;
+ USHORT SlaveAddress;
+ // follwed by optional Vendor Data
+ // followed by PNP_IO_DESCRIPTOR_RESOURCE_NAME
+} PNP_I2C_SERIAL_BUS_DESCRIPTOR, *PPNP_I2C_SERIAL_BUS_DESCRIPTOR;
+
+#include "poppack.h"
+
+#define I2C_SERIAL_BUS_TYPE 0x01
+#define I2C_SERIAL_BUS_SPECIFIC_FLAG_10BIT_ADDRESS 0x0001
+
+/////////////////////////////////////////////////
+//
+// Settings.
+//
+/////////////////////////////////////////////////
+
+//
+// Power settings.
+//
+
+#define MONITOR_POWER_ON 1
+#define MONITOR_POWER_OFF 0
+
+#define IDLE_TIMEOUT_MONITOR_ON 2000
+#define IDLE_TIMEOUT_MONITOR_OFF 50
+
+//
+// Target settings.
+//
+
+typedef enum ADDRESS_MODE
+{
+ AddressMode7Bit,
+ AddressMode10Bit
+}
+ADDRESS_MODE, *PADDRESS_MODE;
+
+typedef struct PBC_TARGET_SETTINGS
+{
+ // TODO: Update this structure to include other
+ // target settings needed to configure the
+ // controller (i.e. connection speed, phase/
+ // polarity for SPI).
+
+ ADDRESS_MODE AddressMode;
+ USHORT Address;
+ ULONG ConnectionSpeed;
+}
+PBC_TARGET_SETTINGS, *PPBC_TARGET_SETTINGS;
+
+
+//
+// Transfer settings.
+//
+
+typedef enum BUS_CONDITION
+{
+ BusConditionFree,
+ BusConditionBusy,
+ BusConditionDontCare
+}
+BUS_CONDITION, *PBUS_CONDITION;
+
+typedef struct PBC_TRANSFER_SETTINGS
+{
+ // TODO: Update this structure to include other
+ // settings needed to configure the controller
+ // for a specific transfer.
+
+ BUS_CONDITION BusCondition;
+ BOOLEAN IsStart;
+ BOOLEAN IsEnd;
+}
+PBC_TRANSFER_SETTINGS, *PPBC_TRANSFER_SETTINGS;
+
+/////////////////////////////////////////////////
+//
+// Context definitions.
+//
+/////////////////////////////////////////////////
+
+typedef struct PBC_DEVICE PBC_DEVICE, *PPBC_DEVICE;
+typedef struct PBC_TARGET PBC_TARGET, *PPBC_TARGET;
+typedef struct PBC_REQUEST PBC_REQUEST, *PPBC_REQUEST;
+
+//
+// Device context.
+//
+
+struct PBC_DEVICE
+{
+ // TODO: Update this structure with variables that
+ // need to be stored in the device context.
+
+ // Handle to the WDF device.
+ WDFDEVICE FxDevice;
+
+ // Structure mapped to the controller's
+ // register interface.
+ PSKELETONI2C_REGISTERS pRegisters;
+ ULONG RegistersCb;
+ PHYSICAL_ADDRESS pRegistersPhysicalAddress;
+
+ // Target that the controller is currently
+ // configured for. In most cases this value is only
+ // set when there is a request being handled, however,
+ // it will persist between lock and unlock requests.
+ // There cannot be more than one current target.
+ PPBC_TARGET pCurrentTarget;
+
+ // Variables to track enabled interrupts
+ // and status between ISR and DPC.
+ WDFINTERRUPT InterruptObject;
+ ULONG InterruptMask;
+ ULONG InterruptStatus;
+
+ // Controller driver spinlock.
+ WDFSPINLOCK Lock;
+
+ // Delay timer used to stall between transfers.
+ WDFTIMER DelayTimer;
+
+ // The power setting callback handle
+ PVOID pMonitorPowerSettingHandle;
+};
+
+//
+// Target context.
+//
+
+struct PBC_TARGET
+{
+ // TODO: Update this structure with variables that
+ // need to be stored in the target context.
+
+ // Handle to the SPB target.
+ SPBTARGET SpbTarget;
+
+ // Target specific settings.
+ PBC_TARGET_SETTINGS Settings;
+
+ // Current request associated with the
+ // target. This value should only be non-null
+ // when this target is the controller's current
+ // target.
+ PPBC_REQUEST pCurrentRequest;
+};
+
+//
+// Request context.
+//
+
+struct PBC_REQUEST
+{
+ // TODO: Update this structure with variables that
+ // need to be stored in the request context.
+
+ //
+ // Variables that persist for the lifetime of
+ // the request. Specifically these apply to an
+ // entire sequence request (not just a single transfer).
+ //
+
+ // Handle to the SPB request.
+ SPBREQUEST SpbRequest;
+
+ // SPB request type.
+ SPB_REQUEST_TYPE Type;
+
+ // Number of transfers in sequence and
+ // index of the current one.
+ ULONG TransferCount;
+ ULONG TransferIndex;
+
+ // Total bytes transferred.
+ size_t TotalInformation;
+
+ // Current status of the request.
+ NTSTATUS Status;
+ BOOLEAN bIoComplete;
+
+
+ //
+ // Variables that are reused for each transfer within
+ // a [sequence] request.
+ //
+
+ // Pointer to the transfer buffer and length.
+ size_t Length;
+ PMDL pMdlChain;
+
+ // Position of the current transfer within
+ // the sequence and its associated controller
+ // settings.
+ SPB_REQUEST_SEQUENCE_POSITION SequencePosition;
+ PBC_TRANSFER_SETTINGS Settings;
+
+ // Direction of the current transfer.
+ SPB_TRANSFER_DIRECTION Direction;
+
+ // Time to delay before starting transfer.
+ ULONG DelayInUs;
+
+ // Interrupt flag indicating data is ready to
+ // be transferred.
+ ULONG DataReadyFlag;
+
+ // Bytes read/written in the current transfer.
+ size_t Information;
+};
+
+//
+// Declate contexts for device, target, and request.
+//
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PBC_DEVICE, GetDeviceContext);
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PBC_TARGET, GetTargetContext);
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PBC_REQUEST, GetRequestContext);
+
+#pragma warning(pop)
+
+#endif // _INTERNAL_H_
diff --git a/spb/SkeletonI2C/resource.rc b/spb/SkeletonI2C/resource.rc
new file mode 100644
index 00000000..dd33e63a
--- /dev/null
+++ b/spb/SkeletonI2C/resource.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR "Skeleton I2C Controller Driver"
+#define VER_INTERNALNAME_STR "skeletoni2c.sys"
+#define VER_ORIGINALFILENAME_STR "skeletoni2c.sys"
+
+#include "common.ver"
diff --git a/spb/SkeletonI2C/skeletoni2c.asl b/spb/SkeletonI2C/skeletoni2c.asl
new file mode 100644
index 00000000..2d3c3a8b
--- /dev/null
+++ b/spb/SkeletonI2C/skeletoni2c.asl
@@ -0,0 +1,14 @@
+//
+// Test controller device node.
+//
+// For a peripheral driver to access this controller
+// via SPB it must specify the ACPI device path within
+// the I2CSerialBus (or SPISerialBus) macro. Depending
+// on the scope this looks something like \_SB.I2C. See
+// spbtesttool.asl for an example.
+//
+Device(I2C)
+{
+ Name(_HID, "skeletoni2c")
+ Name(_UID, 1)
+} \ No newline at end of file
diff --git a/spb/SkeletonI2C/skeletoni2c.h b/spb/SkeletonI2C/skeletoni2c.h
new file mode 100644
index 00000000..d52f1fd2
--- /dev/null
+++ b/spb/SkeletonI2C/skeletoni2c.h
@@ -0,0 +1,96 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ skeletoni2c.h
+
+Abstract:
+
+ This module contains the controller-specific type
+ definitions for the SPB controller driver hardware.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+//
+// Includes for hardware register definitions.
+//
+
+#ifndef _SKELETONI2C_H_
+#define _SKELETONI2C_H_
+
+#include "hw.h"
+
+//
+// Skeleton I2C controller registers.
+//
+
+typedef struct SKELETONI2C_REGISTERS
+{
+ // TODO: Update this register structure to match the
+ // register mapping of the controller hardware.
+
+ __declspec(align(4)) HWREG<ULONG> Reg0;
+ __declspec(align(4)) HWREG<ULONG> Reg1;
+}
+SKELETONI2C_REGISTERS, *PSKELETONI2C_REGISTERS;
+
+// TODO: Update the following defines to match the bit
+// functionalities of each register.
+
+//
+// Reg0 register bits.
+//
+
+#define SI2C_REG_0_BITS_31_28 0xF0000000
+
+//
+// Reg1 register bits.
+//
+
+#define SI2C_REG_1_BITS_31_28 0xF0000000
+
+// TODO: Define other controller-specific values.
+
+#define SI2C_MAX_TRANSFER_LENGTH 0x00001000
+
+// TODO: Remove these generic error defines in favor
+// of real register bit mappings defined above.
+
+#define SI2C_STATUS_ADDRESS_NACK 0x00000000
+#define SI2C_STATUS_DATA_NACK 0x00000000
+#define SI2C_STATUS_GENERIC_ERROR 0x00000000
+
+
+//
+// Register evaluation functions.
+//
+
+FORCEINLINE
+bool
+TestAnyBits(
+ _In_ ULONG V1,
+ _In_ ULONG V2
+ )
+{
+ return (V1 & V2) != 0;
+}
+
+FORCEINLINE
+bool
+TestAllBits(
+ _In_ ULONG V1,
+ _In_ ULONG V2
+ )
+{
+ return ((V1 & V2) == V2);
+}
+
+#endif
diff --git a/spb/SkeletonI2C/skeletoni2c.inx b/spb/SkeletonI2C/skeletoni2c.inx
new file mode 100644
index 00000000..6808f6ab
--- /dev/null
+++ b/spb/SkeletonI2C/skeletoni2c.inx
Binary files differ
diff --git a/spb/SkeletonI2C/skeletoni2c.vcxproj b/spb/SkeletonI2C/skeletoni2c.vcxproj
new file mode 100644
index 00000000..e8f46d2d
--- /dev/null
+++ b/spb/SkeletonI2C/skeletoni2c.vcxproj
@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{B890FE48-579C-41BF-9D89-064D4C8291D3}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{B140B15C-7C0A-4E6F-B705-355E8491EFD1}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems">
+ <ClCompile Include="driver.cpp; device.cpp; controller.cpp; hw.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppScanConfigurationData>i2ctrace.h</WppScanConfigurationData>
+ <WppTraceFunction>Trace(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ </ClCompile>
+ <Inf Include=".\skeletoni2c.inx">
+ <Architecture>$(InfArch)</Architecture>
+ <SpecifyArchitecture>true</SpecifyArchitecture>
+ <CopyOutput>.\$(IntDir)\skeletoni2c.inf</CopyOutput>
+ </Inf>
+ <OtherWpp Include="resource.rc">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppScanConfigurationData>i2ctrace.h</WppScanConfigurationData>
+ <WppTraceFunction>Trace(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ </OtherWpp>
+ </ItemGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>skeletoni2c</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>skeletoni2c</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>skeletoni2c</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>skeletoni2c</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SPB_LIB_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)\SpbCxStubs.lib;$(DDK_LIB_PATH)\ntstrsafe.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SPB_LIB_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)\SpbCxStubs.lib;$(DDK_LIB_PATH)\ntstrsafe.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SPB_LIB_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)\SpbCxStubs.lib;$(DDK_LIB_PATH)\ntstrsafe.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SPB_LIB_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)\SpbCxStubs.lib;$(DDK_LIB_PATH)\ntstrsafe.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/spb/SkeletonI2C/skeletoni2c.vcxproj.Filters b/spb/SkeletonI2C/skeletoni2c.vcxproj.Filters
new file mode 100644
index 00000000..cadb17b1
--- /dev/null
+++ b/spb/SkeletonI2C/skeletoni2c.vcxproj.Filters
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{37426773-F628-425E-981B-E4265FDB33F9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{C7EFBD4E-0814-4137-BC2F-E62AEC040D23}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{C68A1A41-6F14-4755-9338-C517FFC56AC6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{9455FD0C-B897-4788-A02B-5CE9F02EEE04}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="controller.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="device.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="driver.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="hw.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <FilesToPackage Include=".\Debug\\skeletoni2c.inf">
+ <Filter>Driver Files</Filter>
+ </FilesToPackage>
+ <Inf Include=".\skeletoni2c.inx">
+ <Filter>Driver Files</Filter>
+ </Inf>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/spb/SpbTestTool/ReadMe.md b/spb/SpbTestTool/ReadMe.md
new file mode 100644
index 00000000..21a7bf49
--- /dev/null
+++ b/spb/SpbTestTool/ReadMe.md
@@ -0,0 +1,203 @@
+SpbTestTool
+===========
+
+The SpbTestTool sample serves two purposes. First, it demonstrates how to open a handle to the [SPB controller](http://msdn.microsoft.com/en-us/library/windows/hardware/hh698220), use the SPB interface from a KMDF driver, and employ GPIO [passive-level interrupts](http://msdn.microsoft.com/en-us/library/windows/hardware/hh451035). Second, it implements a set of commands for communicating with a peripheral device to aid in debugging.
+
+This sample is incomplete as a driver and merely demonstrates use of the [SPB I/O request interface](http://msdn.microsoft.com/en-us/library/windows/hardware/hh698224) and [GPIO interrupts](http://msdn.microsoft.com/en-us/library/windows/hardware/hh406467). It is not intended for use in a production environment.
+
+Run the sample
+--------------
+
+To install the SpbTestTool peripheral driver, follow these steps:
+
+1. Ensure that the driver builds without errors.
+
+2. Copy the SYS and INF files to a separate folder.
+
+3. Run Devcon.exe. You can find this program in the tools\\devcon folder where you installed the WDK. Type the following command in the command window:
+
+ `devcon.exe update SpbTestTool.inf ACPI\<hwid>`
+
+To launch the SpbTestTool application, follow these steps:
+
+1. Navigate to the directory that contains SpbTestTool.exe.
+
+2. Type the following command in the command window:
+
+ `SpbTestTool.exe`
+
+3. By default, the SpbTestTool application uses the SpbTestTool sample driver. However, an alternate peripheral driver can be used instead. To specify an alternate driver, use the following format for the command line:
+
+ `SpbTestTool.exe /p \\.\<alternate_path>`
+
+4. An input script can used instead of an interactive prompt. The script format requires one command per line. To run the script, use the following format for the command line in the command window:
+
+ `SpbTestTool.exe /i <script.txt>`
+
+Executing commands
+------------------
+
+The SpbTestTool application loops indefinitely waiting for one of the following commands. The commands are translated to the appropriate SPB I/O request without any state tracking in the driver. Transfer status, buffer contents, and error codes are returned as necessary. Type `help` at any time to display this command list. Press Ctrl-C at any time to cancel the current command and exit the application.
+
+Command
+
+Description
+
+*open*
+
+Open handle to SPB controller.
+
+*close*
+
+Close handle to SPB controller.
+
+*lock*
+
+Lock the bus for exclusive access.
+
+*unlock*
+
+Unlock the bus.
+
+*lockconn*
+
+Lock the shared connection for exclusive access. This command is used to synchronize bus transfers by the sample driver with op-region accesses by the ACPI firmware.
+
+*unlockconn*
+
+Unlock the shared connection.
+
+*write {}*
+
+Write a byte array to the peripheral device.
+
+Example: `> write {01, 02, 03}`
+
+*read \<numBytes\>*
+
+Read \<numBytes\> from the peripheral device.
+
+Example: `> read 5`
+
+*writeread {} \<numBytes\>*
+
+Atomically write a byte array to the peripheral device and read \<numBytes\> back.
+
+Example: `> writeread {01, 02, 03} 5`
+
+*signal*
+
+Inform the SpbTestTool driver that the interrupt has been handled.
+
+*help*
+
+Display the list of supported commands.
+
+*Ctrl-C*
+
+Press Ctrl-C at any time to cancel the outstanding command and exit the application.
+
+Code tour
+---------
+
+The following are the relevant functions in the SpbTestTool peripheral driver for using the SPB interface from a KMDF driver.
+
+<table>
+<colgroup>
+<col width="50%" />
+<col width="50%" />
+</colgroup>
+<thead>
+<tr class="header">
+<th align="left">Function
+Description</th>
+</tr>
+</thead>
+<tbody>
+<tr class="odd">
+<td align="left"><p><code>OnPrepareHardware</code></p>
+<p>Traverses the driver’s start resources and caches the connection ID of the I2C or SPI resource. This ID will be used to open the SPB controller later on.</p></td>
+<td align="left"><p><code>SpbPeripheralOpen</code></p>
+<p>Opens a handle to the underlying SPB controller via the resource hub. This allows the peripheral driver to be developed without any underlying knowledge of the platform or hardware connections. Instead, the dependency between controller and peripheral is described in ACPI.</p></td>
+</tr>
+</tbody>
+</table>
+
+The following are the relevant functions in the SpbTestTool peripheral driver for managing GPIO passive-level interrupts from a KMDF driver.
+
+Function
+
+Description
+
+`OnPrepareHardware`
+
+Traverses the driver’s start resources. If "ConnectInterrupt" is set to 1 in the registry, the driver connects the first interrupt resource found and registers an interrupt service routine.
+
+`OnInterruptIsr`
+
+The interrupt service routine, which has been configured to run at passive-level. Doing so enables the driver to acknowledge or quiesce the interrupt using the SPB interface, which cannot be called at DIRQL.
+
+Typically a driver will clear the hardware interrupt and save any volatile information in its ISR, and then it will queue a workitem to continue processing. Our sample driver instead notifies the SpbTestTool app that an interrupt has occurred and calls KeWaitForSingleObject to wait until the interrupt is handled before returning. A "real" driver should never stall in the ISR like this.
+
+`SpbPeripheralWaitOnInterrupt`
+
+Called to pend a WaitOnInterrupt request in the driver, which will be completed when the next interrupt occurs.
+
+`SpbPeripheralInterruptNotify`
+
+Completes an outstanding WaitOnInterrupt request to inform the SpbTestTool app that an interrupt has occurred.
+
+`SpbPeripheralSignalInterrupt`
+
+Notifies the interrupt service routine that the interrupt has been handled and the ISR should return.
+
+File manifest
+-------------
+
+The following source files are in the src\\SPB\\SpbTestTool\\sys folder and are used to build the SpbTestTool.sys and SpbTestTool.inf files.
+
+<table>
+<colgroup>
+<col width="50%" />
+<col width="50%" />
+</colgroup>
+<thead>
+<tr class="header">
+<th align="left">File
+Description</th>
+</tr>
+</thead>
+<tbody>
+<tr class="odd">
+<td align="left"><p>driver.h, driver.cpp</p>
+<p>Events on the Device Object, and read, write, and IOCTLs from the SpbTestTool application. Implements the driver’s interrupt service routine.</p></td>
+<td align="left"><p>internal.h</p>
+<p>Common includes and typedefs</p></td>
+</tr>
+</tbody>
+</table>
+
+The following source files are in the src\\SPB\\SpbTestTool\\exe folder and are used to build the SpbTestTool.exe file.
+
+<table>
+<colgroup>
+<col width="50%" />
+<col width="50%" />
+</colgroup>
+<thead>
+<tr class="header">
+<th align="left">File
+Description</th>
+</tr>
+</thead>
+<tbody>
+<tr class="odd">
+<td align="left"><p>command.h, command.cpp</p>
+<p>Classes respresenting each of the SpbTestTool commands. For the list of commands, see <a href="#executing_commands">Executing commands</a>.</p></td>
+<td align="left"><p>internal.h</p>
+<p>Common includes and function definitions</p></td>
+</tr>
+</tbody>
+</table>
+
+
diff --git a/spb/SpbTestTool/SpbTestTool.sln b/spb/SpbTestTool/SpbTestTool.sln
new file mode 100644
index 00000000..a2403ff8
--- /dev/null
+++ b/spb/SpbTestTool/SpbTestTool.sln
@@ -0,0 +1,46 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0
+MinimumVisualStudioVersion = 12.0
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{4A32006C-4D25-4879-A039-E17F5CBCC8E6}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{82E8C888-5416-4EBA-A956-D127B2464614}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpbTestTool", "exe\SpbTestTool.vcxproj", "{9FC1A48C-5079-4B38-9775-C7623687EF41}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpbTestTool", "sys\SpbTestTool.vcxproj", "{F7BF1596-9FD8-40AC-8F50-0B3E40DD2E1A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {9FC1A48C-5079-4B38-9775-C7623687EF41}.Debug|Win32.ActiveCfg = Debug|Win32
+ {9FC1A48C-5079-4B38-9775-C7623687EF41}.Debug|Win32.Build.0 = Debug|Win32
+ {9FC1A48C-5079-4B38-9775-C7623687EF41}.Release|Win32.ActiveCfg = Release|Win32
+ {9FC1A48C-5079-4B38-9775-C7623687EF41}.Release|Win32.Build.0 = Release|Win32
+ {9FC1A48C-5079-4B38-9775-C7623687EF41}.Debug|x64.ActiveCfg = Debug|x64
+ {9FC1A48C-5079-4B38-9775-C7623687EF41}.Debug|x64.Build.0 = Debug|x64
+ {9FC1A48C-5079-4B38-9775-C7623687EF41}.Release|x64.ActiveCfg = Release|x64
+ {9FC1A48C-5079-4B38-9775-C7623687EF41}.Release|x64.Build.0 = Release|x64
+ {F7BF1596-9FD8-40AC-8F50-0B3E40DD2E1A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F7BF1596-9FD8-40AC-8F50-0B3E40DD2E1A}.Debug|Win32.Build.0 = Debug|Win32
+ {F7BF1596-9FD8-40AC-8F50-0B3E40DD2E1A}.Release|Win32.ActiveCfg = Release|Win32
+ {F7BF1596-9FD8-40AC-8F50-0B3E40DD2E1A}.Release|Win32.Build.0 = Release|Win32
+ {F7BF1596-9FD8-40AC-8F50-0B3E40DD2E1A}.Debug|x64.ActiveCfg = Debug|x64
+ {F7BF1596-9FD8-40AC-8F50-0B3E40DD2E1A}.Debug|x64.Build.0 = Debug|x64
+ {F7BF1596-9FD8-40AC-8F50-0B3E40DD2E1A}.Release|x64.ActiveCfg = Release|x64
+ {F7BF1596-9FD8-40AC-8F50-0B3E40DD2E1A}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {9FC1A48C-5079-4B38-9775-C7623687EF41} = {4A32006C-4D25-4879-A039-E17F5CBCC8E6}
+ {F7BF1596-9FD8-40AC-8F50-0B3E40DD2E1A} = {82E8C888-5416-4EBA-A956-D127B2464614}
+ EndGlobalSection
+EndGlobal
diff --git a/spb/SpbTestTool/exe/SpbTestTool.vcxproj b/spb/SpbTestTool/exe/SpbTestTool.vcxproj
new file mode 100644
index 00000000..89c45a63
--- /dev/null
+++ b/spb/SpbTestTool/exe/SpbTestTool.vcxproj
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{9FC1A48C-5079-4B38-9775-C7623687EF41}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{624E577D-6233-4F5C-A2DB-76C73310E478}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType />
+ <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType />
+ <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType />
+ <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType />
+ <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);kernel32.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);kernel32.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);kernel32.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\sys;$(SDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);kernel32.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="command.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="util.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/spb/SpbTestTool/exe/SpbTestTool.vcxproj.Filters b/spb/SpbTestTool/exe/SpbTestTool.vcxproj.Filters
new file mode 100644
index 00000000..2f7455d1
--- /dev/null
+++ b/spb/SpbTestTool/exe/SpbTestTool.vcxproj.Filters
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{4EE46FAD-BE22-4954-BF04-9F8262309790}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{6BFD6FCC-FA62-42A5-9060-D9F8D0E56AEB}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{6EE9FC61-6E28-4E6C-AAFC-00F9DF2F34EB}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="command.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/spb/SpbTestTool/exe/command.cpp b/spb/SpbTestTool/exe/command.cpp
new file mode 100644
index 00000000..fa559266
--- /dev/null
+++ b/spb/SpbTestTool/exe/command.cpp
@@ -0,0 +1,660 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ command.cpp
+
+Abstract:
+
+ This module contains the class definitions for the various
+ SpbTestTool commands.
+
+Environment:
+
+ user-mode
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+
+long CCommand::s_Index = 1;
+
+VOID
+PrintCommands()
+{
+ printf("\n");
+ printf("Commands:\n");
+ printf(" open open handle to SPB controller\n");
+ printf(" close close handle to SPB controller\n");
+ printf(" lock lock the bus for exclusive access\n");
+ printf(" unlock unlock the bus\n");
+ printf(" lockconn lock the shared connection for exclusive access -\n");
+ printf(" this primitive is used to synchronize with op-region\n");
+ printf(" accesses by firmware\n");
+ printf(" unlockconn unlock the shared connection\n");
+ printf(" write {} write byte array to peripheral\n");
+ printf(" > write {01 02 03}\n");
+ printf(" read <numBytes> read <numBytes> from peripheral\n");
+ printf(" > read 5\n");
+ printf(" writeread {} <numBytes> atomically write byte array to peripheral\n");
+ printf(" and read <numBytes> back\n");
+ printf(" > writeread {01 02 03} 5\n");
+ printf(" fullduplex {} <numBytes> simultaneously write byte array to peripheral\n");
+ printf(" and read <numBytes> back\n");
+ printf(" > full duplex {01 02 03} 5\n");
+ printf(" signal inform the SpbTestTool driver that the\n");
+ printf(" interrupt has been handled\n");
+ printf(" help print command list\n");
+ printf("\n");
+}
+
+PCCommand
+CCommand::_ParseCommand(
+ _In_ __drv_when(return != nullptr, __drv_aliasesMem) list<string> *Parameters
+ )
+{
+ string tag;
+ string name;
+
+ PCCommand command = nullptr;
+
+ if (Parameters->front()[0] == L'@')
+ {
+ if (PopStringParameter(Parameters, &tag) == false)
+ {
+ printf("Error - could not pop tag\n");
+ return nullptr;
+ }
+ }
+
+ if (PopStringParameter(Parameters, &name) == false)
+ {
+ return nullptr;
+ }
+
+ if(_stricmp(name.c_str(), "open") == 0)
+ {
+ command = new COpenCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "close") == 0)
+ {
+ command = new CCloseCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "lock") == 0)
+ {
+ command = new CLockCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "unlock") == 0)
+ {
+ command = new CUnlockCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "lockconn") == 0)
+ {
+ command = new CLockConnectionCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "unlockconn") == 0)
+ {
+ command = new CUnlockConnectionCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "read") == 0)
+ {
+ command = new CReadCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "write") == 0)
+ {
+ command = new CWriteCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "writeread") == 0)
+ {
+ command = new CWriteReadCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "fullduplex") == 0)
+ {
+ command = new CFullDuplexCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "signal") == 0)
+ {
+ command = new CSignalInterruptCommand(Parameters, tag);
+ }
+ else if(_stricmp(name.c_str(), "help") == 0)
+ {
+ PrintCommands();
+ return nullptr;
+ }
+ else
+ {
+ printf("unrecognized command %s\n", name.c_str());
+ PrintCommands();
+ return nullptr;
+ }
+
+ if (command->Parse() == false)
+ {
+ command->DetachParameter(); //avoid double deletion
+ delete command;
+ return nullptr;
+ }
+
+ return command;
+}
+
+bool
+COpenCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Open peripheral target
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_OPEN,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+COpenCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Peripheral target opened\n");
+ }
+}
+
+bool
+CCloseCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Close peripheral target
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_CLOSE,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+CCloseCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Peripheral target closed\n");
+ }
+}
+
+bool
+CLockCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Lock controller for peripheral target
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_LOCK,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+CLockCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Controller locked\n");
+ }
+}
+
+bool
+CUnlockCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Unlock controller
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_UNLOCK,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+CUnlockCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Controller unlocked\n");
+ }
+}
+
+bool
+CLockConnectionCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Lock connection for peripheral target
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_LOCK_CONNECTION,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+CLockConnectionCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Peripheral target locked\n");
+ }
+}
+
+bool
+CUnlockConnectionCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Unlock connection
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_UNLOCK_CONNECTION,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+CUnlockConnectionCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Peripheral target unlocked\n");
+ }
+}
+
+bool
+CReadCommand::Execute(
+ VOID
+ )
+{
+ ULONG bytesRead;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((ReadFile(File,
+ Buffer,
+ Length,
+ &bytesRead,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesRead);
+ }
+
+ return true;
+}
+
+void
+CReadCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", Status);
+ }
+ else
+ {
+ printf("%u bytes read\n", Information);
+ PrintBytes(Information, Buffer);
+ }
+}
+
+bool
+CWriteCommand::Execute(
+ VOID
+ )
+{
+ ULONG bytesWritten;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((WriteFile(File,
+ Buffer,
+ Length,
+ &bytesWritten,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesWritten);
+ }
+
+ return true;
+}
+
+
+void
+CWriteCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", Status);
+ }
+ else
+ {
+ printf("%u bytes written\n", Information);
+ }
+}
+
+bool
+CWriteReadCommand::Execute(
+ VOID
+ )
+{
+ ULONG bytesTransferred;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(File,
+ IOCTL_SPBTESTTOOL_WRITEREAD,
+ WriteBuffer,
+ WriteLength,
+ Buffer,
+ ReadLength,
+ &bytesTransferred,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesTransferred);
+ }
+
+ return true;
+}
+
+void
+CWriteReadCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("%u bytes returned\n", Information);
+ PrintBytes(Information, Buffer);
+ }
+}
+
+bool
+CFullDuplexCommand::Execute(
+ VOID
+ )
+{
+ ULONG bytesTransferred;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(File,
+ IOCTL_SPBTESTTOOL_FULL_DUPLEX,
+ WriteBuffer,
+ WriteLength,
+ Buffer,
+ ReadLength,
+ &bytesTransferred,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesTransferred);
+ }
+
+ return true;
+}
+
+void
+CFullDuplexCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("%u bytes returned\n", Information);
+ PrintBytes(Information, Buffer);
+ }
+}
+
+bool
+CSignalInterruptCommand::Execute(
+ VOID
+ )
+{
+ //
+ // Signal interrupt
+ //
+
+ ULONG bytesReturned;
+
+ if (File == nullptr)
+ {
+ return false;
+ }
+
+ if ((DeviceIoControl(
+ File,
+ IOCTL_SPBTESTTOOL_SIGNAL_INTERRUPT,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ &bytesReturned,
+ &Overlapped) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ FakeCompletion(GetLastError(), bytesReturned);
+ }
+
+ return true;
+}
+
+void
+CSignalInterruptCommand::Complete(
+ _In_ DWORD Status,
+ _In_ DWORD /* Information */
+ )
+{
+ if (Status != NO_ERROR)
+ {
+ printf("Error %u\n", GetLastError());
+ }
+ else
+ {
+ printf("Interrupt signalled\n");
+ }
+}
diff --git a/spb/SpbTestTool/exe/command.h b/spb/SpbTestTool/exe/command.h
new file mode 100644
index 00000000..5e2633bd
--- /dev/null
+++ b/spb/SpbTestTool/exe/command.h
@@ -0,0 +1,638 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ command.h
+
+Abstract:
+
+ This module contains the class declarations for the various
+ SpbTestTool commands.
+
+Environment:
+
+ user-mode
+
+Revision History:
+
+--*/
+
+typedef struct CCommand CCommand, *PCCommand;
+
+pair<ULONG, PBYTE>
+ParseBuffer(
+ _In_ list<string> *Parameters,
+ _In_ list<string>::iterator Start
+ );
+
+VOID
+PrintCommands();
+
+struct CCommand
+{
+public:
+
+ long Index;
+ string Type;
+ list<string> *Parameters;
+
+ //
+ // File handle this command is being run against (if any).
+ //
+
+ HANDLE File;
+
+ //
+ // Overlapped structure for this command to use.
+ //
+
+ OVERLAPPED Overlapped;
+
+ //
+ // Handle to the thread for this command (if run asynchronously)
+ // The thread is signalled when the command is complete.
+ //
+
+ HANDLE Thread;
+
+ //
+ // Common parameters.
+ //
+
+ string Address;
+ PBYTE Buffer;
+
+ static long s_Index;
+
+ CCommand(
+ _In_ string Type,
+ _In_ __drv_aliasesMem list<string> *Tokens
+ ) : Type(Type),
+ Thread(nullptr),
+ Parameters(Tokens),
+ Address(""),
+ Buffer(nullptr),
+ File(nullptr)
+ {
+ ZeroMemory(&Overlapped, sizeof(OVERLAPPED));
+ Index = s_Index;
+ s_Index += 1;
+ return;
+ }
+
+ virtual
+ ~CCommand(
+ void
+ )
+ {
+ delete[] Buffer;
+ delete Parameters;
+ }
+
+ virtual
+ bool
+ Parse(
+ void
+ )
+ {
+ File = g_Peripheral;
+ return true;
+ }
+
+ void
+ DetachParameter(
+ void
+ )
+ {
+ Parameters = nullptr;
+ }
+
+
+ typedef
+ void
+ (FN_PARSE)(
+ _In_ list<string>::const_iterator &Iterator
+ );
+
+public:
+
+ static
+ PCCommand
+ _ParseCommand(
+ _In_ __drv_when(return != nullptr, __drv_aliasesMem) list<string> *Tokens
+ );
+
+public:
+
+ VOID
+ FakeCompletion(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ )
+ {
+ Overlapped.Internal = Status == NO_ERROR ? Status : HRESULT_FROM_WIN32(Status);
+ Overlapped.InternalHigh = Information;
+ SetEvent(Overlapped.hEvent);
+ }
+
+ virtual
+ bool
+ Execute(
+ VOID
+ ) = 0;
+
+ virtual
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ )
+ {
+ printf("%s completed with status %u, information %u\n",
+ Type.c_str(),
+ Status,
+ Information);
+ }
+
+ virtual
+ bool
+ Cancel(
+ VOID
+ )
+ {
+ if (File != nullptr)
+ {
+ return CancelIoEx(File, &Overlapped) ? true : false;
+ }
+ else
+ {
+ return false;
+ }
+ }
+};
+
+class COpenCommand : public CCommand
+{
+private:
+
+public:
+ COpenCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("open", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CCloseCommand : public CCommand
+{
+private:
+
+public:
+ CCloseCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("close", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CLockCommand : public CCommand
+{
+private:
+
+public:
+ CLockCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("lock", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CUnlockCommand : public CCommand
+{
+private:
+
+public:
+ CUnlockCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("unlock", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CLockConnectionCommand : public CCommand
+{
+private:
+
+public:
+ CLockConnectionCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("lockconn", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CUnlockConnectionCommand : public CCommand
+{
+private:
+
+public:
+ CUnlockConnectionCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("unlockconn", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CReadCommand : public CCommand
+{
+private:
+ ULONG Length;
+
+public:
+
+ CReadCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("read", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Parse(
+ void
+ )
+ {
+ if (CCommand::Parse() == false)
+ {
+ return false;
+ }
+
+ if (PopNumberParameter(Parameters, 10, &Length) == false)
+ {
+ printf("Length required\n");
+ return false;
+ }
+
+ if (Length > 0)
+ {
+ Buffer = new BYTE[Length];
+ ZeroMemory(Buffer, Length);
+ }
+ else
+ {
+ Buffer = nullptr;
+ }
+
+ return true;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+
+class CWriteCommand : public CCommand
+{
+private:
+ ULONG Length;
+
+public:
+
+ CWriteCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("write", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Parse(
+ VOID
+ )
+ {
+ if (CCommand::Parse() == false)
+ {
+ return false;
+ }
+
+ pair<ULONG, PBYTE> buf;
+
+ if (PopBufferParameter(Parameters, &buf) == false)
+ {
+ printf("Buffer required\n");
+ return false;
+ }
+
+ Length = buf.first;
+ Buffer = buf.second;
+
+ return true;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CWriteReadCommand : public CCommand
+{
+private:
+ ULONG WriteLength;
+ ULONG ReadLength;
+
+ PBYTE WriteBuffer;
+
+public:
+
+ CWriteReadCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("writeread", Parameters)
+ {
+ WriteBuffer = NULL;
+ return;
+ }
+
+ ~CWriteReadCommand(
+ void
+ )
+ {
+ delete[] WriteBuffer;
+ }
+
+ bool
+ Parse(
+ void
+ )
+ {
+ if (CCommand::Parse() == false)
+ {
+ return false;
+ }
+
+ pair<ULONG, PBYTE> buf;
+
+ if (PopBufferParameter(Parameters, &buf) == false)
+ {
+ printf("Buffer required\n");
+ return false;
+ }
+
+ WriteLength = buf.first;
+ WriteBuffer = buf.second;
+
+ if (PopNumberParameter(Parameters, 10, &ReadLength) == false)
+ {
+ printf("Length required\n");
+ return false;
+ }
+
+ if (ReadLength > 0)
+ {
+ Buffer = new BYTE[ReadLength];
+ ZeroMemory(Buffer, ReadLength);
+ }
+ else
+ {
+ Buffer = nullptr;
+ }
+
+ return true;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CFullDuplexCommand : public CCommand
+{
+private:
+ ULONG WriteLength;
+ ULONG ReadLength;
+
+ PBYTE WriteBuffer;
+
+public:
+
+ CFullDuplexCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("fullduplex", Parameters)
+ {
+ WriteBuffer = NULL;
+ return;
+ }
+
+ ~CFullDuplexCommand(
+ void
+ )
+ {
+ delete[] WriteBuffer;
+ }
+
+ bool
+ Parse(
+ void
+ )
+ {
+ if (CCommand::Parse() == false)
+ {
+ return false;
+ }
+
+ pair<ULONG, PBYTE> buf;
+
+ if (PopBufferParameter(Parameters, &buf) == false)
+ {
+ printf("Buffer required\n");
+ return false;
+ }
+
+ WriteLength = buf.first;
+ WriteBuffer = buf.second;
+
+ if (PopNumberParameter(Parameters, 10, &ReadLength) == false)
+ {
+ printf("Length required\n");
+ return false;
+ }
+
+ if (ReadLength > 0)
+ {
+ Buffer = new BYTE[ReadLength];
+ ZeroMemory(Buffer, ReadLength);
+ }
+ else
+ {
+ Buffer = nullptr;
+ }
+
+ return true;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CSignalInterruptCommand : public CCommand
+{
+private:
+
+public:
+ CSignalInterruptCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("signal", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
+
+class CWaitOnInterruptCommand : public CCommand
+{
+private:
+
+public:
+ CWaitOnInterruptCommand(
+ _In_ __drv_aliasesMem list<string> *Parameters,
+ _In_opt_ string Tag
+ ) : CCommand("waitoninterrupt", Parameters)
+ {
+ return;
+ }
+
+ bool
+ Execute(
+ VOID
+ );
+
+ void
+ Complete(
+ _In_ DWORD Status,
+ _In_ DWORD Information
+ );
+};
diff --git a/spb/SpbTestTool/exe/internal.h b/spb/SpbTestTool/exe/internal.h
new file mode 100644
index 00000000..268186c0
--- /dev/null
+++ b/spb/SpbTestTool/exe/internal.h
@@ -0,0 +1,99 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ internal.h
+
+Abstract:
+
+ This module contains the internal type definitations and
+ helper function declarations for the SpbTestTool app.
+
+Environment:
+
+ user-mode
+
+Revision History:
+
+--*/
+
+#include <string>
+#include <map>
+#include <list>
+#include <map>
+#include <functional>
+
+#include <iomanip>
+
+#include <cstdlib>
+
+#include <stdexcept>
+
+#include <math.h>
+
+#include <wchar.h>
+#include <windows.h>
+#include <winioctl.h>
+#include <specstrings.h>
+
+#include "spbtestioctl.h"
+
+using namespace std;
+
+//
+// Global Variables
+//
+
+extern HANDLE g_Peripheral;
+
+typedef pair<ULONG, PBYTE> BUFPAIR;
+typedef list<BUFPAIR> BUFLIST;
+
+VOID
+PrintBytes(
+ _In_ ULONG BufferCb,
+ _In_reads_bytes_(BufferCb) BYTE Buffer[]
+ );
+
+_Success_(return)
+bool
+PopStringParameter(
+ _Inout_ list<string> *Parameters,
+ _Out_ string *Value,
+ _Out_opt_ bool *Present = nullptr
+ );
+
+typedef pair<ULONG,ULONG> bounds;
+
+_Success_(return)
+bool
+PopNumberParameter(
+ _Inout_ list<string> *Parameters,
+ _In_ ULONG Radix,
+ _Out_ ULONG *Value,
+ _In_opt_ pair<ULONG,ULONG> Bounds = bounds(0,0),
+ _Out_opt_ bool *Present = nullptr
+ );
+
+_Success_(return)
+bool
+ParseNumber(
+ _In_ const string &String,
+ _In_ ULONG Radix,
+ _Out_ ULONG *Value,
+ _In_opt_ bounds Bounds = bounds(0,0)
+ );
+
+_Success_(return)
+bool
+PopBufferParameter(
+ _Inout_ list<string> *Parameters,
+ _Out_ pair<ULONG, PBYTE> *Value
+ );
+
+
+#define countof(x) (sizeof(x) / sizeof(x[0]))
+
+#include "command.h"
diff --git a/spb/SpbTestTool/exe/main.cpp b/spb/SpbTestTool/exe/main.cpp
new file mode 100644
index 00000000..ce9e994a
--- /dev/null
+++ b/spb/SpbTestTool/exe/main.cpp
@@ -0,0 +1,633 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ main.cpp
+
+Abstract:
+
+ This module contains the main entry point for the SpbTestTool
+ app. It facilitates accepting input, building commands, and
+ printing output.
+
+Environment:
+
+ user-mode
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+
+DWORD WINAPI InterruptNotificationThread(
+ _In_ LPVOID pvData
+ );
+
+bool
+ReadCommandFromStream(
+ _In_ FILE* InputStream,
+ _Inout_ list<string> *Tokens
+ );
+
+VOID
+PrintUsage(
+ _In_ PCSTR exeName
+ );
+
+DWORD
+RunCommand(
+ _In_ PCCommand Command
+ );
+
+HANDLE g_Peripheral = nullptr;
+HANDLE g_Event = nullptr;
+HANDLE g_InterruptNotificationThread = nullptr;
+PCCommand g_CurrentCommand = nullptr;
+bool g_WaitOnInterrupt = true;
+
+BOOL
+WINAPI
+OnControlKey(
+ _In_ DWORD ControlType
+ );
+
+void
+__cdecl
+main(
+ _In_ ULONG ArgumentsCe,
+ _In_reads_(ArgumentsCe) PCSTR Arguments[]
+ )
+{
+ FILE* inputStream = stdin;
+ bool prompt = true;
+
+ PCSTR peripheralPath = nullptr;
+ PCSTR inputPath = nullptr;
+
+ //
+ // Parse the command line arguments.
+ //
+
+ ULONG arg = 1;
+
+ while (arg < ArgumentsCe)
+ {
+ if ((Arguments[arg][0] != '/') &&
+ (Arguments[arg][0] != '-'))
+ {
+ PrintUsage(Arguments[0]);
+ goto exit;
+ }
+ else
+ {
+ if (tolower(Arguments[arg][1]) == 'p')
+ {
+ arg++;
+ if (arg == ArgumentsCe)
+ {
+ PrintUsage(Arguments[0]);
+ goto exit;
+ }
+ peripheralPath = Arguments[arg];
+ }
+ else if (tolower(Arguments[arg][1]) == 'i')
+ {
+ arg++;
+ if (arg == ArgumentsCe)
+ {
+ PrintUsage(Arguments[0]);
+ goto exit;
+ }
+ inputPath = Arguments[arg];
+ }
+ else
+ {
+ PrintUsage(Arguments[0]);
+ goto exit;
+ }
+ }
+
+ arg++;
+ }
+
+ //
+ // Open the input file if specified
+ //
+
+ if (inputPath != nullptr)
+ {
+ errno_t error;
+
+ printf("Opening %s as command input file\n", inputPath);
+ error = fopen_s(&inputStream, inputPath, "r");
+
+ if (error != 0)
+ {
+ printf("Error opening input file %s - %d\n", inputPath, error);
+ goto exit;
+ }
+
+ if (inputStream == nullptr)
+ {
+ printf("Error opening input file %s - %d\n", inputPath, error);
+ goto exit;
+ }
+
+ prompt = false;
+ }
+
+ //
+ // Open peripheral driver
+ //
+
+ if (peripheralPath == nullptr)
+ {
+ g_Peripheral = CreateFileW(
+ SPBTESTTOOL_USERMODE_PATH,
+ (GENERIC_READ | GENERIC_WRITE),
+ 0,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ nullptr);
+ }
+ else
+ {
+ printf("Opening %s as peripheral driver path \n", peripheralPath);
+ g_Peripheral = CreateFileA(
+ peripheralPath,
+ (GENERIC_READ | GENERIC_WRITE),
+ 0,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ nullptr);
+ }
+
+ if (g_Peripheral == INVALID_HANDLE_VALUE)
+ {
+ printf("Error opening peripheral driver - %u\n", GetLastError());
+ goto exit;
+ }
+
+ setvbuf(inputStream, nullptr, _IONBF, 0);
+ setvbuf(stdout, nullptr, _IONBF, 0);
+ setvbuf(stderr, nullptr, _IONBF, 0);
+
+ //
+ // Setup a control-C handler.
+ //
+
+ if (SetConsoleCtrlHandler(OnControlKey, true) == FALSE)
+ {
+ printf("Error setting ctrl-C handler - %u\n", GetLastError());
+ goto exit;
+ }
+
+ //
+ // Setup an overlapped structure to use with each I/O.
+ //
+
+ g_Event = CreateEvent(nullptr, true, false, nullptr);
+
+ if (g_Event == nullptr)
+ {
+ printf("error creating I/O event - %u\n", GetLastError());
+ goto exit;
+ }
+
+ //
+ // Create interrupt notification thread.
+ //
+
+ g_InterruptNotificationThread = CreateThread(
+ nullptr,
+ 0,
+ &InterruptNotificationThread,
+ nullptr,
+ 0,
+ nullptr);
+
+ if (g_InterruptNotificationThread == nullptr)
+ {
+ printf("error creating interrupt notification thread - %u\n", GetLastError());
+ goto exit;
+ }
+
+ //
+ // Loop reading commands off the command line and parsing them.
+ // EOF causes an exit.
+ //
+
+ do
+ {
+ list<string> *tokens = new list<string>();
+ PCCommand command;
+
+ if (prompt)
+ {
+ printf("> ");
+ fflush(stdout);
+ }
+
+ if (ReadCommandFromStream(inputStream, tokens) == false)
+ {
+ delete tokens;
+ break;
+ }
+
+ if (tokens->empty())
+ {
+ delete tokens;
+ continue;
+ }
+
+ command = CCommand::_ParseCommand(tokens);
+
+ if (command == nullptr)
+ {
+ delete tokens;
+ continue;
+ }
+
+ g_CurrentCommand = command;
+
+ RunCommand(command);
+
+ g_CurrentCommand = nullptr;
+
+ //
+ // A reference to the tokens list is saved by
+ // the command and will be freed when the command
+ // is deleted.
+ //
+
+ delete command;
+
+ }
+ while (feof(inputStream) == 0);
+
+exit:
+
+ if (prompt == false)
+ {
+ fclose(inputStream);
+ }
+
+ if (g_InterruptNotificationThread != nullptr)
+ {
+ WaitForSingleObject(g_InterruptNotificationThread, INFINITE);
+ CloseHandle(g_InterruptNotificationThread);
+ }
+
+ CloseHandle(g_Peripheral);
+
+ if (g_Event != nullptr)
+ {
+ CloseHandle(g_Event);
+ }
+
+ return;
+}
+
+DWORD WINAPI InterruptNotificationThread(
+ _In_ LPVOID pvData
+ )
+{
+ DWORD status;
+ DWORD bytesReturned;
+ OVERLAPPED ov = {0};
+
+ UNREFERENCED_PARAMETER(pvData);
+
+ ov.hEvent = CreateEvent(nullptr, false, false, nullptr);
+
+ if (ov.hEvent == nullptr)
+ {
+ printf("error creating overlapped event for interrupt thread - %u\n", GetLastError());
+ goto exit;
+ }
+
+ while (g_WaitOnInterrupt == true)
+ {
+ if ((DeviceIoControl(
+ g_Peripheral,
+ IOCTL_SPBTESTTOOL_WAIT_ON_INTERRUPT,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ nullptr,
+ &ov) == TRUE) ||
+ (GetLastError() != ERROR_IO_PENDING))
+ {
+ printf("failed to pend WaitOnInterrupt IOCTL- %u\n", GetLastError());
+ goto exit;
+ }
+
+ status = WaitForSingleObject(ov.hEvent, INFINITE);
+
+ switch (status)
+ {
+ // DeviceIoControl completed.
+ case WAIT_OBJECT_0:
+
+ if (!GetOverlappedResult(g_Peripheral, &ov, &bytesReturned, FALSE))
+ {
+ printf("GetOverlappedResult failed with status: %u\n\n", GetLastError());
+ }
+ else
+ {
+ printf("\n\n");
+ printf(" ** Interrupt detected. Please acknowledge or disable **\n");
+ printf(" *** and type 'signal' to inform the SpbTestTool driver ***\n");
+ printf(" ** that the interrupt has been handled. **\n");
+ printf("\n");
+ }
+ break;
+
+ default:
+ // Error in the WaitForSingleObject; abort.
+ // This indicates a problem with the OVERLAPPED
+ // structure's event handle.
+ printf("WaitOnInterrupt unexpected return %u\n\n", status);
+ break;
+ }
+ }
+
+exit:
+
+ return 0;
+}
+
+VOID
+PrintUsage(
+ _In_ PCSTR exeName
+ )
+{
+ printf("Usage: %s [/p <driver_path>] [/i <script_name>]\n",
+ exeName);
+}
+
+DWORD
+RunCommand(
+ _In_ PCCommand Command
+ )
+{
+
+ DWORD status;
+ DWORD bytesTransferred;
+
+ Command->Overlapped.hEvent = g_Event;
+
+ if (Command->Execute() == true)
+ {
+ if (GetOverlappedResult(Command->File,
+ &(Command->Overlapped),
+ &bytesTransferred,
+ true) == FALSE)
+ {
+ status = GetLastError();
+ }
+ else
+ {
+ status = NO_ERROR;
+ }
+
+ Command->Complete(status, bytesTransferred);
+ }
+ else
+ {
+ status = GetLastError();
+ }
+
+ return status;
+}
+
+
+bool
+ReadLine(
+ _In_ FILE* InputStream,
+ _In_ ULONG BufferCch,
+ _Out_writes_(BufferCch) CHAR Buffer[]
+ )
+{
+ ULONG i;
+
+ for(i = 0; i < BufferCch - 1; i += 1)
+ {
+ if (fread(&(Buffer[i]), sizeof(CHAR), 1, InputStream) == 0)
+ {
+ return false;
+ }
+
+ fputc(Buffer[i], stdout);
+
+ if (Buffer[i] == '\n')
+ {
+ Buffer[i] = '\0';
+ return true;
+ }
+ else if (Buffer[i] == '\b')
+ {
+ Buffer[i] = '\0';
+ i -= 2;
+ }
+ }
+
+ return false;
+}
+
+bool
+ReadCommandFromStream(
+ _In_ FILE* InputStream,
+ _Inout_ list<string> *Tokens
+ )
+{
+ CHAR buffer[255];
+
+ if (ReadLine(InputStream,
+ sizeof(buffer) - 1,
+ buffer) == false)
+ {
+ return false;
+ }
+
+ string currentLine(buffer);
+ string token;
+
+ list<string> tokens;
+
+ Tokens->clear();
+
+ string::size_type i;
+ long start;
+
+ for(i = 0, start = -1;
+ i < currentLine.length() + 1;
+ i += 1)
+ {
+ wchar_t c;
+
+ if (i < currentLine.length())
+ {
+ c = currentLine[i];
+ }
+ else
+ {
+ c = L'\0';
+ }
+
+ if (iswspace(c) || (c == L'\0'))
+ {
+ if (start == -1)
+ {
+ //
+ // not tracking a token - whitespace is skipped.
+ //
+ }
+ else
+ {
+ //
+ // tracking a token - whitespace or NUL terminates it.
+ // if the last character was a } then split the token.
+ //
+
+ if (currentLine[i-1] == '}')
+ {
+ token.assign(currentLine, start, i - start - 1);
+ Tokens->insert(Tokens->end(), token);
+ Tokens->insert(Tokens->end(), string("}"));
+ }
+ else
+ {
+ token.assign(currentLine, start, i - start);
+ Tokens->insert(Tokens->end(), token);
+ }
+
+ start = -1;
+ }
+ }
+ else if (start == -1)
+ {
+ //
+ // first character of a token. If it's { then split the token.
+ //
+
+ if (currentLine[i] == '{')
+ {
+ Tokens->insert(Tokens->end(), string("{"));
+ }
+ else if (currentLine[i] == '}')
+ {
+ Tokens->insert(Tokens->end(), string("}"));
+ }
+ else
+ {
+ start = (long) i;
+ }
+ }
+ else
+ {
+ //
+ // tracking a token - non whitespace is included.
+ //
+ }
+ }
+
+ return true;
+}
+
+VOID
+PrintBytes(
+ _In_ ULONG BufferCb,
+ _In_reads_bytes_(BufferCb) BYTE Buffer[]
+ )
+{
+ ULONG index = 0;
+
+ for(index = 0; index < BufferCb; index += 16)
+ {
+ printf(" ");
+ for(ULONG i = index; i < (index + 16); i += 1)
+ {
+ if (i < BufferCb)
+ {
+ if ((i != index) && (i % 8 == 0))
+ {
+ printf("- ");
+ }
+
+ printf("%02x ", Buffer[i]);
+ }
+ else
+ {
+ if ((i != index) && (i % 8 == 0))
+ {
+ printf(" ");
+ }
+ printf(" ");
+ }
+ }
+
+ printf(" : ");
+
+ for(ULONG i = index; i < (index + 16); i += 1)
+ {
+ if (i < BufferCb)
+ {
+ if ((i != index) && (i % 8 == 0))
+ {
+ printf(" ");
+ }
+
+ if (isprint(Buffer[i]))
+ {
+ printf("%c", Buffer[i]);
+ }
+ else
+ {
+ printf(".");
+ }
+ }
+ else
+ {
+ printf(" ");
+ }
+ }
+
+ printf("\n");
+ }
+}
+
+BOOL
+WINAPI
+OnControlKey(
+ _In_ DWORD ControlType
+ )
+{
+ if (ControlType == CTRL_C_EVENT)
+ {
+ //
+ // If there's a current command then attempt to cancel it.
+ //
+
+ if (g_CurrentCommand != nullptr)
+ {
+ g_CurrentCommand->Cancel();
+ return TRUE;
+ }
+
+ //
+ // Stop the interrupt notification thread
+ //
+
+ g_WaitOnInterrupt = false;
+ CancelIo(g_Peripheral);
+ }
+
+ return FALSE;
+}
diff --git a/spb/SpbTestTool/exe/util.cpp b/spb/SpbTestTool/exe/util.cpp
new file mode 100644
index 00000000..fe6067c9
--- /dev/null
+++ b/spb/SpbTestTool/exe/util.cpp
@@ -0,0 +1,250 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ util.cpp
+
+Abstract:
+
+ This module contains the internal helper function definitions
+ for the SpbTestTool app.
+
+Environment:
+
+ user-mode
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+
+_Success_(return)
+bool
+PopStringParameter(
+ _Inout_ list<string> *Parameters,
+ _Out_ string *Value,
+ _Out_opt_ bool *Present
+ )
+{
+ if (Parameters->empty())
+ {
+ if (Present != nullptr)
+ {
+ *Present = false;
+ *Value = string("");
+ return true;
+ }
+ else
+ {
+ printf("Missing required parameter\n");
+ return false;
+ }
+ }
+
+ if (Present != nullptr)
+ {
+ *Present = true;
+ }
+
+ *Value = Parameters->front();
+ Parameters->pop_front();
+ return true;
+}
+
+_Success_(return)
+bool
+ParseNumber(
+ _In_ const string &String,
+ _In_ ULONG Radix,
+ _Out_ ULONG *Value,
+ _In_opt_ bounds Bounds
+ )
+{
+ PSTR end;
+
+#pragma prefast(suppress:__WARNING_MISSING_ZERO_TERMINATION2 ,"zero-termination is checked below")
+ *Value = strtoul(String.c_str(), &end, Radix);
+
+ //
+ // Make sure the entire string parsed.
+ //
+
+ if (*end != '\0')
+ {
+ printf("Value %s is not a number\n", String.c_str());
+ return false;
+ }
+
+ //
+ // See if we should do a bounds check.
+ //
+
+ if ((Bounds.first != 0) || (Bounds.second != 0))
+ {
+ if ((*Value < Bounds.first) ||
+ (*Value > Bounds.second))
+ {
+ printf("Value %s is out of bounds\n", String.c_str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+_Success_(return)
+bool
+PopNumberParameter(
+ _Inout_ list<string> *Parameters,
+ _In_ ULONG Radix,
+ _Out_ ULONG *Value,
+ _In_opt_ bounds Bounds,
+ _Out_opt_ bool *Present
+ )
+{
+ string s;
+
+ bool found = PopStringParameter(Parameters, &s, Present);
+
+ if ((Present != nullptr) && (*Present == false))
+ {
+ *Value = Bounds.first;
+ return true;
+ }
+ else if (found == false)
+ {
+ return false;
+ }
+
+ return ParseNumber(s, Radix, Value, Bounds);
+}
+
+_Success_(return)
+bool
+PopBufferParameter(
+ _Inout_ list<string> *Parameters,
+ _Out_ pair<ULONG, PBYTE> *Value
+ )
+{
+ ULONG length = 0;
+ PBYTE buffer = nullptr;
+
+ //
+ // Check for an explict length
+ //
+
+ if (Parameters->front() != "{")
+ {
+ if (PopNumberParameter(Parameters, 10, &length) == false)
+ {
+ printf("Length expected\n");
+ return false;
+ }
+
+ //
+ // Consume any leading {
+ //
+
+ if ((Parameters->empty() == false) &&
+ (Parameters->front() == "{"))
+ {
+ Parameters->pop_front();
+ }
+ }
+ else
+ {
+ string tmp;
+ if (PopStringParameter(Parameters, &tmp) == false)
+ {
+ return false;
+ }
+ else if (tmp != "{")
+ {
+ printf("output buffer must start with {\n");
+ return false;
+ }
+
+ //
+ // Count values until the trailing } - assume one byte per value
+ //
+
+ list<string>::iterator i;
+
+ for(i = Parameters->begin();
+ ((i != Parameters->end()) && (*i != "}"));
+ i++)
+ {
+ length += 1;
+ }
+
+ if (*i != "}")
+ {
+ printf("output buffer must end with }\n");
+ return false;
+ }
+ }
+
+ if (length != 0)
+ {
+ ULONG b = 0;
+ bool bufferEnd = false;
+
+ //
+ // Allocate the buffer.
+ //
+
+ buffer = new BYTE[length];
+
+ for(b = 0; b < length; b += 1)
+ {
+ ULONG value = 0;
+
+ if (bufferEnd == false)
+ {
+ string nextElement;
+
+ PopStringParameter(Parameters, &nextElement);
+
+ if (nextElement == "}")
+ {
+ value = 0;
+ bufferEnd = true;
+ }
+ else if (ParseNumber(nextElement, 16, &value, bounds(0, 0xff)) == false)
+ {
+ printf("invalid byte value %s\n", nextElement.c_str());
+ delete [] buffer;
+ return false;
+ }
+ }
+
+ buffer[b] = (BYTE) value;
+ }
+
+ if (bufferEnd == false)
+ {
+ string end;
+
+ if (PopStringParameter(Parameters, &end) == false)
+ {
+ printf("unclosed buffer\n");
+ delete [] buffer;
+ return false;
+ }
+ else if (end != "}")
+ {
+ printf("buffer has too many initializers\n");
+ delete [] buffer;
+ return false;
+ }
+ }
+ }
+
+ Value->first = length;
+ Value->second = buffer;
+
+ return true;
+}
diff --git a/spb/SpbTestTool/sys/SpbTestTool.vcxproj b/spb/SpbTestTool/sys/SpbTestTool.vcxproj
new file mode 100644
index 00000000..c79c2fc3
--- /dev/null
+++ b/spb/SpbTestTool/sys/SpbTestTool.vcxproj
@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{F7BF1596-9FD8-40AC-8F50-0B3E40DD2E1A}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{94D82B59-F741-453B-ADA8-F7394427ED16}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems">
+ <ClCompile Include="driver.cpp; device.cpp; peripheral.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppScanConfigurationData>trace.h</WppScanConfigurationData>
+ <WppTraceFunction>Trace(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ </ClCompile>
+ <Inf Include=".\SpbTestTool.inx">
+ <Architecture>$(InfArch)</Architecture>
+ <SpecifyArchitecture>true</SpecifyArchitecture>
+ <CopyOutput>.\$(IntDir)\SpbTestTool.inf</CopyOutput>
+ </Inf>
+ <OtherWpp Include="resource.rc">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppScanConfigurationData>trace.h</WppScanConfigurationData>
+ <WppTraceFunction>Trace(LEVEL,FLAGS,MSG,...)</WppTraceFunction>
+ </OtherWpp>
+ </ItemGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>SpbTestTool</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/spb/SpbTestTool/sys/SpbTestTool.vcxproj.Filters b/spb/SpbTestTool/sys/SpbTestTool.vcxproj.Filters
new file mode 100644
index 00000000..637381d0
--- /dev/null
+++ b/spb/SpbTestTool/sys/SpbTestTool.vcxproj.Filters
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{BCBB1C45-9E44-4421-AA82-B522FF1D13DF}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{9B409A5B-0FF8-44B1-8827-A0E2ED848AED}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{0CD7CABF-642D-4525-BB60-6A5534613E34}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{B7A5B324-3FC1-4A11-B5BA-4F64EEE4B619}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="device.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="driver.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="peripheral.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <FilesToPackage Include=".\Debug\\SpbTestTool.inf">
+ <Filter>Driver Files</Filter>
+ </FilesToPackage>
+ <Inf Include=".\SpbTestTool.inx">
+ <Filter>Driver Files</Filter>
+ </Inf>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/spb/SpbTestTool/sys/device.cpp b/spb/SpbTestTool/sys/device.cpp
new file mode 100644
index 00000000..28455aa6
--- /dev/null
+++ b/spb/SpbTestTool/sys/device.cpp
@@ -0,0 +1,848 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ device.cpp
+
+Abstract:
+
+ This module contains WDF device initialization
+ functions for the peripheral driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+#include "peripheral.h"
+#include "device.h"
+
+#include "device.tmh"
+
+
+/////////////////////////////////////////////////
+//
+// WDF callbacks.
+//
+/////////////////////////////////////////////////
+
+NTSTATUS
+OnPrepareHardware(
+ _In_ WDFDEVICE FxDevice,
+ _In_ WDFCMRESLIST FxResourcesRaw,
+ _In_ WDFCMRESLIST FxResourcesTranslated
+ )
+/*++
+
+ Routine Description:
+
+ This routine caches the SPB resource connection ID.
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+ FxResourcesRaw - list of translated hardware resources that
+ the PnP manager has assigned to the device
+ FxResourcesTranslated - list of raw hardware resources that
+ the PnP manager has assigned to the device
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ PDEVICE_CONTEXT pDevice = GetDeviceContext(FxDevice);
+ BOOLEAN fSpbResourceFound = FALSE;
+ BOOLEAN fInterruptResourceFound = FALSE;
+ ULONG interruptIndex = 0;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ UNREFERENCED_PARAMETER(FxResourcesRaw);
+
+ //
+ // Parse the peripheral's resources.
+ //
+
+ ULONG resourceCount = WdfCmResourceListGetCount(FxResourcesTranslated);
+
+ for(ULONG i = 0; i < resourceCount; i++)
+ {
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;
+ UCHAR Class;
+ UCHAR Type;
+
+ pDescriptor = WdfCmResourceListGetDescriptor(
+ FxResourcesTranslated, i);
+
+ switch (pDescriptor->Type)
+ {
+ case CmResourceTypeConnection:
+
+ //
+ // Look for I2C or SPI resource and save connection ID.
+ //
+
+ Class = pDescriptor->u.Connection.Class;
+ Type = pDescriptor->u.Connection.Type;
+
+ if ((Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL) &&
+ ((Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C) ||
+ (Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_SPI)))
+ {
+ if (fSpbResourceFound == FALSE)
+ {
+ pDevice->PeripheralId.LowPart =
+ pDescriptor->u.Connection.IdLowPart;
+ pDevice->PeripheralId.HighPart =
+ pDescriptor->u.Connection.IdHighPart;
+
+ fSpbResourceFound = TRUE;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_WDFLOADING,
+ "SPB resource found with ID=0x%llx",
+ pDevice->PeripheralId.QuadPart);
+ }
+ else
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_WDFLOADING,
+ "Duplicate SPB resource found with ID=0x%llx",
+ pDevice->PeripheralId.QuadPart);
+ }
+ }
+
+ break;
+
+ case CmResourceTypeInterrupt:
+
+ if (fInterruptResourceFound == FALSE)
+ {
+ fInterruptResourceFound = TRUE;
+ interruptIndex = i;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_WDFLOADING,
+ "Interrupt resource found");
+ }
+ else
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_WDFLOADING,
+ "Duplicate interrupt resource found");
+ }
+
+ break;
+
+ default:
+
+ //
+ // Ignoring all other resource types.
+ //
+
+ break;
+ }
+ }
+
+ //
+ // An SPB resource is required.
+ //
+
+ if (fSpbResourceFound == FALSE)
+ {
+ status = STATUS_NOT_FOUND;
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "SPB resource not found - %!STATUS!",
+ status);
+ }
+
+ //
+ // Create the interrupt if an interrupt
+ // resource was found.
+ //
+
+ if (NT_SUCCESS(status))
+ {
+ if ((pDevice->ConnectInterrupt == TRUE) &&
+ (fInterruptResourceFound == TRUE))
+ {
+ WDF_INTERRUPT_CONFIG interruptConfig;
+ WDF_INTERRUPT_CONFIG_INIT(
+ &interruptConfig,
+ OnInterruptIsr,
+ NULL);
+
+ interruptConfig.PassiveHandling = TRUE;
+ interruptConfig.InterruptTranslated = WdfCmResourceListGetDescriptor(
+ FxResourcesTranslated,
+ interruptIndex);
+ interruptConfig.InterruptRaw = WdfCmResourceListGetDescriptor(
+ FxResourcesRaw,
+ interruptIndex);
+
+ status = WdfInterruptCreate(
+ pDevice->FxDevice,
+ &interruptConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &pDevice->Interrupt);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "WdfInterruptCreate failed - %!STATUS!",
+ status);
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ KeInitializeEvent(
+ &pDevice->IsrWaitEvent,
+ SynchronizationEvent,
+ FALSE);
+ }
+ }
+ }
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+NTSTATUS
+OnReleaseHardware(
+ _In_ WDFDEVICE FxDevice,
+ _In_ WDFCMRESLIST FxResourcesTranslated
+ )
+/*++
+
+ Routine Description:
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+ FxResourcesTranslated - list of raw hardware resources that
+ the PnP manager has assigned to the device
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ PDEVICE_CONTEXT pDevice = GetDeviceContext(FxDevice);
+ NTSTATUS status = STATUS_SUCCESS;
+
+ UNREFERENCED_PARAMETER(FxResourcesTranslated);
+
+ if (pDevice->Interrupt != nullptr)
+ {
+ WdfObjectDelete(pDevice->Interrupt);
+ }
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+NTSTATUS
+OnD0Entry(
+ _In_ WDFDEVICE FxDevice,
+ _In_ WDF_POWER_DEVICE_STATE FxPreviousState
+ )
+/*++
+
+ Routine Description:
+
+ This routine allocates objects needed by the driver.
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+ FxPreviousState - previous power state
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ UNREFERENCED_PARAMETER(FxPreviousState);
+
+ PDEVICE_CONTEXT pDevice = GetDeviceContext(FxDevice);
+ NTSTATUS status;
+
+ //
+ // Create the SPB target.
+ //
+
+ WDF_OBJECT_ATTRIBUTES targetAttributes;
+ WDF_OBJECT_ATTRIBUTES_INIT(&targetAttributes);
+
+ status = WdfIoTargetCreate(
+ pDevice->FxDevice,
+ &targetAttributes,
+ &pDevice->SpbController);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed to create IO target - %!STATUS!",
+ status);
+ }
+
+ //
+ // InputMemory will be created when an SPB request is about to be
+ // sent. Indicate that it is not yet initialized.
+ //
+
+ pDevice->InputMemory = WDF_NO_HANDLE;
+
+ //
+ // Create the SPB request.
+ //
+
+ if (NT_SUCCESS(status))
+ {
+ WDF_OBJECT_ATTRIBUTES requestAttributes;
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&requestAttributes, REQUEST_CONTEXT);
+
+ status = WdfRequestCreate(
+ &requestAttributes,
+ nullptr,
+ &pDevice->SpbRequest);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Failed to create IO request - %!STATUS!",
+ status);
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ PREQUEST_CONTEXT pRequest = GetRequestContext(
+ pDevice->SpbRequest);
+
+ pRequest->FxDevice = pDevice->FxDevice;
+ pRequest->IsSpbSequenceRequest = FALSE;
+ pRequest->SequenceWriteLength = 0;
+ }
+ }
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+NTSTATUS
+OnD0Exit(
+ _In_ WDFDEVICE FxDevice,
+ _In_ WDF_POWER_DEVICE_STATE FxPreviousState
+ )
+/*++
+
+ Routine Description:
+
+ This routine destroys objects needed by the driver.
+
+ Arguments:
+
+ FxDevice - a handle to the framework device object
+ FxPreviousState - previous power state
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ UNREFERENCED_PARAMETER(FxPreviousState);
+
+ PDEVICE_CONTEXT pDevice = GetDeviceContext(FxDevice);
+
+ if (pDevice->SpbController != WDF_NO_HANDLE)
+ {
+ WdfObjectDelete(pDevice->SpbController);
+ pDevice->SpbController = WDF_NO_HANDLE;
+ }
+
+ if (pDevice->SpbRequest != WDF_NO_HANDLE)
+ {
+ WdfObjectDelete(pDevice->SpbRequest);
+ pDevice->SpbRequest = WDF_NO_HANDLE;
+ }
+
+ if (pDevice->InputMemory != WDF_NO_HANDLE)
+ {
+ WdfObjectDelete(pDevice->InputMemory);
+ pDevice->InputMemory = WDF_NO_HANDLE;
+ }
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+OnFileCleanup(
+ _In_ WDFFILEOBJECT FileObject
+ )
+/*++
+
+ Routine Description:
+
+ This routine is called before a device is stopped.
+
+ Arguments:
+
+ FileObject - a handle to the framework file object
+
+ Return Value:
+
+ None
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ WDFDEVICE device;
+ PDEVICE_CONTEXT pDevice;
+
+ device = WdfFileObjectGetDevice(FileObject);
+ pDevice = GetDeviceContext(device);
+
+ //
+ // The client app has closed the device handle,
+ // make sure the SPB target is closed.
+ //
+
+ SpbPeripheralClose(pDevice);
+
+ //
+ // Signal the ISR wait event just
+ // in case the callback is stalled.
+ //
+
+ if (pDevice->Interrupt != nullptr)
+ {
+ KeSetEvent(
+ &pDevice->IsrWaitEvent,
+ IO_NO_INCREMENT,
+ FALSE);
+ }
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+}
+
+VOID
+OnTopLevelIoDefault(
+ _In_ WDFQUEUE FxQueue,
+ _In_ WDFREQUEST FxRequest
+ )
+/*++
+
+ Routine Description:
+
+ Accepts all incoming requests and pends or forwards appropriately.
+
+ Arguments:
+
+ FxQueue - Handle to the framework queue object that is associated with the
+ I/O request.
+ FxRequest - Handle to a framework request object.
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(FxQueue);
+
+ WDFDEVICE device;
+ PDEVICE_CONTEXT pDevice;
+ WDF_REQUEST_PARAMETERS params;
+ NTSTATUS status;
+
+ device = WdfIoQueueGetDevice(FxQueue);
+ pDevice = GetDeviceContext(device);
+
+ WDF_REQUEST_PARAMETERS_INIT(&params);
+
+ WdfRequestGetParameters(FxRequest, &params);
+
+ if ((params.Type == WdfRequestTypeDeviceControl) &&
+ (params.Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_SPBTESTTOOL_WAIT_ON_INTERRUPT))
+ {
+ SpbPeripheralWaitOnInterrupt(pDevice, FxRequest);
+ }
+ else
+ {
+ status = WdfRequestForwardToIoQueue(FxRequest, pDevice->SpbQueue);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_SPBAPI,
+ "Failed to forward WDFREQUEST %p to SPB queue %p - %!STATUS!",
+ FxRequest,
+ pDevice->SpbQueue,
+ status);
+
+ WdfRequestComplete(FxRequest, status);
+ }
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+OnIoRead (
+ _In_ WDFQUEUE FxQueue,
+ _In_ WDFREQUEST FxRequest,
+ _In_ size_t Length
+ )
+/*++
+
+ Routine Description:
+
+ Performs read from the toaster device. This event is called when the
+ framework receives IRP_MJ_READ requests.
+
+ Arguments:
+
+ FxQueue - Handle to the framework queue object that is associated with the
+ I/O request.
+ FxRequest - Handle to a framework request object.
+ Length - Length of the data buffer associated with the request.
+ By default, the queue does not dispatch zero length read & write
+ requests to the driver and instead to complete such requests with
+ status success. So we will never get a zero length request.
+
+ Return Value:
+
+ None.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(Length);
+
+ WDFDEVICE device;
+ PDEVICE_CONTEXT pDevice;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Read request %p received",
+ FxRequest);
+
+ device = WdfIoQueueGetDevice(FxQueue);
+ pDevice = GetDeviceContext(device);
+
+ //
+ // Send the read request.
+ //
+
+ SpbPeripheralRead(pDevice,FxRequest);
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+OnIoWrite (
+ _In_ WDFQUEUE FxQueue,
+ _In_ WDFREQUEST FxRequest,
+ _In_ size_t Length
+ )
+/*++
+
+Routine Description:
+
+ Performs write to the toaster device. This event is called when the
+ framework receives IRP_MJ_WRITE requests.
+
+Arguments:
+
+ Queue - Handle to the framework queue object that is associated
+ with the I/O request.
+ Request - Handle to a framework request object.
+ Lenght - Length of the data buffer associated with the request.
+ The default property of the queue is to not dispatch
+ zero lenght read & write requests to the driver and
+ complete is with status success. So we will never get
+ a zero length request.
+
+Return Value:
+
+ None
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ UNREFERENCED_PARAMETER(Length);
+
+ WDFDEVICE device;
+ PDEVICE_CONTEXT pDevice;
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Write request %p received",
+ FxRequest);
+
+ device = WdfIoQueueGetDevice(FxQueue);
+ pDevice = GetDeviceContext(device);
+
+ //
+ // Send the write request.
+ //
+
+ SpbPeripheralWrite(pDevice,FxRequest);
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+VOID
+OnIoDeviceControl(
+ _In_ WDFQUEUE FxQueue,
+ _In_ WDFREQUEST FxRequest,
+ _In_ size_t OutputBufferLength,
+ _In_ size_t InputBufferLength,
+ _In_ ULONG IoControlCode
+ )
+/*++
+Routine Description:
+
+ This event is called when the framework receives IRP_MJ_DEVICE_CONTROL
+ requests from the system.
+
+Arguments:
+
+ FxQueue - Handle to the framework queue object that is associated
+ with the I/O request.
+ FxRequest - Handle to a framework request object.
+ OutputBufferLength - length of the request's output buffer,
+ if an output buffer is available.
+ InputBufferLength - length of the request's input buffer,
+ if an input buffer is available.
+ IoControlCode - the driver-defined or system-defined I/O control code
+ (IOCTL) that is associated with the request.
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ WDFDEVICE device;
+ PDEVICE_CONTEXT pDevice;
+ BOOLEAN fSync = FALSE;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ UNREFERENCED_PARAMETER(OutputBufferLength);
+ UNREFERENCED_PARAMETER(InputBufferLength);
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "DeviceIoControl request %p received with IOCTL=%lu",
+ FxRequest,
+ IoControlCode);
+
+ device = WdfIoQueueGetDevice(FxQueue);
+ pDevice = GetDeviceContext(device);
+
+ //
+ // Translate the test IOCTL into the appropriate
+ // SPB API method. Open and close are completed
+ // synchronously.
+ //
+
+ switch (IoControlCode)
+ {
+ case IOCTL_SPBTESTTOOL_OPEN:
+ fSync = TRUE;
+ status = SpbPeripheralOpen(pDevice);
+ break;
+
+ case IOCTL_SPBTESTTOOL_CLOSE:
+ fSync = TRUE;
+ status = SpbPeripheralClose(pDevice);
+ break;
+
+ case IOCTL_SPBTESTTOOL_LOCK:
+ SpbPeripheralLock(pDevice, FxRequest);
+ break;
+
+ case IOCTL_SPBTESTTOOL_UNLOCK:
+ SpbPeripheralUnlock(pDevice, FxRequest);
+ break;
+
+ case IOCTL_SPBTESTTOOL_LOCK_CONNECTION:
+ SpbPeripheralLockConnection(pDevice, FxRequest);
+ break;
+
+ case IOCTL_SPBTESTTOOL_UNLOCK_CONNECTION:
+ SpbPeripheralUnlockConnection(pDevice, FxRequest);
+ break;
+
+ case IOCTL_SPBTESTTOOL_WRITEREAD:
+ SpbPeripheralWriteRead(pDevice, FxRequest);
+ break;
+
+ case IOCTL_SPBTESTTOOL_FULL_DUPLEX:
+ SpbPeripheralFullDuplex(pDevice, FxRequest);
+ break;
+
+ case IOCTL_SPBTESTTOOL_SIGNAL_INTERRUPT:
+ SpbPeripheralSignalInterrupt(pDevice, FxRequest);
+ break;
+
+ default:
+ fSync = TRUE;
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_SPBAPI,
+ "Request %p received with unexpected IOCTL=%lu",
+ FxRequest,
+ IoControlCode);
+ }
+
+ //
+ // Complete the request if necessary.
+ //
+
+ if (fSync)
+ {
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Completing request %p with %!STATUS!",
+ FxRequest,
+ status);
+
+ WdfRequestComplete(FxRequest, status);
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+}
+
+BOOLEAN
+OnInterruptIsr(
+ _In_ WDFINTERRUPT FxInterrupt,
+ _In_ ULONG MessageID
+ )
+/*++
+
+ Routine Description:
+
+ This routine responds to interrupts generated by the H/W.
+ It then waits indefinitely for the user to signal that
+ the interrupt has been acknowledged, allowing the ISR to
+ return. This ISR is called at PASSIVE_LEVEL.
+
+ Arguments:
+
+ Interrupt - a handle to a framework interrupt object
+ MessageID - message number identifying the device's
+ hardware interrupt message (if using MSI)
+
+ Return Value:
+
+ TRUE if interrupt recognized.
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_SPBAPI);
+
+ BOOLEAN fInterruptRecognized = TRUE;
+ BOOLEAN fNotificationSent;
+ WDFDEVICE device;
+ PDEVICE_CONTEXT pDevice;
+
+ UNREFERENCED_PARAMETER(MessageID);
+
+ device = WdfInterruptGetDevice(FxInterrupt);
+ pDevice = GetDeviceContext(device);
+
+ //
+ // Notify the app that an interrupt has occurred.
+ //
+
+ fNotificationSent = SpbPeripheralInterruptNotify(pDevice);
+
+ if (fNotificationSent)
+ {
+ //
+ // Stall in ISR until acknowledged by user.
+ //
+ // Note: In a 'real' driver, the ISR should directly
+ // acknowledge the interrupt and then queue
+ // a workitem to carry out any additional
+ // processing. The ISR should never call
+ // KeWaitForSingleObject as done below.
+ //
+
+ Trace(
+ TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_SPBAPI,
+ "Stalling in ISR until continue command received");
+
+ KeClearEvent(&pDevice->IsrWaitEvent);
+
+ KeWaitForSingleObject(
+ &pDevice->IsrWaitEvent,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+ }
+ else
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_SPBAPI,
+ "Interrupt detected, but failed to send notification, ignoring");
+ }
+
+ FuncExit(TRACE_FLAG_SPBAPI);
+
+ return fInterruptRecognized;
+}
diff --git a/spb/SpbTestTool/sys/device.h b/spb/SpbTestTool/sys/device.h
new file mode 100644
index 00000000..40ec9904
--- /dev/null
+++ b/spb/SpbTestTool/sys/device.h
@@ -0,0 +1,43 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ device.h
+
+Abstract:
+
+ This module contains the function definitions for
+ the WDF device.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _DEVICE_H_
+#define _DEVICE_H_
+
+//
+// WDF event callbacks.
+//
+
+EVT_WDF_DEVICE_PREPARE_HARDWARE OnPrepareHardware;
+EVT_WDF_DEVICE_RELEASE_HARDWARE OnReleaseHardware;
+EVT_WDF_DEVICE_D0_ENTRY OnD0Entry;
+EVT_WDF_DEVICE_D0_EXIT OnD0Exit;
+
+EVT_WDF_FILE_CLEANUP OnFileCleanup;
+
+EVT_WDF_IO_QUEUE_IO_DEFAULT OnTopLevelIoDefault;
+EVT_WDF_IO_QUEUE_IO_READ OnIoRead;
+EVT_WDF_IO_QUEUE_IO_WRITE OnIoWrite;
+EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL OnIoDeviceControl;
+
+EVT_WDF_INTERRUPT_ISR OnInterruptIsr;
+
+#endif \ No newline at end of file
diff --git a/spb/SpbTestTool/sys/driver.cpp b/spb/SpbTestTool/sys/driver.cpp
new file mode 100644
index 00000000..253a8858
--- /dev/null
+++ b/spb/SpbTestTool/sys/driver.cpp
@@ -0,0 +1,414 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ driver.cpp
+
+Abstract:
+
+ This module contains the WDF driver initialization
+ functions for the peripheral driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#include "internal.h"
+#include "driver.h"
+#include "device.h"
+#include "ntstrsafe.h"
+
+#include "driver.tmh"
+
+NTSTATUS
+#pragma prefast(suppress:__WARNING_DRIVER_FUNCTION_TYPE, "thanks, i know this already")
+DriverEntry(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_ PUNICODE_STRING RegistryPath
+ )
+{
+ WDF_DRIVER_CONFIG driverConfig;
+ WDF_OBJECT_ATTRIBUTES driverAttributes;
+
+ WDFDRIVER fxDriver;
+
+ NTSTATUS status;
+
+ WPP_INIT_TRACING(DriverObject, RegistryPath);
+
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ WDF_DRIVER_CONFIG_INIT(&driverConfig, OnDeviceAdd);
+ driverConfig.DriverPoolTag = SPBT_POOL_TAG;
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&driverAttributes);
+ driverAttributes.EvtCleanupCallback = OnDriverCleanup;
+
+ status = WdfDriverCreate(
+ DriverObject,
+ RegistryPath,
+ &driverAttributes,
+ &driverConfig,
+ &fxDriver);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error creating WDF driver object - %!STATUS!",
+ status);
+
+ goto exit;
+ }
+
+ Trace(
+ TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_WDFLOADING,
+ "Created WDF driver object");
+
+exit:
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
+
+VOID
+OnDriverCleanup(
+ _In_ WDFOBJECT Object
+ )
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ UNREFERENCED_PARAMETER(Object);
+
+ WPP_CLEANUP(nullptr);
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+}
+
+NTSTATUS
+OnDeviceAdd(
+ _In_ WDFDRIVER FxDriver,
+ _Inout_ PWDFDEVICE_INIT FxDeviceInit
+ )
+/*++
+
+ Routine Description:
+
+ This routine creates the device object for an SPB
+ controller and the device's child objects.
+
+ Arguments:
+
+ FxDriver - the WDF driver object handle
+ FxDeviceInit - information about the PDO that we are loading on
+
+ Return Value:
+
+ Status
+
+--*/
+{
+ FuncEntry(TRACE_FLAG_WDFLOADING);
+
+ PDEVICE_CONTEXT pDevice;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(FxDriver);
+
+ //
+ // Setup PNP/Power callbacks.
+ //
+
+ {
+ WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks;
+ WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks);
+
+ pnpCallbacks.EvtDevicePrepareHardware = OnPrepareHardware;
+ pnpCallbacks.EvtDeviceReleaseHardware = OnReleaseHardware;
+ pnpCallbacks.EvtDeviceD0Entry = OnD0Entry;
+ pnpCallbacks.EvtDeviceD0Exit = OnD0Exit;
+
+ WdfDeviceInitSetPnpPowerEventCallbacks(FxDeviceInit, &pnpCallbacks);
+ }
+
+ //
+ // Prepare for file object handling.
+ //
+
+ {
+ WDF_FILEOBJECT_CONFIG fileObjectConfig;
+
+ WDF_FILEOBJECT_CONFIG_INIT(
+ &fileObjectConfig,
+ nullptr,
+ nullptr,
+ OnFileCleanup);
+
+ WDF_OBJECT_ATTRIBUTES fileObjectAttributes;
+ WDF_OBJECT_ATTRIBUTES_INIT(&fileObjectAttributes);
+
+ WdfDeviceInitSetFileObjectConfig(
+ FxDeviceInit,
+ &fileObjectConfig,
+ &fileObjectAttributes);
+ }
+
+ //
+ // Set request attributes.
+ //
+
+ {
+ WDF_OBJECT_ATTRIBUTES attributes;
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
+ &attributes,
+ REQUEST_CONTEXT);
+
+ WdfDeviceInitSetRequestAttributes(FxDeviceInit, &attributes);
+ }
+
+ //
+ // Create the device.
+ //
+
+ {
+ WDFDEVICE fxDevice;
+ WDF_OBJECT_ATTRIBUTES deviceAttributes;
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
+
+ status = WdfDeviceCreate(
+ &FxDeviceInit,
+ &deviceAttributes,
+ &fxDevice);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error creating WDFDEVICE - %!STATUS!",
+ status);
+
+ goto exit;
+ }
+
+ pDevice = GetDeviceContext(fxDevice);
+ NT_ASSERT(pDevice != nullptr);
+
+ pDevice->FxDevice = fxDevice;
+ }
+
+ //
+ // Ensure device is disable-able
+ //
+
+ {
+ WDF_DEVICE_STATE deviceState;
+ WDF_DEVICE_STATE_INIT(&deviceState);
+
+ deviceState.NotDisableable = WdfFalse;
+ WdfDeviceSetDeviceState(pDevice->FxDevice, &deviceState);
+ }
+
+ //
+ // Create queues to handle IO
+ //
+
+ {
+ WDF_IO_QUEUE_CONFIG queueConfig;
+ WDFQUEUE queue;
+
+ //
+ // Top-level queue
+ //
+
+ WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
+ &queueConfig,
+ WdfIoQueueDispatchParallel);
+
+ queueConfig.EvtIoDefault = OnTopLevelIoDefault;
+ queueConfig.PowerManaged = WdfFalse;
+
+ status = WdfIoQueueCreate(
+ pDevice->FxDevice,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &queue
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error creating top-level IO queue - %!STATUS!",
+ status);
+
+ goto exit;
+ }
+
+ //
+ // Sequential SPB queue
+ //
+
+ WDF_IO_QUEUE_CONFIG_INIT(
+ &queueConfig,
+ WdfIoQueueDispatchSequential);
+
+ queueConfig.EvtIoRead = OnIoRead;
+ queueConfig.EvtIoWrite = OnIoWrite;
+ queueConfig.EvtIoDeviceControl = OnIoDeviceControl;
+ queueConfig.PowerManaged = WdfFalse;
+
+ status = WdfIoQueueCreate(
+ pDevice->FxDevice,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &pDevice->SpbQueue
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error creating SPB IO queue - %!STATUS!",
+ status);
+
+ goto exit;
+ }
+ }
+
+ //
+ // Create a symbolic link.
+ //
+
+ {
+ DECLARE_UNICODE_STRING_SIZE(symbolicLinkName, 128);
+
+ status = RtlUnicodeStringPrintf(
+ &symbolicLinkName, L"%ws",
+ SPBTESTTOOL_SYMBOLIC_NAME);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error creating symbolic link string for device "
+ "- %!STATUS!",
+ status);
+
+ goto exit;
+ }
+
+ status = WdfDeviceCreateSymbolicLink(
+ pDevice->FxDevice,
+ &symbolicLinkName);
+
+ if (!NT_SUCCESS(status))
+ {
+ Trace(
+ TRACE_LEVEL_ERROR,
+ TRACE_FLAG_WDFLOADING,
+ "Error creating symbolic link for device "
+ "- %!STATUS!",
+ status);
+
+ goto exit;
+ }
+ }
+
+ //
+ // Retrieve registry settings.
+ //
+
+ {
+ WDFKEY key = NULL;
+ WDFKEY subkey = NULL;
+ ULONG connectInterrupt = 0;
+ NTSTATUS settingStatus;
+
+ DECLARE_CONST_UNICODE_STRING(subkeyName, L"Settings");
+ DECLARE_CONST_UNICODE_STRING(connectInterruptName, L"ConnectInterrupt");
+
+ settingStatus = WdfDeviceOpenRegistryKey(
+ pDevice->FxDevice,
+ PLUGPLAY_REGKEY_DEVICE,
+ KEY_READ,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &key);
+
+ if (!NT_SUCCESS(settingStatus))
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_WDFLOADING,
+ "Error opening device registry key - %!STATUS!",
+ settingStatus);
+ }
+
+ if (NT_SUCCESS(settingStatus))
+ {
+ settingStatus = WdfRegistryOpenKey(
+ key,
+ &subkeyName,
+ KEY_READ,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &subkey);
+
+ if (!NT_SUCCESS(settingStatus))
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_WDFLOADING,
+ "Error opening registry subkey for 'Settings' - %!STATUS!",
+ settingStatus);
+ }
+ }
+
+ if (NT_SUCCESS(settingStatus))
+ {
+
+ settingStatus = WdfRegistryQueryULong(
+ subkey,
+ &connectInterruptName,
+ &connectInterrupt);
+
+ if (!NT_SUCCESS(settingStatus))
+ {
+ Trace(
+ TRACE_LEVEL_WARNING,
+ TRACE_FLAG_WDFLOADING,
+ "Error querying registry value for 'ConnectInterrupt' - %!STATUS!",
+ settingStatus);
+ }
+ }
+
+ if (key != NULL)
+ {
+ WdfRegistryClose(key);
+ }
+
+ if (subkey != NULL)
+ {
+ WdfRegistryClose(subkey);
+ }
+
+ pDevice->ConnectInterrupt = (connectInterrupt == 1);
+ }
+
+exit:
+
+ FuncExit(TRACE_FLAG_WDFLOADING);
+
+ return status;
+}
diff --git a/spb/SpbTestTool/sys/driver.h b/spb/SpbTestTool/sys/driver.h
new file mode 100644
index 00000000..1029377c
--- /dev/null
+++ b/spb/SpbTestTool/sys/driver.h
@@ -0,0 +1,36 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ driver.h
+
+Abstract:
+
+ This module contains the function definitions for
+ the WDF driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _DRIVER_H_
+#define _DRIVER_H_
+
+extern "C"
+
+NTSTATUS
+DriverEntry(
+ _In_ PDRIVER_OBJECT pDriverObject,
+ _In_ PUNICODE_STRING pRegistryPath
+ );
+
+EVT_WDF_DRIVER_DEVICE_ADD OnDeviceAdd;
+EVT_WDF_OBJECT_CONTEXT_CLEANUP OnDriverCleanup;
+
+#endif
diff --git a/spb/SpbTestTool/sys/internal.h b/spb/SpbTestTool/sys/internal.h
new file mode 100644
index 00000000..7e40560b
--- /dev/null
+++ b/spb/SpbTestTool/sys/internal.h
@@ -0,0 +1,144 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ internal.h
+
+Abstract:
+
+ This module contains the common internal type and function
+ definitions for the SPB peripheral driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _INTERNAL_H_
+#define _INTERNAL_H_
+
+#pragma warning(push)
+#pragma warning(disable:4512)
+#pragma warning(disable:4480)
+
+#define SPBT_POOL_TAG ((ULONG) 'TBPS')
+
+/////////////////////////////////////////////////
+//
+// Common includes.
+//
+/////////////////////////////////////////////////
+
+#include <ntddk.h>
+#include <wdm.h>
+#include <wdf.h>
+#include <ntstrsafe.h>
+
+#include "spb.h"
+#include "spbtestioctl.h"
+
+#define RESHUB_USE_HELPER_ROUTINES
+#include "reshub.h"
+
+#include "trace.h"
+
+//
+// Forward Declarations
+//
+
+typedef struct _DEVICE_CONTEXT DEVICE_CONTEXT, *PDEVICE_CONTEXT;
+typedef struct _REQUEST_CONTEXT REQUEST_CONTEXT, *PREQUEST_CONTEXT;
+
+struct _DEVICE_CONTEXT
+{
+ //
+ // Handle back to the WDFDEVICE
+ //
+
+ WDFDEVICE FxDevice;
+
+ //
+ // Handle to the sequential SPB queue
+ //
+
+ WDFQUEUE SpbQueue;
+
+ //
+ // Connection ID for SPB peripheral
+ //
+
+ LARGE_INTEGER PeripheralId;
+
+ //
+ // Interrupt object and wait event
+ //
+
+ WDFINTERRUPT Interrupt;
+ KEVENT IsrWaitEvent;
+
+ //
+ // Setting indicating whether the interrupt should be connected
+ //
+
+ BOOLEAN ConnectInterrupt;
+
+ //
+ // SPB controller target
+ //
+
+ WDFIOTARGET SpbController;
+
+ //
+ // SPB request object
+ //
+
+ WDFREQUEST SpbRequest;
+
+ //
+ // Input memory for request. Valid while request in progress.
+ //
+
+ WDFMEMORY InputMemory;
+
+ //
+ // Client request object
+ //
+
+ WDFREQUEST ClientRequest;
+
+ //
+ // WaitOnInterrupt request object
+ //
+
+ WDFREQUEST WaitOnInterruptRequest;
+};
+
+struct _REQUEST_CONTEXT
+{
+ //
+ // Associated framework device object
+ //
+
+ WDFDEVICE FxDevice;
+
+ //
+ // Variables to track write length for a sequence request.
+ // There are needed to complete the client request with
+ // correct bytesReturned value.
+ //
+
+ BOOLEAN IsSpbSequenceRequest;
+ ULONG_PTR SequenceWriteLength;
+};
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, GetDeviceContext);
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(REQUEST_CONTEXT, GetRequestContext);
+
+#pragma warning(pop)
+
+#endif // _INTERNAL_H_
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);
+}
diff --git a/spb/SpbTestTool/sys/peripheral.h b/spb/SpbTestTool/sys/peripheral.h
new file mode 100644
index 00000000..fc1fc806
--- /dev/null
+++ b/spb/SpbTestTool/sys/peripheral.h
@@ -0,0 +1,104 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ peripheral.h
+
+Abstract:
+
+ This module contains the function definitions for
+ interaction with the SPB API.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _PERIPHERAL_H_
+#define _PERIPHERAL_H_
+
+EVT_WDF_REQUEST_COMPLETION_ROUTINE SpbPeripheralOnCompletion;
+EVT_WDF_REQUEST_CANCEL SpbPeripheralOnCancel;
+
+EVT_WDF_REQUEST_CANCEL SpbPeripheralOnWaitOnInterruptCancel;
+
+NTSTATUS
+SpbPeripheralOpen(
+ _In_ PDEVICE_CONTEXT pDevice);
+
+NTSTATUS
+SpbPeripheralClose(
+ _In_ PDEVICE_CONTEXT pDevice);
+
+VOID
+SpbPeripheralLock(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralUnlock(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralLockConnection(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralUnlockConnection(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralRead(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralWrite(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralWriteRead(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralFullDuplex(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralSignalInterrupt(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+VOID
+SpbPeripheralWaitOnInterrupt(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST FxRequest);
+
+BOOLEAN
+SpbPeripheralInterruptNotify(
+ _In_ PDEVICE_CONTEXT pDevice);
+
+NTSTATUS
+SpbPeripheralSendRequest(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ WDFREQUEST SpbRequest,
+ _In_ WDFREQUEST ClientRequest);
+
+VOID
+SpbPeripheralCompleteRequestPair(
+ _In_ PDEVICE_CONTEXT pDevice,
+ _In_ NTSTATUS status,
+ _In_ ULONG_PTR bytesCompleted);
+
+#endif // _PERIPHERAL_H_
diff --git a/spb/SpbTestTool/sys/resource.rc b/spb/SpbTestTool/sys/resource.rc
new file mode 100644
index 00000000..24dd5368
--- /dev/null
+++ b/spb/SpbTestTool/sys/resource.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR "SPB Test Tool Peripheral Driver"
+#define VER_INTERNALNAME_STR "SpbTestTool.sys"
+#define VER_ORIGINALFILENAME_STR "SpbTestTool.sys"
+
+#include "common.ver"
diff --git a/spb/SpbTestTool/sys/spbtestioctl.h b/spb/SpbTestTool/sys/spbtestioctl.h
new file mode 100644
index 00000000..5a0f7b7e
--- /dev/null
+++ b/spb/SpbTestTool/sys/spbtestioctl.h
@@ -0,0 +1,50 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ spbtestioctl.h
+
+Abstract:
+
+ This module contains the public device path names and
+ IOCTL definitions for the SpbTestTool.
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _SPBTESTIOCTL_H_
+#define _SPBTESTIOCTL_H_
+
+//
+// Device path names
+//
+
+#define SPBTESTTOOL_NAME L"SPBTESTTOOL"
+
+#define SPBTESTTOOL_SYMBOLIC_NAME L"\\DosDevices\\" SPBTESTTOOL_NAME
+#define SPBTESTTOOL_USERMODE_PATH L"\\\\.\\" SPBTESTTOOL_NAME
+#define SPBTESTTOOL_USERMODE_PATH_SIZE sizeof(SPBTESTTOOL_USERMODE_PATH)
+
+//
+// Priavte SpbTestTool IOCTLs
+//
+
+#define FILE_DEVICE_SPB_PERIPHERAL 0x400
+
+#define IOCTL_SPBTESTTOOL_OPEN CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x700, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_CLOSE CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x701, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_LOCK CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x702, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_UNLOCK CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x703, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_WRITEREAD CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x704, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_LOCK_CONNECTION CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x705, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_UNLOCK_CONNECTION CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x706, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_SIGNAL_INTERRUPT CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x707, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_WAIT_ON_INTERRUPT CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x708, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SPBTESTTOOL_FULL_DUPLEX CTL_CODE(FILE_DEVICE_SPB_PERIPHERAL, 0x709, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#endif _SPBTESTIOCTL_H_ \ No newline at end of file
diff --git a/spb/SpbTestTool/sys/spbtesttool.asl b/spb/SpbTestTool/sys/spbtesttool.asl
new file mode 100644
index 00000000..fcf3c9ac
--- /dev/null
+++ b/spb/SpbTestTool/sys/spbtesttool.asl
@@ -0,0 +1,26 @@
+//
+// Test peripheral device node
+//
+Device(SPBT)
+{
+ Name(_HID, "SpbTestTool")
+ Name(_UID, 1)
+ Method(_CRS, 0x0, NotSerialized)
+ {
+ Name (RBUF, ResourceTemplate ()
+ {
+ //
+ // Sample I2C and GPIO resources. Modify to match your
+ // platform's underlying controllers and connections.
+ // \_SB.I2C and \_SB.GPIO are paths to predefined I2C
+ // and GPIO controller instances.
+ //
+ // Note: SpbTestTool does not require a GPIO resource.
+ // Remove as necessary.
+ //
+ I2CSerialBus(0x1D, ControllerInitiated, 400000, AddressingMode7Bit, "\\_SB.I2C", , )
+ GpioInt(Level, ActiveHigh, Exclusive, PullDown, 0, "\\_SB.GPIO") {1}
+ })
+ Return(RBUF)
+ }
+} \ No newline at end of file
diff --git a/spb/SpbTestTool/sys/spbtesttool.inx b/spb/SpbTestTool/sys/spbtesttool.inx
new file mode 100644
index 00000000..e1655ab2
--- /dev/null
+++ b/spb/SpbTestTool/sys/spbtesttool.inx
Binary files differ
diff --git a/spb/SpbTestTool/sys/trace.h b/spb/SpbTestTool/sys/trace.h
new file mode 100644
index 00000000..00601dc8
--- /dev/null
+++ b/spb/SpbTestTool/sys/trace.h
@@ -0,0 +1,54 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ trace.h
+
+Abstract:
+
+ This module contains the trace definitions for the SPB
+ peripheral driver.
+
+Environment:
+
+ kernel-mode only
+
+Revision History:
+
+--*/
+
+#ifndef _TRACE_H_
+#define _TRACE_H_
+
+extern "C"
+{
+//
+// Tracing Definitions:
+//
+// Control GUID:
+// {73e3b785-f5fb-423e-94a9-56627fea9053}
+//
+
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID( \
+ SpbTestToolTraceGuid, \
+ (73e3b785,f5fb,423e,94a9,56627fea9053), \
+ WPP_DEFINE_BIT(TRACE_FLAG_WDFLOADING) \
+ WPP_DEFINE_BIT(TRACE_FLAG_SPBAPI) \
+ WPP_DEFINE_BIT(TRACE_FLAG_OTHER) \
+ )
+}
+
+#define WPP_LEVEL_FLAGS_LOGGER(level,flags) WPP_LEVEL_LOGGER(flags)
+#define WPP_LEVEL_FLAGS_ENABLED(level, flags) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= level)
+
+// begin_wpp config
+// FUNC FuncEntry{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS);
+// FUNC FuncExit{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS);
+// USEPREFIX(FuncEntry, "%!STDPREFIX! [%!FUNC!] --> entry");
+// USEPREFIX(FuncExit, "%!STDPREFIX! [%!FUNC!] <--");
+// end_wpp
+
+#endif _TRACE_H_