diff --git a/embedded-service/src/lib.rs b/embedded-service/src/lib.rs index 4ce50d61..aa4dcec8 100644 --- a/embedded-service/src/lib.rs +++ b/embedded-service/src/lib.rs @@ -78,5 +78,4 @@ pub async fn init() { cfu::init(); keyboard::init(); power::policy::init(); - type_c::controller::init(); } diff --git a/embedded-service/src/type_c/controller.rs b/embedded-service/src/type_c/controller.rs index 4d3b5587..947a9941 100644 --- a/embedded-service/src/type_c/controller.rs +++ b/embedded-service/src/type_c/controller.rs @@ -1,6 +1,5 @@ //! PD controller related code use core::future::Future; -use core::sync::atomic::{AtomicBool, Ordering}; use embassy_sync::signal::Signal; use embassy_time::{Duration, with_timeout}; @@ -495,14 +494,19 @@ impl<'a> Device<'a> { } /// Notify that there are pending events on one or more ports - pub fn notify_ports(&self, pending: PortPending) { - CONTEXT.notify_ports(pending); + pub fn notify_ports(&self, ctx: &Context, pending: PortPending) { + ctx.notify_ports(pending); } /// Number of ports on this controller pub fn num_ports(&self) -> usize { self.num_ports } + + /// Slice of global ports on the Device + pub fn ports(&self) -> &'a [GlobalPortId] { + self.ports + } } /// Trait for types that contain a controller struct @@ -665,8 +669,7 @@ pub trait Controller { } /// Internal context for managing PD controllers -struct Context { - controllers: intrusive_list::IntrusiveList, +pub struct Context { port_events: Signal, /// Channel for receiving commands to the type-C service external_command: deferred::Channel>, @@ -674,10 +677,16 @@ struct Context { broadcaster: broadcaster::Immediate, } +impl Default for Context { + fn default() -> Self { + Self::new() + } +} + impl Context { - const fn new() -> Self { + /// Create new Context + pub const fn new() -> Self { Self { - controllers: intrusive_list::IntrusiveList::new(), port_events: Signal::new(), external_command: deferred::Channel::new(), broadcaster: broadcaster::Immediate::new(), @@ -686,7 +695,7 @@ impl Context { /// Notify that there are pending events on one or more ports /// Each bit corresponds to a global port ID - fn notify_ports(&self, pending: PortPending) { + pub fn notify_ports(&self, pending: PortPending) { let raw_pending: u32 = pending.into(); trace!("Notify ports: {:#x}", raw_pending); // Early exit if no events @@ -701,69 +710,15 @@ impl Context { pending }); } -} - -static CONTEXT: Context = Context::new(); - -/// Initialize the PD controller context -pub fn init() {} - -/// Register a PD controller -pub fn register_controller(controller: &'static impl DeviceContainer) -> Result<(), intrusive_list::Error> { - CONTEXT.controllers.push(controller.get_pd_controller_device()) -} - -pub(super) async fn lookup_controller(controller_id: ControllerId) -> Result<&'static Device<'static>, PdError> { - CONTEXT - .controllers - .into_iter() - .filter_map(|node| node.data::()) - .find(|controller| controller.id == controller_id) - .ok_or(PdError::InvalidController) -} - -/// Get total number of ports on the system -pub(super) fn get_num_ports() -> usize { - CONTEXT - .controllers - .iter_only::() - .fold(0, |acc, controller| acc + controller.num_ports()) -} - -/// Register a message receiver for type-C messages -pub fn register_message_receiver( - receiver: &'static broadcaster::Receiver<'_, CommsMessage>, -) -> intrusive_list::Result<()> { - CONTEXT.broadcaster.register_receiver(receiver) -} - -/// 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); - -/// Type to provide access to the PD controller context for service implementations -pub struct ContextToken(()); - -impl ContextToken { - /// Create a new context token, returning None if this function has been called before - pub fn create() -> Option { - static INIT: AtomicBool = AtomicBool::new(false); - if INIT.load(Ordering::SeqCst) { - return None; - } - - INIT.store(true, Ordering::SeqCst); - Some(ContextToken(())) - } /// Send a command to the given controller with no timeout pub async fn send_controller_command_no_timeout( &self, + controllers: &intrusive_list::IntrusiveList, controller_id: ControllerId, command: InternalCommandData, ) -> Result, PdError> { - let node = CONTEXT - .controllers + let node = controllers .into_iter() .find(|node| { if let Some(controller) = node.data::() { @@ -791,12 +746,13 @@ impl ContextToken { /// Send a command to the given controller with a timeout pub async fn send_controller_command( &self, + controllers: &intrusive_list::IntrusiveList, controller_id: ControllerId, command: InternalCommandData, ) -> Result, PdError> { match with_timeout( DEFAULT_TIMEOUT, - self.send_controller_command_no_timeout(controller_id, command), + self.send_controller_command_no_timeout(controllers, controller_id, command), ) .await { @@ -806,15 +762,22 @@ impl ContextToken { } /// Reset the given controller - pub async fn reset_controller(&self, controller_id: ControllerId) -> Result<(), PdError> { - self.send_controller_command(controller_id, InternalCommandData::Reset) + pub async fn reset_controller( + &self, + controllers: &intrusive_list::IntrusiveList, + controller_id: ControllerId, + ) -> Result<(), PdError> { + self.send_controller_command(controllers, controller_id, InternalCommandData::Reset) .await .map(|_| ()) } - fn find_node_by_port(&self, port_id: GlobalPortId) -> Result<&IntrusiveNode, PdError> { - CONTEXT - .controllers + fn find_node_by_port( + &self, + controllers: &intrusive_list::IntrusiveList, + port_id: GlobalPortId, + ) -> Result<&IntrusiveNode, PdError> { + controllers .into_iter() .find(|node| { if let Some(controller) = node.data::() { @@ -829,10 +792,11 @@ impl ContextToken { /// Send a command to the given port pub async fn send_port_command_ucsi_no_timeout( &self, + controllers: &intrusive_list::IntrusiveList, port_id: GlobalPortId, command: lpm::CommandData, ) -> Result { - let node = self.find_node_by_port(port_id)?; + let node = self.find_node_by_port(controllers, port_id)?; match node .data::() @@ -851,12 +815,13 @@ impl ContextToken { /// Send a command to the given port with a timeout pub async fn send_port_command_ucsi( &self, + controllers: &intrusive_list::IntrusiveList, port_id: GlobalPortId, command: lpm::CommandData, ) -> Result { match with_timeout( DEFAULT_TIMEOUT, - self.send_port_command_ucsi_no_timeout(port_id, command), + self.send_port_command_ucsi_no_timeout(controllers, port_id, command), ) .await { @@ -868,10 +833,11 @@ impl ContextToken { /// Send a command to the given port with no timeout pub async fn send_port_command_no_timeout( &self, + controllers: &intrusive_list::IntrusiveList, port_id: GlobalPortId, command: PortCommandData, ) -> Result { - let node = self.find_node_by_port(port_id)?; + let node = self.find_node_by_port(controllers, port_id)?; match node .data::() @@ -893,10 +859,16 @@ impl ContextToken { /// Send a command to the given port with a timeout pub async fn send_port_command( &self, + controllers: &intrusive_list::IntrusiveList, port_id: GlobalPortId, command: PortCommandData, ) -> Result { - match with_timeout(DEFAULT_TIMEOUT, self.send_port_command_no_timeout(port_id, command)).await { + match with_timeout( + DEFAULT_TIMEOUT, + self.send_port_command_no_timeout(controllers, port_id, command), + ) + .await + { Ok(response) => response, Err(_) => Err(PdError::Timeout), } @@ -904,12 +876,19 @@ impl ContextToken { /// Get the current port events pub async fn get_unhandled_events(&self) -> PortPending { - CONTEXT.port_events.wait().await + self.port_events.wait().await } /// Get the unhandled events for the given port - pub async fn get_port_event(&self, port: GlobalPortId) -> Result { - match self.send_port_command(port, PortCommandData::ClearEvents).await? { + pub async fn get_port_event( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + ) -> Result { + match self + .send_port_command(controllers, port, PortCommandData::ClearEvents) + .await? + { PortResponseData::ClearEvents(event) => Ok(event), r => { error!("Invalid response: expected clear events, got {:?}", r); @@ -919,9 +898,14 @@ impl ContextToken { } /// Get the current port status - pub async fn get_port_status(&self, port: GlobalPortId, cached: Cached) -> Result { + pub async fn get_port_status( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + cached: Cached, + ) -> Result { match self - .send_port_command(port, PortCommandData::PortStatus(cached)) + .send_port_command(controllers, port, PortCommandData::PortStatus(cached)) .await? { PortResponseData::PortStatus(status) => Ok(status), @@ -933,8 +917,15 @@ impl ContextToken { } /// Get the oldest unhandled PD alert for the given port - pub async fn get_pd_alert(&self, port: GlobalPortId) -> Result, PdError> { - match self.send_port_command(port, PortCommandData::GetPdAlert).await? { + pub async fn get_pd_alert( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + ) -> Result, PdError> { + match self + .send_port_command(controllers, port, PortCommandData::GetPdAlert) + .await? + { PortResponseData::PdAlert(alert) => Ok(alert), r => { error!("Invalid response: expected PD alert, got {:?}", r); @@ -944,9 +935,13 @@ impl ContextToken { } /// Get the retimer fw update status - pub async fn get_rt_fw_update_status(&self, port: GlobalPortId) -> Result { + pub async fn get_rt_fw_update_status( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + ) -> Result { match self - .send_port_command(port, PortCommandData::RetimerFwUpdateGetState) + .send_port_command(controllers, port, PortCommandData::RetimerFwUpdateGetState) .await? { PortResponseData::RtFwUpdateStatus(status) => Ok(status), @@ -955,9 +950,13 @@ impl ContextToken { } /// Set the retimer fw update state - pub async fn set_rt_fw_update_state(&self, port: GlobalPortId) -> Result<(), PdError> { + pub async fn set_rt_fw_update_state( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + ) -> Result<(), PdError> { match self - .send_port_command(port, PortCommandData::RetimerFwUpdateSetState) + .send_port_command(controllers, port, PortCommandData::RetimerFwUpdateSetState) .await? { PortResponseData::Complete => Ok(()), @@ -966,9 +965,13 @@ impl ContextToken { } /// Clear the retimer fw update state - pub async fn clear_rt_fw_update_state(&self, port: GlobalPortId) -> Result<(), PdError> { + pub async fn clear_rt_fw_update_state( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + ) -> Result<(), PdError> { match self - .send_port_command(port, PortCommandData::RetimerFwUpdateClearState) + .send_port_command(controllers, port, PortCommandData::RetimerFwUpdateClearState) .await? { PortResponseData::Complete => Ok(()), @@ -977,9 +980,13 @@ impl ContextToken { } /// Set the retimer compliance - pub async fn set_rt_compliance(&self, port: GlobalPortId) -> Result<(), PdError> { + pub async fn set_rt_compliance( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + ) -> Result<(), PdError> { match self - .send_port_command(port, PortCommandData::SetRetimerCompliance) + .send_port_command(controllers, port, PortCommandData::SetRetimerCompliance) .await? { PortResponseData::Complete => Ok(()), @@ -988,9 +995,13 @@ impl ContextToken { } /// Reconfigure the retimer for the given port. - pub async fn reconfigure_retimer(&self, port: GlobalPortId) -> Result<(), PdError> { + pub async fn reconfigure_retimer( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + ) -> Result<(), PdError> { match self - .send_port_command(port, PortCommandData::ReconfigureRetimer) + .send_port_command(controllers, port, PortCommandData::ReconfigureRetimer) .await? { PortResponseData::Complete => Ok(()), @@ -1001,9 +1012,14 @@ impl ContextToken { /// 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> { + pub async fn set_max_sink_voltage( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + max_voltage_mv: Option, + ) -> Result<(), PdError> { match self - .send_port_command(port, PortCommandData::SetMaxSinkVoltage(max_voltage_mv)) + .send_port_command(controllers, port, PortCommandData::SetMaxSinkVoltage(max_voltage_mv)) .await? { PortResponseData::Complete => Ok(()), @@ -1012,9 +1028,13 @@ impl ContextToken { } /// Clear the dead battery flag for the given port. - pub async fn clear_dead_battery_flag(&self, port: GlobalPortId) -> Result<(), PdError> { + pub async fn clear_dead_battery_flag( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + ) -> Result<(), PdError> { match self - .send_port_command(port, PortCommandData::ClearDeadBatteryFlag) + .send_port_command(controllers, port, PortCommandData::ClearDeadBatteryFlag) .await? { PortResponseData::Complete => Ok(()), @@ -1025,10 +1045,11 @@ impl ContextToken { /// Get current controller status pub async fn get_controller_status( &self, + controllers: &intrusive_list::IntrusiveList, controller_id: ControllerId, ) -> Result, PdError> { match self - .send_controller_command(controller_id, InternalCommandData::Status) + .send_controller_command(controllers, controller_id, InternalCommandData::Status) .await? { InternalResponseData::Status(status) => Ok(status), @@ -1040,9 +1061,14 @@ impl ContextToken { } /// Set unconstrained power for the given port - pub async fn set_unconstrained_power(&self, port: GlobalPortId, unconstrained: bool) -> Result<(), PdError> { + pub async fn set_unconstrained_power( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + unconstrained: bool, + ) -> Result<(), PdError> { match self - .send_port_command(port, PortCommandData::SetUnconstrainedPower(unconstrained)) + .send_port_command(controllers, port, PortCommandData::SetUnconstrainedPower(unconstrained)) .await? { PortResponseData::Complete => Ok(()), @@ -1051,9 +1077,13 @@ impl ContextToken { } /// Sync controller state - pub async fn sync_controller_state(&self, controller_id: ControllerId) -> Result<(), PdError> { + pub async fn sync_controller_state( + &self, + controllers: &intrusive_list::IntrusiveList, + controller_id: ControllerId, + ) -> Result<(), PdError> { match self - .send_controller_command(controller_id, InternalCommandData::SyncState) + .send_controller_command(controllers, controller_id, InternalCommandData::SyncState) .await? { InternalResponseData::Complete => Ok(()), @@ -1068,22 +1098,24 @@ impl ContextToken { pub async fn wait_external_command( &self, ) -> deferred::Request<'_, GlobalRawMutex, external::Command, external::Response<'static>> { - CONTEXT.external_command.receive().await - } - - /// Notify that there are pending events on one or more ports - pub fn notify_ports(&self, pending: PortPending) { - CONTEXT.notify_ports(pending); + self.external_command.receive().await } /// Get the number of ports on the system - pub fn get_num_ports(&self) -> usize { - get_num_ports() + pub fn get_num_ports(&self, controllers: &intrusive_list::IntrusiveList) -> usize { + get_num_ports(controllers) } /// Get the other vdm for the given port - pub async fn get_other_vdm(&self, port: GlobalPortId) -> Result { - match self.send_port_command(port, PortCommandData::GetOtherVdm).await? { + pub async fn get_other_vdm( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + ) -> Result { + match self + .send_port_command(controllers, port, PortCommandData::GetOtherVdm) + .await? + { PortResponseData::OtherVdm(vdm) => Ok(vdm), r => { error!("Invalid response: expected other VDM, got {:?}", r); @@ -1093,8 +1125,15 @@ impl ContextToken { } /// 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? { + pub async fn get_attn_vdm( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + ) -> Result { + match self + .send_port_command(controllers, port, PortCommandData::GetAttnVdm) + .await? + { PortResponseData::AttnVdm(vdm) => Ok(vdm), r => { error!("Invalid response: expected attention VDM, got {:?}", r); @@ -1104,17 +1143,30 @@ impl ContextToken { } /// 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? { + pub async fn send_vdm( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + tx_vdm: SendVdm, + ) -> Result<(), PdError> { + match self + .send_port_command(controllers, port, PortCommandData::SendVdm(tx_vdm)) + .await? + { PortResponseData::Complete => Ok(()), _ => Err(PdError::InvalidResponse), } } /// Set USB control configuration for the given port - pub async fn set_usb_control(&self, port: GlobalPortId, config: UsbControlConfig) -> Result<(), PdError> { + pub async fn set_usb_control( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + config: UsbControlConfig, + ) -> Result<(), PdError> { match self - .send_port_command(port, PortCommandData::SetUsbControl(config)) + .send_port_command(controllers, port, PortCommandData::SetUsbControl(config)) .await? { PortResponseData::Complete => Ok(()), @@ -1123,8 +1175,15 @@ impl ContextToken { } /// 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? { + pub async fn get_dp_status( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + ) -> Result { + match self + .send_port_command(controllers, port, PortCommandData::GetDpStatus) + .await? + { PortResponseData::DpStatus(status) => Ok(status), r => { error!("Invalid response: expected DP status, got {:?}", r); @@ -1134,9 +1193,14 @@ impl ContextToken { } /// Set DisplayPort configuration for the given port - pub async fn set_dp_config(&self, port: GlobalPortId, config: DpConfig) -> Result<(), PdError> { + pub async fn set_dp_config( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + config: DpConfig, + ) -> Result<(), PdError> { match self - .send_port_command(port, PortCommandData::SetDpConfig(config)) + .send_port_command(controllers, port, PortCommandData::SetDpConfig(config)) .await? { PortResponseData::Complete => Ok(()), @@ -1145,17 +1209,29 @@ impl ContextToken { } /// 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? { + pub async fn execute_drst( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + ) -> Result<(), PdError> { + match self + .send_port_command(controllers, port, PortCommandData::ExecuteDrst) + .await? + { PortResponseData::Complete => Ok(()), _ => Err(PdError::InvalidResponse), } } /// Set Thunderbolt configuration for the given port - pub async fn set_tbt_config(&self, port: GlobalPortId, config: TbtConfig) -> Result<(), PdError> { + pub async fn set_tbt_config( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + config: TbtConfig, + ) -> Result<(), PdError> { match self - .send_port_command(port, PortCommandData::SetTbtConfig(config)) + .send_port_command(controllers, port, PortCommandData::SetTbtConfig(config)) .await? { PortResponseData::Complete => Ok(()), @@ -1166,11 +1242,12 @@ impl ContextToken { /// Set PD state-machine configuration for the given port pub async fn set_pd_state_machine_config( &self, + controllers: &intrusive_list::IntrusiveList, port: GlobalPortId, config: PdStateMachineConfig, ) -> Result<(), PdError> { match self - .send_port_command(port, PortCommandData::SetPdStateMachineConfig(config)) + .send_port_command(controllers, port, PortCommandData::SetPdStateMachineConfig(config)) .await? { PortResponseData::Complete => Ok(()), @@ -1181,11 +1258,12 @@ impl ContextToken { /// Set Type-C state-machine configuration for the given port pub async fn set_type_c_state_machine_config( &self, + controllers: &intrusive_list::IntrusiveList, port: GlobalPortId, state: TypeCStateMachineState, ) -> Result<(), PdError> { match self - .send_port_command(port, PortCommandData::SetTypeCStateMachineConfig(state)) + .send_port_command(controllers, port, PortCommandData::SetTypeCStateMachineConfig(state)) .await? { PortResponseData::Complete => Ok(()), @@ -1196,10 +1274,15 @@ impl ContextToken { /// Execute the given UCSI command pub async fn execute_ucsi_command( &self, + controllers: &intrusive_list::IntrusiveList, command: lpm::GlobalCommand, ) -> Result, PdError> { match self - .send_port_command(command.port(), PortCommandData::ExecuteUcsiCommand(command.operation())) + .send_port_command( + controllers, + command.port(), + PortCommandData::ExecuteUcsiCommand(command.operation()), + ) .await? { PortResponseData::UcsiResponse(response) => response, @@ -1207,17 +1290,30 @@ impl ContextToken { } } + /// Register a message receiver for type-C messages + pub async fn register_message_receiver( + &self, + receiver: &'static broadcaster::Receiver<'_, CommsMessage>, + ) -> intrusive_list::Result<()> { + self.broadcaster.register_receiver(receiver) + } + /// Broadcast a type-C message to all subscribers pub async fn broadcast_message(&self, message: CommsMessage) { - CONTEXT.broadcaster.broadcast(message).await; + self.broadcaster.broadcast(message).await; } } +/// Default command timeout +/// set to high value since this is intended to prevent an unresponsive device from blocking the service implementation +const DEFAULT_TIMEOUT: Duration = Duration::from_millis(5000); + /// Execute an external port command pub(super) async fn execute_external_port_command( + ctx: &Context, command: external::Command, ) -> Result { - match CONTEXT.external_command.execute(command).await { + match ctx.external_command.execute(command).await { external::Response::Port(response) => response, r => { error!("Invalid response: expected external port, got {:?}", r); @@ -1228,9 +1324,10 @@ pub(super) async fn execute_external_port_command( /// Execute an external controller command pub(super) async fn execute_external_controller_command( + ctx: &Context, command: external::Command, ) -> Result, PdError> { - match CONTEXT.external_command.execute(command).await { + match ctx.external_command.execute(command).await { external::Response::Controller(response) => response, r => { error!("Invalid response: expected external controller, got {:?}", r); @@ -1240,8 +1337,11 @@ pub(super) async fn execute_external_controller_command( } /// Execute an external UCSI command -pub(super) async fn execute_external_ucsi_command(command: ucsi::GlobalCommand) -> external::UcsiResponse { - match CONTEXT.external_command.execute(external::Command::Ucsi(command)).await { +pub(super) async fn execute_external_ucsi_command( + ctx: &Context, + command: ucsi::GlobalCommand, +) -> external::UcsiResponse { + match ctx.external_command.execute(external::Command::Ucsi(command)).await { external::Response::Ucsi(response) => response, r => { error!("Invalid response: expected external UCSI, got {:?}", r); @@ -1254,3 +1354,29 @@ pub(super) async fn execute_external_ucsi_command(command: ucsi::GlobalCommand) } } } + +/// Register a PD controller +pub fn register_controller( + controllers: &intrusive_list::IntrusiveList, + controller: &'static impl DeviceContainer, +) -> Result<(), intrusive_list::Error> { + controllers.push(controller.get_pd_controller_device()) +} + +pub(super) fn lookup_controller( + controllers: &intrusive_list::IntrusiveList, + controller_id: ControllerId, +) -> Result<&'static Device<'static>, PdError> { + controllers + .into_iter() + .filter_map(|node| node.data::()) + .find(|controller| controller.id == controller_id) + .ok_or(PdError::InvalidController) +} + +/// Get total number of ports on the system +pub(super) fn get_num_ports(controllers: &intrusive_list::IntrusiveList) -> usize { + controllers + .iter_only::() + .fold(0, |acc, controller| acc + controller.num_ports()) +} diff --git a/embedded-service/src/type_c/external.rs b/embedded-service/src/type_c/external.rs index 6bdbccc6..3bfc8f68 100644 --- a/embedded-service/src/type_c/external.rs +++ b/embedded-service/src/type_c/external.rs @@ -1,10 +1,14 @@ //! Message definitions for external type-C commands use embedded_usb_pd::{GlobalPortId, LocalPortId, PdError, ucsi}; -use crate::type_c::{ - Cached, - controller::{ - PdStateMachineConfig, TbtConfig, TypeCStateMachineState, UsbControlConfig, execute_external_ucsi_command, +use crate::{ + intrusive_list, + type_c::{ + Cached, + controller::{ + Context, PdStateMachineConfig, TbtConfig, TypeCStateMachineState, UsbControlConfig, + execute_external_ucsi_command, + }, }, }; @@ -171,11 +175,14 @@ pub enum Response<'a> { /// Get the status of the given port. /// /// Use the `cached` argument to specify whether to use cached data or force a fetch of register values. -pub async fn get_port_status(port: GlobalPortId, cached: Cached) -> Result { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::PortStatus(cached), - })) +pub async fn get_port_status(ctx: &Context, port: GlobalPortId, cached: Cached) -> Result { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::PortStatus(cached), + }), + ) .await? { PortResponseData::PortStatus(status) => Ok(status), @@ -187,20 +194,25 @@ pub async fn get_port_status(port: GlobalPortId, cached: Cached) -> Result Result { - let global_port = controller_port_to_global_id(controller, port).await?; - get_port_status(global_port, cached).await + let global_port = controller_port_to_global_id(controllers, controller, port)?; + get_port_status(ctx, global_port, cached).await } /// Reset the given controller. -pub async fn reset_controller(controller_id: ControllerId) -> Result<(), PdError> { - match execute_external_controller_command(Command::Controller(ControllerCommand { - id: controller_id, - data: ControllerCommandData::Reset, - })) +pub async fn reset_controller(ctx: &Context, controller_id: ControllerId) -> Result<(), PdError> { + match execute_external_controller_command( + ctx, + Command::Controller(ControllerCommand { + id: controller_id, + data: ControllerCommandData::Reset, + }), + ) .await? { ControllerResponseData::Complete => Ok(()), @@ -210,11 +222,14 @@ pub async fn reset_controller(controller_id: ControllerId) -> Result<(), PdError /// Get the status of the given controller #[allow(unreachable_patterns)] -pub async fn get_controller_status(id: ControllerId) -> Result, PdError> { - match execute_external_controller_command(Command::Controller(ControllerCommand { - id, - data: ControllerCommandData::ControllerStatus, - })) +pub async fn get_controller_status(ctx: &Context, id: ControllerId) -> Result, PdError> { + match execute_external_controller_command( + ctx, + Command::Controller(ControllerCommand { + id, + data: ControllerCommandData::ControllerStatus, + }), + ) .await? { ControllerResponseData::ControllerStatus(status) => Ok(status), @@ -223,24 +238,31 @@ pub async fn get_controller_status(id: ControllerId) -> Result Result { - Ok(lookup_controller(controller_id).await?.num_ports()) +pub fn get_controller_num_ports( + controllers: &intrusive_list::IntrusiveList, + controller_id: ControllerId, +) -> Result { + Ok(lookup_controller(controllers, controller_id)?.num_ports()) } /// Convert a (controller ID, local port ID) to a global port ID -pub async fn controller_port_to_global_id( +pub fn controller_port_to_global_id( + controllers: &intrusive_list::IntrusiveList, controller_id: ControllerId, port_id: LocalPortId, ) -> Result { - lookup_controller(controller_id).await?.lookup_global_port(port_id) + lookup_controller(controllers, controller_id)?.lookup_global_port(port_id) } /// Get the retimer fw update status of the given port -pub async fn port_get_rt_fw_update_status(port: GlobalPortId) -> Result { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::RetimerFwUpdateGetState, - })) +pub async fn port_get_rt_fw_update_status(ctx: &Context, port: GlobalPortId) -> Result { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::RetimerFwUpdateGetState, + }), + ) .await? { PortResponseData::RetimerFwUpdateGetState(status) => Ok(status), @@ -249,11 +271,14 @@ pub async fn port_get_rt_fw_update_status(port: GlobalPortId) -> Result Result<(), PdError> { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::RetimerFwUpdateSetState, - })) +pub async fn port_set_rt_fw_update_state(ctx: &Context, port: GlobalPortId) -> Result<(), PdError> { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::RetimerFwUpdateSetState, + }), + ) .await? { PortResponseData::Complete => Ok(()), @@ -262,11 +287,14 @@ pub async fn port_set_rt_fw_update_state(port: GlobalPortId) -> Result<(), PdErr } /// Clear the retimer fw update state of the given port -pub async fn port_clear_rt_fw_update_state(port: GlobalPortId) -> Result<(), PdError> { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::RetimerFwUpdateClearState, - })) +pub async fn port_clear_rt_fw_update_state(ctx: &Context, port: GlobalPortId) -> Result<(), PdError> { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::RetimerFwUpdateClearState, + }), + ) .await? { PortResponseData::Complete => Ok(()), @@ -275,11 +303,14 @@ pub async fn port_clear_rt_fw_update_state(port: GlobalPortId) -> Result<(), PdE } /// Set the retimer comliance state of the given port -pub async fn port_set_rt_compliance(port: GlobalPortId) -> Result<(), PdError> { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetRetimerCompliance, - })) +pub async fn port_set_rt_compliance(ctx: &Context, port: GlobalPortId) -> Result<(), PdError> { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::SetRetimerCompliance, + }), + ) .await? { PortResponseData::Complete => Ok(()), @@ -288,11 +319,14 @@ pub async fn port_set_rt_compliance(port: GlobalPortId) -> Result<(), PdError> { } /// Trigger a sync of the controller state -pub async fn sync_controller_state(id: ControllerId) -> Result<(), PdError> { - match execute_external_controller_command(Command::Controller(ControllerCommand { - id, - data: ControllerCommandData::SyncState, - })) +pub async fn sync_controller_state(ctx: &Context, id: ControllerId) -> Result<(), PdError> { + match execute_external_controller_command( + ctx, + Command::Controller(ControllerCommand { + id, + data: ControllerCommandData::SyncState, + }), + ) .await? { ControllerResponseData::Complete => Ok(()), @@ -301,18 +335,25 @@ pub async fn sync_controller_state(id: ControllerId) -> Result<(), PdError> { } /// Get number of ports on the system -pub fn get_num_ports() -> usize { - super::controller::get_num_ports() +pub fn get_num_ports(controllers: &intrusive_list::IntrusiveList) -> usize { + super::controller::get_num_ports(controllers) } /// Set the maximum voltage for the given port to a specific value. /// /// See [`PortCommandData::SetMaxSinkVoltage::max_voltage_mv`] for details on the `max_voltage_mv` parameter. -pub async fn set_max_sink_voltage(port: GlobalPortId, max_voltage_mv: Option) -> Result<(), PdError> { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetMaxSinkVoltage { max_voltage_mv }, - })) +pub async fn set_max_sink_voltage( + ctx: &Context, + port: GlobalPortId, + max_voltage_mv: Option, +) -> Result<(), PdError> { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::SetMaxSinkVoltage { max_voltage_mv }, + }), + ) .await? { PortResponseData::Complete => Ok(()), @@ -321,11 +362,14 @@ pub async fn set_max_sink_voltage(port: GlobalPortId, max_voltage_mv: Option Result<(), PdError> { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::ClearDeadBatteryFlag, - })) +pub async fn clear_dead_battery_flag(ctx: &Context, port: GlobalPortId) -> Result<(), PdError> { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::ClearDeadBatteryFlag, + }), + ) .await? { PortResponseData::Complete => Ok(()), @@ -334,11 +378,14 @@ pub async fn clear_dead_battery_flag(port: GlobalPortId) -> Result<(), PdError> } /// Reconfigure the retimer for the given port. -pub async fn reconfigure_retimer(port: GlobalPortId) -> Result<(), PdError> { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::ReconfigureRetimer, - })) +pub async fn reconfigure_retimer(ctx: &Context, port: GlobalPortId) -> Result<(), PdError> { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::ReconfigureRetimer, + }), + ) .await? { PortResponseData::Complete => Ok(()), @@ -347,16 +394,19 @@ pub async fn reconfigure_retimer(port: GlobalPortId) -> Result<(), PdError> { } /// Execute a UCSI command -pub async fn execute_ucsi_command(command: ucsi::GlobalCommand) -> UcsiResponse { - execute_external_ucsi_command(command).await +pub async fn execute_ucsi_command(ctx: &Context, command: ucsi::GlobalCommand) -> UcsiResponse { + execute_external_ucsi_command(ctx, command).await } /// Send vdm to the given port -pub async fn send_vdm(port: GlobalPortId, tx_vdm: SendVdm) -> Result<(), PdError> { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SendVdm(tx_vdm), - })) +pub async fn send_vdm(ctx: &Context, port: GlobalPortId, tx_vdm: SendVdm) -> Result<(), PdError> { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::SendVdm(tx_vdm), + }), + ) .await? { PortResponseData::Complete => Ok(()), @@ -365,11 +415,14 @@ pub async fn send_vdm(port: GlobalPortId, tx_vdm: SendVdm) -> Result<(), PdError } /// Set USB control configuration -pub async fn set_usb_control(port: GlobalPortId, config: UsbControlConfig) -> Result<(), PdError> { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetUsbControl(config), - })) +pub async fn set_usb_control(ctx: &Context, port: GlobalPortId, config: UsbControlConfig) -> Result<(), PdError> { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::SetUsbControl(config), + }), + ) .await? { PortResponseData::Complete => Ok(()), @@ -378,11 +431,14 @@ pub async fn set_usb_control(port: GlobalPortId, config: UsbControlConfig) -> Re } /// Get DisplayPort status for the given port -pub async fn get_dp_status(port: GlobalPortId) -> Result { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::GetDpStatus, - })) +pub async fn get_dp_status(ctx: &Context, port: GlobalPortId) -> Result { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::GetDpStatus, + }), + ) .await? { PortResponseData::GetDpStatus(status) => Ok(status), @@ -391,11 +447,14 @@ pub async fn get_dp_status(port: GlobalPortId) -> Result { } /// Set DisplayPort configuration for the given port -pub async fn set_dp_config(port: GlobalPortId, config: DpConfig) -> Result<(), PdError> { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetDpConfig(config), - })) +pub async fn set_dp_config(ctx: &Context, port: GlobalPortId, config: DpConfig) -> Result<(), PdError> { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::SetDpConfig(config), + }), + ) .await? { PortResponseData::Complete => Ok(()), @@ -404,11 +463,14 @@ pub async fn set_dp_config(port: GlobalPortId, config: DpConfig) -> Result<(), P } /// Execute DisplayPort reset for the given port -pub async fn execute_drst(port: GlobalPortId) -> Result<(), PdError> { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::ExecuteDrst, - })) +pub async fn execute_drst(ctx: &Context, port: GlobalPortId) -> Result<(), PdError> { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::ExecuteDrst, + }), + ) .await? { PortResponseData::Complete => Ok(()), @@ -417,11 +479,14 @@ pub async fn execute_drst(port: GlobalPortId) -> Result<(), PdError> { } /// Set Thunderbolt configuration for the given port -pub async fn set_tbt_config(port: GlobalPortId, config: TbtConfig) -> Result<(), PdError> { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetTbtConfig(config), - })) +pub async fn set_tbt_config(ctx: &Context, port: GlobalPortId, config: TbtConfig) -> Result<(), PdError> { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::SetTbtConfig(config), + }), + ) .await? { PortResponseData::Complete => Ok(()), @@ -430,11 +495,18 @@ pub async fn set_tbt_config(port: GlobalPortId, config: TbtConfig) -> Result<(), } /// Set PD state-machine configuration for the given port -pub async fn set_pd_state_machine_config(port: GlobalPortId, config: PdStateMachineConfig) -> Result<(), PdError> { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetPdStateMachineConfig(config), - })) +pub async fn set_pd_state_machine_config( + ctx: &Context, + port: GlobalPortId, + config: PdStateMachineConfig, +) -> Result<(), PdError> { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::SetPdStateMachineConfig(config), + }), + ) .await? { PortResponseData::Complete => Ok(()), @@ -443,11 +515,18 @@ pub async fn set_pd_state_machine_config(port: GlobalPortId, config: PdStateMach } /// Set Type-C state-machine configuration for the given port -pub async fn set_type_c_state_machine_config(port: GlobalPortId, state: TypeCStateMachineState) -> Result<(), PdError> { - match execute_external_port_command(Command::Port(PortCommand { - port, - data: PortCommandData::SetTypeCStateMachineConfig(state), - })) +pub async fn set_type_c_state_machine_config( + ctx: &Context, + port: GlobalPortId, + state: TypeCStateMachineState, +) -> Result<(), PdError> { + match execute_external_port_command( + ctx, + Command::Port(PortCommand { + port, + data: PortCommandData::SetTypeCStateMachineConfig(state), + }), + ) .await? { PortResponseData::Complete => Ok(()), diff --git a/examples/rt685s-evk/Cargo.lock b/examples/rt685s-evk/Cargo.lock index fc36a246..045a2053 100644 --- a/examples/rt685s-evk/Cargo.lock +++ b/examples/rt685s-evk/Cargo.lock @@ -1598,7 +1598,6 @@ dependencies = [ "embedded-services", "embedded-usb-pd", "heapless", - "static_cell", "tps6699x", ] diff --git a/examples/rt685s-evk/src/bin/type_c.rs b/examples/rt685s-evk/src/bin/type_c.rs index a59b6697..f652367f 100644 --- a/examples/rt685s-evk/src/bin/type_c.rs +++ b/examples/rt685s-evk/src/bin/type_c.rs @@ -9,21 +9,24 @@ use embassy_imxrt::i2c::Async; use embassy_imxrt::i2c::master::{Config, I2cMaster}; use embassy_imxrt::{bind_interrupts, peripherals}; use embassy_sync::mutex::Mutex; +use embassy_sync::pubsub::PubSubChannel; use embassy_time::{self as _, Delay}; use embedded_cfu_protocol::protocol_definitions::{FwUpdateOffer, FwUpdateOfferResponse, FwVersion, HostToken}; -use embedded_services::GlobalRawMutex; -use embedded_services::power::policy::DeviceId as PowerId; +use embedded_services::power::policy::{CommsMessage, DeviceId as PowerId}; use embedded_services::type_c::{self, Cached, ControllerId}; +use embedded_services::{GlobalRawMutex, IntrusiveList}; use embedded_services::{error, info}; use embedded_usb_pd::GlobalPortId; use static_cell::StaticCell; use tps6699x::asynchronous::embassy as tps6699x; use type_c_service::driver::tps6699x::{self as tps6699x_drv}; +use type_c_service::service::Service; use type_c_service::wrapper::ControllerWrapper; use type_c_service::wrapper::backing::{ReferencedStorage, Storage}; extern crate rt685s_evk_example; +const NUM_PD_CONTROLLERS: usize = 1; const CONTROLLER0_ID: ControllerId = ControllerId(0); const PORT0_ID: GlobalPortId = GlobalPortId(0); const PORT1_ID: GlobalPortId = GlobalPortId(1); @@ -64,12 +67,6 @@ async fn interrupt_task(mut int_in: Input<'static>, mut interrupt: Interrupt<'st tps6699x::task::interrupt_task(&mut int_in, &mut [&mut interrupt]).await; } -#[embassy_executor::task] -async fn type_c_service_task() -> ! { - type_c_service::task(Default::default()).await; - unreachable!() -} - #[embassy_executor::task] async fn power_policy_service_task() { power_policy_service::task::task(Default::default()) @@ -77,6 +74,36 @@ async fn power_policy_service_task() { .expect("Failed to start power policy service task"); } +#[embassy_executor::task] +async fn service_task( + controller_context: &'static embedded_services::type_c::controller::Context, + controllers: &'static IntrusiveList, + wrappers: [&'static Wrapper<'static>; NUM_PD_CONTROLLERS], +) { + info!("Starting type-c task"); + + // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot + static POWER_POLICY_CHANNEL: StaticCell> = StaticCell::new(); + + let power_policy_channel = POWER_POLICY_CHANNEL.init(PubSubChannel::new()); + let power_policy_publisher = power_policy_channel.dyn_immediate_publisher(); + // Guaranteed to not panic since we initialized the channel above + let power_policy_subscriber = power_policy_channel.dyn_subscriber().unwrap(); + + let service = Service::create( + type_c_service::service::config::Config::default(), + controller_context, + controllers, + power_policy_publisher, + power_policy_subscriber, + ); + + static SERVICE: StaticCell = StaticCell::new(); + let service = SERVICE.init(service); + + type_c_service::task::task(service, wrappers).await; +} + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_imxrt::init(Default::default()); @@ -84,13 +111,13 @@ async fn main(spawner: Spawner) { info!("Embedded service init"); embedded_services::init().await; - type_c::controller::init(); - info!("Spawining power policy task"); spawner.must_spawn(power_policy_service_task()); - info!("Spawining type-c service task"); - spawner.must_spawn(type_c_service_task()); + static CONTROLLER_LIST: StaticCell = StaticCell::new(); + let controllers = CONTROLLER_LIST.init(IntrusiveList::new()); + static CONTEXT: StaticCell = StaticCell::new(); + let controller_context = CONTEXT.init(embedded_services::type_c::controller::Context::new()); let int_in = Input::new(p.PIO1_7, Pull::Up, Inverter::Disabled); static BUS: StaticCell>> = StaticCell::new(); @@ -127,6 +154,7 @@ async fn main(spawner: Spawner) { static STORAGE: StaticCell> = StaticCell::new(); let storage = STORAGE.init(Storage::new( + controller_context, CONTROLLER0_ID, 0, // CFU component ID [(PORT0_ID, PORT0_PWR_ID), (PORT1_ID, PORT1_PWR_ID)], @@ -147,21 +175,31 @@ async fn main(spawner: Spawner) { let wrapper = WRAPPER.init(ControllerWrapper::try_new(controller_mutex, Default::default(), referenced, Validator).unwrap()); - wrapper.register().await.unwrap(); + info!("Spawining type-c service task"); + spawner.must_spawn(service_task(controller_context, controllers, [wrapper])); + spawner.must_spawn(pd_controller_task(wrapper)); // Sync our internal state with the hardware - type_c::external::sync_controller_state(CONTROLLER0_ID).await.unwrap(); + type_c::external::sync_controller_state(controller_context, CONTROLLER0_ID) + .await + .unwrap(); embassy_time::Timer::after_secs(10).await; - let status = type_c::external::get_controller_status(CONTROLLER0_ID).await.unwrap(); + let status = type_c::external::get_controller_status(controller_context, CONTROLLER0_ID) + .await + .unwrap(); info!("Controller status: {:?}", status); - let status = type_c::external::get_port_status(PORT0_ID, Cached(true)).await.unwrap(); + let status = type_c::external::get_port_status(controller_context, PORT0_ID, Cached(true)) + .await + .unwrap(); info!("Port status: {:?}", status); - let status = type_c::external::get_port_status(PORT1_ID, Cached(true)).await.unwrap(); + let status = type_c::external::get_port_status(controller_context, PORT1_ID, Cached(true)) + .await + .unwrap(); info!("Port status: {:?}", status); } diff --git a/examples/rt685s-evk/src/bin/type_c_cfu.rs b/examples/rt685s-evk/src/bin/type_c_cfu.rs index 4e992915..87cfed3a 100644 --- a/examples/rt685s-evk/src/bin/type_c_cfu.rs +++ b/examples/rt685s-evk/src/bin/type_c_cfu.rs @@ -9,20 +9,23 @@ use embassy_imxrt::i2c::Async; use embassy_imxrt::i2c::master::{Config, I2cMaster}; use embassy_imxrt::{bind_interrupts, peripherals}; use embassy_sync::mutex::Mutex; +use embassy_sync::pubsub::PubSubChannel; use embassy_time::Timer; use embassy_time::{self as _, Delay}; use embedded_cfu_protocol::protocol_definitions::*; use embedded_cfu_protocol::protocol_definitions::{FwUpdateOffer, FwUpdateOfferResponse, FwVersion}; use embedded_services::cfu::component::InternalResponseData; use embedded_services::cfu::component::RequestData; -use embedded_services::power::policy::DeviceId as PowerId; -use embedded_services::type_c::{self, ControllerId}; -use embedded_services::{GlobalRawMutex, cfu}; +use embedded_services::power::policy::{CommsMessage, DeviceId as PowerId}; +use embedded_services::type_c::ControllerId; +use embedded_services::type_c::controller::Context; +use embedded_services::{GlobalRawMutex, IntrusiveList, cfu}; use embedded_services::{error, info}; use embedded_usb_pd::GlobalPortId; use static_cell::StaticCell; use tps6699x::asynchronous::embassy as tps6699x; use type_c_service::driver::tps6699x::{self as tps6699x_drv}; +use type_c_service::service::Service; use type_c_service::wrapper::ControllerWrapper; use type_c_service::wrapper::backing::{ReferencedStorage, Storage}; @@ -48,6 +51,7 @@ type Wrapper<'a> = ControllerWrapper<'a, GlobalRawMutex, Tps6699xMutex<'a>, Vali type Controller<'a> = tps6699x::controller::Controller>; type Interrupt<'a> = tps6699x::Interrupt<'a, GlobalRawMutex, BusDevice<'a>>; +const NUM_PD_CONTROLLERS: usize = 1; const CONTROLLER0_ID: ControllerId = ControllerId(0); const CONTROLLER0_CFU_ID: ComponentId = 0x12; const PORT0_ID: GlobalPortId = GlobalPortId(0); @@ -149,12 +153,6 @@ async fn fw_update_task() { info!("Got version: {:#x}", version); } -#[embassy_executor::task] -async fn type_c_service_task() -> ! { - type_c_service::task(Default::default()).await; - unreachable!() -} - #[embassy_executor::task] async fn power_policy_service_task() { power_policy_service::task::task(Default::default()) @@ -162,6 +160,37 @@ async fn power_policy_service_task() { .expect("Failed to start power policy service task"); } +#[embassy_executor::task] +async fn service_task( + controller_context: &'static Context, + controllers: &'static IntrusiveList, + wrappers: [&'static Wrapper<'static>; NUM_PD_CONTROLLERS], +) -> ! { + info!("Starting type-c task"); + + // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot + static POWER_POLICY_CHANNEL: StaticCell> = StaticCell::new(); + + let power_policy_channel = POWER_POLICY_CHANNEL.init(PubSubChannel::new()); + let power_policy_publisher = power_policy_channel.dyn_immediate_publisher(); + // Guaranteed to not panic since we initialized the channel above + let power_policy_subscriber = power_policy_channel.dyn_subscriber().unwrap(); + + let service = Service::create( + type_c_service::service::config::Config::default(), + controller_context, + controllers, + power_policy_publisher, + power_policy_subscriber, + ); + + static SERVICE: StaticCell = StaticCell::new(); + let service = SERVICE.init(service); + + type_c_service::task::task(service, wrappers).await; + unreachable!() +} + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_imxrt::init(Default::default()); @@ -169,13 +198,13 @@ async fn main(spawner: Spawner) { info!("Embedded service init"); embedded_services::init().await; - type_c::controller::init(); - info!("Spawining power policy task"); spawner.must_spawn(power_policy_service_task()); - info!("Spawining type-c service task"); - spawner.must_spawn(type_c_service_task()); + static CONTROLLER_LIST: StaticCell = StaticCell::new(); + let controllers = CONTROLLER_LIST.init(IntrusiveList::new()); + static CONTEXT: StaticCell = StaticCell::new(); + let controller_context = CONTEXT.init(embedded_services::type_c::controller::Context::new()); let int_in = Input::new(p.PIO1_7, Pull::Up, Inverter::Disabled); static BUS: StaticCell>> = StaticCell::new(); @@ -211,6 +240,7 @@ async fn main(spawner: Spawner) { static STORAGE: StaticCell> = StaticCell::new(); let storage = STORAGE.init(Storage::new( + controller_context, CONTROLLER0_ID, CONTROLLER0_CFU_ID, [(PORT0_ID, PORT0_PWR_ID), (PORT1_ID, PORT1_PWR_ID)], @@ -231,7 +261,9 @@ async fn main(spawner: Spawner) { let wrapper = WRAPPER.init(ControllerWrapper::try_new(controller_mutex, Default::default(), referenced, Validator).unwrap()); - wrapper.register().await.unwrap(); + info!("Spawning type-c service task"); + spawner.must_spawn(service_task(controller_context, controllers, [wrapper])); + spawner.must_spawn(pd_controller_task(wrapper)); spawner.must_spawn(fw_update_task()); diff --git a/examples/std/Cargo.lock b/examples/std/Cargo.lock index 0a917ced..2e789529 100644 --- a/examples/std/Cargo.lock +++ b/examples/std/Cargo.lock @@ -1756,7 +1756,6 @@ dependencies = [ "embedded-usb-pd", "heapless", "log", - "static_cell", "tps6699x", ] diff --git a/examples/std/src/bin/type_c/basic.rs b/examples/std/src/bin/type_c/basic.rs index bc0714fe..1f8441fe 100644 --- a/examples/std/src/bin/type_c/basic.rs +++ b/examples/std/src/bin/type_c/basic.rs @@ -1,8 +1,8 @@ use embassy_executor::{Executor, Spawner}; use embassy_sync::once_lock::OnceLock; use embassy_time::Timer; -use embedded_services::power; use embedded_services::type_c::{Cached, ControllerId, controller}; +use embedded_services::{IntrusiveList, power}; use embedded_usb_pd::ucsi::lpm; use embedded_usb_pd::{GlobalPortId, PdError as Error}; use log::*; @@ -124,13 +124,13 @@ mod test_controller { } #[embassy_executor::task] -async fn controller_task() { +async fn controller_task(controller_list: &'static IntrusiveList) { static CONTROLLER: OnceLock = OnceLock::new(); static PORTS: [GlobalPortId; 2] = [PORT0_ID, PORT1_ID]; let controller = CONTROLLER.get_or_init(|| test_controller::Controller::new(CONTROLLER0_ID, POWER0_ID, &PORTS)); - controller::register_controller(controller).unwrap(); + controller::register_controller(controller_list, controller).unwrap(); loop { controller.process().await; @@ -141,24 +141,35 @@ async fn controller_task() { async fn task(spawner: Spawner) { embedded_services::init().await; - controller::init(); + static CONTROLLER_LIST: StaticCell = StaticCell::new(); + + let controller_list = CONTROLLER_LIST.init(IntrusiveList::new()); info!("Starting controller task"); - spawner.must_spawn(controller_task()); + spawner.must_spawn(controller_task(controller_list)); // Wait for controller to be registered Timer::after_secs(1).await; - let context = controller::ContextToken::create().unwrap(); + let context = controller::Context::new(); - context.reset_controller(CONTROLLER0_ID).await.unwrap(); + context.reset_controller(controller_list, CONTROLLER0_ID).await.unwrap(); - let status = context.get_controller_status(CONTROLLER0_ID).await.unwrap(); + let status = context + .get_controller_status(controller_list, CONTROLLER0_ID) + .await + .unwrap(); info!("Controller 0 status: {status:#?}"); - let status = context.get_port_status(PORT0_ID, Cached(true)).await.unwrap(); + let status = context + .get_port_status(controller_list, PORT0_ID, Cached(true)) + .await + .unwrap(); info!("Port 0 status: {status:#?}"); - let status = context.get_port_status(PORT1_ID, Cached(true)).await.unwrap(); + let status = context + .get_port_status(controller_list, PORT1_ID, Cached(true)) + .await + .unwrap(); info!("Port 1 status: {status:#?}"); } diff --git a/examples/std/src/bin/type_c/external.rs b/examples/std/src/bin/type_c/external.rs index 2ecc8050..a98af51b 100644 --- a/examples/std/src/bin/type_c/external.rs +++ b/examples/std/src/bin/type_c/external.rs @@ -1,55 +1,26 @@ //! Low-level example of external messaging with a simple type-C service use embassy_executor::{Executor, Spawner}; use embassy_sync::mutex::Mutex; +use embassy_sync::pubsub::PubSubChannel; use embassy_time::Timer; use embedded_services::{ - GlobalRawMutex, power, - type_c::{Cached, ControllerId, external}, + GlobalRawMutex, IntrusiveList, power, + type_c::{Cached, ControllerId, controller::Context, external}, }; use embedded_usb_pd::GlobalPortId; use log::*; use static_cell::StaticCell; -use std_examples::type_c::mock_controller; +use std_examples::type_c::mock_controller::{self, Wrapper}; +use type_c_service::service::{Service, config::Config}; use type_c_service::wrapper::backing::Storage; +const NUM_PD_CONTROLLERS: usize = 1; const CONTROLLER0_ID: ControllerId = ControllerId(0); const PORT0_ID: GlobalPortId = GlobalPortId(0); const POWER0_ID: power::policy::DeviceId = power::policy::DeviceId(0); #[embassy_executor::task] -async fn controller_task() { - static STATE: StaticCell = StaticCell::new(); - let state = STATE.init(mock_controller::ControllerState::new()); - - static STORAGE: StaticCell> = StaticCell::new(); - let backing_storage = STORAGE.init(Storage::new( - CONTROLLER0_ID, - 0, // CFU component ID (unused) - [(PORT0_ID, POWER0_ID)], - )); - static REFERENCED: StaticCell> = - StaticCell::new(); - let referenced = REFERENCED.init( - backing_storage - .create_referenced() - .expect("Failed to create referenced storage"), - ); - - static CONTROLLER: StaticCell> = StaticCell::new(); - let controller = CONTROLLER.init(Mutex::new(mock_controller::Controller::new(state))); - - static WRAPPER: StaticCell = StaticCell::new(); - let wrapper = WRAPPER.init( - mock_controller::Wrapper::try_new( - controller, - Default::default(), - referenced, - crate::mock_controller::Validator, - ) - .expect("Failed to create wrapper"), - ); - - wrapper.register().await.unwrap(); +async fn controller_task(wrapper: &'static Wrapper<'static>) { loop { if let Err(e) = wrapper.process_next_event().await { error!("Error processing wrapper: {e:#?}"); @@ -58,59 +29,141 @@ async fn controller_task() { } #[embassy_executor::task] -async fn task(_spawner: Spawner) { +async fn task(_spawner: Spawner, controller_context: &'static Context) { info!("Starting main task"); embedded_services::init().await; // Allow the controller to initialize and register itself Timer::after_secs(1).await; info!("Getting controller status"); - let controller_status = external::get_controller_status(ControllerId(0)).await.unwrap(); + let controller_status = external::get_controller_status(controller_context, ControllerId(0)) + .await + .unwrap(); info!("Controller status: {controller_status:?}"); info!("Getting port status"); - let port_status = external::get_port_status(GlobalPortId(0), Cached(true)).await.unwrap(); + let port_status = external::get_port_status(controller_context, GlobalPortId(0), Cached(true)) + .await + .unwrap(); info!("Port status: {port_status:?}"); info!("Getting retimer fw update status"); - let rt_fw_update_status = external::port_get_rt_fw_update_status(GlobalPortId(0)).await.unwrap(); + let rt_fw_update_status = external::port_get_rt_fw_update_status(controller_context, GlobalPortId(0)) + .await + .unwrap(); info!("Get retimer fw update status: {rt_fw_update_status:?}"); info!("Setting retimer fw update state"); - external::port_set_rt_fw_update_state(GlobalPortId(0)).await.unwrap(); + external::port_set_rt_fw_update_state(controller_context, GlobalPortId(0)) + .await + .unwrap(); info!("Clearing retimer fw update state"); - external::port_clear_rt_fw_update_state(GlobalPortId(0)).await.unwrap(); + external::port_clear_rt_fw_update_state(controller_context, GlobalPortId(0)) + .await + .unwrap(); info!("Setting retimer compliance"); - external::port_set_rt_compliance(GlobalPortId(0)).await.unwrap(); + external::port_set_rt_compliance(controller_context, GlobalPortId(0)) + .await + .unwrap(); info!("Setting max sink voltage"); - external::set_max_sink_voltage(GlobalPortId(0), Some(5000)) + external::set_max_sink_voltage(controller_context, GlobalPortId(0), Some(5000)) .await .unwrap(); info!("Clearing dead battery flag"); - external::clear_dead_battery_flag(GlobalPortId(0)).await.unwrap(); + external::clear_dead_battery_flag(controller_context, GlobalPortId(0)) + .await + .unwrap(); info!("Reconfiguring retimer"); - external::reconfigure_retimer(GlobalPortId(0)).await.unwrap(); + external::reconfigure_retimer(controller_context, GlobalPortId(0)) + .await + .unwrap(); } #[embassy_executor::task] -async fn type_c_service_task() -> ! { - type_c_service::task(Default::default()).await; - unreachable!() +async fn service_task( + controller_context: &'static Context, + controllers: &'static IntrusiveList, + wrappers: [&'static Wrapper<'static>; NUM_PD_CONTROLLERS], +) { + info!("Starting type-c task"); + + // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot + static POWER_POLICY_CHANNEL: StaticCell> = + StaticCell::new(); + + let power_policy_channel = POWER_POLICY_CHANNEL.init(PubSubChannel::new()); + let power_policy_publisher = power_policy_channel.dyn_immediate_publisher(); + // Guaranteed to not panic since we initialized the channel above + let power_policy_subscriber = power_policy_channel.dyn_subscriber().unwrap(); + + let service = Service::create( + Config::default(), + controller_context, + controllers, + power_policy_publisher, + power_policy_subscriber, + ); + + static SERVICE: StaticCell = StaticCell::new(); + let service = SERVICE.init(service); + + type_c_service::task::task(service, wrappers).await; +} + +fn create_wrapper(controller_context: &'static Context) -> &'static mut Wrapper<'static> { + static STATE: StaticCell = StaticCell::new(); + let state = STATE.init(mock_controller::ControllerState::new()); + + static STORAGE: StaticCell> = StaticCell::new(); + let backing_storage = STORAGE.init(Storage::new( + controller_context, + CONTROLLER0_ID, + 0, // CFU component ID (unused) + [(PORT0_ID, POWER0_ID)], + )); + static REFERENCED: StaticCell> = + StaticCell::new(); + let referenced = REFERENCED.init( + backing_storage + .create_referenced() + .expect("Failed to create referenced storage"), + ); + + static CONTROLLER: StaticCell> = StaticCell::new(); + let controller = CONTROLLER.init(Mutex::new(mock_controller::Controller::new(state))); + + static WRAPPER: StaticCell = StaticCell::new(); + WRAPPER.init( + mock_controller::Wrapper::try_new( + controller, + Default::default(), + referenced, + crate::mock_controller::Validator, + ) + .expect("Failed to create wrapper"), + ) } fn main() { env_logger::builder().filter_level(log::LevelFilter::Trace).init(); + static CONTROLLER_LIST: StaticCell = StaticCell::new(); + let controller_list = CONTROLLER_LIST.init(IntrusiveList::new()); + static CONTEXT: StaticCell = StaticCell::new(); + let context = CONTEXT.init(embedded_services::type_c::controller::Context::new()); + + let wrapper = create_wrapper(context); + static EXECUTOR: StaticCell = StaticCell::new(); let executor = EXECUTOR.init(Executor::new()); executor.run(|spawner| { - spawner.must_spawn(type_c_service_task()); - spawner.must_spawn(task(spawner)); - spawner.must_spawn(controller_task()); + spawner.must_spawn(service_task(context, controller_list, [wrapper])); + spawner.must_spawn(task(spawner, context)); + spawner.must_spawn(controller_task(wrapper)); }); } diff --git a/examples/std/src/bin/type_c/service.rs b/examples/std/src/bin/type_c/service.rs index 5e9a7a35..5b7d3373 100644 --- a/examples/std/src/bin/type_c/service.rs +++ b/examples/std/src/bin/type_c/service.rs @@ -1,19 +1,25 @@ use embassy_executor::{Executor, Spawner}; use embassy_sync::mutex::Mutex; use embassy_sync::once_lock::OnceLock; +use embassy_sync::pubsub::PubSubChannel; use embassy_time::Timer; use embedded_services::power::{self}; -use embedded_services::type_c::{ControllerId, controller}; -use embedded_services::{GlobalRawMutex, comms}; +use embedded_services::type_c::ControllerId; +use embedded_services::type_c::controller::Context; +use embedded_services::{GlobalRawMutex, IntrusiveList, comms}; use embedded_usb_pd::GlobalPortId; use embedded_usb_pd::ado::Ado; use embedded_usb_pd::type_c::Current; use log::*; use static_cell::StaticCell; use std_examples::type_c::mock_controller; +use std_examples::type_c::mock_controller::Wrapper; +use type_c_service::service::Service; +use type_c_service::service::config::Config; use type_c_service::wrapper::backing::{ReferencedStorage, Storage}; use type_c_service::wrapper::message::*; +const NUM_PD_CONTROLLERS: usize = 1; const CONTROLLER0_ID: ControllerId = ControllerId(0); const PORT0_ID: GlobalPortId = GlobalPortId(0); const POWER0_ID: power::policy::DeviceId = power::policy::DeviceId(0); @@ -54,36 +60,10 @@ mod debug { } #[embassy_executor::task] -async fn controller_task(state: &'static mock_controller::ControllerState) { - static STORAGE: StaticCell> = StaticCell::new(); - let storage = STORAGE.init(Storage::new( - CONTROLLER0_ID, - 0, // CFU component ID (unused) - [(PORT0_ID, POWER0_ID)], - )); - static REFERENCED: StaticCell> = StaticCell::new(); - let referenced = REFERENCED.init( - storage - .create_referenced() - .expect("Failed to create referenced storage"), - ); - - static CONTROLLER: StaticCell> = StaticCell::new(); - let controller = CONTROLLER.init(Mutex::new(mock_controller::Controller::new(state))); - - static WRAPPER: StaticCell = StaticCell::new(); - let wrapper = WRAPPER.init( - mock_controller::Wrapper::try_new( - controller, - Default::default(), - referenced, - crate::mock_controller::Validator, - ) - .expect("Failed to create wrapper"), - ); - - wrapper.register().await.unwrap(); - +async fn controller_task( + wrapper: &'static Wrapper<'static>, + controller: &'static Mutex>, +) { controller.lock().await.custom_function(); loop { @@ -109,21 +89,21 @@ async fn controller_task(state: &'static mock_controller::ControllerState) { } #[embassy_executor::task] -async fn task(spawner: Spawner) { +async fn task( + spawner: Spawner, + wrapper: &'static Wrapper<'static>, + controller: &'static Mutex>, + state: &'static mock_controller::ControllerState, +) { embedded_services::init().await; - controller::init(); - // Register debug accessory listener static LISTENER: OnceLock = OnceLock::new(); let listener = LISTENER.get_or_init(debug::Listener::new); comms::register_endpoint(listener, &listener.tp).await.unwrap(); - static STATE: OnceLock = OnceLock::new(); - let state = STATE.get_or_init(mock_controller::ControllerState::new); - info!("Starting controller task"); - spawner.must_spawn(controller_task(state)); + spawner.must_spawn(controller_task(wrapper, controller)); // Wait for controller to be registered Timer::after_secs(1).await; @@ -148,12 +128,6 @@ async fn task(spawner: Spawner) { Timer::after_millis(DELAY_MS).await; } -#[embassy_executor::task] -async fn type_c_service_task() -> ! { - type_c_service::task(Default::default()).await; - unreachable!() -} - #[embassy_executor::task] async fn power_policy_service_task() { power_policy_service::task::task(Default::default()) @@ -161,14 +135,96 @@ async fn power_policy_service_task() { .expect("Failed to start power policy service task"); } +#[embassy_executor::task] +async fn service_task( + controller_context: &'static Context, + controllers: &'static IntrusiveList, + wrappers: [&'static Wrapper<'static>; NUM_PD_CONTROLLERS], +) { + info!("Starting type-c task"); + + // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot + static POWER_POLICY_CHANNEL: StaticCell> = + StaticCell::new(); + + let power_policy_channel = POWER_POLICY_CHANNEL.init(PubSubChannel::new()); + let power_policy_publisher = power_policy_channel.dyn_immediate_publisher(); + // Guaranteed to not panic since we initialized the channel above + let power_policy_subscriber = power_policy_channel.dyn_subscriber().unwrap(); + + let service = Service::create( + Config::default(), + controller_context, + controllers, + power_policy_publisher, + power_policy_subscriber, + ); + + static SERVICE: StaticCell = StaticCell::new(); + let service = SERVICE.init(service); + + type_c_service::task::task(service, wrappers).await; +} + +fn create_wrapper( + context: &'static Context, +) -> ( + &'static mut Wrapper<'static>, + &'static Mutex>, + &'static mock_controller::ControllerState, +) { + static STATE: StaticCell = StaticCell::new(); + let state = STATE.init(mock_controller::ControllerState::new()); + + static STORAGE: StaticCell> = StaticCell::new(); + let storage = STORAGE.init(Storage::new( + context, + CONTROLLER0_ID, + 0, // CFU component ID (unused) + [(PORT0_ID, POWER0_ID)], + )); + static REFERENCED: StaticCell> = StaticCell::new(); + let referenced = REFERENCED.init( + storage + .create_referenced() + .expect("Failed to create referenced storage"), + ); + + static CONTROLLER: StaticCell> = StaticCell::new(); + let controller = CONTROLLER.init(Mutex::new(mock_controller::Controller::new(state))); + + static WRAPPER: StaticCell = StaticCell::new(); + ( + WRAPPER.init( + mock_controller::Wrapper::try_new( + controller, + Default::default(), + referenced, + crate::mock_controller::Validator, + ) + .expect("Failed to create wrapper"), + ), + controller, + state, + ) +} + fn main() { env_logger::builder().filter_level(log::LevelFilter::Trace).init(); static EXECUTOR: StaticCell = StaticCell::new(); let executor = EXECUTOR.init(Executor::new()); + + static CONTROLLER_LIST: StaticCell = StaticCell::new(); + let controller_list = CONTROLLER_LIST.init(IntrusiveList::new()); + static CONTEXT: StaticCell = StaticCell::new(); + let controller_context = CONTEXT.init(embedded_services::type_c::controller::Context::new()); + + let (wrapper, controller, state) = create_wrapper(controller_context); + executor.run(|spawner| { spawner.must_spawn(power_policy_service_task()); - spawner.must_spawn(type_c_service_task()); - spawner.must_spawn(task(spawner)); + spawner.must_spawn(service_task(controller_context, controller_list, [wrapper])); + spawner.must_spawn(task(spawner, wrapper, controller, state)); }); } diff --git a/examples/std/src/bin/type_c/ucsi.rs b/examples/std/src/bin/type_c/ucsi.rs index 77f43ee9..376e0531 100644 --- a/examples/std/src/bin/type_c/ucsi.rs +++ b/examples/std/src/bin/type_c/ucsi.rs @@ -1,8 +1,12 @@ +use crate::mock_controller::Wrapper; use embassy_executor::{Executor, Spawner}; use embassy_sync::mutex::Mutex; +use embassy_sync::pubsub::PubSubChannel; use embedded_services::GlobalRawMutex; +use embedded_services::IntrusiveList; use embedded_services::power::policy::{self, PowerCapability}; use embedded_services::type_c::ControllerId; +use embedded_services::type_c::controller::Context; use embedded_services::type_c::external::{UcsiResponseResult, execute_ucsi_command}; use embedded_usb_pd::GlobalPortId; use embedded_usb_pd::ucsi::lpm::get_connector_capability::OperationModeFlags; @@ -13,9 +17,11 @@ use embedded_usb_pd::ucsi::{Command, lpm, ppm}; use log::*; use static_cell::StaticCell; use std_examples::type_c::mock_controller; +use type_c_service::service::Service; use type_c_service::service::config::Config; use type_c_service::wrapper::backing::{ReferencedStorage, Storage}; +const NUM_PD_CONTROLLERS: usize = 2; const CONTROLLER0_ID: ControllerId = ControllerId(0); const CONTROLLER1_ID: ControllerId = ControllerId(1); const PORT0_ID: GlobalPortId = GlobalPortId(0); @@ -26,54 +32,14 @@ const CFU0_ID: u8 = 0x00; const CFU1_ID: u8 = 0x01; #[embassy_executor::task] -async fn opm_task(spawner: Spawner) { - static STORAGE0: StaticCell> = StaticCell::new(); - let storage0 = STORAGE0.init(Storage::new(CONTROLLER0_ID, CFU0_ID, [(PORT0_ID, POWER0_ID)])); - static REFERENCED0: StaticCell> = StaticCell::new(); - let referenced0 = REFERENCED0.init( - storage0 - .create_referenced() - .expect("Failed to create referenced storage"), - ); - - 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 WRAPPER0: StaticCell = StaticCell::new(); - let wrapper0 = WRAPPER0.init( - mock_controller::Wrapper::try_new(controller0, Default::default(), referenced0, mock_controller::Validator) - .expect("Failed to create wrapper"), - ); - spawner.must_spawn(wrapper_task(wrapper0)); - - static STORAGE1: StaticCell> = StaticCell::new(); - let storage1 = STORAGE1.init(Storage::new(CONTROLLER1_ID, CFU1_ID, [(PORT1_ID, POWER1_ID)])); - static REFERENCED1: StaticCell> = StaticCell::new(); - let referenced1 = REFERENCED1.init( - storage1 - .create_referenced() - .expect("Failed to create referenced storage"), - ); - - 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 WRAPPER1: StaticCell = StaticCell::new(); - let wrapper1 = WRAPPER1.init( - mock_controller::Wrapper::try_new(controller1, Default::default(), referenced1, mock_controller::Validator) - .expect("Failed to create wrapper"), - ); - spawner.must_spawn(wrapper_task(wrapper1)); - +async fn opm_task(context: &'static Context, state: [&'static mock_controller::ControllerState; NUM_PD_CONTROLLERS]) { const CAPABILITY: PowerCapability = PowerCapability { voltage_mv: 20000, current_ma: 5000, }; info!("Resetting PPM..."); - let response: UcsiResponseResult = execute_ucsi_command(Command::PpmCommand(ppm::Command::PpmReset)) + let response: UcsiResponseResult = execute_ucsi_command(context, Command::PpmCommand(ppm::Command::PpmReset)) .await .into(); let response = response.unwrap(); @@ -87,11 +53,14 @@ async fn opm_task(spawner: Spawner) { let mut notifications = NotificationEnable::default(); notifications.set_cmd_complete(true); notifications.set_connect_change(true); - let response: UcsiResponseResult = execute_ucsi_command(Command::PpmCommand(ppm::Command::SetNotificationEnable( - ppm::set_notification_enable::Args { - notification_enable: notifications, - }, - ))) + let response: UcsiResponseResult = execute_ucsi_command( + context, + Command::PpmCommand(ppm::Command::SetNotificationEnable( + ppm::set_notification_enable::Args { + notification_enable: notifications, + }, + )), + ) .await .into(); let response = response.unwrap(); @@ -102,12 +71,14 @@ async fn opm_task(spawner: Spawner) { } info!("Sending command complete ack..."); - let response: UcsiResponseResult = - execute_ucsi_command(Command::PpmCommand(ppm::Command::AckCcCi(ppm::ack_cc_ci::Args { + let response: UcsiResponseResult = execute_ucsi_command( + context, + Command::PpmCommand(ppm::Command::AckCcCi(ppm::ack_cc_ci::Args { ack: *Ack::default().set_command_complete(true), - }))) - .await - .into(); + })), + ) + .await + .into(); let response = response.unwrap(); if !response.cci.ack_command() || response.cci.error() { error!("Sending command complete ack failed: {:?}", response.cci); @@ -116,17 +87,20 @@ async fn opm_task(spawner: Spawner) { } info!("Connecting sinks on both ports"); - state0.connect_sink(CAPABILITY, false).await; - state1.connect_sink(CAPABILITY, false).await; + state[0].connect_sink(CAPABILITY, false).await; + state[1].connect_sink(CAPABILITY, false).await; // Ensure connect flow has time to complete embassy_time::Timer::after_millis(1000).await; info!("Port 0: Get connector status..."); - let response: UcsiResponseResult = execute_ucsi_command(Command::LpmCommand(lpm::GlobalCommand::new( - GlobalPortId(0), - lpm::CommandData::GetConnectorStatus, - ))) + let response: UcsiResponseResult = execute_ucsi_command( + context, + Command::LpmCommand(lpm::GlobalCommand::new( + GlobalPortId(0), + lpm::CommandData::GetConnectorStatus, + )), + ) .await .into(); let response = response.unwrap(); @@ -140,12 +114,14 @@ async fn opm_task(spawner: Spawner) { } info!("Sending command complete ack..."); - let response: UcsiResponseResult = - execute_ucsi_command(Command::PpmCommand(ppm::Command::AckCcCi(ppm::ack_cc_ci::Args { + let response: UcsiResponseResult = execute_ucsi_command( + context, + Command::PpmCommand(ppm::Command::AckCcCi(ppm::ack_cc_ci::Args { ack: *Ack::default().set_command_complete(true).set_connector_change(true), - }))) - .await - .into(); + })), + ) + .await + .into(); let response = response.unwrap(); if !response.cci.ack_command() || response.cci.error() { error!("Sending command complete ack failed: {:?}", response.cci); @@ -157,10 +133,13 @@ async fn opm_task(spawner: Spawner) { } info!("Port 1: Get connector status..."); - let response: UcsiResponseResult = execute_ucsi_command(Command::LpmCommand(lpm::GlobalCommand::new( - GlobalPortId(1), - lpm::CommandData::GetConnectorStatus, - ))) + let response: UcsiResponseResult = execute_ucsi_command( + context, + Command::LpmCommand(lpm::GlobalCommand::new( + GlobalPortId(1), + lpm::CommandData::GetConnectorStatus, + )), + ) .await .into(); let response = response.unwrap(); @@ -174,12 +153,14 @@ async fn opm_task(spawner: Spawner) { } info!("Sending command complete ack..."); - let response: UcsiResponseResult = - execute_ucsi_command(Command::PpmCommand(ppm::Command::AckCcCi(ppm::ack_cc_ci::Args { + let response: UcsiResponseResult = execute_ucsi_command( + context, + Command::PpmCommand(ppm::Command::AckCcCi(ppm::ack_cc_ci::Args { ack: *Ack::default().set_command_complete(true).set_connector_change(true), - }))) - .await - .into(); + })), + ) + .await + .into(); let response = response.unwrap(); if !response.cci.ack_command() || response.cci.error() { error!("Sending command complete ack failed: {:?}", response.cci); @@ -193,8 +174,6 @@ async fn opm_task(spawner: Spawner) { #[embassy_executor::task(pool_size = 2)] async fn wrapper_task(wrapper: &'static mock_controller::Wrapper<'static>) { - wrapper.register().await.unwrap(); - loop { if let Err(e) = wrapper.process_next_event().await { error!("Error processing wrapper: {e:#?}"); @@ -202,36 +181,6 @@ async fn wrapper_task(wrapper: &'static mock_controller::Wrapper<'static>) { } } -#[embassy_executor::task] -async fn type_c_service_task() -> ! { - type_c_service::task(Config { - ucsi_capabilities: UcsiCapabilities { - num_connectors: 2, - bcd_usb_pd_spec: 0x0300, - bcd_type_c_spec: 0x0200, - bcd_battery_charging_spec: 0x0120, - ..Default::default() - }, - ucsi_port_capabilities: Some( - *lpm::get_connector_capability::ResponseData::default() - .set_operation_mode( - *OperationModeFlags::default() - .set_drp(true) - .set_usb2(true) - .set_usb3(true), - ) - .set_consumer(true) - .set_provider(true) - .set_swap_to_dfp(true) - .set_swap_to_snk(true) - .set_swap_to_src(true), - ), - ..Default::default() - }) - .await; - unreachable!() -} - #[embassy_executor::task] async fn power_policy_service_task() { power_policy_service::task::task(Default::default()) @@ -240,14 +189,121 @@ async fn power_policy_service_task() { } #[embassy_executor::task] -async fn task(spawner: Spawner) { +async fn service_task( + config: Config, + controller_context: &'static Context, + controllers: &'static IntrusiveList, + wrappers: [&'static Wrapper<'static>; NUM_PD_CONTROLLERS], +) -> ! { + info!("Starting type-c task"); + + // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot + static POWER_POLICY_CHANNEL: StaticCell< + PubSubChannel, + > = StaticCell::new(); + + let power_policy_channel = POWER_POLICY_CHANNEL.init(PubSubChannel::new()); + let power_policy_publisher = power_policy_channel.dyn_immediate_publisher(); + // Guaranteed to not panic since we initialized the channel above + let power_policy_subscriber = power_policy_channel.dyn_subscriber().unwrap(); + + let service = Service::create( + config, + controller_context, + controllers, + power_policy_publisher, + power_policy_subscriber, + ); + + static SERVICE: StaticCell = StaticCell::new(); + let service = SERVICE.init(service); + + type_c_service::task::task(service, wrappers).await; + unreachable!() +} + +#[embassy_executor::task] +async fn type_c_service_task(spawner: Spawner) { info!("Starting main task"); embedded_services::init().await; + static CONTROLLER_LIST: StaticCell = StaticCell::new(); + let controller_list = CONTROLLER_LIST.init(IntrusiveList::new()); + static CONTEXT: StaticCell = StaticCell::new(); + let context = CONTEXT.init(embedded_services::type_c::controller::Context::new()); + + static STORAGE0: StaticCell> = StaticCell::new(); + let storage0 = STORAGE0.init(Storage::new(context, CONTROLLER0_ID, CFU0_ID, [(PORT0_ID, POWER0_ID)])); + static REFERENCED0: StaticCell> = StaticCell::new(); + let referenced0 = REFERENCED0.init( + storage0 + .create_referenced() + .expect("Failed to create referenced storage"), + ); + + 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 WRAPPER0: StaticCell = StaticCell::new(); + let wrapper0 = WRAPPER0.init( + mock_controller::Wrapper::try_new(controller0, Default::default(), referenced0, mock_controller::Validator) + .expect("Failed to create wrapper"), + ); + + static STORAGE1: StaticCell> = StaticCell::new(); + let storage1 = STORAGE1.init(Storage::new(context, CONTROLLER1_ID, CFU1_ID, [(PORT1_ID, POWER1_ID)])); + static REFERENCED1: StaticCell> = StaticCell::new(); + let referenced1 = REFERENCED1.init( + storage1 + .create_referenced() + .expect("Failed to create referenced storage"), + ); + + 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 WRAPPER1: StaticCell = StaticCell::new(); + let wrapper1 = WRAPPER1.init( + mock_controller::Wrapper::try_new(controller1, Default::default(), referenced1, mock_controller::Validator) + .expect("Failed to create wrapper"), + ); + spawner.must_spawn(power_policy_service_task()); - spawner.must_spawn(type_c_service_task()); - spawner.must_spawn(opm_task(spawner)); + spawner.must_spawn(service_task( + Config { + ucsi_capabilities: UcsiCapabilities { + num_connectors: 2, + bcd_usb_pd_spec: 0x0300, + bcd_type_c_spec: 0x0200, + bcd_battery_charging_spec: 0x0120, + ..Default::default() + }, + ucsi_port_capabilities: Some( + *lpm::get_connector_capability::ResponseData::default() + .set_operation_mode( + *OperationModeFlags::default() + .set_drp(true) + .set_usb2(true) + .set_usb3(true), + ) + .set_consumer(true) + .set_provider(true) + .set_swap_to_dfp(true) + .set_swap_to_snk(true) + .set_swap_to_src(true), + ), + ..Default::default() + }, + context, + controller_list, + [wrapper0, wrapper1], + )); + spawner.must_spawn(wrapper_task(wrapper0)); + spawner.must_spawn(wrapper_task(wrapper1)); + spawner.must_spawn(opm_task(context, [state0, state1])); } fn main() { @@ -256,6 +312,6 @@ fn main() { static EXECUTOR: StaticCell = StaticCell::new(); let executor = EXECUTOR.init(Executor::new()); executor.run(|spawner| { - spawner.must_spawn(task(spawner)); + spawner.must_spawn(type_c_service_task(spawner)); }); } diff --git a/examples/std/src/bin/type_c/unconstrained.rs b/examples/std/src/bin/type_c/unconstrained.rs index 8e127962..c9f489b8 100644 --- a/examples/std/src/bin/type_c/unconstrained.rs +++ b/examples/std/src/bin/type_c/unconstrained.rs @@ -1,16 +1,23 @@ -use embassy_executor::{Executor, Spawner}; +use crate::mock_controller::Wrapper; +use embassy_executor::Executor; use embassy_sync::mutex::Mutex; +use embassy_sync::pubsub::PubSubChannel; use embassy_time::Timer; -use embedded_services::GlobalRawMutex; use embedded_services::power::policy::PowerCapability; use embedded_services::power::{self}; -use embedded_services::type_c::{ControllerId, controller}; +use embedded_services::type_c::ControllerId; +use embedded_services::type_c::controller::Context; +use embedded_services::{GlobalRawMutex, IntrusiveList}; use embedded_usb_pd::GlobalPortId; use log::*; use static_cell::StaticCell; use std_examples::type_c::mock_controller; +use type_c_service::service::Service; +use type_c_service::service::config::Config; use type_c_service::wrapper::backing::{ReferencedStorage, Storage}; +const NUM_PD_CONTROLLERS: usize = 3; + const CONTROLLER0_ID: ControllerId = ControllerId(0); const PORT0_ID: GlobalPortId = GlobalPortId(0); const POWER0_ID: power::policy::DeviceId = power::policy::DeviceId(0); @@ -30,8 +37,6 @@ const DELAY_MS: u64 = 1000; #[embassy_executor::task(pool_size = 3)] async fn controller_task(wrapper: &'static mock_controller::Wrapper<'static>) { - wrapper.register().await.unwrap(); - loop { if let Err(e) = wrapper.process_next_event().await { error!("Error processing wrapper: {e:#?}"); @@ -40,13 +45,110 @@ async fn controller_task(wrapper: &'static mock_controller::Wrapper<'static>) { } #[embassy_executor::task] -async fn task(spawner: Spawner) { +async fn task(state: [&'static mock_controller::ControllerState; NUM_PD_CONTROLLERS]) { embedded_services::init().await; - controller::init(); + const CAPABILITY: PowerCapability = PowerCapability { + voltage_mv: 20000, + current_ma: 5000, + }; + + // Wait for controller to be registered + Timer::after_secs(1).await; + + info!("Connecting port 0, unconstrained"); + state[0].connect_sink(CAPABILITY, true).await; + Timer::after_millis(DELAY_MS).await; + + info!("Connecting port 1, constrained"); + state[1].connect_sink(CAPABILITY, false).await; + Timer::after_millis(DELAY_MS).await; + + info!("Disconnecting port 0"); + state[0].disconnect().await; + Timer::after_millis(DELAY_MS).await; + + info!("Disconnecting port 1"); + state[1].disconnect().await; + Timer::after_millis(DELAY_MS).await; + + info!("Connecting port 0, unconstrained"); + state[0].connect_sink(CAPABILITY, true).await; + Timer::after_millis(DELAY_MS).await; + + info!("Connecting port 1, unconstrained"); + state[1].connect_sink(CAPABILITY, true).await; + Timer::after_millis(DELAY_MS).await; + + info!("Connecting port 2, unconstrained"); + state[2].connect_sink(CAPABILITY, true).await; + Timer::after_millis(DELAY_MS).await; + + info!("Disconnecting port 0"); + state[0].disconnect().await; + Timer::after_millis(DELAY_MS).await; + + info!("Disconnecting port 1"); + state[1].disconnect().await; + Timer::after_millis(DELAY_MS).await; + + info!("Disconnecting port 2"); + state[2].disconnect().await; + Timer::after_millis(DELAY_MS).await; +} + +#[embassy_executor::task] +async fn power_policy_service_task() { + power_policy_service::task::task(Default::default()) + .await + .expect("Failed to start power policy service task"); +} + +#[embassy_executor::task] +async fn service_task( + controller_context: &'static Context, + controllers: &'static IntrusiveList, + wrappers: [&'static Wrapper<'static>; NUM_PD_CONTROLLERS], +) -> ! { + info!("Starting type-c task"); + + // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot + static POWER_POLICY_CHANNEL: StaticCell> = + StaticCell::new(); + + let power_policy_channel = POWER_POLICY_CHANNEL.init(PubSubChannel::new()); + let power_policy_publisher = power_policy_channel.dyn_immediate_publisher(); + // Guaranteed to not panic since we initialized the channel above + let power_policy_subscriber = power_policy_channel.dyn_subscriber().unwrap(); + + let service = Service::create( + Config::default(), + controller_context, + controllers, + power_policy_publisher, + power_policy_subscriber, + ); + + static SERVICE: StaticCell = StaticCell::new(); + let service = SERVICE.init(service); + + type_c_service::task::task(service, wrappers).await; + unreachable!() +} + +fn main() { + env_logger::builder().filter_level(log::LevelFilter::Trace).init(); + + static EXECUTOR: StaticCell = StaticCell::new(); + let executor = EXECUTOR.init(Executor::new()); + + static CONTEXT: StaticCell = StaticCell::new(); + let context = CONTEXT.init(embedded_services::type_c::controller::Context::new()); + static CONTROLLER_LIST: StaticCell = StaticCell::new(); + let controller_list = CONTROLLER_LIST.init(IntrusiveList::new()); static STORAGE: StaticCell> = StaticCell::new(); - let storage = STORAGE.init(Storage::new(CONTROLLER0_ID, CFU0_ID, [(PORT0_ID, POWER0_ID)])); + let storage = STORAGE.init(Storage::new(context, CONTROLLER0_ID, CFU0_ID, [(PORT0_ID, POWER0_ID)])); static REFERENCED: StaticCell> = StaticCell::new(); let referenced = REFERENCED.init( storage @@ -70,7 +172,7 @@ async fn task(spawner: Spawner) { ); static STORAGE1: StaticCell> = StaticCell::new(); - let storage1 = STORAGE1.init(Storage::new(CONTROLLER1_ID, CFU1_ID, [(PORT1_ID, POWER1_ID)])); + let storage1 = STORAGE1.init(Storage::new(context, CONTROLLER1_ID, CFU1_ID, [(PORT1_ID, POWER1_ID)])); static REFERENCED1: StaticCell> = StaticCell::new(); let referenced1 = REFERENCED1.init( storage1 @@ -94,7 +196,7 @@ async fn task(spawner: Spawner) { ); static STORAGE2: StaticCell> = StaticCell::new(); - let storage2 = STORAGE2.init(Storage::new(CONTROLLER2_ID, CFU2_ID, [(PORT2_ID, POWER2_ID)])); + let storage2 = STORAGE2.init(Storage::new(context, CONTROLLER2_ID, CFU2_ID, [(PORT2_ID, POWER2_ID)])); static REFERENCED2: StaticCell> = StaticCell::new(); let referenced2 = REFERENCED2.init( storage2 @@ -117,81 +219,13 @@ async fn task(spawner: Spawner) { .expect("Failed to create wrapper"), ); - info!("Starting controller tasks"); - spawner.must_spawn(controller_task(wrapper0)); - spawner.must_spawn(controller_task(wrapper1)); - spawner.must_spawn(controller_task(wrapper2)); - - const CAPABILITY: PowerCapability = PowerCapability { - voltage_mv: 20000, - current_ma: 5000, - }; - - // Wait for controller to be registered - Timer::after_secs(1).await; - - info!("Connecting port 0, unconstrained"); - state0.connect_sink(CAPABILITY, true).await; - Timer::after_millis(DELAY_MS).await; - - info!("Connecting port 1, constrained"); - state1.connect_sink(CAPABILITY, false).await; - Timer::after_millis(DELAY_MS).await; - - info!("Disconnecting port 0"); - state0.disconnect().await; - Timer::after_millis(DELAY_MS).await; - - info!("Disconnecting port 1"); - state1.disconnect().await; - Timer::after_millis(DELAY_MS).await; - - info!("Connecting port 0, unconstrained"); - state0.connect_sink(CAPABILITY, true).await; - Timer::after_millis(DELAY_MS).await; - - info!("Connecting port 1, unconstrained"); - state1.connect_sink(CAPABILITY, true).await; - Timer::after_millis(DELAY_MS).await; - - info!("Connecting port 2, unconstrained"); - state2.connect_sink(CAPABILITY, true).await; - Timer::after_millis(DELAY_MS).await; - - info!("Disconnecting port 0"); - state0.disconnect().await; - Timer::after_millis(DELAY_MS).await; - - info!("Disconnecting port 1"); - state1.disconnect().await; - Timer::after_millis(DELAY_MS).await; - - info!("Disconnecting port 2"); - state2.disconnect().await; - Timer::after_millis(DELAY_MS).await; -} - -#[embassy_executor::task] -async fn type_c_service_task() -> ! { - type_c_service::task(Default::default()).await; - unreachable!() -} - -#[embassy_executor::task] -async fn power_policy_service_task() { - power_policy_service::task::task(Default::default()) - .await - .expect("Failed to start power policy service task"); -} - -fn main() { - env_logger::builder().filter_level(log::LevelFilter::Trace).init(); - - static EXECUTOR: StaticCell = StaticCell::new(); - let executor = EXECUTOR.init(Executor::new()); executor.run(|spawner| { spawner.must_spawn(power_policy_service_task()); - spawner.must_spawn(type_c_service_task()); - spawner.must_spawn(task(spawner)); + spawner.must_spawn(service_task(context, controller_list, [wrapper0, wrapper1, wrapper2])); + spawner.must_spawn(task([state0, state1, state2])); + info!("Starting controller tasks"); + spawner.must_spawn(controller_task(wrapper0)); + spawner.must_spawn(controller_task(wrapper1)); + spawner.must_spawn(controller_task(wrapper2)); }); } diff --git a/type-c-service/Cargo.toml b/type-c-service/Cargo.toml index f8686f29..d52f8291 100644 --- a/type-c-service/Cargo.toml +++ b/type-c-service/Cargo.toml @@ -25,7 +25,6 @@ embedded-services.workspace = true embedded-usb-pd.workspace = true heapless.workspace = true log = { workspace = true, optional = true } -static_cell = { workspace = true } tps6699x = { workspace = true, features = ["embassy"] } [dev-dependencies] @@ -35,6 +34,7 @@ critical-section = { workspace = true, features = ["std"] } embassy-time-driver = { workspace = true } embassy-futures.workspace = true tokio = { workspace = true, features = ["rt", "macros", "time"] } +static_cell.workspace = true [features] default = [] diff --git a/type-c-service/src/lib.rs b/type-c-service/src/lib.rs index a53d22e3..b1d0a5f1 100644 --- a/type-c-service/src/lib.rs +++ b/type-c-service/src/lib.rs @@ -9,7 +9,6 @@ use core::future::Future; use embedded_services::type_c::event::{ PortEvent, PortNotification, PortNotificationSingle, PortPendingIter, PortStatusChanged, }; -pub use task::task; /// Enum to contain all port event variants #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/type-c-service/src/service/controller.rs b/type-c-service/src/service/controller.rs index de1c2273..f4034e7c 100644 --- a/type-c-service/src/service/controller.rs +++ b/type-c-service/src/service/controller.rs @@ -14,7 +14,7 @@ impl<'a> Service<'a> { &self, controller: ControllerId, ) -> external::Response<'static> { - let status = self.context.get_controller_status(controller).await; + let status = self.context.get_controller_status(self.controllers, controller).await; if let Err(e) = status { error!("Error getting controller status: {:#?}", e); } @@ -26,7 +26,7 @@ impl<'a> Service<'a> { &self, controller: ControllerId, ) -> external::Response<'static> { - let status = self.context.sync_controller_state(controller).await; + let status = self.context.sync_controller_state(self.controllers, controller).await; if let Err(e) = status { error!("Error getting controller sync state: {:#?}", e); } @@ -38,7 +38,7 @@ impl<'a> Service<'a> { &self, controller: ControllerId, ) -> external::Response<'static> { - let status = self.context.reset_controller(controller).await; + let status = self.context.reset_controller(self.controllers, controller).await; if let Err(e) = status { error!("Error resetting controller: {:#?}", e); } diff --git a/type-c-service/src/service/mod.rs b/type-c-service/src/service/mod.rs index 753fc5ba..f826d274 100644 --- a/type-c-service/src/service/mod.rs +++ b/type-c-service/src/service/mod.rs @@ -30,10 +30,6 @@ pub mod vdm; const MAX_SUPPORTED_PORTS: usize = 4; -/// Maximum number of power policy events to buffer -/// Arbitrary number, but power policy events in general shouldn't be too frequent -pub const MAX_POWER_POLICY_EVENTS: usize = 4; - /// Type-C service state #[derive(Default)] struct State { @@ -46,9 +42,14 @@ struct 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 token - context: type_c::controller::ContextToken, + /// Type-C context + context: &'a type_c::controller::Context, + /// Controller intrusive list + controllers: &'a intrusive_list::IntrusiveList, /// Current state state: Mutex, /// Config @@ -94,16 +95,19 @@ impl<'a> Service<'a> { /// Create a new service the given configuration pub fn create( config: config::Config, + context: &'a embedded_services::type_c::controller::Context, + controller_list: &'a intrusive_list::IntrusiveList, power_policy_publisher: DynImmediatePublisher<'a, power_policy::CommsMessage>, power_policy_subscriber: DynSubscriber<'a, power_policy::CommsMessage>, - ) -> Option { - Some(Self { - context: type_c::controller::ContextToken::create()?, + ) -> Self { + Self { + context, state: Mutex::new(State::default()), config, power_policy_event_publisher: power_policy_publisher.into(), power_policy_event_subscriber: Mutex::new(power_policy_subscriber), - }) + controllers: controller_list, + } } /// Get the cached port status @@ -159,11 +163,17 @@ impl<'a> Service<'a> { } /// Process external commands - async fn process_external_command(&self, command: &external::Command) -> external::Response<'static> { + async fn process_external_command( + &self, + controllers: &intrusive_list::IntrusiveList, + command: &external::Command, + ) -> external::Response<'static> { match command { external::Command::Controller(command) => self.process_external_controller_command(command).await, - external::Command::Port(command) => self.process_external_port_command(command).await, - external::Command::Ucsi(command) => external::Response::Ucsi(self.process_ucsi_command(command).await), + external::Command::Port(command) => self.process_external_port_command(command, controllers).await, + external::Command::Ucsi(command) => { + external::Response::Ucsi(self.process_ucsi_command(controllers, command).await) + } } } @@ -179,7 +189,10 @@ impl<'a> Service<'a> { { Either3::First(mut stream) => { if let Some((port_id, event)) = stream - .next(|port_id| self.context.get_port_event(GlobalPortId(port_id as u8))) + .next(|port_id| { + self.context + .get_port_event(self.controllers, GlobalPortId(port_id as u8)) + }) .await? { let port_id = GlobalPortId(port_id as u8); @@ -187,7 +200,10 @@ impl<'a> Service<'a> { match event { PortEventVariant::StatusChanged(status_event) => { // Return a port status changed event - let status = self.context.get_port_status(port_id, Cached(true)).await?; + let status = self + .context + .get_port_status(self.controllers, port_id, Cached(true)) + .await?; return Ok(Event::PortStatusChanged(port_id, status_event, status)); } PortEventVariant::Notification(notification) => { @@ -222,7 +238,7 @@ impl<'a> Service<'a> { } Event::ExternalCommand(request) => { trace!("Processing external command"); - let response = self.process_external_command(&request.command).await; + let response = self.process_external_command(self.controllers, &request.command).await; request.respond(response); Ok(()) } @@ -243,4 +259,8 @@ impl<'a> Service<'a> { pub fn register_comms(&'static self) -> Result<(), intrusive_list::Error> { power_policy::policy::register_message_receiver(&self.power_policy_event_publisher) } + + pub(crate) fn controllers(&self) -> &'a intrusive_list::IntrusiveList { + self.controllers + } } diff --git a/type-c-service/src/service/pd.rs b/type-c-service/src/service/pd.rs index 21934fa7..5076953f 100644 --- a/type-c-service/src/service/pd.rs +++ b/type-c-service/src/service/pd.rs @@ -1,5 +1,6 @@ //! Power Delivery (PD) related functionality. +use embedded_services::intrusive_list; use embedded_usb_pd::{GlobalPortId, PdError, ado::Ado}; use super::Service; @@ -8,7 +9,11 @@ impl Service<'_> { /// Get the oldest unhandled PD alert for the given port. /// /// Returns [`None`] if no alerts are pending. - pub async fn get_pd_alert(&self, port: GlobalPortId) -> Result, PdError> { - self.context.get_pd_alert(port).await + pub async fn get_pd_alert( + &self, + controllers: &intrusive_list::IntrusiveList, + port: GlobalPortId, + ) -> Result, PdError> { + self.context.get_pd_alert(controllers, port).await } } diff --git a/type-c-service/src/service/port.rs b/type-c-service/src/service/port.rs index c44f7965..80517dc8 100644 --- a/type-c-service/src/service/port.rs +++ b/type-c-service/src/service/port.rs @@ -30,40 +30,57 @@ impl<'a> Service<'a> { pub(super) async fn process_external_port_command( &self, command: &external::PortCommand, + controllers: &intrusive_list::IntrusiveList, ) -> external::Response<'static> { debug!("Processing external port command: {:#?}", command); match command.data { external::PortCommandData::PortStatus(cached) => { - self.process_external_port_status(command.port, cached).await + self.process_external_port_status(command.port, cached, controllers) + .await } external::PortCommandData::RetimerFwUpdateGetState => { - self.process_get_rt_fw_update_status(command.port).await + self.process_get_rt_fw_update_status(command.port, controllers).await } external::PortCommandData::RetimerFwUpdateSetState => { - self.process_set_rt_fw_update_state(command.port).await + self.process_set_rt_fw_update_state(command.port, controllers).await } external::PortCommandData::RetimerFwUpdateClearState => { - self.process_clear_rt_fw_update_state(command.port).await + self.process_clear_rt_fw_update_state(command.port, controllers).await + } + external::PortCommandData::SetRetimerCompliance => { + self.process_set_rt_compliance(command.port, controllers).await + } + external::PortCommandData::ReconfigureRetimer => { + self.process_reconfigure_retimer(command.port, controllers).await } - external::PortCommandData::SetRetimerCompliance => self.process_set_rt_compliance(command.port).await, - external::PortCommandData::ReconfigureRetimer => self.process_reconfigure_retimer(command.port).await, external::PortCommandData::SetMaxSinkVoltage { max_voltage_mv } => { - self.process_set_max_sink_voltage(command.port, max_voltage_mv).await + self.process_set_max_sink_voltage(command.port, max_voltage_mv, controllers) + .await + } + external::PortCommandData::ClearDeadBatteryFlag => { + self.process_clear_dead_battery_flag(command.port, controllers).await + } + external::PortCommandData::SendVdm(tx_vdm) => { + self.process_send_vdm(command.port, tx_vdm, controllers).await } - external::PortCommandData::ClearDeadBatteryFlag => self.process_clear_dead_battery_flag(command.port).await, - external::PortCommandData::SendVdm(tx_vdm) => self.process_send_vdm(command.port, tx_vdm).await, external::PortCommandData::SetUsbControl(config) => { - self.process_set_usb_control(command.port, config).await + self.process_set_usb_control(command.port, config, controllers).await + } + external::PortCommandData::GetDpStatus => self.process_get_dp_status(command.port, controllers).await, + external::PortCommandData::SetDpConfig(config) => { + self.process_set_dp_config(command.port, config, controllers).await + } + external::PortCommandData::ExecuteDrst => self.process_execute_drst(command.port, controllers).await, + external::PortCommandData::SetTbtConfig(config) => { + self.process_set_tbt_config(command.port, config, controllers).await } - external::PortCommandData::GetDpStatus => self.process_get_dp_status(command.port).await, - external::PortCommandData::SetDpConfig(config) => self.process_set_dp_config(command.port, config).await, - external::PortCommandData::ExecuteDrst => self.process_execute_drst(command.port).await, - external::PortCommandData::SetTbtConfig(config) => self.process_set_tbt_config(command.port, config).await, external::PortCommandData::SetPdStateMachineConfig(config) => { - self.process_set_pd_state_machine_config(command.port, config).await + self.process_set_pd_state_machine_config(command.port, config, controllers) + .await } external::PortCommandData::SetTypeCStateMachineConfig(state) => { - self.process_set_type_c_state_machine_config(command.port, state).await + self.process_set_type_c_state_machine_config(command.port, state, controllers) + .await } } } @@ -73,8 +90,9 @@ impl<'a> Service<'a> { &self, port_id: GlobalPortId, cached: Cached, + controllers: &intrusive_list::IntrusiveList, ) -> external::Response<'static> { - let status = self.context.get_port_status(port_id, cached).await; + let status = self.context.get_port_status(controllers, port_id, cached).await; if let Err(e) = status { error!("Error getting port status: {:#?}", e); } @@ -82,8 +100,12 @@ impl<'a> Service<'a> { } /// Process get retimer fw update status commands - pub(super) async fn process_get_rt_fw_update_status(&self, port_id: GlobalPortId) -> external::Response<'static> { - let status = self.context.get_rt_fw_update_status(port_id).await; + pub(super) async fn process_get_rt_fw_update_status( + &self, + port_id: GlobalPortId, + controllers: &intrusive_list::IntrusiveList, + ) -> external::Response<'static> { + let status = self.context.get_rt_fw_update_status(controllers, port_id).await; if let Err(e) = status { error!("Error getting retimer fw update status: {:#?}", e); } @@ -92,8 +114,12 @@ impl<'a> Service<'a> { } /// Process set retimer fw update state commands - pub(super) async fn process_set_rt_fw_update_state(&self, port_id: GlobalPortId) -> external::Response<'static> { - let status = self.context.set_rt_fw_update_state(port_id).await; + pub(super) async fn process_set_rt_fw_update_state( + &self, + port_id: GlobalPortId, + controllers: &intrusive_list::IntrusiveList, + ) -> external::Response<'static> { + let status = self.context.set_rt_fw_update_state(controllers, port_id).await; if let Err(e) = status { error!("Error setting retimer fw update state: {:#?}", e); } @@ -102,8 +128,12 @@ impl<'a> Service<'a> { } /// Process clear retimer fw update state commands - pub(super) async fn process_clear_rt_fw_update_state(&self, port_id: GlobalPortId) -> external::Response<'static> { - let status = self.context.clear_rt_fw_update_state(port_id).await; + pub(super) async fn process_clear_rt_fw_update_state( + &self, + port_id: GlobalPortId, + controllers: &intrusive_list::IntrusiveList, + ) -> external::Response<'static> { + let status = self.context.clear_rt_fw_update_state(controllers, port_id).await; if let Err(e) = status { error!("Error clear retimer fw update state: {:#?}", e); } @@ -112,8 +142,12 @@ impl<'a> Service<'a> { } /// Process set retimer compliance - pub(super) async fn process_set_rt_compliance(&self, port_id: GlobalPortId) -> external::Response<'static> { - let status = self.context.set_rt_compliance(port_id).await; + pub(super) async fn process_set_rt_compliance( + &self, + port_id: GlobalPortId, + controllers: &intrusive_list::IntrusiveList, + ) -> external::Response<'static> { + let status = self.context.set_rt_compliance(controllers, port_id).await; if let Err(e) = status { error!("Error set retimer compliance: {:#?}", e); } @@ -121,8 +155,12 @@ impl<'a> Service<'a> { external::Response::Port(status.map(|_| external::PortResponseData::Complete)) } - async fn process_reconfigure_retimer(&self, port_id: GlobalPortId) -> external::Response<'static> { - let status = self.context.reconfigure_retimer(port_id).await; + async fn process_reconfigure_retimer( + &self, + port_id: GlobalPortId, + controllers: &intrusive_list::IntrusiveList, + ) -> external::Response<'static> { + let status = self.context.reconfigure_retimer(controllers, port_id).await; if let Err(e) = status { error!("Error reconfiguring retimer: {:#?}", e); } @@ -134,8 +172,12 @@ impl<'a> Service<'a> { &self, port_id: GlobalPortId, max_voltage_mv: Option, + controllers: &intrusive_list::IntrusiveList, ) -> external::Response<'static> { - let status = self.context.set_max_sink_voltage(port_id, max_voltage_mv).await; + let status = self + .context + .set_max_sink_voltage(controllers, port_id, max_voltage_mv) + .await; if let Err(e) = status { error!("Error setting max voltage: {:#?}", e); } @@ -143,8 +185,12 @@ impl<'a> Service<'a> { external::Response::Port(status.map(|_| external::PortResponseData::Complete)) } - async fn process_clear_dead_battery_flag(&self, port_id: GlobalPortId) -> external::Response<'static> { - let status = self.context.clear_dead_battery_flag(port_id).await; + async fn process_clear_dead_battery_flag( + &self, + port_id: GlobalPortId, + controllers: &intrusive_list::IntrusiveList, + ) -> external::Response<'static> { + let status = self.context.clear_dead_battery_flag(controllers, port_id).await; if let Err(e) = status { error!("Error clearing dead battery flag: {:#?}", e); } @@ -153,8 +199,14 @@ impl<'a> Service<'a> { } /// Process send vdm commands - async fn process_send_vdm(&self, port_id: GlobalPortId, tx_vdm: SendVdm) -> external::Response<'static> { - let status = self.context.send_vdm(port_id, tx_vdm).await; + /// Process send vdm commands + async fn process_send_vdm( + &self, + port_id: GlobalPortId, + tx_vdm: SendVdm, + controllers: &intrusive_list::IntrusiveList, + ) -> external::Response<'static> { + let status = self.context.send_vdm(controllers, port_id, tx_vdm).await; if let Err(e) = status { error!("Error sending VDM data: {:#?}", e); } @@ -167,8 +219,9 @@ impl<'a> Service<'a> { &self, port_id: GlobalPortId, config: UsbControlConfig, + controllers: &intrusive_list::IntrusiveList, ) -> external::Response<'static> { - let status = self.context.set_usb_control(port_id, config).await; + let status = self.context.set_usb_control(controllers, port_id, config).await; if let Err(e) = status { error!("Error setting USB control: {:#?}", e); } @@ -177,8 +230,12 @@ impl<'a> Service<'a> { } /// Process get DisplayPort status commands - async fn process_get_dp_status(&self, port_id: GlobalPortId) -> external::Response<'static> { - let status = self.context.get_dp_status(port_id).await; + async fn process_get_dp_status( + &self, + port_id: GlobalPortId, + controllers: &intrusive_list::IntrusiveList, + ) -> external::Response<'static> { + let status = self.context.get_dp_status(controllers, port_id).await; if let Err(e) = status { error!("Error getting DP status: {:#?}", e); } @@ -187,8 +244,13 @@ impl<'a> Service<'a> { } /// Process set DisplayPort config commands - async fn process_set_dp_config(&self, port_id: GlobalPortId, config: DpConfig) -> external::Response<'static> { - let status = self.context.set_dp_config(port_id, config).await; + async fn process_set_dp_config( + &self, + port_id: GlobalPortId, + config: DpConfig, + controllers: &intrusive_list::IntrusiveList, + ) -> external::Response<'static> { + let status = self.context.set_dp_config(controllers, port_id, config).await; if let Err(e) = status { error!("Error setting DP config: {:#?}", e); } @@ -197,8 +259,12 @@ impl<'a> Service<'a> { } /// Process execute DisplayPort reset commands - async fn process_execute_drst(&self, port_id: GlobalPortId) -> external::Response<'static> { - let status = self.context.execute_drst(port_id).await; + async fn process_execute_drst( + &self, + port_id: GlobalPortId, + controllers: &intrusive_list::IntrusiveList, + ) -> external::Response<'static> { + let status = self.context.execute_drst(controllers, port_id).await; if let Err(e) = status { error!("Error executing DP reset: {:#?}", e); } @@ -207,8 +273,13 @@ impl<'a> Service<'a> { } /// Process set Thunderbolt configuration command - async fn process_set_tbt_config(&self, port_id: GlobalPortId, config: TbtConfig) -> external::Response<'static> { - let status = self.context.set_tbt_config(port_id, config).await; + async fn process_set_tbt_config( + &self, + port_id: GlobalPortId, + config: TbtConfig, + controllers: &intrusive_list::IntrusiveList, + ) -> external::Response<'static> { + let status = self.context.set_tbt_config(controllers, port_id, config).await; if let Err(e) = status { error!("Error setting TBT config: {:#?}", e); } @@ -221,8 +292,12 @@ impl<'a> Service<'a> { &self, port_id: GlobalPortId, config: PdStateMachineConfig, + controllers: &intrusive_list::IntrusiveList, ) -> external::Response<'static> { - let status = self.context.set_pd_state_machine_config(port_id, config).await; + let status = self + .context + .set_pd_state_machine_config(controllers, port_id, config) + .await; if let Err(e) = status { error!("Error setting PD state-machine config: {:#?}", e); } @@ -235,8 +310,12 @@ impl<'a> Service<'a> { &self, port_id: GlobalPortId, state: TypeCStateMachineState, + controllers: &intrusive_list::IntrusiveList, ) -> external::Response<'static> { - let status = self.context.set_type_c_state_machine_config(port_id, state).await; + let status = self + .context + .set_type_c_state_machine_config(controllers, port_id, state) + .await; if let Err(e) = status { error!("Error setting Type-C state-machine config: {:#?}", e); } diff --git a/type-c-service/src/service/power.rs b/type-c-service/src/service/power.rs index c6bb628d..64c77a8c 100644 --- a/type-c-service/src/service/power.rs +++ b/type-c-service/src/service/power.rs @@ -32,9 +32,9 @@ impl<'a> Service<'a> { /// Set the unconstrained state for all ports pub(super) async fn set_unconstrained_all(&self, unconstrained: bool) -> Result<(), Error> { - for port_index in 0..self.context.get_num_ports() { + for port_index in 0..self.context.get_num_ports(self.controllers) { self.context - .set_unconstrained_power(GlobalPortId(port_index as u8), unconstrained) + .set_unconstrained_power(self.controllers, GlobalPortId(port_index as u8), unconstrained) .await?; } Ok(()) @@ -57,7 +57,7 @@ impl<'a> Service<'a> { 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 num_ports = self.context.get_num_ports(self.controllers); let unconstrained_port = state .port_status .iter() @@ -74,7 +74,11 @@ impl<'a> Service<'a> { ); for port_index in 0..num_ports { self.context - .set_unconstrained_power(GlobalPortId(port_index as u8), port_index != unconstrained_index) + .set_unconstrained_power( + self.controllers, + GlobalPortId(port_index as u8), + port_index != unconstrained_index, + ) .await?; } } else { diff --git a/type-c-service/src/service/ucsi.rs b/type-c-service/src/service/ucsi.rs index e11a1602..53bf886e 100644 --- a/type-c-service/src/service/ucsi.rs +++ b/type-c-service/src/service/ucsi.rs @@ -44,10 +44,10 @@ impl<'a> Service<'a> { } /// PPM get capabilities implementation - fn process_get_capabilities(&self) -> ppm::ResponseData { + fn process_get_capabilities(&self, controllers: &intrusive_list::IntrusiveList) -> ppm::ResponseData { debug!("Get PPM capabilities: {:?}", self.config.ucsi_capabilities); let mut capabilities = self.config.ucsi_capabilities; - capabilities.num_connectors = external::get_num_ports() as u8; + capabilities.num_connectors = external::get_num_ports(controllers) as u8; ppm::ResponseData::GetCapability(capabilities) } @@ -55,13 +55,14 @@ impl<'a> Service<'a> { &self, state: &mut State, command: &ucsi::ppm::Command, + controllers: &intrusive_list::IntrusiveList, ) -> Result, PdError> { match command { ppm::Command::SetNotificationEnable(enable) => { self.process_set_notification_enable(state, enable.notification_enable); Ok(None) } - ppm::Command::GetCapability => Ok(Some(self.process_get_capabilities())), + ppm::Command::GetCapability => Ok(Some(self.process_get_capabilities(controllers))), _ => Ok(None), // Other commands are currently no-ops } } @@ -97,6 +98,7 @@ impl<'a> Service<'a> { &self, state: &mut super::State, command: &ucsi::lpm::GlobalCommand, + controllers: &intrusive_list::IntrusiveList, ) -> Result, PdError> { debug!("Processing LPM command: {:?}", command); match command.operation() { @@ -105,11 +107,11 @@ impl<'a> Service<'a> { if let Some(capabilities) = &self.config.ucsi_port_capabilities { Ok(Some(lpm::ResponseData::GetConnectorCapability(*capabilities))) } else { - self.context.execute_ucsi_command(*command).await + self.context.execute_ucsi_command(controllers, *command).await } } lpm::CommandData::GetConnectorStatus => { - let mut response = self.context.execute_ucsi_command(*command).await; + let mut response = self.context.execute_ucsi_command(controllers, *command).await; if let Ok(Some(lpm::ResponseData::GetConnectorStatus(lpm::get_connector_status::ResponseData { status_change: ref mut states_change, status: @@ -129,7 +131,7 @@ impl<'a> Service<'a> { response } - _ => self.context.execute_ucsi_command(*command).await, + _ => self.context.execute_ucsi_command(controllers, *command).await, } } @@ -168,7 +170,11 @@ impl<'a> Service<'a> { } /// Process an external UCSI command - pub(super) async fn process_ucsi_command(&self, command: &GlobalCommand) -> external::UcsiResponse { + pub(super) async fn process_ucsi_command( + &self, + controllers: &intrusive_list::IntrusiveList, + command: &GlobalCommand, + ) -> external::UcsiResponse { let state = &mut self.state.lock().await; let mut next_input = Some(PpmInput::Command(command)); let mut response: external::UcsiResponse = external::UcsiResponse { @@ -212,12 +218,12 @@ impl<'a> Service<'a> { match command { ucsi::GlobalCommand::PpmCommand(ppm_command) => { response.data = self - .process_ppm_command(&mut state.ucsi, ppm_command) + .process_ppm_command(&mut state.ucsi, ppm_command, controllers) .map(|inner| inner.map(ResponseData::Ppm)); } ucsi::GlobalCommand::LpmCommand(lpm_command) => { response.data = self - .process_lpm_command(state, lpm_command) + .process_lpm_command(state, lpm_command, controllers) .await .map(|inner| inner.map(ResponseData::Lpm)); } diff --git a/type-c-service/src/service/vdm.rs b/type-c-service/src/service/vdm.rs index eda35679..43e97e5e 100644 --- a/type-c-service/src/service/vdm.rs +++ b/type-c-service/src/service/vdm.rs @@ -1,5 +1,6 @@ //! VDM (Vendor Defined Messages) related functionality. +use embedded_services::intrusive_list; use embedded_services::type_c::controller::{AttnVdm, OtherVdm}; use embedded_usb_pd::{GlobalPortId, PdError}; @@ -7,12 +8,20 @@ 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 + pub async fn get_other_vdm( + &self, + controllers: &intrusive_list::IntrusiveList, + port_id: GlobalPortId, + ) -> Result { + self.context.get_other_vdm(controllers, 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 + pub async fn get_attn_vdm( + &self, + controllers: &intrusive_list::IntrusiveList, + port_id: GlobalPortId, + ) -> Result { + self.context.get_attn_vdm(controllers, port_id).await } } diff --git a/type-c-service/src/task.rs b/type-c-service/src/task.rs index 0d965e8e..11664a1d 100644 --- a/type-c-service/src/task.rs +++ b/type-c-service/src/task.rs @@ -1,48 +1,49 @@ use core::future::Future; -use embassy_sync::pubsub::PubSubChannel; -use embedded_services::{GlobalRawMutex, error, info, power}; -use static_cell::StaticCell; +use embedded_services::{error, info}; -use crate::service::config::Config; -use crate::service::{MAX_POWER_POLICY_EVENTS, Service}; +use crate::{service::Service, wrapper::ControllerWrapper}; /// Task to run the Type-C service, takes a closure to customize the event loop -pub async fn task_closure<'a, Fut: Future, F: Fn(&'a Service) -> Fut>(config: Config, f: F) { +pub async fn task_closure<'a, M, C, V, Fut: Future, F: Fn(&'a Service) -> Fut, const N: usize>( + service: &'static Service<'a>, + wrappers: [&'a ControllerWrapper<'a, M, C, V>; N], + f: F, +) where + M: embassy_sync::blocking_mutex::raw::RawMutex, + C: embedded_services::sync::Lockable, + V: crate::wrapper::FwOfferValidator, + ::Inner: embedded_services::type_c::controller::Controller, +{ info!("Starting type-c task"); - // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot - static POWER_POLICY_CHANNEL: StaticCell< - PubSubChannel, - > = StaticCell::new(); - - let power_policy_channel = POWER_POLICY_CHANNEL.init(PubSubChannel::new()); - let power_policy_publisher = power_policy_channel.dyn_immediate_publisher(); - let Ok(power_policy_subscriber) = power_policy_channel.dyn_subscriber() else { - error!("Failed to create power policy subscriber"); - return; - }; - - let service = Service::create(config, power_policy_publisher, power_policy_subscriber); - let Some(service) = service else { - error!("Type-C service already initialized"); - return; - }; - - static SERVICE: StaticCell = StaticCell::new(); - let service = SERVICE.init(service); - if service.register_comms().is_err() { error!("Failed to register type-c service endpoint"); return; } + for controller_wrapper in wrappers { + if controller_wrapper.register(service.controllers()).await.is_err() { + error!("Failed to register a controller"); + return; + } + } + loop { f(service).await; } } -pub async fn task(config: Config) { - task_closure(config, |service: &Service| async { +/// Task to run the Type-C service, running the default event loop +pub async fn task<'a, M, C, V, const N: usize>( + service: &'static Service<'a>, + wrappers: [&'a ControllerWrapper<'a, M, C, V>; N], +) where + M: embassy_sync::blocking_mutex::raw::RawMutex, + C: embedded_services::sync::Lockable, + V: crate::wrapper::FwOfferValidator, + ::Inner: embedded_services::type_c::controller::Controller, +{ + task_closure(service, wrappers, |service: &Service| async { if let Err(e) = service.process_next_event().await { error!("Type-C service processing error: {:#?}", e); } diff --git a/type-c-service/src/wrapper/backing.rs b/type-c-service/src/wrapper/backing.rs index baf9cf5a..5ab878d0 100644 --- a/type-c-service/src/wrapper/backing.rs +++ b/type-c-service/src/wrapper/backing.rs @@ -26,9 +26,10 @@ //! //! const NUM_PORTS: usize = 2; //! -//! fn init() { +//! fn init(context: &'static embedded_services::type_c::controller::Context) { //! static STORAGE: StaticCell> = StaticCell::new(); //! let storage = STORAGE.init(Storage::new( +//! context, //! ControllerId(0), //! 0x0, //! [(GlobalPortId(0), power::policy::DeviceId(0)), (GlobalPortId(1), power::policy::DeviceId(1))], @@ -158,6 +159,7 @@ pub trait DynPortState<'a> { /// Service registration objects pub struct Registration<'a> { + pub context: &'a embedded_services::type_c::controller::Context, pub pd_controller: &'a embedded_services::type_c::controller::Device<'a>, pub cfu_device: &'a embedded_services::cfu::component::CfuDevice, pub power_devices: &'a [embedded_services::power::policy::device::Device], @@ -175,6 +177,7 @@ const MAX_BUFFERED_PD_ALERTS: usize = 4; /// Base storage pub struct Storage { // Registration-related + context: &'static embedded_services::type_c::controller::Context, controller_id: ControllerId, pd_ports: [GlobalPortId; N], cfu_device: embedded_services::cfu::component::CfuDevice, @@ -186,11 +189,13 @@ pub struct Storage { impl Storage { pub fn new( + context: &'static embedded_services::type_c::controller::Context, controller_id: ControllerId, cfu_id: ComponentId, ports: [(GlobalPortId, power::policy::DeviceId); N], ) -> Self { Self { + context, controller_id, pd_ports: ports.map(|(port, _)| port), cfu_device: embedded_services::cfu::component::CfuDevice::new(cfu_id), @@ -235,6 +240,7 @@ impl<'a, const N: usize, M: RawMutex> ReferencedStorage<'a, N, M> { { self.state.try_borrow_mut().ok().map(|state| Backing { registration: Registration { + context: self.storage.context, pd_controller: &self.pd_controller, cfu_device: &self.storage.cfu_device, power_devices: &self.storage.power_devices, diff --git a/type-c-service/src/wrapper/mod.rs b/type-c-service/src/wrapper/mod.rs index 38dcb170..fb2853c0 100644 --- a/type-c-service/src/wrapper/mod.rs +++ b/type-c-service/src/wrapper/mod.rs @@ -27,12 +27,12 @@ use embassy_sync::mutex::Mutex; use embassy_sync::signal::Signal; use embassy_time::Instant; use embedded_cfu_protocol::protocol_definitions::{FwUpdateOffer, FwUpdateOfferResponse, FwVersion}; -use embedded_services::GlobalRawMutex; use embedded_services::power::policy::device::StateKind; use embedded_services::power::policy::{self, action}; use embedded_services::sync::Lockable; use embedded_services::type_c::controller::{self, Controller, PortStatus}; use embedded_services::type_c::event::{PortEvent, PortNotificationSingle, PortPending, PortStatusChanged}; +use embedded_services::{GlobalRawMutex, intrusive_list}; use embedded_services::{debug, error, info, trace, warn}; use embedded_usb_pd::ado::Ado; use embedded_usb_pd::{Error, LocalPortId, PdError}; @@ -301,8 +301,10 @@ where let mut pending = PortPending::none(); pending .pend_port(global_port_id.0 as usize) - .map_err(|_| Error::Pd(PdError::InvalidPort))?; - self.registration.pd_controller.notify_ports(pending); + .map_err(|_| PdError::InvalidPort)?; + self.registration + .pd_controller + .notify_ports(self.registration.context, pending); trace!("P{}: Notified service for events: {:#?}", global_port_id.0, events); } @@ -337,8 +339,10 @@ where let mut pending = PortPending::none(); pending .pend_port(global_port_id.0 as usize) - .map_err(|_| Error::Pd(PdError::InvalidPort))?; - self.registration.pd_controller.notify_ports(pending); + .map_err(|_| PdError::InvalidPort)?; + self.registration + .pd_controller + .notify_ports(self.registration.context, pending); Ok(()) } @@ -603,7 +607,11 @@ where } /// Register all devices with their respective services - pub async fn register(&'static self) -> Result<(), Error<::BusError>> { + pub async fn register( + &'static self, + controllers: &intrusive_list::IntrusiveList, + ) -> Result<(), Error<::BusError>> { + // TODO: Unify these devices? for device in self.registration.power_devices { policy::register_device(device).map_err(|_| { error!( @@ -615,7 +623,7 @@ where })?; } - controller::register_controller(self.registration.pd_controller).map_err(|_| { + controller::register_controller(controllers, self.registration.pd_controller).map_err(|_| { error!( "Controller{}: Failed to register PD controller", self.registration.pd_controller.id().0 diff --git a/type-c-service/src/wrapper/vdm.rs b/type-c-service/src/wrapper/vdm.rs index 0eb01fa5..029b7f7d 100644 --- a/type-c-service/src/wrapper/vdm.rs +++ b/type-c-service/src/wrapper/vdm.rs @@ -58,7 +58,9 @@ where pending .pend_port(global_port_id.0 as usize) .map_err(|_| PdError::InvalidPort)?; - self.registration.pd_controller.notify_ports(pending); + self.registration + .pd_controller + .notify_ports(self.registration.context, pending); Ok(()) } }