diff --git a/Cargo.lock b/Cargo.lock index 4f5c1a89..89bd9182 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2262,6 +2262,7 @@ dependencies = [ "defmt 0.3.100", "embedded-services", "embedded-usb-pd", + "heapless 0.8.0", "log", "power-policy-interface", ] diff --git a/examples/rt685s-evk/Cargo.lock b/examples/rt685s-evk/Cargo.lock index 342fa1dc..2ed81527 100644 --- a/examples/rt685s-evk/Cargo.lock +++ b/examples/rt685s-evk/Cargo.lock @@ -1512,6 +1512,7 @@ dependencies = [ "defmt 0.3.100", "embedded-services", "embedded-usb-pd", + "heapless 0.8.0", "power-policy-interface", ] diff --git a/examples/std/Cargo.lock b/examples/std/Cargo.lock index 5d622782..9d833a99 100644 --- a/examples/std/Cargo.lock +++ b/examples/std/Cargo.lock @@ -382,9 +382,9 @@ dependencies = [ [[package]] name = "device-driver" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aa3d97b2acf349b9d52c75470e2ccfc7224c49597ec12c2fb0e28826e910495" +checksum = "c2e4547bd66511372d2a38ac3c1b2892c7ebf83cf0d5411c3406e496c85a1d96" dependencies = [ "embedded-io 0.6.1", "embedded-io-async 0.6.1", @@ -641,7 +641,7 @@ dependencies = [ [[package]] name = "embedded-usb-pd" version = "0.1.0" -source = "git+https://github.com/OpenDevicePartnership/embedded-usb-pd#1a8e79d3a2ac0d2837a34b045087cf0863146f7d" +source = "git+https://github.com/OpenDevicePartnership/embedded-usb-pd#0061a1e94a25c8db33ac1e8a0bb5a6b638fe2cd7" dependencies = [ "aquamarine", "bincode", @@ -1388,7 +1388,7 @@ dependencies = [ [[package]] name = "tps6699x" version = "0.1.0" -source = "git+https://github.com/OpenDevicePartnership/tps6699x?branch=v0.2.0#c908a50747e8fcce831d4e53026072b5b6916a7b" +source = "git+https://github.com/OpenDevicePartnership/tps6699x?branch=v0.2.0#abe5568183bfe5fb2ea81806dded6cb60f3f9b58" dependencies = [ "bincode", "bitfield 0.19.4", @@ -1472,6 +1472,7 @@ dependencies = [ "bitfield 0.17.0", "embedded-services", "embedded-usb-pd", + "heapless 0.8.0", "log", "power-policy-interface", ] diff --git a/examples/std/src/lib/type_c/mock_controller.rs b/examples/std/src/lib/type_c/mock_controller.rs index 206857cd..10454d90 100644 --- a/examples/std/src/lib/type_c/mock_controller.rs +++ b/examples/std/src/lib/type_c/mock_controller.rs @@ -4,6 +4,7 @@ use embassy_sync::{channel, mutex::Mutex, signal::Signal}; 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::{PowerRole, type_c::Current}; use embedded_usb_pd::{type_c::ConnectionState, ucsi::lpm}; @@ -14,6 +15,7 @@ use type_c_interface::control::dp::{DpConfig, DpPinConfig, DpStatus}; use type_c_interface::control::pd::{PdStateMachineConfig, PortStatus}; use type_c_interface::control::power::SystemPowerState; use type_c_interface::control::retimer::RetimerFwUpdateState; +use type_c_interface::control::svid::DiscoveredSvids; use type_c_interface::control::tbt::TbtConfig; use type_c_interface::control::type_c::TypeCStateMachineState; use type_c_interface::control::usb::UsbControlConfig; @@ -228,6 +230,29 @@ impl type_c_interface::controller::pd::Pd for Controller<'_> { debug!("Set Thunderbolt config for port {port:?}: {config:?}"); Ok(()) } + + async fn hard_reset(&mut self, port: LocalPortId) -> Result<(), PdError> { + debug!("Hard reset for port {port:?}"); + Ok(()) + } + + async fn get_discovered_svids(&mut self, port: LocalPortId) -> Result { + debug!("Get discovered SVIDs for port {port:?}"); + Ok(DiscoveredSvids::default()) + } + + async fn get_discover_identity_sop_response(&mut self, port: LocalPortId) -> Result { + debug!("Get Discover Identity SOP response for port {port:?}"); + Err(PdError::Failed) + } + + async fn get_discover_identity_sop_prime_response( + &mut self, + port: LocalPortId, + ) -> Result { + debug!("Get Discover Identity SOP' response for port {port:?}"); + Err(PdError::Failed) + } } impl type_c_interface::controller::max_sink_voltage::MaxSinkVoltage for Controller<'_> { diff --git a/type-c-interface/Cargo.toml b/type-c-interface/Cargo.toml index 27857480..f8565d54 100644 --- a/type-c-interface/Cargo.toml +++ b/type-c-interface/Cargo.toml @@ -15,6 +15,7 @@ defmt = { workspace = true, optional = true } embedded-services.workspace = true embedded-usb-pd.workspace = true power-policy-interface.workspace = true +heapless.workspace = true [lints] workspace = true diff --git a/type-c-interface/src/control/mod.rs b/type-c-interface/src/control/mod.rs index 05607dec..426d42af 100644 --- a/type-c-interface/src/control/mod.rs +++ b/type-c-interface/src/control/mod.rs @@ -3,6 +3,7 @@ pub mod dp; pub mod pd; pub mod power; pub mod retimer; +pub mod svid; pub mod tbt; pub mod type_c; pub mod usb; diff --git a/type-c-interface/src/control/svid.rs b/type-c-interface/src/control/svid.rs new file mode 100644 index 00000000..259b2f53 --- /dev/null +++ b/type-c-interface/src/control/svid.rs @@ -0,0 +1,62 @@ +use embedded_usb_pd::vdm::structured::Svid; +use heapless::Vec; + +/// Response from the `Discover SVIDs REQ` message and the PortCommandData::GetDiscoveredSvids command. +// Could be changed to hold the heapless::Vec directly if they were Copy or if PortResponseData was not Copy +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DiscoveredSvids { + num_sop: usize, + sop: [Svid; Self::NUM_SVIDS], + + num_sop_prime: usize, + sop_prime: [Svid; Self::NUM_SVIDS], +} + +impl DiscoveredSvids { + /// The number of SVIDs that can be reported in a single DiscoveredSvids response. + pub const NUM_SVIDS: usize = 8; + + /// Create a new response object from `sop` and `sop_prime`. + pub fn new(sop: Vec, sop_prime: Vec) -> Self { + let num_sop = sop.len(); + let num_sop_prime = sop_prime.len(); + + let mut sop_array = [Svid(0); _]; + for (svid, dest) in sop.into_iter().zip(sop_array.iter_mut()) { + *dest = svid; + } + + let mut sop_prime_array = [Svid(0); _]; + for (svid, dest) in sop_prime.into_iter().zip(sop_prime_array.iter_mut()) { + *dest = svid; + } + + Self { + num_sop, + sop: sop_array, + num_sop_prime, + sop_prime: sop_prime_array, + } + } + + /// Returns the number of SVIDs discovered on the SOP port partner. + pub fn number_sop_svids(&self) -> usize { + self.num_sop + } + + /// Returns an iterator over the SVIDs discovered on the SOP port partner. + pub fn svid_sop(&self) -> impl ExactSizeIterator { + self.sop.iter().copied().take(self.num_sop) + } + + /// Returns the number of SVIDs discovered on the SOP' cable plug. + pub fn number_sop_prime_svids(&self) -> usize { + self.num_sop_prime + } + + /// Returns an iterator over the SVIDs discovered on the SOP' cable plug. + pub fn svid_sop_prime(&self) -> impl ExactSizeIterator { + self.sop_prime.iter().copied().take(self.num_sop_prime) + } +} diff --git a/type-c-interface/src/controller/pd.rs b/type-c-interface/src/controller/pd.rs index 4e20afd9..287fef71 100644 --- a/type-c-interface/src/controller/pd.rs +++ b/type-c-interface/src/controller/pd.rs @@ -1,9 +1,11 @@ use embedded_services::named::Named; +use embedded_usb_pd::vdm::structured::command::discover_identity::{sop, sop_prime}; use embedded_usb_pd::{LocalPortId, PdError, ado::Ado}; use crate::control::{ dp::{DpConfig, DpStatus}, pd::{PdStateMachineConfig, PortStatus}, + svid::DiscoveredSvids, tbt::TbtConfig, usb::UsbControlConfig, vdm::{AttnVdm, OtherVdm, SendVdm}, @@ -38,6 +40,8 @@ pub trait Pd: Named { fn send_vdm(&mut self, port: LocalPortId, tx_vdm: SendVdm) -> impl Future>; /// Execute PD Data Reset for the given port fn execute_drst(&mut self, port: LocalPortId) -> impl Future>; + /// Execute a Hard Reset on the given port. + fn hard_reset(&mut self, port: LocalPortId) -> impl Future>; /// Get DisplayPort status for the given port fn get_dp_status(&mut self, port: LocalPortId) -> impl Future>; @@ -53,6 +57,21 @@ pub trait Pd: Named { port: LocalPortId, config: UsbControlConfig, ) -> impl Future>; + + /// Get the given port's discovered SVIDs + fn get_discovered_svids(&mut self, port: LocalPortId) -> impl Future>; + + /// Get the latest response from the Discover Identity command targeting SOP. + fn get_discover_identity_sop_response( + &mut self, + port: LocalPortId, + ) -> impl Future>; + + /// Get the latest response from the Discover Identity command targeting SOP'. + fn get_discover_identity_sop_prime_response( + &mut self, + port: LocalPortId, + ) -> impl Future>; } /// PD state machine related controller functionality diff --git a/type-c-interface/src/port/pd.rs b/type-c-interface/src/port/pd.rs index 547f948b..3cf613a0 100644 --- a/type-c-interface/src/port/pd.rs +++ b/type-c-interface/src/port/pd.rs @@ -1,9 +1,11 @@ use embedded_services::named::Named; +use embedded_usb_pd::vdm::structured::command::discover_identity::{sop, sop_prime}; use embedded_usb_pd::{PdError, ado::Ado}; use crate::control::{ dp::{DpConfig, DpStatus}, pd::{PdStateMachineConfig, PortStatus}, + svid::DiscoveredSvids, tbt::TbtConfig, usb::UsbControlConfig, vdm::{AttnVdm, OtherVdm, SendVdm}, @@ -34,6 +36,8 @@ pub trait Pd: Named { fn send_vdm(&mut self, tx_vdm: SendVdm) -> impl Future>; /// Execute PD Data Reset for this port fn execute_drst(&mut self) -> impl Future>; + /// Execute a Hard Reset on this port. + fn hard_reset(&mut self) -> impl Future>; /// Get DisplayPort status for this port fn get_dp_status(&mut self) -> impl Future>; @@ -45,6 +49,17 @@ pub trait Pd: Named { /// Set USB control configuration for this port fn set_usb_control(&mut self, config: UsbControlConfig) -> impl Future>; + + /// Get this port's discovered SVIDs + fn get_discovered_svids(&mut self) -> impl Future>; + + /// Get the latest response from the Discover Identity command targeting SOP. + fn get_discover_identity_sop_response(&mut self) -> impl Future>; + + /// Get the latest response from the Discover Identity command targeting SOP'. + fn get_discover_identity_sop_prime_response( + &mut self, + ) -> impl Future>; } /// PD state machine related controller functionality diff --git a/type-c-service/src/controller/pd.rs b/type-c-service/src/controller/pd.rs index 2091c924..f3b862f5 100644 --- a/type-c-service/src/controller/pd.rs +++ b/type-c-service/src/controller/pd.rs @@ -2,9 +2,11 @@ use embedded_services::{event::Sender, sync::Lockable}; use embedded_usb_pd::PdError; use embedded_usb_pd::ado::Ado; +use embedded_usb_pd::vdm::structured::command::discover_identity::{sop, sop_prime}; use type_c_interface::control::{ dp::{DpConfig, DpStatus}, pd::{PdStateMachineConfig, PortStatus}, + svid::DiscoveredSvids, tbt::TbtConfig, usb::UsbControlConfig, vdm::{AttnVdm, OtherVdm, SendVdm}, @@ -130,6 +132,30 @@ impl< async fn set_usb_control(&mut self, config: UsbControlConfig) -> Result<(), PdError> { self.controller.lock().await.set_usb_control(self.port, config).await } + + async fn hard_reset(&mut self) -> Result<(), PdError> { + self.controller.lock().await.hard_reset(self.port).await + } + + async fn get_discovered_svids(&mut self) -> Result { + self.controller.lock().await.get_discovered_svids(self.port).await + } + + async fn get_discover_identity_sop_response(&mut self) -> Result { + self.controller + .lock() + .await + .get_discover_identity_sop_response(self.port) + .await + } + + async fn get_discover_identity_sop_prime_response(&mut self) -> Result { + self.controller + .lock() + .await + .get_discover_identity_sop_prime_response(self.port) + .await + } } impl< diff --git a/type-c-service/src/driver/tps6699x.rs b/type-c-service/src/driver/tps6699x.rs index 1560cd70..caa01909 100644 --- a/type-c-service/src/driver/tps6699x.rs +++ b/type-c-service/src/driver/tps6699x.rs @@ -16,6 +16,7 @@ use embedded_usb_pd::type_c::Current as TypecCurrent; use embedded_usb_pd::ucsi::lpm; use embedded_usb_pd::{DataRole, Error, LocalPortId, PdError, PlugOrientation, PowerRole}; use fw_update_interface::basic::{Error as BasicFwUpdateError, FwUpdate as BasicFwUpdate}; +use heapless::Vec; use tps6699x::MAX_SUPPORTED_PORTS; use tps6699x::asynchronous::embassy::{self as tps6699x_drv, interrupt}; use tps6699x::asynchronous::fw_update::UpdateTarget; @@ -33,6 +34,7 @@ use type_c_interface::control::dp::{DpConfig, DpPinConfig, DpStatus}; use type_c_interface::control::pd::{PdStateMachineConfig, PortStatus}; use type_c_interface::control::power::SystemPowerState; use type_c_interface::control::retimer::RetimerFwUpdateState; +use type_c_interface::control::svid::DiscoveredSvids; use type_c_interface::control::tbt::TbtConfig; use type_c_interface::control::type_c::TypeCStateMachineState; use type_c_interface::control::usb::UsbControlConfig; @@ -719,6 +721,76 @@ impl Pd for Tps6699x<'_, M, B> { { self.tps6699x.lock_inner().await.set_tbt_config(port, config_reg).await }.map_err(|e| self.log_error(e)) } + + async fn hard_reset(&mut self, port: LocalPortId) -> Result<(), PdError> { + self.guard_no_fw_update_active()?; + match self.tps6699x.execute_hrst(port).await.map_err(|e| self.log_error(e))? { + ReturnValue::Success => Ok(()), + r => { + error!("Error executing hard reset on port {}: {:#?}", port.0, r); + Err(PdError::InvalidResponse) + } + } + } + + async fn get_discovered_svids(&mut self, port: LocalPortId) -> Result { + self.guard_no_fw_update_active()?; + let svids = self + .tps6699x + .get_discovered_svids(port) + .await + .map_err(|e| self.log_error(e))?; + debug!("{:?} discovered SVIDs: {:?}", port, svids); + let mut sop = Vec::new(); + for svid in svids.svid_sop().take(sop.capacity()) { + let _ = sop.push(svid); + } + + let mut sop_prime = Vec::new(); + for svid in svids.svid_sop_prime().take(sop_prime.capacity()) { + let _ = sop_prime.push(svid); + } + + Ok(DiscoveredSvids::new(sop, sop_prime)) + } + + async fn get_discover_identity_sop_response( + &mut self, + port: LocalPortId, + ) -> Result { + self.guard_no_fw_update_active()?; + let data = self + .tps6699x + .get_received_sop_identity_data(port) + .await + .map_err(|e| self.log_error(e))?; + match data.try_into() { + Ok(vdos) => Ok(vdos), + Err(e) => { + error!("Error deserializing Received SOP Identity Data: {:?}", e); + Err(PdError::Serialize) + } + } + } + + async fn get_discover_identity_sop_prime_response( + &mut self, + port: LocalPortId, + ) -> Result { + self.guard_no_fw_update_active()?; + let data = self + .tps6699x + .get_received_sop_prime_identity_data(port) + .await + .map_err(|e| self.log_error(e))?; + match data.try_into() { + Ok(vdos) => Ok(vdos), + Err(e) => { + error!("Error deserializing Received SOP Prime Identity Data: {:?}", e); + Err(PdError::Serialize) + } + } + } } impl Retimer for Tps6699x<'_, M, B> {