diff --git a/Cargo.lock b/Cargo.lock index 3c5e577d..cd2d69a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2259,8 +2259,6 @@ version = "0.1.0" dependencies = [ "bitfield 0.17.0", "defmt 0.3.100", - "embassy-sync", - "embassy-time", "embedded-services", "embedded-usb-pd", "log", diff --git a/examples/rt685s-evk/Cargo.lock b/examples/rt685s-evk/Cargo.lock index 24458c16..ab677628 100644 --- a/examples/rt685s-evk/Cargo.lock +++ b/examples/rt685s-evk/Cargo.lock @@ -1510,8 +1510,6 @@ version = "0.1.0" dependencies = [ "bitfield 0.17.0", "defmt 0.3.100", - "embassy-sync", - "embassy-time", "embedded-services", "embedded-usb-pd", "power-policy-interface", diff --git a/examples/rt685s-evk/src/bin/type_c.rs b/examples/rt685s-evk/src/bin/type_c.rs index 75512407..9efa5a92 100644 --- a/examples/rt685s-evk/src/bin/type_c.rs +++ b/examples/rt685s-evk/src/bin/type_c.rs @@ -8,26 +8,20 @@ use embassy_imxrt::gpio::{Input, Inverter, Pull}; use embassy_imxrt::i2c::Async; use embassy_imxrt::i2c::master::{Config, I2cMaster}; use embassy_imxrt::{bind_interrupts, peripherals}; -use embassy_sync::channel::{Channel, DynamicReceiver, DynamicSender}; +use embassy_sync::channel::{DynamicReceiver, DynamicSender}; use embassy_sync::mutex::Mutex; use embassy_sync::pubsub::{DynImmediatePublisher, DynSubscriber, PubSubChannel}; use embassy_time::{self as _, Delay}; use embedded_services::GlobalRawMutex; -use embedded_services::event::MapSender; +use embedded_services::event::{MapSender, NoopSender}; use embedded_services::{error, info}; -use embedded_usb_pd::{GlobalPortId, LocalPortId}; +use embedded_usb_pd::LocalPortId; use power_policy_interface::psu; use power_policy_service::psu::PsuEventReceivers; use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use tps6699x::asynchronous::embassy as tps6699x; -use type_c_interface::controller::ControllerId; -use type_c_interface::port::Device; -use type_c_interface::port::PortRegistration; use type_c_interface::port::event::PortEventBitfield; -use type_c_interface::service::event::PortEvent as ServicePortEvent; -use type_c_service::bridge::Bridge; -use type_c_service::bridge::event_receiver::EventReceiver as BridgeEventReceiver; use type_c_service::controller::Port; use type_c_service::controller::event_receiver::{ EventReceiver as PortEventReceiver, InterruptReceiver as _, PortEventSplitter, @@ -36,16 +30,11 @@ use type_c_service::controller::macros::PortComponents; use type_c_service::controller::state::SharedState; use type_c_service::define_controller_port_static_cell_channel; use type_c_service::driver::tps6699x::{self as tps6699x_drv}; -use type_c_service::service::{EventReceiver as ServiceEventReceiver, Service}; +use type_c_service::service::Service; +use type_c_service::service::registration::PortData; extern crate rt685s_evk_example; -const CHANNEL_CAPACITY: usize = 4; - -const CONTROLLER0_ID: ControllerId = ControllerId(0); -const PORT0_ID: GlobalPortId = GlobalPortId(0); -const PORT1_ID: GlobalPortId = GlobalPortId(1); - bind_interrupts!(struct Irqs { FLEXCOMM2 => embassy_imxrt::i2c::InterruptHandler; }); @@ -57,6 +46,7 @@ type PortType = Mutex< 'static, Tps6699xMutex<'static>, SharedStateType, + DynamicSender<'static, type_c_interface::service::event::PortEventData>, DynamicSender<'static, power_policy_interface::psu::event::EventData>, DynamicSender<'static, type_c_service::controller::event::Loopback>, >, @@ -88,7 +78,20 @@ type PowerPolicyServiceType = Mutex< >, >; -type ServiceType = Service<'static>; +const PORT_COUNT: usize = 2; +type PortReceiverType = DynamicReceiver<'static, type_c_interface::service::event::PortEventData>; +type TypeCServiceEventReceiverType = type_c_service::service::event_receiver::ArrayEventReceiver< + 'static, + PORT_COUNT, + PortType, + PortReceiverType, + PowerPolicyReceiverType, +>; + +type TypeCServiceSenderType = NoopSender; +type TypeCRegistrationType = + type_c_service::service::registration::ArrayRegistration<'static, PortType, PORT_COUNT, TypeCServiceSenderType, 1>; +type TypeCServiceType = type_c_service::service::Service<'static, TypeCRegistrationType>; type PortEventReceiverType = PortEventReceiver< 'static, SharedStateType, @@ -96,18 +99,6 @@ type PortEventReceiverType = PortEventReceiver< DynamicReceiver<'static, type_c_service::controller::event::Loopback>, >; -#[embassy_executor::task] -async fn bridge_task( - mut event_receiver: BridgeEventReceiver, - mut bridge: Bridge<'static, Tps6699xMutex<'static>>, -) -> ! { - loop { - let event = event_receiver.wait_next().await; - let output = bridge.process_event(event).await; - event_receiver.finalize(output); - } -} - #[embassy_executor::task(pool_size = 2)] async fn port_task(mut event_receiver: PortEventReceiverType, port: &'static PortType) { port.lock().await.sync_state().await.unwrap(); @@ -147,8 +138,8 @@ async fn power_policy_task( #[embassy_executor::task] async fn type_c_service_task( - service: &'static Mutex, - event_receiver: ServiceEventReceiver<'static, PowerPolicyReceiverType>, + service: &'static Mutex, + event_receiver: TypeCServiceEventReceiverType, ) { type_c_service::task::task(service, event_receiver).await; } @@ -193,9 +184,6 @@ async fn main(spawner: Spawner) { .await .unwrap(); - static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); - let controller_context = CONTROLLER_CONTEXT.init(type_c_interface::service::context::Context::new()); - info!("Spawining PD controller task"); static CONTROLLER_MUTEX: StaticCell> = StaticCell::new(); let controller_mutex = CONTROLLER_MUTEX.init(Mutex::new(tps6699x_drv::tps66994( @@ -205,45 +193,14 @@ async fn main(spawner: Spawner) { "tps6699x_0", ))); - static PORT0_CHANNEL: Channel = Channel::new(); - static PORT1_CHANNEL: Channel = Channel::new(); - - static PORT_REGISTRATION: StaticCell<[PortRegistration; 2]> = StaticCell::new(); - let port_registration = PORT_REGISTRATION.init([ - PortRegistration { - id: PORT0_ID, - sender: PORT0_CHANNEL.dyn_sender(), - receiver: PORT0_CHANNEL.dyn_receiver(), - }, - PortRegistration { - id: PORT1_ID, - sender: PORT1_CHANNEL.dyn_sender(), - receiver: PORT1_CHANNEL.dyn_receiver(), - }, - ]); - - static PD_REGISTRATION: StaticCell> = StaticCell::new(); - let pd_registration = PD_REGISTRATION.init(Device::new(CONTROLLER0_ID, port_registration)); - - controller_context.register_controller(pd_registration).unwrap(); - define_controller_port_static_cell_channel!(pub(self), port0, GlobalRawMutex, Tps6699xMutex<'static>); let PortComponents { port: port0, power_policy_receiver: policy_receiver0, event_receiver: event_receiver0, interrupt_sender: port0_interrupt_sender, - } = port0::create( - "PD0", - LocalPortId(0), - PORT0_ID, - Default::default(), - controller_mutex, - controller_context, - ); - - let bridge_receiver = BridgeEventReceiver::new(pd_registration); - let bridge = Bridge::new(controller_mutex, pd_registration); + type_c_receiver: type_c_receiver0, + } = port0::create("PD0", LocalPortId(0), Default::default(), controller_mutex); define_controller_port_static_cell_channel!(pub(self), port1, GlobalRawMutex, Tps6699xMutex<'static>); let PortComponents { @@ -251,14 +208,8 @@ async fn main(spawner: Spawner) { power_policy_receiver: policy_receiver1, event_receiver: event_receiver1, interrupt_sender: port1_interrupt_sender, - } = port1::create( - "PD1", - LocalPortId(0), - PORT1_ID, - Default::default(), - controller_mutex, - controller_context, - ); + type_c_receiver: type_c_receiver1, + } = port1::create("PD1", LocalPortId(1), Default::default(), controller_mutex); let port_event_splitter = PortEventSplitter::new([port0_interrupt_sender, port1_interrupt_sender]); @@ -286,14 +237,32 @@ async fn main(spawner: Spawner) { power_policy_service::service::config::Config::default(), ))); - static TYPE_C_SERVICE: StaticCell> = StaticCell::new(); - let type_c_service = TYPE_C_SERVICE.init(Mutex::new(Service::create(Default::default(), controller_context))); + static TYPE_C_SERVICE: StaticCell> = StaticCell::new(); + let type_c_service = TYPE_C_SERVICE.init(Mutex::new(Service::create( + Default::default(), + TypeCRegistrationType { + ports: [port0, port1], + service_senders: [NoopSender], + port_data: [ + PortData { + local_port: Some(LocalPortId(0)), + }, + PortData { + local_port: Some(LocalPortId(1)), + }, + ], + }, + ))); info!("Spawining type-c service task"); spawner.spawn( type_c_service_task( type_c_service, - ServiceEventReceiver::new(controller_context, power_policy_subscriber), + TypeCServiceEventReceiverType::new( + [port0, port1], + [type_c_receiver0, type_c_receiver1], + power_policy_subscriber, + ), ) .expect("Failed to create type-c service task"), ); @@ -307,7 +276,6 @@ async fn main(spawner: Spawner) { .expect("Failed to create power policy task"), ); - spawner.spawn(bridge_task(bridge_receiver, bridge).expect("Failed to create bridge task")); spawner.spawn(port_task(event_receiver0, port0).expect("Failed to create controller0 task")); spawner.spawn(port_task(event_receiver1, port1).expect("Failed to create controller1 task")); diff --git a/examples/rt685s-evk/src/bin/type_c_cfu.rs b/examples/rt685s-evk/src/bin/type_c_cfu.rs index 622336de..4f484753 100644 --- a/examples/rt685s-evk/src/bin/type_c_cfu.rs +++ b/examples/rt685s-evk/src/bin/type_c_cfu.rs @@ -11,7 +11,7 @@ use embassy_imxrt::gpio::{Input, Inverter, Pull}; use embassy_imxrt::i2c::Async; use embassy_imxrt::i2c::master::{Config, I2cMaster}; use embassy_imxrt::{bind_interrupts, peripherals}; -use embassy_sync::channel::{Channel, DynamicReceiver, DynamicSender}; +use embassy_sync::channel::{DynamicReceiver, DynamicSender}; use embassy_sync::mutex::Mutex; use embassy_sync::once_lock::OnceLock; use embassy_sync::pubsub::{DynImmediatePublisher, DynSubscriber, PubSubChannel}; @@ -20,20 +20,15 @@ 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::MapSender; +use embedded_services::event::{MapSender, NoopSender}; use embedded_services::{error, info}; -use embedded_usb_pd::{GlobalPortId, LocalPortId}; +use embedded_usb_pd::LocalPortId; use power_policy_interface::psu; use power_policy_service::psu::PsuEventReceivers; use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use tps6699x::asynchronous::embassy as tps6699x; -use type_c_interface::controller::ControllerId; use type_c_interface::port::event::PortEventBitfield; -use type_c_interface::port::{Device, PortRegistration}; -use type_c_interface::service::event::PortEvent as ServicePortEvent; -use type_c_service::bridge::Bridge; -use type_c_service::bridge::event_receiver::EventReceiver as BridgeEventReceiver; use type_c_service::controller::Port; use type_c_service::controller::event_receiver::{ EventReceiver as PortEventReceiver, InterruptReceiver as _, PortEventSplitter, @@ -42,12 +37,11 @@ use type_c_service::controller::macros::PortComponents; use type_c_service::controller::state::SharedState as PortSharedState; use type_c_service::define_controller_port_static_cell_channel; use type_c_service::driver::tps6699x::{self as tps6699x_drv}; -use type_c_service::service::{EventReceiver as ServiceEventReceiver, Service}; +use type_c_service::service::Service; +use type_c_service::service::registration::PortData; extern crate rt685s_evk_example; -const CHANNEL_CAPACITY: usize = 4; - bind_interrupts!(struct Irqs { FLEXCOMM2 => embassy_imxrt::i2c::InterruptHandler; }); @@ -68,6 +62,7 @@ type PortType = Mutex< 'static, Tps6699xMutex<'static>, PortSharedStateType, + DynamicSender<'static, type_c_interface::service::event::PortEventData>, DynamicSender<'static, power_policy_interface::psu::event::EventData>, DynamicSender<'static, type_c_service::controller::event::Loopback>, >, @@ -99,33 +94,32 @@ type PowerPolicyServiceType = Mutex< >, >; -type ServiceType = Service<'static>; +const PORT_COUNT: usize = 2; +type PortReceiverType = DynamicReceiver<'static, type_c_interface::service::event::PortEventData>; +type TypeCServiceEventReceiverType = type_c_service::service::event_receiver::ArrayEventReceiver< + 'static, + PORT_COUNT, + PortType, + PortReceiverType, + PowerPolicyReceiverType, +>; + +type TypeCServiceSenderType = NoopSender; +type TypeCRegistrationType = + type_c_service::service::registration::ArrayRegistration<'static, PortType, PORT_COUNT, TypeCServiceSenderType, 1>; +type TypeCServiceType = type_c_service::service::Service<'static, TypeCRegistrationType>; type PortEventReceiverType = PortEventReceiver< 'static, PortSharedStateType, DynamicReceiver<'static, PortEventBitfield>, DynamicReceiver<'static, type_c_service::controller::event::Loopback>, >; + type CfuUpdaterSharedStateType = Mutex; type CfuUpdaterType<'a> = cfu_service::basic::Updater<'a, Tps6699xMutex<'a>, CfuUpdaterSharedStateType, CfuCustomization>; -const CONTROLLER0_ID: ControllerId = ControllerId(0); const CONTROLLER0_CFU_ID: ComponentId = 0x12; -const PORT0_ID: GlobalPortId = GlobalPortId(0); -const PORT1_ID: GlobalPortId = GlobalPortId(1); - -#[embassy_executor::task] -async fn bridge_task( - mut event_receiver: BridgeEventReceiver, - mut bridge: Bridge<'static, Tps6699xMutex<'static>>, -) -> ! { - loop { - let event = event_receiver.wait_next().await; - let output = bridge.process_event(event).await; - event_receiver.finalize(output); - } -} #[embassy_executor::task(pool_size = 2)] async fn port_task(mut event_receiver: PortEventReceiverType, port: &'static PortType) { @@ -259,8 +253,8 @@ async fn power_policy_task( #[embassy_executor::task] async fn type_c_service_task( - service: &'static Mutex, - event_receiver: ServiceEventReceiver<'static, PowerPolicyReceiverType>, + service: &'static Mutex, + event_receiver: TypeCServiceEventReceiverType, ) { type_c_service::task::task(service, event_receiver).await; } @@ -305,9 +299,6 @@ async fn main(spawner: Spawner) { .await .unwrap(); - static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); - let controller_context = CONTROLLER_CONTEXT.init(type_c_interface::service::context::Context::new()); - info!("Spawining PD controller task"); static CONTROLLER_MUTEX: StaticCell> = StaticCell::new(); let controller_mutex = CONTROLLER_MUTEX.init(Mutex::new(tps6699x_drv::tps66994( @@ -317,27 +308,6 @@ async fn main(spawner: Spawner) { "tps6699x_0", ))); - static PORT0_CHANNEL: Channel = Channel::new(); - static PORT1_CHANNEL: Channel = Channel::new(); - static PORT_REGISTRATION: StaticCell<[PortRegistration; 2]> = StaticCell::new(); - let port_registration = PORT_REGISTRATION.init([ - PortRegistration { - id: PORT0_ID, - sender: PORT0_CHANNEL.dyn_sender(), - receiver: PORT0_CHANNEL.dyn_receiver(), - }, - PortRegistration { - id: PORT1_ID, - sender: PORT1_CHANNEL.dyn_sender(), - receiver: PORT1_CHANNEL.dyn_receiver(), - }, - ]); - - static PD_REGISTRATION: StaticCell> = StaticCell::new(); - let pd_registration = PD_REGISTRATION.init(Device::new(CONTROLLER0_ID, port_registration)); - - controller_context.register_controller(pd_registration).unwrap(); - // Create controller CFU device and updater static CFU_DEVICE: StaticCell = StaticCell::new(); let cfu_device = CFU_DEVICE.init(CfuDevice::new(CONTROLLER0_CFU_ID)); @@ -355,8 +325,6 @@ async fn main(spawner: Spawner) { CONTROLLER0_CFU_ID, CfuCustomization, ); - let bridge_receiver = BridgeEventReceiver::new(pd_registration); - let bridge = Bridge::new(controller_mutex, pd_registration); // Create CFU client static CFU_CLIENT: OnceLock = OnceLock::new(); @@ -369,14 +337,8 @@ async fn main(spawner: Spawner) { power_policy_receiver: policy_receiver0, event_receiver: event_receiver0, interrupt_sender: port0_interrupt_sender, - } = port0::create( - "PD0", - LocalPortId(0), - PORT0_ID, - Default::default(), - controller_mutex, - controller_context, - ); + type_c_receiver: type_c_receiver0, + } = port0::create("PD0", LocalPortId(0), Default::default(), controller_mutex); define_controller_port_static_cell_channel!(pub(self),port1, GlobalRawMutex, Tps6699xMutex<'static>); let PortComponents { @@ -384,14 +346,8 @@ async fn main(spawner: Spawner) { power_policy_receiver: policy_receiver1, event_receiver: event_receiver1, interrupt_sender: port1_interrupt_sender, - } = port1::create( - "PD1", - LocalPortId(0), - PORT1_ID, - Default::default(), - controller_mutex, - controller_context, - ); + type_c_receiver: type_c_receiver1, + } = port1::create("PD1", LocalPortId(1), Default::default(), controller_mutex); let port_event_splitter = PortEventSplitter::new([port0_interrupt_sender, port1_interrupt_sender]); @@ -419,14 +375,32 @@ async fn main(spawner: Spawner) { power_policy_service::service::config::Config::default(), ))); - static TYPE_C_SERVICE: StaticCell> = StaticCell::new(); - let type_c_service = TYPE_C_SERVICE.init(Mutex::new(Service::create(Default::default(), controller_context))); + static TYPE_C_SERVICE: StaticCell> = StaticCell::new(); + let type_c_service = TYPE_C_SERVICE.init(Mutex::new(Service::create( + Default::default(), + TypeCRegistrationType { + ports: [port0, port1], + port_data: [ + PortData { + local_port: Some(LocalPortId(0)), + }, + PortData { + local_port: Some(LocalPortId(1)), + }, + ], + service_senders: [NoopSender], + }, + ))); info!("Spawining type-c service task"); spawner.spawn( type_c_service_task( type_c_service, - ServiceEventReceiver::new(controller_context, power_policy_subscriber), + TypeCServiceEventReceiverType::new( + [port0, port1], + [type_c_receiver0, type_c_receiver1], + power_policy_subscriber, + ), ) .expect("Failed to spawn type-c service task"), ); @@ -440,7 +414,6 @@ async fn main(spawner: Spawner) { .expect("Failed to create power policy task"), ); - spawner.spawn(bridge_task(bridge_receiver, bridge).expect("Failed to create bridge task")); spawner.spawn(port_task(event_receiver0, port0).expect("Failed to create controller0 task")); spawner.spawn(port_task(event_receiver1, port1).expect("Failed to create controller1 task")); diff --git a/examples/std/Cargo.lock b/examples/std/Cargo.lock index 65e83771..16c0a12b 100644 --- a/examples/std/Cargo.lock +++ b/examples/std/Cargo.lock @@ -1470,8 +1470,6 @@ name = "type-c-interface" version = "0.1.0" dependencies = [ "bitfield 0.17.0", - "embassy-sync", - "embassy-time", "embedded-services", "embedded-usb-pd", "log", diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 5bca75b3..a1b11088 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -63,10 +63,6 @@ path = "src/lib/lib.rs" name = "debug" path = "src/bin/debug.rs" -[[bin]] -name = "type-c-basic" -path = "src/bin/type_c/basic.rs" - [[bin]] name = "type-c-service" path = "src/bin/type_c/service.rs" diff --git a/examples/std/src/bin/type_c/basic.rs b/examples/std/src/bin/type_c/basic.rs deleted file mode 100644 index 516e5606..00000000 --- a/examples/std/src/bin/type_c/basic.rs +++ /dev/null @@ -1,127 +0,0 @@ -use embassy_executor::{Executor, Spawner}; -use embassy_sync::channel::Channel; -use embassy_time::Timer; -use embedded_services::GlobalRawMutex; - -use embedded_usb_pd::{GlobalPortId, PdError as Error}; -use log::*; -use static_cell::StaticCell; -use type_c_interface::controller::ControllerId; -use type_c_interface::port::{self, PortRegistration}; -use type_c_interface::service::context::{Context, DeviceContainer}; -use type_c_interface::service::event::PortEvent as ServicePortEvent; - -const CONTROLLER0_ID: ControllerId = ControllerId(0); -const PORT0_ID: GlobalPortId = GlobalPortId(0); -const PORT1_ID: GlobalPortId = GlobalPortId(1); -const CHANNEL_CAPACITY: usize = 4; - -mod test_controller { - use type_c_interface::port::PortRegistration; - - use super::*; - - pub struct Controller<'a> { - pub controller: port::Device<'a>, - } - - impl DeviceContainer for Controller<'_> { - fn get_pd_controller_device(&self) -> &port::Device<'_> { - &self.controller - } - } - - impl<'a> Controller<'a> { - pub fn new(id: ControllerId, ports: &'a [PortRegistration]) -> Self { - Self { - controller: port::Device::new(id, ports), - } - } - - async fn process_controller_command( - &self, - command: port::InternalCommandData, - ) -> Result { - match command { - port::InternalCommandData::Reset => { - info!("Reset controller"); - Ok(port::InternalResponseData::Complete) - } - port::InternalCommandData::SyncState => { - info!("Sync controller state"); - Ok(port::InternalResponseData::Complete) - } - } - } - - async fn process_port_command(&self, command: port::PortCommand) -> Result { - info!("Port command for port {}", command.port.0); - Ok(port::PortResponseData::Complete) - } - - pub async fn process(&self) { - let request = self.controller.receive().await; - let response = match request.command { - port::Command::Controller(command) => { - port::Response::Controller(self.process_controller_command(command).await) - } - port::Command::Port(command) => port::Response::Port(self.process_port_command(command).await), - }; - - request.respond(response); - } - } -} - -#[embassy_executor::task] -async fn controller_task(controller_context: &'static Context) { - static PORT0_CHANNEL: Channel = Channel::new(); - static PORT1_CHANNEL: Channel = Channel::new(); - - static PORTS: StaticCell<[PortRegistration; 2]> = StaticCell::new(); - let ports = PORTS.init([ - PortRegistration { - id: PORT0_ID, - sender: PORT0_CHANNEL.dyn_sender(), - receiver: PORT0_CHANNEL.dyn_receiver(), - }, - PortRegistration { - id: PORT1_ID, - sender: PORT1_CHANNEL.dyn_sender(), - receiver: PORT1_CHANNEL.dyn_receiver(), - }, - ]); - - static CONTROLLER: StaticCell = StaticCell::new(); - let controller = CONTROLLER.init(test_controller::Controller::new(CONTROLLER0_ID, ports.as_slice())); - controller_context.register_controller(controller).unwrap(); - - loop { - controller.process().await; - } -} - -#[embassy_executor::task] -async fn task(spawner: Spawner) { - embedded_services::init().await; - - static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); - let controller_context = CONTROLLER_CONTEXT.init(Context::new()); - - info!("Starting controller task"); - spawner.spawn(controller_task(controller_context).expect("Failed to create controller task")); - // Wait for controller to be registered - Timer::after_secs(1).await; - - controller_context.reset_controller(CONTROLLER0_ID).await.unwrap(); -} - -fn main() { - env_logger::builder().filter_level(log::LevelFilter::Info).init(); - - static EXECUTOR: StaticCell = StaticCell::new(); - let executor = EXECUTOR.init(Executor::new()); - executor.run(|spawner| { - spawner.spawn(task(spawner).expect("Failed to create task")); - }); -} diff --git a/examples/std/src/bin/type_c/service.rs b/examples/std/src/bin/type_c/service.rs index 008c20e4..3b5bbbc2 100644 --- a/examples/std/src/bin/type_c/service.rs +++ b/examples/std/src/bin/type_c/service.rs @@ -1,13 +1,13 @@ use embassy_executor::{Executor, Spawner}; -use embassy_sync::channel::{Channel, DynamicReceiver, DynamicSender}; +use embassy_sync::channel::{DynamicReceiver, DynamicSender}; use embassy_sync::mutex::Mutex; use embassy_sync::pubsub::{DynImmediatePublisher, DynSubscriber, PubSubChannel}; use embassy_time::Timer; use embedded_services::GlobalRawMutex; -use embedded_services::event::MapSender; +use embedded_services::event::{MapSender, NoopSender}; +use embedded_usb_pd::LocalPortId; use embedded_usb_pd::ado::Ado; use embedded_usb_pd::type_c::Current; -use embedded_usb_pd::{GlobalPortId, LocalPortId}; use log::*; use power_policy_interface::charger::mock::ChargerType; use power_policy_interface::psu; @@ -16,25 +16,17 @@ use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use std_examples::type_c::mock_controller::Port; use std_examples::type_c::mock_controller::{self, InterruptReceiver}; -use type_c_interface::controller::ControllerId; use type_c_interface::port::event::PortEventBitfield; -use type_c_interface::port::{Device, PortRegistration}; -use type_c_interface::service::event::PortEvent as ServicePortEvent; use type_c_interface::service::event::PortEventData as ServicePortEventData; -use type_c_service::bridge::Bridge; -use type_c_service::bridge::event_receiver::EventReceiver as BridgeEventReceiver; use type_c_service::controller::event_receiver::InterruptReceiver as _; use type_c_service::controller::event_receiver::{EventReceiver as PortEventReceiver, PortEventSplitter}; use type_c_service::controller::macros::PortComponents; use type_c_service::controller::state::SharedState; use type_c_service::define_controller_port_static_cell_channel; +use type_c_service::service::Service; use type_c_service::service::config::Config; -use type_c_service::service::{EventReceiver as ServiceEventReceiver, Service}; use type_c_service::util::power_capability_from_current; -const CHANNEL_CAPACITY: usize = 4; -const CONTROLLER0_ID: ControllerId = ControllerId(0); -const PORT0_ID: GlobalPortId = GlobalPortId(0); const DELAY_MS: u64 = 1000; type ControllerType = Mutex>; @@ -59,7 +51,20 @@ type PowerPolicyServiceType = Mutex< >, >; -type ServiceType = Service<'static>; +const PORT_COUNT: usize = 1; +type PortReceiverType = DynamicReceiver<'static, type_c_interface::service::event::PortEventData>; +type TypeCServiceEventReceiverType = type_c_service::service::event_receiver::ArrayEventReceiver< + 'static, + PORT_COUNT, + PortType, + PortReceiverType, + PowerPolicyReceiverType, +>; +type TypeCServiceSenderType = NoopSender; +type TypeCRegistrationType = + type_c_service::service::registration::ArrayRegistration<'static, PortType, PORT_COUNT, TypeCServiceSenderType, 1>; + +type ServiceType = type_c_service::service::Service<'static, TypeCRegistrationType>; type SharedStateType = Mutex; type PortEventReceiverType = PortEventReceiver< 'static, @@ -95,48 +100,15 @@ async fn interrupt_splitter_task( } } -#[embassy_executor::task] -async fn bridge_task( - mut event_receiver: BridgeEventReceiver, - mut bridge: Bridge<'static, Mutex>>, -) -> ! { - loop { - let event = event_receiver.wait_next().await; - let output = bridge.process_event(event).await; - event_receiver.finalize(output); - } -} - #[embassy_executor::task] async fn task(spawner: Spawner) { embedded_services::init().await; - // Create power policy service - static CONTEXT: StaticCell = StaticCell::new(); - let controller_context = CONTEXT.init(type_c_interface::service::context::Context::new()); - static STATE: StaticCell = StaticCell::new(); let state = STATE.init(mock_controller::ControllerState::new()); static CONTROLLER: StaticCell = StaticCell::new(); - let controller = CONTROLLER.init(Mutex::new(mock_controller::Controller::new(state))); - - static PORT_CHANNEL: Channel = Channel::new(); - - static PORT_REGISTRATION: StaticCell<[PortRegistration; 1]> = StaticCell::new(); - let port_registration = PORT_REGISTRATION.init([PortRegistration { - id: PORT0_ID, - sender: PORT_CHANNEL.dyn_sender(), - receiver: PORT_CHANNEL.dyn_receiver(), - }]); - - static PD_REGISTRATION: StaticCell> = StaticCell::new(); - let pd_registration = PD_REGISTRATION.init(Device::new(CONTROLLER0_ID, port_registration)); - - controller_context.register_controller(pd_registration).unwrap(); - - let bridge_receiver = BridgeEventReceiver::new(pd_registration); - let bridge = Bridge::new(controller, pd_registration); + let controller = CONTROLLER.init(Mutex::new(mock_controller::Controller::new(state, "Controller0"))); define_controller_port_static_cell_channel!(pub(self), port, GlobalRawMutex, Mutex>); let PortComponents { @@ -144,14 +116,8 @@ async fn task(spawner: Spawner) { power_policy_receiver, event_receiver, interrupt_sender: port_interrupt_sender, - } = port::create( - "PD0", - LocalPortId(0), - PORT0_ID, - Default::default(), - controller, - controller_context, - ); + type_c_receiver, + } = port::create("PD0", LocalPortId(0), Default::default(), controller); // Create type-c service // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot @@ -178,7 +144,16 @@ async fn task(spawner: Spawner) { ))); static TYPE_C_SERVICE: StaticCell> = StaticCell::new(); - let type_c_service = TYPE_C_SERVICE.init(Mutex::new(Service::create(Config::default(), controller_context))); + let type_c_service = TYPE_C_SERVICE.init(Mutex::new(Service::create( + Config::default(), + type_c_service::service::registration::ArrayRegistration { + ports: [port], + service_senders: [NoopSender], + port_data: [type_c_service::service::registration::PortData { + local_port: Some(LocalPortId(0)), + }], + }, + ))); // Spin up power policy service spawner.spawn( @@ -188,12 +163,11 @@ async fn task(spawner: Spawner) { spawner.spawn( type_c_service_task( type_c_service, - ServiceEventReceiver::new(controller_context, power_policy_subscriber), + TypeCServiceEventReceiverType::new([port], [type_c_receiver], power_policy_subscriber), ) .expect("Failed to create type-c service task"), ); - spawner.spawn(bridge_task(bridge_receiver, bridge).expect("Failed to create bridge task")); spawner.spawn(port_task(event_receiver, port).expect("Failed to create controller task")); spawner.spawn( @@ -239,7 +213,7 @@ async fn power_policy_psu_task( #[embassy_executor::task] async fn type_c_service_task( service: &'static Mutex, - event_receiver: ServiceEventReceiver<'static, PowerPolicyReceiverType>, + event_receiver: TypeCServiceEventReceiverType, ) { info!("Starting type-c task"); type_c_service::task::task(service, event_receiver).await; diff --git a/examples/std/src/bin/type_c/ucsi.rs b/examples/std/src/bin/type_c/ucsi.rs index 0262d5fd..3797ce49 100644 --- a/examples/std/src/bin/type_c/ucsi.rs +++ b/examples/std/src/bin/type_c/ucsi.rs @@ -6,7 +6,7 @@ use embassy_sync::mutex::Mutex; use embassy_sync::once_lock::OnceLock; use embassy_sync::pubsub::{DynImmediatePublisher, DynSubscriber, PubSubChannel}; use embedded_services::IntrusiveList; -use embedded_services::event::MapSender; +use embedded_services::event::{MapSender, NoopSender}; use embedded_services::{GlobalRawMutex, event}; use embedded_usb_pd::ucsi::lpm::get_connector_capability::OperationModeFlags; use embedded_usb_pd::ucsi::ppm::ack_cc_ci::Ack; @@ -24,26 +24,16 @@ use static_cell::StaticCell; use std_examples::type_c::mock_controller::{self, InterruptReceiver, Port}; use type_c_interface::controller::ControllerId; use type_c_interface::port::event::PortEventBitfield; -use type_c_interface::port::{Device, PortRegistration}; -use type_c_interface::service::context::Context; use type_c_interface::service::event::{PortEvent as ServicePortEvent, PortEventData as ServicePortEventData}; -use type_c_service::bridge::Bridge; -use type_c_service::bridge::event_receiver::EventReceiver as BridgeEventReceiver; use type_c_service::controller::event::Event as PortEvent; use type_c_service::controller::event_receiver::InterruptReceiver as _; use type_c_service::controller::event_receiver::{EventReceiver as PortEventReceiver, PortEventSplitter}; use type_c_service::controller::macros::PortComponents; use type_c_service::controller::state::SharedState; use type_c_service::define_controller_port_static_cell_channel; +use type_c_service::service::Service; use type_c_service::service::config::Config; -use type_c_service::service::{EventReceiver as ServiceEventReceiver, Service}; - -const CHANNEL_CAPACITY: usize = 4; -const NUM_PD_CONTROLLERS: usize = 2; -const CONTROLLER0_ID: ControllerId = ControllerId(0); -const CONTROLLER1_ID: ControllerId = ControllerId(1); -const PORT0_ID: GlobalPortId = GlobalPortId(0); -const PORT1_ID: GlobalPortId = GlobalPortId(1); +use type_c_service::service::registration::PortData; type ControllerType = Mutex>; type PortType = Mutex>; @@ -67,7 +57,19 @@ type PowerPolicyServiceType = Mutex< >, >; -type ServiceType = Service<'static>; +const PORT_COUNT: usize = 2; +type TypeCServiceSenderType = NoopSender; +type PortReceiverType = DynamicReceiver<'static, type_c_interface::service::event::PortEventData>; +type TypeCServiceEventReceiverType = type_c_service::service::event_receiver::ArrayEventReceiver< + 'static, + PORT_COUNT, + PortType, + PortReceiverType, + PowerPolicyReceiverType, +>; +type TypeCRegistrationType = + type_c_service::service::registration::ArrayRegistration<'static, PortType, PORT_COUNT, TypeCServiceSenderType, 1>; +type TypeCServiceType = Service<'static, TypeCRegistrationType>; type SharedStateType = Mutex; type PortEventReceiverType = PortEventReceiver< 'static, @@ -77,7 +79,7 @@ type PortEventReceiverType = PortEventReceiver< >; #[embassy_executor::task] -async fn opm_task(_context: &'static Context, _state: [&'static mock_controller::ControllerState; NUM_PD_CONTROLLERS]) { +async fn opm_task(_state: [&'static mock_controller::ControllerState; PORT_COUNT]) { // TODO: migrate this logic to an integration test when we move away from 'static lifetimes. /*const CAPABILITY: PowerCapability = PowerCapability { voltage_mv: 20000, @@ -208,18 +210,6 @@ async fn opm_task(_context: &'static Context, _state: [&'static mock_controller: }*/ } -#[embassy_executor::task(pool_size = 2)] -async fn bridge_task( - mut event_receiver: BridgeEventReceiver, - mut bridge: Bridge<'static, Mutex>>, -) -> ! { - loop { - let event = event_receiver.wait_next().await; - let output = bridge.process_event(event).await; - event_receiver.finalize(output); - } -} - #[embassy_executor::task(pool_size = 2)] async fn port_task(mut event_receiver: PortEventReceiverType, port: &'static PortType) { loop { @@ -252,8 +242,8 @@ async fn power_policy_task( #[embassy_executor::task] async fn type_c_service_task( - service: &'static Mutex, - event_receiver: ServiceEventReceiver<'static, PowerPolicyReceiverType>, + service: &'static Mutex, + event_receiver: TypeCServiceEventReceiverType, ) { type_c_service::task::task(service, event_receiver).await; } @@ -264,26 +254,10 @@ async fn task(spawner: Spawner) { embedded_services::init().await; - static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); - let controller_context = CONTROLLER_CONTEXT.init(Context::new()); - static STATE0: StaticCell = StaticCell::new(); let state0 = STATE0.init(mock_controller::ControllerState::new()); static CONTROLLER0: StaticCell = StaticCell::new(); - let controller0 = CONTROLLER0.init(Mutex::new(mock_controller::Controller::new(state0))); - - static PORT_CHANNEL0: Channel = Channel::new(); - static PORT_REGISTRATION0: StaticCell<[PortRegistration; 1]> = StaticCell::new(); - let port_registration0 = PORT_REGISTRATION0.init([PortRegistration { - id: PORT0_ID, - sender: PORT_CHANNEL0.dyn_sender(), - receiver: PORT_CHANNEL0.dyn_receiver(), - }]); - - static PD_REGISTRATION0: StaticCell> = StaticCell::new(); - let pd_registration0 = PD_REGISTRATION0.init(Device::new(CONTROLLER0_ID, port_registration0)); - - controller_context.register_controller(pd_registration0).unwrap(); + let controller0 = CONTROLLER0.init(Mutex::new(mock_controller::Controller::new(state0, "Controller0"))); define_controller_port_static_cell_channel!(pub(self), port0, GlobalRawMutex, Mutex>); let PortComponents { @@ -291,35 +265,13 @@ async fn task(spawner: Spawner) { power_policy_receiver: policy_receiver0, event_receiver: event_receiver0, interrupt_sender: port0_interrupt_sender, - } = port0::create( - "PD0", - LocalPortId(0), - PORT0_ID, - Default::default(), - controller0, - controller_context, - ); - - let bridge_receiver0 = BridgeEventReceiver::new(pd_registration0); - let bridge0 = Bridge::new(controller0, pd_registration0); + type_c_receiver: type_c_receiver0, + } = port0::create("PD0", LocalPortId(0), Default::default(), controller0); static STATE1: StaticCell = StaticCell::new(); let state1 = STATE1.init(mock_controller::ControllerState::new()); static CONTROLLER1: StaticCell = StaticCell::new(); - let controller1 = CONTROLLER1.init(Mutex::new(mock_controller::Controller::new(state1))); - - static PORT1_CHANNEL: Channel = Channel::new(); - static PORT_REGISTRATION1: StaticCell<[PortRegistration; 1]> = StaticCell::new(); - let port_registration1 = PORT_REGISTRATION1.init([PortRegistration { - id: PORT1_ID, - sender: PORT1_CHANNEL.dyn_sender(), - receiver: PORT1_CHANNEL.dyn_receiver(), - }]); - - static PD_REGISTRATION1: StaticCell> = StaticCell::new(); - let pd_registration1 = PD_REGISTRATION1.init(Device::new(CONTROLLER1_ID, port_registration1)); - - controller_context.register_controller(pd_registration1).unwrap(); + let controller1 = CONTROLLER1.init(Mutex::new(mock_controller::Controller::new(state1, "Controller1"))); define_controller_port_static_cell_channel!(pub(self), port1, GlobalRawMutex, Mutex>); let PortComponents { @@ -327,17 +279,8 @@ async fn task(spawner: Spawner) { power_policy_receiver: policy_receiver1, event_receiver: event_receiver1, interrupt_sender: port1_interrupt_sender, - } = port1::create( - "PD1", - LocalPortId(0), - PORT1_ID, - Default::default(), - controller1, - controller_context, - ); - - let bridge_receiver1 = BridgeEventReceiver::new(pd_registration1); - let bridge1 = Bridge::new(controller1, pd_registration1); + type_c_receiver: type_c_receiver1, + } = port1::create("PD1", LocalPortId(0), Default::default(), controller1); // Create power policy service // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot @@ -364,7 +307,7 @@ async fn task(spawner: Spawner) { ))); // Create type-c service - static TYPE_C_SERVICE: StaticCell> = StaticCell::new(); + static TYPE_C_SERVICE: StaticCell> = StaticCell::new(); let type_c_service = TYPE_C_SERVICE.init(Mutex::new(Service::create( Config { ucsi_capabilities: UcsiCapabilities { @@ -390,7 +333,18 @@ async fn task(spawner: Spawner) { ), ..Default::default() }, - controller_context, + TypeCRegistrationType { + ports: [port0, port1], + service_senders: [NoopSender], + port_data: [ + PortData { + local_port: Some(LocalPortId(0)), + }, + PortData { + local_port: Some(LocalPortId(1)), + }, + ], + }, ))); spawner.spawn( @@ -404,12 +358,14 @@ async fn task(spawner: Spawner) { spawner.spawn( type_c_service_task( type_c_service, - ServiceEventReceiver::new(controller_context, power_policy_subscriber), + TypeCServiceEventReceiverType::new( + [port0, port1], + [type_c_receiver0, type_c_receiver1], + power_policy_subscriber, + ), ) .expect("Failed to create type-c service task"), ); - spawner.spawn(bridge_task(bridge_receiver0, bridge0).expect("Failed to create bridge0 task")); - spawner.spawn(bridge_task(bridge_receiver1, bridge1).expect("Failed to create bridge1 task")); spawner.spawn(port_task(event_receiver0, port0).expect("Failed to create wrapper0 task")); spawner.spawn( interrupt_splitter_task( @@ -426,7 +382,7 @@ async fn task(spawner: Spawner) { ) .expect("Failed to create interrupt splitter 1 task"), ); - spawner.spawn(opm_task(controller_context, [state0, state1]).expect("Failed to create opm task")); + spawner.spawn(opm_task([state0, state1]).expect("Failed to create opm task")); } fn main() { diff --git a/examples/std/src/bin/type_c/unconstrained.rs b/examples/std/src/bin/type_c/unconstrained.rs index 8b119146..03969074 100644 --- a/examples/std/src/bin/type_c/unconstrained.rs +++ b/examples/std/src/bin/type_c/unconstrained.rs @@ -1,5 +1,4 @@ use embassy_executor::{Executor, Spawner}; -use embassy_sync::channel::Channel; use embassy_sync::channel::DynamicReceiver; use embassy_sync::channel::DynamicSender; use embassy_sync::mutex::Mutex; @@ -7,7 +6,8 @@ use embassy_sync::pubsub::{DynImmediatePublisher, DynSubscriber, PubSubChannel}; use embassy_time::Timer; use embedded_services::GlobalRawMutex; use embedded_services::event::MapSender; -use embedded_usb_pd::{GlobalPortId, LocalPortId}; +use embedded_services::event::NoopSender; +use embedded_usb_pd::LocalPortId; use log::*; use power_policy_interface::capability::PowerCapability; use power_policy_interface::charger::mock::ChargerType; @@ -17,30 +17,14 @@ use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use std_examples::type_c::mock_controller::Port; use std_examples::type_c::mock_controller::{self, InterruptReceiver}; -use type_c_interface::controller::ControllerId; -use type_c_interface::port::Device; -use type_c_interface::port::PortRegistration; use type_c_interface::port::event::PortEventBitfield; -use type_c_interface::service::event::PortEvent as ServicePortEvent; -use type_c_service::bridge::Bridge; -use type_c_service::bridge::event_receiver::EventReceiver as BridgeEventReceiver; use type_c_service::controller::event_receiver::InterruptReceiver as _; use type_c_service::controller::event_receiver::{EventReceiver as PortEventReceiver, PortEventSplitter}; use type_c_service::controller::macros::PortComponents; use type_c_service::controller::state::SharedState; use type_c_service::define_controller_port_static_cell_channel; -use type_c_service::service::{EventReceiver as ServiceEventReceiver, Service}; - -const CHANNEL_CAPACITY: usize = 4; - -const CONTROLLER0_ID: ControllerId = ControllerId(0); -const PORT0_ID: GlobalPortId = GlobalPortId(0); - -const CONTROLLER1_ID: ControllerId = ControllerId(1); -const PORT1_ID: GlobalPortId = GlobalPortId(1); - -const CONTROLLER2_ID: ControllerId = ControllerId(2); -const PORT2_ID: GlobalPortId = GlobalPortId(2); +use type_c_service::service::Service; +use type_c_service::service::registration::PortData; const DELAY_MS: u64 = 1000; @@ -66,7 +50,19 @@ type PowerPolicyServiceType = Mutex< >, >; -type ServiceType = Service<'static>; +const PORT_COUNT: usize = 3; +type TypeCServiceSenderType = NoopSender; +type PortReceiverType = DynamicReceiver<'static, type_c_interface::service::event::PortEventData>; +type TypeCServiceEventReceiverType = type_c_service::service::event_receiver::ArrayEventReceiver< + 'static, + PORT_COUNT, + PortType, + PortReceiverType, + PowerPolicyReceiverType, +>; +type TypeCRegistrationType = + type_c_service::service::registration::ArrayRegistration<'static, PortType, PORT_COUNT, TypeCServiceSenderType, 1>; +type TypeCServiceType = Service<'static, TypeCRegistrationType>; type SharedStateType = Mutex; type PortEventReceiverType = PortEventReceiver< 'static, @@ -75,18 +71,6 @@ type PortEventReceiverType = PortEventReceiver< DynamicReceiver<'static, type_c_service::controller::event::Loopback>, >; -#[embassy_executor::task(pool_size = 3)] -async fn bridge_task( - mut event_receiver: BridgeEventReceiver, - mut bridge: Bridge<'static, Mutex>>, -) -> ! { - loop { - let event = event_receiver.wait_next().await; - let output = bridge.process_event(event).await; - event_receiver.finalize(output); - } -} - #[embassy_executor::task(pool_size = 3)] async fn port_task(mut event_receiver: PortEventReceiverType, port: &'static PortType) { loop { @@ -113,27 +97,10 @@ async fn interrupt_splitter_task( async fn task(spawner: Spawner) { embedded_services::init().await; - // Create power policy service - static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); - let controller_context = CONTROLLER_CONTEXT.init(type_c_interface::service::context::Context::new()); - static STATE0: StaticCell = StaticCell::new(); let state0 = STATE0.init(mock_controller::ControllerState::new()); static CONTROLLER0: StaticCell = StaticCell::new(); - let controller0 = CONTROLLER0.init(Mutex::new(mock_controller::Controller::new(state0))); - - static PORT_CHANNEL0: Channel = Channel::new(); - static PORT_REGISTRATION0: StaticCell<[PortRegistration; 1]> = StaticCell::new(); - let port_registration0 = PORT_REGISTRATION0.init([PortRegistration { - id: PORT0_ID, - sender: PORT_CHANNEL0.dyn_sender(), - receiver: PORT_CHANNEL0.dyn_receiver(), - }]); - - static PD_REGISTRATION0: StaticCell> = StaticCell::new(); - let pd_registration0 = PD_REGISTRATION0.init(Device::new(CONTROLLER0_ID, port_registration0)); - - controller_context.register_controller(pd_registration0).unwrap(); + let controller0 = CONTROLLER0.init(Mutex::new(mock_controller::Controller::new(state0, "Controller0"))); define_controller_port_static_cell_channel!(pub(self), port0, GlobalRawMutex, Mutex>); let PortComponents { @@ -141,34 +108,13 @@ async fn task(spawner: Spawner) { power_policy_receiver: policy_receiver0, event_receiver: event_receiver0, interrupt_sender: port0_interrupt_sender, - } = port0::create( - "PD0", - LocalPortId(0), - PORT0_ID, - Default::default(), - controller0, - controller_context, - ); - let bridge_receiver0 = BridgeEventReceiver::new(pd_registration0); - let bridge0 = Bridge::new(controller0, pd_registration0); + type_c_receiver: type_c_receiver0, + } = port0::create("PD0", LocalPortId(0), Default::default(), controller0); static STATE1: StaticCell = StaticCell::new(); let state1 = STATE1.init(mock_controller::ControllerState::new()); static CONTROLLER1: StaticCell = StaticCell::new(); - let controller1 = CONTROLLER1.init(Mutex::new(mock_controller::Controller::new(state1))); - - static PORT1_CHANNEL: Channel = Channel::new(); - static PORT_REGISTRATION1: StaticCell<[PortRegistration; 1]> = StaticCell::new(); - let port_registration1 = PORT_REGISTRATION1.init([PortRegistration { - id: PORT1_ID, - sender: PORT1_CHANNEL.dyn_sender(), - receiver: PORT1_CHANNEL.dyn_receiver(), - }]); - - static PD_REGISTRATION1: StaticCell> = StaticCell::new(); - let pd_registration1 = PD_REGISTRATION1.init(Device::new(CONTROLLER1_ID, port_registration1)); - - controller_context.register_controller(pd_registration1).unwrap(); + let controller1 = CONTROLLER1.init(Mutex::new(mock_controller::Controller::new(state1, "Controller1"))); define_controller_port_static_cell_channel!(pub(self), port1, GlobalRawMutex, Mutex>); let PortComponents { @@ -176,34 +122,13 @@ async fn task(spawner: Spawner) { power_policy_receiver: policy_receiver1, event_receiver: event_receiver1, interrupt_sender: port1_interrupt_sender, - } = port1::create( - "PD1", - LocalPortId(0), - PORT1_ID, - Default::default(), - controller1, - controller_context, - ); - let bridge_receiver1 = BridgeEventReceiver::new(pd_registration1); - let bridge1 = Bridge::new(controller1, pd_registration1); + type_c_receiver: type_c_receiver1, + } = port1::create("PD1", LocalPortId(0), Default::default(), controller1); static STATE2: StaticCell = StaticCell::new(); let state2 = STATE2.init(mock_controller::ControllerState::new()); static CONTROLLER2: StaticCell = StaticCell::new(); - let controller2 = CONTROLLER2.init(Mutex::new(mock_controller::Controller::new(state2))); - - static PORT2_CHANNEL: Channel = Channel::new(); - static PORT_REGISTRATION2: StaticCell<[PortRegistration; 1]> = StaticCell::new(); - let port_registration2 = PORT_REGISTRATION2.init([PortRegistration { - id: PORT2_ID, - sender: PORT2_CHANNEL.dyn_sender(), - receiver: PORT2_CHANNEL.dyn_receiver(), - }]); - - static PD_REGISTRATION2: StaticCell> = StaticCell::new(); - let pd_registration2 = PD_REGISTRATION2.init(Device::new(CONTROLLER2_ID, port_registration2)); - - controller_context.register_controller(pd_registration2).unwrap(); + let controller2 = CONTROLLER2.init(Mutex::new(mock_controller::Controller::new(state2, "Controller2"))); define_controller_port_static_cell_channel!(pub(self), port2, GlobalRawMutex, Mutex>); let PortComponents { @@ -211,16 +136,8 @@ async fn task(spawner: Spawner) { power_policy_receiver: policy_receiver2, event_receiver: event_receiver2, interrupt_sender: port2_interrupt_sender, - } = port2::create( - "PD2", - LocalPortId(0), - PORT2_ID, - Default::default(), - controller2, - controller_context, - ); - let bridge_receiver2 = BridgeEventReceiver::new(pd_registration2); - let bridge2 = Bridge::new(controller2, pd_registration2); + type_c_receiver: type_c_receiver2, + } = port2::create("PD2", LocalPortId(0), Default::default(), controller2); // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot static POWER_POLICY_CHANNEL: StaticCell< @@ -246,8 +163,25 @@ async fn task(spawner: Spawner) { ))); // Create type-c service - static TYPE_C_SERVICE: StaticCell> = StaticCell::new(); - let type_c_service = TYPE_C_SERVICE.init(Mutex::new(Service::create(Default::default(), controller_context))); + static TYPE_C_SERVICE: StaticCell> = StaticCell::new(); + let type_c_service = TYPE_C_SERVICE.init(Mutex::new(Service::create( + Default::default(), + TypeCRegistrationType { + ports: [port0, port1, port2], + service_senders: [NoopSender], + port_data: [ + PortData { + local_port: Some(LocalPortId(0)), + }, + PortData { + local_port: Some(LocalPortId(1)), + }, + PortData { + local_port: Some(LocalPortId(2)), + }, + ], + }, + ))); spawner.spawn( power_policy_task( @@ -262,14 +196,15 @@ async fn task(spawner: Spawner) { spawner.spawn( type_c_service_task( type_c_service, - ServiceEventReceiver::new(controller_context, power_policy_subscriber), + TypeCServiceEventReceiverType::new( + [port0, port1, port2], + [type_c_receiver0, type_c_receiver1, type_c_receiver2], + power_policy_subscriber, + ), ) .expect("Failed to create type-c service task"), ); - spawner.spawn(bridge_task(bridge_receiver0, bridge0).expect("Failed to create bridge0 task")); - spawner.spawn(bridge_task(bridge_receiver1, bridge1).expect("Failed to create bridge1 task")); - spawner.spawn(bridge_task(bridge_receiver2, bridge2).expect("Failed to create bridge2 task")); spawner.spawn(port_task(event_receiver0, port0).expect("Failed to create controller0 task")); spawner.spawn( interrupt_splitter_task( @@ -354,8 +289,8 @@ async fn power_policy_task( #[embassy_executor::task] async fn type_c_service_task( - service: &'static Mutex, - event_receiver: ServiceEventReceiver<'static, PowerPolicyReceiverType>, + service: &'static Mutex, + event_receiver: TypeCServiceEventReceiverType, ) { info!("Starting type-c task"); type_c_service::task::task(service, event_receiver).await; diff --git a/examples/std/src/lib/type_c/mock_controller.rs b/examples/std/src/lib/type_c/mock_controller.rs index 5acab89d..206857cd 100644 --- a/examples/std/src/lib/type_c/mock_controller.rs +++ b/examples/std/src/lib/type_c/mock_controller.rs @@ -2,6 +2,7 @@ use std::num::NonZeroU8; use embassy_sync::{channel, mutex::Mutex, signal::Signal}; use embedded_services::GlobalRawMutex; +use embedded_services::named::Named; use embedded_usb_pd::ado::Ado; use embedded_usb_pd::{LocalPortId, PdError}; use embedded_usb_pd::{PowerRole, type_c::Current}; @@ -107,11 +108,12 @@ impl Default for ControllerState { pub struct Controller<'a> { state: &'a ControllerState, + name: &'static str, } impl<'a> Controller<'a> { - pub fn new(state: &'a ControllerState) -> Self { - Self { state } + pub fn new(state: &'a ControllerState, name: &'static str) -> Self { + Self { state, name } } /// Function to demonstrate calling functions directly on the controller @@ -133,6 +135,12 @@ impl type_c_service::controller::event_receiver::InterruptReceiv } } +impl Named for Controller<'_> { + fn name(&self) -> &'static str { + self.name + } +} + impl type_c_interface::controller::Controller for Controller<'_> { async fn reset_controller(&mut self) -> Result<(), PdError> { debug!("Reset controller"); @@ -316,6 +324,7 @@ pub type Port<'a> = type_c_service::controller::Port< 'a, Mutex>, Mutex, + channel::DynamicSender<'a, type_c_interface::service::event::PortEventData>, channel::DynamicSender<'a, power_policy_interface::psu::event::EventData>, channel::DynamicSender<'a, type_c_service::controller::event::Loopback>, >; diff --git a/type-c-interface/Cargo.toml b/type-c-interface/Cargo.toml index 8b11bc79..27857480 100644 --- a/type-c-interface/Cargo.toml +++ b/type-c-interface/Cargo.toml @@ -10,8 +10,6 @@ ignored = ["log"] [dependencies] bitfield.workspace = true -embassy-sync.workspace = true -embassy-time.workspace = true log = { workspace = true, optional = true } defmt = { workspace = true, optional = true } embedded-services.workspace = true @@ -25,16 +23,8 @@ workspace = true 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", -] +log = ["dep:log", "embedded-services/log", "power-policy-interface/log"] diff --git a/type-c-interface/src/controller/mod.rs b/type-c-interface/src/controller/mod.rs index d149d5da..9268ba28 100644 --- a/type-c-interface/src/controller/mod.rs +++ b/type-c-interface/src/controller/mod.rs @@ -1,5 +1,6 @@ //! Module for PD controller related code +use embedded_services::named::Named; use embedded_usb_pd::PdError; pub mod electrical_disconnect; @@ -15,7 +16,7 @@ pub mod type_c; pub struct ControllerId(pub u8); /// PD controller trait -pub trait Controller { +pub trait Controller: Named { /// Reset the controller fn reset_controller(&mut self) -> impl Future>; } diff --git a/type-c-interface/src/controller/pd.rs b/type-c-interface/src/controller/pd.rs index f909f484..4e20afd9 100644 --- a/type-c-interface/src/controller/pd.rs +++ b/type-c-interface/src/controller/pd.rs @@ -1,3 +1,4 @@ +use embedded_services::named::Named; use embedded_usb_pd::{LocalPortId, PdError, ado::Ado}; use crate::control::{ @@ -9,7 +10,7 @@ use crate::control::{ }; /// Trait for basic functionality from the PD spec. -pub trait Pd { +pub trait Pd: Named { /// Returns the port status fn get_port_status(&mut self, port: LocalPortId) -> impl Future>; diff --git a/type-c-interface/src/controller/power.rs b/type-c-interface/src/controller/power.rs index 238ce82c..c485258d 100644 --- a/type-c-interface/src/controller/power.rs +++ b/type-c-interface/src/controller/power.rs @@ -1,7 +1,8 @@ +use embedded_services::named::Named; use embedded_usb_pd::{LocalPortId, PdError}; /// System power state related controller functionality -pub trait SystemPowerStateStatus { +pub trait SystemPowerStateStatus: Named { /// Set the system power state on the given port. /// /// This notifies the PD controller of the current system power state, diff --git a/type-c-interface/src/controller/retimer.rs b/type-c-interface/src/controller/retimer.rs index 843a53b7..e676141d 100644 --- a/type-c-interface/src/controller/retimer.rs +++ b/type-c-interface/src/controller/retimer.rs @@ -1,9 +1,10 @@ +use embedded_services::named::Named; use embedded_usb_pd::{LocalPortId, PdError}; use crate::control::retimer::RetimerFwUpdateState; /// Retimer-related functionality -pub trait Retimer { +pub trait Retimer: Named { /// Returns the retimer fw update state fn get_rt_fw_update_status( &mut self, diff --git a/type-c-interface/src/port/event.rs b/type-c-interface/src/port/event.rs index 312680fd..d46c76b1 100644 --- a/type-c-interface/src/port/event.rs +++ b/type-c-interface/src/port/event.rs @@ -6,7 +6,8 @@ //! Consequently [`PortNotificationEventBitfield`] implements iterator traits to allow for processing these events as a stream. use bitfield::bitfield; -use crate::port::{AttnVdm, OtherVdm}; +use crate::control::vdm::AttnVdm; +use crate::control::vdm::OtherVdm; bitfield! { /// Raw bitfield of possible port status events diff --git a/type-c-interface/src/port/mod.rs b/type-c-interface/src/port/mod.rs index ffc8e8bb..7920f6db 100644 --- a/type-c-interface/src/port/mod.rs +++ b/type-c-interface/src/port/mod.rs @@ -1,11 +1,4 @@ -//! PD controller related code -use embassy_sync::channel::{DynamicReceiver, DynamicSender}; -use embedded_usb_pd::ucsi::lpm; -use embedded_usb_pd::{GlobalPortId, LocalPortId, PdError, ado::Ado}; - -use embedded_services::ipc::deferred; -use embedded_services::{GlobalRawMutex, intrusive_list}; - +//! Type-C port related code pub mod electrical_disconnect; pub mod event; pub mod max_sink_voltage; @@ -13,219 +6,3 @@ pub mod pd; pub mod power; pub mod retimer; pub mod type_c; -use crate::control::dp::{DpConfig, DpStatus}; -use crate::control::pd::PdStateMachineConfig; -use crate::control::retimer::RetimerFwUpdateState; -use crate::control::tbt::TbtConfig; -use crate::control::type_c::TypeCStateMachineState; -use crate::control::usb::UsbControlConfig; -use crate::control::vdm::{AttnVdm, OtherVdm, SendVdm}; -use crate::controller::ControllerId; -use crate::service::event::PortEvent as ServicePortEvent; - -/// Port-specific command data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PortCommandData { - /// 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, - /// 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, -} - -/// Port-specific response data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PortResponseData { - /// Command completed with no error - Complete, - /// 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, - /// 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), -} - -/// Controller-specific response data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InternalResponseData { - /// Command complete - Complete, -} - -/// Response for controller-specific commands -pub type InternalResponse = Result; - -/// PD controller command response -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Response { - /// Controller response - Controller(InternalResponse), - /// Port response - Port(PortResponse), -} - -/// Per-port registration info -pub struct PortRegistration { - /// Global port ID of the port - pub id: GlobalPortId, - /// Event receiver for the type-C service - pub receiver: DynamicReceiver<'static, ServicePortEvent>, - /// Event sender for the type-C service - pub sender: DynamicSender<'static, ServicePortEvent>, -} - -/// PD controller -pub struct Device<'a> { - node: intrusive_list::Node, - id: ControllerId, - pub ports: &'a [PortRegistration], - 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 [PortRegistration]) -> 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)?.id) - } - - /// Convert a global port ID to a local port ID - pub fn lookup_local_port(&self, port: GlobalPortId) -> Result { - self.ports - .iter() - .position(|descriptor| descriptor.id == 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> { - self.command.receive().await - } - - /// Number of ports on this controller - pub fn num_ports(&self) -> usize { - self.num_ports - } -} diff --git a/type-c-interface/src/port/pd.rs b/type-c-interface/src/port/pd.rs index 2f00ca68..547f948b 100644 --- a/type-c-interface/src/port/pd.rs +++ b/type-c-interface/src/port/pd.rs @@ -1,3 +1,4 @@ +use embedded_services::named::Named; use embedded_usb_pd::{PdError, ado::Ado}; use crate::control::{ @@ -9,7 +10,7 @@ use crate::control::{ }; /// Trait for basic functionality from the PD spec. -pub trait Pd { +pub trait Pd: Named { /// Returns the port status fn get_port_status(&mut self) -> impl Future>; diff --git a/type-c-interface/src/port/power.rs b/type-c-interface/src/port/power.rs index 4701328c..afdb3b10 100644 --- a/type-c-interface/src/port/power.rs +++ b/type-c-interface/src/port/power.rs @@ -1,7 +1,8 @@ +use embedded_services::named::Named; use embedded_usb_pd::PdError; /// System power state related controller functionality -pub trait SystemPowerStateStatus { +pub trait SystemPowerStateStatus: Named { /// Set the system power state on this port. /// /// This notifies the PD controller of the current system power state, diff --git a/type-c-interface/src/port/retimer.rs b/type-c-interface/src/port/retimer.rs index d64962c2..c79831c9 100644 --- a/type-c-interface/src/port/retimer.rs +++ b/type-c-interface/src/port/retimer.rs @@ -1,9 +1,10 @@ +use embedded_services::named::Named; use embedded_usb_pd::PdError; use crate::control::retimer::RetimerFwUpdateState; /// Retimer-related functionality -pub trait Retimer { +pub trait Retimer: Named { /// Returns the retimer fw update state fn get_rt_fw_update_status(&mut self) -> impl Future>; /// Set retimer fw update state diff --git a/type-c-interface/src/service/context.rs b/type-c-interface/src/service/context.rs deleted file mode 100644 index 4d221783..00000000 --- a/type-c-interface/src/service/context.rs +++ /dev/null @@ -1,433 +0,0 @@ -use embassy_time::{Duration, with_timeout}; -use embedded_usb_pd::ucsi::lpm; -use embedded_usb_pd::{GlobalPortId, PdError}; - -use crate::control::dp::{DpConfig, DpStatus}; -use crate::control::pd::PdStateMachineConfig; -use crate::control::retimer::RetimerFwUpdateState; -use crate::control::tbt::TbtConfig; -use crate::control::type_c::TypeCStateMachineState; -use crate::control::usb::UsbControlConfig; -use crate::control::vdm::{AttnVdm, OtherVdm, SendVdm}; -use crate::controller::ControllerId; -use crate::port::{ - Command, Device, InternalCommandData, InternalResponseData, PortCommand, PortCommandData, PortResponseData, - Response, -}; -use crate::service; -use crate::service::event::{Event, PortEvent}; -use embedded_services::{IntrusiveNode, broadcaster::immediate as broadcaster, error, intrusive_list}; - -/// 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 { - /// Event broadcaster - broadcaster: broadcaster::Immediate, - /// Controller list - pub controllers: intrusive_list::IntrusiveList, -} - -impl Default for Context { - fn default() -> Self { - Self::new() - } -} - -impl Context { - /// Create new Context - pub const fn new() -> Self { - Self { - broadcaster: broadcaster::Immediate::new(), - controllers: intrusive_list::IntrusiveList::new(), - } - } - - /// 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 { - 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 { - 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 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 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), - } - } - - /// 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(()), - } - } - - /// 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()) - } - - pub async fn send_port_event(&self, event: PortEvent) -> Result<(), PdError> { - let node = self.find_node_by_port(event.port)?; - - node.data::() - .ok_or(PdError::InvalidController)? - .ports - .iter() - .find(|descriptor| descriptor.id == event.port) - .ok_or(PdError::InvalidPort)? - .sender - .send(event) - .await; - Ok(()) - } -} diff --git a/type-c-interface/src/service/event.rs b/type-c-interface/src/service/event.rs index e8ca1bf1..e20f6bf2 100644 --- a/type-c-interface/src/service/event.rs +++ b/type-c-interface/src/service/event.rs @@ -1,10 +1,14 @@ //! Comms service message definitions +use embedded_services::sync::Lockable; use embedded_usb_pd::{GlobalPortId, ado::Ado}; use crate::{ control::{dp::DpStatus, pd::PortStatus}, - port::event::{PortStatusEventBitfield, VdmData}, + port::{ + event::{PortStatusEventBitfield, VdmData}, + pd::Pd, + }, }; /// Struct containing data for a [`PortEventData::StatusChanged`] event @@ -40,17 +44,15 @@ pub enum PortEventData { /// Struct containing a complete port event #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PortEvent { - pub port: GlobalPortId, +pub struct PortEvent<'port, Port: Lockable> { + pub port: &'port Port, pub event: PortEventData, } /// Message generated when a debug accessory is connected or disconnected #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DebugAccessory { - /// Port - pub port: GlobalPortId, +pub struct DebugAccessoryData { /// Connected pub connected: bool, } @@ -58,7 +60,7 @@ pub struct DebugAccessory { /// UCSI connector change message #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct UsciChangeIndicator { +pub struct UsciChangeIndicatorData { /// Port pub port: GlobalPortId, /// Notify OPM @@ -68,9 +70,24 @@ pub struct UsciChangeIndicator { /// Top-level comms message #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Event { - /// Debug accessory message - DebugAccessory(DebugAccessory), - /// UCSI CCI message - UcsiCci(UsciChangeIndicator), +pub enum EventData { + DebugAccessory(DebugAccessoryData), + UsciChangeIndicator(UsciChangeIndicatorData), +} + +/// Top-level comms message +#[derive(Copy, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Event<'port, Port: Lockable> { + pub port: &'port Port, + pub event: EventData, +} + +impl<'port, Port: Lockable> Clone for Event<'port, Port> { + fn clone(&self) -> Self { + Self { + port: self.port, + event: self.event, + } + } } diff --git a/type-c-interface/src/service/mod.rs b/type-c-interface/src/service/mod.rs index 23749388..53f11265 100644 --- a/type-c-interface/src/service/mod.rs +++ b/type-c-interface/src/service/mod.rs @@ -1,2 +1 @@ -pub mod context; pub mod event; diff --git a/type-c-interface/src/ucsi.rs b/type-c-interface/src/ucsi.rs index 2ed23175..536d5d69 100644 --- a/type-c-interface/src/ucsi.rs +++ b/type-c-interface/src/ucsi.rs @@ -1,7 +1,8 @@ +use embedded_services::named::Named; use embedded_usb_pd::{PdError, ucsi::lpm}; /// UCSI LPM command execution trait -pub trait Lpm { +pub trait Lpm: Named { /// Execute the given LPM command fn execute_lpm_command( &mut self, diff --git a/type-c-service/src/bridge/event_receiver.rs b/type-c-service/src/bridge/event_receiver.rs deleted file mode 100644 index da1b4be1..00000000 --- a/type-c-service/src/bridge/event_receiver.rs +++ /dev/null @@ -1,32 +0,0 @@ -use embedded_services::{GlobalRawMutex, ipc::deferred}; -use type_c_interface::port; - -pub type ControllerCommand<'a> = deferred::Request<'a, GlobalRawMutex, port::Command, port::Response>; - -/// Controller command output data -pub struct OutputControllerCommand<'a> { - /// Controller request - pub request: ControllerCommand<'a>, - /// Response - pub response: port::Response, -} - -pub struct EventReceiver { - /// PD controller - pub pd_controller: &'static port::Device<'static>, -} - -impl EventReceiver { - /// Create a new instance - pub fn new(pd_controller: &'static port::Device<'static>) -> Self { - Self { pd_controller } - } - - pub fn wait_next(&mut self) -> impl Future> { - self.pd_controller.receive() - } - - pub fn finalize(&mut self, output: OutputControllerCommand<'static>) { - output.request.respond(output.response); - } -} diff --git a/type-c-service/src/bridge/mod.rs b/type-c-service/src/bridge/mod.rs deleted file mode 100644 index a4e8bf7f..00000000 --- a/type-c-service/src/bridge/mod.rs +++ /dev/null @@ -1,147 +0,0 @@ -//! Temporary bridge between a controller and the type-C service - -use embedded_services::{debug, sync::Lockable}; -use embedded_usb_pd::{PdError, ucsi::lpm}; -use type_c_interface::controller::{ - Controller, - pd::{Pd, StateMachine as PdStateMachine}, - retimer::Retimer, - type_c::StateMachine as TypeCStateMachine, -}; -use type_c_interface::port::{self, InternalResponseData, Response}; -use type_c_interface::ucsi::Lpm as UcsiLpm; - -use crate::bridge::event_receiver::{ControllerCommand, OutputControllerCommand}; -pub mod event_receiver; - -pub struct Bridge<'device, C: Lockable> -where - C::Inner: Controller + Pd + PdStateMachine + Retimer + TypeCStateMachine + UcsiLpm, -{ - controller: &'device C, - registration: &'static port::Device<'static>, -} - -impl<'device, C: Lockable> Bridge<'device, C> -where - C::Inner: Controller + Pd + PdStateMachine + Retimer + TypeCStateMachine + UcsiLpm, -{ - pub fn new(controller: &'device C, registration: &'static port::Device<'static>) -> Self { - Self { - controller, - registration, - } - } - - /// Handle a port command - pub async fn process_port_command(&mut self, command: &port::PortCommand) -> Response { - let local_port = if let Ok(port) = self.registration.lookup_local_port(command.port) { - port - } else { - debug!("Invalid port: {:?}", command.port); - return port::Response::Port(Err(PdError::InvalidPort)); - }; - - let mut controller = self.controller.lock().await; - port::Response::Port(match command.data { - port::PortCommandData::RetimerFwUpdateGetState => controller - .get_rt_fw_update_status(local_port) - .await - .map(port::PortResponseData::RtFwUpdateStatus), - port::PortCommandData::RetimerFwUpdateSetState => controller - .set_rt_fw_update_state(local_port) - .await - .map(|_| port::PortResponseData::Complete), - port::PortCommandData::RetimerFwUpdateClearState => controller - .clear_rt_fw_update_state(local_port) - .await - .map(|_| port::PortResponseData::Complete), - port::PortCommandData::SetRetimerCompliance => controller - .set_rt_compliance(local_port) - .await - .map(|_| port::PortResponseData::Complete), - port::PortCommandData::ReconfigureRetimer => controller - .reconfigure_retimer(local_port) - .await - .map(|_| port::PortResponseData::Complete), - // This command isn't sent by the type-C service, disable it for the transition - port::PortCommandData::SetMaxSinkVoltage(_) => Ok(port::PortResponseData::Complete), - port::PortCommandData::SetUnconstrainedPower(unconstrained) => controller - .set_unconstrained_power(local_port, unconstrained) - .await - .map(|_| port::PortResponseData::Complete), - port::PortCommandData::ClearDeadBatteryFlag => controller - .clear_dead_battery_flag(local_port) - .await - .map(|_| port::PortResponseData::Complete), - port::PortCommandData::GetOtherVdm => controller.get_other_vdm(local_port).await.map(|vdm| { - debug!("Port{}: Other VDM: {:?}", local_port.0, vdm); - port::PortResponseData::OtherVdm(vdm) - }), - port::PortCommandData::GetAttnVdm => controller.get_attn_vdm(local_port).await.map(|vdm| { - debug!("Port{}: Attention VDM: {:?}", local_port.0, vdm); - port::PortResponseData::AttnVdm(vdm) - }), - port::PortCommandData::SendVdm(tx_vdm) => controller - .send_vdm(local_port, tx_vdm) - .await - .map(|_| port::PortResponseData::Complete), - port::PortCommandData::SetUsbControl(config) => controller - .set_usb_control(local_port, config) - .await - .map(|_| port::PortResponseData::Complete), - port::PortCommandData::GetDpStatus => controller.get_dp_status(local_port).await.map(|status| { - debug!("Port{}: DP Status: {:?}", local_port.0, status); - port::PortResponseData::DpStatus(status) - }), - port::PortCommandData::SetDpConfig(config) => controller - .set_dp_config(local_port, config) - .await - .map(|_| port::PortResponseData::Complete), - port::PortCommandData::ExecuteDrst => controller - .execute_drst(local_port) - .await - .map(|_| port::PortResponseData::Complete), - port::PortCommandData::SetTbtConfig(config) => controller - .set_tbt_config(local_port, config) - .await - .map(|_| port::PortResponseData::Complete), - port::PortCommandData::SetPdStateMachineConfig(config) => controller - .set_pd_state_machine_config(local_port, config) - .await - .map(|_| port::PortResponseData::Complete), - port::PortCommandData::SetTypeCStateMachineConfig(state) => controller - .set_type_c_state_machine_config(local_port, state) - .await - .map(|_| port::PortResponseData::Complete), - port::PortCommandData::ExecuteUcsiCommand(command_data) => Ok(port::PortResponseData::UcsiResponse( - controller - .execute_lpm_command(lpm::Command::new(local_port, command_data)) - .await, - )), - }) - } - - pub async fn process_controller_command(&mut self, command: &port::InternalCommandData) -> Response { - let mut controller = self.controller.lock().await; - match command { - port::InternalCommandData::SyncState => port::Response::Controller(Ok(InternalResponseData::Complete)), - port::InternalCommandData::Reset => { - let result = controller.reset_controller().await; - port::Response::Controller(result.map(|_| InternalResponseData::Complete)) - } - } - } - - /// Handle a PD controller command - pub async fn process_event(&mut self, command: ControllerCommand<'static>) -> OutputControllerCommand<'static> { - let response = match command.command { - port::Command::Port(command) => self.process_port_command(&command).await, - port::Command::Controller(command) => self.process_controller_command(&command).await, - }; - OutputControllerCommand { - request: command, - response, - } - } -} diff --git a/type-c-service/src/controller/electrical_disconnect.rs b/type-c-service/src/controller/electrical_disconnect.rs index d4d59df6..8ef5499b 100644 --- a/type-c-service/src/controller/electrical_disconnect.rs +++ b/type-c-service/src/controller/electrical_disconnect.rs @@ -12,10 +12,11 @@ impl< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, > type_c_interface::port::electrical_disconnect::ElectricalDisconnect - for Port<'device, C, Shared, PowerSender, LoopbackSender> + for Port<'device, C, Shared, TypeCSender, PowerSender, LoopbackSender> { async fn execute_electrical_disconnect(&mut self, reconnect_time_s: Option) -> Result<(), PdError> { self.controller diff --git a/type-c-service/src/controller/macros.rs b/type-c-service/src/controller/macros.rs index 8d27f94e..0fe30d21 100644 --- a/type-c-service/src/controller/macros.rs +++ b/type-c-service/src/controller/macros.rs @@ -7,6 +7,7 @@ use type_c_interface::port::event::PortEventBitfield; use crate::controller::{event_receiver::EventReceiver, state}; pub const DEFAULT_POWER_POLICY_CHANNEL_SIZE: usize = 2; +pub const DEFAULT_TYPE_C_CHANNEL_SIZE: usize = 2; pub const DEFAULT_LOOPBACK_CHANNEL_SIZE: usize = 1; pub const DEFAULT_INTERRUPT_CHANNEL_SIZE: usize = 4; @@ -15,6 +16,7 @@ pub struct PortComponents< 'a, Port, SharedState: Lockable, + TypeCReceiver: Receiver, PowerPolicyReceveiver: Receiver, LoopbackReceiver: Receiver, InterruptReceiver: Receiver, @@ -22,6 +24,8 @@ pub struct PortComponents< > { /// Port instance pub port: &'a Port, + /// Type-C service event receiver + pub type_c_receiver: TypeCReceiver, /// Power policy event receiver pub power_policy_receiver: PowerPolicyReceveiver, /// Port event receiver @@ -46,6 +50,11 @@ macro_rules! define_controller_port_static_cell_channel { pub type InnerPowerPolicyReceiverType = ::embassy_sync::channel::DynamicReceiver<'static, ::power_policy_interface::psu::event::EventData>; + /// Type alias for the type-c service event sender + pub type InnerTypeCSenderType = ::embassy_sync::channel::DynamicSender<'static, ::type_c_interface::service::event::PortEventData>; + /// Type alias for the type-c service event receiver + pub type InnerTypeCReceiverType = ::embassy_sync::channel::DynamicReceiver<'static, ::type_c_interface::service::event::PortEventData>; + /// Type alias for the loopback sender pub type InnerLoopbackSenderType = ::embassy_sync::channel::DynamicSender<'static, $crate::controller::event::Loopback>; @@ -72,6 +81,8 @@ macro_rules! define_controller_port_static_cell_channel { $controller, // Shared state type InnerSharedStateType, + // Type-C service event sender type + InnerTypeCSenderType, // Power policy event sender type InnerPowerPolicySenderType, // Loopback event sender type @@ -79,6 +90,14 @@ macro_rules! define_controller_port_static_cell_channel { >, >; + /// Channel to send events to the type-c service + static TYPE_C_CHANNEL: ::static_cell::StaticCell< + ::embassy_sync::channel::Channel< + $mutex, + ::type_c_interface::service::event::PortEventData, + { $crate::controller::macros::DEFAULT_TYPE_C_CHANNEL_SIZE }, + >, + > = ::static_cell::StaticCell::new(); /// Channel to send events to the power policy service static POWER_POLICY_CHANNEL: ::static_cell::StaticCell< ::embassy_sync::channel::Channel< @@ -113,14 +132,13 @@ macro_rules! define_controller_port_static_cell_channel { pub fn create( name: &'static str, port: ::embedded_usb_pd::LocalPortId, - global_port: ::embedded_usb_pd::GlobalPortId, config: $crate::controller::config::Config, controller: &'static $controller, - context: &'static type_c_interface::service::context::Context, ) -> $crate::controller::macros::PortComponents< 'static, InnerPortType, InnerSharedStateType, + InnerTypeCReceiverType, InnerPowerPolicyReceiverType, InnerLoopbackReceiverType, InnerInterruptReceiverType, @@ -134,6 +152,10 @@ macro_rules! define_controller_port_static_cell_channel { let power_policy_sender = power_policy_channel.dyn_sender(); let power_policy_receiver = power_policy_channel.dyn_receiver(); + let type_c_channel = TYPE_C_CHANNEL.init(::embassy_sync::channel::Channel::new()); + let type_c_sender = type_c_channel.dyn_sender(); + let type_c_receiver = type_c_channel.dyn_receiver(); + let loopback_channel = LOOPBACK_CHANNEL.init(::embassy_sync::channel::Channel::new()); let loopback_sender = loopback_channel.dyn_sender(); let loopback_receiver = loopback_channel.dyn_receiver(); @@ -146,12 +168,11 @@ macro_rules! define_controller_port_static_cell_channel { name, config, port, - global_port, controller, shared_state, + type_c_sender, power_policy_sender, loopback_sender, - context, ))); let event_receiver = $crate::controller::event_receiver::EventReceiver::new( shared_state, @@ -160,6 +181,7 @@ macro_rules! define_controller_port_static_cell_channel { ); $crate::controller::macros::PortComponents { port, + type_c_receiver, power_policy_receiver, event_receiver, interrupt_sender, diff --git a/type-c-service/src/controller/max_sink_voltage.rs b/type-c-service/src/controller/max_sink_voltage.rs index 8cee7725..78bd4059 100644 --- a/type-c-service/src/controller/max_sink_voltage.rs +++ b/type-c-service/src/controller/max_sink_voltage.rs @@ -10,9 +10,11 @@ impl< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, -> type_c_interface::port::max_sink_voltage::MaxSinkVoltage for Port<'device, C, Shared, PowerSender, LoopbackSender> +> type_c_interface::port::max_sink_voltage::MaxSinkVoltage + for Port<'device, C, Shared, TypeCSender, PowerSender, LoopbackSender> { async fn set_max_sink_voltage(&mut self, voltage_mv: Option) -> Result<(), PdError> { self.controller diff --git a/type-c-service/src/controller/mod.rs b/type-c-service/src/controller/mod.rs index bc739d27..e81cff9b 100644 --- a/type-c-service/src/controller/mod.rs +++ b/type-c-service/src/controller/mod.rs @@ -1,14 +1,12 @@ //! Struct that manages per-port state, interfacing with a controller object that exposes multiple ports. use embedded_services::{debug, error, event::Sender, info, named::Named, sync::Lockable}; -use embedded_usb_pd::{GlobalPortId, LocalPortId, PdError}; +use embedded_usb_pd::{LocalPortId, PdError}; use power_policy_interface::psu::PsuState; use type_c_interface::control::pd::PortStatus; use type_c_interface::controller::pd::Pd; use type_c_interface::port::event::PortEventBitfield; use type_c_interface::port::{event::PortEvent as InterfacePortEvent, event::PortStatusEventBitfield}; -use type_c_interface::service::event::{ - PortEvent as ServicePortEvent, PortEventData as ServicePortEventData, StatusChangedData, -}; +use type_c_interface::service::event::{PortEventData as ServicePortEventData, StatusChangedData}; use crate::controller::event::{Event, Loopback}; use crate::controller::state::SharedState; @@ -30,13 +28,12 @@ pub struct Port< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, > { /// Local port port: LocalPortId, - /// Global port - global_port: GlobalPortId, /// Controller controller: &'device C, /// Per-port PSU state @@ -45,14 +42,14 @@ pub struct Port< name: &'static str, /// Cached port status status: PortStatus, + /// Sender for type-c service events + type_c_sender: TypeCSender, /// Sender for power policy events power_policy_sender: PowerSender, /// Configuration config: config::Config, /// Shared state shared_state: &'device Shared, - /// Type-C service context - context: &'device type_c_interface::service::context::Context, /// Loopback sender loopback_sender: LoopbackSender, } @@ -61,36 +58,35 @@ impl< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, -> Port<'device, C, Shared, PowerSender, LoopbackSender> +> Port<'device, C, Shared, TypeCSender, PowerSender, LoopbackSender> { /// Create new Port instance - // Argument count will be reduced as the last bit of refactoring is done + // TODO: refactor arguments into a registration struct #[allow(clippy::too_many_arguments)] pub fn new( name: &'static str, config: config::Config, port: LocalPortId, - global_port: GlobalPortId, controller: &'device C, shared_state: &'device Shared, + type_c_sender: TypeCSender, power_policy_sender: PowerSender, loopback_sender: LoopbackSender, - context: &'device type_c_interface::service::context::Context, ) -> Self { Self { name, controller, port, - global_port, status: PortStatus::default(), psu_state: power_policy_interface::psu::State::default(), power_policy_sender, config, shared_state, - context, loopback_sender, + type_c_sender, } } @@ -153,12 +149,7 @@ impl< current_status: new_status, }); self.status = new_status; - self.context - .send_port_event(ServicePortEvent { - port: self.global_port, - event, - }) - .await?; + self.type_c_sender.send(event).await; Ok(event) } @@ -227,9 +218,10 @@ impl< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, -> Named for Port<'device, C, Shared, PowerSender, LoopbackSender> +> Named for Port<'device, C, Shared, TypeCSender, PowerSender, LoopbackSender> { fn name(&self) -> &'static str { self.name diff --git a/type-c-service/src/controller/pd.rs b/type-c-service/src/controller/pd.rs index 0cfe2bc6..2091c924 100644 --- a/type-c-service/src/controller/pd.rs +++ b/type-c-service/src/controller/pd.rs @@ -11,7 +11,7 @@ use type_c_interface::control::{ }; use type_c_interface::controller::pd::StateMachine; use type_c_interface::port::event::{VdmData, VdmNotification}; -use type_c_interface::service::event::{PortEvent as ServicePortEvent, PortEventData as ServicePortEventData}; +use type_c_interface::service::event::PortEventData as ServicePortEventData; use super::*; use crate::controller::state::SharedState; @@ -20,9 +20,10 @@ impl< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, -> Port<'device, C, Shared, PowerSender, LoopbackSender> +> Port<'device, C, Shared, TypeCSender, PowerSender, LoopbackSender> { /// Process a VDM event by retrieving the relevant VDM data from the `controller` for the appropriate `port`. pub(super) async fn process_vdm_event(&mut self, event: VdmNotification) -> Result { @@ -38,13 +39,7 @@ impl< }; let event = ServicePortEventData::Vdm(vdm_data); - let _ = self - .context - .send_port_event(ServicePortEvent { - port: self.global_port, - event: ServicePortEventData::Vdm(vdm_data), - }) - .await; + self.type_c_sender.send(event).await; Ok(event) } @@ -53,13 +48,7 @@ impl< debug!("({}): Processing DP status update event", self.name); let status = self.controller.lock().await.get_dp_status(self.port).await?; let event = ServicePortEventData::DpStatusUpdate(status); - let _ = self - .context - .send_port_event(ServicePortEvent { - port: self.global_port, - event, - }) - .await; + self.type_c_sender.send(event).await; Ok(event) } @@ -68,13 +57,7 @@ impl< debug!("({}): PD alert: {:#?}", self.name, ado); if let Some(ado) = ado { let event = ServicePortEventData::Alert(ado); - let _ = self - .context - .send_port_event(ServicePortEvent { - port: self.global_port, - event, - }) - .await; + self.type_c_sender.send(event).await; Ok(Some(event)) } else { // For some reason we didn't read an alert, nothing to do @@ -87,9 +70,10 @@ impl< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, -> type_c_interface::port::pd::Pd for Port<'device, C, Shared, PowerSender, LoopbackSender> +> type_c_interface::port::pd::Pd for Port<'device, C, Shared, TypeCSender, PowerSender, LoopbackSender> { async fn get_port_status(&mut self) -> Result { self.controller.lock().await.get_port_status(self.port).await @@ -152,9 +136,10 @@ impl< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, -> type_c_interface::port::pd::StateMachine for Port<'device, C, Shared, PowerSender, LoopbackSender> +> type_c_interface::port::pd::StateMachine for Port<'device, C, Shared, TypeCSender, PowerSender, LoopbackSender> { async fn set_pd_state_machine_config(&mut self, config: PdStateMachineConfig) -> Result<(), PdError> { self.controller diff --git a/type-c-service/src/controller/power.rs b/type-c-service/src/controller/power.rs index 9e477e41..8d48d381 100644 --- a/type-c-service/src/controller/power.rs +++ b/type-c-service/src/controller/power.rs @@ -19,9 +19,10 @@ impl< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, -> Port<'device, C, Shared, PowerSender, LoopbackSender> +> Port<'device, C, Shared, TypeCSender, PowerSender, LoopbackSender> { /// Handle a new contract as consumer pub(super) async fn process_new_consumer_contract(&mut self, new_status: &PortStatus) -> Result<(), PdError> { @@ -114,9 +115,10 @@ impl< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, -> Psu for Port<'device, C, Shared, PowerSender, LoopbackSender> +> Psu for Port<'device, C, Shared, TypeCSender, PowerSender, LoopbackSender> { async fn disconnect(&mut self) -> Result<(), PsuError> { self.controller @@ -169,9 +171,11 @@ impl< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, -> type_c_interface::port::power::SystemPowerStateStatus for Port<'device, C, Shared, PowerSender, LoopbackSender> +> type_c_interface::port::power::SystemPowerStateStatus + for Port<'device, C, Shared, TypeCSender, PowerSender, LoopbackSender> { async fn set_system_power_state_status( &mut self, diff --git a/type-c-service/src/controller/retimer.rs b/type-c-service/src/controller/retimer.rs index cdb9a4a0..f7710169 100644 --- a/type-c-service/src/controller/retimer.rs +++ b/type-c-service/src/controller/retimer.rs @@ -11,9 +11,10 @@ impl< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, -> type_c_interface::port::retimer::Retimer for Port<'device, C, Shared, PowerSender, LoopbackSender> +> type_c_interface::port::retimer::Retimer for Port<'device, C, Shared, TypeCSender, PowerSender, LoopbackSender> { async fn get_rt_fw_update_status(&mut self) -> Result { self.controller.lock().await.get_rt_fw_update_status(self.port).await diff --git a/type-c-service/src/controller/type_c.rs b/type-c-service/src/controller/type_c.rs index 663212e6..30bd84c8 100644 --- a/type-c-service/src/controller/type_c.rs +++ b/type-c-service/src/controller/type_c.rs @@ -11,9 +11,10 @@ impl< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, -> type_c_interface::port::type_c::StateMachine for Port<'device, C, Shared, PowerSender, LoopbackSender> +> type_c_interface::port::type_c::StateMachine for Port<'device, C, Shared, TypeCSender, PowerSender, LoopbackSender> { async fn set_type_c_state_machine_config(&mut self, state: TypeCStateMachineState) -> Result<(), PdError> { self.controller diff --git a/type-c-service/src/controller/ucsi.rs b/type-c-service/src/controller/ucsi.rs index b899720b..3dfdbf6d 100644 --- a/type-c-service/src/controller/ucsi.rs +++ b/type-c-service/src/controller/ucsi.rs @@ -1,6 +1,6 @@ //! UCSI LPM port trait implementation use embedded_services::{event::Sender, sync::Lockable}; -use embedded_usb_pd::PdError; +use embedded_usb_pd::{PdError, ucsi::lpm}; use type_c_interface::ucsi::Lpm as UcsiLpm; use super::*; @@ -10,14 +10,12 @@ impl< 'device, C: Lockable, Shared: Lockable, + TypeCSender: Sender, PowerSender: Sender, LoopbackSender: Sender, -> type_c_interface::ucsi::Lpm for Port<'device, C, Shared, PowerSender, LoopbackSender> +> type_c_interface::ucsi::Lpm for Port<'device, C, Shared, TypeCSender, PowerSender, LoopbackSender> { - async fn execute_lpm_command( - &mut self, - command: embedded_usb_pd::ucsi::lpm::LocalCommand, - ) -> Result, PdError> { + async fn execute_lpm_command(&mut self, command: lpm::LocalCommand) -> Result, PdError> { self.controller.lock().await.execute_lpm_command(command).await } } diff --git a/type-c-service/src/lib.rs b/type-c-service/src/lib.rs index 9e42b200..2ee1d648 100644 --- a/type-c-service/src/lib.rs +++ b/type-c-service/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -pub mod bridge; pub mod controller; pub mod driver; pub mod service; diff --git a/type-c-service/src/service/event_receiver.rs b/type-c-service/src/service/event_receiver.rs new file mode 100644 index 00000000..b048fc9a --- /dev/null +++ b/type-c-service/src/service/event_receiver.rs @@ -0,0 +1,100 @@ +use core::pin::pin; + +use crate::service::Event; +use embassy_futures::select::{Either, select, select_slice}; +use embedded_services::{event::Receiver, sync::Lockable}; +use power_policy_interface::service::event::EventData as PowerPolicyEventData; +use type_c_interface::{port::pd::Pd, service::event::PortEvent}; + +struct PowerPolicySubscriber> { + receiver: PowerReceiver, +} + +impl> PowerPolicySubscriber { + /// Wait for a power policy event + async fn wait_next(&mut self) -> PowerPolicyEventData { + self.receiver.wait_next().await + } +} + +pub struct ArrayPortReceivers< + 'port, + const N: usize, + Port: Lockable, + PortReceiver: Receiver, +> { + ports: [&'port Port; N], + port_receivers: [PortReceiver; N], +} + +impl< + 'port, + const N: usize, + Port: Lockable, + PortReceiver: Receiver, +> ArrayPortReceivers<'port, N, Port, PortReceiver> +{ + /// Get the next pending PSU event + pub async fn wait_next(&mut self) -> Event<'port, Port> { + let ((event, port), _) = { + let mut futures = heapless::Vec::<_, N>::new(); + for (receiver, psu) in self.port_receivers.iter_mut().zip(self.ports.iter()) { + // Push will never fail since the number of receivers is the same as the capacity of the vector + let _ = futures.push(async move { (receiver.wait_next().await, psu) }); + } + select_slice(pin!(&mut futures)).await + }; + + Event::PortEvent(PortEvent { port: *port, event }) + } +} + +/// Struct used to contain port event receivers and manage mapping from a receiver to its corresponding device. +pub struct ArrayEventReceiver< + 'a, + const N: usize, + Port: Lockable, + PortReceiver: Receiver, + PowerReceiver: Receiver, +> { + /// Power policy event subscriber + power_policy_event_subscriber: PowerPolicySubscriber, + /// Port event receivers and corresponding ports + port_receivers: ArrayPortReceivers<'a, N, Port, PortReceiver>, +} + +impl< + 'port, + const N: usize, + Port: Lockable, + PortReceiver: Receiver, + PowerReceiver: Receiver, +> ArrayEventReceiver<'port, N, Port, PortReceiver, PowerReceiver> +{ + /// Create a new instance + pub fn new( + ports: [&'port Port; N], + port_receivers: [PortReceiver; N], + power_policy_event_receiver: PowerReceiver, + ) -> Self { + Self { + port_receivers: ArrayPortReceivers { ports, port_receivers }, + power_policy_event_subscriber: PowerPolicySubscriber { + receiver: power_policy_event_receiver, + }, + } + } + + /// Wait for the next event, whether it's a port event or a power policy event + pub async fn wait_next(&mut self) -> Event<'port, Port> { + match select( + self.port_receivers.wait_next(), + self.power_policy_event_subscriber.wait_next(), + ) + .await + { + Either::First(event) => event, + Either::Second(event) => Event::PowerPolicy(event), + } + } +} diff --git a/type-c-service/src/service/mod.rs b/type-c-service/src/service/mod.rs index a0ff5ec8..699a4c27 100644 --- a/type-c-service/src/service/mod.rs +++ b/type-c-service/src/service/mod.rs @@ -1,156 +1,152 @@ -use core::cell::RefCell; -use core::future::pending; -use core::pin::pin; +use core::marker::PhantomData; +use core::ptr; -use embassy_futures::select::select_slice; -use embassy_futures::select::{Either, select}; -use embedded_services::{debug, error, event::Receiver, info, trace}; +use embedded_services::event::Sender as _; +use embedded_services::named::Named as _; +use embedded_services::sync::Lockable; +use embedded_services::{debug, error, info, trace}; use embedded_usb_pd::GlobalPortId; use embedded_usb_pd::PdError as Error; use power_policy_interface::service::event::EventData as PowerPolicyEventData; use type_c_interface::control::pd::PortStatus; -use type_c_interface::service::event::{PortEvent, PortEventData}; +use type_c_interface::port::pd::Pd; +use type_c_interface::service::event::{DebugAccessoryData, EventData, PortEvent, PortEventData}; -use type_c_interface::port::Device; use type_c_interface::port::event::PortStatusEventBitfield; -use type_c_interface::service::event; +use type_c_interface::service::event::Event as ServiceEvent; + +use crate::service::registration::Registration; pub mod config; +pub mod event_receiver; mod power; +pub mod registration; mod ucsi; -pub mod vdm; - -const MAX_SUPPORTED_PORTS: usize = 4; - -/// Type-C service state -#[derive(Default)] -struct State { - /// Current port status - port_status: [PortStatus; MAX_SUPPORTED_PORTS], - /// UCSI state - ucsi: ucsi::State, -} /// Type-C service /// /// Constructing a Service is the first step in using the Type-C service. /// Arguments should be an initialized context -pub struct Service<'a> { - /// Type-C context - pub(crate) context: &'a type_c_interface::service::context::Context, - /// Current state - state: State, +pub struct Service<'port, Reg: Registration<'port>> { + /// UCSI state + ucsi: ucsi::State, /// Config config: config::Config, -} - -/// Power policy events -// 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), - /// Consumer disconnected - ConsumerDisconnected, - /// Consumer connected - ConsumerConnected, + /// Service registration + registration: Reg, + _phantom: PhantomData<&'port ()>, } /// Type-C service events -#[derive(Copy, Clone)] -pub enum Event { +#[derive(Clone)] +pub enum Event<'port, Port: Lockable> { /// Port event - PortEvent(PortEvent), + PortEvent(PortEvent<'port, Port>), /// Power policy event - PowerPolicy(PowerPolicyEvent), + PowerPolicy(PowerPolicyEventData), } -impl<'a> Service<'a> { +impl<'port, Reg: Registration<'port>> Service<'port, Reg> { /// Create a new service the given configuration - pub fn create(config: config::Config, context: &'a type_c_interface::service::context::Context) -> Self { + pub fn create(config: config::Config, registration: Reg) -> Self { Self { - context, - state: State::default(), + ucsi: ucsi::State::default(), config, + registration, + _phantom: PhantomData, } } - /// Get the cached port status - pub fn get_cached_port_status(&self, port_id: GlobalPortId) -> Result { - Ok(*self - .state - .port_status + fn get_port_index(&self, port: &'port Reg::Port) -> Result { + self.registration + .ports() + .iter() + .position(|p| ptr::eq(*p, port)) + .ok_or(Error::InvalidPort) + } + + /// Look up the port for a given global port ID + fn lookup_port(&self, port_id: GlobalPortId) -> Result<&'port Reg::Port, Error> { + self.registration + .ports() .get(port_id.0 as usize) - .ok_or(Error::InvalidPort)?) + .ok_or(Error::InvalidPort) + .copied() } - /// Set the cached port status - fn set_cached_port_status(&mut self, port_id: GlobalPortId, status: PortStatus) -> Result<(), Error> { - *self - .state - .port_status - .get_mut(port_id.0 as usize) - .ok_or(Error::InvalidPort)? = status; - Ok(()) + /// Send an event to all registered listeners + async fn broadcast_event(&mut self, event: ServiceEvent<'port, Reg::Port>) { + for sender in self.registration.event_senders() { + sender.send(event.clone()).await; + } } /// Process events for a specific port async fn process_port_status_event( &mut self, - port_id: GlobalPortId, + port: &'port Reg::Port, event: PortStatusEventBitfield, - status: PortStatus, + new_status: PortStatus, + old_status: PortStatus, ) -> Result<(), Error> { - let old_status = self.get_cached_port_status(port_id)?; + let port_name = { port.lock().await.name() }; - debug!("Port{}: Event: {:#?}", port_id.0, event); - debug!("Port{} Previous status: {:#?}", port_id.0, old_status); - debug!("Port{} Status: {:#?}", port_id.0, status); + debug!("({}): Event: {:#?}", port_name, event); + debug!("({}) Previous status: {:#?}", port_name, old_status); + debug!("({}) Status: {:#?}", port_name, new_status); - let connection_changed = status.is_connected() != old_status.is_connected(); - if connection_changed && (status.is_debug_accessory() || old_status.is_debug_accessory()) { + let connection_changed = new_status.is_connected() != old_status.is_connected(); + if connection_changed && (new_status.is_debug_accessory() || old_status.is_debug_accessory()) { // Notify that a debug connection has connected/disconnected - if status.is_connected() { - debug!("Port{}: Debug accessory connected", port_id.0); + if new_status.is_connected() { + debug!("({}): Debug accessory connected", port_name); } else { - debug!("Port{}: Debug accessory disconnected", port_id.0); + debug!("({}): Debug accessory disconnected", port_name); } - self.context - .broadcast_message(event::Event::DebugAccessory(event::DebugAccessory { - port: port_id, - connected: status.is_connected(), - })) - .await; + self.broadcast_event(ServiceEvent { + port, + event: EventData::DebugAccessory(DebugAccessoryData { + connected: new_status.is_connected(), + }), + }) + .await; } - self.set_cached_port_status(port_id, status)?; - self.handle_ucsi_port_event(port_id, event, &status).await; + self.handle_ucsi_port_event(port, GlobalPortId(self.get_port_index(port)? as u8), event, &new_status) + .await; Ok(()) } - async fn process_port_event(&mut self, event: &PortEvent) -> Result<(), Error> { + async fn process_port_event(&mut self, event: &PortEvent<'port, Reg::Port>) -> Result<(), Error> { match &event.event { PortEventData::StatusChanged(status_event) => { - self.process_port_status_event(event.port, status_event.status_event, status_event.current_status) - .await + self.process_port_status_event( + event.port, + status_event.status_event, + status_event.current_status, + status_event.previous_status, + ) + .await } unhandled => { // Currently just log notifications, but may want to do more in the future - debug!("Port{}: Received notification event: {:#?}", event.port.0, unhandled); + debug!( + "({}): Received notification event: {:#?}", + event.port.lock().await.name(), + unhandled + ); Ok(()) } } } /// Process the given event - pub async fn process_event(&mut self, event: Event) -> Result<(), Error> { + pub async fn process_event(&mut self, event: Event<'port, Reg::Port>) -> Result<(), Error> { match event { Event::PortEvent(event) => { - trace!("Port{}: Processing port event", event.port.0); + trace!("({}): Processing port event", event.port.lock().await.name()); self.process_port_event(&event).await } Event::PowerPolicy(event) => { @@ -160,76 +156,3 @@ impl<'a> Service<'a> { } } } - -/// Event receiver for the Type-C service -pub struct EventReceiver<'a, PowerReceiver: Receiver> { - /// Type-C context - pub(crate) context: &'a type_c_interface::service::context::Context, - /// Power policy event subscriber - /// - /// Used to allow partial borrows of Self for the call to select - power_policy_event_subscriber: RefCell, -} - -impl<'a, PowerReceiver: Receiver> EventReceiver<'a, PowerReceiver> { - /// Create a new event receiver - pub fn new( - context: &'a type_c_interface::service::context::Context, - power_policy_event_subscriber: PowerReceiver, - ) -> Self { - Self { - context, - power_policy_event_subscriber: RefCell::new(power_policy_event_subscriber), - } - } - - /// Wait for the next event - pub async fn wait_next(&mut self) -> Event { - match select(self.wait_port_event(), self.wait_power_policy_event()).await { - Either::First(event) => event, - Either::Second(event) => event, - } - } - - /// Wait for a port event - async fn wait_port_event(&self) -> Event { - let (event, _) = { - let mut futures = heapless::Vec::<_, MAX_SUPPORTED_PORTS>::new(); - for device in self.context.controllers.iter_only::() { - for descriptor in device.ports.iter() { - let _ = futures.push(async move { descriptor.receiver.receive().await }); - } - } - select_slice(pin!(&mut futures)).await - }; - - Event::PortEvent(event) - } - - /// Wait for a power policy event - #[allow(clippy::await_holding_refcell_ref)] - async fn wait_power_policy_event(&self) -> Event { - let Ok(mut subscriber) = self.power_policy_event_subscriber.try_borrow_mut() else { - // This should never happen because this function is not public and is only called from wait_next, which takes &mut self - error!("Attempt to call `wait_power_policy_event` simultaneously"); - return pending().await; - }; - - loop { - match subscriber.wait_next().await { - power_policy_interface::service::event::EventData::Unconstrained(state) => { - return Event::PowerPolicy(PowerPolicyEvent::Unconstrained(state)); - } - power_policy_interface::service::event::EventData::ConsumerDisconnected => { - return Event::PowerPolicy(PowerPolicyEvent::ConsumerDisconnected); - } - power_policy_interface::service::event::EventData::ConsumerConnected(_) => { - return Event::PowerPolicy(PowerPolicyEvent::ConsumerConnected); - } - _ => { - // No other events currently implemented - } - } - } - } -} diff --git a/type-c-service/src/service/power.rs b/type-c-service/src/service/power.rs index 4ec9e380..5e443713 100644 --- a/type-c-service/src/service/power.rs +++ b/type-c-service/src/service/power.rs @@ -1,14 +1,17 @@ +use core::ptr; + +use embedded_services::sync::Lockable as _; use power_policy_interface::service as power_policy; +use power_policy_interface::service::event::EventData as PowerPolicyEventData; +use type_c_interface::port::pd::Pd as _; use super::*; -impl Service<'_> { +impl<'a, Reg: Registration<'a>> Service<'a, Reg> { /// Set the unconstrained state for all ports pub(super) async fn set_unconstrained_all(&mut self, unconstrained: bool) -> Result<(), Error> { - for port_index in 0..self.context.get_num_ports() { - self.context - .set_unconstrained_power(GlobalPortId(port_index as u8), unconstrained) - .await?; + for port in self.registration.ports() { + port.lock().await.set_unconstrained_power(unconstrained).await?; } Ok(()) } @@ -28,25 +31,27 @@ impl Service<'_> { 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(); - let unconstrained_port = self - .state - .port_status - .iter() - .take(num_ports) - .position(|status| status.available_sink_contract.is_some() && status.unconstrained_power); + let mut unconstrained_port = None; + for port in self.registration.ports().iter() { + let status = port.lock().await.get_port_status().await?; + if status.available_sink_contract.is_some() && status.unconstrained_power { + unconstrained_port = Some(*port); + break; + } + } - if let Some(unconstrained_index) = unconstrained_port { + if let Some(unconstrained_port) = unconstrained_port { // One of our ports is the unconstrained consumer // If it switches to sourcing then the system will no longer be unconstrained // So set that port to constrained and unconstrain all others info!( - "Setting port{} to constrained, all others unconstrained", - unconstrained_index + "Setting port ({}) to constrained, all others unconstrained", + unconstrained_port.lock().await.name() ); - for port_index in 0..num_ports { - self.context - .set_unconstrained_power(GlobalPortId(port_index as u8), port_index != unconstrained_index) + for port in self.registration.ports().iter() { + port.lock() + .await + .set_unconstrained_power(!ptr::eq(*port, unconstrained_port)) .await?; } } else { @@ -66,21 +71,22 @@ impl Service<'_> { } /// Process power policy events - pub(super) async fn process_power_policy_event(&mut self, message: &PowerPolicyEvent) -> Result<(), Error> { + pub(super) async fn process_power_policy_event(&mut self, message: &PowerPolicyEventData) -> Result<(), Error> { match message { - PowerPolicyEvent::Unconstrained(state) => self.process_unconstrained_state_change(state).await, - PowerPolicyEvent::ConsumerDisconnected => { - self.state.ucsi.psu_connected = false; + PowerPolicyEventData::Unconstrained(state) => self.process_unconstrained_state_change(state).await, + PowerPolicyEventData::ConsumerDisconnected => { + self.ucsi.psu_connected = false; // Notify OPM because this can affect battery charging capability status self.pend_ucsi_connected_ports().await; Ok(()) } - PowerPolicyEvent::ConsumerConnected => { - self.state.ucsi.psu_connected = true; + PowerPolicyEventData::ConsumerConnected(_) => { + self.ucsi.psu_connected = true; // Notify OPM because this can affect battery charging capability status self.pend_ucsi_connected_ports().await; Ok(()) } + _ => Ok(()), // Other events don't require any action from the service } } } diff --git a/type-c-service/src/service/registration.rs b/type-c-service/src/service/registration.rs new file mode 100644 index 00000000..815b151c --- /dev/null +++ b/type-c-service/src/service/registration.rs @@ -0,0 +1,67 @@ +//! Code related to registration with the type-C service + +use embedded_services::{event::Sender, sync::Lockable}; +use embedded_usb_pd::{GlobalPortId, LocalPortId}; +use type_c_interface::port::pd::Pd; +use type_c_interface::service::event::Event as ServiceEvent; +use type_c_interface::ucsi::Lpm as UcsiLpm; + +/// Registration trait that abstracts over various registration details. +pub trait Registration<'port> { + type Port: Lockable + 'port; + type ServiceSender: Sender>; + + /// Returns a slice to access ports + fn ports(&self) -> &[&'port Self::Port]; + /// Returns a slice to access type-c event senders + fn event_senders(&mut self) -> &mut [Self::ServiceSender]; + /// Returns the ucsi local port ID for a given global port + fn ucsi_local_port_id(&self, global_port: GlobalPortId) -> Option; +} + +pub struct PortData { + /// local port ID + pub local_port: Option, +} + +/// A registration implementation based around arrays +pub struct ArrayRegistration< + 'port, + Port: Lockable + 'port, + const PORT_COUNT: usize, + ServiceSender: Sender>, + const SERVICE_SENDER_COUNT: usize, +> { + /// Array of registered ports + pub ports: [&'port Port; PORT_COUNT], + /// Array of local port data + pub port_data: [PortData; PORT_COUNT], + /// Array of service event senders + pub service_senders: [ServiceSender; SERVICE_SENDER_COUNT], +} + +impl< + 'port, + Port: Lockable + 'port, + const PORT_COUNT: usize, + ServiceSender: Sender>, + const SERVICE_SENDER_COUNT: usize, +> Registration<'port> for ArrayRegistration<'port, Port, PORT_COUNT, ServiceSender, SERVICE_SENDER_COUNT> +{ + type Port = Port; + type ServiceSender = ServiceSender; + + fn event_senders(&mut self) -> &mut [Self::ServiceSender] { + &mut self.service_senders + } + + fn ports(&self) -> &[&'port Self::Port] { + &self.ports + } + + fn ucsi_local_port_id(&self, global_port: GlobalPortId) -> Option { + self.port_data + .get(global_port.0 as usize) + .and_then(|data| data.local_port) + } +} diff --git a/type-c-service/src/service/ucsi.rs b/type-c-service/src/service/ucsi.rs index 9b75db63..ce34a84d 100644 --- a/type-c-service/src/service/ucsi.rs +++ b/type-c-service/src/service/ucsi.rs @@ -1,3 +1,4 @@ +use embedded_services::sync::Lockable; use embedded_services::warn; use embedded_usb_pd::ucsi::cci::{Cci, GlobalCci}; use embedded_usb_pd::ucsi::lpm::get_connector_status::{BatteryChargingCapabilityStatus, ConnectorStatusChange}; @@ -7,10 +8,13 @@ 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 type_c_interface::service::event::{Event, UsciChangeIndicatorData}; +use type_c_interface::ucsi::Lpm as _; use super::*; +const MAX_SUPPORTED_PORTS: usize = 4; + /// UCSI command response #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -41,26 +45,26 @@ pub(super) struct State { pub(super) psu_connected: bool, } -impl Service<'_> { +impl<'port, Reg: Registration<'port>> Service<'port, Reg> { /// PPM reset implementation fn process_ppm_reset(&mut self) { debug!("Resetting PPM"); - self.state.ucsi.notifications_enabled = NotificationEnable::default(); - self.state.ucsi.pending_ports.clear(); - self.state.ucsi.valid_battery_charging_capability.clear(); + self.ucsi.notifications_enabled = NotificationEnable::default(); + self.ucsi.pending_ports.clear(); + self.ucsi.valid_battery_charging_capability.clear(); } /// Set notification enable implementation fn process_set_notification_enable(&mut self, enable: NotificationEnable) { debug!("Set Notification Enable: {:?}", enable); - self.state.ucsi.notifications_enabled = enable; + self.ucsi.notifications_enabled = enable; } /// PPM get capabilities implementation 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 = self.context.get_num_ports() as u8; + capabilities.num_connectors = self.registration.ports().len() as u8; ppm::ResponseData::GetCapability(capabilities) } @@ -82,7 +86,7 @@ impl Service<'_> { port_status: &PortStatus, ) -> Option { if port_status.power_role == PowerRole::Sink { - if self.state.ucsi.valid_battery_charging_capability.contains(&port_id) && !self.state.ucsi.psu_connected { + if self.ucsi.valid_battery_charging_capability.contains(&port_id) && !self.ucsi.psu_connected { // Only run this logic when no PSU is attached to prevent excessive notifications // when new type-C PSUs are attached let power_mw = port_status @@ -106,17 +110,24 @@ impl Service<'_> { command: &ucsi::lpm::GlobalCommand, ) -> Result, PdError> { debug!("Processing LPM command: {:?}", command); + let mut port = self.lookup_port(command.port())?.lock().await; + let local_port_id = self + .registration + .ucsi_local_port_id(command.port()) + .ok_or(PdError::InvalidPort)?; + let local_command = ucsi::lpm::LocalCommand::new(local_port_id, command.operation()); + match command.operation() { lpm::CommandData::GetConnectorCapability => { // Override the capabilities if present in the config if let Some(capabilities) = &self.config.ucsi_port_capabilities { Ok(Some(lpm::ResponseData::GetConnectorCapability(*capabilities))) } else { - self.context.execute_ucsi_command(*command).await + port.execute_lpm_command(local_command).await } } lpm::CommandData::GetConnectorStatus => { - let mut response = self.context.execute_ucsi_command(*command).await; + let mut response = port.execute_lpm_command(local_command).await; if let Ok(Some(lpm::ResponseData::GetConnectorStatus(lpm::get_connector_status::ResponseData { status_change: ref mut states_change, status: @@ -127,22 +138,21 @@ impl Service<'_> { .. }))) = response { - let raw_port = command.port().0 as usize; - let port_status = self.state.port_status.get(raw_port).ok_or(PdError::InvalidPort)?; + let port_status = port.get_port_status().await?; *battery_charging_status = - self.determine_battery_charging_capability_status(command.port(), port_status); + self.determine_battery_charging_capability_status(command.port(), &port_status); states_change.set_battery_charging_status_change(battery_charging_status.is_some()); } response } - _ => self.context.execute_ucsi_command(*command).await, + _ => port.execute_lpm_command(local_command).await, } } /// Update the CCI connector change field based on the current pending port fn set_cci_connector_change(&self, cci: &mut GlobalCci) { - if let Some(current_port) = self.state.ucsi.pending_ports.front() { + if let Some(current_port) = self.ucsi.pending_ports.front() { // UCSI connector numbers are 1-based cci.set_connector_change(GlobalPortId(current_port.0 + 1)); } else { @@ -154,22 +164,31 @@ impl Service<'_> { /// Acknowledge the current connector change and move to the next if present async fn ack_connector_change(&mut self, cci: &mut GlobalCci) { // Pop the just acknowledged port and move to the next if present - if let Some(_current_port) = self.state.ucsi.pending_ports.pop_front() { - if let Some(next_port) = self.state.ucsi.pending_ports.front() { - debug!("ACK_CCI processed, next pending port: {:?}", next_port); - self.context - .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, - })) - .await; - } else { - debug!("ACK_CCI processed, no more pending ports"); - } - } else { + let Some(_current_port) = self.ucsi.pending_ports.pop_front() else { warn!("Received ACK_CCI with no pending connector changes"); - } + return; + }; + + let Some(next_port) = self.ucsi.pending_ports.front() else { + debug!("ACK_CCI processed, no more pending ports"); + return; + }; + + debug!("ACK_CCI processed, next pending port: {:?}", next_port); + let Ok(port) = self.lookup_port(*next_port) else { + error!("Invalid port ID in pending ports: {:?}", next_port); + return; + }; + + self.broadcast_event(Event { + port, + event: EventData::UsciChangeIndicator(UsciChangeIndicatorData { + port: *next_port, + // False here because the OPM gets notified by the CCI, don't need a separate notification + notify_opm: false, + }), + }) + .await; self.set_cci_connector_change(cci); } @@ -188,7 +207,7 @@ impl Service<'_> { // Using a loop allows all logic to be centralized loop { let output = if let Some(next_input) = next_input.take() { - self.state.ucsi.ppm_state_machine.consume(next_input) + self.ucsi.ppm_state_machine.consume(next_input) } else { error!("Unexpected end of state machine processing"); return UcsiResponse { @@ -232,14 +251,14 @@ impl Service<'_> { // Don't return yet, need to inform state machine that command is complete } PpmOutput::OpmNotifyCommandComplete => { - response.notify_opm = self.state.ucsi.notifications_enabled.cmd_complete(); + response.notify_opm = self.ucsi.notifications_enabled.cmd_complete(); response.cci.set_cmd_complete(true); response.cci.set_error(response.data.is_err()); self.set_cci_connector_change(&mut response.cci); return response; } PpmOutput::AckComplete(ack) => { - response.notify_opm = self.state.ucsi.notifications_enabled.cmd_complete(); + response.notify_opm = self.ucsi.notifications_enabled.cmd_complete(); if ack.command_complete() { response.cci.set_ack_command(true); } @@ -262,7 +281,7 @@ impl Service<'_> { } PpmOutput::OpmNotifyBusy => { // Notify if notifications are enabled in general - response.notify_opm = !self.state.ucsi.notifications_enabled.is_empty(); + response.notify_opm = !self.ucsi.notifications_enabled.is_empty(); response.cci.set_busy(true); self.set_cci_connector_change(&mut response.cci); return response; @@ -283,6 +302,7 @@ impl Service<'_> { /// Handle PD port events, update UCSI state, and generate corresponding UCSI notifications pub(super) async fn handle_ucsi_port_event( &mut self, + port: &'port Reg::Port, port_id: GlobalPortId, port_event: PortStatusEventBitfield, port_status: &PortStatus, @@ -308,48 +328,51 @@ impl Service<'_> { ucsi_event.set_battery_charging_status_change(true); // Power negotiation completed, battery charging capability status is now valid - if self - .state - .ucsi - .valid_battery_charging_capability - .insert(port_id) - .is_err() - { - error!("Valid battery charging capability overflow for port {:?}", port_id); + if self.ucsi.valid_battery_charging_capability.insert(port_id).is_err() { + error!( + "({}): Valid battery charging capability overflow", + port.lock().await.name() + ); } } if !port_status.is_connected() { // Reset battery charging capability status when disconnected - let _ = self.state.ucsi.valid_battery_charging_capability.remove(&port_id); + let _ = self.ucsi.valid_battery_charging_capability.remove(&port_id); } - if ucsi_event - .filter_enabled(self.state.ucsi.notifications_enabled) - .is_empty() - { + if ucsi_event.filter_enabled(self.ucsi.notifications_enabled).is_empty() { trace!("{:?}: event received, but no UCSI notifications enabled", port_id); return; } - self.pend_ucsi_port(port_id).await; + self.pend_ucsi_port(port, port_id).await; } /// Pend UCSI events for all connected ports pub(super) async fn pend_ucsi_connected_ports(&mut self) { // Panic Safety: i is limited by the length of port_status #[allow(clippy::indexing_slicing)] - for i in 0..self.state.port_status.len() { + for i in 0..self.registration.ports().len() { let port_id = GlobalPortId(i as u8); - if self.state.port_status[i].is_connected() { - self.pend_ucsi_port(port_id).await; + let Some(port) = self.registration.ports().get(i) else { + error!("Invalid port ID: {}", i); + continue; + }; + + if let Ok(port_status) = port.lock().await.get_port_status().await { + if port_status.is_connected() { + self.pend_ucsi_port(port, port_id).await; + } + } else { + error!("({}): Failed to get status for port", port.lock().await.name()); } } } /// Pend a UCSI event for the given port - async fn pend_ucsi_port(&mut self, port_id: GlobalPortId) { - if self.state.ucsi.pending_ports.iter().any(|pending| *pending == port_id) { + async fn pend_ucsi_port(&mut self, port: &'port Reg::Port, port_id: GlobalPortId) { + if self.ucsi.pending_ports.iter().any(|pending| *pending == port_id) { // Already have a pending event for this port, don't need to process it twice return; } @@ -357,14 +380,16 @@ impl Service<'_> { // Only notifiy the OPM if we don't have any pending events // Once the OPM starts processing events, the next pending port will be sent as part // of the CCI response to the ACK_CC_CI command. See [`Self::set_cci_connector_change`] - let notify_opm = self.state.ucsi.pending_ports.is_empty(); - if self.state.ucsi.pending_ports.push_back(port_id).is_ok() { - self.context - .broadcast_message(Event::UcsiCci(UsciChangeIndicator { + let notify_opm = self.ucsi.pending_ports.is_empty(); + if self.ucsi.pending_ports.push_back(port_id).is_ok() { + self.broadcast_event(Event { + port, + event: EventData::UsciChangeIndicator(UsciChangeIndicatorData { port: port_id, notify_opm, - })) - .await; + }), + }) + .await; } else { // This shouldn't happen because we have a single slot per port // Would likely indicate that an invalid port ID got in somehow diff --git a/type-c-service/src/service/vdm.rs b/type-c-service/src/service/vdm.rs deleted file mode 100644 index d238c61c..00000000 --- a/type-c-service/src/service/vdm.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! VDM (Vendor Defined Messages) related functionality. - -use embedded_usb_pd::{GlobalPortId, PdError}; -use type_c_interface::control::vdm::{AttnVdm, OtherVdm}; - -use super::Service; - -impl Service<'_> { - /// Get the other vdm for the given port - 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, 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 98dbe4f1..8ad364bd 100644 --- a/type-c-service/src/task.rs +++ b/type-c-service/src/task.rs @@ -1,12 +1,18 @@ use embedded_services::{error, event::Receiver, info, sync::Lockable}; use power_policy_interface::service::event::EventData as PowerPolicyEventData; +use type_c_interface::port::pd::Pd; -use crate::service::{EventReceiver, Service}; +use crate::service::{Service, event_receiver::ArrayEventReceiver, registration::Registration}; /// Task to run the Type-C service, running the default event loop -pub async fn task>( - service: &'static impl Lockable>, - mut event_receiver: EventReceiver<'static, PowerReceiver>, +pub async fn task< + const N: usize, + Port: Lockable, + PortReceiver: Receiver, + PowerReceiver: Receiver, +>( + service: &'static impl Lockable>>, + mut event_receiver: ArrayEventReceiver<'static, N, Port, PortReceiver, PowerReceiver>, ) { info!("Starting type-c task");