Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 40 additions & 6 deletions examples/std/src/lib/type_c/mock_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ use embedded_services::GlobalRawMutex;
use embedded_services::named::Named;
use embedded_usb_pd::ado::Ado;
use embedded_usb_pd::vdm::structured::command::discover_identity::{sop, sop_prime};
use embedded_usb_pd::{LocalPortId, PdError};
use embedded_usb_pd::{LocalPortId, PdError, pdo};
use embedded_usb_pd::{PowerRole, type_c::Current};
use embedded_usb_pd::{type_c::ConnectionState, ucsi::v1_2::lpm};
use log::{debug, info};

use power_policy_interface::capability::PowerCapability;
use type_c_interface::control::dp::{DpConfig, DpPinConfig, DpStatus};
use type_c_interface::control::pd::{PdStateMachineConfig, PortStatus};
use type_c_interface::control::pd::{
PdSinkInfo, PdSourceInfo, PdStateMachineConfig, PortStatus, SinkContract, SourceContract,
};
use type_c_interface::control::power::SystemPowerState;
use type_c_interface::control::retimer::RetimerFwUpdateState;
use type_c_interface::control::svid::DiscoveredSvids;
Expand Down Expand Up @@ -55,13 +57,45 @@ impl ControllerState {
let mut events = PortEventBitfield::none();
match role {
PowerRole::Source => {
status.available_source_contract = Some(capability);
status.unconstrained_power = unconstrained;
let pdo = pdo::source::Pdo::Fixed(pdo::source::FixedData {
voltage_mv: capability.voltage_mv,
current_ma: capability.current_ma,
..Default::default()
});
status.available_source_contract = Some(SourceContract {
capability,
pd: pdo::Rdo::for_pdo(0, pdo).map(|rdo| PdSourceInfo {
rx_fixed_5v_data: Some(pdo::sink::FixedData {
voltage_mv: capability.voltage_mv,
operational_current_ma: capability.current_ma,
unconstrained_power: unconstrained,
..Default::default()
}),
pdo,
rdo,
}),
});
events.status.set_new_power_contract_as_provider(true);
}
PowerRole::Sink => {
status.available_sink_contract = Some(capability);
status.unconstrained_power = unconstrained;
let pdo = pdo::sink::Pdo::Fixed(pdo::sink::FixedData {
voltage_mv: capability.voltage_mv,
operational_current_ma: capability.current_ma,
..Default::default()
});
status.available_sink_contract = Some(SinkContract {
capability,
pd: pdo::Rdo::for_pdo(0, pdo).map(|rdo| PdSinkInfo {
rx_fixed_5v_data: pdo::source::FixedData {
voltage_mv: capability.voltage_mv,
current_ma: capability.current_ma,
unconstrained_power: unconstrained,
..Default::default()
},
pdo,
rdo,
}),
});
events.status.set_new_power_contract_as_consumer(true);
events.status.set_sink_ready(true);
}
Expand Down
47 changes: 41 additions & 6 deletions type-c-interface-mocks/src/port/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ use embedded_services::error;
use embedded_services::event::NonBlockingSender;
use embedded_services::named::Named;
use embedded_usb_pd::vdm::structured::command::discover_identity::{sop, sop_prime};
use embedded_usb_pd::{PdError, PlugOrientation, PowerRole, ado::Ado, type_c::ConnectionState};
use embedded_usb_pd::{PdError, PlugOrientation, PowerRole, ado::Ado, pdo, type_c::ConnectionState};
use power_policy_interface::capability::{
ConsumerFlags, ConsumerPowerCapability, PowerCapability, ProviderFlags, ProviderPowerCapability, PsuType,
};
use power_policy_interface::psu::{Error as PsuError, Psu, State};
use type_c_interface::control::{
dp::{DpConfig, DpStatus},
pd::PortStatus,
pd::{PdSinkInfo, PdSourceInfo, PortStatus, SinkContract, SourceContract},
svid::DiscoveredSvids,
tbt::TbtConfig,
usb::UsbControlConfig,
Expand Down Expand Up @@ -100,7 +100,6 @@ where
) -> Result<(), PsuError> {
let mut status = PortStatus::new();
status.connection_state = Some(ConnectionState::Attached);
status.dual_power = config.dual_power;
status.plug_orientation = config.plug_orientation;
status.power_role = role;
self.psu_state.attach()?;
Expand All @@ -121,7 +120,24 @@ where
PowerRole::Sink => {
status_event.set_new_power_contract_as_consumer(true);
status_event.set_sink_ready(true);
status.available_sink_contract = Some(capability);
let pdo = pdo::sink::Pdo::Fixed(pdo::sink::FixedData {
voltage_mv: capability.voltage_mv,
operational_current_ma: capability.current_ma,
..Default::default()
});
status.available_sink_contract = Some(SinkContract {
capability,
pd: pdo::Rdo::for_pdo(0, pdo).map(|rdo| PdSinkInfo {
rx_fixed_5v_data: pdo::source::FixedData {
dual_role_power: config.dual_power,
voltage_mv: capability.voltage_mv,
current_ma: capability.current_ma,
..Default::default()
},
pdo,
rdo,
}),
});
self.psu_state
.update_consumer_power_capability(Some(ConsumerPowerCapability {
capability,
Expand All @@ -133,7 +149,24 @@ where
}
PowerRole::Source => {
status_event.set_new_power_contract_as_provider(true);
status.available_source_contract = Some(capability);
let pdo = pdo::source::Pdo::Fixed(pdo::source::FixedData {
voltage_mv: capability.voltage_mv,
current_ma: capability.current_ma,
..Default::default()
});
status.available_source_contract = Some(SourceContract {
capability,
pd: pdo::Rdo::for_pdo(0, pdo).map(|rdo| PdSourceInfo {
rx_fixed_5v_data: Some(pdo::sink::FixedData {
dual_role_power: config.dual_power,
voltage_mv: capability.voltage_mv,
operational_current_ma: capability.current_ma,
..Default::default()
}),
pdo,
rdo,
}),
});
self.psu_state
.update_requested_provider_power_capability(Some(ProviderPowerCapability {
capability,
Expand Down Expand Up @@ -240,7 +273,9 @@ where
}

fn reports_unconstrained_power(&self) -> bool {
self.status.available_sink_contract.is_some() && self.status.unconstrained_power
self.status
.available_sink_contract
.is_some_and(|contract| contract.unconstrained_power())
}

async fn get_other_vdm(&mut self) -> Result<OtherVdm, PdError> {
Expand Down
20 changes: 17 additions & 3 deletions type-c-interface-mocks/tests/connect_disconnect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,18 @@ async fn test_plug_sink_broadcasts_events() {
assert!(!data.previous_status.is_connected());
assert!(data.current_status.is_connected());
assert_eq!(data.current_status.connection_state, Some(ConnectionState::Attached));
assert_eq!(data.current_status.available_sink_contract, Some(TEST_CAPABILITY));
assert_eq!(
data.current_status
.available_sink_contract
.map(|contract| contract.capability),
Some(TEST_CAPABILITY)
);
assert_eq!(data.current_status.power_role, PowerRole::Sink);
assert!(data.current_status.dual_power);
assert!(
data.current_status
.available_sink_contract
.is_some_and(|contract| contract.dual_role_power())
);
assert_eq!(data.current_status.plug_orientation, PlugOrientation::CC2);
assert!(type_c_channel.try_receive().is_err());

Expand Down Expand Up @@ -90,7 +99,12 @@ async fn test_plug_source_broadcasts_events() {
};
assert!(data.status_event.plug_inserted_or_removed());
assert!(data.status_event.new_power_contract_as_provider());
assert_eq!(data.current_status.available_source_contract, Some(TEST_CAPABILITY));
assert_eq!(
data.current_status
.available_source_contract
.map(|contract| contract.capability),
Some(TEST_CAPABILITY)
);
assert_eq!(data.current_status.power_role, PowerRole::Source);

// State should be Idle since power policy hasn't directed us to connect yet
Expand Down
159 changes: 148 additions & 11 deletions type-c-interface/src/control/pd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,165 @@
use embedded_usb_pd::{
DataRole, PlugOrientation, PowerRole,
pdinfo::{AltMode, PowerPathStatus},
pdo::{self, sink::FrsRequiredCurrent},
type_c::ConnectionState,
};
use power_policy_interface::capability::PowerCapability;

/// Information about the negotiated source contract from PD
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PdSourceInfo {
/// Received 5V sink PDO data from port partner
///
/// Contains various flags that aren't present in other PDOs
pub rx_fixed_5v_data: Option<pdo::sink::FixedData>,
/// PDO associated with this contract
pub pdo: pdo::source::Pdo,
/// RDO associated with this contract
pub rdo: pdo::Rdo,
}

/// Source contract
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SourceContract {
/// Power capability
pub capability: PowerCapability,
/// PD contract information, if any
pub pd: Option<PdSourceInfo>,
}

impl SourceContract {
/// Create a source contract without PD-specific information
pub const fn from_capability(capability: PowerCapability) -> Self {
Self { capability, pd: None }
}

/// Returns true if the port partner has dual role power capability
pub fn dual_role_power(&self) -> bool {
self.pd
.and_then(|pd| pd.rx_fixed_5v_data.map(|data| data.dual_role_power))
.unwrap_or(false)
}

/// Returns true if the port partner has higher capability
pub fn higher_capability(&self) -> bool {
self.pd
.and_then(|pd| pd.rx_fixed_5v_data.map(|data| data.higher_capability))
.unwrap_or(false)
}

/// Returns true if the port partner has unconstrained power
pub fn unconstrained_power(&self) -> bool {
self.pd
.and_then(|pd| pd.rx_fixed_5v_data.map(|data| data.unconstrained_power))
.unwrap_or(false)
}

/// Returns true if the port partner is USB comms capable
pub fn usb_comms_capable(&self) -> bool {
self.pd
.and_then(|pd| pd.rx_fixed_5v_data.map(|data| data.usb_comms_capable))
.unwrap_or(false)
}

/// Returns true if the port partner has dual role data capability
pub fn dual_role_data(&self) -> bool {
self.pd
.and_then(|pd| pd.rx_fixed_5v_data.map(|data| data.dual_role_data))
.unwrap_or(false)
}

/// Returns required FRS current for the port partner, if supported
pub fn frs_required_current(&self) -> Option<FrsRequiredCurrent> {
self.pd
.and_then(|pd| pd.rx_fixed_5v_data.map(|data| data.frs_required_current))
}
}

/// Information about the negotiated sink contract from PD
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PdSinkInfo {
/// Received 5V source PDO data from port partner
///
/// Contains various flags that aren't present in other PDOs
pub rx_fixed_5v_data: pdo::source::FixedData,
/// PDO associated with this contract
pub pdo: pdo::sink::Pdo,
/// RDO associated with this contract
pub rdo: pdo::Rdo,
}

/// Sink contract
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SinkContract {
/// Power capability
pub capability: PowerCapability,
/// PD contract information, if any
pub pd: Option<PdSinkInfo>,
}

impl SinkContract {
/// Create a sink contract without PD-specific information
pub const fn from_capability(capability: PowerCapability) -> Self {
Self { capability, pd: None }
}

/// Returns true if the port partner has dual role power capability
pub fn dual_role_power(&self) -> bool {
self.pd.map(|pd| pd.rx_fixed_5v_data.dual_role_power).unwrap_or(false)
}

/// Returns true if the port partner has USB suspend supported
pub fn usb_suspend_supported(&self) -> bool {
self.pd
.map(|pd| pd.rx_fixed_5v_data.usb_suspend_supported)
.unwrap_or(false)
}

/// Returns true if the port partner has unconstrained power
pub fn unconstrained_power(&self) -> bool {
self.pd
.map(|pd| pd.rx_fixed_5v_data.unconstrained_power)
.unwrap_or(false)
}

/// Returns true if the port partner is USB comms capable
pub fn usb_comms_capable(&self) -> bool {
self.pd.map(|pd| pd.rx_fixed_5v_data.usb_comms_capable).unwrap_or(false)
}

/// Returns true if the port partner has dual role data capability
pub fn dual_role_data(&self) -> bool {
self.pd.map(|pd| pd.rx_fixed_5v_data.dual_role_data).unwrap_or(false)
}

/// Returns true if the port partner has unchunked extended messages support
pub fn unchunked_extended_messages_support(&self) -> bool {
self.pd
.map(|pd| pd.rx_fixed_5v_data.unchunked_extended_messages_support)
.unwrap_or(false)
}

/// Returns true if the port partner is EPR capable
pub fn epr_capable(&self) -> bool {
self.pd.map(|pd| pd.rx_fixed_5v_data.epr_capable).unwrap_or(false)
}
}

/// Port status
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PortStatus {
/// Current available source contract
pub available_source_contract: Option<power_policy_interface::capability::PowerCapability>,
pub available_source_contract: Option<SourceContract>,
/// Current available sink contract
pub available_sink_contract: Option<power_policy_interface::capability::PowerCapability>,
pub available_sink_contract: Option<SinkContract>,
/// Current connection state
pub connection_state: Option<ConnectionState>,
/// Port partner supports dual-power roles
pub dual_power: bool,
/// plug orientation
pub plug_orientation: PlugOrientation,
/// power role
Expand All @@ -28,10 +172,6 @@ pub struct PortStatus {
pub alt_mode: AltMode,
/// Power path status
pub power_path: PowerPathStatus,
/// EPR mode active
pub epr: bool,
/// Port partner is unconstrained
pub unconstrained_power: bool,
}

impl PortStatus {
Expand All @@ -42,14 +182,11 @@ impl PortStatus {
available_source_contract: None,
available_sink_contract: None,
connection_state: None,
dual_power: false,
plug_orientation: PlugOrientation::CC1,
power_role: PowerRole::Sink,
data_role: DataRole::Dfp,
alt_mode: AltMode::none(),
power_path: PowerPathStatus::none(),
epr: false,
unconstrained_power: false,
}
}

Expand Down
2 changes: 1 addition & 1 deletion type-c-interface/src/port/pd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub trait Pd: Named {
/// Returns whether this port reports unconstrained power to the system.
///
/// This is the port's own determination and can differ from
/// [`PortStatus::unconstrained_power`], which is what the partner reports in its PDO.
/// [`PortStatus`], which is what the partner reports in its PDO.
fn reports_unconstrained_power(&self) -> bool;
Comment thread
RobertZ2011 marked this conversation as resolved.

/// Get the Rx Other VDM data for this port
Expand Down
Loading
Loading