summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Froese <[email protected]>2016-10-19 13:25:24 -0700
committerPhilip Froese <[email protected]>2016-10-19 13:25:24 -0700
commit7995677b7883bad51cbadd6bd1d1674dbd689c38 (patch)
treebbde63ce3535c264de5202e337a86fb0606819b7
parentce06de38a6daee9815dbf7eb8ddd8f335612ccc0 (diff)
Adding UcmTcpciCxClientSample
-rw-r--r--usb/UcmTcpciCxClientSample/Alert.cpp329
-rw-r--r--usb/UcmTcpciCxClientSample/Alert.h68
-rw-r--r--usb/UcmTcpciCxClientSample/Device.cpp335
-rw-r--r--usb/UcmTcpciCxClientSample/Device.h106
-rw-r--r--usb/UcmTcpciCxClientSample/Driver.cpp161
-rw-r--r--usb/UcmTcpciCxClientSample/Driver.h45
-rw-r--r--usb/UcmTcpciCxClientSample/I2C.cpp1048
-rw-r--r--usb/UcmTcpciCxClientSample/I2C.h126
-rw-r--r--usb/UcmTcpciCxClientSample/PortControllerInterface.cpp604
-rw-r--r--usb/UcmTcpciCxClientSample/PortControllerInterface.h73
-rw-r--r--usb/UcmTcpciCxClientSample/Queue.cpp230
-rw-r--r--usb/UcmTcpciCxClientSample/Queue.h30
-rw-r--r--usb/UcmTcpciCxClientSample/README.md28
-rw-r--r--usb/UcmTcpciCxClientSample/Register.h79
-rw-r--r--usb/UcmTcpciCxClientSample/Sample.asl142
-rw-r--r--usb/UcmTcpciCxClientSample/Trace.h61
-rw-r--r--usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.infbin0 -> 3024 bytes
-rw-r--r--usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.sln40
-rw-r--r--usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.vcxproj186
-rw-r--r--usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.vcxproj.Filters72
-rw-r--r--usb/UcmTcpciCxClientSample/makefile8
-rw-r--r--usb/UcmTcpciCxClientSample/resource.rc9
22 files changed, 3780 insertions, 0 deletions
diff --git a/usb/UcmTcpciCxClientSample/Alert.cpp b/usb/UcmTcpciCxClientSample/Alert.cpp
new file mode 100644
index 00000000..b63692db
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/Alert.cpp
@@ -0,0 +1,329 @@
+/*++
+
+Module Name:
+
+ Alert.c
+
+Abstract:
+
+ This file contains functions that handle alerts from the port controller hardware.
+
+Environment:
+
+ Kernel-mode Driver Framework
+
+--*/
+
+#include "Driver.h"
+#include "alert.tmh"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text (PAGE, OnInterruptPassiveIsr)
+#endif
+
+BOOLEAN
+OnInterruptPassiveIsr(
+ _In_ WDFINTERRUPT PortControllerInterrupt,
+ _In_ ULONG MessageID
+)
+/*++
+
+Routine Description:
+
+ Per the TCPCI spec, the port controller hardware will drive the Alert pin high
+ when a hardware event occurs. This routine services such a hardware interrupt at PASSIVE_LEVEL.
+ The routine determines if an interrupt is an alert from the port controller hardware;
+ if so, it completes processing of the alert.
+
+Arguments:
+
+ Interrupt: A handle to a framework interrupt object.
+
+ MessageID: If the device is using message-signaled interrupts (MSIs), this parameter
+ is the message number that identifies the device's hardware interrupt message.
+ Otherwise, this value is 0.
+
+Return Value:
+
+ TRUE if the function services the hardware interrupt.
+ Otherwise, this function must return FALSE.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_ALERT);
+
+ UNREFERENCED_PARAMETER(MessageID);
+ PAGED_CODE();
+
+ NTSTATUS status;
+ PDEVICE_CONTEXT deviceContext;
+ ALERT_REGISTER alertRegister;
+ BOOLEAN interruptRecognized = FALSE;
+ int numAlertsProcessed = 0;
+ deviceContext = DeviceGetContext(WdfInterruptGetDevice(PortControllerInterrupt));
+
+ // Process the alerts as long as there are bits set in the alert register.
+ // Set a maximum number of alerts to process in this loop. If the hardware is messed up and we're unable
+ // to quiesce the interrupt by writing to the alert register, then we don't want to be stuck in an
+ // infinite loop.
+ while (numAlertsProcessed <= MAX_ALERTS_TO_PROCESS)
+ {
+ status = I2CReadSynchronously(deviceContext,
+ I2CRequestSourceAlertIsr,
+ ALERT,
+ &alertRegister,
+ sizeof(alertRegister));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+ // If there are no bits set in the alert register, we should not service this interrupt.
+ if (alertRegister.AsUInt16 == 0)
+ {
+ goto Exit;
+ }
+
+ // Since there are bits set in the alert register, we can safely assume that the
+ // interrupt is ours to process.
+ interruptRecognized = TRUE;
+
+ ProcessAndSendAlerts(&alertRegister, deviceContext);
+ ++numAlertsProcessed;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_ALERT);
+ return interruptRecognized;
+}
+
+void
+ProcessAndSendAlerts(
+ _In_ PALERT_REGISTER AlertRegister,
+ _In_ PDEVICE_CONTEXT DeviceContext
+)
+/*++
+
+Routine Description:
+
+ Processes the set of hardware alerts that were reported and notifies UcmTcpciCx of the alerts
+ along with the contents of relevant registers.
+
+Arguments:
+
+ AlertRegister: Pointer to alert register contents.
+
+ DeviceContext: Device's context space.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_ALERT);
+ PAGED_CODE();
+
+ NTSTATUS status;
+ size_t numAlerts = 0;
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA alertData;
+ UCMTCPCI_PORT_CONTROLLER_CC_STATUS ccStatus;
+ UCMTCPCI_PORT_CONTROLLER_POWER_STATUS powerStatus;
+ UCMTCPCI_PORT_CONTROLLER_FAULT_STATUS faultStatus;
+ UCMTCPCI_PORT_CONTROLLER_RECEIVE_BUFFER receiveBuffer;
+
+ // UcmTcpciCx expects the information on all of the alerts firing presently.
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA hardwareAlerts[MAX_ALERTS];
+
+ status = STATUS_SUCCESS;
+
+ if (AlertRegister->CCStatus == 1)
+ {
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA_INIT(&alertData);
+ alertData.AlertType = UcmTcpciPortControllerAlertCCStatus;
+
+ // We must read the CC status register and send the contents to
+ // UcmTcpciCx along with the CC status alert.
+ status = I2CReadSynchronously(DeviceContext,
+ I2CRequestSourceAlertIsr,
+ CC_STATUS,
+ &ccStatus,
+ sizeof(ccStatus));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ alertData.CCStatus = ccStatus;
+ hardwareAlerts[numAlerts] = alertData;
+ ++numAlerts;
+ }
+
+ if (AlertRegister->PowerStatus == 1)
+ {
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA_INIT(&alertData);
+ alertData.AlertType = UcmTcpciPortControllerAlertPowerStatus;
+
+ // We must read the power status register and send the contents to
+ // UcmTcpciCx along with the power status alert.
+ status = I2CReadSynchronously(DeviceContext,
+ I2CRequestSourceAlertIsr,
+ POWER_STATUS,
+ &powerStatus,
+ sizeof(powerStatus));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ alertData.PowerStatus = powerStatus;
+ hardwareAlerts[numAlerts] = alertData;
+ ++numAlerts;
+ }
+
+ if (AlertRegister->Fault == 1)
+ {
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA_INIT(&alertData);
+ alertData.AlertType = UcmTcpciPortControllerAlertFault;
+
+ // We must read the fault status register and send the contents to
+ // UcmTcpciCx along with the fault alert.
+ status = I2CReadSynchronously(DeviceContext,
+ I2CRequestSourceAlertIsr,
+ FAULT_STATUS,
+ &faultStatus,
+ sizeof(faultStatus));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ alertData.FaultStatus = faultStatus;
+ hardwareAlerts[numAlerts] = alertData;
+ ++numAlerts;
+
+ // Clear FAULT_STATUS Register.
+
+ // Mask reserved bit 7 in TCPCI Rev 1.0 Ver 1.0 only, see spec section 4.4.6.3
+ faultStatus.AsUInt8 &= 0x7F;
+
+ status = I2CWriteSynchronously(DeviceContext,
+ I2CRequestSourceAlertIsr,
+ FAULT_STATUS,
+ &faultStatus,
+ sizeof(faultStatus));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+ }
+
+ if (AlertRegister->ReceiveSOPMessageStatus == 1)
+ {
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA_INIT(&alertData);
+ alertData.AlertType = UcmTcpciPortControllerAlertReceiveSOPMessageStatus;
+
+ // We must read the receive buffer register and send the contents to
+ // UcmTcpciCx along with the receive SOP alert.
+ status = I2CReadSynchronously(DeviceContext,
+ I2CRequestSourceAlertIsr,
+ RECEIVE_BUFFER,
+ &receiveBuffer,
+ sizeof(receiveBuffer));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ alertData.ReceiveBuffer = &receiveBuffer;
+ hardwareAlerts[numAlerts] = alertData;
+ ++numAlerts;
+ }
+
+ // The remainder of the alert types do not require us to provide any extra
+ // information to UcmTcpciCx.
+ if (AlertRegister->ReceivedHardReset == 1)
+ {
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA_INIT(&alertData);
+ alertData.AlertType = UcmTcpciPortControllerAlertReceivedHardReset;
+ hardwareAlerts[numAlerts] = alertData;
+ ++numAlerts;
+ }
+
+ if (AlertRegister->RxBufferOverflow == 1)
+ {
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA_INIT(&alertData);
+ alertData.AlertType = UcmTcpciPortControllerAlertRxBufferOverflow;
+ hardwareAlerts[numAlerts] = alertData;
+ ++numAlerts;
+ }
+
+ if (AlertRegister->TransmitSOPMessageDiscarded == 1)
+ {
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA_INIT(&alertData);
+ alertData.AlertType = UcmTcpciPortControllerAlertTransmitSOPMessageDiscarded;
+ hardwareAlerts[numAlerts] = alertData;
+ ++numAlerts;
+ }
+
+ if (AlertRegister->TransmitSOPMessageFailed == 1)
+ {
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA_INIT(&alertData);
+ alertData.AlertType = UcmTcpciPortControllerAlertTransmitSOPMessageFailed;
+ hardwareAlerts[numAlerts] = alertData;
+ ++numAlerts;
+ }
+
+ if (AlertRegister->TransmitSOPMessageSuccessful == 1)
+ {
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA_INIT(&alertData);
+ alertData.AlertType = UcmTcpciPortControllerAlertTransmitSOPMessageSuccessful;
+ hardwareAlerts[numAlerts] = alertData;
+ ++numAlerts;
+ }
+
+ if (AlertRegister->VbusSinkDisconnectDetected == 1)
+ {
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA_INIT(&alertData);
+ alertData.AlertType = UcmTcpciPortControllerAlertVbusSinkDisconnectDetected;
+ hardwareAlerts[numAlerts] = alertData;
+ ++numAlerts;
+ }
+
+ if (AlertRegister->VbusVoltageAlarmHi == 1)
+ {
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA_INIT(&alertData);
+ alertData.AlertType = UcmTcpciPortControllerAlertVbusVoltageAlarmHi;
+ hardwareAlerts[numAlerts] = alertData;
+ ++numAlerts;
+ }
+
+ if (AlertRegister->VbusVoltageAlarmLo == 1)
+ {
+ UCMTCPCI_PORT_CONTROLLER_ALERT_DATA_INIT(&alertData);
+ alertData.AlertType = UcmTcpciPortControllerAlertVbusVoltageAlarmLo;
+ hardwareAlerts[numAlerts] = alertData;
+ ++numAlerts;
+ }
+
+ // Only write back non-reserved bits see spec section 4.4.2
+ // TCPCI Rev 1.0 Ver 1.0: 0x0FFF
+ // TCPCI Rev 1.0 Ver 1.1: 0x8FFF
+ AlertRegister->AsUInt16 &= 0x0FFF;
+
+ // Quiesce the interrupt by writing back the alert register.
+ // Per TCPCI spec 4.4.2, the alert is cleared by writing a 1 back to the bit position it is to clear.
+ status = I2CWriteSynchronously(DeviceContext,
+ I2CRequestSourceAlertIsr,
+ ALERT,
+ AlertRegister,
+ sizeof(*AlertRegister));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+Exit:
+ if (NT_SUCCESS(status))
+ {
+ // Send the list of hardware alerts to UcmTcpciCx.
+ UcmTcpciPortControllerAlert(DeviceContext->PortController, hardwareAlerts, numAlerts);
+ }
+
+ TRACE_FUNC_EXIT(TRACE_ALERT);
+} \ No newline at end of file
diff --git a/usb/UcmTcpciCxClientSample/Alert.h b/usb/UcmTcpciCxClientSample/Alert.h
new file mode 100644
index 00000000..a480bb5a
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/Alert.h
@@ -0,0 +1,68 @@
+/*++
+
+Module Name:
+
+ Alert.h
+
+Abstract:
+
+ This file contains the declarations for alert callbacks.
+
+Environment:
+
+ Kernel-mode Driver Framework
+
+--*/
+
+#pragma once
+
+#pragma warning(push)
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+
+// Pack structure so that we can directly fill it with the contents of the alert register.
+// Without this step, the structure may have extra padding that would cause the alert register data
+// to not match up with the fields in the struct.
+#include <pshpack1.h>
+
+// Alert register as defined in the USB-Port Controller Specification R1.0.
+typedef union _ALERT_REGISTER
+{
+ UINT16 AsUInt16;
+
+ struct
+ {
+ UINT16 CCStatus : 1;
+ UINT16 PowerStatus : 1;
+ UINT16 ReceiveSOPMessageStatus : 1;
+ UINT16 ReceivedHardReset : 1;
+ UINT16 TransmitSOPMessageFailed : 1;
+ UINT16 TransmitSOPMessageDiscarded : 1;
+ UINT16 TransmitSOPMessageSuccessful : 1;
+ UINT16 VbusVoltageAlarmHi : 1;
+ UINT16 VbusVoltageAlarmLo : 1;
+ UINT16 Fault : 1;
+ UINT16 RxBufferOverflow : 1;
+ UINT16 VbusSinkDisconnectDetected : 1;
+ UINT16 : 4;
+ };
+} ALERT_REGISTER, *PALERT_REGISTER;
+
+#include <poppack.h>
+#pragma warning(pop)
+
+#define MAX_ALERTS 12
+#define MAX_ALERTS_TO_PROCESS 10
+
+EXTERN_C_START
+
+EVT_WDF_INTERRUPT_ISR
+OnInterruptPassiveIsr;
+
+void
+ProcessAndSendAlerts(
+ _In_ PALERT_REGISTER AlertRegister,
+ _In_ PDEVICE_CONTEXT DeviceContext
+);
+
+EXTERN_C_END \ No newline at end of file
diff --git a/usb/UcmTcpciCxClientSample/Device.cpp b/usb/UcmTcpciCxClientSample/Device.cpp
new file mode 100644
index 00000000..fc2330ad
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/Device.cpp
@@ -0,0 +1,335 @@
+/*++
+
+Module Name:
+
+ Device.c - Device handling events.
+
+Abstract:
+
+ This file contains the device definitions.
+
+Environment:
+
+ Kernel-mode Driver Framework
+
+--*/
+
+#include "Driver.h"
+#include "device.tmh"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text (PAGE, EvtCreateDevice)
+#pragma alloc_text (PAGE, EvtPrepareHardware)
+#pragma alloc_text (PAGE, EvtDeviceD0Entry)
+#pragma alloc_text (PAGE, EvtReleaseHardware)
+#endif
+
+NTSTATUS
+EvtCreateDevice(
+ _Inout_ PWDFDEVICE_INIT DeviceInit
+)
+/*++
+
+Routine Description:
+
+ Worker routine called to create a device and its software resources.
+
+Arguments:
+
+ DeviceInit - Pointer to an opaque init structure. Memory for this
+ structure will be freed by the framework when the WdfDeviceCreate
+ succeeds. Don't access the structure after that point.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_DEVICE);
+
+ PAGED_CODE();
+
+ NTSTATUS status;
+ WDFDEVICE device;
+ PDEVICE_CONTEXT deviceContext;
+ UCMTCPCI_DEVICE_CONFIG config;
+ WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
+ WDF_OBJECT_ATTRIBUTES attributes;
+
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT);
+
+ WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
+ pnpPowerCallbacks.EvtDevicePrepareHardware = EvtPrepareHardware;
+ pnpPowerCallbacks.EvtDeviceReleaseHardware = EvtReleaseHardware;
+ pnpPowerCallbacks.EvtDeviceD0Entry = EvtDeviceD0Entry;
+ WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
+
+ status = UcmTcpciDeviceInitInitialize(DeviceInit);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
+ "[PWDFDEVICE_INIT: 0x%p] UcmTcpciDeviceInitInitialize failed - %!STATUS!",
+ DeviceInit, status);
+ goto Exit;
+ }
+
+ status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
+ "[PWDFDEVICE_INIT: 0x%p] WdfDeviceCreate failed - %!STATUS!", DeviceInit, status);
+ goto Exit;
+ }
+
+ deviceContext = DeviceGetContext(device);
+
+ // Save the device in the context so we can access it later.
+ deviceContext->Device = device;
+
+ // Initialize platform-level device reset.
+ deviceContext->ResetAttempts = 0;
+
+ RtlZeroMemory(&deviceContext->ResetInterface, sizeof(deviceContext->ResetInterface));
+ deviceContext->ResetInterface.Size = sizeof(deviceContext->ResetInterface);
+ deviceContext->ResetInterface.Version = 1;
+
+ status = WdfFdoQueryForInterface(
+ deviceContext->Device,
+ &GUID_DEVICE_RESET_INTERFACE_STANDARD,
+ (PINTERFACE)&deviceContext->ResetInterface,
+ sizeof(deviceContext->ResetInterface),
+ 1,
+ NULL);
+
+ // The reset interface may not exist on certain environments.
+ // In this case, we will fall back to using a different reset method.
+ // Zero the reset interface and ignore the error.
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
+ "[WDFDEVICE: 0x%p] WdfFdoQueryForInterface for GUID_DEVICE_RESET_INTERFACE_STANDARD failed. Status: %!STATUS!",
+ device, status);
+ RtlZeroMemory(&deviceContext->ResetInterface, sizeof(deviceContext->ResetInterface));
+ status = STATUS_SUCCESS;
+ }
+ else
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
+ "[WDFDEVICE: 0x%p] Successfully initialized platform-level device reset.", deviceContext->Device);
+ }
+
+ // Register our device with UcmTcpciCx.
+ UCMTCPCI_DEVICE_CONFIG_INIT(&config);
+ status = UcmTcpciDeviceInitialize(device, &config);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
+ "[WDFDEVICE: 0x%p] UcmTcpciDeviceInitialize failed - %!STATUS!",
+ device, status);
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_DEVICE);
+ return status;
+}
+
+NTSTATUS
+EvtPrepareHardware(
+ _In_ WDFDEVICE Device,
+ _In_ WDFCMRESLIST ResourcesRaw,
+ _In_ WDFCMRESLIST ResourcesTranslated
+)
+/*++
+
+Routine Description:
+
+ A driver's EvtDevicePrepareHardware event callback function performs any operations
+ that are needed to make a device accessible to the driver.
+
+Arguments:
+
+ Device - A handle to a framework device object.
+
+ ResourcesRaw - A handle to a framework resource-list object that identifies the raw hardware
+ resources that the Plug and Play manager has assigned to the device.
+
+ ResourcesTranslated - A handle to a framework resource-list object that identifies the
+ translated hardware resources that the Plug and Play manager has assigned to the device.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_DEVICE);
+
+ PAGED_CODE();
+
+ NTSTATUS status;
+ PDEVICE_CONTEXT deviceContext;
+
+ deviceContext = DeviceGetContext(Device);
+
+ //// Initialize the I2C communication channel to read from/write to the hardware.
+ status = I2CInitialize(deviceContext, ResourcesRaw, ResourcesTranslated);
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = I2COpen(deviceContext);
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+Exit:
+ return status;
+}
+
+NTSTATUS EvtDeviceD0Entry(
+ _In_ WDFDEVICE Device,
+ _In_ WDF_POWER_DEVICE_STATE PreviousState
+)
+{
+ TRACE_FUNC_ENTRY(TRACE_DEVICE);
+
+ UNREFERENCED_PARAMETER(PreviousState);
+
+ PAGED_CODE();
+
+ NTSTATUS status;
+ PDEVICE_CONTEXT deviceContext;
+ UCMTCPCIPORTCONTROLLER portController = WDF_NO_HANDLE;
+ UCMTCPCI_PORT_CONTROLLER_CONFIG config;
+ UCMTCPCI_PORT_CONTROLLER_IDENTIFICATION ident;
+ UCMTCPCI_PORT_CONTROLLER_CAPABILITIES capabilities;
+
+ deviceContext = DeviceGetContext(Device);
+
+ UCMTCPCI_PORT_CONTROLLER_IDENTIFICATION_INIT(&ident);
+ UCMTCPCI_PORT_CONTROLLER_CAPABILITIES_INIT(&capabilities);
+
+ // Read device identification and capabilities from the registers.
+ REGISTER_ITEM items[] = {
+ GEN_REGISTER_ITEM(VENDOR_ID, ident.VendorId),
+ GEN_REGISTER_ITEM(PRODUCT_ID, ident.ProductId),
+ GEN_REGISTER_ITEM(DEVICE_ID, ident.DeviceId),
+ GEN_REGISTER_ITEM(USBTYPEC_REV, ident.TypeCRevisionInBcd),
+ GEN_REGISTER_ITEM(USBPD_REV_VER, ident.PDRevisionAndVersionInBcd),
+ GEN_REGISTER_ITEM(PD_INTERFACE_REV, ident.PDInterfaceRevisionAndVersionInBcd),
+ GEN_REGISTER_ITEM(DEVICE_CAPABILITIES_1, capabilities.DeviceCapabilities1),
+ GEN_REGISTER_ITEM(DEVICE_CAPABILITIES_2, capabilities.DeviceCapabilities2),
+ GEN_REGISTER_ITEM(STANDARD_INPUT_CAPABILITIES, capabilities.StandardInputCapabilities),
+ GEN_REGISTER_ITEM(STANDARD_OUTPUT_CAPABILITIES, capabilities.StandardOutputCapabilities),
+ };
+
+ status = I2CReadSynchronouslyMultiple(deviceContext,
+ I2CRequestSourceClient,
+ items,
+ _countof(items));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+
+ capabilities.IsPowerDeliveryCapable = TRUE;
+
+ UCMTCPCI_PORT_CONTROLLER_CONFIG_INIT(&config, &ident, &capabilities);
+
+ // Create a UCMTCPCIPORTCONTROLLER framework object.
+ status = UcmTcpciPortControllerCreate(Device, &config, WDF_NO_OBJECT_ATTRIBUTES, &portController);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "[WDFDEVICE: 0x%p] UcmTcpciPortControllerCreate "
+ "failed - %!STATUS!", Device, status);
+ goto Exit;
+ }
+
+ // Save the UCMTCPCIPORTCONTROLLER in our device context.
+ deviceContext = DeviceGetContext(Device);
+ deviceContext->PortController = portController;
+
+ // Set the hardware request queue for our device.
+ status = HardwareRequestQueueInitialize(Device);
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ // Direct UcmTcpciCx to start the port controller.
+ // At this point, UcmTcpciCx will assume control of USB Type-C and Power Delivery.
+ // After the port controller is started, UcmTcpciCx may start putting requests into the
+ // hardware request queue.
+ status = UcmTcpciPortControllerStart(portController);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "[UCMTCPCIPORTCONTROLLER: 0x%p]"
+ "UcmTcpciPortControllerStart failed - %!STATUS!", portController, status);
+ goto Exit;
+ }
+
+Exit:
+ if (!NT_SUCCESS(status) && (portController != WDF_NO_HANDLE))
+ {
+ WdfObjectDelete(portController);
+ deviceContext->PortController = WDF_NO_HANDLE;
+ }
+
+ TRACE_FUNC_EXIT(TRACE_DEVICE);
+ return status;
+}
+
+NTSTATUS
+EvtReleaseHardware(
+ _In_ WDFDEVICE Device,
+ _In_ WDFCMRESLIST ResourcesTranslated
+)
+/*++
+
+Routine Description:
+
+ A driver's EvtDeviceReleaseHardware event callback function performs operations
+ that are needed when a device is no longer accessible.
+
+Arguments:
+
+ Device - A handle to a framework device object.
+
+ ResourcesTranslated - A handle to a resource list object that identifies the translated
+ hardware resources that the Plug and Play manager has assigned to the device.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_DEVICE);
+
+ UNREFERENCED_PARAMETER(ResourcesTranslated);
+ PAGED_CODE();
+
+ NTSTATUS status = STATUS_SUCCESS;
+ PDEVICE_CONTEXT deviceContext;
+
+ deviceContext = DeviceGetContext(Device);
+
+ if (deviceContext->PortController != WDF_NO_HANDLE)
+ {
+ // Direct UcmTcpciCx to stop the port controller and then delete the backing object.
+ UcmTcpciPortControllerStop(deviceContext->PortController);
+ WdfObjectDelete(deviceContext->PortController);
+ deviceContext->PortController = WDF_NO_HANDLE;
+ }
+
+ // Close the I2C controller.
+ I2CClose(deviceContext);
+
+ TRACE_FUNC_EXIT(TRACE_DEVICE);
+ return status;
+}
diff --git a/usb/UcmTcpciCxClientSample/Device.h b/usb/UcmTcpciCxClientSample/Device.h
new file mode 100644
index 00000000..70ff151d
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/Device.h
@@ -0,0 +1,106 @@
+/*++
+
+Module Name:
+
+ Device.h
+
+Abstract:
+
+ This file contains the device declarations.
+
+Environment:
+
+ Kernel-mode Driver Framework
+
+--*/
+
+//
+// The I2C_REQUEST_SOURCE enum defines the type of requests that we sent to I2C controller.
+// At any time there will be at most one request per source type. For example:
+//
+// 1 - We receive an incoming request from EvtIoDeviceControl. The queue is sequential and
+// thus we'll get at most one incoming request at any time.
+// 2 - On response, we can:
+// * Either send an async request to I2C controller (SourceAsync). Its completion routine
+// will complete the parent incoming request automatically;
+// * Or send one or multiple sync requests (SourceClient). The caller needs to complete the
+// parent incoming request manually.
+// 3 - Also when I2C controller triggers an interrupt, the ISR handler can also:
+// * Send a sync request (SourceAlertIsr). The caller also needs to complete the parent
+// incoming request manually later.
+//
+enum I2C_REQUEST_SOURCE
+{
+ I2CRequestSourceAsync = 0,
+ I2CRequestSourceClient,
+ I2CRequestSourceAlertIsr,
+ I2CRequestSourceMax
+};
+
+typedef struct _DEVICE_CONTEXT
+{
+ WDFDEVICE Device;
+
+ UCMTCPCIPORTCONTROLLER PortController;
+
+ // Request that we recieved from I/O queue.
+ WDFREQUEST IncomingRequest;
+
+ // Requests that we created and sent out.
+ WDFREQUEST OutgoingRequests[I2CRequestSourceMax];
+
+ // An alias since we use this async request frequently.
+ #define I2CAsyncRequest \
+ OutgoingRequests[I2CRequestSourceAsync]
+
+ // Alert processing.
+ WDFINTERRUPT AlertInterrupt;
+
+ WDFIOTARGET I2CIoTarget;
+
+ UINT8 I2CRegisterAddress;
+
+ LARGE_INTEGER I2CConnectionId;
+
+ WDFMEMORY I2CMemory;
+
+ UINT8 I2CAsyncBuffer[I2C_BUFFER_SIZE];
+
+ // Used to perform a device reset.
+ DEVICE_RESET_INTERFACE_STANDARD ResetInterface;
+
+ UINT8 ResetAttempts;
+
+ WDFWORKITEM I2CWorkItemGetStatus;
+
+ WDFWORKITEM I2CWorkItemGetControl;
+
+} DEVICE_CONTEXT, *PDEVICE_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, DeviceGetContext)
+
+typedef struct _WORKITEM_CONTEXT
+{
+ WDFDEVICE Device;
+ WDFREQUEST Request;
+} WORKITEM_CONTEXT, *PWORKITEM_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(WORKITEM_CONTEXT, WorkitemGetContext);
+
+EXTERN_C_START
+
+NTSTATUS
+EvtCreateDevice(
+ _Inout_ PWDFDEVICE_INIT DeviceInit
+);
+
+EVT_WDF_DEVICE_PREPARE_HARDWARE
+EvtPrepareHardware;
+
+EVT_WDF_DEVICE_D0_ENTRY
+EvtDeviceD0Entry;
+
+EVT_WDF_DEVICE_RELEASE_HARDWARE
+EvtReleaseHardware;
+
+EXTERN_C_END \ No newline at end of file
diff --git a/usb/UcmTcpciCxClientSample/Driver.cpp b/usb/UcmTcpciCxClientSample/Driver.cpp
new file mode 100644
index 00000000..d533951d
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/Driver.cpp
@@ -0,0 +1,161 @@
+/*++
+
+Module Name:
+
+ driver.c
+
+Abstract:
+
+ This file contains the driver definitions.
+
+Environment:
+
+ Kernel-mode Driver Framework
+
+--*/
+
+#include "Driver.h"
+#include "driver.tmh"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text (INIT, DriverEntry)
+#pragma alloc_text (PAGE, EvtDeviceAdd)
+#pragma alloc_text (PAGE, EvtDriverContextCleanup)
+#endif
+
+NTSTATUS
+DriverEntry(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_ PUNICODE_STRING RegistryPath
+)
+/*++
+
+Routine Description:
+ DriverEntry initializes the driver and is the first routine called by the
+ system after the driver is loaded. DriverEntry specifies the other entry
+ points in the function driver, such as EvtDevice and DriverUnload.
+
+Parameters Description:
+
+ DriverObject - represents the instance of the function driver that is loaded
+ into memory. DriverEntry must initialize members of DriverObject before it
+ returns to the caller. DriverObject is allocated by the system before the
+ driver is loaded, and it is released by the system after the system unloads
+ the function driver from memory.
+
+ RegistryPath - represents the driver specific path in the Registry.
+ The function driver can use the path to store driver related data between
+ reboots. The path does not store hardware instance specific data.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ // Initialize WPP Tracing
+ WPP_INIT_TRACING(DriverObject, RegistryPath);
+ TRACE_FUNC_ENTRY(TRACE_DRIVER);
+
+ NTSTATUS status;
+ WDF_DRIVER_CONFIG config;
+ WDF_OBJECT_ATTRIBUTES attributes;
+
+ // Register a cleanup callback so that we can call WPP_CLEANUP when
+ // the framework driver object is deleted during driver unload.
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.EvtCleanupCallback = EvtDriverContextCleanup;
+
+ WDF_DRIVER_CONFIG_INIT(&config, EvtDeviceAdd);
+
+ status = WdfDriverCreate(DriverObject, RegistryPath, &attributes, &config, WDF_NO_HANDLE);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
+ "[PDRIVER_OBJECT: 0x%p] WdfDriverCreate failed - %!STATUS!", DriverObject, status);
+
+ TRACE_FUNC_EXIT(TRACE_DRIVER);
+
+ // Cleanup tracing here because EvtDriverContextCleanup will not be called
+ // as we have failed to create WDFDRIVER object itself.
+ //
+ // Please note that if your return failure from DriverEntry after the
+ // WDFDRIVER object is created successfully, you don't have to
+ // call WPP cleanup because in those cases DriverContextCleanup
+ // will be executed when the framework deletes the DriverObject.
+ WPP_CLEANUP(DriverObject);
+
+ return status;
+ }
+
+ TRACE_FUNC_EXIT(TRACE_DRIVER);
+
+ return status;
+}
+
+NTSTATUS
+EvtDeviceAdd(
+ _In_ WDFDRIVER Driver,
+ _Inout_ PWDFDEVICE_INIT DeviceInit
+)
+/*++
+Routine Description:
+
+ EvtDeviceAdd is called by the framework in response to AddDevice
+ call from the PnP manager. We create and initialize a device object to
+ represent a new instance of the device.
+
+Arguments:
+
+ Driver - Handle to a framework driver object created in DriverEntry
+
+ DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_DRIVER);
+
+ UNREFERENCED_PARAMETER(Driver);
+ PAGED_CODE();
+
+ NTSTATUS status;
+
+ status = EvtCreateDevice(DeviceInit);
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_DRIVER);
+ return status;
+}
+
+VOID
+EvtDriverContextCleanup(
+ _In_ WDFOBJECT DriverObject
+)
+/*++
+Routine Description:
+
+ Free all the resources allocated in DriverEntry.
+
+Arguments:
+
+ DriverObject - handle to a WDF Driver object.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_DRIVER);
+
+ PAGED_CODE();
+
+ TRACE_FUNC_EXIT(TRACE_DRIVER);
+
+ // Stop WPP Tracing
+ WPP_CLEANUP(WdfDriverWdmGetDriverObject((WDFDRIVER)DriverObject));
+}
diff --git a/usb/UcmTcpciCxClientSample/Driver.h b/usb/UcmTcpciCxClientSample/Driver.h
new file mode 100644
index 00000000..4e450119
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/Driver.h
@@ -0,0 +1,45 @@
+/*++
+
+Module Name:
+
+ Driver.h
+
+Abstract:
+
+ This file contains the driver declarations.
+
+Environment:
+
+ Kernel-mode Driver Framework
+
+--*/
+
+#include <ntddk.h>
+#include <wdf.h>
+#include <initguid.h>
+#include <wdmguid.h>
+#define RESHUB_USE_HELPER_ROUTINES
+#include <reshub.h>
+#include <spb.h>
+#include <UcmTcpciCx.h>
+
+#include "I2C.h"
+#include "Device.h"
+#include "Alert.h"
+#include "PortControllerInterface.h"
+#include "Queue.h"
+#include "Register.h"
+#include "Trace.h"
+
+EXTERN_C_START
+
+DRIVER_INITIALIZE
+DriverEntry;
+
+EVT_WDF_DRIVER_DEVICE_ADD
+EvtDeviceAdd;
+
+EVT_WDF_OBJECT_CONTEXT_CLEANUP
+EvtDriverContextCleanup;
+
+EXTERN_C_END
diff --git a/usb/UcmTcpciCxClientSample/I2C.cpp b/usb/UcmTcpciCxClientSample/I2C.cpp
new file mode 100644
index 00000000..5a79fced
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/I2C.cpp
@@ -0,0 +1,1048 @@
+/*++
+
+Module Name:
+
+ I2C.c
+
+Abstract:
+
+ This file contains the definitions for I2C functions and callbacks.
+
+ TODO: If your port controller hardware is not compliant with the Type-C Port Controller
+ Interface Specification in the respect that it does not use I2C,
+ you will need to modify this file accordingly.
+
+Environment:
+
+ Kernel-mode Driver Framework
+
+--*/
+
+#include "Driver.h"
+#include "I2C.tmh"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text (PAGE, I2CInitialize)
+#pragma alloc_text (PAGE, I2COpen)
+#pragma alloc_text (PAGE, I2CClose)
+#pragma alloc_text (PAGE, I2CReadSynchronously)
+#pragma alloc_text (PAGE, I2CWriteSynchronously)
+#pragma alloc_text (PAGE, I2CPerformDeviceReset)
+#endif
+
+NTSTATUS
+I2CInitialize(
+ _In_ PDEVICE_CONTEXT DeviceContext,
+ _In_ WDFCMRESLIST ResourcesRaw,
+ _In_ WDFCMRESLIST ResourcesTranslated
+)
+/*++
+
+Routine Description:
+
+ Initialize the I2C resource that provides a communications channel to the
+ port controller hardware.
+
+Arguments:
+
+ DeviceContext - Context for a framework device.
+
+ ResourcesRaw - A handle to a framework resource-list object that identifies the raw hardware
+ resources that the Plug and Play manager has assigned to the device.
+
+ ResourcesTranslated - A handle to a framework resource-list object that identifies the
+ translated hardware resources that the Plug and Play manager has assigned to the device.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_I2C);
+
+ UNREFERENCED_PARAMETER(ResourcesRaw);
+ PAGED_CODE();
+
+ NTSTATUS status = STATUS_SUCCESS;
+ WDF_INTERRUPT_CONFIG interruptConfig;
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor = nullptr;
+ ULONG interruptIndex = 0;
+ BOOLEAN connFound = FALSE;
+ BOOLEAN interruptFound = FALSE;
+ ULONG resourceCount;
+
+ // Check for I2C and Interrupt resources from the resources that PnP manager has
+ // allocated to our device.
+ resourceCount = WdfCmResourceListGetCount(ResourcesTranslated);
+
+ for (ULONG i = 0; ((connFound == FALSE) || (interruptFound == FALSE)) && (i < resourceCount); i++)
+ {
+ descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, i);
+
+ switch (descriptor->Type)
+ {
+ case CmResourceTypeConnection:
+ // Check for I2C resource
+ if (descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL &&
+ descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C)
+ {
+ DeviceContext->I2CConnectionId.LowPart = descriptor->u.Connection.IdLowPart;
+ DeviceContext->I2CConnectionId.HighPart = descriptor->u.Connection.IdHighPart;
+
+ connFound = TRUE;
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_I2C,
+ "I2C resource found with connection id: 0x%llx",
+ DeviceContext->I2CConnectionId.QuadPart);
+ }
+ break;
+
+ case CmResourceTypeInterrupt:
+ // We've found an interrupt resource.
+ interruptFound = TRUE;
+ interruptIndex = i;
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_I2C,
+ "Interrupt resource found at index: %lu", interruptIndex);
+ break;
+
+ default:
+ // We don't care about other descriptors.
+ break;
+ }
+ }
+
+ // Fail if either connection or interrupt resource was not found.
+ if (!connFound)
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "Failed finding required I2C resource. Status: %!STATUS!", status);
+
+ goto Exit;
+ }
+
+ if (!interruptFound)
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "Failed finding required interrupt resource. Status: %!STATUS!", status);
+
+ goto Exit;
+ }
+
+ // The alerts from the port controller hardware will be handled in a passive ISR.
+ // The ISR performs hardware read and write operations which block until the hardware access is complete.
+ // Waiting is unacceptable at DIRQL, so we perform our ISR at PASSIVE_LEVEL.
+ WDF_INTERRUPT_CONFIG_INIT(&interruptConfig, OnInterruptPassiveIsr, NULL);
+
+ interruptConfig.PassiveHandling = TRUE;
+ interruptConfig.InterruptTranslated = WdfCmResourceListGetDescriptor(ResourcesTranslated, interruptIndex);
+ interruptConfig.InterruptRaw = WdfCmResourceListGetDescriptor(ResourcesRaw, interruptIndex);
+
+ status = WdfInterruptCreate(
+ DeviceContext->Device,
+ &interruptConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &DeviceContext->AlertInterrupt);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "WdfInterruptCreate failed. status: %!STATUS!", status);
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_I2C);
+ return status;
+}
+
+NTSTATUS
+I2COpen(
+ _In_ PDEVICE_CONTEXT DeviceContext
+)
+/*++
+
+Routine Description:
+
+ This routine opens a handle to the I2C controller.
+
+Arguments:
+
+ DeviceContext - a pointer to the device context
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_I2C);
+
+ PAGED_CODE();
+
+ NTSTATUS status;
+ WDF_IO_TARGET_OPEN_PARAMS openParams;
+ WDF_OBJECT_ATTRIBUTES requestAttributes;
+ WDF_OBJECT_ATTRIBUTES workitemAttributes;
+ WDF_WORKITEM_CONFIG workitemConfig;
+
+ // Create the device path using the connection ID.
+ DECLARE_UNICODE_STRING_SIZE(DevicePath, RESOURCE_HUB_PATH_SIZE);
+
+ RESOURCE_HUB_CREATE_PATH_FROM_ID(
+ &DevicePath,
+ DeviceContext->I2CConnectionId.LowPart,
+ DeviceContext->I2CConnectionId.HighPart);
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_I2C,
+ "Opening handle to I2C target via %wZ", &DevicePath);
+
+ status = WdfIoTargetCreate(DeviceContext->Device, WDF_NO_OBJECT_ATTRIBUTES, &DeviceContext->I2CIoTarget);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "WdfIoTargetCreate failed - %!STATUS!", status);
+ goto Exit;
+ }
+
+ // Open a handle to the I2C controller.
+ WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
+ &openParams,
+ &DevicePath,
+ (GENERIC_READ | GENERIC_WRITE));
+
+ openParams.ShareAccess = 0;
+ openParams.CreateDisposition = FILE_OPEN;
+ openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+
+ status = WdfIoTargetOpen(DeviceContext->I2CIoTarget, &openParams);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "Failed to open I2C I/O target - %!STATUS!", status);
+ goto Exit;
+ }
+
+ // Create a WDFMEMORY object. Do call WdfMemoryAssignBuffer before use it,
+ status = WdfMemoryCreatePreallocated(
+ WDF_NO_OBJECT_ATTRIBUTES,
+ static_cast<PVOID>(&status), // initial value does not matter
+ sizeof(status),
+ &DeviceContext->I2CMemory);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "WdfMemoryCreatePreallocated failed with status %!STATUS!", status);
+ goto Exit;
+ }
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&requestAttributes);
+ requestAttributes.ParentObject = DeviceContext->I2CIoTarget;
+
+ for (ULONG i = 0; i < I2CRequestSourceMax; i++)
+ {
+ status = WdfRequestCreate(&requestAttributes, DeviceContext->I2CIoTarget, &DeviceContext->OutgoingRequests[i]);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "WdfRequestCreate failed with status %!STATUS!", status);
+ goto Exit;
+ }
+ }
+
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&workitemAttributes, WORKITEM_CONTEXT);
+ workitemAttributes.ParentObject = DeviceContext->I2CIoTarget;
+
+ WDF_WORKITEM_CONFIG_INIT(&workitemConfig, EvtWorkItemGetStatus);
+ status = WdfWorkItemCreate(&workitemConfig, &workitemAttributes, &DeviceContext->I2CWorkItemGetStatus);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "WdfWorkItemCreate failed with status %!STATUS!", status);
+ goto Exit;
+ }
+
+ WDF_WORKITEM_CONFIG_INIT(&workitemConfig, EvtWorkItemGetControl);
+ status = WdfWorkItemCreate(&workitemConfig, &workitemAttributes, &DeviceContext->I2CWorkItemGetControl);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "WdfWorkItemCreate failed with status %!STATUS!", status);
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_I2C);
+ return status;
+}
+
+void
+I2CClose(
+ _In_ PDEVICE_CONTEXT DeviceContext
+)
+/*++
+
+Routine Description:
+
+ This routine closes a handle to the I2C controller.
+
+Arguments:
+
+ DeviceContext - a pointer to the device context.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_I2C);
+
+ PAGED_CODE();
+
+ if (DeviceContext->I2CIoTarget != WDF_NO_HANDLE)
+ {
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_I2C, "Closing handle to I2C target");
+ WdfIoTargetClose(DeviceContext->I2CIoTarget);
+ }
+
+ TRACE_FUNC_EXIT(TRACE_I2C);
+}
+
+NTSTATUS
+I2CWriteAsynchronously(
+ _In_ PDEVICE_CONTEXT DeviceContext,
+ _In_ UINT8 RegisterAddress,
+ _In_reads_bytes_(Length) PVOID Data,
+ _In_ ULONG Length
+)
+/*++
+
+Routine Description:
+
+ Sends data to the I2C controller to write to the specified register
+ on the port controller hardware.
+
+ Before calling, DeviceContext->IncomingRequest should be set to a valid value. The IncomingRequest
+ will be completed automatically when writing is finished.
+
+Arguments:
+
+ DeviceContext - Context information about the device.
+
+ RegisterAddress - The I2C register address to write data to.
+
+ Data - Pointer to the data to write.
+
+ Length - Length of the data to write.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_I2C);
+
+ NTSTATUS status;
+ WDF_REQUEST_REUSE_PARAMS reuseParams;
+
+ if (Length == 0)
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C, "Parameter 'Length' cannot be 0.");
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ if (REGISTER_ADDR_SIZE + Length > sizeof(DeviceContext->I2CAsyncBuffer))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C, "Unexpected value of data length. Length: %lu. Size of buffer: %lu", Length, sizeof(DeviceContext->I2CAsyncBuffer));
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ // Reuse the preallocated WDFREQUEST for I2C.
+ WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS);
+ status = WdfRequestReuse(DeviceContext->I2CAsyncRequest, &reuseParams);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "WdfRequestReuse failed with status %!STATUS!", status);
+ goto Exit;
+ }
+
+ WdfRequestSetCompletionRoutine(DeviceContext->I2CAsyncRequest, I2COnWriteCompletion, DeviceContext);
+
+ // Combine register address and user data to write into a single buffer.
+ DeviceContext->I2CAsyncBuffer[0] = RegisterAddress;
+
+ RtlCopyMemory(&DeviceContext->I2CAsyncBuffer[REGISTER_ADDR_SIZE], Data, Length);
+
+ status = WdfMemoryAssignBuffer(
+ DeviceContext->I2CMemory,
+ static_cast<PVOID>(DeviceContext->I2CAsyncBuffer),
+ REGISTER_ADDR_SIZE + Length);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "WdfMemoryAssignBuffer failed with status %!STATUS!", status);
+ }
+
+ status = WdfIoTargetFormatRequestForWrite(
+ DeviceContext->I2CIoTarget,
+ DeviceContext->I2CAsyncRequest,
+ DeviceContext->I2CMemory,
+ NULL,
+ NULL);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "WdfIoTargetFormatRequestForWrite failed with status %!STATUS!", status);
+ goto Exit;
+ }
+
+ // Send the request to the I2C I/O Target.
+ if (WdfRequestSend(DeviceContext->I2CAsyncRequest, DeviceContext->I2CIoTarget, NULL) == FALSE)
+ {
+ status = WdfRequestGetStatus(DeviceContext->I2CAsyncRequest);
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "[WDFREQUEST: 0x%p] WdfRequestSend for I2C write failed with status %!STATUS!",
+ DeviceContext->I2CAsyncRequest, status);
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_I2C);
+ return status;
+}
+
+NTSTATUS
+I2CReadAsynchronously(
+ _In_ PDEVICE_CONTEXT DeviceContext,
+ _In_ UINT8 RegisterAddress,
+ _In_ ULONG Length
+)
+/*++
+
+Routine Description:
+
+ Asynchronously reads data from the port controller's registers over the I2C controller.
+
+ Before calling, DeviceContext->IncomingRequest should be set to a valid value. The IncomingRequest
+ will be completed automatically when reading is finished, and any data read out from the
+ controller will be stored in the output buffer of IncomingRequest.
+
+ Note: This function is not used in the sample yet. It is left here for future reference.
+
+Arguments:
+
+ DeviceContext - Context information for the port controller device.
+
+ RegisterAddress - The I2C register address from which to read data.
+
+ Length - Length of the data to read.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_I2C);
+
+ NTSTATUS status;
+ WDF_REQUEST_REUSE_PARAMS reuseParams;
+
+ // Store the address.
+ DeviceContext->I2CRegisterAddress = RegisterAddress;
+
+ if (Length == 0)
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C, "Parameter 'Length' cannot be 0.");
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ // Reuse the specified WDFREQUEST
+ WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS);
+ status = WdfRequestReuse(DeviceContext->I2CAsyncRequest, &reuseParams);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "WdfRequestReuse failed with status %!STATUS!", status);
+ goto Exit;
+ }
+
+ WdfRequestSetCompletionRoutine(DeviceContext->I2CAsyncRequest, I2COnReadCompletion, DeviceContext);
+
+ // Prepare the I2C transfer.
+
+ if (Length > sizeof(DeviceContext->I2CAsyncBuffer))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C, "Unexpected value of data length. Length: %lu. Size of buffer: %lu", Length, sizeof(DeviceContext->I2CAsyncBuffer));
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ SPB_TRANSFER_LIST_AND_ENTRIES(I2C_TRANSFER_COUNT) transferList;
+ SPB_TRANSFER_LIST_INIT(&(transferList.List), I2C_TRANSFER_COUNT);
+
+ transferList.List.Transfers[0] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE(
+ SpbTransferDirectionToDevice,
+ 0,
+ &DeviceContext->I2CRegisterAddress,
+ REGISTER_ADDR_SIZE);
+
+ transferList.List.Transfers[1] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE(
+ SpbTransferDirectionFromDevice,
+ 0,
+ DeviceContext->I2CAsyncBuffer,
+ Length);
+
+ // The IOCTL is METHOD_BUFFERED, so the memory (transferList) doesn't
+ // have to persist until the request is completed.
+ status = WdfMemoryAssignBuffer(
+ DeviceContext->I2CMemory,
+ static_cast<PVOID>(&transferList),
+ sizeof(transferList));
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "WdfMemoryAssignBuffer failed with status %!STATUS!", status);
+ }
+
+ status = WdfIoTargetFormatRequestForIoctl(
+ DeviceContext->I2CIoTarget,
+ DeviceContext->I2CAsyncRequest,
+ IOCTL_SPB_EXECUTE_SEQUENCE,
+ DeviceContext->I2CMemory,
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "WdfIoTargetFormatRequestForIoctl failed with status %!STATUS!", status);
+ goto Exit;
+ }
+
+ // Send the request to the I2C I/O Target.
+ if (WdfRequestSend(DeviceContext->I2CAsyncRequest, DeviceContext->I2CIoTarget, NULL) == FALSE)
+ {
+ status = WdfRequestGetStatus(DeviceContext->I2CAsyncRequest);
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "[WDFREQUEST: 0x%p] WdfRequestSend for I2C read failed with status %!STATUS!",
+ DeviceContext->I2CAsyncRequest, status);
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_I2C);
+ return status;
+}
+
+NTSTATUS
+I2CReadSynchronously(
+ _In_ PDEVICE_CONTEXT DeviceContext,
+ _In_ I2C_REQUEST_SOURCE RequestSource,
+ _In_ UINT8 RegisterAddress,
+ _Out_writes_bytes_(Length) PVOID Data,
+ _In_ ULONG Length
+)
+/*++
+
+Routine Description:
+
+ Synchronously reads data from the port controller's registers over the I2C controller
+ for a request originating from the client driver.
+
+Arguments:
+
+ DeviceContext - Context information for the port controller device.
+
+ RequestSource - Identify the caller so the correct WDFREQUEST can be re-used
+
+ RegisterAddress - The I2C register address from which to read data.
+
+ Length - Length of the data to read.
+
+ Data - The data read from the registers.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_I2C);
+
+ PAGED_CODE();
+
+ NTSTATUS status;
+ WDF_REQUEST_SEND_OPTIONS requestOptions;
+ WDF_MEMORY_DESCRIPTOR memoryDescriptor;
+ WDF_REQUEST_REUSE_PARAMS reuseParams;
+ ULONG_PTR bytesTransferred = 0;
+ UINT8 transferBuffer[I2C_BUFFER_SIZE];
+
+ WDFREQUEST request = DeviceContext->OutgoingRequests[RequestSource];
+
+ // Reuse the preallocated WDFREQUEST for internal requests.
+ WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS);
+ status = WdfRequestReuse(request, &reuseParams);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "[WDFREQUEST: 0x%p] WdfRequestReuse for I2CSyncRequest failed with status %!STATUS!",
+ request, status);
+ goto Exit;
+ }
+
+ if (Length == 0)
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C, "Parameter 'Length' cannot be 0.");
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ if (Length > sizeof(transferBuffer))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C, "Unexpected value of data length. Length: %lu. Size of buffer: %lu", Length, sizeof(transferBuffer));
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ // Prepare the I2C transfer.
+ SPB_TRANSFER_LIST_AND_ENTRIES(I2C_TRANSFER_COUNT) transferList;
+ SPB_TRANSFER_LIST_INIT(&(transferList.List), I2C_TRANSFER_COUNT);
+
+ transferList.List.Transfers[0] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE(
+ SpbTransferDirectionToDevice,
+ 0,
+ &RegisterAddress,
+ REGISTER_ADDR_SIZE);
+
+ transferList.List.Transfers[1] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE(
+ SpbTransferDirectionFromDevice,
+ 0,
+ transferBuffer,
+ Length);
+
+ // Initialize the memory descriptor with the transfer list.
+ WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, &transferList, sizeof(transferList));
+
+ WDF_REQUEST_SEND_OPTIONS_INIT(&requestOptions, WDF_REQUEST_SEND_OPTION_TIMEOUT);
+ requestOptions.Timeout = WDF_REL_TIMEOUT_IN_MS(I2C_SYNCHRONOUS_TIMEOUT);
+
+ // Send the request to the I2C I/O Target.
+ status = WdfIoTargetSendIoctlSynchronously(DeviceContext->I2CIoTarget,
+ request,
+ IOCTL_SPB_EXECUTE_SEQUENCE,
+ &memoryDescriptor,
+ NULL,
+ &requestOptions,
+ &bytesTransferred);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "[WDFREQUEST: 0x%p] WdfIoTargetSendIoctlSynchronously failed with status %!STATUS!",
+ request, status);
+
+ // A synchronous I2C request failing is a good indicator that I2C is unresponsive.
+ // Attempt to reset the device.
+ I2CPerformDeviceReset(DeviceContext);
+
+ goto Exit;
+ }
+
+ if (bytesTransferred != REGISTER_ADDR_SIZE + Length)
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C, "Unexpected number of bytes transferred. Expected %lu, transferred %Iu.", REGISTER_ADDR_SIZE + Length, bytesTransferred);
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto Exit;
+ }
+
+ // Get the returned data out of the buffer.
+ RtlCopyMemory(Data, transferBuffer, Length);
+Exit:
+ TRACE_FUNC_EXIT(TRACE_I2C);
+ return status;
+}
+
+NTSTATUS
+I2CReadSynchronouslyMultiple(
+ _In_ PDEVICE_CONTEXT DeviceContext,
+ _In_ I2C_REQUEST_SOURCE requestSource,
+ _Inout_updates_(Count) REGISTER_ITEM* Items,
+ _In_ ULONG Count
+)
+/*++
+
+Routine Description:
+
+ Synchronously reads data from the port controller's registers over the I2C controller
+ for a request originating from the client driver.
+
+Arguments:
+
+ DeviceContext - Context information for the port controller device.
+
+ RequestSource - Identify the caller so the correct WDFREQUEST can be re-used
+
+ Items - Array of (register, data, length) triplets
+
+ Count - Count of elements from the Items array above
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_I2C);
+
+ PAGED_CODE();
+
+ NTSTATUS status = STATUS_SUCCESS;
+ PREGISTER_ITEM item;
+
+ for (ULONG i = 0; i < Count; i++)
+ {
+ item = &Items[i];
+ status = I2CReadSynchronously(DeviceContext,
+ requestSource,
+ item->RegisterAddress,
+ item->Data,
+ item->Length);
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_I2C);
+ return status;
+}
+
+NTSTATUS
+I2CWriteSynchronously(
+ _In_ PDEVICE_CONTEXT DeviceContext,
+ _In_ I2C_REQUEST_SOURCE RequestSource,
+ _In_ UINT8 RegisterAddress,
+ _In_reads_(Length) PVOID Data,
+ _In_ ULONG Length
+)
+/*++
+
+Routine Description:
+
+ Synchronously writes data from the port controller's registers over the I2C controller
+ for a request originating from the client driver.
+
+Arguments:
+
+ DeviceContext - Context information for the port controller device.
+
+ RequestSource - Identify the caller so the correct WDFREQUEST can be re-used
+
+ RegisterAddress - The I2C register address from which to write data.
+
+ Data - The data to write.
+
+ Length - Length of the data to write.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_I2C);
+
+ PAGED_CODE();
+
+ NTSTATUS status;
+ WDF_REQUEST_SEND_OPTIONS requestOptions;
+ WDF_MEMORY_DESCRIPTOR memoryDescriptor;
+ WDF_REQUEST_REUSE_PARAMS reuseParams;
+ ULONG_PTR bytesTransferred = 0;
+ UINT8 transferBuffer[I2C_BUFFER_SIZE];
+
+ WDFREQUEST request = DeviceContext->OutgoingRequests[RequestSource];
+
+ if (Length == 0)
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C, "Parameter 'Length' cannot be 0.");
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ // Reuse the preallocated WDFREQUEST for internal requests.
+ WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS);
+ status = WdfRequestReuse(request, &reuseParams);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "[WDFREQUEST: 0x%p] WdfRequestReuse failed with status %!STATUS!",
+ request, status);
+ goto Exit;
+ }
+
+ transferBuffer[0] = RegisterAddress;
+
+ if (REGISTER_ADDR_SIZE + Length > sizeof(transferBuffer))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C, "Unexpected value of length. Length: %lu. Size of buffer: %lu", Length, sizeof(transferBuffer));
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ RtlCopyMemory(&transferBuffer[REGISTER_ADDR_SIZE], Data, Length);
+
+ // Initialize the memory descriptor with the write buffer.
+ WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, &transferBuffer, REGISTER_ADDR_SIZE + Length);
+
+ WDF_REQUEST_SEND_OPTIONS_INIT(&requestOptions, WDF_REQUEST_SEND_OPTION_TIMEOUT);
+ requestOptions.Timeout = WDF_REL_TIMEOUT_IN_MS(I2C_SYNCHRONOUS_TIMEOUT);
+
+ // Send the write request to the I2C I/O Target.
+ status = WdfIoTargetSendWriteSynchronously(
+ DeviceContext->I2CIoTarget,
+ request,
+ &memoryDescriptor,
+ NULL,
+ &requestOptions,
+ &bytesTransferred);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "[WDFREQUEST: 0x%p] WdfIoTargetSendWriteSynchronously failed with status %!STATUS!",
+ request, status);
+
+ // A synchronous I2C request failing is a good indicator that I2C is unresponsive.
+ // Attempt to reset the device.
+ I2CPerformDeviceReset(DeviceContext);
+
+ goto Exit;
+ }
+
+ if (bytesTransferred != REGISTER_ADDR_SIZE + Length)
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C, "Unexpected number of bytes transferred. Expected %lu, transferred %Iu.", REGISTER_ADDR_SIZE + Length, bytesTransferred);
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_I2C);
+ return status;
+}
+
+VOID
+I2COnReadCompletion(
+ _In_ WDFREQUEST Request,
+ _In_ WDFIOTARGET Target,
+ _In_ PWDF_REQUEST_COMPLETION_PARAMS Params,
+ _In_ WDFCONTEXT Context
+)
+/*++
+
+Routine Description:
+
+ Completion routine for hardware access after reading. Completes the WDFREQUEST from UcmTcpciCx.
+
+Arguments:
+
+ Request - A handle to a framework request object that represents the completed I/O request.
+
+ Target - A handle to an I/O target object that represents the I/O target that completed the request.
+
+ Params - A pointer to a WDF_REQUEST_COMPLETION_PARAMS structure that contains
+ information about the completed request.
+
+ Context - Driver-supplied context information,
+ which the driver specified in a previous call to WdfRequestSetCompletionRoutine. In this case,
+ it is of type PDEVICE_CONTEXT.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_I2C);
+
+ UNREFERENCED_PARAMETER(Target);
+
+ NTSTATUS status;
+ PDEVICE_CONTEXT deviceContext;
+ PVOID outputBuffer;
+ size_t readLength = 0;
+
+ deviceContext = (PDEVICE_CONTEXT)Context;
+
+ status = Params->IoStatus.Status;
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "[WDFREQUEST: 0x%p] I2CTcpciRequestOnReadCompletion - WDFREQUEST completed with failure status: %!STATUS!",
+ Request, status);
+ goto Exit;
+ }
+
+ readLength = Params->IoStatus.Information;
+ if (readLength <= REGISTER_ADDR_SIZE)
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "[WDFREQUEST: 0x%p] Invalid output buffer length: %Ix",
+ Request, readLength);
+ readLength = 0;
+ goto Exit;
+ }
+
+ // Subtract REGISTER_ADDR_SIZE from readLength to account for only the read bytes.
+ readLength -= REGISTER_ADDR_SIZE;
+
+ // Retrieve the output buffer of the pending UcmTcpciCx request
+ status = WdfRequestRetrieveOutputBuffer(deviceContext->IncomingRequest, readLength, &outputBuffer, NULL);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "WdfRequestRetrieveOutputBuffer failed. Status: %!STATUS!", status);
+ goto Exit;
+ }
+
+ // Copy the read bytes into the output buffer of the WDFREQUEST from UcmTcpciCx.
+ RtlCopyMemory(outputBuffer, deviceContext->I2CAsyncBuffer, readLength);
+
+ WdfRequestSetInformation(deviceContext->IncomingRequest, readLength);
+
+Exit:
+
+ WdfRequestComplete(deviceContext->IncomingRequest, status);
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_I2C,
+ "[WDFREQUEST: 0x%p] WDFREQUEST from UcmTcpciCx completed with status: %!STATUS!", deviceContext->IncomingRequest, status);
+
+ TRACE_FUNC_EXIT(TRACE_I2C);
+}
+
+VOID
+I2COnWriteCompletion(
+ _In_ WDFREQUEST Request,
+ _In_ WDFIOTARGET Target,
+ _In_ PWDF_REQUEST_COMPLETION_PARAMS Params,
+ _In_ WDFCONTEXT Context
+)
+/*++
+
+Routine Description:
+
+ Completion routine for hardware access after a write. Completes the WDFREQUEST from UcmTcpciCx.
+
+Arguments:
+
+ Request - A handle to a framework request object that represents the completed I/O request.
+
+ Target - A handle to an I/O target object that represents the I/O target that completed the request.
+
+ Params - A pointer to a WDF_REQUEST_COMPLETION_PARAMS structure that contains
+ information about the completed request.
+
+ Context - Driver-supplied context information,
+ which the driver specified in a previous call to WdfRequestSetCompletionRoutine. In this case,
+ it is of type PDEVICE_CONTEXT.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_I2C);
+
+ UNREFERENCED_PARAMETER(Target);
+
+ NTSTATUS status;
+ PDEVICE_CONTEXT deviceContext;
+
+ deviceContext = (PDEVICE_CONTEXT)Context;
+
+ status = Params->IoStatus.Status;
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "[WDFREQUEST: 0x%p] I2COnWriteCompletion - WDFREQUEST completed with failure status: %!STATUS!",
+ Request, status);
+ goto Exit;
+ }
+
+Exit:
+ // Complete the WDFREQUEST from UcmTcpciCx.
+ WdfRequestComplete(deviceContext->IncomingRequest, status);
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_I2C,
+ "[WDFREQUEST: 0x%p] WDFREQUEST from UcmTcpciCx completed with status: %!STATUS!",
+ deviceContext->IncomingRequest, status);
+
+ TRACE_FUNC_EXIT(TRACE_I2C);
+}
+
+void
+I2CPerformDeviceReset(
+ _In_ PDEVICE_CONTEXT DeviceContext
+)
+/*++
+
+Routine Description:
+
+ Recovery mechanism for a malfunctioning I2C bus.
+ Attempt a platform-level device reset.
+ If unsuccessful or we have exceeded the maximum number of reset attempts, call WdfDeviceSetFailed.
+
+Arguments:
+
+ DeviceContext - Context information for the port controller device.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_I2C);
+
+ PAGED_CODE();
+
+ NTSTATUS status;
+
+ if (DeviceContext->ResetInterface.DeviceReset != NULL && DeviceContext->ResetAttempts <= MAX_DEVICE_RESET_ATTEMPTS)
+ {
+ // Attempt a platform-level device reset (PLDR) to recover from an I2C error.
+ // This will disconnect the device from the power rail and reconnect it.
+
+ ++DeviceContext->ResetAttempts;
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_I2C,
+ "[WDFDEVICE: 0x%p] Performing PlatformLevelDeviceReset", DeviceContext->Device);
+ status = DeviceContext->ResetInterface.DeviceReset(DeviceContext->ResetInterface.Context, PlatformLevelDeviceReset, 0, NULL);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
+ "[WDFDEVICE: 0x%p] PlatformLevelDeviceReset failed with status: %!STATUS!", DeviceContext->Device, status);
+
+ // If PLDR fails, perform WdfDeviceSetFailed.
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_I2C,
+ "[WDFDEVICE: 0x%p] Performing WdfDeviceSetFailed with WdfDeviceFailedAttemptRestart", DeviceContext->Device);
+
+ WdfDeviceSetFailed(DeviceContext->Device, WdfDeviceFailedAttemptRestart);
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_I2C,
+ "[WDFDEVICE: 0x%p] WdfDeviceSetFailed complete", DeviceContext->Device);
+ }
+ }
+ else
+ {
+ // Either platform-level device reset failed or DEVICE_RESET_INTERFACE_STANDARD was not
+ // supported by the bus driver. Use WdfDeviceSetFailed and attempt to restart the device.
+ // When the driver is reloaded, it will reinitialize I2C.
+ // If several consecutive restart attempts fail (because the restarted driver again reports an error),
+ // the framework stops trying to restart the device.
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_I2C,
+ "[WDFDEVICE: 0x%p] Performing WdfDeviceSetFailed with WdfDeviceFailedAttemptRestart",DeviceContext->Device);
+
+ WdfDeviceSetFailed(DeviceContext->Device, WdfDeviceFailedAttemptRestart);
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_I2C,
+ "[WDFDEVICE: 0x%p] WdfDeviceSetFailed complete", DeviceContext->Device);
+ }
+
+ TRACE_FUNC_EXIT(TRACE_I2C);
+}
diff --git a/usb/UcmTcpciCxClientSample/I2C.h b/usb/UcmTcpciCxClientSample/I2C.h
new file mode 100644
index 00000000..0d3173fb
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/I2C.h
@@ -0,0 +1,126 @@
+/*++
+
+Module Name:
+
+ I2C.h
+
+Abstract:
+
+ This file contains the declarations for I2C functions and callbacks.
+
+Environment:
+
+ Kernel-mode Driver Framework
+
+--*/
+
+#pragma once
+
+// Number of platform-level device resets to attempt.
+#define MAX_DEVICE_RESET_ATTEMPTS 3
+
+// Transfers required for an I2C read or write operation.
+// The first transfer in the sequence writes a one-byte register address to the device.
+// The second transfer reads from or writes to the selected register.
+#define I2C_TRANSFER_COUNT 2
+
+#define REGISTER_ADDR_SIZE 1
+
+// Timeout in milliseconds for synchronous I2C reads/writes.
+// The I2C specification does not specify a timeout. 300 ms was chosen arbitrarily.
+#define I2C_SYNCHRONOUS_TIMEOUT 300
+
+// Size used to initialize the I2C read and write buffers.
+#define I2C_BUFFER_SIZE 50
+
+typedef struct _DEVICE_CONTEXT DEVICE_CONTEXT, *PDEVICE_CONTEXT;
+
+enum I2C_REQUEST_SOURCE;
+
+typedef struct _REGISTER_ITEM
+{
+ UINT8 RegisterAddress;
+ _Out_writes_bytes_(Length) PVOID Data;
+ ULONG Length;
+} REGISTER_ITEM, *PREGISTER_ITEM;
+
+// Helper macro to generate an array item.
+#define GEN_REGISTER_ITEM(RegisterAddress, Variable) \
+ { (RegisterAddress), &(Variable), sizeof((Variable)) }
+
+#ifndef _countof
+#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
+#endif
+
+EXTERN_C_START
+
+NTSTATUS
+I2CInitialize(
+ _In_ PDEVICE_CONTEXT DeviceContext,
+ _In_ WDFCMRESLIST ResourcesRaw,
+ _In_ WDFCMRESLIST ResourcesTranslated
+);
+
+NTSTATUS
+I2COpen(
+ _In_ PDEVICE_CONTEXT DeviceContext
+);
+
+void
+I2CClose(
+ _In_ PDEVICE_CONTEXT DeviceContext
+);
+
+NTSTATUS
+I2CWriteAsynchronously(
+ _In_ PDEVICE_CONTEXT DeviceContext,
+ _In_ UINT8 RegisterAddress,
+ _In_reads_bytes_(Length) PVOID Data,
+ _In_ ULONG Length
+);
+
+NTSTATUS
+I2CReadAsynchronously(
+ _In_ PDEVICE_CONTEXT DeviceContext,
+ _In_ UINT8 RegisterAddress,
+ _In_ ULONG Length
+);
+
+NTSTATUS
+I2CReadSynchronously(
+ _In_ PDEVICE_CONTEXT DeviceContext,
+ _In_ I2C_REQUEST_SOURCE requestSource,
+ _In_ UINT8 RegisterAddress,
+ _Out_writes_bytes_(Length) PVOID Data,
+ _In_ ULONG Length
+);
+
+NTSTATUS
+I2CWriteSynchronously(
+ _In_ PDEVICE_CONTEXT DeviceContext,
+ _In_ I2C_REQUEST_SOURCE requestSource,
+ _In_ UINT8 RegisterAddress,
+ _In_reads_(Length) PVOID Data,
+ _In_ ULONG Length
+);
+
+NTSTATUS
+I2CReadSynchronouslyMultiple(
+ _In_ PDEVICE_CONTEXT DeviceContext,
+ _In_ I2C_REQUEST_SOURCE requestSource,
+ _Inout_updates_(Count) REGISTER_ITEM* Items,
+ _In_ ULONG Count
+);
+
+EVT_WDF_REQUEST_COMPLETION_ROUTINE
+I2COnReadCompletion;
+
+EVT_WDF_REQUEST_COMPLETION_ROUTINE
+I2COnWriteCompletion;
+
+void
+I2CPerformDeviceReset(
+ _In_ PDEVICE_CONTEXT DeviceContext
+);
+
+EXTERN_C_END \ No newline at end of file
diff --git a/usb/UcmTcpciCxClientSample/PortControllerInterface.cpp b/usb/UcmTcpciCxClientSample/PortControllerInterface.cpp
new file mode 100644
index 00000000..a8aff83d
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/PortControllerInterface.cpp
@@ -0,0 +1,604 @@
+/*++
+
+Module Name:
+
+ PortControllerInterface.c
+
+Abstract:
+
+ This file contains the definitions of functions to read to and write from the
+ Type-C port controller hardware registers.
+
+Environment:
+
+ Kernel-mode Driver Framework
+
+--*/
+
+#include "Driver.h"
+#include "portcontrollerinterface.tmh"
+
+void
+PostponeToWorkitem(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device,
+ _In_ WDFWORKITEM WorkItem
+)
+/*++
+
+Routine Description:
+
+ Because EvtIoDeviceControl was called at dispatch-level, it cannot send I/O and then wait synchronously;
+ instead, the work has to be postponed to a passive-level workitem.
+
+Arguments:
+
+ Request - Handle to a framework request object.
+
+ Device - Handle to a framework device object.
+
+ WorkItem - Handle to a framework workitem object
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_PORTCONTROLLERINTERFACE);
+ PWORKITEM_CONTEXT workItemContext;
+
+ workItemContext = WorkitemGetContext(WorkItem);
+ workItemContext->Device = Device;
+ workItemContext->Request = Request;
+
+ WdfWorkItemEnqueue(WorkItem);
+
+ TRACE_FUNC_EXIT(TRACE_PORTCONTROLLERINTERFACE);
+}
+
+void
+EvtWorkItemGetStatus(
+ _In_ WDFWORKITEM WorkItem
+)
+/*++
+
+Routine Description:
+
+ This routine handles IOCTL_UCMTCPCI_PORT_CONTROLLER_GET_STATUS.
+
+ Read the contents of the CC Status, Fault Status, and Power Status registers from the device
+ and complete the WDFREQUEST.
+
+Arguments:
+
+ WorkItem - Handle to a framework workitem object.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_PORTCONTROLLERINTERFACE);
+
+ PUCMTCPCI_PORT_CONTROLLER_GET_STATUS_OUT_PARAMS outParams;
+ PWORKITEM_CONTEXT workItemContext;
+ PDEVICE_CONTEXT deviceContext;
+ WDFREQUEST request;
+ NTSTATUS status;
+
+ workItemContext = WorkitemGetContext(WorkItem);
+ deviceContext = DeviceGetContext(workItemContext->Device);
+ request = workItemContext->Request;
+
+ status = WdfRequestRetrieveOutputBuffer(request,
+ sizeof(*outParams),
+ reinterpret_cast<PVOID*>(&outParams),
+ NULL);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_PORTCONTROLLERINTERFACE,
+ "WdfRequestRetrieveOutputBuffer failed. Status: %!STATUS!", status);
+ goto Exit;
+ }
+
+ REGISTER_ITEM items[] = {
+ GEN_REGISTER_ITEM(CC_STATUS, outParams->CCStatus),
+ GEN_REGISTER_ITEM(POWER_STATUS, outParams->PowerStatus),
+ GEN_REGISTER_ITEM(FAULT_STATUS, outParams->FaultStatus),
+ };
+
+ status = I2CReadSynchronouslyMultiple(deviceContext,
+ I2CRequestSourceClient,
+ items,
+ _countof(items));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ WdfRequestSetInformation(request, sizeof(*outParams));
+
+Exit:
+ WdfRequestComplete(request, status);
+
+ TRACE_FUNC_EXIT(TRACE_PORTCONTROLLERINTERFACE);
+}
+
+void
+EvtWorkItemGetControl(
+ _In_ WDFWORKITEM WorkItem
+)
+/*++
+
+Routine Description:
+
+ This routine handles IOCTL_UCMTCPCI_PORT_CONTROLLER_GET_CONTROL.
+
+ Read the contents of the TCPC Control, Role Control, Fault Control, and Power Control registers from the device
+ and complete the WDFREQUEST.
+
+Arguments:
+
+ WorkItem - Handle to a framework workitem object.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_PORTCONTROLLERINTERFACE);
+
+ PUCMTCPCI_PORT_CONTROLLER_GET_CONTROL_OUT_PARAMS outParams;
+ PWORKITEM_CONTEXT workItemContext;
+ PDEVICE_CONTEXT deviceContext;
+ WDFREQUEST request;
+ NTSTATUS status;
+
+ workItemContext = WorkitemGetContext(WorkItem);
+ deviceContext = DeviceGetContext(workItemContext->Device);
+ request = workItemContext->Request;
+
+ status = WdfRequestRetrieveOutputBuffer(request,
+ sizeof(*outParams),
+ reinterpret_cast<PVOID*>(&outParams),
+ NULL);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_PORTCONTROLLERINTERFACE,
+ "WdfRequestRetrieveOutputBuffer failed. Status: %!STATUS!", status);
+ goto Exit;
+ }
+
+ REGISTER_ITEM items[] = {
+ GEN_REGISTER_ITEM(TCPC_CONTROL, outParams->TCPCControl),
+ GEN_REGISTER_ITEM(ROLE_CONTROL, outParams->RoleControl),
+ GEN_REGISTER_ITEM(FAULT_CONTROL, outParams->FaultControl),
+ GEN_REGISTER_ITEM(POWER_CONTROL, outParams->PowerControl),
+ };
+
+ status = I2CReadSynchronouslyMultiple(deviceContext,
+ I2CRequestSourceClient,
+ items,
+ _countof(items));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ WdfRequestSetInformation(request, sizeof(*outParams));
+
+Exit:
+ WdfRequestComplete(request, status);
+
+ TRACE_FUNC_EXIT(TRACE_PORTCONTROLLERINTERFACE);
+}
+
+void
+EvtSetControl(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+)
+/*++
+
+Routine Description:
+
+ Set the contents of the TCPC Control, Role Control, Fault Control, or Power Control
+ registers on the device and complete the WDFREQUEST.
+
+Arguments:
+
+ Request - Handle to a framework request object.
+
+ Device - Handle to a framework device object.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_PORTCONTROLLERINTERFACE);
+
+ NTSTATUS status;
+ PDEVICE_CONTEXT deviceContext;
+ PUCMTCPCI_PORT_CONTROLLER_SET_CONTROL_IN_PARAMS inParams;
+ UCMTCPCI_PORT_CONTROLLER_CONTROL_TYPE controlType;
+
+ deviceContext = DeviceGetContext(Device);
+
+ status = WdfRequestRetrieveInputBuffer(Request,
+ sizeof(UCMTCPCI_PORT_CONTROLLER_SET_COMMAND_IN_PARAMS),
+ reinterpret_cast<PVOID*>(&inParams),
+ NULL);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_PORTCONTROLLERINTERFACE,
+ "WdfRequestRetrieveInputBuffer failed. Status: %!STATUS!", status);
+ goto Exit;
+ }
+
+ controlType = inParams->ControlType;
+
+ switch (controlType)
+ {
+ case UcmTcpciPortControllerTcpcControl:
+ {
+ status = I2CWriteAsynchronously(deviceContext,
+ TCPC_CONTROL,
+ &inParams->TCPCControl,
+ sizeof(inParams->TCPCControl));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ break;
+ }
+ case UcmTcpciPortControllerRoleControl:
+ {
+ status = I2CWriteAsynchronously(deviceContext,
+ ROLE_CONTROL,
+ &inParams->RoleControl,
+ sizeof(inParams->RoleControl));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ break;
+ }
+ case UcmTcpciPortControllerFaultControl:
+ {
+ status = I2CWriteAsynchronously(deviceContext,
+ FAULT_CONTROL,
+ &inParams->FaultControl,
+ sizeof(inParams->FaultControl));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ break;
+ }
+ case UcmTcpciPortControllerPowerControl:
+ {
+ status = I2CWriteAsynchronously(deviceContext,
+ POWER_CONTROL,
+ &inParams->PowerControl,
+ sizeof(inParams->PowerControl));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ break;
+ }
+ default:
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_PORTCONTROLLERINTERFACE, "Invalid control register type.");
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_PORTCONTROLLERINTERFACE);
+}
+
+void
+EvtSetCommand(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+)
+/*++
+
+Routine Description:
+
+ Set the contents of the command register on the device and complete the WDFREQUEST.
+
+Arguments:
+
+ Request - Handle to a framework request object.
+
+ Device - Handle to a framework device object.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_PORTCONTROLLERINTERFACE);
+
+ NTSTATUS status;
+ PDEVICE_CONTEXT deviceContext;
+ PUCMTCPCI_PORT_CONTROLLER_SET_COMMAND_IN_PARAMS inParams;
+ UINT8 cmd;
+
+ deviceContext = DeviceGetContext(Device);
+
+ status = WdfRequestRetrieveInputBuffer(Request,
+ sizeof(UCMTCPCI_PORT_CONTROLLER_SET_COMMAND_IN_PARAMS),
+ reinterpret_cast<PVOID*>(&inParams),
+ NULL);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_PORTCONTROLLERINTERFACE,
+ "WdfRequestRetrieveInputBuffer failed. Status: %!STATUS!", status);
+ goto Exit;
+ }
+
+ // UCMTCPCI_PORT_CONTROLLER_COMMAND (from inParams->Command) is defined as an enum.
+ // Thus sizeof() typically returns 4 bytes, instead of 1 byte as required by TCPCI spec.
+ // The workaround is to copy it to a local 1-byte variable before writing it down.
+ cmd = static_cast<UINT8>(inParams->Command);
+
+ status = I2CWriteAsynchronously(deviceContext,
+ COMMAND,
+ &cmd,
+ sizeof(cmd));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_PORTCONTROLLERINTERFACE);
+}
+
+void
+EvtSetConfigStandardOutput(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+)
+/*++
+
+Routine Description:
+
+ Set the contents of the config standard output register on the
+ device and complete the WDFREQUEST.
+
+Arguments:
+
+ Request - Handle to a framework request object.
+
+ Device - Handle to a framework device object.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_PORTCONTROLLERINTERFACE);
+
+ NTSTATUS status;
+ PDEVICE_CONTEXT deviceContext;
+ PUCMTCPCI_PORT_CONTROLLER_SET_CONFIG_STANDARD_OUTPUT_IN_PARAMS inParams;
+
+ deviceContext = DeviceGetContext(Device);
+
+ status = WdfRequestRetrieveInputBuffer(Request,
+ sizeof(UCMTCPCI_PORT_CONTROLLER_SET_CONFIG_STANDARD_OUTPUT_IN_PARAMS),
+ reinterpret_cast<PVOID*>(&inParams),
+ NULL);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_PORTCONTROLLERINTERFACE,
+ "WdfRequestRetrieveInputBuffer failed. Status: %!STATUS!", status);
+ goto Exit;
+ }
+
+ status = I2CWriteAsynchronously(deviceContext,
+ CONFIG_STANDARD_OUTPUT,
+ &inParams->ConfigStandardOutput,
+ sizeof(inParams->ConfigStandardOutput));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_PORTCONTROLLERINTERFACE);
+}
+
+void
+EvtSetMessageHeaderInfo(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+)
+/*++
+
+Routine Description:
+
+ Set the contents of the message header info register on the device and complete the WDFREQUEST.
+
+Arguments:
+
+ Request - Handle to a framework request object.
+
+ Device - Handle to a framework device object.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_PORTCONTROLLERINTERFACE);
+
+ NTSTATUS status;
+ PDEVICE_CONTEXT deviceContext;
+ PUCMTCPCI_PORT_CONTROLLER_SET_MESSAGE_HEADER_INFO_IN_PARAMS inParams;
+
+ deviceContext = DeviceGetContext(Device);
+
+ status = WdfRequestRetrieveInputBuffer(Request,
+ sizeof(UCMTCPCI_PORT_CONTROLLER_SET_MESSAGE_HEADER_INFO_IN_PARAMS),
+ reinterpret_cast<PVOID*>(&inParams),
+ NULL);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_PORTCONTROLLERINTERFACE,
+ "WdfRequestRetrieveInputBuffer failed. Status: %!STATUS!", status);
+ goto Exit;
+ }
+
+ status = I2CWriteAsynchronously(deviceContext,
+ MESSAGE_HEADER_INFO,
+ &inParams->MessageHeaderInfo,
+ sizeof(inParams->MessageHeaderInfo));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_PORTCONTROLLERINTERFACE);
+}
+
+void
+EvtSetReceiveDetect(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+)
+/*++
+
+Routine Description:
+
+ Set the contents of the receive detect register on the device and complete the WDFREQUEST.
+
+Arguments:
+
+ Request - Handle to a framework request object.
+
+ Device - Handle to a framework device object.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_PORTCONTROLLERINTERFACE);
+
+ NTSTATUS status;
+ PDEVICE_CONTEXT deviceContext;
+ PUCMTCPCI_PORT_CONTROLLER_SET_RECEIVE_DETECT_IN_PARAMS inParams;
+
+ deviceContext = DeviceGetContext(Device);
+
+ status = WdfRequestRetrieveInputBuffer(Request,
+ sizeof(UCMTCPCI_PORT_CONTROLLER_SET_RECEIVE_DETECT_IN_PARAMS),
+ reinterpret_cast<PVOID*>(&inParams),
+ NULL);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_PORTCONTROLLERINTERFACE,
+ "WdfRequestRetrieveInputBuffer failed. Status: %!STATUS!", status);
+ goto Exit;
+ }
+
+ status = I2CWriteAsynchronously(deviceContext,
+ RECEIVE_DETECT,
+ &inParams->ReceiveDetect,
+ sizeof(inParams->ReceiveDetect));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_PORTCONTROLLERINTERFACE);
+}
+
+void
+EvtSetTransmit(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+)
+/*++
+
+Routine Description:
+
+ Set the contents of the transmit register on the device and complete the WDFREQUEST.
+
+Arguments:
+
+ Request - Handle to a framework request object.
+
+ Device - Handle to a framework device object.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_PORTCONTROLLERINTERFACE);
+
+ NTSTATUS status;
+ PDEVICE_CONTEXT deviceContext;
+ PUCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT_IN_PARAMS inParams;
+
+ deviceContext = DeviceGetContext(Device);
+
+ status = WdfRequestRetrieveInputBuffer(Request,
+ sizeof(PUCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT_IN_PARAMS),
+ reinterpret_cast<PVOID*>(&inParams),
+ NULL);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_PORTCONTROLLERINTERFACE,
+ "WdfRequestRetrieveInputBuffer failed. Status: %!STATUS!", status);
+ goto Exit;
+ }
+
+ status = I2CWriteAsynchronously(deviceContext,
+ TRANSMIT,
+ &inParams->Transmit,
+ sizeof(inParams->Transmit));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_PORTCONTROLLERINTERFACE);
+}
+
+void
+EvtSetTransmitBuffer(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+)
+/*++
+
+Routine Description:
+
+ Set the contents of the transmit buffer register on the device and complete the WDFREQUEST.
+
+Arguments:
+
+ Request - Handle to a framework request object.
+
+ Device - Handle to a framework device object.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_PORTCONTROLLERINTERFACE);
+
+ NTSTATUS status;
+ PDEVICE_CONTEXT deviceContext;
+ PUCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT_BUFFER_IN_PARAMS inParams;
+
+ deviceContext = DeviceGetContext(Device);
+
+ status = WdfRequestRetrieveInputBuffer(Request,
+ sizeof(UCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT_BUFFER_IN_PARAMS),
+ reinterpret_cast<PVOID*>(&inParams),
+ NULL);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_PORTCONTROLLERINTERFACE,
+ "WdfRequestRetrieveInputBuffer failed. Status: %!STATUS!", status);
+ goto Exit;
+ }
+
+ status = I2CWriteAsynchronously(deviceContext,
+ TRANSMIT_BUFFER,
+ &inParams->TransmitBuffer,
+ inParams->TransmitBuffer.TransmitByteCount + sizeof(inParams->TransmitBuffer.TransmitByteCount));
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_PORTCONTROLLERINTERFACE);
+} \ No newline at end of file
diff --git a/usb/UcmTcpciCxClientSample/PortControllerInterface.h b/usb/UcmTcpciCxClientSample/PortControllerInterface.h
new file mode 100644
index 00000000..1d236178
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/PortControllerInterface.h
@@ -0,0 +1,73 @@
+/*++
+
+Module Name:
+
+ PortControllerInterface.h
+
+Abstract:
+
+ This file contains the declarations of functions to read to and write from the
+ Type-C port controller hardware registers.
+
+Environment:
+
+ Kernel-mode Driver Framework
+
+--*/
+
+#pragma once
+
+void
+PostponeToWorkitem(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device,
+ _In_ WDFWORKITEM Workitem
+);
+
+EVT_WDF_WORKITEM
+EvtWorkItemGetStatus;
+
+EVT_WDF_WORKITEM
+EvtWorkItemGetControl;
+
+void
+EvtSetControl(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+);
+
+void
+EvtSetCommand(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+);
+
+void
+EvtSetConfigStandardOutput(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+);
+
+void
+EvtSetMessageHeaderInfo(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+);
+
+void
+EvtSetReceiveDetect(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+);
+
+void
+EvtSetTransmit(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+);
+
+void
+EvtSetTransmitBuffer(
+ _In_ WDFREQUEST Request,
+ _In_ WDFDEVICE Device
+);
diff --git a/usb/UcmTcpciCxClientSample/Queue.cpp b/usb/UcmTcpciCxClientSample/Queue.cpp
new file mode 100644
index 00000000..d17a789d
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/Queue.cpp
@@ -0,0 +1,230 @@
+/*++
+
+Module Name:
+
+ Queue.c
+
+Abstract:
+
+ This file contains the I/O queue definitions.
+
+Environment:
+
+ Kernel-mode Driver Framework
+
+--*/
+
+#include "Driver.h"
+#include "queue.tmh"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text (PAGE, HardwareRequestQueueInitialize)
+#endif
+
+NTSTATUS
+HardwareRequestQueueInitialize(
+ _In_ WDFDEVICE Device
+)
+/*++
+
+Routine Description:
+
+ The I/O dispatch callbacks for the frameworks device object
+ are configured in this function.
+
+ A single I/O Queue is configured for sequential request
+ processing, and a driver context memory allocation is created
+ to hold our structure QUEUE_CONTEXT.
+
+Arguments:
+
+ Device - Handle to a framework device object.
+
+Returns:
+
+ NTSTATUS
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_QUEUE);
+
+ PAGED_CODE();
+
+ NTSTATUS status;
+ WDFQUEUE hardwareRequestQueue;
+ WDF_IO_QUEUE_CONFIG queueConfig;
+ PDEVICE_CONTEXT deviceContext;
+
+ deviceContext = DeviceGetContext(Device);
+
+ WDF_IO_QUEUE_CONFIG_INIT(
+ &queueConfig,
+ WdfIoQueueDispatchSequential);
+
+ queueConfig.EvtIoDeviceControl = EvtIoDeviceControl;
+ queueConfig.EvtIoStop = EvtIoStop;
+
+ // Create the hardware request queue.
+ status = WdfIoQueueCreate(Device,
+ &queueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &hardwareRequestQueue);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_QUEUE, "WdfIoQueueCreate failed %!STATUS!", status);
+ goto Exit;
+ }
+
+ // Set this queue as the one to which UcmTcpciCx will forward its hardware requests.
+ UcmTcpciPortControllerSetHardwareRequestQueue(deviceContext->PortController, hardwareRequestQueue);
+
+Exit:
+ TRACE_FUNC_EXIT(TRACE_QUEUE);
+ return status;
+}
+
+VOID
+EvtIoDeviceControl(
+ _In_ WDFQUEUE Queue,
+ _In_ WDFREQUEST Request,
+ _In_ size_t OutputBufferLength,
+ _In_ size_t InputBufferLength,
+ _In_ ULONG IoControlCode
+)
+/*++
+
+Routine Description:
+
+ This event is invoked when the framework receives IRP_MJ_DEVICE_CONTROL request.
+
+Arguments:
+
+ Queue - Handle to the framework queue object that is associated with the
+ I/O request.
+
+ Request - Handle to a framework request object.
+
+ OutputBufferLength - Size of the output buffer in bytes
+
+ InputBufferLength - Size of the input buffer in bytes
+
+ IoControlCode - I/O control code.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_QUEUE);
+
+ WDFDEVICE device;
+ PDEVICE_CONTEXT deviceContext;
+
+ TraceEvents(TRACE_LEVEL_INFORMATION,
+ TRACE_QUEUE,
+ "Queue 0x%p, Request 0x%p OutputBufferLength %Iu InputBufferLength %Iu IoControlCode %!UCMTCPCI_PORT_CONTROLLER_IOCTL!",
+ Queue, Request, OutputBufferLength, InputBufferLength, IoControlCode);
+
+ device = WdfIoQueueGetDevice(Queue);
+ deviceContext = DeviceGetContext(device);
+
+ // Save the WDFREQUEST so we can complete it later.
+ deviceContext->IncomingRequest = Request;
+
+ // Check if we recognize the IOCTL.
+ // The helper functions perform the requested hardware read or write and complete the WDFREQUEST.
+
+ // Note: The driver would need to support some of the other IOCTLs from UcmTcpciCx if the
+ // capabilities indicate that they are supported.
+ switch (IoControlCode)
+ {
+ case IOCTL_UCMTCPCI_PORT_CONTROLLER_GET_STATUS:
+ PostponeToWorkitem(Request, device, deviceContext->I2CWorkItemGetStatus);
+ break;
+
+ case IOCTL_UCMTCPCI_PORT_CONTROLLER_GET_CONTROL:
+ PostponeToWorkitem(Request, device, deviceContext->I2CWorkItemGetControl);
+ break;
+
+ case IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_CONTROL:
+ EvtSetControl(Request, device);
+ break;
+
+ case IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT:
+ EvtSetTransmit(Request, device);
+ break;
+
+ case IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT_BUFFER:
+ EvtSetTransmitBuffer(Request, device);
+ break;
+
+ case IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_RECEIVE_DETECT:
+ EvtSetReceiveDetect(Request, device);
+ break;
+
+ case IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_CONFIG_STANDARD_OUTPUT:
+ EvtSetConfigStandardOutput(Request, device);
+ break;
+
+ case IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_COMMAND:
+ EvtSetCommand(Request, device);
+ break;
+
+ case IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_MESSAGE_HEADER_INFO:
+ EvtSetMessageHeaderInfo(Request, device);
+ break;
+
+ default:
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_QUEUE,
+ "Received unexpected IoControlCode %lu", IoControlCode);
+ deviceContext->IncomingRequest = WDF_NO_HANDLE;
+ // If we don't recognize the IOCTL, we must complete the WDFREQUEST here.
+ WdfRequestComplete(Request, STATUS_NOT_SUPPORTED);
+ }
+
+ TRACE_FUNC_EXIT(TRACE_QUEUE);
+}
+
+VOID
+EvtIoStop(
+ _In_ WDFQUEUE Queue,
+ _In_ WDFREQUEST Request,
+ _In_ ULONG ActionFlags
+)
+/*++
+
+Routine Description:
+
+ This event is invoked for a power-managed queue before the device leaves the working state (D0).
+
+Arguments:
+
+ Queue - Handle to the framework queue object that is associated with the
+ I/O request.
+
+ Request - Handle to a framework request object.
+
+ ActionFlags - A bitwise OR of one or more WDF_REQUEST_STOP_ACTION_FLAGS-typed flags
+ that identify the reason that the callback function is being called
+ and whether the request is cancelable.
+
+--*/
+{
+ TRACE_FUNC_ENTRY(TRACE_QUEUE);
+
+ UNREFERENCED_PARAMETER(ActionFlags);
+ UNREFERENCED_PARAMETER(Request);
+
+ WDFDEVICE device;
+ PDEVICE_CONTEXT deviceContext;
+
+ device = WdfIoQueueGetDevice(Queue);
+ deviceContext = DeviceGetContext(device);
+
+ // Attempt to cancel the I2C WDFREQUESTs.
+ if (deviceContext->I2CAsyncRequest != WDF_NO_HANDLE)
+ {
+ TraceEvents(TRACE_LEVEL_ERROR, TRACE_QUEUE,
+ "[WDFREQUEST: 0x%p] Attempting to cancel.", deviceContext->I2CAsyncRequest);
+ WdfRequestCancelSentRequest(deviceContext->I2CAsyncRequest);
+ }
+
+ TRACE_FUNC_EXIT(TRACE_QUEUE);
+} \ No newline at end of file
diff --git a/usb/UcmTcpciCxClientSample/Queue.h b/usb/UcmTcpciCxClientSample/Queue.h
new file mode 100644
index 00000000..92ed2d68
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/Queue.h
@@ -0,0 +1,30 @@
+/*++
+
+Module Name:
+
+ queue.h
+
+Abstract:
+
+ This file contains the queue declarations.
+
+Environment:
+
+ Kernel-mode Driver Framework
+
+--*/
+
+EXTERN_C_START
+
+NTSTATUS
+HardwareRequestQueueInitialize(
+ _In_ WDFDEVICE Device
+);
+
+EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL
+EvtIoDeviceControl;
+
+EVT_WDF_IO_QUEUE_IO_STOP
+EvtIoStop;
+
+EXTERN_C_END \ No newline at end of file
diff --git a/usb/UcmTcpciCxClientSample/README.md b/usb/UcmTcpciCxClientSample/README.md
new file mode 100644
index 00000000..ceaba383
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/README.md
@@ -0,0 +1,28 @@
+# UcmTcpciCx Port Controller Client Driver
+
+This is a skeleton sample driver that shows how to create a Windows USB Type-C port controller driver using the USB Connector Manager Type-C Port Controller Interface class extension driver (UcmTcpciCx). Refer to the UcmTcpciCx documentation for more information.
+
+This sample demonstrates the following:
+
+- Registration with the UcmTcpci class extension driver.
+- Initializing the port controller's Type-C and Power Delivery capabilities.
+- Initializing the I2C communications channel to the port controller hardware.
+- Performing reads/writes over I2C
+- Handling hardware requests from UcmTcpciCx.
+- Handling alerts from the port controller hardware and notifying UcmTcpciCx of the alert.
+- Power management
+- Platform-level device reset in the case of an unresponsive I2C controller.
+
+## Customizing the sample for your port controller
+The sample contains a number of comments prefaced with `// TODO` - review them and modify the code as necessary as you are writing your driver.
+
+## Note regarding Type-C port controller hardware
+This sample assumes a device that complies with the USB Type-C Port Controller Interface specification, Revision 1.0 (part of the [USB 3.1 specification download](http://usb.org/developers/docs)). Such a device uses a predefined register layout and an I2C communications channel.
+If your port controller hardware is not exactly compliant with the specification, you will need to make additional modifications to the sample.
+
+## Performing read/writes over I2C
+The USB Type-C Port Controller Interface specification defines I2C to be the channel by which software communicates with the port controller hardware.
+If your port controller hardware is not compliant with the specification and does not use I2C as the communications channel, you will need to make additional modifications to the sample.
+
+## When to write a UcmTcpciCx client driver
+UcmTcpciCx is intended for system port controller drivers. If you are bringing up a USB Type-C peripheral, you do not need to write a USB Type-C specific driver; a regular USB client driver will suffice. Refer to [Developing Windows client drivers for USB devices](https://msdn.microsoft.com/en-us/library/windows/hardware/hh406260(v=vs.85).aspx) to determine what type of driver, if any, you need to write to make your USB device work with Windows. \ No newline at end of file
diff --git a/usb/UcmTcpciCxClientSample/Register.h b/usb/UcmTcpciCxClientSample/Register.h
new file mode 100644
index 00000000..0c6d9f41
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/Register.h
@@ -0,0 +1,79 @@
+/*++
+
+Module Name:
+
+ register.h
+
+Abstract:
+
+ This file contains the definitions for TCPCI device register addresses as defined in the
+ USB Type-C Port Controller Interface Specification, Revision 1.0.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#pragma once
+
+#define VENDOR_ID 0x00
+#define PRODUCT_ID 0x02
+#define DEVICE_ID 0x04
+#define USBTYPEC_REV 0x06
+#define USBPD_REV_VER 0x08
+#define PD_INTERFACE_REV 0x0A
+
+// 0x0C - 0x0F are reserved.
+
+#define ALERT 0x10
+#define ALERT_MASK 0x12
+#define POWER_STATUS_MASK 0x14
+#define FAULT_STATUS_MASK 0x15
+
+// 0x16 - 0x17 are reserved.
+
+#define CONFIG_STANDARD_OUTPUT 0x18
+#define TCPC_CONTROL 0x19
+#define ROLE_CONTROL 0x1A
+#define FAULT_CONTROL 0x1B
+#define POWER_CONTROL 0x1C
+#define CC_STATUS 0x1D
+#define POWER_STATUS 0x1E
+#define FAULT_STATUS 0x1F
+
+// 0x20 - 0x22 are reserved.
+
+#define COMMAND 0x23
+#define DEVICE_CAPABILITIES_1 0x24
+#define DEVICE_CAPABILITIES_2 0x26
+#define STANDARD_INPUT_CAPABILITIES 0x28
+#define STANDARD_OUTPUT_CAPABILITIES 0x29
+
+// 0x2A - 0x2D are reserved.
+
+#define MESSAGE_HEADER_INFO 0x2E
+#define RECEIVE_DETECT 0x2F
+
+// Receive buffer. The driver will read the entire receive buffer at once.
+// Since the receive buffer registers are consecutive, we need to only define the starting address.
+
+#define RECEIVE_BUFFER 0x30
+
+#define TRANSMIT 0x50
+
+// Transmit buffer. The driver will write the entire transmit buffer at once.
+// Since the transmit buffer registers are consecutive, we need to only define the starting address.
+
+#define TRANSMIT_BUFFER 0x51
+
+#define VBUS_VOLTAGE 0x70
+#define VBUS_SINK_DISCONNECT_THRESHOLD 0x72
+#define VBUS_STOP_DISCHARGE_THRESHOLD 0x74
+#define VBUS_VOLTAGE_ALARM_HI_CFG 0x76
+#define VBUS_VOLTAGE_ALARM_LO_CFG 0x78
+
+// 0x7A - 0x7F are reserved.
+
+// TODO: Define any vendor-defined bits here.
+// The TCPCI spec allocates registers 0x80 - 0xFF for vendor defined bits. \ No newline at end of file
diff --git a/usb/UcmTcpciCxClientSample/Sample.asl b/usb/UcmTcpciCxClientSample/Sample.asl
new file mode 100644
index 00000000..7e3cd9ba
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/Sample.asl
@@ -0,0 +1,142 @@
+//
+// Compile with:
+// asl.exe sample.asl
+//
+// Copy ACPITABL.dat to %windir%\system32, turn on testsigning, and reboot.
+//
+
+DefinitionBlock("ACPITABL.dat", "SSDT", 5, "MSFT", "TCPCI", 1)
+{
+ Scope(\_SB)
+ {
+// // TODO: Power resource for the reset power rail. This is used for platform-level device reset.
+// // Each of the devices that use this power resource must declare it.
+// PowerResource(PWFR, 0x5, 0x0)
+// {
+// Method(_RST, 0x0, NotSerialized) { }
+// // TODO: Placeholder methods as power resources need _ON, _OFF, _STA.
+// Method(_STA, 0x0, NotSerialized)
+// {
+// Return(0xF)
+// }
+//
+// Method(_ON_, 0x0, NotSerialized) { }
+//
+// Method(_OFF, 0x0, NotSerialized) { }
+//
+// } // PowerResource()
+
+ // UCM-TCPCI device. Can be named anything.
+ Device(USBC)
+ {
+ // This device needs to be enumerated by ACPI, so it needs a HWID.
+ // Your INF should match on it.
+ Name(_HID, "USBC0001")
+ Method(_CRS, 0x0, NotSerialized)
+ {
+ Name (RBUF, ResourceTemplate ()
+ {
+ //
+ // Sample I2C and GPIO resources. TODO: Modify to match your
+ // platform's underlying controllers and connections.
+ // \_SB.I2C and \_SB.GPIO are paths to predefined I2C
+ // and GPIO controller instances.
+ //
+ I2CSerialBus(0x50, ControllerInitiated, 400000, AddressingMode7Bit, "\\_SB.I2C1")
+ GpioInt(Level, ActiveLow, Exclusive, PullDown, 0, "\\_SB.GPI0") {5}
+
+ })
+ Return(RBUF)
+ }
+
+ // // Declare PWFR as the reset power rail
+ // Name(_PRR, Package(One)
+ // {
+ // \_SB.PWFR
+ // })
+
+ // Inside the scope of the UCM-TCPCI device, you need to define one "connector" device.
+ // It can be named anything.
+ Device(CON0)
+ {
+ // This device is not meant to be enumerated by ACPI, hence you should not assign a
+ // HWID to it. Instead, use _ADR to assign address 0 to it.
+ Name(_ADR, 0x00000000)
+
+ // _PLD as defined in the ACPI spec. The GroupToken and GroupPosition are used to
+ // derive a unique "Connector ID". This PLD should correlate with the PLD associated
+ // with the XHCI device for the same port.
+ Name(_PLD, Package()
+ {
+ Buffer()
+ {
+ 0x82, // Revision 2, ignore color.
+ 0x00,0x00,0x00, // Color (ignored).
+ 0x00,0x00,0x00,0x00, // Width and height.
+ 0x69, // User visible; Back panel; VerticalPos:Center.
+ 0x0c, // HorizontalPos:0; Shape:Vertical Rectangle; GroupOrientation:0.
+ 0x80,0x00, // Group Token:0; Group Position:1; So Connector ID is 1.
+ 0x00,0x00,0x00,0x00, // Not ejectable.
+ 0xFF,0xFF,0xFF,0xFF // Vert. and horiz. offsets not supplied.
+ }
+ })
+
+ // _UPC as defined in the ACPI spec.
+ Name(_UPC, Package()
+ {
+ 0x01, // Port is connectable.
+ 0x09, // Connector type: Type C connector - USB2 and SS with switch.
+ 0x00000000, // Reserved0 must be zero.
+ 0x00000000 // Reserved1 must be zero.
+ })
+
+ Name(_DSD, Package()
+ {
+ // The UUID for Type-C connector capabilities.
+ ToUUID("6b856e62-40f4-4688-bd46-5e888a2260de"),
+
+ // The data structure which contains the connector capabilities. Each package
+ // element contains two elements: the capability type ID, and the capability data
+ // (which depends on the capability type). Note that any information defined here
+ // will override similar information described by the driver itself. For example, if
+ // the driver claims the port controller is DRP-capable, but ACPI says it is UFP-only
+ // ACPI will take precedence.
+ Package()
+ {
+ Package() {1, 4}, // Supported operating modes (DRP).
+ Package() {2, 1}, // Supported Type-C sourcing capabilities (DefaultUSB).
+ Package() {3, 0}, // Audio accessory capable (False).
+ Package() {4, 1}, // Is PD supported (True).
+ Package() {5, 3}, // Supported power roles (Sink and Source).
+ Package()
+ {
+ 6, // Capability type ID of PD Source Capabilities.
+ Package()
+ {
+ 0x0001905A // Source PDO #0: Fixed:5V, 900mA. No need to describe fixed bits.
+ }
+ },
+ Package()
+ {
+ 7, // Capability type ID of PD Sink Capabilities.
+ Package ()
+ {
+ 0x00019096 // Sink PDO #0: Fixed:5V, 1.5A. No need to describe fixed bits.
+ }
+ },
+ Package()
+ {
+ 8, // Capability type ID of supported PD Alternate Modes.
+ // TODO: If your device supports alternate modes, update this with the SVID
+ // and Mode of the alternate modes.
+ Package()
+ {
+ 0x1111, 0x22222222
+ }
+ }
+ }
+ })
+ } // Device(CON0)
+ } // Device(USBC)
+ } // Scope(\_SB)
+} // DefinitionBlock
diff --git a/usb/UcmTcpciCxClientSample/Trace.h b/usb/UcmTcpciCxClientSample/Trace.h
new file mode 100644
index 00000000..830437ec
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/Trace.h
@@ -0,0 +1,61 @@
+/*++
+
+Module Name:
+
+ Trace.h
+
+Abstract:
+
+ This file contains the debug tracing related function declarations and macros.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#pragma once
+
+// Define the tracing flags.
+// TODO: Define your own tracing GUID
+// Tracing GUID - 1dc982f3-068f-4577-bcdf-1bc844e457b2
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID( \
+ UcmTcpciCxClientSampleTraceGuid, (1dc982f3,068f,4577,bcdf,1bc844e457b2), \
+ \
+ WPP_DEFINE_BIT(TRACE_ALERT) \
+ WPP_DEFINE_BIT(TRACE_DRIVER) \
+ WPP_DEFINE_BIT(TRACE_DEVICE) \
+ WPP_DEFINE_BIT(TRACE_QUEUE) \
+ WPP_DEFINE_BIT(TRACE_I2C) \
+ WPP_DEFINE_BIT(TRACE_PORTCONTROLLERINTERFACE) \
+ )
+
+#define WPP_FLAG_LEVEL_LOGGER(flag, level) \
+ WPP_LEVEL_LOGGER(flag)
+
+#define WPP_FLAG_LEVEL_ENABLED(flag, level) \
+ (WPP_LEVEL_ENABLED(flag) && \
+ WPP_CONTROL(WPP_BIT_ ## flag).Level >= level)
+
+#define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) \
+ WPP_LEVEL_LOGGER(flags)
+
+#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \
+ (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl)
+
+//
+// This comment block is scanned by the trace preprocessor to define our
+// Trace function.
+//
+// begin_wpp config
+// FUNC TraceEvents(LEVEL, FLAGS, MSG, ...);
+//
+// FUNC TRACE_FUNC_ENTRY{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS, ...);
+// USESUFFIX(TRACE_FUNC_ENTRY, "%!FUNC! Entry");
+//
+// FUNC TRACE_FUNC_EXIT{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS, ...);
+// USESUFFIX(TRACE_FUNC_EXIT, "%!FUNC! Exit");
+//
+// end_wpp
+//
diff --git a/usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.inf b/usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.inf
new file mode 100644
index 00000000..9fbeeeec
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.inf
Binary files differ
diff --git a/usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.sln b/usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.sln
new file mode 100644
index 00000000..c79b6211
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.sln
@@ -0,0 +1,40 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.24720.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UcmTcpciCxClientSample", "UcmTcpciCxClientSample.vcxproj", "{2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM = Debug|ARM
+ Debug|ARM64 = Debug|ARM64
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|ARM = Release|ARM
+ Release|ARM64 = Release|ARM64
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Debug|ARM.ActiveCfg = Debug|ARM
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Debug|ARM.Build.0 = Debug|ARM
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Debug|ARM64.Build.0 = Debug|ARM64
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Debug|x64.ActiveCfg = Debug|x64
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Debug|x64.Build.0 = Debug|x64
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Debug|x86.ActiveCfg = Debug|Win32
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Debug|x86.Build.0 = Debug|Win32
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Release|ARM.ActiveCfg = Release|ARM
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Release|ARM.Build.0 = Release|ARM
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Release|ARM64.ActiveCfg = Release|ARM64
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Release|ARM64.Build.0 = Release|ARM64
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Release|x64.ActiveCfg = Release|x64
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Release|x64.Build.0 = Release|x64
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Release|x86.ActiveCfg = Release|Win32
+ {2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.vcxproj b/usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.vcxproj
new file mode 100644
index 00000000..8be9bcb7
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.vcxproj
@@ -0,0 +1,186 @@
+<?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>
+ <ProjectConfiguration Include="Debug|ARM">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|ARM">
+ <Configuration>Release</Configuration>
+ <Platform>ARM</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|ARM64">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|ARM64">
+ <Configuration>Release</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="Alert.cpp" />
+ <ClCompile Include="I2C.cpp" />
+ <ClCompile Include="Device.cpp" />
+ <ClCompile Include="Driver.cpp" />
+ <ClCompile Include="PortControllerInterface.cpp" />
+ <ClCompile Include="Queue.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="Alert.h" />
+ <ClInclude Include="I2C.h" />
+ <ClInclude Include="Device.h" />
+ <ClInclude Include="Driver.h" />
+ <ClInclude Include="PortControllerInterface.h" />
+ <ClInclude Include="Queue.h" />
+ <ClInclude Include="Register.h" />
+ <ClInclude Include="Trace.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Include="UcmTcpciCxClientSample.inf" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{2BE4D87B-CC81-43AB-8A6D-E3A90161A1E7}</ProjectGuid>
+ <TemplateGuid>{497e31cb-056b-4f31-abb8-447fd55ee5a5}</TemplateGuid>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
+ <Configuration>Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <RootNamespace>UcmTcpciCxClientSample</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ <DriverType>KMDF</DriverType>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ <DriverType>KMDF</DriverType>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ <DriverType>KMDF</DriverType>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ <DriverType>KMDF</DriverType>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ <DriverType>KMDF</DriverType>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ <DriverType>KMDF</DriverType>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ <DriverType>KMDF</DriverType>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ <DriverType>KMDF</DriverType>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
+ <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
+ <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+ </PropertyGroup>
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <WppEnabled>true</WppEnabled>
+ <WppRecorderEnabled>true</WppRecorderEnabled>
+ <WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
+ <WppKernelMode>true</WppKernelMode>
+ <AdditionalIncludeDirectories>$(DDK_INC_PATH)\UcmTcpci\1.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <WppAdditionalOptions>-scan:"$(DDK_INC_PATH)\UcmTcpci\1.0\UcmTcpciTraceEnums.h"</WppAdditionalOptions>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(DDK_LIB_PATH)\UcmTcpci\1.0\UcmTcpciCxStub.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <FilesToPackage Include="$(TargetPath)" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.vcxproj.Filters b/usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.vcxproj.Filters
new file mode 100644
index 00000000..3a457df2
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.vcxproj.Filters
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ <Filter Include="Driver Files">
+ <UniqueIdentifier>{8E41214B-6785-4CFE-B992-037D68949A14}</UniqueIdentifier>
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Include="UcmTcpciCxClientSample.inf">
+ <Filter>Driver Files</Filter>
+ </Inf>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="Driver.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Queue.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Trace.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="PortControllerInterface.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Alert.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="I2C.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Device.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Register.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="Device.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Driver.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Alert.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="I2C.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PortControllerInterface.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Queue.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/usb/UcmTcpciCxClientSample/makefile b/usb/UcmTcpciCxClientSample/makefile
new file mode 100644
index 00000000..d5bedee2
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/makefile
@@ -0,0 +1,8 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the driver components of the Windows NT DDK
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.def
+
diff --git a/usb/UcmTcpciCxClientSample/resource.rc b/usb/UcmTcpciCxClientSample/resource.rc
new file mode 100644
index 00000000..81e21105
--- /dev/null
+++ b/usb/UcmTcpciCxClientSample/resource.rc
@@ -0,0 +1,9 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR "Sample driver for USB Connector Manager (Ucm) Type-C Port Controller Interface (Tcpci) Class Extension (Cx) Client"
+#define VER_INTERNALNAME_STR "UcmTcpciCxClientSample.sys"
+
+#include <common.ver>