summaryrefslogtreecommitdiff
path: root/SPB/SkeletonI2C
diff options
context:
space:
mode:
authorWei Mao <[email protected]>2017-03-17 19:47:04 -0700
committerWei Mao <[email protected]>2017-03-17 19:47:04 -0700
commit1a3e0d580380e58bf336a242d2affc8a1e2d1ddf (patch)
treebf5d9c5b0b4cba1b81726b9f78c4d5ff5c636fea /SPB/SkeletonI2C
parentda21c8784c83c5fd614f3030323e229d6a5fb10e (diff)
Fix cases
Diffstat (limited to 'SPB/SkeletonI2C')
-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 -> 3874 bytes
-rw-r--r--SPB/SkeletonI2C/skeletoni2c.vcxproj197
-rw-r--r--SPB/SkeletonI2C/skeletoni2c.vcxproj.Filters45
18 files changed, 5082 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..029fb5bb
--- /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", "{8C1BB5BA-283E-460F-A682-4548A1DAFA59}"
+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
+ {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Debug|Win32.Build.0 = Debug|Win32
+ {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Release|Win32.ActiveCfg = Release|Win32
+ {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Release|Win32.Build.0 = Release|Win32
+ {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Debug|x64.ActiveCfg = Debug|x64
+ {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Debug|x64.Build.0 = Debug|x64
+ {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.Release|x64.ActiveCfg = Release|x64
+ {8C1BB5BA-283E-460F-A682-4548A1DAFA59}.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..7b6747e2
--- /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) MmMapIoSpaceEx(
+ res->u.Memory.Start,
+ res->u.Memory.Length,
+ PAGE_NOCACHE | PAGE_READWRITE);
+
+ 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..06d993d2
--- /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 | MdlMappingNoExecute);
+
+ 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 | MdlMappingNoExecute);
+
+ 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..1a911e9d
--- /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..0959033e
--- /dev/null
+++ b/SPB/SkeletonI2C/skeletoni2c.vcxproj
@@ -0,0 +1,197 @@
+<?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>{8C1BB5BA-283E-460F-A682-4548A1DAFA59}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{666F5885-C0E7-43DD-AD99-B596AC486945}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</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>Universal</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>Universal</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>Universal</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'" />
+ </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..cf122cb8
--- /dev/null
+++ b/SPB/SkeletonI2C/skeletoni2c.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>{1DB6B959-556C-418B-8941-89D785DB9D9D}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{AEDA4A99-AB5E-48A3-B894-E5F836E5062D}</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>{8B843BE9-9726-433C-B120-1381C41CEE1A}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{7CE1D0C2-AED3-49B7-9E48-81DB84B58616}</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>
+ <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