diff --git a/Cargo.lock b/Cargo.lock index ce5ddac49..cb379bdfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2514,13 +2514,27 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "type-c-interface" +version = "0.1.0" +dependencies = [ + "bitfield 0.17.0", + "bitvec", + "defmt 0.3.100", + "embassy-sync", + "embassy-time", + "embedded-services", + "embedded-usb-pd", + "log", + "power-policy-interface", +] + [[package]] name = "type-c-service" version = "0.1.0" dependencies = [ "bitfield 0.17.0", "bitflags 2.9.4", - "bitvec", "cfu-service", "critical-section", "defmt 0.3.100", @@ -2540,6 +2554,7 @@ dependencies = [ "static_cell", "tokio", "tps6699x", + "type-c-interface", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ffe8c00b1..ad4fe567f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ members = [ "keyboard-service", "power-policy-interface", "odp-service-common", + "type-c-interface", ] exclude = ["examples/*"] @@ -105,6 +106,7 @@ static_cell = "2.1.0" toml = { version = "0.8", default-features = false } thermal-service-messages = { path = "./thermal-service-messages" } time-alarm-service-messages = { path = "./time-alarm-service-messages" } +type-c-interface = { path = "./type-c-interface" } syn = "2.0" tps6699x = { git = "https://github.com/OpenDevicePartnership/tps6699x" } tokio = { version = "1.42.0" } diff --git a/examples/rt685s-evk/Cargo.lock b/examples/rt685s-evk/Cargo.lock index e8ffda556..ea9969a94 100644 --- a/examples/rt685s-evk/Cargo.lock +++ b/examples/rt685s-evk/Cargo.lock @@ -1426,6 +1426,7 @@ dependencies = [ "time-alarm-service", "time-alarm-service-messages", "tps6699x", + "type-c-interface", "type-c-service", ] @@ -1672,13 +1673,26 @@ dependencies = [ "itertools 0.14.0", ] +[[package]] +name = "type-c-interface" +version = "0.1.0" +dependencies = [ + "bitfield 0.17.0", + "bitvec", + "defmt 0.3.100", + "embassy-sync", + "embassy-time", + "embedded-services", + "embedded-usb-pd", + "power-policy-interface", +] + [[package]] name = "type-c-service" version = "0.1.0" dependencies = [ "bitfield 0.17.0", "bitflags 2.9.4", - "bitvec", "cfu-service", "defmt 0.3.100", "embassy-futures", @@ -1693,6 +1707,7 @@ dependencies = [ "heapless", "power-policy-interface", "tps6699x", + "type-c-interface", ] [[package]] diff --git a/examples/rt685s-evk/Cargo.toml b/examples/rt685s-evk/Cargo.toml index 18c0f7956..98fa21e8e 100644 --- a/examples/rt685s-evk/Cargo.toml +++ b/examples/rt685s-evk/Cargo.toml @@ -69,6 +69,7 @@ embedded-usb-pd = { git = "https://github.com/OpenDevicePartnership/embedded-usb "defmt", ] } type-c-service = { path = "../../type-c-service", features = ["defmt"] } +type-c-interface = { path = "../../type-c-interface", features = ["defmt"] } time-alarm-service = { path = "../../time-alarm-service", features = ["defmt"] } time-alarm-service-messages = { path = "../../time-alarm-service-messages", features = [ "defmt", diff --git a/examples/rt685s-evk/src/bin/type_c.rs b/examples/rt685s-evk/src/bin/type_c.rs index 2d7ac8724..629230a74 100644 --- a/examples/rt685s-evk/src/bin/type_c.rs +++ b/examples/rt685s-evk/src/bin/type_c.rs @@ -15,8 +15,8 @@ use embassy_sync::once_lock::OnceLock; use embassy_sync::pubsub::PubSubChannel; use embassy_time::{self as _, Delay}; use embedded_cfu_protocol::protocol_definitions::{FwUpdateOffer, FwUpdateOfferResponse, FwVersion, HostToken}; +use embedded_services::GlobalRawMutex; use embedded_services::event::NoopSender; -use embedded_services::{GlobalRawMutex, IntrusiveList}; use embedded_services::{error, info}; use embedded_usb_pd::GlobalPortId; use power_policy_interface::psu; @@ -24,9 +24,9 @@ use power_policy_service::psu::ArrayEventReceivers; use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use tps6699x::asynchronous::embassy as tps6699x; +use type_c_interface::port::ControllerId; use type_c_service::driver::tps6699x::{self as tps6699x_drv}; use type_c_service::service::Service; -use type_c_service::type_c::{Cached, ControllerId}; use type_c_service::wrapper::ControllerWrapper; use type_c_service::wrapper::backing::{IntermediateStorage, ReferencedStorage, Storage}; use type_c_service::wrapper::proxy::PowerProxyDevice; @@ -143,11 +143,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); - static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); - let controller_context = CONTROLLER_CONTEXT.init(type_c_service::type_c::controller::Context::new()); - - static CONTROLLER_LIST: StaticCell = StaticCell::new(); - let controller_list = CONTROLLER_LIST.init(IntrusiveList::new()); + static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); + let controller_context = CONTROLLER_CONTEXT.init(type_c_interface::service::context::Context::new()); static STORAGE: StaticCell> = StaticCell::new(); let storage = STORAGE.init(Storage::new( @@ -227,7 +224,6 @@ async fn main(spawner: Spawner) { let type_c_service = TYPE_C_SERVICE.init(Service::create( Default::default(), controller_context, - controller_list, power_policy_publisher, power_policy_subscriber, )); @@ -249,31 +245,4 @@ async fn main(spawner: Spawner) { )); spawner.must_spawn(pd_controller_task(wrapper)); - - // Sync our internal state with the hardware - controller_context - .sync_controller_state_external(CONTROLLER0_ID) - .await - .unwrap(); - - embassy_time::Timer::after_secs(10).await; - - let status = controller_context - .get_controller_status_external(CONTROLLER0_ID) - .await - .unwrap(); - - info!("Controller status: {:?}", status); - - let status = controller_context - .get_port_status_external(PORT0_ID, Cached(true)) - .await - .unwrap(); - info!("Port status: {:?}", status); - - let status = controller_context - .get_port_status_external(PORT1_ID, Cached(true)) - .await - .unwrap(); - info!("Port status: {:?}", status); } diff --git a/examples/rt685s-evk/src/bin/type_c_cfu.rs b/examples/rt685s-evk/src/bin/type_c_cfu.rs index 20f5b65d7..7defa355c 100644 --- a/examples/rt685s-evk/src/bin/type_c_cfu.rs +++ b/examples/rt685s-evk/src/bin/type_c_cfu.rs @@ -18,8 +18,8 @@ use embassy_time::Timer; use embassy_time::{self as _, Delay}; use embedded_cfu_protocol::protocol_definitions::*; use embedded_cfu_protocol::protocol_definitions::{FwUpdateOffer, FwUpdateOfferResponse, FwVersion}; +use embedded_services::GlobalRawMutex; use embedded_services::event::NoopSender; -use embedded_services::{GlobalRawMutex, IntrusiveList}; use embedded_services::{error, info}; use embedded_usb_pd::GlobalPortId; use power_policy_interface::psu; @@ -27,9 +27,9 @@ use power_policy_service::psu::ArrayEventReceivers; use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use tps6699x::asynchronous::embassy as tps6699x; +use type_c_interface::port::ControllerId; use type_c_service::driver::tps6699x::{self as tps6699x_drv}; use type_c_service::service::Service; -use type_c_service::type_c::ControllerId; use type_c_service::wrapper::ControllerWrapper; use type_c_service::wrapper::backing::{IntermediateStorage, ReferencedStorage, Storage}; use type_c_service::wrapper::proxy::PowerProxyDevice; @@ -227,11 +227,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); - static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); - let controller_context = CONTROLLER_CONTEXT.init(type_c_service::type_c::controller::Context::new()); - - static CONTROLLER_LIST: StaticCell = StaticCell::new(); - let controller_list = CONTROLLER_LIST.init(IntrusiveList::new()); + static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); + let controller_context = CONTROLLER_CONTEXT.init(type_c_interface::service::context::Context::new()); static STORAGE: StaticCell> = StaticCell::new(); let storage = STORAGE.init(Storage::new( @@ -315,7 +312,6 @@ async fn main(spawner: Spawner) { let type_c_service = TYPE_C_SERVICE.init(Service::create( Default::default(), controller_context, - controller_list, power_policy_publisher, power_policy_subscriber, )); diff --git a/examples/std/Cargo.lock b/examples/std/Cargo.lock index a032962ee..a4c0d09dd 100644 --- a/examples/std/Cargo.lock +++ b/examples/std/Cargo.lock @@ -1661,6 +1661,7 @@ dependencies = [ "static_cell", "thermal-service", "thermal-service-messages", + "type-c-interface", "type-c-service", ] @@ -1868,13 +1869,26 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "type-c-interface" +version = "0.1.0" +dependencies = [ + "bitfield 0.17.0", + "bitvec", + "embassy-sync", + "embassy-time", + "embedded-services", + "embedded-usb-pd", + "log", + "power-policy-interface", +] + [[package]] name = "type-c-service" version = "0.1.0" dependencies = [ "bitfield 0.17.0", "bitflags 2.9.4", - "bitvec", "cfu-service", "embassy-futures", "embassy-sync", @@ -1889,6 +1903,7 @@ dependencies = [ "log", "power-policy-interface", "tps6699x", + "type-c-interface", ] [[package]] diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 21aa1c4aa..ce6047a7e 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -39,6 +39,7 @@ embedded-cfu-protocol = { git = "https://github.com/OpenDevicePartnership/embedd embedded-batteries-async = "0.3" battery-service = { path = "../../battery-service", features = ["log", "mock"] } type-c-service = { path = "../../type-c-service", features = ["log"] } +type-c-interface = { path = "../../type-c-interface", features = ["log"] } embedded-sensors-hal-async = "0.3.0" embedded-fans-async = "0.2.0" diff --git a/examples/std/src/bin/type_c/basic.rs b/examples/std/src/bin/type_c/basic.rs index 1bdbb604d..57739b021 100644 --- a/examples/std/src/bin/type_c/basic.rs +++ b/examples/std/src/bin/type_c/basic.rs @@ -1,12 +1,12 @@ use embassy_executor::{Executor, Spawner}; use embassy_sync::once_lock::OnceLock; use embassy_time::Timer; -use embedded_services::IntrusiveList; use embedded_usb_pd::ucsi::lpm; use embedded_usb_pd::{GlobalPortId, PdError as Error}; use log::*; use static_cell::StaticCell; -use type_c_service::type_c::{Cached, ControllerId, controller}; +use type_c_interface::port::{self, Cached, ControllerId}; +use type_c_interface::service::context::{Context, DeviceContainer}; const CONTROLLER0_ID: ControllerId = ControllerId(0); const PORT0_ID: GlobalPortId = GlobalPortId(0); @@ -14,16 +14,16 @@ const PORT1_ID: GlobalPortId = GlobalPortId(1); mod test_controller { use embedded_usb_pd::ucsi; - use type_c_service::type_c::controller::{ControllerStatus, PortStatus}; + use type_c_interface::port::{ControllerStatus, PortStatus}; use super::*; pub struct Controller<'a> { - pub controller: controller::Device<'a>, + pub controller: port::Device<'a>, } - impl controller::DeviceContainer for Controller<'_> { - fn get_pd_controller_device(&self) -> &controller::Device<'_> { + impl DeviceContainer for Controller<'_> { + fn get_pd_controller_device(&self) -> &port::Device<'_> { &self.controller } } @@ -31,31 +31,31 @@ mod test_controller { impl<'a> Controller<'a> { pub fn new(id: ControllerId, ports: &'a [GlobalPortId]) -> Self { Self { - controller: controller::Device::new(id, ports), + controller: port::Device::new(id, ports), } } async fn process_controller_command( &self, - command: controller::InternalCommandData, - ) -> Result, Error> { + command: port::InternalCommandData, + ) -> Result, Error> { match command { - controller::InternalCommandData::Reset => { + port::InternalCommandData::Reset => { info!("Reset controller"); - Ok(controller::InternalResponseData::Complete) + Ok(port::InternalResponseData::Complete) } - controller::InternalCommandData::Status => { + port::InternalCommandData::Status => { info!("Get controller status"); - Ok(controller::InternalResponseData::Status(ControllerStatus { + Ok(port::InternalResponseData::Status(ControllerStatus { mode: "Test", valid_fw_bank: true, fw_version0: 0xbadf00d, fw_version1: 0xdeadbeef, })) } - controller::InternalCommandData::SyncState => { + port::InternalCommandData::SyncState => { info!("Sync controller state"); - Ok(controller::InternalResponseData::Complete) + Ok(port::InternalResponseData::Complete) } } } @@ -79,18 +79,15 @@ mod test_controller { } } - async fn process_port_command( - &self, - command: controller::PortCommand, - ) -> Result { + async fn process_port_command(&self, command: port::PortCommand) -> Result { Ok(match command.data { - controller::PortCommandData::PortStatus(Cached(true)) => { + port::PortCommandData::PortStatus(Cached(true)) => { info!("Port status for port {}", command.port.0); - controller::PortResponseData::PortStatus(PortStatus::new()) + port::PortResponseData::PortStatus(PortStatus::new()) } _ => { info!("Port command for port {}", command.port.0); - controller::PortResponseData::Complete + port::PortResponseData::Complete } }) } @@ -98,15 +95,11 @@ mod test_controller { pub async fn process(&self) { let request = self.controller.receive().await; let response = match request.command { - controller::Command::Controller(command) => { - controller::Response::Controller(self.process_controller_command(command).await) - } - controller::Command::Lpm(command) => { - controller::Response::Ucsi(self.process_ucsi_command(&command).await) - } - controller::Command::Port(command) => { - controller::Response::Port(self.process_port_command(command).await) + port::Command::Controller(command) => { + port::Response::Controller(self.process_controller_command(command).await) } + port::Command::Lpm(command) => port::Response::Ucsi(self.process_ucsi_command(&command).await), + port::Command::Port(command) => port::Response::Port(self.process_port_command(command).await), }; request.respond(response); @@ -115,13 +108,13 @@ mod test_controller { } #[embassy_executor::task] -async fn controller_task(controller_list: &'static IntrusiveList) { +async fn controller_task(controller_context: &'static Context) { static CONTROLLER: OnceLock = OnceLock::new(); static PORTS: [GlobalPortId; 2] = [PORT0_ID, PORT1_ID]; let controller = CONTROLLER.get_or_init(|| test_controller::Controller::new(CONTROLLER0_ID, &PORTS)); - controller::register_controller(controller_list, controller).unwrap(); + controller_context.register_controller(controller).unwrap(); loop { controller.process().await; @@ -132,32 +125,27 @@ async fn controller_task(controller_list: &'static IntrusiveList) { async fn task(spawner: Spawner) { embedded_services::init().await; - static CONTROLLER_LIST: StaticCell = StaticCell::new(); - let controller_list = CONTROLLER_LIST.init(IntrusiveList::new()); + static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); + let controller_context = CONTROLLER_CONTEXT.init(Context::new()); info!("Starting controller task"); - spawner.must_spawn(controller_task(controller_list)); + spawner.must_spawn(controller_task(controller_context)); // Wait for controller to be registered Timer::after_secs(1).await; - let context = controller::Context::new(); + controller_context.reset_controller(CONTROLLER0_ID).await.unwrap(); - context.reset_controller(controller_list, CONTROLLER0_ID).await.unwrap(); - - let status = context - .get_controller_status(controller_list, CONTROLLER0_ID) - .await - .unwrap(); + let status = controller_context.get_controller_status(CONTROLLER0_ID).await.unwrap(); info!("Controller 0 status: {status:#?}"); - let status = context - .get_port_status(controller_list, PORT0_ID, Cached(true)) + let status = controller_context + .get_port_status(PORT0_ID, Cached(true)) .await .unwrap(); info!("Port 0 status: {status:#?}"); - let status = context - .get_port_status(controller_list, PORT1_ID, Cached(true)) + let status = controller_context + .get_port_status(PORT1_ID, Cached(true)) .await .unwrap(); info!("Port 1 status: {status:#?}"); diff --git a/examples/std/src/bin/type_c/service.rs b/examples/std/src/bin/type_c/service.rs index c11d91a77..bb6f1d7b7 100644 --- a/examples/std/src/bin/type_c/service.rs +++ b/examples/std/src/bin/type_c/service.rs @@ -5,8 +5,8 @@ use embassy_sync::mutex::Mutex; use embassy_sync::once_lock::OnceLock; use embassy_sync::pubsub::PubSubChannel; use embassy_time::Timer; +use embedded_services::GlobalRawMutex; use embedded_services::event::NoopSender; -use embedded_services::{GlobalRawMutex, IntrusiveList}; use embedded_usb_pd::GlobalPortId; use embedded_usb_pd::ado::Ado; use embedded_usb_pd::type_c::Current; @@ -17,10 +17,11 @@ use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use std_examples::type_c::mock_controller; use std_examples::type_c::mock_controller::Wrapper; +use type_c_interface::port::ControllerId; +use type_c_interface::service::context::Context; use type_c_service::service::Service; use type_c_service::service::config::Config; -use type_c_service::type_c::controller::Context; -use type_c_service::type_c::{ControllerId, power_capability_from_current}; +use type_c_service::util::power_capability_from_current; use type_c_service::wrapper::backing::Storage; use type_c_service::wrapper::message::*; use type_c_service::wrapper::proxy::PowerProxyDevice; @@ -74,8 +75,8 @@ async fn task(spawner: Spawner) { static POWER_SERVICE_CONTEXT: StaticCell = StaticCell::new(); let power_service_context = POWER_SERVICE_CONTEXT.init(power_policy_service::service::context::Context::new()); - static CONTEXT: StaticCell = StaticCell::new(); - let controller_context = CONTEXT.init(type_c_service::type_c::controller::Context::new()); + static CONTEXT: StaticCell = StaticCell::new(); + let controller_context = CONTEXT.init(type_c_interface::service::context::Context::new()); let (wrapper, policy_receiver, controller, state) = create_wrapper(controller_context); @@ -102,14 +103,10 @@ async fn task(spawner: Spawner) { // Guaranteed to not panic since we initialized the channel above let power_policy_subscriber = power_policy_channel.dyn_subscriber().unwrap(); - static CONTROLLER_LIST: StaticCell = StaticCell::new(); - let controller_list = CONTROLLER_LIST.init(IntrusiveList::new()); - static TYPE_C_SERVICE: StaticCell> = StaticCell::new(); let type_c_service = TYPE_C_SERVICE.init(Service::create( Config::default(), controller_context, - controller_list, power_policy_publisher, power_policy_subscriber, )); diff --git a/examples/std/src/bin/type_c/ucsi.rs b/examples/std/src/bin/type_c/ucsi.rs index ba6d9cd07..ce9e7d3ee 100644 --- a/examples/std/src/bin/type_c/ucsi.rs +++ b/examples/std/src/bin/type_c/ucsi.rs @@ -1,3 +1,4 @@ +#![allow(unused_imports)] use crate::mock_controller::Wrapper; use cfu_service::CfuClient; use embassy_executor::{Executor, Spawner}; @@ -21,11 +22,10 @@ use power_policy_service::psu::ArrayEventReceivers; use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use std_examples::type_c::mock_controller; +use type_c_interface::service::context::Context; use type_c_service::service::Service; use type_c_service::service::config::Config; -use type_c_service::type_c::ControllerId; -use type_c_service::type_c::controller::Context; -use type_c_service::type_c::external::UcsiResponseResult; +use type_c_interface::port::ControllerId; use type_c_service::wrapper::backing::Storage; use type_c_service::wrapper::proxy::PowerProxyDevice; @@ -45,8 +45,8 @@ type PowerPolicyServiceType = Mutex< >; #[embassy_executor::task] -async fn opm_task(context: &'static Context, state: [&'static mock_controller::ControllerState; NUM_PD_CONTROLLERS]) { - const CAPABILITY: PowerCapability = PowerCapability { +async fn opm_task(_context: &'static Context, _state: [&'static mock_controller::ControllerState; NUM_PD_CONTROLLERS]) { + /*const CAPABILITY: PowerCapability = PowerCapability { voltage_mv: 20000, current_ma: 5000, }; @@ -172,7 +172,7 @@ async fn opm_task(context: &'static Context, state: [&'static mock_controller::C "Sending command complete ack successful, connector change: {:?}", response.cci.connector_change() ); - } + }*/ } #[embassy_executor::task(pool_size = 2)] @@ -208,11 +208,8 @@ async fn task(spawner: Spawner) { embedded_services::init().await; - static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); - let controller_context = CONTROLLER_CONTEXT.init(type_c_service::type_c::controller::Context::new()); - - static CONTROLLER_LIST: StaticCell = StaticCell::new(); - let controller_list = CONTROLLER_LIST.init(IntrusiveList::new()); + static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); + let controller_context = CONTROLLER_CONTEXT.init(Context::new()); static STORAGE0: StaticCell> = StaticCell::new(); let storage0 = STORAGE0.init(Storage::new(controller_context, CONTROLLER0_ID, CFU0_ID, [PORT0_ID])); @@ -359,7 +356,6 @@ async fn task(spawner: Spawner) { ..Default::default() }, controller_context, - controller_list, power_policy_publisher, power_policy_subscriber, )); diff --git a/examples/std/src/bin/type_c/unconstrained.rs b/examples/std/src/bin/type_c/unconstrained.rs index a11b38ee2..d207fcf54 100644 --- a/examples/std/src/bin/type_c/unconstrained.rs +++ b/examples/std/src/bin/type_c/unconstrained.rs @@ -8,8 +8,8 @@ use embassy_sync::mutex::Mutex; use embassy_sync::once_lock::OnceLock; use embassy_sync::pubsub::PubSubChannel; use embassy_time::Timer; +use embedded_services::GlobalRawMutex; use embedded_services::event::NoopSender; -use embedded_services::{GlobalRawMutex, IntrusiveList}; use embedded_usb_pd::GlobalPortId; use log::*; use power_policy_interface::capability::PowerCapability; @@ -18,8 +18,8 @@ use power_policy_service::psu::ArrayEventReceivers; use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use std_examples::type_c::mock_controller; +use type_c_interface::port::ControllerId; use type_c_service::service::Service; -use type_c_service::type_c::ControllerId; const NUM_PD_CONTROLLERS: usize = 3; use type_c_service::wrapper::backing::{IntermediateStorage, ReferencedStorage, Storage}; @@ -63,13 +63,8 @@ async fn task(spawner: Spawner) { static POWER_SERVICE_CONTEXT: StaticCell = StaticCell::new(); let power_service_context = POWER_SERVICE_CONTEXT.init(power_policy_service::service::context::Context::new()); - static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); - let controller_context = CONTROLLER_CONTEXT.init(type_c_service::type_c::controller::Context::new()); - - static CONTEXT: StaticCell = StaticCell::new(); - let context = CONTEXT.init(type_c_service::type_c::controller::Context::new()); - static CONTROLLER_LIST: StaticCell = StaticCell::new(); - let controller_list = CONTROLLER_LIST.init(IntrusiveList::new()); + static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); + let controller_context = CONTROLLER_CONTEXT.init(type_c_interface::service::context::Context::new()); static POLICY_CHANNEL0: StaticCell> = StaticCell::new(); let policy_channel0 = POLICY_CHANNEL0.init(Channel::new()); @@ -77,7 +72,7 @@ async fn task(spawner: Spawner) { let policy_receiver0 = policy_channel0.dyn_receiver(); static STORAGE0: StaticCell> = StaticCell::new(); - let storage0 = STORAGE0.init(Storage::new(context, CONTROLLER0_ID, CFU0_ID, [PORT0_ID])); + let storage0 = STORAGE0.init(Storage::new(controller_context, CONTROLLER0_ID, CFU0_ID, [PORT0_ID])); static INTERMEDIATE0: StaticCell< IntermediateStorage<1, GlobalRawMutex, DynamicSender<'static, psu::event::EventData>>, > = StaticCell::new(); @@ -113,7 +108,7 @@ async fn task(spawner: Spawner) { let policy_receiver1 = policy_channel1.dyn_receiver(); static STORAGE1: StaticCell> = StaticCell::new(); - let storage1 = STORAGE1.init(Storage::new(context, CONTROLLER1_ID, CFU1_ID, [PORT1_ID])); + let storage1 = STORAGE1.init(Storage::new(controller_context, CONTROLLER1_ID, CFU1_ID, [PORT1_ID])); static INTERMEDIATE1: StaticCell< IntermediateStorage<1, GlobalRawMutex, DynamicSender<'static, psu::event::EventData>>, > = StaticCell::new(); @@ -149,7 +144,7 @@ async fn task(spawner: Spawner) { let policy_receiver2 = policy_channel2.dyn_receiver(); static STORAGE2: StaticCell> = StaticCell::new(); - let storage2 = STORAGE2.init(Storage::new(context, CONTROLLER2_ID, CFU2_ID, [PORT2_ID])); + let storage2 = STORAGE2.init(Storage::new(controller_context, CONTROLLER2_ID, CFU2_ID, [PORT2_ID])); static INTERMEDIATE2: StaticCell< IntermediateStorage<1, GlobalRawMutex, DynamicSender<'static, psu::event::EventData>>, > = StaticCell::new(); @@ -210,7 +205,6 @@ async fn task(spawner: Spawner) { let type_c_service = TYPE_C_SERVICE.init(Service::create( Default::default(), controller_context, - controller_list, power_policy_publisher, power_policy_subscriber, )); diff --git a/examples/std/src/lib/type_c/mock_controller.rs b/examples/std/src/lib/type_c/mock_controller.rs index 1f9352c5b..5f4d4e962 100644 --- a/examples/std/src/lib/type_c/mock_controller.rs +++ b/examples/std/src/lib/type_c/mock_controller.rs @@ -8,14 +8,11 @@ use embedded_usb_pd::{type_c::ConnectionState, ucsi::lpm}; use log::{debug, info, trace}; use power_policy_interface::capability::PowerCapability; -use type_c_service::type_c::{ - controller::{ - AttnVdm, ControllerStatus, DpConfig, DpPinConfig, DpStatus, OtherVdm, PdStateMachineConfig, PortStatus, - RetimerFwUpdateState, SendVdm, TbtConfig, TypeCStateMachineState, UsbControlConfig, - }, - event::PortEvent, - power_capability_from_current, +use type_c_interface::port::{ + AttnVdm, ControllerStatus, DpConfig, DpPinConfig, DpStatus, OtherVdm, PdStateMachineConfig, PortStatus, + RetimerFwUpdateState, SendVdm, TbtConfig, TypeCStateMachineState, UsbControlConfig, event::PortEvent, }; +use type_c_service::util::power_capability_from_current; pub struct ControllerState { events: Signal, @@ -116,7 +113,7 @@ impl<'a> Controller<'a> { } } -impl type_c_service::type_c::controller::Controller for Controller<'_> { +impl type_c_interface::port::Controller for Controller<'_> { type BusError = (); async fn wait_port_event(&mut self) -> Result<(), Error> { diff --git a/type-c-interface/Cargo.toml b/type-c-interface/Cargo.toml new file mode 100644 index 000000000..b425a1fed --- /dev/null +++ b/type-c-interface/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "type-c-interface" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +bitfield.workspace = true +bitvec.workspace = true +embassy-sync.workspace = true +embassy-time.workspace = true +log = { workspace = true, optional = true } +defmt = { workspace = true, optional = true } +embedded-services.workspace = true +embedded-usb-pd.workspace = true +power-policy-interface.workspace = true + +[lints] +workspace = true + +[features] +default = [] +defmt = [ + "dep:defmt", + "embassy-sync/defmt", + "embassy-time/defmt", + "embedded-services/defmt", + "embedded-usb-pd/defmt", + "power-policy-interface/defmt", +] +log = [ + "dep:log", + "embassy-sync/log", + "embassy-time/log", + "embedded-services/log", + "power-policy-interface/log", +] diff --git a/type-c-interface/src/lib.rs b/type-c-interface/src/lib.rs new file mode 100644 index 000000000..8c5e96b62 --- /dev/null +++ b/type-c-interface/src/lib.rs @@ -0,0 +1,4 @@ +//! Interface for type-C service. +#![no_std] +pub mod port; +pub mod service; diff --git a/type-c-service/src/type_c/event.rs b/type-c-interface/src/port/event.rs similarity index 100% rename from type-c-service/src/type_c/event.rs rename to type-c-interface/src/port/event.rs diff --git a/type-c-interface/src/port/mod.rs b/type-c-interface/src/port/mod.rs new file mode 100644 index 000000000..f52836b6a --- /dev/null +++ b/type-c-interface/src/port/mod.rs @@ -0,0 +1,668 @@ +//! PD controller related code +use core::future::Future; + +use embedded_usb_pd::ucsi::{self, lpm}; +use embedded_usb_pd::{ + DataRole, Error, GlobalPortId, LocalPortId, PdError, PlugOrientation, PowerRole, + ado::Ado, + pdinfo::{AltMode, PowerPathStatus}, + type_c::ConnectionState, +}; + +use embedded_services::ipc::deferred; +use embedded_services::{GlobalRawMutex, intrusive_list}; + +pub mod event; + +use event::{PortEvent, PortPending}; + +/// Length of the Other VDM data +pub const OTHER_VDM_LEN: usize = 29; +/// Length of the Attention VDM data +pub const ATTN_VDM_LEN: usize = 9; +/// maximum number of data objects in a VDM +pub const MAX_NUM_DATA_OBJECTS: usize = 7; // 7 VDOs of 4 bytes each + +/// Newtype to help clarify arguments to port status commands +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Cached(pub bool); + +/// Controller ID +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct ControllerId(pub u8); + +/// Port status +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PortStatus { + /// Current available source contract + pub available_source_contract: Option, + /// Current available sink contract + pub available_sink_contract: Option, + /// Current connection state + pub connection_state: Option, + /// Port partner supports dual-power roles + pub dual_power: bool, + /// plug orientation + pub plug_orientation: PlugOrientation, + /// power role + pub power_role: PowerRole, + /// data role + pub data_role: DataRole, + /// Active alt-modes + 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 { + /// Create a new blank port status + /// Needed because default() is not const + pub const fn new() -> Self { + Self { + 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, + } + } + + /// Check if the port is connected + pub fn is_connected(&self) -> bool { + matches!( + self.connection_state, + Some(ConnectionState::Attached) + | Some(ConnectionState::DebugAccessory) + | Some(ConnectionState::AudioAccessory) + ) + } + + /// Check if a debug accessory is connected + pub fn is_debug_accessory(&self) -> bool { + matches!(self.connection_state, Some(ConnectionState::DebugAccessory)) + } +} + +impl Default for PortStatus { + fn default() -> Self { + Self::new() + } +} + +/// Other Vdm data +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct OtherVdm { + /// Other VDM data + pub data: [u8; OTHER_VDM_LEN], +} + +impl Default for OtherVdm { + fn default() -> Self { + Self { + data: [0; OTHER_VDM_LEN], + } + } +} + +impl From for [u8; OTHER_VDM_LEN] { + fn from(vdm: OtherVdm) -> Self { + vdm.data + } +} + +impl From<[u8; OTHER_VDM_LEN]> for OtherVdm { + fn from(data: [u8; OTHER_VDM_LEN]) -> Self { + Self { data } + } +} + +/// Attention Vdm data +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct AttnVdm { + /// Attention VDM data + pub data: [u8; ATTN_VDM_LEN], +} + +/// DisplayPort pin configuration +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DpPinConfig { + /// 4L DP connection using USBC-USBC cable (Pin Assignment C) + pub pin_c: bool, + /// 2L USB + 2L DP connection using USBC-USBC cable (Pin Assignment D) + pub pin_d: bool, + /// 4L DP connection using USBC-DP cable (Pin Assignment E) + pub pin_e: bool, +} + +/// DisplayPort status data +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DpStatus { + /// DP alt-mode entered + pub alt_mode_entered: bool, + /// Get DP DFP pin config + pub dfp_d_pin_cfg: DpPinConfig, +} + +/// DisplayPort configuration data +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DpConfig { + /// DP alt-mode enabled + pub enable: bool, + /// Set DP DFP pin config + pub dfp_d_pin_cfg: DpPinConfig, +} + +impl Default for AttnVdm { + fn default() -> Self { + Self { + data: [0; ATTN_VDM_LEN], + } + } +} + +impl From for [u8; ATTN_VDM_LEN] { + fn from(vdm: AttnVdm) -> Self { + vdm.data + } +} + +impl From<[u8; ATTN_VDM_LEN]> for AttnVdm { + fn from(data: [u8; ATTN_VDM_LEN]) -> Self { + Self { data } + } +} + +/// Send VDM data +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct SendVdm { + /// initiating a VDM sequence + pub initiator: bool, + /// VDO count + pub vdo_count: u8, + /// VDO data + pub vdo_data: [u32; MAX_NUM_DATA_OBJECTS], +} + +impl SendVdm { + /// Create a new blank port status + pub const fn new() -> Self { + Self { + initiator: false, + vdo_count: 0, + vdo_data: [0; MAX_NUM_DATA_OBJECTS], + } + } +} + +impl Default for SendVdm { + fn default() -> Self { + Self::new() + } +} + +/// USB control configuration +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct UsbControlConfig { + /// Enable USB2 data path + pub usb2_enabled: bool, + /// Enable USB3 data path + pub usb3_enabled: bool, + /// Enable USB4 data path + pub usb4_enabled: bool, +} + +impl Default for UsbControlConfig { + fn default() -> Self { + Self { + usb2_enabled: true, + usb3_enabled: true, + usb4_enabled: true, + } + } +} + +/// Thunderbolt control configuration +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Clone, Default, Copy, PartialEq)] +pub struct TbtConfig { + /// Enable Thunderbolt + pub tbt_enabled: bool, +} + +/// PD state-machine configuration +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Clone, Default, Copy, PartialEq)] +pub struct PdStateMachineConfig { + /// Enable or disable the PD state-machine + pub enabled: bool, +} + +/// TypeC State Machine +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TypeCStateMachineState { + /// Sink state machine only + Sink, + /// Source state machine only + Source, + /// DRP state machine + Drp, + /// Disabled + Disabled, +} + +/// Port-specific command data +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum PortCommandData { + /// Get port status + PortStatus(Cached), + /// Get and clear events + ClearEvents, + /// Get retimer fw update state + RetimerFwUpdateGetState, + /// Set retimer fw update state + RetimerFwUpdateSetState, + /// Clear retimer fw update state + RetimerFwUpdateClearState, + /// Set retimer compliance + SetRetimerCompliance, + /// Reconfigure retimer + ReconfigureRetimer, + /// Get oldest unhandled PD alert + GetPdAlert, + /// Set the maximum sink voltage in mV for the given port + SetMaxSinkVoltage(Option), + /// Set unconstrained power + SetUnconstrainedPower(bool), + /// Clear the dead battery flag for the given port + ClearDeadBatteryFlag, + /// Get other VDM + GetOtherVdm, + /// Get attention VDM + GetAttnVdm, + /// Send VDM + SendVdm(SendVdm), + /// Set USB control configuration + SetUsbControl(UsbControlConfig), + /// Get DisplayPort status + GetDpStatus, + /// Set DisplayPort configuration + SetDpConfig(DpConfig), + /// Execute DisplayPort reset + ExecuteDrst, + /// Set Thunderbolt configuration + SetTbtConfig(TbtConfig), + /// Set PD state-machine configuration + SetPdStateMachineConfig(PdStateMachineConfig), + /// Set Type-C state-machine configuration + SetTypeCStateMachineConfig(TypeCStateMachineState), + /// Execute the UCSI command + ExecuteUcsiCommand(lpm::CommandData), +} + +/// Port-specific commands +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PortCommand { + /// Port ID + pub port: GlobalPortId, + /// Command data + pub data: PortCommandData, +} + +/// PD controller command-specific data +#[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RetimerFwUpdateState { + /// Retimer FW Update Inactive + Inactive, + /// Retimer FW Update Active + Active, +} + +/// Port-specific response data +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum PortResponseData { + /// Command completed with no error + Complete, + /// Port status + PortStatus(PortStatus), + /// ClearEvents + ClearEvents(PortEvent), + /// Retimer Fw Update status + RtFwUpdateStatus(RetimerFwUpdateState), + /// PD alert + PdAlert(Option), + /// Get other VDM + OtherVdm(OtherVdm), + /// Get attention VDM + AttnVdm(AttnVdm), + /// Get DisplayPort status + DpStatus(DpStatus), + /// UCSI response + UcsiResponse(Result, PdError>), +} + +impl PortResponseData { + /// Helper function to convert to a result + pub fn complete_or_err(self) -> Result<(), PdError> { + match self { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } +} + +/// Port-specific command response +pub type PortResponse = Result; + +/// PD controller command-specific data +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum InternalCommandData { + /// Reset the PD controller + Reset, + /// Get controller status + Status, + /// Sync controller state + SyncState, +} + +/// PD controller command +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Command { + /// Controller specific command + Controller(InternalCommandData), + /// Port command + Port(PortCommand), + /// UCSI command passthrough + Lpm(lpm::GlobalCommand), +} + +/// Controller-specific response data +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum InternalResponseData<'a> { + /// Command complete + Complete, + /// Controller status + Status(ControllerStatus<'a>), +} + +/// Response for controller-specific commands +pub type InternalResponse<'a> = Result, PdError>; + +/// PD controller command response +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Response<'a> { + /// Controller response + Controller(InternalResponse<'a>), + /// UCSI response passthrough + Ucsi(ucsi::GlobalResponse), + /// Port response + Port(PortResponse), +} + +/// Controller status +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct ControllerStatus<'a> { + /// Current controller mode + pub mode: &'a str, + /// True if we did not have to boot from a backup FW bank + pub valid_fw_bank: bool, + /// FW version 0 + pub fw_version0: u32, + /// FW version 1 + pub fw_version1: u32, +} + +/// PD controller +pub struct Device<'a> { + node: intrusive_list::Node, + id: ControllerId, + ports: &'a [GlobalPortId], + num_ports: usize, + command: deferred::Channel>, +} + +impl intrusive_list::NodeContainer for Device<'static> { + fn get_node(&self) -> &intrusive_list::Node { + &self.node + } +} + +impl<'a> Device<'a> { + /// Create a new PD controller struct + pub fn new(id: ControllerId, ports: &'a [GlobalPortId]) -> Self { + Self { + node: intrusive_list::Node::uninit(), + id, + ports, + num_ports: ports.len(), + command: deferred::Channel::new(), + } + } + + /// Get the controller ID + pub fn id(&self) -> ControllerId { + self.id + } + + /// Send a command to this controller + pub async fn execute_command(&self, command: Command) -> Response<'_> { + self.command.execute(command).await + } + + /// Check if this controller has the given port + pub fn has_port(&self, port: GlobalPortId) -> bool { + self.lookup_local_port(port).is_ok() + } + + /// Convert a local port ID to a global port ID + pub fn lookup_global_port(&self, port: LocalPortId) -> Result { + Ok(*self.ports.get(port.0 as usize).ok_or(PdError::InvalidParams)?) + } + + /// Convert a global port ID to a local port ID + pub fn lookup_local_port(&self, port: GlobalPortId) -> Result { + self.ports + .iter() + .position(|p| *p == port) + .map(|p| LocalPortId(p as u8)) + .ok_or(PdError::InvalidParams) + } + + /// Create a command handler for this controller + /// + /// DROP SAFETY: Direct call to deferred channel primitive + pub async fn receive(&self) -> deferred::Request<'_, GlobalRawMutex, Command, Response<'static>> { + self.command.receive().await + } + + /// Notify that there are pending events on one or more ports + pub fn notify_ports(&self, ctx: &crate::service::context::Context, pending: PortPending) { + ctx.notify_ports(pending); + } + + /// Number of ports on this controller + pub fn num_ports(&self) -> usize { + self.num_ports + } + + /// Slice of global ports on the Device + pub fn ports(&self) -> &'a [GlobalPortId] { + self.ports + } +} + +/// PD controller trait that device drivers may use to integrate with internal messaging system +pub trait Controller { + /// Type of error returned by the bus + type BusError; + + /// Wait for a port event to occur + /// # Implementation guide + /// This function should be drop safe. + /// Any intermediate side effects must be undone if the returned [`Future`] is dropped before completing. + fn wait_port_event(&mut self) -> impl Future>>; + /// Returns and clears current events for the given port + /// # Implementation guide + /// This function should be drop safe. + /// Any intermediate side effects must be undone if the returned [`Future`] is dropped before completing. + fn clear_port_events( + &mut self, + port: LocalPortId, + ) -> impl Future>>; + /// Returns the port status + fn get_port_status(&mut self, port: LocalPortId) + -> impl Future>>; + + /// Reset the controller + fn reset_controller(&mut self) -> impl Future>>; + + /// Returns the retimer fw update state + fn get_rt_fw_update_status( + &mut self, + port: LocalPortId, + ) -> impl Future>>; + /// Set retimer fw update state + fn set_rt_fw_update_state(&mut self, port: LocalPortId) -> impl Future>>; + /// Clear retimer fw update state + fn clear_rt_fw_update_state( + &mut self, + port: LocalPortId, + ) -> impl Future>>; + /// Set retimer compliance + fn set_rt_compliance(&mut self, port: LocalPortId) -> impl Future>>; + + /// Reconfigure the retimer for the given port. + fn reconfigure_retimer(&mut self, port: LocalPortId) -> impl Future>>; + + /// Clear the dead battery flag for the given port. + fn clear_dead_battery_flag(&mut self, port: LocalPortId) + -> impl Future>>; + + /// Enable or disable sink path + fn enable_sink_path( + &mut self, + port: LocalPortId, + enable: bool, + ) -> impl Future>>; + /// Get current controller status + fn get_controller_status( + &mut self, + ) -> impl Future, Error>>; + /// Get current PD alert + fn get_pd_alert(&mut self, port: LocalPortId) -> impl Future, Error>>; + /// Set the maximum sink voltage for the given port + /// + /// This may trigger a renegotiation + fn set_max_sink_voltage( + &mut self, + port: LocalPortId, + voltage_mv: Option, + ) -> impl Future>>; + /// Set port unconstrained status + fn set_unconstrained_power( + &mut self, + port: LocalPortId, + unconstrained: bool, + ) -> impl Future>>; + + // TODO: remove all these once we migrate to a generic FW update trait + // https://github.com/OpenDevicePartnership/embedded-services/issues/242 + /// Get current FW version + fn get_active_fw_version(&mut self) -> impl Future>>; + /// Start a firmware update + fn start_fw_update(&mut self) -> impl Future>>; + /// Abort a firmware update + fn abort_fw_update(&mut self) -> impl Future>>; + /// Finalize a firmware update + fn finalize_fw_update(&mut self) -> impl Future>>; + /// Write firmware update contents + fn write_fw_contents( + &mut self, + offset: usize, + data: &[u8], + ) -> impl Future>>; + /// Get the Rx Other VDM data for the given port + fn get_other_vdm(&mut self, port: LocalPortId) -> impl Future>>; + /// Get the Rx Attention VDM data for the given port + fn get_attn_vdm(&mut self, port: LocalPortId) -> impl Future>>; + /// Send a VDM to the given port + fn send_vdm( + &mut self, + port: LocalPortId, + tx_vdm: SendVdm, + ) -> impl Future>>; + + /// Set USB control configuration for the given port + fn set_usb_control( + &mut self, + port: LocalPortId, + config: UsbControlConfig, + ) -> impl Future>>; + + /// Get DisplayPort status for the given port + fn get_dp_status(&mut self, port: LocalPortId) -> impl Future>>; + /// Set DisplayPort configuration for the given port + fn set_dp_config( + &mut self, + port: LocalPortId, + config: DpConfig, + ) -> impl Future>>; + /// Execute PD Data Reset for the given port + fn execute_drst(&mut self, port: LocalPortId) -> impl Future>>; + + /// Set Thunderbolt configuration for the given port + fn set_tbt_config( + &mut self, + port: LocalPortId, + config: TbtConfig, + ) -> impl Future>>; + + /// Set PD state-machine configuration for the given port + fn set_pd_state_machine_config( + &mut self, + port: LocalPortId, + config: PdStateMachineConfig, + ) -> impl Future>>; + + /// Set Type-C state-machine configuration for the given port + fn set_type_c_state_machine_config( + &mut self, + port: LocalPortId, + state: TypeCStateMachineState, + ) -> impl Future>>; + + /// Execute the given UCSI command + fn execute_ucsi_command( + &mut self, + command: lpm::LocalCommand, + ) -> impl Future, Error>>; +} diff --git a/type-c-interface/src/service/context.rs b/type-c-interface/src/service/context.rs new file mode 100644 index 000000000..5efa98652 --- /dev/null +++ b/type-c-interface/src/service/context.rs @@ -0,0 +1,537 @@ +use embassy_sync::signal::Signal; +use embassy_time::{Duration, with_timeout}; +use embedded_usb_pd::ucsi::{self, lpm}; +use embedded_usb_pd::{GlobalPortId, PdError, ado::Ado}; + +use crate::port::event::{PortEvent, PortPending}; +use crate::port::{ + AttnVdm, Command, ControllerStatus, Device, DpConfig, DpStatus, InternalCommandData, InternalResponseData, + OtherVdm, PdStateMachineConfig, PortCommand, PortCommandData, PortResponseData, PortStatus, Response, + RetimerFwUpdateState, SendVdm, TbtConfig, TypeCStateMachineState, UsbControlConfig, +}; +use crate::port::{Cached, ControllerId}; +use crate::service; +use crate::service::event::Event; +use embedded_services::{ + GlobalRawMutex, IntrusiveNode, broadcaster::immediate as broadcaster, error, intrusive_list, trace, +}; + +/// Default command timeout +/// set to high value since this is intended to prevent an unresponsive device from blocking the service implementation +const DEFAULT_TIMEOUT: Duration = Duration::from_millis(5000); + +/// Trait for types that contain a controller struct +pub trait DeviceContainer { + /// Get the controller struct + fn get_pd_controller_device(&self) -> &Device<'_>; +} + +impl DeviceContainer for Device<'_> { + fn get_pd_controller_device(&self) -> &Device<'_> { + self + } +} + +/// Type-C service context +/// +/// This struct is going to be merged into the service implementation and removed from here. +pub struct Context { + port_events: Signal, + /// Event broadcaster + broadcaster: broadcaster::Immediate, + /// Controller list + controllers: intrusive_list::IntrusiveList, +} + +impl Default for Context { + fn default() -> Self { + Self::new() + } +} + +impl Context { + /// Create new Context + pub const fn new() -> Self { + Self { + port_events: Signal::new(), + broadcaster: broadcaster::Immediate::new(), + controllers: intrusive_list::IntrusiveList::new(), + } + } + + /// Notify that there are pending events on one or more ports + /// Each bit corresponds to a global port ID + pub fn notify_ports(&self, pending: PortPending) { + let raw_pending: u32 = pending.into(); + trace!("Notify ports: {:#x}", raw_pending); + // Early exit if no events + if pending.is_none() { + return; + } + + self.port_events + .signal(if let Some(flags) = self.port_events.try_take() { + flags.union(pending) + } else { + pending + }); + } + + /// Send a command to the given controller with no timeout + pub async fn send_controller_command_no_timeout( + &self, + controller_id: ControllerId, + command: InternalCommandData, + ) -> Result, PdError> { + let node = self + .controllers + .into_iter() + .find(|node| { + if let Some(controller) = node.data::() { + controller.id() == controller_id + } else { + false + } + }) + .ok_or(PdError::InvalidController)?; + + match node + .data::() + .ok_or(PdError::InvalidController)? + .execute_command(Command::Controller(command)) + .await + { + Response::Controller(response) => response, + r => { + error!("Invalid response: expected controller, got {:?}", r); + Err(PdError::InvalidResponse) + } + } + } + + /// Send a command to the given controller with a timeout + pub async fn send_controller_command( + &self, + controller_id: ControllerId, + command: InternalCommandData, + ) -> Result, PdError> { + match with_timeout( + DEFAULT_TIMEOUT, + self.send_controller_command_no_timeout(controller_id, command), + ) + .await + { + Ok(response) => response, + Err(_) => Err(PdError::Timeout), + } + } + + /// Reset the given controller + pub async fn reset_controller(&self, controller_id: ControllerId) -> Result<(), PdError> { + self.send_controller_command(controller_id, InternalCommandData::Reset) + .await + .map(|_| ()) + } + + fn find_node_by_port(&self, port_id: GlobalPortId) -> Result<&IntrusiveNode, PdError> { + self.controllers + .into_iter() + .find(|node| { + if let Some(controller) = node.data::() { + controller.has_port(port_id) + } else { + false + } + }) + .ok_or(PdError::InvalidPort) + } + + /// Send a command to the given port + pub async fn send_port_command_ucsi_no_timeout( + &self, + port_id: GlobalPortId, + command: lpm::CommandData, + ) -> Result { + let node = self.find_node_by_port(port_id)?; + + match node + .data::() + .ok_or(PdError::InvalidController)? + .execute_command(Command::Lpm(lpm::Command::new(port_id, command))) + .await + { + Response::Ucsi(response) => Ok(response), + r => { + error!("Invalid response: expected LPM, got {:?}", r); + Err(PdError::InvalidResponse) + } + } + } + + /// Send a command to the given port with a timeout + pub async fn send_port_command_ucsi( + &self, + port_id: GlobalPortId, + command: lpm::CommandData, + ) -> Result { + match with_timeout( + DEFAULT_TIMEOUT, + self.send_port_command_ucsi_no_timeout(port_id, command), + ) + .await + { + Ok(response) => response, + Err(_) => Err(PdError::Timeout), + } + } + + /// Send a command to the given port with no timeout + pub async fn send_port_command_no_timeout( + &self, + port_id: GlobalPortId, + command: PortCommandData, + ) -> Result { + let node = self.find_node_by_port(port_id)?; + + match node + .data::() + .ok_or(PdError::InvalidController)? + .execute_command(Command::Port(PortCommand { + port: port_id, + data: command, + })) + .await + { + Response::Port(response) => response, + r => { + error!("Invalid response: expected port, got {:?}", r); + Err(PdError::InvalidResponse) + } + } + } + + /// Send a command to the given port with a timeout + pub async fn send_port_command( + &self, + port_id: GlobalPortId, + command: PortCommandData, + ) -> Result { + match with_timeout(DEFAULT_TIMEOUT, self.send_port_command_no_timeout(port_id, command)).await { + Ok(response) => response, + Err(_) => Err(PdError::Timeout), + } + } + + /// Get the current port events + pub async fn get_unhandled_events(&self) -> PortPending { + self.port_events.wait().await + } + + /// Get the unhandled events for the given port + pub async fn get_port_event(&self, port: GlobalPortId) -> Result { + match self.send_port_command(port, PortCommandData::ClearEvents).await? { + PortResponseData::ClearEvents(event) => Ok(event), + r => { + error!("Invalid response: expected clear events, got {:?}", r); + Err(PdError::InvalidResponse) + } + } + } + + /// Get the current port status + pub async fn get_port_status(&self, port: GlobalPortId, cached: Cached) -> Result { + match self + .send_port_command(port, PortCommandData::PortStatus(cached)) + .await? + { + PortResponseData::PortStatus(status) => Ok(status), + r => { + error!("Invalid response: expected port status, got {:?}", r); + Err(PdError::InvalidResponse) + } + } + } + + /// Get the oldest unhandled PD alert for the given port + pub async fn get_pd_alert(&self, port: GlobalPortId) -> Result, PdError> { + match self.send_port_command(port, PortCommandData::GetPdAlert).await? { + PortResponseData::PdAlert(alert) => Ok(alert), + r => { + error!("Invalid response: expected PD alert, got {:?}", r); + Err(PdError::InvalidResponse) + } + } + } + + /// Get the retimer fw update status + pub async fn get_rt_fw_update_status(&self, port: GlobalPortId) -> Result { + match self + .send_port_command(port, PortCommandData::RetimerFwUpdateGetState) + .await? + { + PortResponseData::RtFwUpdateStatus(status) => Ok(status), + _ => Err(PdError::InvalidResponse), + } + } + + /// Set the retimer fw update state + pub async fn set_rt_fw_update_state(&self, port: GlobalPortId) -> Result<(), PdError> { + match self + .send_port_command(port, PortCommandData::RetimerFwUpdateSetState) + .await? + { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Clear the retimer fw update state + pub async fn clear_rt_fw_update_state(&self, port: GlobalPortId) -> Result<(), PdError> { + match self + .send_port_command(port, PortCommandData::RetimerFwUpdateClearState) + .await? + { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Set the retimer compliance + pub async fn set_rt_compliance(&self, port: GlobalPortId) -> Result<(), PdError> { + match self + .send_port_command(port, PortCommandData::SetRetimerCompliance) + .await? + { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Reconfigure the retimer for the given port. + pub async fn reconfigure_retimer(&self, port: GlobalPortId) -> Result<(), PdError> { + match self + .send_port_command(port, PortCommandData::ReconfigureRetimer) + .await? + { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Set the maximum sink voltage for the given port. + /// + /// See [`PortCommandData::SetMaxSinkVoltage`] for details on the `max_voltage_mv` parameter. + pub async fn set_max_sink_voltage(&self, port: GlobalPortId, max_voltage_mv: Option) -> Result<(), PdError> { + match self + .send_port_command(port, PortCommandData::SetMaxSinkVoltage(max_voltage_mv)) + .await? + { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Clear the dead battery flag for the given port. + pub async fn clear_dead_battery_flag(&self, port: GlobalPortId) -> Result<(), PdError> { + match self + .send_port_command(port, PortCommandData::ClearDeadBatteryFlag) + .await? + { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Get current controller status + pub async fn get_controller_status( + &self, + controller_id: ControllerId, + ) -> Result, PdError> { + match self + .send_controller_command(controller_id, InternalCommandData::Status) + .await? + { + InternalResponseData::Status(status) => Ok(status), + r => { + error!("Invalid response: expected controller status, got {:?}", r); + Err(PdError::InvalidResponse) + } + } + } + + /// Set unconstrained power for the given port + pub async fn set_unconstrained_power(&self, port: GlobalPortId, unconstrained: bool) -> Result<(), PdError> { + match self + .send_port_command(port, PortCommandData::SetUnconstrainedPower(unconstrained)) + .await? + { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Sync controller state + pub async fn sync_controller_state(&self, controller_id: ControllerId) -> Result<(), PdError> { + match self + .send_controller_command(controller_id, InternalCommandData::SyncState) + .await? + { + InternalResponseData::Complete => Ok(()), + r => { + error!("Invalid response: expected controller status, got {:?}", r); + Err(PdError::InvalidResponse) + } + } + } + + /// Get the other vdm for the given port + pub async fn get_other_vdm(&self, port: GlobalPortId) -> Result { + match self.send_port_command(port, PortCommandData::GetOtherVdm).await? { + PortResponseData::OtherVdm(vdm) => Ok(vdm), + r => { + error!("Invalid response: expected other VDM, got {:?}", r); + Err(PdError::InvalidResponse) + } + } + } + + /// Get the attention vdm for the given port + pub async fn get_attn_vdm(&self, port: GlobalPortId) -> Result { + match self.send_port_command(port, PortCommandData::GetAttnVdm).await? { + PortResponseData::AttnVdm(vdm) => Ok(vdm), + r => { + error!("Invalid response: expected attention VDM, got {:?}", r); + Err(PdError::InvalidResponse) + } + } + } + + /// Send VDM to the given port + pub async fn send_vdm(&self, port: GlobalPortId, tx_vdm: SendVdm) -> Result<(), PdError> { + match self.send_port_command(port, PortCommandData::SendVdm(tx_vdm)).await? { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Set USB control configuration for the given port + pub async fn set_usb_control(&self, port: GlobalPortId, config: UsbControlConfig) -> Result<(), PdError> { + match self + .send_port_command(port, PortCommandData::SetUsbControl(config)) + .await? + { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Get DisplayPort status for the given port + pub async fn get_dp_status(&self, port: GlobalPortId) -> Result { + match self.send_port_command(port, PortCommandData::GetDpStatus).await? { + PortResponseData::DpStatus(status) => Ok(status), + r => { + error!("Invalid response: expected DP status, got {:?}", r); + Err(PdError::InvalidResponse) + } + } + } + + /// Set DisplayPort configuration for the given port + pub async fn set_dp_config(&self, port: GlobalPortId, config: DpConfig) -> Result<(), PdError> { + match self + .send_port_command(port, PortCommandData::SetDpConfig(config)) + .await? + { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Execute PD Data Reset for the given port + pub async fn execute_drst(&self, port: GlobalPortId) -> Result<(), PdError> { + match self.send_port_command(port, PortCommandData::ExecuteDrst).await? { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Set Thunderbolt configuration for the given port + pub async fn set_tbt_config(&self, port: GlobalPortId, config: TbtConfig) -> Result<(), PdError> { + match self + .send_port_command(port, PortCommandData::SetTbtConfig(config)) + .await? + { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Set PD state-machine configuration for the given port + pub async fn set_pd_state_machine_config( + &self, + port: GlobalPortId, + config: PdStateMachineConfig, + ) -> Result<(), PdError> { + match self + .send_port_command(port, PortCommandData::SetPdStateMachineConfig(config)) + .await? + { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Set Type-C state-machine configuration for the given port + pub async fn set_type_c_state_machine_config( + &self, + port: GlobalPortId, + state: TypeCStateMachineState, + ) -> Result<(), PdError> { + match self + .send_port_command(port, PortCommandData::SetTypeCStateMachineConfig(state)) + .await? + { + PortResponseData::Complete => Ok(()), + _ => Err(PdError::InvalidResponse), + } + } + + /// Execute the given UCSI command + pub async fn execute_ucsi_command( + &self, + command: lpm::GlobalCommand, + ) -> Result, PdError> { + match self + .send_port_command(command.port(), PortCommandData::ExecuteUcsiCommand(command.operation())) + .await? + { + PortResponseData::UcsiResponse(response) => response, + _ => Err(PdError::InvalidResponse), + } + } + + /// Register a message receiver for type-C messages + pub async fn register_message_receiver( + &self, + receiver: &'static broadcaster::Receiver<'_, service::event::Event>, + ) -> intrusive_list::Result<()> { + self.broadcaster.register_receiver(receiver) + } + + /// Broadcast a type-C message to all subscribers + pub async fn broadcast_message(&self, message: service::event::Event) { + self.broadcaster.broadcast(message).await; + } + + /// Register a PD controller + pub fn register_controller(&self, controller: &'static impl DeviceContainer) -> Result<(), intrusive_list::Error> { + self.controllers.push(controller.get_pd_controller_device()) + } + + /// Get total number of ports on the system + pub fn get_num_ports(&self) -> usize { + self.controllers + .iter_only::() + .fold(0, |acc, controller| acc + controller.num_ports()) + } +} diff --git a/type-c-service/src/type_c/comms.rs b/type-c-interface/src/service/event.rs similarity index 88% rename from type-c-service/src/type_c/comms.rs rename to type-c-interface/src/service/event.rs index 9a998cd31..800d15558 100644 --- a/type-c-service/src/type_c/comms.rs +++ b/type-c-interface/src/service/event.rs @@ -5,7 +5,7 @@ use embedded_usb_pd::GlobalPortId; /// Message generated when a debug acessory is connected or disconnected #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DebugAccessoryMessage { +pub struct DebugAccessory { /// Port pub port: GlobalPortId, /// Connected @@ -25,9 +25,9 @@ pub struct UsciChangeIndicator { /// Top-level comms message #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum CommsMessage { +pub enum Event { /// Debug accessory message - DebugAccessory(DebugAccessoryMessage), + DebugAccessory(DebugAccessory), /// UCSI CCI message UcsiCci(UsciChangeIndicator), } diff --git a/type-c-interface/src/service/mod.rs b/type-c-interface/src/service/mod.rs new file mode 100644 index 000000000..23749388f --- /dev/null +++ b/type-c-interface/src/service/mod.rs @@ -0,0 +1,2 @@ +pub mod context; +pub mod event; diff --git a/type-c-service/Cargo.toml b/type-c-service/Cargo.toml index 812ed8972..1ae9105f3 100644 --- a/type-c-service/Cargo.toml +++ b/type-c-service/Cargo.toml @@ -28,7 +28,7 @@ log = { workspace = true, optional = true } tps6699x = { workspace = true, features = ["embassy"] } cfu-service.workspace = true power-policy-interface.workspace = true -bitvec.workspace = true +type-c-interface.workspace = true [dev-dependencies] embassy-time = { workspace = true, features = ["std", "generic-queue-8"] } @@ -50,6 +50,7 @@ defmt = [ "embedded-usb-pd/defmt", "cfu-service/defmt", "power-policy-interface/defmt", + "type-c-interface/defmt", ] log = [ "dep:log", @@ -59,4 +60,5 @@ log = [ "tps6699x/log", "cfu-service/log", "power-policy-interface/log", + "type-c-interface/log", ] diff --git a/type-c-service/src/driver/tps6699x.rs b/type-c-service/src/driver/tps6699x.rs index 8470af87f..f801a6e85 100644 --- a/type-c-service/src/driver/tps6699x.rs +++ b/type-c-service/src/driver/tps6699x.rs @@ -1,10 +1,3 @@ -use crate::type_c; -use crate::type_c::ATTN_VDM_LEN; -use crate::type_c::controller::{ - self, AttnVdm, Controller, ControllerStatus, DpPinConfig, OtherVdm, PortStatus, SendVdm, TbtConfig, - TypeCStateMachineState, UsbControlConfig, -}; -use crate::type_c::event::PortEvent; use ::tps6699x::registers::field_sets::IntEventBus1; use ::tps6699x::registers::{PdCcPullUp, PpExtVbusSw, PpIntVbusSw}; use ::tps6699x::{PORT0, PORT1, TPS66993_NUM_PORTS, TPS66994_NUM_PORTS}; @@ -35,9 +28,15 @@ use tps6699x::command::{ }; use tps6699x::fw_update::UpdateConfig as FwUpdateConfig; use tps6699x::registers::port_config::TypeCStateMachine; +use type_c_interface::port::event::PortEvent; +use type_c_interface::port::{ATTN_VDM_LEN, DpConfig, DpStatus, PdStateMachineConfig, RetimerFwUpdateState}; +use type_c_interface::port::{ + AttnVdm, Controller, ControllerStatus, DpPinConfig, OtherVdm, PortStatus, SendVdm, TbtConfig, + TypeCStateMachineState, UsbControlConfig, +}; -use crate::type_c::power_capability_from_current; -use crate::type_c::power_capability_try_from_contract; +use crate::util::power_capability_from_current; +use crate::util::power_capability_try_from_contract; type Updater<'a, M, B> = BorrowedUpdaterInProgress>; @@ -409,10 +408,10 @@ impl Controller for Tps6699x<'_, M, B> { async fn get_rt_fw_update_status( &mut self, port: LocalPortId, - ) -> Result> { + ) -> Result> { match self.tps6699x.get_rt_fw_update_status(port).await { - Ok(true) => Ok(type_c::controller::RetimerFwUpdateState::Active), - Ok(false) => Ok(type_c::controller::RetimerFwUpdateState::Inactive), + Ok(true) => Ok(RetimerFwUpdateState::Active), + Ok(false) => Ok(RetimerFwUpdateState::Inactive), Err(e) => Err(e), } } @@ -658,7 +657,7 @@ impl Controller for Tps6699x<'_, M, B> { Ok(()) } - async fn get_dp_status(&mut self, port: LocalPortId) -> Result> { + async fn get_dp_status(&mut self, port: LocalPortId) -> Result> { let dp_status = self.tps6699x.get_dp_status(port).await?; debug!("Port{} DP status: {:#?}", port.0, dp_status); @@ -668,17 +667,13 @@ impl Controller for Tps6699x<'_, M, B> { let cfg_raw: PdDpPinConfig = dp_config.config_pin().into(); let pin_config: DpPinConfig = cfg_raw.into(); - Ok(controller::DpStatus { + Ok(DpStatus { alt_mode_entered, dfp_d_pin_cfg: pin_config, }) } - async fn set_dp_config( - &mut self, - port: LocalPortId, - config: controller::DpConfig, - ) -> Result<(), Error> { + async fn set_dp_config(&mut self, port: LocalPortId, config: DpConfig) -> Result<(), Error> { debug!("Port{} setting DP config: {:#?}", port.0, config); let mut dp_config_reg = self.tps6699x.get_dp_config(port).await?; @@ -717,7 +712,7 @@ impl Controller for Tps6699x<'_, M, B> { async fn set_pd_state_machine_config( &mut self, port: LocalPortId, - config: controller::PdStateMachineConfig, + config: PdStateMachineConfig, ) -> Result<(), Error> { debug!("Port{} setting PD state machine config: {:#?}", port.0, config); @@ -731,7 +726,7 @@ impl Controller for Tps6699x<'_, M, B> { async fn set_type_c_state_machine_config( &mut self, port: LocalPortId, - state: controller::TypeCStateMachineState, + state: TypeCStateMachineState, ) -> Result<(), Error> { debug!("Port{} setting Type-C state machine state: {:#?}", port.0, state); diff --git a/type-c-service/src/lib.rs b/type-c-service/src/lib.rs index c2bb53404..e15a4513e 100644 --- a/type-c-service/src/lib.rs +++ b/type-c-service/src/lib.rs @@ -2,12 +2,14 @@ pub mod driver; pub mod service; pub mod task; -pub mod type_c; +pub mod util; pub mod wrapper; use core::future::Future; -use type_c::event::{PortEvent, PortNotification, PortNotificationSingle, PortPendingIter, PortStatusChanged}; +use type_c_interface::port::event::{ + PortEvent, PortNotification, PortNotificationSingle, PortPendingIter, PortStatusChanged, +}; /// Enum to contain all port event variants #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -122,7 +124,7 @@ impl PortEventStreamer { mod tests { use core::sync::atomic::AtomicBool; - use crate::type_c::event::PortPending; + use type_c_interface::port::event::PortPending; use super::*; diff --git a/type-c-service/src/service/controller.rs b/type-c-service/src/service/controller.rs deleted file mode 100644 index 74debf8c0..000000000 --- a/type-c-service/src/service/controller.rs +++ /dev/null @@ -1,61 +0,0 @@ -use embedded_services::{debug, error}; - -use super::*; -use crate::type_c::{ - ControllerId, - external::{self, ControllerCommandData}, -}; - -impl<'a, PSU: Lockable> Service<'a, PSU> -where - PSU::Inner: psu::Psu, -{ - /// Process external controller status command - pub(super) async fn process_external_controller_status( - &self, - controller: ControllerId, - ) -> external::Response<'static> { - let status = self.context.get_controller_status(self.controllers, controller).await; - if let Err(e) = status { - error!("Error getting controller status: {:#?}", e); - } - external::Response::Controller(status.map(external::ControllerResponseData::ControllerStatus)) - } - - /// Process external controller sync state command - pub(super) async fn process_external_controller_sync_state( - &self, - controller: ControllerId, - ) -> external::Response<'static> { - let status = self.context.sync_controller_state(self.controllers, controller).await; - if let Err(e) = status { - error!("Error getting controller sync state: {:#?}", e); - } - external::Response::Controller(status.map(|_| external::ControllerResponseData::Complete)) - } - - /// Process external controller reset command - pub(super) async fn process_external_controller_reset( - &self, - controller: ControllerId, - ) -> external::Response<'static> { - let status = self.context.reset_controller(self.controllers, controller).await; - if let Err(e) = status { - error!("Error resetting controller: {:#?}", e); - } - external::Response::Controller(status.map(|_| external::ControllerResponseData::Complete)) - } - - /// Process external controller commands - pub(super) async fn process_external_controller_command( - &self, - command: &external::ControllerCommand, - ) -> external::Response<'static> { - debug!("Processing external controller command: {:#?}", command); - match command.data { - ControllerCommandData::ControllerStatus => self.process_external_controller_status(command.id).await, - ControllerCommandData::SyncState => self.process_external_controller_sync_state(command.id).await, - ControllerCommandData::Reset => self.process_external_controller_reset(command.id).await, - } - } -} diff --git a/type-c-service/src/service/mod.rs b/type-c-service/src/service/mod.rs index 68124cc56..4e209f883 100644 --- a/type-c-service/src/service/mod.rs +++ b/type-c-service/src/service/mod.rs @@ -1,24 +1,19 @@ -use embassy_futures::select::{Either3, select3}; +use embassy_futures::select::{Either, select}; use embassy_sync::{ mutex::Mutex, pubsub::{DynImmediatePublisher, DynSubscriber}, }; -use embedded_services::{GlobalRawMutex, debug, error, info, intrusive_list, ipc::deferred, sync::Lockable, trace}; +use embedded_services::{GlobalRawMutex, debug, error, info, sync::Lockable, trace}; use embedded_usb_pd::GlobalPortId; use embedded_usb_pd::PdError as Error; use power_policy_interface::psu; -use crate::type_c::{ - self, Cached, comms, - controller::PortStatus, - event::{PortNotificationSingle, PortStatusChanged}, - external, -}; - use crate::{PortEventStreamer, PortEventVariant}; +use type_c_interface::port::event::{PortNotificationSingle, PortStatusChanged}; +use type_c_interface::port::{Cached, PortStatus}; +use type_c_interface::service::event; pub mod config; -mod controller; pub mod pd; mod port; mod power; @@ -47,9 +42,7 @@ where PSU::Inner: psu::Psu, { /// Type-C context - context: &'a type_c::controller::Context, - /// Controller intrusive list - controllers: &'a intrusive_list::IntrusiveList, + pub(crate) context: &'a type_c_interface::service::context::Context, /// Current state state: Mutex, /// Config @@ -72,6 +65,7 @@ where // This is present instead of just using [`power_policy::CommsMessage`] to allow for // supporting variants like `ConsumerConnected(GlobalPortId, ConsumerPowerCapability)` // But there's currently not a way to do look-ups between power policy device IDs and GlobalPortIds +#[derive(Copy, Clone)] pub enum PowerPolicyEvent { /// Unconstrained state changed Unconstrained(power_policy_interface::service::UnconstrainedState), @@ -82,13 +76,12 @@ pub enum PowerPolicyEvent { } /// Type-C service events -pub enum Event<'a> { +#[derive(Copy, Clone)] +pub enum Event { /// Port event PortStatusChanged(GlobalPortId, PortStatusChanged, PortStatus), /// A controller notified of an event that occurred. PortNotification(GlobalPortId, PortNotificationSingle), - /// External command - ExternalCommand(deferred::Request<'a, GlobalRawMutex, external::Command, external::Response<'static>>), /// Power policy event PowerPolicy(PowerPolicyEvent), } @@ -100,8 +93,7 @@ where /// Create a new service the given configuration pub fn create( config: config::Config, - context: &'a crate::type_c::controller::Context, - controller_list: &'a intrusive_list::IntrusiveList, + context: &'a type_c_interface::service::context::Context, power_policy_publisher: DynImmediatePublisher<'a, power_policy_interface::service::event::Event<'a, PSU>>, power_policy_subscriber: DynSubscriber<'a, power_policy_interface::service::event::Event<'a, PSU>>, ) -> Self { @@ -111,7 +103,6 @@ where config, _power_policy_event_publisher: power_policy_publisher.into(), power_policy_event_subscriber: Mutex::new(power_policy_subscriber), - controllers: controller_list, } } @@ -154,7 +145,7 @@ where } self.context - .broadcast_message(comms::CommsMessage::DebugAccessory(comms::DebugAccessoryMessage { + .broadcast_message(event::Event::DebugAccessory(event::DebugAccessory { port: port_id, connected: status.is_connected(), })) @@ -167,33 +158,13 @@ where Ok(()) } - /// Process external commands - async fn process_external_command(&self, command: &external::Command) -> external::Response<'static> { - match command { - external::Command::Controller(command) => self.process_external_controller_command(command).await, - external::Command::Port(command) => self.process_external_port_command(command, self.controllers).await, - external::Command::Ucsi(command) => { - external::Response::Ucsi(self.process_ucsi_command(self.controllers, command).await) - } - } - } - /// Wait for the next event - pub async fn wait_next(&self) -> Result, Error> { + pub async fn wait_next(&self) -> Result { loop { - match select3( - self.wait_port_flags(), - self.context.wait_external_command(), - self.wait_power_policy_event(), - ) - .await - { - Either3::First(mut stream) => { + match select(self.wait_port_flags(), self.wait_power_policy_event()).await { + Either::First(mut stream) => { if let Some((port_id, event)) = stream - .next(|port_id| { - self.context - .get_port_event(self.controllers, GlobalPortId(port_id as u8)) - }) + .next(|port_id| self.context.get_port_event(GlobalPortId(port_id as u8))) .await? { let port_id = GlobalPortId(port_id as u8); @@ -201,10 +172,7 @@ where match event { PortEventVariant::StatusChanged(status_event) => { // Return a port status changed event - let status = self - .context - .get_port_status(self.controllers, port_id, Cached(true)) - .await?; + let status = self.context.get_port_status(port_id, Cached(true)).await?; return Ok(Event::PortStatusChanged(port_id, status_event, status)); } PortEventVariant::Notification(notification) => { @@ -217,16 +185,13 @@ where self.state.lock().await.port_event_streaming_state = None; } } - Either3::Second(request) => { - return Ok(Event::ExternalCommand(request)); - } - Either3::Third(event) => return Ok(event), + Either::Second(event) => return Ok(event), } } } /// Process the given event - pub async fn process_event(&self, event: Event<'_>) -> Result<(), Error> { + pub async fn process_event(&self, event: Event) -> Result<(), Error> { match event { Event::PortStatusChanged(port, event_kind, status) => { trace!("Port{}: Processing port status changed", port.0); @@ -237,12 +202,6 @@ where info!("Port{}: Got port notification: {:?}", port.0, notification); Ok(()) } - Event::ExternalCommand(request) => { - trace!("Processing external command"); - let response = self.process_external_command(&request.command).await; - request.respond(response); - Ok(()) - } Event::PowerPolicy(event) => { trace!("Processing power policy event"); self.process_power_policy_event(&event).await @@ -255,8 +214,4 @@ where let event = self.wait_next().await?; self.process_event(event).await } - - pub(crate) fn controllers(&self) -> &'a intrusive_list::IntrusiveList { - self.controllers - } } diff --git a/type-c-service/src/service/pd.rs b/type-c-service/src/service/pd.rs index 2070dbbc7..b0d940ee4 100644 --- a/type-c-service/src/service/pd.rs +++ b/type-c-service/src/service/pd.rs @@ -1,6 +1,6 @@ //! Power Delivery (PD) related functionality. -use embedded_services::{intrusive_list, sync::Lockable}; +use embedded_services::sync::Lockable; use embedded_usb_pd::{GlobalPortId, PdError, ado::Ado}; use power_policy_interface::psu; @@ -13,11 +13,7 @@ where /// Get the oldest unhandled PD alert for the given port. /// /// Returns [`None`] if no alerts are pending. - pub async fn get_pd_alert( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - ) -> Result, PdError> { - self.context.get_pd_alert(controllers, port).await + pub async fn get_pd_alert(&self, port: GlobalPortId) -> Result, PdError> { + self.context.get_pd_alert(port).await } } diff --git a/type-c-service/src/service/port.rs b/type-c-service/src/service/port.rs index ccf1bdc4f..b1aa047e7 100644 --- a/type-c-service/src/service/port.rs +++ b/type-c-service/src/service/port.rs @@ -1,15 +1,6 @@ -use embedded_services::{debug, error}; -use embedded_usb_pd::GlobalPortId; - use super::*; use crate::PortEventStreamer; -use crate::type_c::controller::SendVdm; -use crate::type_c::{ - controller::{DpConfig, PdStateMachineConfig, TbtConfig, TypeCStateMachineState, UsbControlConfig}, - external, -}; - impl<'a, PSU: Lockable> Service<'a, PSU> where PSU::Inner: psu::Psu, @@ -26,301 +17,4 @@ where PortEventStreamer::new(self.context.get_unhandled_events().await.into_iter()) } } - - /// Process external port commands - pub(super) async fn process_external_port_command( - &self, - command: &external::PortCommand, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - debug!("Processing external port command: {:#?}", command); - match command.data { - external::PortCommandData::PortStatus(cached) => { - self.process_external_port_status(command.port, cached, controllers) - .await - } - external::PortCommandData::RetimerFwUpdateGetState => { - self.process_get_rt_fw_update_status(command.port, controllers).await - } - external::PortCommandData::RetimerFwUpdateSetState => { - self.process_set_rt_fw_update_state(command.port, controllers).await - } - external::PortCommandData::RetimerFwUpdateClearState => { - self.process_clear_rt_fw_update_state(command.port, controllers).await - } - external::PortCommandData::SetRetimerCompliance => { - self.process_set_rt_compliance(command.port, controllers).await - } - external::PortCommandData::ReconfigureRetimer => { - self.process_reconfigure_retimer(command.port, controllers).await - } - external::PortCommandData::SetMaxSinkVoltage { max_voltage_mv } => { - self.process_set_max_sink_voltage(command.port, max_voltage_mv, controllers) - .await - } - external::PortCommandData::ClearDeadBatteryFlag => { - self.process_clear_dead_battery_flag(command.port, controllers).await - } - external::PortCommandData::SendVdm(tx_vdm) => { - self.process_send_vdm(command.port, tx_vdm, controllers).await - } - external::PortCommandData::SetUsbControl(config) => { - self.process_set_usb_control(command.port, config, controllers).await - } - external::PortCommandData::GetDpStatus => self.process_get_dp_status(command.port, controllers).await, - external::PortCommandData::SetDpConfig(config) => { - self.process_set_dp_config(command.port, config, controllers).await - } - external::PortCommandData::ExecuteDrst => self.process_execute_drst(command.port, controllers).await, - external::PortCommandData::SetTbtConfig(config) => { - self.process_set_tbt_config(command.port, config, controllers).await - } - external::PortCommandData::SetPdStateMachineConfig(config) => { - self.process_set_pd_state_machine_config(command.port, config, controllers) - .await - } - external::PortCommandData::SetTypeCStateMachineConfig(state) => { - self.process_set_type_c_state_machine_config(command.port, state, controllers) - .await - } - } - } - - /// Process external port status command - pub(super) async fn process_external_port_status( - &self, - port_id: GlobalPortId, - cached: Cached, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self.context.get_port_status(controllers, port_id, cached).await; - if let Err(e) = status { - error!("Error getting port status: {:#?}", e); - } - external::Response::Port(status.map(external::PortResponseData::PortStatus)) - } - - /// Process get retimer fw update status commands - pub(super) async fn process_get_rt_fw_update_status( - &self, - port_id: GlobalPortId, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self.context.get_rt_fw_update_status(controllers, port_id).await; - if let Err(e) = status { - error!("Error getting retimer fw update status: {:#?}", e); - } - - external::Response::Port(status.map(external::PortResponseData::RetimerFwUpdateGetState)) - } - - /// Process set retimer fw update state commands - pub(super) async fn process_set_rt_fw_update_state( - &self, - port_id: GlobalPortId, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self.context.set_rt_fw_update_state(controllers, port_id).await; - if let Err(e) = status { - error!("Error setting retimer fw update state: {:#?}", e); - } - - external::Response::Port(status.map(|_| external::PortResponseData::Complete)) - } - - /// Process clear retimer fw update state commands - pub(super) async fn process_clear_rt_fw_update_state( - &self, - port_id: GlobalPortId, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self.context.clear_rt_fw_update_state(controllers, port_id).await; - if let Err(e) = status { - error!("Error clear retimer fw update state: {:#?}", e); - } - - external::Response::Port(status.map(|_| external::PortResponseData::Complete)) - } - - /// Process set retimer compliance - pub(super) async fn process_set_rt_compliance( - &self, - port_id: GlobalPortId, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self.context.set_rt_compliance(controllers, port_id).await; - if let Err(e) = status { - error!("Error set retimer compliance: {:#?}", e); - } - - external::Response::Port(status.map(|_| external::PortResponseData::Complete)) - } - - async fn process_reconfigure_retimer( - &self, - port_id: GlobalPortId, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self.context.reconfigure_retimer(controllers, port_id).await; - if let Err(e) = status { - error!("Error reconfiguring retimer: {:#?}", e); - } - - external::Response::Port(status.map(|_| external::PortResponseData::Complete)) - } - - async fn process_set_max_sink_voltage( - &self, - port_id: GlobalPortId, - max_voltage_mv: Option, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self - .context - .set_max_sink_voltage(controllers, port_id, max_voltage_mv) - .await; - if let Err(e) = status { - error!("Error setting max voltage: {:#?}", e); - } - - external::Response::Port(status.map(|_| external::PortResponseData::Complete)) - } - - async fn process_clear_dead_battery_flag( - &self, - port_id: GlobalPortId, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self.context.clear_dead_battery_flag(controllers, port_id).await; - if let Err(e) = status { - error!("Error clearing dead battery flag: {:#?}", e); - } - - external::Response::Port(status.map(|_| external::PortResponseData::Complete)) - } - - /// Process send vdm commands - /// Process send vdm commands - async fn process_send_vdm( - &self, - port_id: GlobalPortId, - tx_vdm: SendVdm, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self.context.send_vdm(controllers, port_id, tx_vdm).await; - if let Err(e) = status { - error!("Error sending VDM data: {:#?}", e); - } - - external::Response::Port(status.map(|_| external::PortResponseData::Complete)) - } - - /// Process set USB control commands - async fn process_set_usb_control( - &self, - port_id: GlobalPortId, - config: UsbControlConfig, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self.context.set_usb_control(controllers, port_id, config).await; - if let Err(e) = status { - error!("Error setting USB control: {:#?}", e); - } - - external::Response::Port(status.map(|_| external::PortResponseData::Complete)) - } - - /// Process get DisplayPort status commands - async fn process_get_dp_status( - &self, - port_id: GlobalPortId, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self.context.get_dp_status(controllers, port_id).await; - if let Err(e) = status { - error!("Error getting DP status: {:#?}", e); - } - - external::Response::Port(status.map(external::PortResponseData::GetDpStatus)) - } - - /// Process set DisplayPort config commands - async fn process_set_dp_config( - &self, - port_id: GlobalPortId, - config: DpConfig, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self.context.set_dp_config(controllers, port_id, config).await; - if let Err(e) = status { - error!("Error setting DP config: {:#?}", e); - } - - external::Response::Port(status.map(|_| external::PortResponseData::Complete)) - } - - /// Process execute DisplayPort reset commands - async fn process_execute_drst( - &self, - port_id: GlobalPortId, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self.context.execute_drst(controllers, port_id).await; - if let Err(e) = status { - error!("Error executing DP reset: {:#?}", e); - } - - external::Response::Port(status.map(|_| external::PortResponseData::Complete)) - } - - /// Process set Thunderbolt configuration command - async fn process_set_tbt_config( - &self, - port_id: GlobalPortId, - config: TbtConfig, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self.context.set_tbt_config(controllers, port_id, config).await; - if let Err(e) = status { - error!("Error setting TBT config: {:#?}", e); - } - - external::Response::Port(status.map(|_| external::PortResponseData::Complete)) - } - - /// Process set PD state-machine configuration command - async fn process_set_pd_state_machine_config( - &self, - port_id: GlobalPortId, - config: PdStateMachineConfig, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self - .context - .set_pd_state_machine_config(controllers, port_id, config) - .await; - if let Err(e) = status { - error!("Error setting PD state-machine config: {:#?}", e); - } - - external::Response::Port(status.map(|_| external::PortResponseData::Complete)) - } - - /// Process set Type-C state-machine configuration command - async fn process_set_type_c_state_machine_config( - &self, - port_id: GlobalPortId, - state: TypeCStateMachineState, - controllers: &intrusive_list::IntrusiveList, - ) -> external::Response<'static> { - let status = self - .context - .set_type_c_state_machine_config(controllers, port_id, state) - .await; - if let Err(e) = status { - error!("Error setting Type-C state-machine config: {:#?}", e); - } - - external::Response::Port(status.map(|_| external::PortResponseData::Complete)) - } } diff --git a/type-c-service/src/service/power.rs b/type-c-service/src/service/power.rs index ba16485e9..68e0ec5c3 100644 --- a/type-c-service/src/service/power.rs +++ b/type-c-service/src/service/power.rs @@ -8,7 +8,7 @@ where PSU::Inner: psu::Psu, { /// Wait for a power policy event - pub(super) async fn wait_power_policy_event(&self) -> Event<'_> { + pub(super) async fn wait_power_policy_event(&self) -> Event { loop { match self.power_policy_event_subscriber.lock().await.next_message().await { WaitResult::Lagged(lagged) => { @@ -35,9 +35,9 @@ where /// Set the unconstrained state for all ports pub(super) async fn set_unconstrained_all(&self, unconstrained: bool) -> Result<(), Error> { - for port_index in 0..self.context.get_num_ports(self.controllers) { + for port_index in 0..self.context.get_num_ports() { self.context - .set_unconstrained_power(self.controllers, GlobalPortId(port_index as u8), unconstrained) + .set_unconstrained_power(GlobalPortId(port_index as u8), unconstrained) .await?; } Ok(()) @@ -60,7 +60,7 @@ where self.set_unconstrained_all(true).await?; } else { // Only one unconstrained device is present, see if that's one of our ports - let num_ports = self.context.get_num_ports(self.controllers); + let num_ports = self.context.get_num_ports(); let unconstrained_port = state .port_status .iter() @@ -77,11 +77,7 @@ where ); for port_index in 0..num_ports { self.context - .set_unconstrained_power( - self.controllers, - GlobalPortId(port_index as u8), - port_index != unconstrained_index, - ) + .set_unconstrained_power(GlobalPortId(port_index as u8), port_index != unconstrained_index) .await?; } } else { diff --git a/type-c-service/src/service/ucsi.rs b/type-c-service/src/service/ucsi.rs index c07c90ce3..ed3d2108e 100644 --- a/type-c-service/src/service/ucsi.rs +++ b/type-c-service/src/service/ucsi.rs @@ -7,9 +7,22 @@ use embedded_usb_pd::ucsi::ppm::state_machine::{ }; use embedded_usb_pd::ucsi::{GlobalCommand, ResponseData, lpm, ppm}; use embedded_usb_pd::{PdError, PowerRole}; +use type_c_interface::service::event::{Event, UsciChangeIndicator}; use super::*; +/// UCSI command response +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct UcsiResponse { + /// Notify the OPM, the function call + pub notify_opm: bool, + /// Response CCI + pub cci: GlobalCci, + /// UCSI response data + pub data: Result, PdError>, +} + /// UCSI state #[derive(Default)] pub(super) struct State { @@ -47,10 +60,10 @@ where } /// PPM get capabilities implementation - fn process_get_capabilities(&self, controllers: &intrusive_list::IntrusiveList) -> ppm::ResponseData { + fn process_get_capabilities(&self) -> ppm::ResponseData { debug!("Get PPM capabilities: {:?}", self.config.ucsi_capabilities); let mut capabilities = self.config.ucsi_capabilities; - capabilities.num_connectors = external::get_num_ports(controllers) as u8; + capabilities.num_connectors = self.context.get_num_ports() as u8; ppm::ResponseData::GetCapability(capabilities) } @@ -58,14 +71,13 @@ where &self, state: &mut State, command: &ucsi::ppm::Command, - controllers: &intrusive_list::IntrusiveList, ) -> Result, PdError> { match command { ppm::Command::SetNotificationEnable(enable) => { self.process_set_notification_enable(state, enable.notification_enable); Ok(None) } - ppm::Command::GetCapability => Ok(Some(self.process_get_capabilities(controllers))), + ppm::Command::GetCapability => Ok(Some(self.process_get_capabilities())), _ => Ok(None), // Other commands are currently no-ops } } @@ -101,7 +113,6 @@ where &self, state: &mut super::State, command: &ucsi::lpm::GlobalCommand, - controllers: &intrusive_list::IntrusiveList, ) -> Result, PdError> { debug!("Processing LPM command: {:?}", command); match command.operation() { @@ -110,11 +121,11 @@ where if let Some(capabilities) = &self.config.ucsi_port_capabilities { Ok(Some(lpm::ResponseData::GetConnectorCapability(*capabilities))) } else { - self.context.execute_ucsi_command(controllers, *command).await + self.context.execute_ucsi_command(*command).await } } lpm::CommandData::GetConnectorStatus => { - let mut response = self.context.execute_ucsi_command(controllers, *command).await; + let mut response = self.context.execute_ucsi_command(*command).await; if let Ok(Some(lpm::ResponseData::GetConnectorStatus(lpm::get_connector_status::ResponseData { status_change: ref mut states_change, status: @@ -134,7 +145,7 @@ where response } - _ => self.context.execute_ucsi_command(controllers, *command).await, + _ => self.context.execute_ucsi_command(*command).await, } } @@ -156,7 +167,7 @@ where if let Some(next_port) = state.pending_ports.front() { debug!("ACK_CCI processed, next pending port: {:?}", next_port); self.context - .broadcast_message(comms::CommsMessage::UcsiCci(comms::UsciChangeIndicator { + .broadcast_message(Event::UcsiCci(UsciChangeIndicator { port: *next_port, // False here because the OPM gets notified by the CCI, don't need a separate notification notify_opm: false, @@ -172,15 +183,11 @@ where self.set_cci_connector_change(state, cci); } - /// Process an external UCSI command - pub(super) async fn process_ucsi_command( - &self, - controllers: &intrusive_list::IntrusiveList, - command: &GlobalCommand, - ) -> external::UcsiResponse { + /// Process a UCSI command + pub async fn process_ucsi_command(&self, command: &GlobalCommand) -> UcsiResponse { let state = &mut self.state.lock().await; let mut next_input = Some(PpmInput::Command(command)); - let mut response: external::UcsiResponse = external::UcsiResponse { + let mut response = UcsiResponse { notify_opm: false, cci: Cci::default(), data: Ok(None), @@ -194,7 +201,7 @@ where state.ucsi.ppm_state_machine.consume(next_input) } else { error!("Unexpected end of state machine processing"); - return external::UcsiResponse { + return UcsiResponse { notify_opm: true, cci: Cci::new_error(), data: Err(PdError::InvalidMode), @@ -205,7 +212,7 @@ where Ok(output) => output, Err(e @ InvalidTransition { .. }) => { error!("PPM state machine transition failed: {:#?}", e); - return external::UcsiResponse { + return UcsiResponse { notify_opm: true, cci: Cci::new_error(), data: Err(PdError::Failed), @@ -221,12 +228,12 @@ where match command { ucsi::GlobalCommand::PpmCommand(ppm_command) => { response.data = self - .process_ppm_command(&mut state.ucsi, ppm_command, controllers) + .process_ppm_command(&mut state.ucsi, ppm_command) .map(|inner| inner.map(ResponseData::Ppm)); } ucsi::GlobalCommand::LpmCommand(lpm_command) => { response.data = self - .process_lpm_command(state, lpm_command, controllers) + .process_lpm_command(state, lpm_command) .await .map(|inner| inner.map(ResponseData::Lpm)); } @@ -352,7 +359,7 @@ where let notify_opm = state.pending_ports.is_empty(); if state.pending_ports.push_back(port_id).is_ok() { self.context - .broadcast_message(comms::CommsMessage::UcsiCci(comms::UsciChangeIndicator { + .broadcast_message(Event::UcsiCci(UsciChangeIndicator { port: port_id, notify_opm, })) diff --git a/type-c-service/src/service/vdm.rs b/type-c-service/src/service/vdm.rs index ca4770e52..ed2cfc157 100644 --- a/type-c-service/src/service/vdm.rs +++ b/type-c-service/src/service/vdm.rs @@ -1,9 +1,9 @@ //! VDM (Vendor Defined Messages) related functionality. -use crate::type_c::controller::{AttnVdm, OtherVdm}; -use embedded_services::{intrusive_list, sync::Lockable}; +use embedded_services::sync::Lockable; use embedded_usb_pd::{GlobalPortId, PdError}; use power_policy_interface::psu; +use type_c_interface::port::{AttnVdm, OtherVdm}; use super::Service; @@ -12,20 +12,12 @@ where PSU::Inner: psu::Psu, { /// Get the other vdm for the given port - pub async fn get_other_vdm( - &self, - controllers: &intrusive_list::IntrusiveList, - port_id: GlobalPortId, - ) -> Result { - self.context.get_other_vdm(controllers, port_id).await + pub async fn get_other_vdm(&self, port_id: GlobalPortId) -> Result { + self.context.get_other_vdm(port_id).await } /// Get the attention vdm for the given port - pub async fn get_attn_vdm( - &self, - controllers: &intrusive_list::IntrusiveList, - port_id: GlobalPortId, - ) -> Result { - self.context.get_attn_vdm(controllers, port_id).await + pub async fn get_attn_vdm(&self, port_id: GlobalPortId) -> Result { + self.context.get_attn_vdm(port_id).await } } diff --git a/type-c-service/src/task.rs b/type-c-service/src/task.rs index afd52d886..cdc999e26 100644 --- a/type-c-service/src/task.rs +++ b/type-c-service/src/task.rs @@ -27,7 +27,7 @@ pub async fn task_closure< PSU::Inner: psu::Psu, S: event::Sender, V: crate::wrapper::FwOfferValidator, - D::Inner: crate::type_c::controller::Controller, + D::Inner: type_c_interface::port::Controller, { info!("Starting type-c task"); @@ -35,7 +35,7 @@ pub async fn task_closure< // See https://github.com/OpenDevicePartnership/embedded-services/issues/742 for controller_wrapper in wrappers { - if controller_wrapper.register(service.controllers(), cfu_client).is_err() { + if controller_wrapper.register(cfu_client).is_err() { error!("Failed to register a controller"); return; } @@ -57,7 +57,7 @@ pub async fn task<'a, M, D, PSU: Lockable, S, V, const N: usize>( PSU::Inner: psu::Psu, S: event::Sender, V: crate::wrapper::FwOfferValidator, - ::Inner: crate::type_c::controller::Controller, + ::Inner: type_c_interface::port::Controller, { task_closure(service, wrappers, cfu_client, |service: &Service<'_, PSU>| async { if let Err(e) = service.process_next_event().await { diff --git a/type-c-service/src/type_c/controller.rs b/type-c-service/src/type_c/controller.rs deleted file mode 100644 index 6f20fd8fc..000000000 --- a/type-c-service/src/type_c/controller.rs +++ /dev/null @@ -1,1380 +0,0 @@ -//! PD controller related code -use core::future::Future; - -use embassy_sync::signal::Signal; -use embassy_time::{Duration, with_timeout}; -use embedded_usb_pd::ucsi::{self, lpm}; -use embedded_usb_pd::{ - DataRole, Error, GlobalPortId, LocalPortId, PdError, PlugOrientation, PowerRole, - ado::Ado, - pdinfo::{AltMode, PowerPathStatus}, - type_c::ConnectionState, -}; - -use super::{ATTN_VDM_LEN, ControllerId, OTHER_VDM_LEN, external}; -use crate::type_c::Cached; -use crate::type_c::comms::CommsMessage; -use crate::type_c::event::{PortEvent, PortPending}; -use embedded_services::ipc::deferred; -use embedded_services::{ - GlobalRawMutex, IntrusiveNode, broadcaster::immediate as broadcaster, error, intrusive_list, trace, -}; - -/// maximum number of data objects in a VDM -pub const MAX_NUM_DATA_OBJECTS: usize = 7; // 7 VDOs of 4 bytes each - -/// Port status -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PortStatus { - /// Current available source contract - pub available_source_contract: Option, - /// Current available sink contract - pub available_sink_contract: Option, - /// Current connection state - pub connection_state: Option, - /// Port partner supports dual-power roles - pub dual_power: bool, - /// plug orientation - pub plug_orientation: PlugOrientation, - /// power role - pub power_role: PowerRole, - /// data role - pub data_role: DataRole, - /// Active alt-modes - 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 { - /// Create a new blank port status - /// Needed because default() is not const - pub const fn new() -> Self { - Self { - 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, - } - } - - /// Check if the port is connected - pub fn is_connected(&self) -> bool { - matches!( - self.connection_state, - Some(ConnectionState::Attached) - | Some(ConnectionState::DebugAccessory) - | Some(ConnectionState::AudioAccessory) - ) - } - - /// Check if a debug accessory is connected - pub fn is_debug_accessory(&self) -> bool { - matches!(self.connection_state, Some(ConnectionState::DebugAccessory)) - } -} - -impl Default for PortStatus { - fn default() -> Self { - Self::new() - } -} - -/// Other Vdm data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct OtherVdm { - /// Other VDM data - pub data: [u8; OTHER_VDM_LEN], -} - -impl Default for OtherVdm { - fn default() -> Self { - Self { - data: [0; OTHER_VDM_LEN], - } - } -} - -impl From for [u8; OTHER_VDM_LEN] { - fn from(vdm: OtherVdm) -> Self { - vdm.data - } -} - -impl From<[u8; OTHER_VDM_LEN]> for OtherVdm { - fn from(data: [u8; OTHER_VDM_LEN]) -> Self { - Self { data } - } -} - -/// Attention Vdm data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct AttnVdm { - /// Attention VDM data - pub data: [u8; ATTN_VDM_LEN], -} - -/// DisplayPort pin configuration -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DpPinConfig { - /// 4L DP connection using USBC-USBC cable (Pin Assignment C) - pub pin_c: bool, - /// 2L USB + 2L DP connection using USBC-USBC cable (Pin Assignment D) - pub pin_d: bool, - /// 4L DP connection using USBC-DP cable (Pin Assignment E) - pub pin_e: bool, -} - -/// DisplayPort status data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DpStatus { - /// DP alt-mode entered - pub alt_mode_entered: bool, - /// Get DP DFP pin config - pub dfp_d_pin_cfg: DpPinConfig, -} - -/// DisplayPort configuration data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DpConfig { - /// DP alt-mode enabled - pub enable: bool, - /// Set DP DFP pin config - pub dfp_d_pin_cfg: DpPinConfig, -} - -impl Default for AttnVdm { - fn default() -> Self { - Self { - data: [0; ATTN_VDM_LEN], - } - } -} - -impl From for [u8; ATTN_VDM_LEN] { - fn from(vdm: AttnVdm) -> Self { - vdm.data - } -} - -impl From<[u8; ATTN_VDM_LEN]> for AttnVdm { - fn from(data: [u8; ATTN_VDM_LEN]) -> Self { - Self { data } - } -} - -/// Send VDM data -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SendVdm { - /// initiating a VDM sequence - pub initiator: bool, - /// VDO count - pub vdo_count: u8, - /// VDO data - pub vdo_data: [u32; MAX_NUM_DATA_OBJECTS], -} - -impl SendVdm { - /// Create a new blank port status - pub const fn new() -> Self { - Self { - initiator: false, - vdo_count: 0, - vdo_data: [0; MAX_NUM_DATA_OBJECTS], - } - } -} - -impl Default for SendVdm { - fn default() -> Self { - Self::new() - } -} - -/// USB control configuration -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct UsbControlConfig { - /// Enable USB2 data path - pub usb2_enabled: bool, - /// Enable USB3 data path - pub usb3_enabled: bool, - /// Enable USB4 data path - pub usb4_enabled: bool, -} - -impl Default for UsbControlConfig { - fn default() -> Self { - Self { - usb2_enabled: true, - usb3_enabled: true, - usb4_enabled: true, - } - } -} - -/// Thunderbolt control configuration -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Debug, Clone, Default, Copy, PartialEq)] -pub struct TbtConfig { - /// Enable Thunderbolt - pub tbt_enabled: bool, -} - -/// PD state-machine configuration -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Debug, Clone, Default, Copy, PartialEq)] -pub struct PdStateMachineConfig { - /// Enable or disable the PD state-machine - pub enabled: bool, -} - -/// TypeC State Machine -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum TypeCStateMachineState { - /// Sink state machine only - Sink, - /// Source state machine only - Source, - /// DRP state machine - Drp, - /// Disabled - Disabled, -} - -/// Port-specific command data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PortCommandData { - /// Get port status - PortStatus(Cached), - /// Get and clear events - ClearEvents, - /// Get retimer fw update state - RetimerFwUpdateGetState, - /// Set retimer fw update state - RetimerFwUpdateSetState, - /// Clear retimer fw update state - RetimerFwUpdateClearState, - /// Set retimer compliance - SetRetimerCompliance, - /// Reconfigure retimer - ReconfigureRetimer, - /// Get oldest unhandled PD alert - GetPdAlert, - /// Set the maximum sink voltage in mV for the given port - SetMaxSinkVoltage(Option), - /// Set unconstrained power - SetUnconstrainedPower(bool), - /// Clear the dead battery flag for the given port - ClearDeadBatteryFlag, - /// Get other VDM - GetOtherVdm, - /// Get attention VDM - GetAttnVdm, - /// Send VDM - SendVdm(SendVdm), - /// Set USB control configuration - SetUsbControl(UsbControlConfig), - /// Get DisplayPort status - GetDpStatus, - /// Set DisplayPort configuration - SetDpConfig(DpConfig), - /// Execute DisplayPort reset - ExecuteDrst, - /// Set Thunderbolt configuration - SetTbtConfig(TbtConfig), - /// Set PD state-machine configuration - SetPdStateMachineConfig(PdStateMachineConfig), - /// Set Type-C state-machine configuration - SetTypeCStateMachineConfig(TypeCStateMachineState), - /// Execute the UCSI command - ExecuteUcsiCommand(lpm::CommandData), -} - -/// Port-specific commands -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PortCommand { - /// Port ID - pub port: GlobalPortId, - /// Command data - pub data: PortCommandData, -} - -/// PD controller command-specific data -#[derive(Copy, Clone, Debug, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum RetimerFwUpdateState { - /// Retimer FW Update Inactive - Inactive, - /// Revimer FW Update Active - Active, -} - -/// Port-specific response data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PortResponseData { - /// Command completed with no error - Complete, - /// Port status - PortStatus(PortStatus), - /// ClearEvents - ClearEvents(PortEvent), - /// Retimer Fw Update status - RtFwUpdateStatus(RetimerFwUpdateState), - /// PD alert - PdAlert(Option), - /// Get other VDM - OtherVdm(OtherVdm), - /// Get attention VDM - AttnVdm(AttnVdm), - /// Get DisplayPort status - DpStatus(DpStatus), - /// UCSI response - UcsiResponse(Result, PdError>), -} - -impl PortResponseData { - /// Helper function to convert to a result - pub fn complete_or_err(self) -> Result<(), PdError> { - match self { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } -} - -/// Port-specific command response -pub type PortResponse = Result; - -/// PD controller command-specific data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InternalCommandData { - /// Reset the PD controller - Reset, - /// Get controller status - Status, - /// Sync controller state - SyncState, -} - -/// PD controller command -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Command { - /// Controller specific command - Controller(InternalCommandData), - /// Port command - Port(PortCommand), - /// UCSI command passthrough - Lpm(lpm::GlobalCommand), -} - -/// Controller-specific response data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InternalResponseData<'a> { - /// Command complete - Complete, - /// Controller status - Status(ControllerStatus<'a>), -} - -/// Response for controller-specific commands -pub type InternalResponse<'a> = Result, PdError>; - -/// PD controller command response -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Response<'a> { - /// Controller response - Controller(InternalResponse<'a>), - /// UCSI response passthrough - Ucsi(ucsi::GlobalResponse), - /// Port response - Port(PortResponse), -} - -/// Controller status -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct ControllerStatus<'a> { - /// Current controller mode - pub mode: &'a str, - /// True if we did not have to boot from a backup FW bank - pub valid_fw_bank: bool, - /// FW version 0 - pub fw_version0: u32, - /// FW version 1 - pub fw_version1: u32, -} - -/// PD controller -pub struct Device<'a> { - node: intrusive_list::Node, - id: ControllerId, - ports: &'a [GlobalPortId], - num_ports: usize, - command: deferred::Channel>, -} - -impl intrusive_list::NodeContainer for Device<'static> { - fn get_node(&self) -> &intrusive_list::Node { - &self.node - } -} - -impl<'a> Device<'a> { - /// Create a new PD controller struct - pub fn new(id: ControllerId, ports: &'a [GlobalPortId]) -> Self { - Self { - node: intrusive_list::Node::uninit(), - id, - ports, - num_ports: ports.len(), - command: deferred::Channel::new(), - } - } - - /// Get the controller ID - pub fn id(&self) -> ControllerId { - self.id - } - - /// Send a command to this controller - pub async fn execute_command(&self, command: Command) -> Response<'_> { - self.command.execute(command).await - } - - /// Check if this controller has the given port - pub fn has_port(&self, port: GlobalPortId) -> bool { - self.lookup_local_port(port).is_ok() - } - - /// Covert a local port ID to a global port ID - pub fn lookup_global_port(&self, port: LocalPortId) -> Result { - Ok(*self.ports.get(port.0 as usize).ok_or(PdError::InvalidParams)?) - } - - /// Convert a global port ID to a local port ID - pub fn lookup_local_port(&self, port: GlobalPortId) -> Result { - self.ports - .iter() - .position(|p| *p == port) - .map(|p| LocalPortId(p as u8)) - .ok_or(PdError::InvalidParams) - } - - /// Create a command handler for this controller - /// - /// DROP SAFETY: Direct call to deferred channel primitive - pub async fn receive(&self) -> deferred::Request<'_, GlobalRawMutex, Command, Response<'static>> { - self.command.receive().await - } - - /// Notify that there are pending events on one or more ports - pub fn notify_ports(&self, ctx: &Context, pending: PortPending) { - ctx.notify_ports(pending); - } - - /// Number of ports on this controller - pub fn num_ports(&self) -> usize { - self.num_ports - } - - /// Slice of global ports on the Device - pub fn ports(&self) -> &'a [GlobalPortId] { - self.ports - } -} - -/// Trait for types that contain a controller struct -pub trait DeviceContainer { - /// Get the controller struct - fn get_pd_controller_device(&self) -> &Device<'_>; -} - -impl DeviceContainer for Device<'_> { - fn get_pd_controller_device(&self) -> &Device<'_> { - self - } -} - -/// PD controller trait that device drivers may use to integrate with internal messaging system -pub trait Controller { - /// Type of error returned by the bus - type BusError; - - /// Wait for a port event to occur - /// # Implementation guide - /// This function should be drop safe. - /// Any intermediate side effects must be undone if the returned [`Future`] is dropped before completing. - fn wait_port_event(&mut self) -> impl Future>>; - /// Returns and clears current events for the given port - /// # Implementation guide - /// This function should be drop safe. - /// Any intermediate side effects must be undone if the returned [`Future`] is dropped before completing. - fn clear_port_events( - &mut self, - port: LocalPortId, - ) -> impl Future>>; - /// Returns the port status - fn get_port_status(&mut self, port: LocalPortId) - -> impl Future>>; - - /// Reset the controller - fn reset_controller(&mut self) -> impl Future>>; - - /// Returns the retimer fw update state - fn get_rt_fw_update_status( - &mut self, - port: LocalPortId, - ) -> impl Future>>; - /// Set retimer fw update state - fn set_rt_fw_update_state(&mut self, port: LocalPortId) -> impl Future>>; - /// Clear retimer fw update state - fn clear_rt_fw_update_state( - &mut self, - port: LocalPortId, - ) -> impl Future>>; - /// Set retimer compliance - fn set_rt_compliance(&mut self, port: LocalPortId) -> impl Future>>; - - /// Reconfigure the retimer for the given port. - fn reconfigure_retimer(&mut self, port: LocalPortId) -> impl Future>>; - - /// Clear the dead battery flag for the given port. - fn clear_dead_battery_flag(&mut self, port: LocalPortId) - -> impl Future>>; - - /// Enable or disable sink path - fn enable_sink_path( - &mut self, - port: LocalPortId, - enable: bool, - ) -> impl Future>>; - /// Get current controller status - fn get_controller_status( - &mut self, - ) -> impl Future, Error>>; - /// Get current PD alert - fn get_pd_alert(&mut self, port: LocalPortId) -> impl Future, Error>>; - /// Set the maximum sink voltage for the given port - /// - /// This may trigger a renegotiation - fn set_max_sink_voltage( - &mut self, - port: LocalPortId, - voltage_mv: Option, - ) -> impl Future>>; - /// Set port unconstrained status - fn set_unconstrained_power( - &mut self, - port: LocalPortId, - unconstrained: bool, - ) -> impl Future>>; - - // TODO: remove all these once we migrate to a generic FW update trait - // https://github.com/OpenDevicePartnership/embedded-services/issues/242 - /// Get current FW version - fn get_active_fw_version(&mut self) -> impl Future>>; - /// Start a firmware update - fn start_fw_update(&mut self) -> impl Future>>; - /// Abort a firmware update - fn abort_fw_update(&mut self) -> impl Future>>; - /// Finalize a firmware update - fn finalize_fw_update(&mut self) -> impl Future>>; - /// Write firmware update contents - fn write_fw_contents( - &mut self, - offset: usize, - data: &[u8], - ) -> impl Future>>; - /// Get the Rx Other VDM data for the given port - fn get_other_vdm(&mut self, port: LocalPortId) -> impl Future>>; - /// Get the Rx Attention VDM data for the given port - fn get_attn_vdm(&mut self, port: LocalPortId) -> impl Future>>; - /// Send a VDM to the given port - fn send_vdm( - &mut self, - port: LocalPortId, - tx_vdm: SendVdm, - ) -> impl Future>>; - - /// Set USB control configuration for the given port - fn set_usb_control( - &mut self, - port: LocalPortId, - config: UsbControlConfig, - ) -> impl Future>>; - - /// Get DisplayPort status for the given port - fn get_dp_status(&mut self, port: LocalPortId) -> impl Future>>; - /// Set DisplayPort configuration for the given port - fn set_dp_config( - &mut self, - port: LocalPortId, - config: DpConfig, - ) -> impl Future>>; - /// Execute PD Data Reset for the given port - fn execute_drst(&mut self, port: LocalPortId) -> impl Future>>; - - /// Set Thunderbolt configuration for the given port - fn set_tbt_config( - &mut self, - port: LocalPortId, - config: TbtConfig, - ) -> impl Future>>; - - /// Set PD state-machine configuration for the given port - fn set_pd_state_machine_config( - &mut self, - port: LocalPortId, - config: PdStateMachineConfig, - ) -> impl Future>>; - - /// Set Type-C state-machine configuration for the given port - fn set_type_c_state_machine_config( - &mut self, - port: LocalPortId, - state: TypeCStateMachineState, - ) -> impl Future>>; - - /// Execute the given UCSI command - fn execute_ucsi_command( - &mut self, - command: lpm::LocalCommand, - ) -> impl Future, Error>>; -} - -/// Internal context for managing PD controllers -pub struct Context { - port_events: Signal, - /// Channel for receiving commands to the type-C service - external_command: deferred::Channel>, - /// Event broadcaster - broadcaster: broadcaster::Immediate, -} - -impl Default for Context { - fn default() -> Self { - Self::new() - } -} - -impl Context { - /// Create new Context - pub const fn new() -> Self { - Self { - port_events: Signal::new(), - external_command: deferred::Channel::new(), - broadcaster: broadcaster::Immediate::new(), - } - } - - /// Notify that there are pending events on one or more ports - /// Each bit corresponds to a global port ID - pub fn notify_ports(&self, pending: PortPending) { - let raw_pending: u32 = pending.into(); - trace!("Notify ports: {:#x}", raw_pending); - // Early exit if no events - if pending.is_none() { - return; - } - - self.port_events - .signal(if let Some(flags) = self.port_events.try_take() { - flags.union(pending) - } else { - pending - }); - } - - /// Send a command to the given controller with no timeout - pub async fn send_controller_command_no_timeout( - &self, - controllers: &intrusive_list::IntrusiveList, - controller_id: ControllerId, - command: InternalCommandData, - ) -> Result, PdError> { - let node = controllers - .into_iter() - .find(|node| { - if let Some(controller) = node.data::() { - controller.id == controller_id - } else { - false - } - }) - .ok_or(PdError::InvalidController)?; - - match node - .data::() - .ok_or(PdError::InvalidController)? - .execute_command(Command::Controller(command)) - .await - { - Response::Controller(response) => response, - r => { - error!("Invalid response: expected controller, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Send a command to the given controller with a timeout - pub async fn send_controller_command( - &self, - controllers: &intrusive_list::IntrusiveList, - controller_id: ControllerId, - command: InternalCommandData, - ) -> Result, PdError> { - match with_timeout( - DEFAULT_TIMEOUT, - self.send_controller_command_no_timeout(controllers, controller_id, command), - ) - .await - { - Ok(response) => response, - Err(_) => Err(PdError::Timeout), - } - } - - /// Reset the given controller - pub async fn reset_controller( - &self, - controllers: &intrusive_list::IntrusiveList, - controller_id: ControllerId, - ) -> Result<(), PdError> { - self.send_controller_command(controllers, controller_id, InternalCommandData::Reset) - .await - .map(|_| ()) - } - - fn find_node_by_port( - &self, - controllers: &intrusive_list::IntrusiveList, - port_id: GlobalPortId, - ) -> Result<&IntrusiveNode, PdError> { - controllers - .into_iter() - .find(|node| { - if let Some(controller) = node.data::() { - controller.has_port(port_id) - } else { - false - } - }) - .ok_or(PdError::InvalidPort) - } - - /// Send a command to the given port - pub async fn send_port_command_ucsi_no_timeout( - &self, - controllers: &intrusive_list::IntrusiveList, - port_id: GlobalPortId, - command: lpm::CommandData, - ) -> Result { - let node = self.find_node_by_port(controllers, port_id)?; - - match node - .data::() - .ok_or(PdError::InvalidController)? - .execute_command(Command::Lpm(lpm::Command::new(port_id, command))) - .await - { - Response::Ucsi(response) => Ok(response), - r => { - error!("Invalid response: expected LPM, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Send a command to the given port with a timeout - pub async fn send_port_command_ucsi( - &self, - controllers: &intrusive_list::IntrusiveList, - port_id: GlobalPortId, - command: lpm::CommandData, - ) -> Result { - match with_timeout( - DEFAULT_TIMEOUT, - self.send_port_command_ucsi_no_timeout(controllers, port_id, command), - ) - .await - { - Ok(response) => response, - Err(_) => Err(PdError::Timeout), - } - } - - /// Send a command to the given port with no timeout - pub async fn send_port_command_no_timeout( - &self, - controllers: &intrusive_list::IntrusiveList, - port_id: GlobalPortId, - command: PortCommandData, - ) -> Result { - let node = self.find_node_by_port(controllers, port_id)?; - - match node - .data::() - .ok_or(PdError::InvalidController)? - .execute_command(Command::Port(PortCommand { - port: port_id, - data: command, - })) - .await - { - Response::Port(response) => response, - r => { - error!("Invalid response: expected port, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Send a command to the given port with a timeout - pub async fn send_port_command( - &self, - controllers: &intrusive_list::IntrusiveList, - port_id: GlobalPortId, - command: PortCommandData, - ) -> Result { - match with_timeout( - DEFAULT_TIMEOUT, - self.send_port_command_no_timeout(controllers, port_id, command), - ) - .await - { - Ok(response) => response, - Err(_) => Err(PdError::Timeout), - } - } - - /// Get the current port events - pub async fn get_unhandled_events(&self) -> PortPending { - self.port_events.wait().await - } - - /// Get the unhandled events for the given port - pub async fn get_port_event( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - ) -> Result { - match self - .send_port_command(controllers, port, PortCommandData::ClearEvents) - .await? - { - PortResponseData::ClearEvents(event) => Ok(event), - r => { - error!("Invalid response: expected clear events, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Get the current port status - pub async fn get_port_status( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - cached: Cached, - ) -> Result { - match self - .send_port_command(controllers, port, PortCommandData::PortStatus(cached)) - .await? - { - PortResponseData::PortStatus(status) => Ok(status), - r => { - error!("Invalid response: expected port status, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Get the oldest unhandled PD alert for the given port - pub async fn get_pd_alert( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - ) -> Result, PdError> { - match self - .send_port_command(controllers, port, PortCommandData::GetPdAlert) - .await? - { - PortResponseData::PdAlert(alert) => Ok(alert), - r => { - error!("Invalid response: expected PD alert, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Get the retimer fw update status - pub async fn get_rt_fw_update_status( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - ) -> Result { - match self - .send_port_command(controllers, port, PortCommandData::RetimerFwUpdateGetState) - .await? - { - PortResponseData::RtFwUpdateStatus(status) => Ok(status), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set the retimer fw update state - pub async fn set_rt_fw_update_state( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::RetimerFwUpdateSetState) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Clear the retimer fw update state - pub async fn clear_rt_fw_update_state( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::RetimerFwUpdateClearState) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set the retimer compliance - pub async fn set_rt_compliance( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::SetRetimerCompliance) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Reconfigure the retimer for the given port. - pub async fn reconfigure_retimer( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::ReconfigureRetimer) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set the maximum sink voltage for the given port. - /// - /// See [`PortCommandData::SetMaxSinkVoltage`] for details on the `max_voltage_mv` parameter. - pub async fn set_max_sink_voltage( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - max_voltage_mv: Option, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::SetMaxSinkVoltage(max_voltage_mv)) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Clear the dead battery flag for the given port. - pub async fn clear_dead_battery_flag( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::ClearDeadBatteryFlag) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Get current controller status - pub async fn get_controller_status( - &self, - controllers: &intrusive_list::IntrusiveList, - controller_id: ControllerId, - ) -> Result, PdError> { - match self - .send_controller_command(controllers, controller_id, InternalCommandData::Status) - .await? - { - InternalResponseData::Status(status) => Ok(status), - r => { - error!("Invalid response: expected controller status, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Set unconstrained power for the given port - pub async fn set_unconstrained_power( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - unconstrained: bool, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::SetUnconstrainedPower(unconstrained)) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Sync controller state - pub async fn sync_controller_state( - &self, - controllers: &intrusive_list::IntrusiveList, - controller_id: ControllerId, - ) -> Result<(), PdError> { - match self - .send_controller_command(controllers, controller_id, InternalCommandData::SyncState) - .await? - { - InternalResponseData::Complete => Ok(()), - r => { - error!("Invalid response: expected controller status, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Wait for an external command - pub async fn wait_external_command( - &self, - ) -> deferred::Request<'_, GlobalRawMutex, external::Command, external::Response<'static>> { - self.external_command.receive().await - } - - /// Get the number of ports on the system - pub fn get_num_ports(&self, controllers: &intrusive_list::IntrusiveList) -> usize { - get_num_ports(controllers) - } - - /// Get the other vdm for the given port - pub async fn get_other_vdm( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - ) -> Result { - match self - .send_port_command(controllers, port, PortCommandData::GetOtherVdm) - .await? - { - PortResponseData::OtherVdm(vdm) => Ok(vdm), - r => { - error!("Invalid response: expected other VDM, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Get the attention vdm for the given port - pub async fn get_attn_vdm( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - ) -> Result { - match self - .send_port_command(controllers, port, PortCommandData::GetAttnVdm) - .await? - { - PortResponseData::AttnVdm(vdm) => Ok(vdm), - r => { - error!("Invalid response: expected attention VDM, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Send VDM to the given port - pub async fn send_vdm( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - tx_vdm: SendVdm, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::SendVdm(tx_vdm)) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set USB control configuration for the given port - pub async fn set_usb_control( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - config: UsbControlConfig, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::SetUsbControl(config)) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Get DisplayPort status for the given port - pub async fn get_dp_status( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - ) -> Result { - match self - .send_port_command(controllers, port, PortCommandData::GetDpStatus) - .await? - { - PortResponseData::DpStatus(status) => Ok(status), - r => { - error!("Invalid response: expected DP status, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Set DisplayPort configuration for the given port - pub async fn set_dp_config( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - config: DpConfig, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::SetDpConfig(config)) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Execute PD Data Reset for the given port - pub async fn execute_drst( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::ExecuteDrst) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set Thunderbolt configuration for the given port - pub async fn set_tbt_config( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - config: TbtConfig, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::SetTbtConfig(config)) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set PD state-machine configuration for the given port - pub async fn set_pd_state_machine_config( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - config: PdStateMachineConfig, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::SetPdStateMachineConfig(config)) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set Type-C state-machine configuration for the given port - pub async fn set_type_c_state_machine_config( - &self, - controllers: &intrusive_list::IntrusiveList, - port: GlobalPortId, - state: TypeCStateMachineState, - ) -> Result<(), PdError> { - match self - .send_port_command(controllers, port, PortCommandData::SetTypeCStateMachineConfig(state)) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Execute the given UCSI command - pub async fn execute_ucsi_command( - &self, - controllers: &intrusive_list::IntrusiveList, - command: lpm::GlobalCommand, - ) -> Result, PdError> { - match self - .send_port_command( - controllers, - command.port(), - PortCommandData::ExecuteUcsiCommand(command.operation()), - ) - .await? - { - PortResponseData::UcsiResponse(response) => response, - _ => Err(PdError::InvalidResponse), - } - } - - /// Execute an external port command - pub(super) async fn execute_external_port_command( - &self, - command: external::Command, - ) -> Result { - match self.external_command.execute(command).await { - external::Response::Port(response) => response, - r => { - error!("Invalid response: expected external port, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Execute an external UCSI command - pub(super) async fn execute_external_ucsi_command(&self, command: ucsi::GlobalCommand) -> external::UcsiResponse { - match self.external_command.execute(external::Command::Ucsi(command)).await { - external::Response::Ucsi(response) => response, - r => { - error!("Invalid response: expected external UCSI, got {:?}", r); - external::UcsiResponse { - // Always notify OPM of an error - notify_opm: true, - cci: ucsi::cci::GlobalCci::new_error(), - data: Err(PdError::InvalidResponse), - } - } - } - } - - /// Execute an external controller command - pub(super) async fn execute_external_controller_command( - &self, - command: external::Command, - ) -> Result, PdError> { - match self.external_command.execute(command).await { - external::Response::Controller(response) => response, - r => { - error!("Invalid response: expected external controller, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Register a message receiver for type-C messages - pub async fn register_message_receiver( - &self, - receiver: &'static broadcaster::Receiver<'_, CommsMessage>, - ) -> intrusive_list::Result<()> { - self.broadcaster.register_receiver(receiver) - } - - /// Broadcast a type-C message to all subscribers - pub async fn broadcast_message(&self, message: CommsMessage) { - self.broadcaster.broadcast(message).await; - } -} - -/// Default command timeout -/// set to high value since this is intended to prevent an unresponsive device from blocking the service implementation -const DEFAULT_TIMEOUT: Duration = Duration::from_millis(5000); - -/// Register a PD controller -pub fn register_controller( - controllers: &intrusive_list::IntrusiveList, - controller: &'static impl DeviceContainer, -) -> Result<(), intrusive_list::Error> { - controllers.push(controller.get_pd_controller_device()) -} - -pub(super) fn lookup_controller( - controllers: &intrusive_list::IntrusiveList, - controller_id: ControllerId, -) -> Result<&'static Device<'static>, PdError> { - controllers - .into_iter() - .filter_map(|node| node.data::()) - .find(|controller| controller.id == controller_id) - .ok_or(PdError::InvalidController) -} - -/// Get total number of ports on the system -pub(super) fn get_num_ports(controllers: &intrusive_list::IntrusiveList) -> usize { - controllers - .iter_only::() - .fold(0, |acc, controller| acc + controller.num_ports()) -} diff --git a/type-c-service/src/type_c/external.rs b/type-c-service/src/type_c/external.rs deleted file mode 100644 index ef253db2a..000000000 --- a/type-c-service/src/type_c/external.rs +++ /dev/null @@ -1,495 +0,0 @@ -//! Message definitions for external type-C commands -use embedded_usb_pd::{GlobalPortId, LocalPortId, PdError, ucsi}; - -use embedded_services::intrusive_list; - -use super::{ - ControllerId, - controller::{ControllerStatus, DpConfig, DpStatus, PortStatus, RetimerFwUpdateState, SendVdm, lookup_controller}, -}; - -use crate::type_c::{ - Cached, - controller::{Context, PdStateMachineConfig, TbtConfig, TypeCStateMachineState, UsbControlConfig}, -}; - -/// Data for controller-specific commands -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum ControllerCommandData { - /// Get controller status - ControllerStatus, - /// Sync controller state - SyncState, - /// Controller reset - Reset, -} - -/// Controller-specific commands -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct ControllerCommand { - /// Controller ID - pub id: ControllerId, - /// Command data - pub data: ControllerCommandData, -} - -/// Response data for controller-specific commands -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum ControllerResponseData<'a> { - /// Command complete - Complete, - /// Get controller status - ControllerStatus(ControllerStatus<'a>), -} - -/// Controller-specific command response -pub type ControllerResponse<'a> = Result, PdError>; - -/// Data for port-specific commands -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PortCommandData { - /// Get port status. The `bool` argument indicates whether to use cached data or force a fetch of register values. - PortStatus(Cached), - /// Get retimer fw update status - RetimerFwUpdateGetState, - /// Set retimer fw update status - RetimerFwUpdateSetState, - /// Clear retimer fw update status - RetimerFwUpdateClearState, - /// Set retimer compliance - SetRetimerCompliance, - /// Reconfigure retimer - ReconfigureRetimer, - /// Set max sink voltage to a specific value. - SetMaxSinkVoltage { - /// The maximum voltage to set, in millivolts. - /// If [`None`], the port will be set to its default maximum voltage. - max_voltage_mv: Option, - }, - /// Clear the dead battery flag for the given port. - ClearDeadBatteryFlag, - /// Set USB control - SetUsbControl(UsbControlConfig), - /// Send VDM - SendVdm(SendVdm), - /// Get DisplayPort status - GetDpStatus, - /// Set DisplayPort configuration - SetDpConfig(DpConfig), - /// Execute DisplayPort reset - ExecuteDrst, - /// Set Thunderbolt configuration - SetTbtConfig(TbtConfig), - /// Set PD state-machine configuration - SetPdStateMachineConfig(PdStateMachineConfig), - /// Set Type-C state-machine configuration - SetTypeCStateMachineConfig(TypeCStateMachineState), -} - -/// Port-specific commands -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PortCommand { - /// Port ID - pub port: GlobalPortId, - /// Command data - pub data: PortCommandData, -} - -/// Response data for port-specific commands -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PortResponseData { - /// Command completed with no error - Complete, - /// Get port status - PortStatus(PortStatus), - /// Get retimer fw update status - RetimerFwUpdateGetState(RetimerFwUpdateState), - /// Get DisplayPort status - GetDpStatus(DpStatus), -} - -/// Port-specific command response -pub type PortResponse = Result; - -/// External commands for type-C service -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Command { - /// Port command - Port(PortCommand), - /// Controller command - Controller(ControllerCommand), - /// UCSI command - Ucsi(ucsi::GlobalCommand), -} - -/// UCSI command response -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct UcsiResponse { - /// Notify the OPM, the function call - pub notify_opm: bool, - /// Response CCI - pub cci: ucsi::cci::GlobalCci, - /// UCSI response data - pub data: Result, PdError>, -} - -/// Alias to help simplify conversion into a result -pub type UcsiResponseResult = Result; - -impl From for UcsiResponseResult { - fn from(value: UcsiResponse) -> Self { - match value.data { - Ok(data) => Ok(ucsi::GlobalResponse { cci: value.cci, data }), - Err(err) => Err(err), - } - } -} - -/// External command response for type-C service -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Response<'a> { - /// Port command response - Port(PortResponse), - /// Controller command response - Controller(ControllerResponse<'a>), - /// UCSI command response - Ucsi(UcsiResponse), -} - -impl Context { - /// Get the status of the given port. - /// - /// Use the `cached` argument to specify whether to use cached data or force a fetch of register values. - pub async fn get_port_status_external(&self, port: GlobalPortId, cached: Cached) -> Result { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::PortStatus(cached), - })) - .await? - { - PortResponseData::PortStatus(status) => Ok(status), - _ => Err(PdError::InvalidResponse), - } - } - - /// Get the status of the given port by its controller and local port ID. - /// - /// Use the `cached` argument to specify whether to use cached data or force a fetch of register values. - pub async fn get_controller_port_status_external( - &self, - controllers: &intrusive_list::IntrusiveList, - controller: ControllerId, - port: LocalPortId, - cached: Cached, - ) -> Result { - let global_port = controller_port_to_global_id(controllers, controller, port)?; - self.get_port_status_external(global_port, cached).await - } - - /// Reset the given controller. - pub async fn reset_controller_external(&self, controller_id: ControllerId) -> Result<(), PdError> { - match self - .execute_external_controller_command(Command::Controller(ControllerCommand { - id: controller_id, - data: ControllerCommandData::Reset, - })) - .await? - { - ControllerResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Get the status of the given controller - #[allow(unreachable_patterns)] - pub async fn get_controller_status_external(&self, id: ControllerId) -> Result, PdError> { - match self - .execute_external_controller_command(Command::Controller(ControllerCommand { - id, - data: ControllerCommandData::ControllerStatus, - })) - .await? - { - ControllerResponseData::ControllerStatus(status) => Ok(status), - _ => Err(PdError::InvalidResponse), - } - } - - /// Get the retimer fw update status of the given port - pub async fn port_get_rt_fw_update_status_external( - &self, - port: GlobalPortId, - ) -> Result { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::RetimerFwUpdateGetState, - })) - .await? - { - PortResponseData::RetimerFwUpdateGetState(status) => Ok(status), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set the retimer fw update state of the given port - pub async fn port_set_rt_fw_update_state_external(&self, port: GlobalPortId) -> Result<(), PdError> { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::RetimerFwUpdateSetState, - })) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Clear the retimer fw update state of the given port - pub async fn port_clear_rt_fw_update_state_external(&self, port: GlobalPortId) -> Result<(), PdError> { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::RetimerFwUpdateClearState, - })) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set the retimer comliance state of the given port - pub async fn port_set_rt_compliance_external(&self, port: GlobalPortId) -> Result<(), PdError> { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetRetimerCompliance, - })) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Trigger a sync of the controller state - pub async fn sync_controller_state_external(&self, id: ControllerId) -> Result<(), PdError> { - match self - .execute_external_controller_command(Command::Controller(ControllerCommand { - id, - data: ControllerCommandData::SyncState, - })) - .await? - { - ControllerResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set the maximum voltage for the given port to a specific value. - /// - /// See [`PortCommandData::SetMaxSinkVoltage::max_voltage_mv`] for details on the `max_voltage_mv` parameter. - pub async fn set_max_sink_voltage_external( - &self, - port: GlobalPortId, - max_voltage_mv: Option, - ) -> Result<(), PdError> { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetMaxSinkVoltage { max_voltage_mv }, - })) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Clear the dead battery flag for the given port. - pub async fn clear_dead_battery_flag_external(&self, port: GlobalPortId) -> Result<(), PdError> { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::ClearDeadBatteryFlag, - })) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Reconfigure the retimer for the given port. - pub async fn reconfigure_retimer_external(&self, port: GlobalPortId) -> Result<(), PdError> { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::ReconfigureRetimer, - })) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Execute a UCSI command - pub async fn execute_ucsi_command_external(&self, command: ucsi::GlobalCommand) -> UcsiResponse { - self.execute_external_ucsi_command(command).await - } - - /// Send vdm to the given port - pub async fn send_vdm_external(&self, port: GlobalPortId, tx_vdm: SendVdm) -> Result<(), PdError> { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SendVdm(tx_vdm), - })) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set USB control configuration - pub async fn set_usb_control_external(&self, port: GlobalPortId, config: UsbControlConfig) -> Result<(), PdError> { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetUsbControl(config), - })) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Get DisplayPort status for the given port - pub async fn get_dp_status_external(&self, port: GlobalPortId) -> Result { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::GetDpStatus, - })) - .await? - { - PortResponseData::GetDpStatus(status) => Ok(status), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set DisplayPort configuration for the given port - pub async fn set_dp_config_external(&self, port: GlobalPortId, config: DpConfig) -> Result<(), PdError> { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetDpConfig(config), - })) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Execute DisplayPort reset for the given port - pub async fn execute_drst_external(&self, port: GlobalPortId) -> Result<(), PdError> { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::ExecuteDrst, - })) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set Thunderbolt configuration for the given port - pub async fn set_tbt_config_external(&self, port: GlobalPortId, config: TbtConfig) -> Result<(), PdError> { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetTbtConfig(config), - })) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set PD state-machine configuration for the given port - pub async fn set_pd_state_machine_config_external( - &self, - port: GlobalPortId, - config: PdStateMachineConfig, - ) -> Result<(), PdError> { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetPdStateMachineConfig(config), - })) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } - - /// Set Type-C state-machine configuration for the given port - pub async fn set_type_c_state_machine_config_external( - &self, - port: GlobalPortId, - state: TypeCStateMachineState, - ) -> Result<(), PdError> { - match self - .execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetTypeCStateMachineConfig(state), - })) - .await? - { - PortResponseData::Complete => Ok(()), - _ => Err(PdError::InvalidResponse), - } - } -} - -/// Get the number of ports on the given controller -pub fn get_controller_num_ports( - controllers: &intrusive_list::IntrusiveList, - controller_id: ControllerId, -) -> Result { - Ok(lookup_controller(controllers, controller_id)?.num_ports()) -} - -/// Convert a (controller ID, local port ID) to a global port ID -pub fn controller_port_to_global_id( - controllers: &intrusive_list::IntrusiveList, - controller_id: ControllerId, - port_id: LocalPortId, -) -> Result { - lookup_controller(controllers, controller_id)?.lookup_global_port(port_id) -} - -/// Get number of ports on the system -pub fn get_num_ports(controllers: &intrusive_list::IntrusiveList) -> usize { - super::controller::get_num_ports(controllers) -} diff --git a/type-c-service/src/type_c/mod.rs b/type-c-service/src/util.rs similarity index 74% rename from type-c-service/src/type_c/mod.rs rename to type-c-service/src/util.rs index a5100bf17..9eaadeaf7 100644 --- a/type-c-service/src/type_c/mod.rs +++ b/type-c-service/src/util.rs @@ -1,22 +1,7 @@ -//! Type-C service +//! Type-C service utility functions and constants. use embedded_usb_pd::pdo::{Common, Contract}; use embedded_usb_pd::type_c; -pub mod comms; -pub mod controller; -pub mod event; -pub mod external; - -/// Controller ID -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct ControllerId(pub u8); - -/// Length of the Other VDM data -pub const OTHER_VDM_LEN: usize = 29; -/// Length of the Attention VDM data -pub const ATTN_VDM_LEN: usize = 9; - pub fn power_capability_try_from_contract( contract: Contract, ) -> Option { @@ -61,8 +46,3 @@ pub const POWER_CAPABILITY_5V_3A0: power_policy_interface::capability::PowerCapa voltage_mv: 5000, current_ma: 3000, }; - -/// Newtype to help clarify arguments to port status commands -#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Cached(pub bool); diff --git a/type-c-service/src/wrapper/backing.rs b/type-c-service/src/wrapper/backing.rs index 68c54916d..92e182d34 100644 --- a/type-c-service/src/wrapper/backing.rs +++ b/type-c-service/src/wrapper/backing.rs @@ -16,11 +16,8 @@ use embedded_cfu_protocol::protocol_definitions::ComponentId; use embedded_services::event; use embedded_usb_pd::{GlobalPortId, ado::Ado}; -use crate::type_c::{ - ControllerId, - controller::PortStatus, - event::{PortEvent, PortStatusChanged}, -}; +use type_c_interface::port::event::{PortEvent, PortStatusChanged}; +use type_c_interface::port::{ControllerId, PortStatus}; use crate::{ PortEventStreamer, @@ -50,8 +47,8 @@ impl Default for ControllerState { /// Service registration objects pub struct Registration<'a, M: RawMutex> { - pub context: &'a crate::type_c::controller::Context, - pub pd_controller: &'a crate::type_c::controller::Device<'a>, + pub context: &'a type_c_interface::service::context::Context, + pub pd_controller: &'a type_c_interface::port::Device<'a>, pub cfu_device: &'a CfuDevice, pub power_devices: &'a [&'a Mutex>], } @@ -68,7 +65,7 @@ const MAX_BUFFERED_PD_ALERTS: usize = 4; /// Base storage pub struct Storage<'a, const N: usize, M: RawMutex> { // Registration-related - context: &'a crate::type_c::controller::Context, + context: &'a type_c_interface::service::context::Context, controller_id: ControllerId, pd_ports: [GlobalPortId; N], cfu_device: CfuDevice, @@ -80,7 +77,7 @@ pub struct Storage<'a, const N: usize, M: RawMutex> { impl<'a, const N: usize, M: RawMutex> Storage<'a, N, M> { pub fn new( - context: &'a crate::type_c::controller::Context, + context: &'a type_c_interface::service::context::Context, controller_id: ControllerId, cfu_id: ComponentId, pd_ports: [GlobalPortId; N], @@ -207,7 +204,7 @@ pub struct ReferencedStorage< S: event::Sender, > { intermediate: &'a IntermediateStorage<'a, N, M, S>, - pd_controller: crate::type_c::controller::Device<'a>, + pd_controller: type_c_interface::port::Device<'a>, power_devices: [&'a Mutex>; N], } @@ -218,7 +215,7 @@ impl<'a, const N: usize, M: RawMutex, S: event::Sender) -> Option { Some(Self { intermediate, - pd_controller: crate::type_c::controller::Device::new( + pd_controller: type_c_interface::port::Device::new( intermediate.storage.controller_id, intermediate.storage.pd_ports.as_slice(), ), diff --git a/type-c-service/src/wrapper/cfu.rs b/type-c-service/src/wrapper/cfu.rs index beb786e90..4e206ded5 100644 --- a/type-c-service/src/wrapper/cfu.rs +++ b/type-c-service/src/wrapper/cfu.rs @@ -1,11 +1,11 @@ //! CFU message bridge //! TODO: remove this once we have a more generic FW update implementation -use crate::type_c::controller::Controller; use crate::wrapper::backing::ControllerState; use cfu_service::component::{InternalResponseData, RequestData}; use embassy_futures::select::{Either, select}; use embedded_cfu_protocol::protocol_definitions::*; use embedded_services::{debug, error}; +use type_c_interface::port::Controller; use super::message::EventCfu; use super::*; diff --git a/type-c-service/src/wrapper/dp.rs b/type-c-service/src/wrapper/dp.rs index 0bf3165a0..1ca250685 100644 --- a/type-c-service/src/wrapper/dp.rs +++ b/type-c-service/src/wrapper/dp.rs @@ -1,9 +1,9 @@ use super::{ControllerWrapper, FwOfferValidator}; -use crate::type_c::controller::Controller; use crate::wrapper::message::OutputDpStatusChanged; use embassy_sync::blocking_mutex::raw::RawMutex; use embedded_services::{event, sync::Lockable, trace}; use embedded_usb_pd::{Error, LocalPortId}; +use type_c_interface::port::Controller; impl< 'device, diff --git a/type-c-service/src/wrapper/message.rs b/type-c-service/src/wrapper/message.rs index c49d31475..aa4c5c03a 100644 --- a/type-c-service/src/wrapper/message.rs +++ b/type-c-service/src/wrapper/message.rs @@ -2,9 +2,9 @@ use embedded_services::{GlobalRawMutex, ipc::deferred}; use embedded_usb_pd::{LocalPortId, ado::Ado}; -use crate::type_c::{ - controller::{self, DpStatus, PortStatus}, - event::{PortNotificationSingle, PortStatusChanged}, +use type_c_interface::{ + port::event::{PortNotificationSingle, PortStatusChanged}, + port::{self, DpStatus, PortStatus}, }; /// Port status changed event data @@ -56,7 +56,7 @@ pub enum Event<'a> { /// Power policy command received PowerPolicyCommand(EventPowerPolicyCommand), /// Command from TCPM - ControllerCommand(deferred::Request<'a, GlobalRawMutex, controller::Command, controller::Response<'static>>), + ControllerCommand(deferred::Request<'a, GlobalRawMutex, port::Command, port::Response<'static>>), /// Cfu event CfuEvent(EventCfu), } @@ -94,15 +94,15 @@ pub struct OutputPowerPolicyCommand { /// Controller command output data pub struct OutputControllerCommand<'a> { /// Controller request - pub request: deferred::Request<'a, GlobalRawMutex, controller::Command, controller::Response<'static>>, + pub request: deferred::Request<'a, GlobalRawMutex, port::Command, port::Response<'static>>, /// Response - pub response: controller::Response<'static>, + pub response: port::Response<'static>, } pub mod vdm { //! Events and output for vendor-defined messaging. use super::LocalPortId; - use crate::type_c::controller::{AttnVdm, OtherVdm}; + use type_c_interface::port::{AttnVdm, OtherVdm}; /// The kind of output from processing a vendor-defined message. #[derive(Copy, Clone, Debug)] diff --git a/type-c-service/src/wrapper/mod.rs b/type-c-service/src/wrapper/mod.rs index a724e92fd..91506384a 100644 --- a/type-c-service/src/wrapper/mod.rs +++ b/type-c-service/src/wrapper/mod.rs @@ -1,8 +1,8 @@ //! This module contains the [`ControllerWrapper`] struct. This struct serves as a bridge between various service messages -//! and the actual controller functions provided by [`crate::type_c::controller::Controller`]. +//! and the actual controller functions provided by [`type_c_interface::port::Controller`]. //! # Supported service messaging -//! This struct current currently supports messages from the following services: -//! * Type-C: [`crate::type_c::controller::Command`] +//! This struct currently supports messages from the following services: +//! * Type-C: [`type_c_interface::port::Command`] //! * CFU: [`cfu_service::Request`] //! # Event loop //! This struct follows a standard wait/process/finalize event loop. @@ -19,8 +19,6 @@ use core::array::from_fn; use core::future::pending; use core::ops::DerefMut; -use crate::type_c::controller::{self, Controller, PortStatus}; -use crate::type_c::event::{PortEvent, PortNotificationSingle, PortPending, PortStatusChanged}; use crate::wrapper::backing::{ControllerState, PortState}; use cfu_service::CfuClient; use embassy_futures::select::{Either, Either5, select, select_array, select5}; @@ -29,9 +27,9 @@ use embassy_sync::mutex::Mutex; use embassy_sync::signal::Signal; use embassy_time::Instant; use embedded_cfu_protocol::protocol_definitions::{FwUpdateOffer, FwUpdateOfferResponse, FwVersion}; +use embedded_services::event; use embedded_services::sync::Lockable; use embedded_services::{debug, error, info, trace, warn}; -use embedded_services::{event, intrusive_list}; use embedded_usb_pd::ado::Ado; use embedded_usb_pd::{Error, LocalPortId, PdError}; @@ -49,6 +47,9 @@ mod power; pub mod proxy; mod vdm; +use type_c_interface::port::event::{PortEvent, PortNotificationSingle, PortPending, PortStatusChanged}; +use type_c_interface::port::{Controller, PortStatus}; + /// Base interval for checking for FW update timeouts and recovery attempts pub const DEFAULT_FW_UPDATE_TICK_INTERVAL_MS: u64 = 5000; /// Default number of ticks before we consider a firmware update to have timed out @@ -66,7 +67,7 @@ pub trait FwOfferValidator { /// Maximum number of supported ports pub const MAX_SUPPORTED_PORTS: usize = 2; -/// Common functionality implemented on top of [`crate::type_c::controller::Controller`] +/// Common functionality implemented on top of [`type_c_interface::port::Controller`] pub struct ControllerWrapper< 'device, M: RawMutex, @@ -591,18 +592,17 @@ where } /// Register all devices with their respective services - pub fn register( - &'static self, - controllers: &intrusive_list::IntrusiveList, - cfu_client: &CfuClient, - ) -> Result<(), Error<::BusError>> { - controller::register_controller(controllers, self.registration.pd_controller).map_err(|_| { - error!( - "Controller{}: Failed to register PD controller", - self.registration.pd_controller.id().0 - ); - Error::Pd(PdError::Failed) - })?; + pub fn register(&'static self, cfu_client: &CfuClient) -> Result<(), Error<::BusError>> { + self.registration + .context + .register_controller(self.registration.pd_controller) + .map_err(|_| { + error!( + "Controller{}: Failed to register PD controller", + self.registration.pd_controller.id().0 + ); + Error::Pd(PdError::Failed) + })?; //TODO: Remove when we have a more general framework in place cfu_client.register_device(self.registration.cfu_device).map_err(|_| { diff --git a/type-c-service/src/wrapper/pd.rs b/type-c-service/src/wrapper/pd.rs index a842bb2b4..a789f3b38 100644 --- a/type-c-service/src/wrapper/pd.rs +++ b/type-c-service/src/wrapper/pd.rs @@ -1,5 +1,3 @@ -use crate::type_c::Cached; -use crate::type_c::controller::{InternalResponseData, Response}; use crate::wrapper::backing::ControllerState; use embassy_futures::yield_now; use embassy_sync::pubsub::WaitResult; @@ -8,6 +6,9 @@ use embedded_services::debug; use embedded_usb_pd::constants::{T_PS_TRANSITION_EPR_MS, T_PS_TRANSITION_SPR_MS}; use embedded_usb_pd::ucsi::{self, lpm}; use power_policy_interface::psu::{self, PsuState}; +use type_c_interface::port; +use type_c_interface::port::Cached; +use type_c_interface::port::{InternalResponseData, Response}; use super::*; @@ -110,7 +111,7 @@ where state: &psu::State, local_port: LocalPortId, voltage_mv: Option, - ) -> Result { + ) -> Result { let psu_state = state.psu_state; debug!("Port{}: Current state: {:#?}", local_port.0, psu_state); if matches!(psu_state, PsuState::ConnectedConsumer(_)) { @@ -130,7 +131,7 @@ where } match controller.set_max_sink_voltage(local_port, voltage_mv).await { - Ok(()) => Ok(controller::PortResponseData::Complete), + Ok(()) => Ok(port::PortResponseData::Complete), Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), @@ -144,12 +145,12 @@ where port_state: &mut PortState<'_, S>, local_port: LocalPortId, cached: Cached, - ) -> Result { + ) -> Result { if cached.0 { - Ok(controller::PortResponseData::PortStatus(port_state.status)) + Ok(port::PortResponseData::PortStatus(port_state.status)) } else { match controller.get_port_status(local_port).await { - Ok(status) => Ok(controller::PortResponseData::PortStatus(status)), + Ok(status) => Ok(port::PortResponseData::PortStatus(status)), Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), @@ -163,83 +164,81 @@ where &self, controller_state: &mut ControllerState, controller: &mut D::Inner, - command: &controller::PortCommand, + command: &port::PortCommand, ) -> Response<'static> { if controller_state.fw_update_state.in_progress() { debug!("FW update in progress, ignoring port command"); - return controller::Response::Port(Err(PdError::Busy)); + return port::Response::Port(Err(PdError::Busy)); } let local_port = if let Ok(port) = self.registration.pd_controller.lookup_local_port(command.port) { port } else { debug!("Invalid port: {:?}", command.port); - return controller::Response::Port(Err(PdError::InvalidPort)); + return port::Response::Port(Err(PdError::InvalidPort)); }; let Some(port) = self.ports.get(local_port.0 as usize) else { debug!("Invalid port: {:?}", command.port); - return controller::Response::Port(Err(PdError::InvalidPort)); + return port::Response::Port(Err(PdError::InvalidPort)); }; let mut port_state = port.state.lock().await; - controller::Response::Port(match command.data { - controller::PortCommandData::PortStatus(cached) => { + port::Response::Port(match command.data { + port::PortCommandData::PortStatus(cached) => { self.process_get_port_status(controller, &mut port_state, local_port, cached) .await } - controller::PortCommandData::ClearEvents => { + port::PortCommandData::ClearEvents => { let event = core::mem::take(&mut port_state.pending_events); - Ok(controller::PortResponseData::ClearEvents(event)) + Ok(port::PortResponseData::ClearEvents(event)) } - controller::PortCommandData::RetimerFwUpdateGetState => { + port::PortCommandData::RetimerFwUpdateGetState => { match controller.get_rt_fw_update_status(local_port).await { - Ok(status) => Ok(controller::PortResponseData::RtFwUpdateStatus(status)), + Ok(status) => Ok(port::PortResponseData::RtFwUpdateStatus(status)), Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, } } - controller::PortCommandData::RetimerFwUpdateSetState => { + port::PortCommandData::RetimerFwUpdateSetState => { match controller.set_rt_fw_update_state(local_port).await { - Ok(()) => Ok(controller::PortResponseData::Complete), + Ok(()) => Ok(port::PortResponseData::Complete), Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, } } - controller::PortCommandData::RetimerFwUpdateClearState => { + port::PortCommandData::RetimerFwUpdateClearState => { match controller.clear_rt_fw_update_state(local_port).await { - Ok(()) => Ok(controller::PortResponseData::Complete), + Ok(()) => Ok(port::PortResponseData::Complete), Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, } } - controller::PortCommandData::SetRetimerCompliance => match controller.set_rt_compliance(local_port).await { - Ok(()) => Ok(controller::PortResponseData::Complete), + port::PortCommandData::SetRetimerCompliance => match controller.set_rt_compliance(local_port).await { + Ok(()) => Ok(port::PortResponseData::Complete), Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, }, - controller::PortCommandData::ReconfigureRetimer => match controller.reconfigure_retimer(local_port).await { - Ok(()) => Ok(controller::PortResponseData::Complete), + port::PortCommandData::ReconfigureRetimer => match controller.reconfigure_retimer(local_port).await { + Ok(()) => Ok(port::PortResponseData::Complete), Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, }, - controller::PortCommandData::GetPdAlert => { - match self.process_get_pd_alert(&mut port_state, local_port).await { - Ok(alert) => Ok(controller::PortResponseData::PdAlert(alert)), - Err(e) => Err(e), - } - } - controller::PortCommandData::SetMaxSinkVoltage(voltage_mv) => { + port::PortCommandData::GetPdAlert => match self.process_get_pd_alert(&mut port_state, local_port).await { + Ok(alert) => Ok(port::PortResponseData::PdAlert(alert)), + Err(e) => Err(e), + }, + port::PortCommandData::SetMaxSinkVoltage(voltage_mv) => { match self.registration.pd_controller.lookup_local_port(command.port) { Ok(local_port) => { let psu_state = port.proxy.lock().await.psu_state; @@ -255,124 +254,116 @@ where Err(e) => Err(e), } } - controller::PortCommandData::SetUnconstrainedPower(unconstrained) => { + port::PortCommandData::SetUnconstrainedPower(unconstrained) => { match controller.set_unconstrained_power(local_port, unconstrained).await { - Ok(()) => Ok(controller::PortResponseData::Complete), + Ok(()) => Ok(port::PortResponseData::Complete), Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, } } - controller::PortCommandData::ClearDeadBatteryFlag => { - match controller.clear_dead_battery_flag(local_port).await { - Ok(()) => Ok(controller::PortResponseData::Complete), - Err(e) => match e { - Error::Bus(_) => Err(PdError::Failed), - Error::Pd(e) => Err(e), - }, - } - } - controller::PortCommandData::GetOtherVdm => match controller.get_other_vdm(local_port).await { + port::PortCommandData::ClearDeadBatteryFlag => match controller.clear_dead_battery_flag(local_port).await { + Ok(()) => Ok(port::PortResponseData::Complete), + Err(e) => match e { + Error::Bus(_) => Err(PdError::Failed), + Error::Pd(e) => Err(e), + }, + }, + port::PortCommandData::GetOtherVdm => match controller.get_other_vdm(local_port).await { Ok(vdm) => { debug!("Port{}: Other VDM: {:?}", local_port.0, vdm); - Ok(controller::PortResponseData::OtherVdm(vdm)) + Ok(port::PortResponseData::OtherVdm(vdm)) } Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, }, - controller::PortCommandData::GetAttnVdm => match controller.get_attn_vdm(local_port).await { + port::PortCommandData::GetAttnVdm => match controller.get_attn_vdm(local_port).await { Ok(vdm) => { debug!("Port{}: Attention VDM: {:?}", local_port.0, vdm); - Ok(controller::PortResponseData::AttnVdm(vdm)) + Ok(port::PortResponseData::AttnVdm(vdm)) } Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, }, - controller::PortCommandData::SendVdm(tx_vdm) => match controller.send_vdm(local_port, tx_vdm).await { - Ok(()) => Ok(controller::PortResponseData::Complete), + port::PortCommandData::SendVdm(tx_vdm) => match controller.send_vdm(local_port, tx_vdm).await { + Ok(()) => Ok(port::PortResponseData::Complete), Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, }, - controller::PortCommandData::SetUsbControl(config) => { + port::PortCommandData::SetUsbControl(config) => { match controller.set_usb_control(local_port, config).await { - Ok(()) => Ok(controller::PortResponseData::Complete), + Ok(()) => Ok(port::PortResponseData::Complete), Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, } } - controller::PortCommandData::GetDpStatus => match controller.get_dp_status(local_port).await { + port::PortCommandData::GetDpStatus => match controller.get_dp_status(local_port).await { Ok(status) => { debug!("Port{}: DP Status: {:?}", local_port.0, status); - Ok(controller::PortResponseData::DpStatus(status)) + Ok(port::PortResponseData::DpStatus(status)) } Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, }, - controller::PortCommandData::SetDpConfig(config) => { - match controller.set_dp_config(local_port, config).await { - Ok(()) => Ok(controller::PortResponseData::Complete), - Err(e) => match e { - Error::Bus(_) => Err(PdError::Failed), - Error::Pd(e) => Err(e), - }, - } - } - controller::PortCommandData::ExecuteDrst => match controller.execute_drst(local_port).await { - Ok(()) => Ok(controller::PortResponseData::Complete), + port::PortCommandData::SetDpConfig(config) => match controller.set_dp_config(local_port, config).await { + Ok(()) => Ok(port::PortResponseData::Complete), Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, }, - controller::PortCommandData::SetTbtConfig(config) => { - match controller.set_tbt_config(local_port, config).await { - Ok(()) => Ok(controller::PortResponseData::Complete), - Err(e) => match e { - Error::Bus(_) => Err(PdError::Failed), - Error::Pd(e) => Err(e), - }, - } - } - controller::PortCommandData::SetPdStateMachineConfig(config) => { + port::PortCommandData::ExecuteDrst => match controller.execute_drst(local_port).await { + Ok(()) => Ok(port::PortResponseData::Complete), + Err(e) => match e { + Error::Bus(_) => Err(PdError::Failed), + Error::Pd(e) => Err(e), + }, + }, + port::PortCommandData::SetTbtConfig(config) => match controller.set_tbt_config(local_port, config).await { + Ok(()) => Ok(port::PortResponseData::Complete), + Err(e) => match e { + Error::Bus(_) => Err(PdError::Failed), + Error::Pd(e) => Err(e), + }, + }, + port::PortCommandData::SetPdStateMachineConfig(config) => { match controller.set_pd_state_machine_config(local_port, config).await { - Ok(()) => Ok(controller::PortResponseData::Complete), + Ok(()) => Ok(port::PortResponseData::Complete), Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, } } - controller::PortCommandData::SetTypeCStateMachineConfig(state) => { + port::PortCommandData::SetTypeCStateMachineConfig(state) => { match controller.set_type_c_state_machine_config(local_port, state).await { - Ok(()) => Ok(controller::PortResponseData::Complete), + Ok(()) => Ok(port::PortResponseData::Complete), Err(e) => match e { Error::Bus(_) => Err(PdError::Failed), Error::Pd(e) => Err(e), }, } } - controller::PortCommandData::ExecuteUcsiCommand(command_data) => { - Ok(controller::PortResponseData::UcsiResponse( - controller - .execute_ucsi_command(lpm::Command::new(local_port, command_data)) - .await - .map_err(|e| match e { - Error::Bus(_) => PdError::Failed, - Error::Pd(e) => e, - }), - )) - } + port::PortCommandData::ExecuteUcsiCommand(command_data) => Ok(port::PortResponseData::UcsiResponse( + controller + .execute_ucsi_command(lpm::Command::new(local_port, command_data)) + .await + .map_err(|e| match e { + Error::Bus(_) => PdError::Failed, + Error::Pd(e) => e, + }), + )), }) } @@ -380,29 +371,29 @@ where &self, controller_state: &mut ControllerState, controller: &mut D::Inner, - command: &controller::InternalCommandData, + command: &port::InternalCommandData, ) -> Response<'static> { if controller_state.fw_update_state.in_progress() { debug!("FW update in progress, ignoring controller command"); - return controller::Response::Controller(Err(PdError::Busy)); + return port::Response::Controller(Err(PdError::Busy)); } match command { - controller::InternalCommandData::Status => { + port::InternalCommandData::Status => { let status = controller.get_controller_status().await; - controller::Response::Controller(status.map(InternalResponseData::Status).map_err(|_| PdError::Failed)) + port::Response::Controller(status.map(InternalResponseData::Status).map_err(|_| PdError::Failed)) } - controller::InternalCommandData::SyncState => { + port::InternalCommandData::SyncState => { let result = self.sync_state_internal(controller).await; - controller::Response::Controller( + port::Response::Controller( result .map(|_| InternalResponseData::Complete) .map_err(|_| PdError::Failed), ) } - controller::InternalCommandData::Reset => { + port::InternalCommandData::Reset => { let result = controller.reset_controller().await; - controller::Response::Controller( + port::Response::Controller( result .map(|_| InternalResponseData::Complete) .map_err(|_| PdError::Failed), @@ -416,17 +407,15 @@ where &self, controller_state: &mut ControllerState, controller: &mut D::Inner, - command: &controller::Command, + command: &port::Command, ) -> Response<'static> { match command { - controller::Command::Port(command) => { - self.process_port_command(controller_state, controller, command).await - } - controller::Command::Controller(command) => { + port::Command::Port(command) => self.process_port_command(controller_state, controller, command).await, + port::Command::Controller(command) => { self.process_controller_command(controller_state, controller, command) .await } - controller::Command::Lpm(_) => controller::Response::Ucsi(ucsi::Response { + port::Command::Lpm(_) => port::Response::Ucsi(ucsi::Response { cci: ucsi::cci::Cci::new_error(), data: None, }), diff --git a/type-c-service/src/wrapper/vdm.rs b/type-c-service/src/wrapper/vdm.rs index baa088503..896c2b2a8 100644 --- a/type-c-service/src/wrapper/vdm.rs +++ b/type-c-service/src/wrapper/vdm.rs @@ -4,10 +4,8 @@ use embedded_usb_pd::{Error, LocalPortId, PdError}; use crate::wrapper::message::vdm::OutputKind; -use crate::type_c::{ - controller::Controller, - event::{PortPending, VdmNotification}, -}; +use type_c_interface::port::Controller; +use type_c_interface::port::event::{PortPending, VdmNotification}; use super::{ControllerWrapper, FwOfferValidator, message::vdm::Output};