diff options
| author | Philip Froese <[email protected]> | 2016-10-19 13:25:24 -0700 |
|---|---|---|
| committer | Philip Froese <[email protected]> | 2016-10-19 13:25:24 -0700 |
| commit | 7995677b7883bad51cbadd6bd1d1674dbd689c38 (patch) | |
| tree | bbde63ce3535c264de5202e337a86fb0606819b7 | |
| parent | ce06de38a6daee9815dbf7eb8ddd8f335612ccc0 (diff) | |
Adding UcmTcpciCxClientSample
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 Binary files differnew file mode 100644 index 00000000..9fbeeeec --- /dev/null +++ b/usb/UcmTcpciCxClientSample/UcmTcpciCxClientSample.inf 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> |
