From dac7dd51805e1ae6c756b4cbfc1a19f6c1743546 Mon Sep 17 00:00:00 2001 From: Robert Zieba Date: Mon, 16 Mar 2026 13:33:40 -0700 Subject: [PATCH 1/2] Fix power consumption calculation with type-C sinks Implicit source contracts were not being included in the power policy's calculations. Also fix provider disconnected events not being broadcast in some cases. --- power-policy-service/src/lib.rs | 21 ++++++++++++++++++++- power-policy-service/src/provider.rs | 2 +- type-c-service/src/driver/tps6699x.rs | 3 ++- type-c-service/src/wrapper/mod.rs | 7 ++++++- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/power-policy-service/src/lib.rs b/power-policy-service/src/lib.rs index c43719f8..d1f1dde9 100644 --- a/power-policy-service/src/lib.rs +++ b/power-policy-service/src/lib.rs @@ -12,6 +12,8 @@ pub mod provider; pub mod task; pub use config::Config; + +use crate::provider::PowerState; pub mod charger; const MAX_CONNECTED_PROVIDERS: usize = 4; @@ -105,7 +107,24 @@ impl PowerPolicy { /// /// Returns true if the device was operating as a provider async fn remove_connected_provider(&self, device_id: DeviceId) -> bool { - if self.state.lock().await.connected_providers.remove(&device_id) { + let mut state = self.state.lock().await; + + if state.connected_providers.remove(&device_id) { + // Determine total requested power draw + let mut total_power_mw = 0; + for device in self.context.devices().iter_only::() { + total_power_mw += device + .provider_capability() + .await + .map_or(0, |cap| cap.capability.max_power_mw()); + } + + if total_power_mw > self.config.limited_power_threshold_mw { + state.current_provider_state.state = PowerState::Limited; + } else { + state.current_provider_state.state = PowerState::Unlimited; + } + self.comms_notify(CommsMessage { data: CommsData::ProviderDisconnected(device_id), }) diff --git a/power-policy-service/src/provider.rs b/power-policy-service/src/provider.rs index 83050957..2b31c163 100644 --- a/power-policy-service/src/provider.rs +++ b/power-policy-service/src/provider.rs @@ -22,7 +22,7 @@ pub enum PowerState { #[derive(Clone, Copy, Default)] pub(super) struct State { /// Current power state - state: PowerState, + pub state: PowerState, } impl PowerPolicy { diff --git a/type-c-service/src/driver/tps6699x.rs b/type-c-service/src/driver/tps6699x.rs index 6e78becd..64761648 100644 --- a/type-c-service/src/driver/tps6699x.rs +++ b/type-c-service/src/driver/tps6699x.rs @@ -372,7 +372,8 @@ impl Controller for Tps6699x<'_, M, B> { port_status.dual_power = source_pdos[0].dual_role_power(); port_status.unconstrained_power = source_pdos[0].unconstrained_power(); } - } else if pd_status.is_source() { + } else if status.port_role() { + // port_role is true for source // Implicit source contract let current = TypecCurrent::try_from(port_control.typec_current()).map_err(Error::Pd)?; debug!("Port{} type-C source current: {:#?}", port.0, current); diff --git a/type-c-service/src/wrapper/mod.rs b/type-c-service/src/wrapper/mod.rs index 73846318..db89ae8e 100644 --- a/type-c-service/src/wrapper/mod.rs +++ b/type-c-service/src/wrapper/mod.rs @@ -237,6 +237,11 @@ where .lookup_global_port(local_port_id) .map_err(Error::Pd)?; + let previous_status = state + .port_states() + .get(local_port_id.0 as usize) + .ok_or(Error::Pd(PdError::InvalidPort))? + .status; let status = controller.get_port_status(local_port_id).await?; trace!("Port{} status: {:#?}", global_port_id.0, status); @@ -289,7 +294,7 @@ where self.process_new_consumer_contract(power, &status).await?; } - if status_event.new_power_contract_as_provider() { + if status.is_connected() && status.available_source_contract != previous_status.available_source_contract { self.process_new_provider_contract(power, &status).await?; } From abe529b9eb8d8b46913c507702b859eac8ab7722 Mon Sep 17 00:00:00 2001 From: Robert Zieba Date: Tue, 17 Mar 2026 13:33:44 -0700 Subject: [PATCH 2/2] power-policy-service: Don't hold lock in `remove_connected_provider` --- power-policy-service/src/lib.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/power-policy-service/src/lib.rs b/power-policy-service/src/lib.rs index d1f1dde9..bd463bc3 100644 --- a/power-policy-service/src/lib.rs +++ b/power-policy-service/src/lib.rs @@ -107,9 +107,7 @@ impl PowerPolicy { /// /// Returns true if the device was operating as a provider async fn remove_connected_provider(&self, device_id: DeviceId) -> bool { - let mut state = self.state.lock().await; - - if state.connected_providers.remove(&device_id) { + if self.state.lock().await.connected_providers.remove(&device_id) { // Determine total requested power draw let mut total_power_mw = 0; for device in self.context.devices().iter_only::() { @@ -119,11 +117,12 @@ impl PowerPolicy { .map_or(0, |cap| cap.capability.max_power_mw()); } - if total_power_mw > self.config.limited_power_threshold_mw { - state.current_provider_state.state = PowerState::Limited; - } else { - state.current_provider_state.state = PowerState::Unlimited; - } + self.state.lock().await.current_provider_state.state = + if total_power_mw > self.config.limited_power_threshold_mw { + PowerState::Limited + } else { + PowerState::Unlimited + }; self.comms_notify(CommsMessage { data: CommsData::ProviderDisconnected(device_id),