summaryrefslogtreecommitdiff
path: root/usb/UcmCxUcsi/UcmNotifications.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'usb/UcmCxUcsi/UcmNotifications.cpp')
-rw-r--r--usb/UcmCxUcsi/UcmNotifications.cpp909
1 files changed, 909 insertions, 0 deletions
diff --git a/usb/UcmCxUcsi/UcmNotifications.cpp b/usb/UcmCxUcsi/UcmNotifications.cpp
new file mode 100644
index 00000000..babed947
--- /dev/null
+++ b/usb/UcmCxUcsi/UcmNotifications.cpp
@@ -0,0 +1,909 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ UcmNotifications.cpp
+
+Abstract:
+
+ Type-C Platform Policy Manager. Interface to notify UcmCx of events.
+
+Environment:
+
+Kernel-mode only.
+
+--*/
+
+#include "Pch.h"
+#include "UcmNotifications.tmh"
+
+#pragma alloc_text(PAGE, Ucm_CreateConnectors)
+#pragma alloc_text(PAGE, Ucm_EvtGetPdosCompleted)
+#pragma alloc_text(PAGE, Ucm_EvtSetDataRoleCompleted)
+#pragma alloc_text(PAGE, Ucm_EvtSetPowerRoleCompleted)
+#pragma alloc_text(PAGE, Ucm_ReportTypeCAttach)
+#pragma alloc_text(PAGE, Ucm_ReportTypeCDetach)
+#pragma alloc_text(PAGE, Ucm_ReportPowerOperationMode)
+#pragma alloc_text(PAGE, Ucm_ReportPowerDirectionChanged)
+#pragma alloc_text(PAGE, Ucm_ReportDataDirectionChanged)
+#pragma alloc_text(PAGE, Ucm_ReportChargingStatusChanged)
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+Ucm_CreateConnectors (
+ _In_ PPPM_CONTEXT PpmCtx
+)
+/*++
+
+Routine Description:
+
+ Create connector objects and register them with UCM.
+
+Arguments:
+
+ PpmCtx - Platform policy manager context object.
+
+--*/
+{
+ NTSTATUS status;
+ WDFDEVICE device;
+ UCSI_GET_CAPABILITY_IN caps;
+ UCSI_GET_CONNECTOR_CAPABILITY_IN connCaps;
+ UCM_CONNECTOR_CONFIG connConfig;
+ UCM_CONNECTOR_TYPEC_CONFIG typeCConfig;
+ UCM_CONNECTOR_PD_CONFIG pdConfig;
+ ULONG supportedOperatingModes;
+ ULONG supportedPowerSourcingCapabilities;
+ ULONG supportedPdPowerRoles;
+ PPPM_CONNECTOR connector;
+ ULONG connectorCount;
+ WDF_OBJECT_ATTRIBUTES attributes;
+ PPPM_UCM_CONNECTOR_CONTEXT connectorCtx;
+ CONNECTOR_INDEX i;
+
+ UNREFERENCED_PARAMETER(PpmCtx);
+
+ PAGED_CODE();
+
+ TRACE_FUNC_ENTRY(TRACE_FLAG_UCMNOTIFICATIONS);
+
+ device = Context_GetWdfDevice(PpmCtx);
+
+ status = Ppm_GetCapability(PpmCtx, &caps);
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ TRACE_INFO(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] PPM optional features: 0x%x", device, caps.OptionalFeatures);
+
+ connectorCount = WdfCollectionGetCount(PpmCtx->Connectors);
+ if (connectorCount != caps.bNumConnectors)
+ {
+ status = STATUS_DEVICE_PROTOCOL_ERROR;
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Connector count mismatch. ACPI reported %lu, UCSI reported %lu", device, connectorCount, caps.bNumConnectors);
+ goto Exit;
+ }
+
+ for (i = 1; i <= connectorCount; ++i)
+ {
+ status = Ppm_GetConnectorCapability(PpmCtx, i, &connCaps);
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ connector = Ppm_GetConnector(PpmCtx, i);
+
+ supportedOperatingModes = (connCaps.OperationMode.DfpOnly ? UcmTypeCOperatingModeDfp : 0) |
+ (connCaps.OperationMode.UfpOnly ? UcmTypeCOperatingModeUfp : 0) |
+ (connCaps.OperationMode.Drp ? UcmTypeCOperatingModeDrp : 0);
+
+ if (connCaps.Provider)
+ {
+ supportedPowerSourcingCapabilities = UcmTypeCCurrentDefaultUsb |
+ UcmTypeCCurrent1500mA |
+ UcmTypeCCurrent3000mA;
+ }
+ else
+ {
+ supportedPowerSourcingCapabilities = 0;
+ }
+
+ //
+ // Assemble the Type-C and PD configuration for UCM.
+ //
+
+ UCM_CONNECTOR_CONFIG_INIT(&connConfig, connector->Id);
+
+ UCM_CONNECTOR_TYPEC_CONFIG_INIT(&typeCConfig,
+ supportedOperatingModes,
+ supportedPowerSourcingCapabilities);
+
+ typeCConfig.EvtSetDataRole = Ucm_EvtConnectorSetDataRole;
+ typeCConfig.AudioAccessoryCapable = connCaps.OperationMode.AudioAccessoryMode;
+
+ supportedPdPowerRoles = (connCaps.Provider ? UcmPowerRoleSource : 0) |
+ (connCaps.Consumer ? UcmPowerRoleSink : 0);
+
+ UCM_CONNECTOR_PD_CONFIG_INIT(&pdConfig, supportedPdPowerRoles);
+
+ pdConfig.EvtSetPowerRole = Ucm_EvtConnectorSetPowerRole;
+
+ connConfig.TypeCConfig = &typeCConfig;
+ connConfig.PdConfig = &pdConfig;
+
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, PPM_UCM_CONNECTOR_CONTEXT);
+
+ //
+ // Create the UCM connector object.
+ //
+
+ status = UcmConnectorCreate(device, &connConfig, &attributes, &connector->Handle);
+ if (!NT_SUCCESS(status))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] UcmConnectorCreate failed - %!STATUS!", device, status);
+ goto Exit;
+ }
+
+ connectorCtx = PpmUcmConnector_GetContext(connector->Handle);
+ connectorCtx->PpmConnector = connector;
+ }
+
+Exit:
+
+ TRACE_FUNC_EXIT(TRACE_FLAG_UCMNOTIFICATIONS);
+
+ return status;
+}
+
+
+VOID
+Ucm_EvtGetPdosCompleted (
+ _In_ UCSI_CONTROL Command,
+ _In_ PVOID Context,
+ _Inout_ PPPM_COMMAND_ACK_PARAMS CommandAckParams
+)
+/*++
+
+Routine Description:
+
+ GetPdos command completion routine. Notifies UCM of new source capabilities and PD contract.
+
+Arguments:
+
+ Command - The UCSI command that was completed. In this case, it should only be UcsiCommandGetPdos.
+
+ Context - Platform policy manager context object.
+
+ CommandAckParams - UCSI command acknowledge parameters.
+
+--*/
+{
+ WDFDEVICE device;
+ PPPM_CONTEXT ppmCtx;
+ PUCSI_GET_PDOS_IN pdos;
+ UCHAR pdoCount;
+ UINT8 pdoOffset;
+ UCSI_CONTROL newCmd;
+ PPPM_CONNECTOR connector;
+ UCM_CONNECTOR_PD_CONN_STATE_CHANGED_PARAMS connParams;
+ bool forPartner;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ TRACE_FUNC_ENTRY(TRACE_FLAG_UCMNOTIFICATIONS);
+
+ ppmCtx = (PPPM_CONTEXT)Context;
+ device = Context_GetWdfDevice(ppmCtx);
+
+ NT_ASSERT(Command.Command == UcsiCommandGetPdos);
+
+ //
+ // This GetPdos command was sent in response to a change in the PD contract. So we
+ // need to acknowledge the connector change that we know is pending.
+ //
+
+ CommandAckParams->AckConnectorChange = TRUE;
+
+ if (!UCSI_CMD_SUCCEEDED(ppmCtx->UcsiDataBlock->CCI))
+ {
+ //
+ // The command failed. Acknowledge the connector change and move on.
+ //
+
+ goto Exit;
+ }
+
+ pdos = &ppmCtx->UcsiDataBlock->MessageIn.Pdos;
+ pdoOffset = Command.GetPdos.PdoOffset;
+ pdoCount = (UCHAR)(ppmCtx->UcsiDataBlock->CCI.DataLength / sizeof(pdos->Pdos[0]));
+ connector = Ppm_GetConnector(ppmCtx, Command.GetPdos.ConnectorNumber);
+ forPartner = (Command.GetPdos.PartnerPdo == 1);
+
+ if (pdoCount == 0)
+ {
+ if (forPartner)
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Port partner of connector ID 0x%I64x returned zero source PDOs", device, connector->Id);
+ }
+ else
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Connector ID 0x%I64x returned zero source PDOs", device, connector->Id);
+ }
+
+ goto Exit;
+ }
+
+ NT_ASSERT_ASSUME(pdoCount <= MAX_MESSAGE_IN_PDOS);
+
+ if (pdoOffset >= ARRAYSIZE(ppmCtx->ConnectorChangeCtx.Pdos))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Invalid offset (%u) used in %!UCSI_COMMAND! command on connector ID 0x%I64x", device, pdoOffset, Command.Command, connector->Id);
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ RtlCopyMemory(&ppmCtx->ConnectorChangeCtx.Pdos[pdoOffset], pdos->Pdos, pdoCount * sizeof(pdos->Pdos[0]));
+ ppmCtx->ConnectorChangeCtx.PdoCount = pdoOffset + pdoCount;
+
+ //
+ // Check to see if there might be more PDOs to retrieve. If so, don't acknowledge the connector
+ // change just yet, and send the command again to fetch the remaining PDOs.
+ //
+ // If we got 4 PDOs in this batch (the maximum we can retrieve at once), and the total number of
+ // PDOs we have received so far is less than 7 (the maximum allowed by the PD spec), there may
+ // be more PDOs to retrieve.
+ //
+
+ if ((pdoCount == MAX_MESSAGE_IN_PDOS) && (ppmCtx->ConnectorChangeCtx.PdoCount < MAX_PDO_COUNT))
+ {
+ newCmd = Command;
+ newCmd.GetPdos.PdoOffset = ppmCtx->ConnectorChangeCtx.PdoCount;
+
+ status = Ppm_SendCommand(ppmCtx, newCmd, Ucm_EvtGetPdosCompleted, ppmCtx);
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ CommandAckParams->AckConnectorChange = FALSE;
+ goto Exit;
+ }
+
+ if (forPartner)
+ {
+ //
+ // Notify UCM of the partner's source capabilities.
+ //
+
+ TRACE_INFO(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Reporting PD port partner source caps on connector ID 0x%I64x", device, connector->Id);
+ status = UcmConnectorPdPartnerSourceCaps(connector->Handle,
+ ppmCtx->ConnectorChangeCtx.Pdos,
+ ppmCtx->ConnectorChangeCtx.PdoCount);
+ if (!NT_SUCCESS(status))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] UcmConnectorPdPartnerSourceCaps for connector ID 0x%I64x failed - %!STATUS!", device, connector->Id, status);
+ goto Exit;
+ }
+ }
+ else
+ {
+ //
+ // Notify UCM of our own source capabilities.
+ //
+
+ TRACE_INFO(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Reporting PD source caps on connector ID 0x%I64x", device, connector->Id);
+ status = UcmConnectorPdSourceCaps(connector->Handle,
+ ppmCtx->ConnectorChangeCtx.Pdos,
+ ppmCtx->ConnectorChangeCtx.PdoCount);
+ if (!NT_SUCCESS(status))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] UcmConnectorPdSourceCaps for connector ID 0x%I64x failed - %!STATUS!", device, connector->Id, status);
+ goto Exit;
+ }
+ }
+
+ UCM_CONNECTOR_PD_CONN_STATE_CHANGED_PARAMS_INIT(&connParams,
+ UcmPdConnStateNegotiationSucceeded);
+
+ if (forPartner)
+ {
+ connParams.ChargingState = ppmCtx->ConnectorChangeCtx.ChargingState;
+ }
+ connParams.Rdo = ppmCtx->ConnectorChangeCtx.Rdo;
+
+ //
+ // Notify UCM that the PD connection state has changed on a certain connector.
+ //
+
+ TRACE_INFO(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Reporting PD connection state change on connector ID 0x%I64x", device, connector->Id);
+
+ status = UcmConnectorPdConnectionStateChanged(connector->Handle, &connParams);
+ if (!NT_SUCCESS(status))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] UcmConnectorPdConnectionStateChanged for connector ID 0x%I64x failed - %!STATUS!", device, connector->Id, status);
+ goto Exit;
+ }
+
+ //
+ // A PD contract has been established at this point. Perform role correction if necessary.
+ //
+
+ if (connector->PerformRoleCorrectionOnNextPdContract)
+ {
+ connector->PerformRoleCorrectionOnNextPdContract = FALSE;
+ status = Ppm_PerformRoleCorrection(ppmCtx, connector);
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+ }
+
+Exit:
+
+ TRACE_FUNC_EXIT(TRACE_FLAG_UCMNOTIFICATIONS);
+}
+
+
+VOID
+Ucm_EvtSetPowerRoleCompleted (
+ _In_ UCSI_CONTROL Command,
+ _In_ PVOID Context,
+ _Inout_ PPPM_COMMAND_ACK_PARAMS CommandAckParams
+)
+/*++
+
+Routine Description:
+
+ Set power role command completion routine. Notifies UCM that the power direction has changed.
+
+Arguments:
+
+ Command - The UCSI command that was completed. In this case, it should be only SetPdr.PowerDirectionRole.
+
+ Context - Platform policy manager context object.
+
+ CommandAckParams - UCSI command acknowledge parameters.
+
+--*/
+{
+ UCM_POWER_ROLE powerRole;
+ PPPM_CONTEXT ppmCtx;
+ WDFDEVICE device;
+ PPPM_CONNECTOR connector;
+ BOOLEAN success;
+
+ UNREFERENCED_PARAMETER(CommandAckParams);
+
+ PAGED_CODE();
+
+ TRACE_FUNC_ENTRY(TRACE_FLAG_UCMNOTIFICATIONS);
+
+ ppmCtx = (PPPM_CONTEXT)Context;
+ device = Context_GetWdfDevice(ppmCtx);
+ connector = Ppm_GetConnector(ppmCtx, Command.SetPdr.ConnectorNumber);
+
+ NT_VERIFY(Convert((UCSI_POWER_DIRECTION_ROLE)Command.SetPdr.PowerDirectionRole, powerRole));
+
+ success = UCSI_CMD_SUCCEEDED(ppmCtx->UcsiDataBlock->CCI);
+
+ if (success)
+ {
+ TRACE_INFO(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Power role successfully changed to %!UCM_POWER_ROLE!", device, powerRole);
+ }
+ else
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Power role change to %!UCM_POWER_ROLE! failed", device, powerRole);
+ }
+
+ //
+ // Notify UCM that the power direction has changed.
+ //
+
+ UcmConnectorPowerDirectionChanged(connector->Handle, success, powerRole);
+
+ TRACE_FUNC_EXIT(TRACE_FLAG_UCMNOTIFICATIONS);
+}
+
+
+VOID
+Ucm_EvtSetDataRoleCompleted (
+ _In_ UCSI_CONTROL Command,
+ _In_ PVOID Context,
+ _Inout_ PPPM_COMMAND_ACK_PARAMS CommandAckParams
+)
+/*++
+
+Routine Description:
+
+ Set data role command completion routine. Notifies UCM that the power direction has changed.
+
+Arguments:
+
+ Command - The UCSI command that was completed. In this case, it should be only SetUor.UsbOperationRole.
+
+ Context - Platform policy manager context object.
+
+ CommandAckParams - UCSI command acknowledge parameters.
+
+--*/
+{
+ UCM_DATA_ROLE dataRole;
+ PPPM_CONTEXT ppmCtx;
+ WDFDEVICE device;
+ PPPM_CONNECTOR connector;
+ BOOLEAN success;
+
+ UNREFERENCED_PARAMETER(CommandAckParams);
+
+ PAGED_CODE();
+
+ TRACE_FUNC_ENTRY(TRACE_FLAG_UCMNOTIFICATIONS);
+
+ ppmCtx = (PPPM_CONTEXT)Context;
+ device = Context_GetWdfDevice(ppmCtx);
+ connector = Ppm_GetConnector(ppmCtx, Command.SetUor.ConnectorNumber);
+
+ NT_VERIFY(Convert((UCSI_USB_OPERATION_ROLE)Command.SetUor.UsbOperationRole, dataRole));
+
+ success = UCSI_CMD_SUCCEEDED(ppmCtx->UcsiDataBlock->CCI);
+
+ if (success)
+ {
+ TRACE_INFO(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Data role successfully changed to %!UCM_DATA_ROLE!", device, dataRole);
+ }
+ else
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Data role change to %!UCM_DATA_ROLE! failed", device, dataRole);
+ }
+
+ //
+ // Notify UCM that the data direction has changed.
+ //
+
+ UcmConnectorDataDirectionChanged(connector->Handle, success, dataRole);
+
+ TRACE_FUNC_EXIT(TRACE_FLAG_UCMNOTIFICATIONS);
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+Ucm_ReportPowerOperationMode (
+ _In_ PPPM_CONTEXT PpmCtx,
+ _In_ PPPM_CONNECTOR Connector,
+ _Inout_ PUCSI_GET_CONNECTOR_STATUS_IN ConnStatus,
+ _Inout_ PPPM_COMMAND_ACK_PARAMS CommandAckParams
+)
+/*++
+
+Routine Description:
+
+ Report power information to UCM.
+
+Arguments:
+
+ PpmCtx - Platform policy manager context object.
+
+ Connector - The connector for which to report the mode.
+
+ ConnStatus - The connector status for the given connector.
+
+ CommandAckParams - UCSI command acknowledge parameters.
+
+--*/
+{
+ WDFDEVICE device;
+ NTSTATUS status;
+ UCM_TYPEC_CURRENT currentAd;
+
+ PAGED_CODE();
+
+ TRACE_FUNC_ENTRY(TRACE_FLAG_UCMNOTIFICATIONS);
+
+ device = Context_GetWdfDevice(PpmCtx);
+
+ if (ConnStatus->PowerOperationMode == UcsiPowerOperationModePd)
+ {
+ Ppm_ReportNegotiatedPowerLevelChanged(PpmCtx, Connector, ConnStatus, CommandAckParams);
+
+ goto Exit;
+ }
+
+ //
+ // The other cases indicate a change in Type-C current advertisement.
+ //
+ if (!Convert((UCSI_POWER_OPERATION_MODE)ConnStatus->PowerOperationMode, currentAd))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Invalid power operation mode %u", device, ConnStatus->PowerOperationMode);
+ goto Exit;
+ }
+
+ TRACE_INFO(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Reporting Type-C current advertisement change on connector ID 0x%I64x", device, Connector->Id);
+
+ //
+ // Notify UCM that the Type-C current advertisement has changed.
+ //
+
+ status = UcmConnectorTypeCCurrentAdChanged(Connector->Handle, currentAd);
+ if (!NT_SUCCESS(status))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] UcmConnectorTypeCCurrentAdChanged for connector 0x%I64x failed - %!STATUS!", device, Connector->Id, status);
+ goto Exit;
+ }
+
+ ConnStatus->ConnectorStatusChange.PowerOperationModeChange = 0;
+
+Exit:
+
+ TRACE_FUNC_EXIT(TRACE_FLAG_UCMNOTIFICATIONS);
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+Ucm_ReportTypeCAttach (
+ _In_ PPPM_CONTEXT PpmCtx,
+ _In_ PPPM_CONNECTOR Connector,
+ _Inout_ PUCSI_GET_CONNECTOR_STATUS_IN ConnStatus,
+ _Inout_ PPPM_COMMAND_ACK_PARAMS CommandAckParams
+)
+/*++
+
+Routine Description:
+
+ Report a Type-C attach event to UCM.
+
+Arguments:
+
+ PpmCtx - Platform policy manager context object.
+
+ Connector - The connector for which to report the attach.
+
+ ConnStatus - The connector status for the given connector.
+
+ CommandAckParams - UCSI command acknowledge parameters.
+
+--*/
+{
+ WDFDEVICE device;
+ NTSTATUS status;
+ UCM_CONNECTOR_TYPEC_ATTACH_PARAMS typeCParams;
+ UCM_TYPEC_PARTNER partner;
+
+ UNREFERENCED_PARAMETER(CommandAckParams);
+
+ PAGED_CODE();
+
+ TRACE_FUNC_ENTRY(TRACE_FLAG_UCMNOTIFICATIONS);
+
+ device = Context_GetWdfDevice(PpmCtx);
+
+ TRACE_INFO(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Reporting Type-C attach on connector ID 0x%I64x", device, Connector->Id);
+
+ if (!Convert((UCSI_CONNECTOR_PARTNER_TYPE)ConnStatus->ConnectorPartnerType, partner))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Invalid connector partner type %u", device, ConnStatus->ConnectorPartnerType);
+ goto Exit;
+ }
+
+ UCM_CONNECTOR_TYPEC_ATTACH_PARAMS_INIT(&typeCParams, partner);
+
+ if (ConnStatus->PowerDirection == UcsiPowerDirectionConsumer)
+ {
+ if (!Convert((UCSI_BATTERY_CHARGING_STATUS)ConnStatus->BatteryChargingStatus,
+ typeCParams.ChargingState))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Invalid battery charging state %u", device, ConnStatus->BatteryChargingStatus);
+ goto Exit;
+ }
+ }
+
+ //
+ // N.B. Even in the case of PD, convert the power operation mode to the some Type-C
+ // current advertisement. UCSI has no way of telling us the Type-C advertisement when
+ // there is a PD connection, and the PD spec has certain rules around what that
+ // advertisement should be. Since we don't have that information from UCSI,
+ // we simply assume the current level is 3.0A in the PD case.
+ //
+
+ if (!Convert((UCSI_POWER_OPERATION_MODE)ConnStatus->PowerOperationMode,
+ typeCParams.CurrentAdvertisement))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Invalid connector power operation mode %u", device, ConnStatus->PowerOperationMode);
+ goto Exit;
+ }
+
+ //
+ // Notify UCM that we have detected an attach event on the connector.
+ //
+
+ status = UcmConnectorTypeCAttach(Connector->Handle, &typeCParams);
+ if (!NT_SUCCESS(status))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] UcmConnectorTypeCAttach failed - %!STATUS!", device, status);
+ goto Exit;
+ }
+
+ //
+ // Clear all the bits for information we may have processed here.
+ //
+
+ ConnStatus->ConnectorStatusChange.ConnectChange = 0;
+ ConnStatus->ConnectorStatusChange.ConnectorPartnerChange = 0;
+ ConnStatus->ConnectorStatusChange.BatteryChargingStatusChange = 0;
+ ConnStatus->ConnectorStatusChange.PowerOperationModeChange = 0;
+
+ if (!PpmCtx->IsUsbDeviceControllerEnabled && (partner == UcmTypeCPartnerDfp))
+ {
+ Connector->PerformRoleCorrectionOnNextPdContract = TRUE;
+ }
+ else
+ {
+ Connector->PerformRoleCorrectionOnNextPdContract = FALSE;
+ }
+
+Exit:
+
+ TRACE_FUNC_EXIT(TRACE_FLAG_UCMNOTIFICATIONS);
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+Ucm_ReportTypeCDetach (
+ _In_ PPPM_CONTEXT PpmCtx,
+ _In_ PPPM_CONNECTOR Connector,
+ _Inout_ PUCSI_GET_CONNECTOR_STATUS_IN ConnStatus,
+ _Inout_ PPPM_COMMAND_ACK_PARAMS CommandAckParams
+)
+/*++
+
+Routine Description:
+
+ Report a Type-C detach event to UCM.
+
+Arguments:
+
+ PpmCtx - Platform policy manager context object.
+
+ Connector - The connector for which to report the detach.
+
+ ConnStatus - The connector status for the given connector.
+
+ CommandAckParams - UCSI command acknowledge parameters.
+
+--*/
+{
+ WDFDEVICE device;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(ConnStatus);
+ UNREFERENCED_PARAMETER(CommandAckParams);
+
+ PAGED_CODE();
+
+ TRACE_FUNC_ENTRY(TRACE_FLAG_UCMNOTIFICATIONS);
+
+ device = Context_GetWdfDevice(PpmCtx);
+
+ TRACE_INFO(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Reporting Type-C detach on connector ID 0x%I64x", device, Connector->Id);
+
+ //
+ // Notify UCM that we have detected a detach event on the connector.
+ //
+
+ status = UcmConnectorTypeCDetach(Connector->Handle);
+ if (!NT_SUCCESS(status))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] UcmConnectorTypeCDetach failed - %!STATUS!", device, status);
+ goto Exit;
+ }
+
+Exit:
+
+ TRACE_FUNC_EXIT(TRACE_FLAG_UCMNOTIFICATIONS);
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+Ucm_ReportPowerDirectionChanged (
+ _In_ PPPM_CONTEXT PpmCtx,
+ _In_ PPPM_CONNECTOR Connector,
+ _Inout_ PUCSI_GET_CONNECTOR_STATUS_IN ConnStatus,
+ _Inout_ PPPM_COMMAND_ACK_PARAMS CommandAckParams
+)
+/*++
+
+Routine Description:
+
+ Report to UCM that the power direction has changed. This may be called
+ after a role swap completes.
+
+Arguments:
+
+ PpmCtx - Platform policy manager context object.
+
+ Connector - The connector for which to report the power direction change.
+
+ ConnStatus - The connector status for the given connector.
+
+ CommandAckParams - UCSI command acknowledge parameters.
+
+--*/
+{
+ WDFDEVICE device;
+ UCM_POWER_ROLE powerRole;
+
+ UNREFERENCED_PARAMETER(CommandAckParams);
+
+ PAGED_CODE();
+
+ TRACE_FUNC_ENTRY(TRACE_FLAG_UCMNOTIFICATIONS);
+
+ device = Context_GetWdfDevice(PpmCtx);
+
+ if (!Convert((UCSI_POWER_DIRECTION)ConnStatus->PowerDirection, powerRole))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Invalid power direction %u", device, ConnStatus->PowerDirection);
+ goto Exit;
+ }
+
+ TRACE_INFO(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Reporting power role change to %!UCM_POWER_ROLE! on connector ID 0x%I64x", device, powerRole, Connector->Id);
+
+ //
+ // Notify UCM that the power direction has changed.
+ //
+
+ UcmConnectorPowerDirectionChanged(Connector->Handle, TRUE, powerRole);
+
+ ConnStatus->ConnectorStatusChange.PowerDirectionChange = 0;
+
+Exit:
+
+ TRACE_FUNC_EXIT(TRACE_FLAG_UCMNOTIFICATIONS);
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+Ucm_ReportDataDirectionChanged (
+ _In_ PPPM_CONTEXT PpmCtx,
+ _In_ PPPM_CONNECTOR Connector,
+ _Inout_ PUCSI_GET_CONNECTOR_STATUS_IN ConnStatus,
+ _Inout_ PPPM_COMMAND_ACK_PARAMS CommandAckParams
+)
+/*++
+
+Routine Description:
+
+ Report to UCM that the data direction has changed. This may be called
+ after a role swap completes.
+
+Arguments:
+
+ PpmCtx - Platform policy manager context object.
+
+ Connector - The connector for which to report the data direction change.
+
+ ConnStatus - The connector status for the given connector.
+
+ CommandAckParams - UCSI command acknowledge parameters.
+
+--*/
+{
+ WDFDEVICE device;
+ UCM_TYPEC_PARTNER partnerType;
+ UCM_DATA_ROLE newDataRole;
+
+ UNREFERENCED_PARAMETER(CommandAckParams);
+
+ PAGED_CODE();
+
+ TRACE_FUNC_ENTRY(TRACE_FLAG_UCMNOTIFICATIONS);
+
+ device = Context_GetWdfDevice(PpmCtx);
+
+ if (!Convert((UCSI_CONNECTOR_PARTNER_TYPE)ConnStatus->ConnectorPartnerType, partnerType))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Invalid connector partner type %u", device, ConnStatus->ConnectorPartnerType);
+ goto Exit;
+ }
+
+ if ((partnerType != UcmTypeCPartnerDfp) && (partnerType != UcmTypeCPartnerUfp))
+ {
+ //
+ // This is one of those other cases where the partner type changed, for instance,
+ // PoweredCableNoUfp changed to PoweredCableWithUfp.
+ //
+
+ goto Exit;
+ }
+
+ newDataRole = (partnerType == UcmTypeCPartnerDfp) ? UcmDataRoleUfp : UcmDataRoleDfp;
+
+ TRACE_INFO(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Reporting date role change to %!UCM_DATA_ROLE! on connector ID 0x%I64x", device, newDataRole, Connector->Id);
+
+ //
+ // Notify UCM that the data direction has changed.
+ //
+
+ UcmConnectorDataDirectionChanged(Connector->Handle, TRUE, newDataRole);
+
+ ConnStatus->ConnectorStatusChange.ConnectorPartnerChange = 0;
+
+Exit:
+
+ TRACE_FUNC_EXIT(TRACE_FLAG_UCMNOTIFICATIONS);
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+Ucm_ReportChargingStatusChanged (
+ _In_ PPPM_CONTEXT PpmCtx,
+ _In_ PPPM_CONNECTOR Connector,
+ _Inout_ PUCSI_GET_CONNECTOR_STATUS_IN ConnStatus,
+ _Inout_ PPPM_COMMAND_ACK_PARAMS CommandAckParams
+)
+/*++
+
+Routine Description:
+
+ Report to UCM that the charging status has changed. This may be called
+ after a role swap completes.
+
+Arguments:
+
+ PpmCtx - Platform policy manager context object.
+
+ Connector - The connector for which to report the charging status change.
+
+ ConnStatus - The connector status for the given connector.
+
+ CommandAckParams - UCSI command acknowledge parameters.
+
+--*/
+{
+ WDFDEVICE device;
+ UCM_CHARGING_STATE chargingState;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(CommandAckParams);
+
+ PAGED_CODE();
+
+ TRACE_FUNC_ENTRY(TRACE_FLAG_UCMNOTIFICATIONS);
+
+ device = Context_GetWdfDevice(PpmCtx);
+
+ if (!Convert((UCSI_BATTERY_CHARGING_STATUS)ConnStatus->BatteryChargingStatus, chargingState))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Invalid charging state %u", device, ConnStatus->BatteryChargingStatus);
+ goto Exit;
+ }
+
+ TRACE_INFO(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Reporting charging status change to %!UCM_CHARGING_STATE! on connector ID 0x%I64x", device, chargingState, Connector->Id);
+
+ //
+ // Notify UCM that the charging state has changed.
+ //
+
+ status = UcmConnectorChargingStateChanged(Connector->Handle, chargingState);
+ if (!NT_SUCCESS(status))
+ {
+ TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] UcmConnectorChargingStateChanged failed - %!STATUS!", device, status);
+ goto Exit;
+ }
+
+ ConnStatus->ConnectorStatusChange.BatteryChargingStatusChange = 0;
+
+Exit:
+
+ TRACE_FUNC_EXIT(TRACE_FLAG_UCMNOTIFICATIONS);
+}