diff --git a/Cargo.lock b/Cargo.lock index cb379bdf..0b25298a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2519,7 +2519,6 @@ name = "type-c-interface" version = "0.1.0" dependencies = [ "bitfield 0.17.0", - "bitvec", "defmt 0.3.100", "embassy-sync", "embassy-time", diff --git a/examples/rt685s-evk/Cargo.lock b/examples/rt685s-evk/Cargo.lock index ea9969a9..122969dc 100644 --- a/examples/rt685s-evk/Cargo.lock +++ b/examples/rt685s-evk/Cargo.lock @@ -1678,7 +1678,6 @@ name = "type-c-interface" version = "0.1.0" dependencies = [ "bitfield 0.17.0", - "bitvec", "defmt 0.3.100", "embassy-sync", "embassy-time", diff --git a/examples/rt685s-evk/src/bin/type_c.rs b/examples/rt685s-evk/src/bin/type_c.rs index 3b94eec7..2728fd05 100644 --- a/examples/rt685s-evk/src/bin/type_c.rs +++ b/examples/rt685s-evk/src/bin/type_c.rs @@ -25,6 +25,8 @@ use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use tps6699x::asynchronous::embassy as tps6699x; use type_c_interface::port::ControllerId; +use type_c_interface::port::PortRegistration; +use type_c_interface::service::event::PortEvent as ServicePortEvent; use type_c_service::driver::tps6699x::{self as tps6699x_drv}; use type_c_service::service::{EventReceiver, Service}; use type_c_service::wrapper::ControllerWrapper; @@ -33,6 +35,8 @@ use type_c_service::wrapper::proxy::PowerProxyDevice; extern crate rt685s_evk_example; +const CHANNEL_CAPACITY: usize = 4; + const NUM_PD_CONTROLLERS: usize = 1; const CONTROLLER0_ID: ControllerId = ControllerId(0); const PORT0_ID: GlobalPortId = GlobalPortId(0); @@ -163,12 +167,25 @@ async fn main(spawner: Spawner) { static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); let controller_context = CONTROLLER_CONTEXT.init(type_c_interface::service::context::Context::new()); + static PORT0_CHANNEL: Channel = Channel::new(); + static PORT1_CHANNEL: Channel = Channel::new(); static STORAGE: StaticCell> = StaticCell::new(); let storage = STORAGE.init(Storage::new( controller_context, CONTROLLER0_ID, 0, // CFU component ID - [PORT0_ID, PORT1_ID], + [ + PortRegistration { + id: PORT0_ID, + sender: Mutex::new(PORT0_CHANNEL.dyn_sender()), + receiver: PORT0_CHANNEL.dyn_receiver(), + }, + PortRegistration { + id: PORT1_ID, + sender: Mutex::new(PORT1_CHANNEL.dyn_sender()), + receiver: PORT1_CHANNEL.dyn_receiver(), + }, + ], )); static POLICY_CHANNEL0: StaticCell> = StaticCell::new(); diff --git a/examples/rt685s-evk/src/bin/type_c_cfu.rs b/examples/rt685s-evk/src/bin/type_c_cfu.rs index 9e4c9da2..1ea63531 100644 --- a/examples/rt685s-evk/src/bin/type_c_cfu.rs +++ b/examples/rt685s-evk/src/bin/type_c_cfu.rs @@ -28,6 +28,8 @@ use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use tps6699x::asynchronous::embassy as tps6699x; use type_c_interface::port::ControllerId; +use type_c_interface::port::PortRegistration; +use type_c_interface::service::event::PortEvent as ServicePortEvent; use type_c_service::driver::tps6699x::{self as tps6699x_drv}; use type_c_service::service::{EventReceiver, Service}; use type_c_service::wrapper::ControllerWrapper; @@ -36,6 +38,8 @@ use type_c_service::wrapper::proxy::PowerProxyDevice; extern crate rt685s_evk_example; +const CHANNEL_CAPACITY: usize = 4; + bind_interrupts!(struct Irqs { FLEXCOMM2 => embassy_imxrt::i2c::InterruptHandler; }); @@ -247,12 +251,25 @@ async fn main(spawner: Spawner) { static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); let controller_context = CONTROLLER_CONTEXT.init(type_c_interface::service::context::Context::new()); + static PORT0_CHANNEL: Channel = Channel::new(); + static PORT1_CHANNEL: Channel = Channel::new(); static STORAGE: StaticCell> = StaticCell::new(); let storage = STORAGE.init(Storage::new( controller_context, CONTROLLER0_ID, - CONTROLLER0_CFU_ID, - [PORT0_ID, PORT1_ID], + 0, // CFU component ID + [ + PortRegistration { + id: PORT0_ID, + sender: Mutex::new(PORT0_CHANNEL.dyn_sender()), + receiver: PORT0_CHANNEL.dyn_receiver(), + }, + PortRegistration { + id: PORT1_ID, + sender: Mutex::new(PORT1_CHANNEL.dyn_sender()), + receiver: PORT1_CHANNEL.dyn_receiver(), + }, + ], )); static POLICY_CHANNEL0: StaticCell> = StaticCell::new(); diff --git a/examples/std/Cargo.lock b/examples/std/Cargo.lock index a4c0d09d..fc4709fc 100644 --- a/examples/std/Cargo.lock +++ b/examples/std/Cargo.lock @@ -1874,7 +1874,6 @@ name = "type-c-interface" version = "0.1.0" dependencies = [ "bitfield 0.17.0", - "bitvec", "embassy-sync", "embassy-time", "embedded-services", diff --git a/examples/std/src/bin/type_c/basic.rs b/examples/std/src/bin/type_c/basic.rs index 57739b02..2022b646 100644 --- a/examples/std/src/bin/type_c/basic.rs +++ b/examples/std/src/bin/type_c/basic.rs @@ -1,20 +1,24 @@ use embassy_executor::{Executor, Spawner}; -use embassy_sync::once_lock::OnceLock; +use embassy_sync::channel::Channel; +use embassy_sync::mutex::Mutex; use embassy_time::Timer; +use embedded_services::GlobalRawMutex; use embedded_usb_pd::ucsi::lpm; use embedded_usb_pd::{GlobalPortId, PdError as Error}; use log::*; use static_cell::StaticCell; -use type_c_interface::port::{self, Cached, ControllerId}; +use type_c_interface::port::{self, ControllerId, PortRegistration}; use type_c_interface::service::context::{Context, DeviceContainer}; +use type_c_interface::service::event::PortEvent as ServicePortEvent; const CONTROLLER0_ID: ControllerId = ControllerId(0); const PORT0_ID: GlobalPortId = GlobalPortId(0); const PORT1_ID: GlobalPortId = GlobalPortId(1); +const CHANNEL_CAPACITY: usize = 4; mod test_controller { use embedded_usb_pd::ucsi; - use type_c_interface::port::{ControllerStatus, PortStatus}; + use type_c_interface::port::{ControllerStatus, PortRegistration}; use super::*; @@ -29,7 +33,7 @@ mod test_controller { } impl<'a> Controller<'a> { - pub fn new(id: ControllerId, ports: &'a [GlobalPortId]) -> Self { + pub fn new(id: ControllerId, ports: &'a [PortRegistration]) -> Self { Self { controller: port::Device::new(id, ports), } @@ -80,16 +84,8 @@ mod test_controller { } async fn process_port_command(&self, command: port::PortCommand) -> Result { - Ok(match command.data { - port::PortCommandData::PortStatus(Cached(true)) => { - info!("Port status for port {}", command.port.0); - port::PortResponseData::PortStatus(PortStatus::new()) - } - _ => { - info!("Port command for port {}", command.port.0); - port::PortResponseData::Complete - } - }) + info!("Port command for port {}", command.port.0); + Ok(port::PortResponseData::Complete) } pub async fn process(&self) { @@ -109,11 +105,25 @@ mod test_controller { #[embassy_executor::task] async fn controller_task(controller_context: &'static Context) { - static CONTROLLER: OnceLock = OnceLock::new(); - - static PORTS: [GlobalPortId; 2] = [PORT0_ID, PORT1_ID]; - - let controller = CONTROLLER.get_or_init(|| test_controller::Controller::new(CONTROLLER0_ID, &PORTS)); + static PORT0_CHANNEL: Channel = Channel::new(); + static PORT1_CHANNEL: Channel = Channel::new(); + + static PORTS: StaticCell<[PortRegistration; 2]> = StaticCell::new(); + let ports = PORTS.init([ + PortRegistration { + id: PORT0_ID, + sender: Mutex::new(PORT0_CHANNEL.dyn_sender()), + receiver: PORT0_CHANNEL.dyn_receiver(), + }, + PortRegistration { + id: PORT1_ID, + sender: Mutex::new(PORT1_CHANNEL.dyn_sender()), + receiver: PORT1_CHANNEL.dyn_receiver(), + }, + ]); + + static CONTROLLER: StaticCell = StaticCell::new(); + let controller = CONTROLLER.init(test_controller::Controller::new(CONTROLLER0_ID, ports.as_slice())); controller_context.register_controller(controller).unwrap(); loop { @@ -137,18 +147,6 @@ async fn task(spawner: Spawner) { let status = controller_context.get_controller_status(CONTROLLER0_ID).await.unwrap(); info!("Controller 0 status: {status:#?}"); - - let status = controller_context - .get_port_status(PORT0_ID, Cached(true)) - .await - .unwrap(); - info!("Port 0 status: {status:#?}"); - - let status = controller_context - .get_port_status(PORT1_ID, Cached(true)) - .await - .unwrap(); - info!("Port 1 status: {status:#?}"); } fn main() { diff --git a/examples/std/src/bin/type_c/service.rs b/examples/std/src/bin/type_c/service.rs index 346ee26a..d5862bef 100644 --- a/examples/std/src/bin/type_c/service.rs +++ b/examples/std/src/bin/type_c/service.rs @@ -17,8 +17,9 @@ use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use std_examples::type_c::mock_controller; use std_examples::type_c::mock_controller::Wrapper; -use type_c_interface::port::ControllerId; +use type_c_interface::port::{ControllerId, PortRegistration}; use type_c_interface::service::context::Context; +use type_c_interface::service::event::PortEvent as ServicePortEvent; use type_c_service::service::config::Config; use type_c_service::service::{EventReceiver, Service}; use type_c_service::util::power_capability_from_current; @@ -27,6 +28,7 @@ use type_c_service::wrapper::message::*; use type_c_service::wrapper::proxy::PowerProxyDevice; const NUM_PD_CONTROLLERS: usize = 1; +const CHANNEL_CAPACITY: usize = 4; const CONTROLLER0_ID: ControllerId = ControllerId(0); const PORT0_ID: GlobalPortId = GlobalPortId(0); const DELAY_MS: u64 = 1000; @@ -193,12 +195,18 @@ fn create_wrapper( static STATE: StaticCell = StaticCell::new(); let state = STATE.init(mock_controller::ControllerState::new()); + static PORT0_CHANNEL: Channel = Channel::new(); + static STORAGE: StaticCell> = StaticCell::new(); let storage = STORAGE.init(Storage::new( context, CONTROLLER0_ID, 0, // CFU component ID (unused) - [PORT0_ID], + [PortRegistration { + id: PORT0_ID, + sender: Mutex::new(PORT0_CHANNEL.dyn_sender()), + receiver: PORT0_CHANNEL.dyn_receiver(), + }], )); static POLICY_CHANNEL: StaticCell> = diff --git a/examples/std/src/bin/type_c/ucsi.rs b/examples/std/src/bin/type_c/ucsi.rs index 42c16603..4f8f9caf 100644 --- a/examples/std/src/bin/type_c/ucsi.rs +++ b/examples/std/src/bin/type_c/ucsi.rs @@ -22,13 +22,15 @@ use power_policy_service::psu::ArrayEventReceivers; use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use std_examples::type_c::mock_controller; -use type_c_interface::port::ControllerId; +use type_c_interface::port::{ControllerId, PortRegistration}; use type_c_interface::service::context::Context; +use type_c_interface::service::event::PortEvent as ServicePortEvent; use type_c_service::service::config::Config; use type_c_service::service::{EventReceiver, Service}; use type_c_service::wrapper::backing::Storage; use type_c_service::wrapper::proxy::PowerProxyDevice; +const CHANNEL_CAPACITY: usize = 4; const NUM_PD_CONTROLLERS: usize = 2; const CONTROLLER0_ID: ControllerId = ControllerId(0); const CONTROLLER1_ID: ControllerId = ControllerId(1); @@ -229,8 +231,18 @@ async fn task(spawner: Spawner) { static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); let controller_context = CONTROLLER_CONTEXT.init(Context::new()); + static PORT0_CHANNEL: Channel = Channel::new(); static STORAGE0: StaticCell> = StaticCell::new(); - let storage0 = STORAGE0.init(Storage::new(controller_context, CONTROLLER0_ID, CFU0_ID, [PORT0_ID])); + let storage0 = STORAGE0.init(Storage::new( + controller_context, + CONTROLLER0_ID, + CFU0_ID, + [PortRegistration { + id: PORT0_ID, + sender: Mutex::new(PORT0_CHANNEL.dyn_sender()), + receiver: PORT0_CHANNEL.dyn_receiver(), + }], + )); static POLICY_CHANNEL0: StaticCell> = StaticCell::new(); let policy_channel0 = POLICY_CHANNEL0.init(Channel::new()); @@ -280,8 +292,18 @@ async fn task(spawner: Spawner) { let policy_sender1 = policy_channel1.dyn_sender(); let policy_receiver1 = policy_channel1.dyn_receiver(); + static PORT1_CHANNEL: Channel = Channel::new(); static STORAGE1: StaticCell> = StaticCell::new(); - let storage1 = STORAGE1.init(Storage::new(controller_context, CONTROLLER1_ID, CFU1_ID, [PORT1_ID])); + let storage1 = STORAGE1.init(Storage::new( + controller_context, + CONTROLLER1_ID, + CFU1_ID, + [PortRegistration { + id: PORT1_ID, + sender: Mutex::new(PORT1_CHANNEL.dyn_sender()), + receiver: PORT1_CHANNEL.dyn_receiver(), + }], + )); static INTERMEDIATE1: StaticCell< type_c_service::wrapper::backing::IntermediateStorage< 1, diff --git a/examples/std/src/bin/type_c/unconstrained.rs b/examples/std/src/bin/type_c/unconstrained.rs index 4ce1ddb6..97e54b5b 100644 --- a/examples/std/src/bin/type_c/unconstrained.rs +++ b/examples/std/src/bin/type_c/unconstrained.rs @@ -19,10 +19,14 @@ use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use std_examples::type_c::mock_controller; use type_c_interface::port::ControllerId; +use type_c_interface::port::PortRegistration; +use type_c_interface::service::event::PortEvent as ServicePortEvent; use type_c_service::service::{EventReceiver, Service}; use type_c_service::wrapper::backing::{IntermediateStorage, ReferencedStorage, Storage}; use type_c_service::wrapper::proxy::PowerProxyDevice; +const CHANNEL_CAPACITY: usize = 4; + const NUM_PD_CONTROLLERS: usize = 3; const CONTROLLER0_ID: ControllerId = ControllerId(0); @@ -87,8 +91,18 @@ async fn task(spawner: Spawner) { let policy_sender0 = policy_channel0.dyn_sender(); let policy_receiver0 = policy_channel0.dyn_receiver(); + static PORT0_CHANNEL: Channel = Channel::new(); static STORAGE0: StaticCell> = StaticCell::new(); - let storage0 = STORAGE0.init(Storage::new(controller_context, CONTROLLER0_ID, CFU0_ID, [PORT0_ID])); + let storage0 = STORAGE0.init(Storage::new( + controller_context, + CONTROLLER0_ID, + CFU0_ID, + [PortRegistration { + id: PORT0_ID, + sender: Mutex::new(PORT0_CHANNEL.dyn_sender()), + receiver: PORT0_CHANNEL.dyn_receiver(), + }], + )); static INTERMEDIATE0: StaticCell< IntermediateStorage<1, GlobalRawMutex, DynamicSender<'static, psu::event::EventData>>, > = StaticCell::new(); @@ -123,8 +137,18 @@ async fn task(spawner: Spawner) { let policy_sender1 = policy_channel1.dyn_sender(); let policy_receiver1 = policy_channel1.dyn_receiver(); + static PORT1_CHANNEL: Channel = Channel::new(); static STORAGE1: StaticCell> = StaticCell::new(); - let storage1 = STORAGE1.init(Storage::new(controller_context, CONTROLLER1_ID, CFU1_ID, [PORT1_ID])); + let storage1 = STORAGE1.init(Storage::new( + controller_context, + CONTROLLER1_ID, + CFU1_ID, + [PortRegistration { + id: PORT1_ID, + sender: Mutex::new(PORT1_CHANNEL.dyn_sender()), + receiver: PORT1_CHANNEL.dyn_receiver(), + }], + )); static INTERMEDIATE1: StaticCell< IntermediateStorage<1, GlobalRawMutex, DynamicSender<'static, psu::event::EventData>>, > = StaticCell::new(); @@ -159,8 +183,18 @@ async fn task(spawner: Spawner) { let policy_sender2 = policy_channel2.dyn_sender(); let policy_receiver2 = policy_channel2.dyn_receiver(); + static PORT2_CHANNEL: Channel = Channel::new(); static STORAGE2: StaticCell> = StaticCell::new(); - let storage2 = STORAGE2.init(Storage::new(controller_context, CONTROLLER2_ID, CFU2_ID, [PORT2_ID])); + let storage2 = STORAGE2.init(Storage::new( + controller_context, + CONTROLLER2_ID, + CFU2_ID, + [PortRegistration { + id: PORT2_ID, + sender: Mutex::new(PORT2_CHANNEL.dyn_sender()), + receiver: PORT2_CHANNEL.dyn_receiver(), + }], + )); static INTERMEDIATE2: StaticCell< IntermediateStorage<1, GlobalRawMutex, DynamicSender<'static, psu::event::EventData>>, > = StaticCell::new(); diff --git a/examples/std/src/lib/type_c/mock_controller.rs b/examples/std/src/lib/type_c/mock_controller.rs index 5f4d4e96..f9783011 100644 --- a/examples/std/src/lib/type_c/mock_controller.rs +++ b/examples/std/src/lib/type_c/mock_controller.rs @@ -10,12 +10,12 @@ use log::{debug, info, trace}; use power_policy_interface::capability::PowerCapability; use type_c_interface::port::{ AttnVdm, ControllerStatus, DpConfig, DpPinConfig, DpStatus, OtherVdm, PdStateMachineConfig, PortStatus, - RetimerFwUpdateState, SendVdm, TbtConfig, TypeCStateMachineState, UsbControlConfig, event::PortEvent, + RetimerFwUpdateState, SendVdm, TbtConfig, TypeCStateMachineState, UsbControlConfig, event::PortEventBitfield, }; use type_c_service::util::power_capability_from_current; pub struct ControllerState { - events: Signal, + events: Signal, status: Mutex, pd_alert: Mutex>, } @@ -38,7 +38,7 @@ impl ControllerState { ConnectionState::Attached }); - let mut events = PortEvent::none(); + let mut events = PortEventBitfield::none(); match role { PowerRole::Source => { status.available_source_contract = Some(capability); @@ -67,7 +67,7 @@ impl ControllerState { pub async fn disconnect(&self) { *self.status.lock().await = PortStatus::default(); - let mut events = PortEvent::none(); + let mut events = PortEventBitfield::none(); events.status.set_plug_inserted_or_removed(true); self.events.signal(events); } @@ -82,7 +82,7 @@ impl ControllerState { pub async fn send_pd_alert(&self, ado: Ado) { *self.pd_alert.lock().await = Some(ado); - let mut events = PortEvent::none(); + let mut events = PortEventBitfield::none(); events.notification.set_alert(true); self.events.signal(events); } @@ -96,14 +96,14 @@ impl Default for ControllerState { pub struct Controller<'a> { state: &'a ControllerState, - events: PortEvent, + events: PortEventBitfield, } impl<'a> Controller<'a> { pub fn new(state: &'a ControllerState) -> Self { Self { state, - events: PortEvent::none(), + events: PortEventBitfield::none(), } } @@ -123,10 +123,10 @@ impl type_c_interface::port::Controller for Controller<'_> { Ok(()) } - async fn clear_port_events(&mut self, _port: LocalPortId) -> Result> { + async fn clear_port_events(&mut self, _port: LocalPortId) -> Result> { let events = self.events; debug!("Clear port events: {events:#?}"); - self.events = PortEvent::none(); + self.events = PortEventBitfield::none(); Ok(events) } diff --git a/type-c-interface/Cargo.toml b/type-c-interface/Cargo.toml index b425a1fe..6d33373b 100644 --- a/type-c-interface/Cargo.toml +++ b/type-c-interface/Cargo.toml @@ -7,7 +7,6 @@ repository.workspace = true [dependencies] bitfield.workspace = true -bitvec.workspace = true embassy-sync.workspace = true embassy-time.workspace = true log = { workspace = true, optional = true } diff --git a/type-c-interface/src/port/event.rs b/type-c-interface/src/port/event.rs index eb951079..312680fd 100644 --- a/type-c-interface/src/port/event.rs +++ b/type-c-interface/src/port/event.rs @@ -1,18 +1,18 @@ //! This module provides TCPM event types and bitfields. //! Hardware typically uses bitfields to store pending events/interrupts so we provide generic versions of these. -//! [`PortStatusChanged`] contains events related to the overall port state (plug state, power contract, etc). -//! Processing these events typically requires acessing similar registers so they are grouped together. -//! [`PortNotification`] contains events that are typically more message-like (PD alerts, VDMs, etc) and can be processed independently. -//! Consequently [`PortNotification`] implements iterator traits to allow for processing these events as a stream. +//! [`PortStatusEventBitfield`] contains events related to the overall port state (plug state, power contract, etc). +//! Processing these events typically requires accessing similar registers so they are grouped together. +//! [`PortNotificationEventBitfield`] contains events that are typically more message-like (PD alerts, VDMs, etc) and can be processed independently. +//! Consequently [`PortNotificationEventBitfield`] implements iterator traits to allow for processing these events as a stream. use bitfield::bitfield; -use bitvec::BitArr; -use embedded_services::error; + +use crate::port::{AttnVdm, OtherVdm}; bitfield! { /// Raw bitfield of possible port status events #[derive(Copy, Clone, PartialEq, Eq, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] - struct PortStatusChangedRaw(u16); + struct PortStatusEventBitfieldRaw(u16); impl Debug; /// Plug inserted or removed pub u8, plug_inserted_or_removed, set_plug_inserted_or_removed: 0, 0; @@ -34,31 +34,23 @@ bitfield! { pub u8, pd_hard_reset, set_pd_hard_reset: 8, 8; } -/// Port pending errors -#[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PortPendingError { - /// Invalid port - InvalidPort(usize), -} - /// Port status change events /// This is a type-safe wrapper around the raw bitfield /// These events are related to the overall port state and typically need to be considered together. #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PortStatusChanged(PortStatusChangedRaw); +pub struct PortStatusEventBitfield(PortStatusEventBitfieldRaw); -impl PortStatusChanged { +impl PortStatusEventBitfield { /// Create a new PortEventKind with no pending events pub const fn none() -> Self { - Self(PortStatusChangedRaw(0)) + Self(PortStatusEventBitfieldRaw(0)) } /// Returns the union of self and other - pub fn union(self, other: PortStatusChanged) -> PortStatusChanged { + pub fn union(self, other: PortStatusEventBitfield) -> PortStatusEventBitfield { // This spacing is what rustfmt wants - PortStatusChanged(PortStatusChangedRaw(self.0.0 | other.0.0)) + PortStatusEventBitfield(PortStatusEventBitfieldRaw(self.0.0 | other.0.0)) } /// Returns true if a plug was inserted or removed @@ -156,7 +148,7 @@ bitfield! { /// Raw bitfield of possible port notification events #[derive(Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] - struct PortNotificationRaw(u16); + struct PortNotificationEventBitfieldRaw(u16); impl Debug; /// PD alert pub u8, alert, set_alert: 0, 0; @@ -181,18 +173,18 @@ bitfield! { /// These events are unrelated to the overall port state and each other. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PortNotification(PortNotificationRaw); +pub struct PortNotificationEventBitfield(PortNotificationEventBitfieldRaw); -impl PortNotification { +impl PortNotificationEventBitfield { /// Create a new PortNotification with no pending events pub const fn none() -> Self { - Self(PortNotificationRaw(0)) + Self(PortNotificationEventBitfieldRaw(0)) } /// Returns the union of self and other - pub fn union(self, other: PortNotification) -> PortNotification { + pub fn union(self, other: PortNotificationEventBitfield) -> PortNotificationEventBitfield { // This spacing is what rustfmt wants - PortNotification(PortNotificationRaw(self.0.0 | other.0.0)) + PortNotificationEventBitfield(PortNotificationEventBitfieldRaw(self.0.0 | other.0.0)) } /// Returns true if an alert was received @@ -290,10 +282,26 @@ pub enum VdmNotification { OtherReceived, } -/// Individual port notifications +/// VDM event data +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum VdmData { + /// Entered custom mode + Entered(OtherVdm), + /// Exited custom mode + Exited(OtherVdm), + /// Received a vendor-defined other message + ReceivedOther(OtherVdm), + /// Received a vendor-defined attention message + ReceivedAttn(AttnVdm), +} + +/// Enum to contain all port event variants #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PortNotificationSingle { +pub enum PortEvent { + /// Port status change events + StatusChanged(PortStatusEventBitfield), /// PD alert Alert, /// VDM @@ -306,34 +314,34 @@ pub enum PortNotificationSingle { DpStatusUpdate, } -impl Iterator for PortNotification { - type Item = PortNotificationSingle; +impl Iterator for PortNotificationEventBitfield { + type Item = PortEvent; fn next(&mut self) -> Option { if self.alert() { self.set_alert(false); - Some(PortNotificationSingle::Alert) + Some(PortEvent::Alert) } else if self.custom_mode_entered() { self.set_custom_mode_entered(false); - Some(PortNotificationSingle::Vdm(VdmNotification::Entered)) + Some(PortEvent::Vdm(VdmNotification::Entered)) } else if self.custom_mode_exited() { self.set_custom_mode_exited(false); - Some(PortNotificationSingle::Vdm(VdmNotification::Exited)) + Some(PortEvent::Vdm(VdmNotification::Exited)) } else if self.custom_mode_attention_received() { self.set_custom_mode_attention_received(false); - Some(PortNotificationSingle::Vdm(VdmNotification::AttentionReceived)) + Some(PortEvent::Vdm(VdmNotification::AttentionReceived)) } else if self.custom_mode_other_vdm_received() { self.set_custom_mode_other_vdm_received(false); - Some(PortNotificationSingle::Vdm(VdmNotification::OtherReceived)) + Some(PortEvent::Vdm(VdmNotification::OtherReceived)) } else if self.discover_mode_completed() { self.set_discover_mode_completed(false); - Some(PortNotificationSingle::DiscoverModeCompleted) + Some(PortEvent::DiscoverModeCompleted) } else if self.usb_mux_error_recovery() { self.set_usb_mux_error_recovery(false); - Some(PortNotificationSingle::UsbMuxErrorRecovery) + Some(PortEvent::UsbMuxErrorRecovery) } else if self.dp_status_update() { self.set_dp_status_update(false); - Some(PortNotificationSingle::DpStatusUpdate) + Some(PortEvent::DpStatusUpdate) } else { None } @@ -343,304 +351,146 @@ impl Iterator for PortNotification { /// Overall port event type #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PortEvent { +pub struct PortEventBitfield { /// Port status change events - pub status: PortStatusChanged, + pub status: PortStatusEventBitfield, /// Port notification events - pub notification: PortNotification, + pub notification: PortNotificationEventBitfield, } -impl PortEvent { +impl PortEventBitfield { /// Creates a new PortEvent with no pending events pub const fn none() -> Self { Self { - status: PortStatusChanged::none(), - notification: PortNotification::none(), + status: PortStatusEventBitfield::none(), + notification: PortNotificationEventBitfield::none(), } } /// Returns the union of self and other - pub fn union(self, other: PortEvent) -> PortEvent { - PortEvent { + pub fn union(self, other: PortEventBitfield) -> PortEventBitfield { + PortEventBitfield { status: self.status.union(other.status), notification: self.notification.union(other.notification), } } } -impl Default for PortEvent { +impl Default for PortEventBitfield { fn default() -> Self { Self::none() } } -impl From for PortEvent { - fn from(status: PortStatusChanged) -> Self { +impl From for PortEventBitfield { + fn from(status: PortStatusEventBitfield) -> Self { Self { status, - notification: PortNotification::none(), + notification: PortNotificationEventBitfield::none(), } } } -impl From for PortEvent { - fn from(notification: PortNotification) -> Self { +impl From for PortEventBitfield { + fn from(notification: PortNotificationEventBitfield) -> Self { Self { - status: PortStatusChanged::none(), + status: PortStatusEventBitfield::none(), notification, } } } -/// Bit vector type to store pending port events -type PortPendingVec = BitArr!(for 32, in u32); - -/// Pending port events -/// -/// This type works using usize to allow use with both global and local port IDs. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(transparent)] -pub struct PortPending(PortPendingVec); - -impl PortPending { - /// Creates a new PortPending with no pending ports - pub const fn none() -> Self { - Self(PortPendingVec::ZERO) - } - - /// Returns true if there are no pending ports - pub fn is_none(&self) -> bool { - self.0 == PortPendingVec::ZERO - } - - /// Marks the given port as pending - pub fn pend_port(&mut self, port: usize) -> Result<(), PortPendingError> { - if port >= self.0.len() { - return Err(PortPendingError::InvalidPort(port)); - } - self.0.set(port, true); - - Ok(()) - } - - /// Marks the indexes given by the iterator as pending - pub fn pend_ports>(&mut self, iter: I) { - for port in iter { - if self.pend_port(port).is_err() { - error!("Error pending port {}", port); - } - } - } - - /// Clears the pending status of the given port - pub fn clear_port(&mut self, port: usize) -> Result<(), PortPendingError> { - if port >= self.0.len() { - return Err(PortPendingError::InvalidPort(port)); - } - - self.0.set(port, false); - Ok(()) - } - - /// Returns true if the given port is pending - pub fn is_pending(&self, port: usize) -> Result { - Ok(*self.0.get(port).ok_or(PortPendingError::InvalidPort(port))?) - } - - /// Returns a combination of the current pending ports and other - pub fn union(&self, other: PortPending) -> PortPending { - PortPending(self.0 | other.0) - } - - /// Returns the number of bits in Self - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> usize { - self.0.len() - } -} - -impl From for u32 { - fn from(flags: PortPending) -> Self { - flags.0.data[0] - } -} - -impl Default for PortPending { - fn default() -> Self { - Self::none() - } -} - -impl FromIterator for PortPending { - fn from_iter>(iter: T) -> Self { - let mut flags = PortPending::none(); - flags.pend_ports(iter); - flags - } -} - -/// An iterator over the pending port event flags -#[derive(Copy, Clone)] -pub struct PortPendingIter { - /// The flags being iterated over - flags: PortPending, - /// The current index in the flags - index: usize, -} - -impl Iterator for PortPendingIter { - type Item = usize; - - fn next(&mut self) -> Option { - while self.index < self.flags.len() { - let port_index = self.index; - self.index += 1; - if self.flags.is_pending(port_index).unwrap_or(false) { - if self.flags.clear_port(port_index).is_ok() { - return Some(port_index); - } else { - continue; - } - } - } - None - } -} - -impl IntoIterator for PortPending { - type Item = usize; - type IntoIter = PortPendingIter; - - fn into_iter(self) -> PortPendingIter { - PortPendingIter { flags: self, index: 0 } - } -} - #[cfg(test)] #[allow(clippy::unwrap_used)] mod tests { use super::*; - #[test] - fn test_port_event_flags_iter() { - let mut pending = PortPending::none(); - - pending.pend_port(0).unwrap(); - pending.pend_port(1).unwrap(); - pending.pend_port(2).unwrap(); - pending.pend_port(10).unwrap(); - pending.pend_port(23).unwrap(); - pending.pend_port(31).unwrap(); - - let result = pending.pend_port(32); - let expected = Err(PortPendingError::InvalidPort(32)); - assert_eq!(expected, result); - - let mut iter = pending.into_iter(); - assert_eq!(iter.next(), Some(0)); - assert_eq!(iter.next(), Some(1)); - assert_eq!(iter.next(), Some(2)); - assert_eq!(iter.next(), Some(10)); - assert_eq!(iter.next(), Some(23)); - assert_eq!(iter.next(), Some(31)); - assert_eq!(iter.next(), None); - } - #[test] fn test_port_notification_iter_all() { - let mut notification = PortNotification::none(); + let mut notification = PortNotificationEventBitfield::none(); notification.set_alert(true); notification.set_custom_mode_entered(true); - assert_eq!(notification.next(), Some(PortNotificationSingle::Alert)); - assert_eq!( - notification.next(), - Some(PortNotificationSingle::Vdm(VdmNotification::Entered)) - ); + assert_eq!(notification.next(), Some(PortEvent::Alert)); + assert_eq!(notification.next(), Some(PortEvent::Vdm(VdmNotification::Entered))); assert_eq!(notification.next(), None); } #[test] fn test_port_notification_iter_alert() { - let mut notification = PortNotification::none(); + let mut notification = PortNotificationEventBitfield::none(); notification.set_alert(true); - assert_eq!(notification.next(), Some(PortNotificationSingle::Alert)); + assert_eq!(notification.next(), Some(PortEvent::Alert)); assert_eq!(notification.next(), None); } #[test] fn test_port_notification_iter_custom_mode_entered() { - let mut notification = PortNotification::none(); + let mut notification = PortNotificationEventBitfield::none(); notification.set_custom_mode_entered(true); - assert_eq!( - notification.next(), - Some(PortNotificationSingle::Vdm(VdmNotification::Entered)) - ); + assert_eq!(notification.next(), Some(PortEvent::Vdm(VdmNotification::Entered))); assert_eq!(notification.next(), None); } #[test] fn test_port_notification_iter_custom_mode_exited() { - let mut notification = PortNotification::none(); + let mut notification = PortNotificationEventBitfield::none(); notification.set_custom_mode_exited(true); - assert_eq!( - notification.next(), - Some(PortNotificationSingle::Vdm(VdmNotification::Exited)) - ); + assert_eq!(notification.next(), Some(PortEvent::Vdm(VdmNotification::Exited))); assert_eq!(notification.next(), None); } #[test] fn test_port_notification_iter_custom_mode_attention_received() { - let mut notification = PortNotification::none(); + let mut notification = PortNotificationEventBitfield::none(); notification.set_custom_mode_attention_received(true); assert_eq!( notification.next(), - Some(PortNotificationSingle::Vdm(VdmNotification::AttentionReceived)) + Some(PortEvent::Vdm(VdmNotification::AttentionReceived)) ); assert_eq!(notification.next(), None); } #[test] fn test_port_notification_iter_custom_mode_other_vdm_received() { - let mut notification = PortNotification::none(); + let mut notification = PortNotificationEventBitfield::none(); notification.set_custom_mode_other_vdm_received(true); assert_eq!( notification.next(), - Some(PortNotificationSingle::Vdm(VdmNotification::OtherReceived)) + Some(PortEvent::Vdm(VdmNotification::OtherReceived)) ); assert_eq!(notification.next(), None); } #[test] fn test_port_notification_iter_discover_mode_completed() { - let mut notification = PortNotification::none(); + let mut notification = PortNotificationEventBitfield::none(); notification.set_discover_mode_completed(true); - assert_eq!(notification.next(), Some(PortNotificationSingle::DiscoverModeCompleted)); + assert_eq!(notification.next(), Some(PortEvent::DiscoverModeCompleted)); assert_eq!(notification.next(), None); } #[test] fn test_port_notification_iter_usb_mux_error_recovery() { - let mut notification = PortNotification::none(); + let mut notification = PortNotificationEventBitfield::none(); notification.set_usb_mux_error_recovery(true); - assert_eq!(notification.next(), Some(PortNotificationSingle::UsbMuxErrorRecovery)); + assert_eq!(notification.next(), Some(PortEvent::UsbMuxErrorRecovery)); assert_eq!(notification.next(), None); } #[test] fn test_port_notification_iter_dp_status_update() { - let mut notification = PortNotification::none(); + let mut notification = PortNotificationEventBitfield::none(); notification.set_dp_status_update(true); - assert_eq!(notification.next(), Some(PortNotificationSingle::DpStatusUpdate)); + assert_eq!(notification.next(), Some(PortEvent::DpStatusUpdate)); assert_eq!(notification.next(), None); } } diff --git a/type-c-interface/src/port/mod.rs b/type-c-interface/src/port/mod.rs index f52836b6..2f6c77fb 100644 --- a/type-c-interface/src/port/mod.rs +++ b/type-c-interface/src/port/mod.rs @@ -1,6 +1,8 @@ //! PD controller related code use core::future::Future; +use embassy_sync::channel::{DynamicReceiver, DynamicSender}; +use embassy_sync::mutex::Mutex; use embedded_usb_pd::ucsi::{self, lpm}; use embedded_usb_pd::{ DataRole, Error, GlobalPortId, LocalPortId, PdError, PlugOrientation, PowerRole, @@ -14,7 +16,8 @@ use embedded_services::{GlobalRawMutex, intrusive_list}; pub mod event; -use event::{PortEvent, PortPending}; +use crate::port::event::PortEventBitfield; +use crate::service::event::PortEvent as ServicePortEvent; /// Length of the Other VDM data pub const OTHER_VDM_LEN: usize = 29; @@ -23,11 +26,6 @@ pub const ATTN_VDM_LEN: usize = 9; /// maximum number of data objects in a VDM pub const MAX_NUM_DATA_OBJECTS: usize = 7; // 7 VDOs of 4 bytes each -/// Newtype to help clarify arguments to port status commands -#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Cached(pub bool); - /// Controller ID #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -275,10 +273,6 @@ pub enum TypeCStateMachineState { #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum PortCommandData { - /// Get port status - PortStatus(Cached), - /// Get and clear events - ClearEvents, /// Get retimer fw update state RetimerFwUpdateGetState, /// Set retimer fw update state @@ -289,8 +283,6 @@ pub enum PortCommandData { SetRetimerCompliance, /// Reconfigure retimer ReconfigureRetimer, - /// Get oldest unhandled PD alert - GetPdAlert, /// Set the maximum sink voltage in mV for the given port SetMaxSinkVoltage(Option), /// Set unconstrained power @@ -347,10 +339,6 @@ pub enum RetimerFwUpdateState { pub enum PortResponseData { /// Command completed with no error Complete, - /// Port status - PortStatus(PortStatus), - /// ClearEvents - ClearEvents(PortEvent), /// Retimer Fw Update status RtFwUpdateStatus(RetimerFwUpdateState), /// PD alert @@ -441,11 +429,21 @@ pub struct ControllerStatus<'a> { pub fw_version1: u32, } +/// Per-port registration info +pub struct PortRegistration { + /// Global port ID of the port + pub id: GlobalPortId, + /// Event receiver for the type-C service + pub receiver: DynamicReceiver<'static, ServicePortEvent>, + /// Event sender for the type-C service + pub sender: Mutex>, +} + /// PD controller pub struct Device<'a> { node: intrusive_list::Node, id: ControllerId, - ports: &'a [GlobalPortId], + pub ports: &'a [PortRegistration], num_ports: usize, command: deferred::Channel>, } @@ -458,7 +456,7 @@ impl intrusive_list::NodeContainer for Device<'static> { impl<'a> Device<'a> { /// Create a new PD controller struct - pub fn new(id: ControllerId, ports: &'a [GlobalPortId]) -> Self { + pub fn new(id: ControllerId, ports: &'a [PortRegistration]) -> Self { Self { node: intrusive_list::Node::uninit(), id, @@ -485,14 +483,14 @@ impl<'a> Device<'a> { /// Convert a local port ID to a global port ID pub fn lookup_global_port(&self, port: LocalPortId) -> Result { - Ok(*self.ports.get(port.0 as usize).ok_or(PdError::InvalidParams)?) + Ok(self.ports.get(port.0 as usize).ok_or(PdError::InvalidParams)?.id) } /// Convert a global port ID to a local port ID pub fn lookup_local_port(&self, port: GlobalPortId) -> Result { self.ports .iter() - .position(|p| *p == port) + .position(|descriptor| descriptor.id == port) .map(|p| LocalPortId(p as u8)) .ok_or(PdError::InvalidParams) } @@ -504,20 +502,10 @@ impl<'a> Device<'a> { self.command.receive().await } - /// Notify that there are pending events on one or more ports - pub fn notify_ports(&self, ctx: &crate::service::context::Context, pending: PortPending) { - ctx.notify_ports(pending); - } - /// Number of ports on this controller pub fn num_ports(&self) -> usize { self.num_ports } - - /// Slice of global ports on the Device - pub fn ports(&self) -> &'a [GlobalPortId] { - self.ports - } } /// PD controller trait that device drivers may use to integrate with internal messaging system @@ -537,7 +525,7 @@ pub trait Controller { fn clear_port_events( &mut self, port: LocalPortId, - ) -> impl Future>>; + ) -> impl Future>>; /// Returns the port status fn get_port_status(&mut self, port: LocalPortId) -> impl Future>>; diff --git a/type-c-interface/src/service/context.rs b/type-c-interface/src/service/context.rs index 5efa9865..f56ebdbe 100644 --- a/type-c-interface/src/service/context.rs +++ b/type-c-interface/src/service/context.rs @@ -1,20 +1,16 @@ -use embassy_sync::signal::Signal; use embassy_time::{Duration, with_timeout}; use embedded_usb_pd::ucsi::{self, lpm}; -use embedded_usb_pd::{GlobalPortId, PdError, ado::Ado}; +use embedded_usb_pd::{GlobalPortId, PdError}; -use crate::port::event::{PortEvent, PortPending}; +use crate::port::ControllerId; use crate::port::{ AttnVdm, Command, ControllerStatus, Device, DpConfig, DpStatus, InternalCommandData, InternalResponseData, - OtherVdm, PdStateMachineConfig, PortCommand, PortCommandData, PortResponseData, PortStatus, Response, - RetimerFwUpdateState, SendVdm, TbtConfig, TypeCStateMachineState, UsbControlConfig, + OtherVdm, PdStateMachineConfig, PortCommand, PortCommandData, PortResponseData, Response, RetimerFwUpdateState, + SendVdm, TbtConfig, TypeCStateMachineState, UsbControlConfig, }; -use crate::port::{Cached, ControllerId}; use crate::service; -use crate::service::event::Event; -use embedded_services::{ - GlobalRawMutex, IntrusiveNode, broadcaster::immediate as broadcaster, error, intrusive_list, trace, -}; +use crate::service::event::{Event, PortEvent}; +use embedded_services::{IntrusiveNode, broadcaster::immediate as broadcaster, error, intrusive_list}; /// Default command timeout /// set to high value since this is intended to prevent an unresponsive device from blocking the service implementation @@ -36,11 +32,10 @@ impl DeviceContainer for Device<'_> { /// /// This struct is going to be merged into the service implementation and removed from here. pub struct Context { - port_events: Signal, /// Event broadcaster broadcaster: broadcaster::Immediate, /// Controller list - controllers: intrusive_list::IntrusiveList, + pub controllers: intrusive_list::IntrusiveList, } impl Default for Context { @@ -53,30 +48,11 @@ impl Context { /// Create new Context pub const fn new() -> Self { Self { - port_events: Signal::new(), broadcaster: broadcaster::Immediate::new(), controllers: intrusive_list::IntrusiveList::new(), } } - /// Notify that there are pending events on one or more ports - /// Each bit corresponds to a global port ID - pub fn notify_ports(&self, pending: PortPending) { - let raw_pending: u32 = pending.into(); - trace!("Notify ports: {:#x}", raw_pending); - // Early exit if no events - if pending.is_none() { - return; - } - - self.port_events - .signal(if let Some(flags) = self.port_events.try_take() { - flags.union(pending) - } else { - pending - }); - } - /// Send a command to the given controller with no timeout pub async fn send_controller_command_no_timeout( &self, @@ -222,47 +198,6 @@ impl Context { } } - /// Get the current port events - pub async fn get_unhandled_events(&self) -> PortPending { - self.port_events.wait().await - } - - /// Get the unhandled events for the given port - pub async fn get_port_event(&self, port: GlobalPortId) -> Result { - match self.send_port_command(port, PortCommandData::ClearEvents).await? { - PortResponseData::ClearEvents(event) => Ok(event), - r => { - error!("Invalid response: expected clear events, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Get the current port status - pub async fn get_port_status(&self, port: GlobalPortId, cached: Cached) -> Result { - match self - .send_port_command(port, PortCommandData::PortStatus(cached)) - .await? - { - PortResponseData::PortStatus(status) => Ok(status), - r => { - error!("Invalid response: expected port status, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - - /// Get the oldest unhandled PD alert for the given port - pub async fn get_pd_alert(&self, port: GlobalPortId) -> Result, PdError> { - match self.send_port_command(port, PortCommandData::GetPdAlert).await? { - PortResponseData::PdAlert(alert) => Ok(alert), - r => { - error!("Invalid response: expected PD alert, got {:?}", r); - Err(PdError::InvalidResponse) - } - } - } - /// Get the retimer fw update status pub async fn get_rt_fw_update_status(&self, port: GlobalPortId) -> Result { match self @@ -534,4 +469,21 @@ impl Context { .iter_only::() .fold(0, |acc, controller| acc + controller.num_ports()) } + + pub async fn send_port_event(&self, event: PortEvent) -> Result<(), PdError> { + let node = self.find_node_by_port(event.port)?; + + node.data::() + .ok_or(PdError::InvalidController)? + .ports + .iter() + .find(|descriptor| descriptor.id == event.port) + .ok_or(PdError::InvalidPort)? + .sender + .lock() + .await + .send(event) + .await; + Ok(()) + } } diff --git a/type-c-interface/src/service/event.rs b/type-c-interface/src/service/event.rs index 800d1555..0445f5a3 100644 --- a/type-c-interface/src/service/event.rs +++ b/type-c-interface/src/service/event.rs @@ -1,8 +1,39 @@ //! Comms service message definitions -use embedded_usb_pd::GlobalPortId; +use embedded_usb_pd::{GlobalPortId, ado::Ado}; -/// Message generated when a debug acessory is connected or disconnected +use crate::port::{ + PortStatus, + event::{PortStatusEventBitfield, VdmData}, +}; + +/// Enum to contain all port event variants +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum PortEventData { + /// Port status change events + StatusChanged(PortStatusEventBitfield, PortStatus), + /// PD alert + Alert(Ado), + /// VDM + Vdm(VdmData), + /// Discover mode completed + DiscoverModeCompleted, + /// USB mux error recovery + UsbMuxErrorRecovery, + /// DP status update + DpStatusUpdate, +} + +/// Enum to contain all port event variants +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PortEvent { + pub port: GlobalPortId, + pub event: PortEventData, +} + +/// Message generated when a debug accessory is connected or disconnected #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DebugAccessory { diff --git a/type-c-service/src/driver/tps6699x.rs b/type-c-service/src/driver/tps6699x.rs index f801a6e8..ab7f89eb 100644 --- a/type-c-service/src/driver/tps6699x.rs +++ b/type-c-service/src/driver/tps6699x.rs @@ -28,7 +28,7 @@ use tps6699x::command::{ }; use tps6699x::fw_update::UpdateConfig as FwUpdateConfig; use tps6699x::registers::port_config::TypeCStateMachine; -use type_c_interface::port::event::PortEvent; +use type_c_interface::port::event::PortEventBitfield; use type_c_interface::port::{ATTN_VDM_LEN, DpConfig, DpStatus, PdStateMachineConfig, RetimerFwUpdateState}; use type_c_interface::port::{ AttnVdm, Controller, ControllerStatus, DpPinConfig, OtherVdm, PortStatus, SendVdm, TbtConfig, @@ -52,7 +52,7 @@ struct FwUpdateState<'a, M: RawMutex, B: I2c> { } pub struct Tps6699x<'a, M: RawMutex, B: I2c> { - port_events: heapless::Vec, + port_events: heapless::Vec, tps6699x: tps6699x_drv::Tps6699x<'a, M, B>, update_state: Option>, /// Firmware update configuration @@ -73,7 +73,7 @@ impl<'a, M: RawMutex, B: I2c> Tps6699x<'a, M, B> { } else { Some(Self { // num_ports validated by branch - port_events: heapless::Vec::from_iter((0..num_ports).map(|_| PortEvent::none())), + port_events: heapless::Vec::from_iter((0..num_ports).map(|_| PortEventBitfield::none())), tps6699x, update_state: None, fw_update_config, @@ -274,9 +274,7 @@ impl Controller for Tps6699x<'_, M, B> { } /// Returns and clears current events for the given port - /// - /// Drop safety: All state changes happen after await point - async fn clear_port_events(&mut self, port: LocalPortId) -> Result> { + async fn clear_port_events(&mut self, port: LocalPortId) -> Result> { Ok(core::mem::take( self.port_events.get_mut(port.0 as usize).ok_or(PdError::InvalidPort)?, )) diff --git a/type-c-service/src/lib.rs b/type-c-service/src/lib.rs index e15a4513..14b903be 100644 --- a/type-c-service/src/lib.rs +++ b/type-c-service/src/lib.rs @@ -8,53 +8,43 @@ pub mod wrapper; use core::future::Future; use type_c_interface::port::event::{ - PortEvent, PortNotification, PortNotificationSingle, PortPendingIter, PortStatusChanged, + PortEvent, PortEventBitfield, PortNotificationEventBitfield, PortStatusEventBitfield, }; -/// Enum to contain all port event variants -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PortEventVariant { - /// Port status change events - StatusChanged(PortStatusChanged), - /// Port notification events - Notification(PortNotificationSingle), -} - /// Struct to convert port events into a stream of events -#[derive(Clone, Copy)] -pub struct PortEventStreamer { +#[derive(Clone)] +pub struct PortEventStreamer> { /// Current port index being processed port_index: Option, /// Iterator over pending ports - pending_iter: PortPendingIter, + port_iter: Iter, /// Notification to be streamed - pending_notifications: Option, + pending_notifications: Option, } -impl PortEventStreamer { +impl> PortEventStreamer { /// Create new PortEventStreamer /// /// Returns none if there are no pending ports to process. - pub fn new(pending_iter: PortPendingIter) -> Self { + pub fn new(port_iter: Iter) -> Self { Self { port_index: None, - pending_iter, + port_iter, pending_notifications: None, } } } -impl PortEventStreamer { +impl> PortEventStreamer { /// Get the next port event, calls the closure if it needs to get pending events for the current port. - pub async fn next>, F: FnMut(usize) -> Fut>( + pub async fn next>, F: FnMut(usize) -> Fut>( &mut self, mut f: F, - ) -> Result, E> { + ) -> Result, E> { loop { let port_index = if let Some(index) = self.port_index { index - } else if let Some(next_port) = self.pending_iter.next() { + } else if let Some(next_port) = self.port_iter.next() { // First time this function is called, get our starting port index self.port_index = Some(next_port); next_port @@ -70,7 +60,7 @@ impl PortEventStreamer { if let Some(port_event) = pending.next() { // Return a single notification self.pending_notifications = Some(pending); - ret = Some((port_index, PortEventVariant::Notification(port_event))); + ret = Some((port_index, port_event)); } else { // Done with pending notifications, continue to the next port advance_port = true; @@ -80,7 +70,7 @@ impl PortEventStreamer { // Haven't read port events yet let event = f(port_index).await?; - if event.notification != PortNotification::none() { + if event.notification != PortNotificationEventBitfield::none() { // Have pending notifications to stream as events, store those for the next loop/call to this function self.pending_notifications = Some(event.notification); } else { @@ -89,14 +79,14 @@ impl PortEventStreamer { self.pending_notifications = None; } - if event.status != PortStatusChanged::none() { + if event.status != PortStatusEventBitfield::none() { // Return the port status changed event first if there is one - ret = Some((port_index, PortEventVariant::StatusChanged(event.status))); + ret = Some((port_index, PortEvent::StatusChanged(event.status))); } } if advance_port { - if let Some(next_port) = self.pending_iter.next() { + if let Some(next_port) = self.port_iter.next() { // Move to the next port self.port_index = Some(next_port); } else if ret.is_none() { @@ -122,15 +112,12 @@ impl PortEventStreamer { #[cfg(test)] #[allow(clippy::unwrap_used)] mod tests { - use core::sync::atomic::AtomicBool; - - use type_c_interface::port::event::PortPending; - use super::*; + use core::sync::atomic::AtomicBool; /// Utitily function to create a PortStatusChanged event - fn status_changed(plug_event: bool, power_contract: bool, sink_ready: bool) -> PortStatusChanged { - let mut status_changed = PortStatusChanged::none(); + fn status_changed(plug_event: bool, power_contract: bool, sink_ready: bool) -> PortStatusEventBitfield { + let mut status_changed = PortStatusEventBitfield::none(); status_changed.set_plug_inserted_or_removed(plug_event); status_changed.set_new_power_contract_as_consumer(power_contract); status_changed.set_sink_ready(sink_ready); @@ -138,8 +125,8 @@ mod tests { } /// Utility function to create a PortNotification event - fn notification(alert: bool, discover_mode_completed: bool) -> PortNotification { - let mut notification = PortNotification::none(); + fn notification(alert: bool, discover_mode_completed: bool) -> PortNotificationEventBitfield { + let mut notification = PortNotificationEventBitfield::none(); notification.set_alert(alert); notification.set_discover_mode_completed(discover_mode_completed); notification @@ -148,22 +135,14 @@ mod tests { /// Test iterating over port status changed events #[tokio::test] async fn test_port_status_changed() { - let mut pending_ports = PortPending::none(); - pending_ports.pend_port(0).unwrap(); - pending_ports.pend_port(2).unwrap(); - pending_ports.pend_port(3).unwrap(); - - let mut streamer = PortEventStreamer::new(pending_ports.into_iter()); + let mut streamer = PortEventStreamer::new(0..3); let event = streamer .next::<(), _, _>(async |_| Ok(status_changed(true, true, true).into())) .await; assert_eq!( event, - Ok(Some(( - 0, - PortEventVariant::StatusChanged(status_changed(true, true, true)) - ))) + Ok(Some((0, PortEvent::StatusChanged(status_changed(true, true, true))))) ); let event = streamer @@ -171,10 +150,7 @@ mod tests { .await; assert_eq!( event, - Ok(Some(( - 2, - PortEventVariant::StatusChanged(status_changed(true, false, true)) - ))) + Ok(Some((1, PortEvent::StatusChanged(status_changed(true, false, true))))) ); let event = streamer @@ -182,10 +158,7 @@ mod tests { .await; assert_eq!( event, - Ok(Some(( - 3, - PortEventVariant::StatusChanged(status_changed(false, false, true)) - ))) + Ok(Some((2, PortEvent::StatusChanged(status_changed(false, false, true))))) ); let event = streamer @@ -197,28 +170,16 @@ mod tests { /// Test iterating over port notifications #[tokio::test] async fn test_port_notification() { - let mut pending_ports = PortPending::none(); - pending_ports.pend_port(0).unwrap(); - - let mut streamer = PortEventStreamer::new(pending_ports.into_iter()); + let mut streamer = PortEventStreamer::new(0..1); let event = streamer .next::<(), _, _>(async |_| Ok(notification(true, true).into())) .await; - assert_eq!( - event, - Ok(Some((0, PortEventVariant::Notification(PortNotificationSingle::Alert)))) - ); + assert_eq!(event, Ok(Some((0, PortEvent::Alert)))); let event = streamer .next::<(), _, _>(async |_| Ok(notification(true, true).into())) .await; - assert_eq!( - event, - Ok(Some(( - 0, - PortEventVariant::Notification(PortNotificationSingle::DiscoverModeCompleted) - ))) - ); + assert_eq!(event, Ok(Some((0, PortEvent::DiscoverModeCompleted)))); let event = streamer .next::<(), _, _>(async |_| Ok(notification(true, true).into())) @@ -229,20 +190,14 @@ mod tests { /// Test the the final port with no pending notifications #[tokio::test] async fn test_last_notifications() { - let mut pending_ports = PortPending::none(); - pending_ports.pend_port(0).unwrap(); - - let mut streamer = PortEventStreamer::new(pending_ports.into_iter()); + let mut streamer = PortEventStreamer::new(0..1); // Test p0 events let p0_event = status_changed(true, true, true).into(); let event = streamer.next::<(), _, _>(async |_| Ok(p0_event)).await; assert_eq!( event, - Ok(Some(( - 0, - PortEventVariant::StatusChanged(status_changed(true, true, true)) - ))) + Ok(Some((0, PortEvent::StatusChanged(status_changed(true, true, true))))) ); let event = streamer.next::<(), _, _>(async |_| Ok(p0_event)).await; @@ -252,14 +207,10 @@ mod tests { /// Test iterating over both status and notification events #[tokio::test] async fn test_port_event() { - let mut pending_ports = PortPending::none(); - pending_ports.pend_port(0).unwrap(); - pending_ports.pend_port(6).unwrap(); - - let mut streamer = PortEventStreamer::new(pending_ports.into_iter()); + let mut streamer = PortEventStreamer::new(0..2); // Test p0 events - let p0_event = PortEvent { + let p0_event = PortEventBitfield { status: status_changed(true, true, true), notification: notification(true, false), }; @@ -267,51 +218,35 @@ mod tests { let event = streamer.next::<(), _, _>(async |_| Ok(p0_event)).await; assert_eq!( event, - Ok(Some(( - 0, - PortEventVariant::StatusChanged(status_changed(true, true, true)) - ))) + Ok(Some((0, PortEvent::StatusChanged(status_changed(true, true, true))))) ); let event = streamer.next::<(), _, _>(async |_| Ok(p0_event)).await; - assert_eq!( - event, - Ok(Some((0, PortEventVariant::Notification(PortNotificationSingle::Alert)))) - ); + assert_eq!(event, Ok(Some((0, PortEvent::Alert)))); - // Test p6 events - let p6_event = PortEvent { + // Test p1 events + let p1_event = PortEventBitfield { status: status_changed(false, true, false), notification: notification(false, true), }; - let event = streamer.next::<(), _, _>(async |_| Ok(p6_event)).await; + let event = streamer.next::<(), _, _>(async |_| Ok(p1_event)).await; assert_eq!( event, - Ok(Some(( - 6, - PortEventVariant::StatusChanged(status_changed(false, true, false)) - ))) + Ok(Some((1, PortEvent::StatusChanged(status_changed(false, true, false))))) ); - let event = streamer.next::<(), _, _>(async |_| Ok(p6_event)).await; - assert_eq!( - event, - Ok(Some(( - 6, - PortEventVariant::Notification(PortNotificationSingle::DiscoverModeCompleted) - ))) - ); + let event = streamer.next::<(), _, _>(async |_| Ok(p1_event)).await; + assert_eq!(event, Ok(Some((1, PortEvent::DiscoverModeCompleted)))); - let event = streamer.next::<(), _, _>(async |_| Ok(p6_event)).await; + let event = streamer.next::<(), _, _>(async |_| Ok(p1_event)).await; assert_eq!(event, Ok(None)); } /// Test no pending ports #[tokio::test] async fn test_no_pending_ports() { - let pending_ports = PortPending::none(); - let mut streamer = PortEventStreamer::new(pending_ports.into_iter()); + let mut streamer = PortEventStreamer::new(0..0); let event = streamer .next::<(), _, _>(async |_| Ok(status_changed(true, true, true).into())) .await; @@ -321,22 +256,15 @@ mod tests { /// Test a port with a pending event with no actual event #[tokio::test] async fn test_empty_event() { - let mut pending_ports = PortPending::none(); - pending_ports.pend_port(0).unwrap(); - - let mut streamer = PortEventStreamer::new(pending_ports.into_iter()); - let event = streamer.next::<(), _, _>(async |_| Ok(PortEvent::none())).await; + let mut streamer = PortEventStreamer::new(0..1); + let event = streamer.next::<(), _, _>(async |_| Ok(PortEventBitfield::none())).await; assert_eq!(event, Ok(None)); } /// Test advancing to the next port when there are no events #[tokio::test] async fn test_skip_no_pending() { - let mut pending_ports = PortPending::none(); - pending_ports.pend_port(0).unwrap(); - pending_ports.pend_port(1).unwrap(); - - let mut streamer = PortEventStreamer::new(pending_ports.into_iter()); + let mut streamer = PortEventStreamer::new(0..2); let event = streamer .next::<(), _, _>(async |_| { static HAVE_EVENTS: AtomicBool = AtomicBool::new(false); @@ -348,10 +276,7 @@ mod tests { .await; assert_eq!( event, - Ok(Some(( - 1, - PortEventVariant::StatusChanged(status_changed(true, true, true)) - ))) + Ok(Some((1, PortEvent::StatusChanged(status_changed(true, true, true))))) ); let event = streamer diff --git a/type-c-service/src/service/mod.rs b/type-c-service/src/service/mod.rs index d2e4b5f6..1e1db4b1 100644 --- a/type-c-service/src/service/mod.rs +++ b/type-c-service/src/service/mod.rs @@ -1,19 +1,20 @@ use core::cell::RefCell; use core::future::pending; +use core::pin::pin; +use embassy_futures::select::select_slice; use embassy_futures::select::{Either, select}; use embedded_services::{debug, error, event::Receiver, info, trace}; use embedded_usb_pd::GlobalPortId; use embedded_usb_pd::PdError as Error; use power_policy_interface::service::event::EventData as PowerPolicyEventData; +use type_c_interface::service::event::{PortEvent, PortEventData}; -use crate::{PortEventStreamer, PortEventVariant}; -use type_c_interface::port::event::{PortNotificationSingle, PortStatusChanged}; -use type_c_interface::port::{Cached, PortStatus}; +use type_c_interface::port::event::PortStatusEventBitfield; +use type_c_interface::port::{Device, PortStatus}; use type_c_interface::service::event; pub mod config; -pub mod pd; mod power; mod ucsi; pub mod vdm; @@ -60,9 +61,7 @@ pub enum PowerPolicyEvent { #[derive(Copy, Clone)] pub enum Event { /// Port event - PortStatusChanged(GlobalPortId, PortStatusChanged, PortStatus), - /// A controller notified of an event that occurred. - PortNotification(GlobalPortId, PortNotificationSingle), + PortEvent(PortEvent), /// Power policy event PowerPolicy(PowerPolicyEvent), } @@ -97,10 +96,10 @@ impl<'a> Service<'a> { } /// Process events for a specific port - async fn process_port_event( + async fn process_port_status_event( &mut self, port_id: GlobalPortId, - event: PortStatusChanged, + event: PortStatusEventBitfield, status: PortStatus, ) -> Result<(), Error> { let old_status = self.get_cached_port_status(port_id)?; @@ -132,17 +131,25 @@ impl<'a> Service<'a> { Ok(()) } + async fn process_port_event(&mut self, event: &PortEvent) -> Result<(), Error> { + match &event.event { + PortEventData::StatusChanged(status_event, status) => { + self.process_port_status_event(event.port, *status_event, *status).await + } + unhandled => { + // Currently just log notifications, but may want to do more in the future + debug!("Port{}: Received notification event: {:#?}", event.port.0, unhandled); + Ok(()) + } + } + } + /// Process the given event pub async fn process_event(&mut self, event: Event) -> Result<(), Error> { match event { - Event::PortStatusChanged(port, event_kind, status) => { - trace!("Port{}: Processing port status changed", port.0); - self.process_port_event(port, event_kind, status).await - } - Event::PortNotification(port, notification) => { - // Other port notifications - info!("Port{}: Got port notification: {:?}", port.0, notification); - Ok(()) + Event::PortEvent(event) => { + trace!("Port{}: Processing port event", event.port.0); + self.process_port_event(&event).await } Event::PowerPolicy(event) => { trace!("Processing power policy event"); @@ -156,8 +163,6 @@ impl<'a> Service<'a> { pub struct EventReceiver<'a, PowerReceiver: Receiver> { /// Type-C context pub(crate) context: &'a type_c_interface::service::context::Context, - /// Next port to check, this is used to round-robin through ports - port_event_streaming_state: Option, /// Power policy event subscriber /// /// Used to allow partial borrows of Self for the call to select @@ -172,54 +177,31 @@ impl<'a, PowerReceiver: Receiver> EventReceiver<'a, PowerR ) -> Self { Self { context, - port_event_streaming_state: None, power_policy_event_subscriber: RefCell::new(power_policy_event_subscriber), } } /// Wait for the next event - pub async fn wait_next(&mut self) -> Result { - loop { - match select(self.wait_port_flags(), self.wait_power_policy_event()).await { - Either::First(mut stream) => { - if let Some((port_id, event)) = stream - .next(|port_id| self.context.get_port_event(GlobalPortId(port_id as u8))) - .await? - { - let port_id = GlobalPortId(port_id as u8); - self.port_event_streaming_state = Some(stream); - match event { - PortEventVariant::StatusChanged(status_event) => { - // Return a port status changed event - let status = self.context.get_port_status(port_id, Cached(true)).await?; - return Ok(Event::PortStatusChanged(port_id, status_event, status)); - } - PortEventVariant::Notification(notification) => { - // Other notifications - trace!("Port notification: {:?}", notification); - return Ok(Event::PortNotification(port_id, notification)); - } - } - } else { - self.port_event_streaming_state = None; - } - } - Either::Second(event) => return Ok(event), - } + pub async fn wait_next(&mut self) -> Event { + match select(self.wait_port_event(), self.wait_power_policy_event()).await { + Either::First(event) => event, + Either::Second(event) => event, } } - /// Wait for port flags - async fn wait_port_flags(&self) -> PortEventStreamer { - if let Some(ref streamer) = self.port_event_streaming_state { - // If we have an existing iterator, return it - // Yield first to prevent starving other tasks - embassy_futures::yield_now().await; - *streamer - } else { - // Wait for the next port event and create a streamer - PortEventStreamer::new(self.context.get_unhandled_events().await.into_iter()) - } + /// Wait for a port event + async fn wait_port_event(&self) -> Event { + let (event, _) = { + let mut futures = heapless::Vec::<_, MAX_SUPPORTED_PORTS>::new(); + for device in self.context.controllers.iter_only::() { + for descriptor in device.ports.iter() { + let _ = futures.push(async move { descriptor.receiver.receive().await }); + } + } + select_slice(pin!(&mut futures)).await + }; + + Event::PortEvent(event) } /// Wait for a power policy event diff --git a/type-c-service/src/service/pd.rs b/type-c-service/src/service/pd.rs deleted file mode 100644 index 21934fa7..00000000 --- a/type-c-service/src/service/pd.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Power Delivery (PD) related functionality. - -use embedded_usb_pd::{GlobalPortId, PdError, ado::Ado}; - -use super::Service; - -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 - } -} diff --git a/type-c-service/src/service/ucsi.rs b/type-c-service/src/service/ucsi.rs index 62749cc8..9b75db63 100644 --- a/type-c-service/src/service/ucsi.rs +++ b/type-c-service/src/service/ucsi.rs @@ -284,7 +284,7 @@ impl Service<'_> { pub(super) async fn handle_ucsi_port_event( &mut self, port_id: GlobalPortId, - port_event: PortStatusChanged, + port_event: PortStatusEventBitfield, port_status: &PortStatus, ) { let mut ucsi_event = ConnectorStatusChange::default(); diff --git a/type-c-service/src/task.rs b/type-c-service/src/task.rs index ce446aaa..a1ad9330 100644 --- a/type-c-service/src/task.rs +++ b/type-c-service/src/task.rs @@ -34,14 +34,7 @@ pub async fn task, con } loop { - let event = match event_receiver.wait_next().await { - Ok(event) => event, - Err(e) => { - error!("Error waiting for event: {:#?}", e); - continue; - } - }; - + let event = event_receiver.wait_next().await; if let Err(e) = service.lock().await.process_event(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 92e182d3..7ee663fb 100644 --- a/type-c-service/src/wrapper/backing.rs +++ b/type-c-service/src/wrapper/backing.rs @@ -2,22 +2,16 @@ //! //! TODO: update this documentation when the type-C service is refactored //! -use core::array::from_fn; +use core::{array::from_fn, ops::Range}; use cfu_service::component::CfuDevice; -use embassy_sync::{ - blocking_mutex::raw::RawMutex, - mutex::Mutex, - pubsub::{DynImmediatePublisher, DynSubscriber, PubSubChannel}, -}; +use embassy_sync::{blocking_mutex::raw::RawMutex, mutex::Mutex}; use embassy_time::Instant; use embedded_cfu_protocol::protocol_definitions::ComponentId; use embedded_services::event; -use embedded_usb_pd::{GlobalPortId, ado::Ado}; -use type_c_interface::port::event::{PortEvent, PortStatusChanged}; -use type_c_interface::port::{ControllerId, PortStatus}; +use type_c_interface::port::{ControllerId, PortRegistration, PortStatus, event::PortStatusEventBitfield}; use crate::{ PortEventStreamer, @@ -28,12 +22,12 @@ use crate::{ }; /// Internal per-controller state -#[derive(Copy, Clone)] +#[derive(Clone)] pub struct ControllerState { /// If we're currently doing a firmware update pub(crate) fw_update_state: cfu::FwUpdateState, /// State used to keep track of where we are as we turn the event bitfields into a stream of events - pub(crate) port_event_streaming_state: Option, + pub(crate) port_event_streaming_state: Option>>, } impl Default for ControllerState { @@ -59,20 +53,14 @@ impl<'a, M: RawMutex> Registration<'a, M> { } } -/// PD alerts should be fairly uncommon, four seems like a reasonable number to start with. -const MAX_BUFFERED_PD_ALERTS: usize = 4; - /// Base storage pub struct Storage<'a, const N: usize, M: RawMutex> { // Registration-related context: &'a type_c_interface::service::context::Context, controller_id: ControllerId, - pd_ports: [GlobalPortId; N], + pd_ports: [PortRegistration; N], cfu_device: CfuDevice, power_proxy_channels: [PowerProxyChannel; N], - - // State-related - pd_alerts: [PubSubChannel; N], } impl<'a, const N: usize, M: RawMutex> Storage<'a, N, M> { @@ -80,7 +68,7 @@ impl<'a, const N: usize, M: RawMutex> Storage<'a, N, M> { context: &'a type_c_interface::service::context::Context, controller_id: ControllerId, cfu_id: ComponentId, - pd_ports: [GlobalPortId; N], + pd_ports: [PortRegistration; N], ) -> Self { Self { context, @@ -88,7 +76,6 @@ impl<'a, const N: usize, M: RawMutex> Storage<'a, N, M> { pd_ports, cfu_device: CfuDevice::new(cfu_id), power_proxy_channels: from_fn(|_| PowerProxyChannel::new()), - pd_alerts: [const { PubSubChannel::new() }; N], } } @@ -103,34 +90,26 @@ impl<'a, const N: usize, M: RawMutex> Storage<'a, N, M> { pub struct Port<'a, M: RawMutex, S: event::Sender> { pub proxy: Mutex>, - pub state: Mutex>, + pub state: Mutex>, } -pub struct PortState<'a, S: event::Sender> { +pub struct PortState> { /// Cached port status pub(crate) status: PortStatus, /// Software status event - pub(crate) sw_status_event: PortStatusChanged, + pub(crate) sw_status_event: PortStatusEventBitfield, /// Sink ready deadline instant pub(crate) sink_ready_deadline: Option, - /// Pending events for the type-C service - pub(crate) pending_events: PortEvent, - /// PD alert channel for this port - // There's no direct immediate equivalent of a channel. PubSubChannel has immediate publisher behavior - // so we use that, but this requires us to keep separate publisher and subscriber objects. - pub(crate) pd_alerts: (DynImmediatePublisher<'a, Ado>, DynSubscriber<'a, Ado>), /// Sender to send events to the power policy service pub(crate) power_policy_sender: S, } -impl<'a, S: event::Sender> PortState<'a, S> { - pub fn new(pd_alerts: (DynImmediatePublisher<'a, Ado>, DynSubscriber<'a, Ado>), power_policy_sender: S) -> Self { +impl> PortState { + pub fn new(power_policy_sender: S) -> Self { Self { status: PortStatus::default(), - sw_status_event: PortStatusChanged::default(), + sw_status_event: PortStatusEventBitfield::default(), sink_ready_deadline: None, - pending_events: PortEvent::default(), - pd_alerts, power_policy_sender, } } @@ -155,21 +134,15 @@ impl<'a, const N: usize, M: RawMutex, S: event::Sender::new(); let mut power_proxy_receivers = heapless::Vec::<_, N>::new(); - for ((power_proxy_channel, pd_alert), (name, policy_sender)) in storage - .power_proxy_channels - .iter() - .zip(storage.pd_alerts.iter()) - .zip(power_policy_init.into_iter()) + for (power_proxy_channel, (name, policy_sender)) in + storage.power_proxy_channels.iter().zip(power_policy_init.into_iter()) { let (device_sender, device_receiver) = power_proxy_channel.get_device_components(); ports .push(Port { proxy: Mutex::new(PowerProxyDevice::new(name, device_sender, device_receiver)), - state: Mutex::new(PortState::new( - (pd_alert.dyn_immediate_publisher(), pd_alert.dyn_subscriber().ok()?), - policy_sender, - )), + state: Mutex::new(PortState::new(policy_sender)), }) .ok()?; power_proxy_receivers diff --git a/type-c-service/src/wrapper/message.rs b/type-c-service/src/wrapper/message.rs index aa4c5c03..b3bc32c5 100644 --- a/type-c-service/src/wrapper/message.rs +++ b/type-c-service/src/wrapper/message.rs @@ -3,32 +3,22 @@ use embedded_services::{GlobalRawMutex, ipc::deferred}; use embedded_usb_pd::{LocalPortId, ado::Ado}; use type_c_interface::{ - port::event::{PortNotificationSingle, PortStatusChanged}, + port::event::PortStatusEventBitfield, port::{self, DpStatus, PortStatus}, }; -/// Port status changed event data +/// Port event #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct EventPortStatusChanged { +pub struct LocalPortEvent { /// Port ID pub port: LocalPortId, - /// Status changed event - pub status_event: PortStatusChanged, -} - -/// Port notification event data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct EventPortNotification { - /// Port ID - pub port: LocalPortId, - /// Notification event - pub notification: PortNotificationSingle, + /// Port event + pub event: type_c_interface::port::event::PortEvent, } /// Power policy command event data -pub struct EventPowerPolicyCommand { +pub struct PowerPolicyCommand { /// Port ID pub port: LocalPortId, /// Power policy request @@ -50,11 +40,9 @@ pub enum EventCfu { /// Wrapper events pub enum Event<'a> { /// Port status changed - PortStatusChanged(EventPortStatusChanged), - /// Port notification - PortNotification(EventPortNotification), + PortEvent(LocalPortEvent), /// Power policy command received - PowerPolicyCommand(EventPowerPolicyCommand), + PowerPolicyCommand(PowerPolicyCommand), /// Command from TCPM ControllerCommand(deferred::Request<'a, GlobalRawMutex, port::Command, port::Response<'static>>), /// Cfu event @@ -68,7 +56,7 @@ pub struct OutputPortStatusChanged { /// Port ID pub port: LocalPortId, /// Status changed event - pub status_event: PortStatusChanged, + pub status_event: PortStatusEventBitfield, /// Port status pub status: PortStatus, } @@ -101,22 +89,9 @@ pub struct OutputControllerCommand<'a> { pub mod vdm { //! Events and output for vendor-defined messaging. - use super::LocalPortId; - use type_c_interface::port::{AttnVdm, OtherVdm}; + use type_c_interface::port::event::VdmData; - /// The kind of output from processing a vendor-defined message. - #[derive(Copy, Clone, Debug)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum OutputKind { - /// Entered custom mode - Entered(OtherVdm), - /// Exited custom mode - Exited(OtherVdm), - /// Received a vendor-defined other message - ReceivedOther(OtherVdm), - /// Received a vendor-defined attention message - ReceivedAttn(AttnVdm), - } + use super::LocalPortId; /// Output from processing a vendor-defined message. #[derive(Copy, Clone, Debug)] @@ -124,9 +99,8 @@ pub mod vdm { pub struct Output { /// The port that the VDM message is associated with. pub port: LocalPortId, - - /// The kind of VDM output. - pub kind: OutputKind, + /// VDM data + pub vdm_data: VdmData, } } diff --git a/type-c-service/src/wrapper/mod.rs b/type-c-service/src/wrapper/mod.rs index 91506384..95ee565d 100644 --- a/type-c-service/src/wrapper/mod.rs +++ b/type-c-service/src/wrapper/mod.rs @@ -17,7 +17,7 @@ //! any caching/buffering of data, and notifies the type-C service implementation of the event if needed. use core::array::from_fn; use core::future::pending; -use core::ops::DerefMut; +use core::ops::{DerefMut, Range}; use crate::wrapper::backing::{ControllerState, PortState}; use cfu_service::CfuClient; @@ -29,13 +29,15 @@ use embassy_time::Instant; use embedded_cfu_protocol::protocol_definitions::{FwUpdateOffer, FwUpdateOfferResponse, FwVersion}; use embedded_services::event; use embedded_services::sync::Lockable; -use embedded_services::{debug, error, info, trace, warn}; +use embedded_services::{debug, error, info, trace}; use embedded_usb_pd::ado::Ado; use embedded_usb_pd::{Error, LocalPortId, PdError}; +use type_c_interface::port::event::PortEvent as InterfacePortEvent; +use type_c_interface::service::event::{PortEvent as ServicePortEvent, PortEventData as ServicePortEventData}; +use crate::PortEventStreamer; use crate::wrapper::message::*; use crate::wrapper::proxy::PowerProxyReceiver; -use crate::{PortEventStreamer, PortEventVariant}; pub mod backing; mod cfu; @@ -47,7 +49,7 @@ mod power; pub mod proxy; mod vdm; -use type_c_interface::port::event::{PortEvent, PortNotificationSingle, PortPending, PortStatusChanged}; +use type_c_interface::port::event::PortStatusEventBitfield; use type_c_interface::port::{Controller, PortStatus}; /// Base interval for checking for FW update timeouts and recovery attempts @@ -174,7 +176,7 @@ where } port_state.sw_status_event = status_changed; - if port_state.sw_status_event != PortStatusChanged::none() { + if port_state.sw_status_event != PortStatusEventBitfield::none() { // Have a status changed event, notify trace!("Port{} status changed: {:#?}", i, status); self.sw_status_event.signal(()); @@ -186,7 +188,7 @@ where /// Handle a plug event async fn process_plug_event( &self, - port_state: &mut PortState<'_, S>, + port_state: &mut PortState, status: &PortStatus, ) -> Result<(), Error<::BusError>> { info!("Plug event"); @@ -212,7 +214,7 @@ where &self, controller: &mut D::Inner, local_port_id: LocalPortId, - status_event: PortStatusChanged, + status_event: PortStatusEventBitfield, ) -> Result, Error<::BusError>> { let global_port_id = self .registration @@ -264,7 +266,7 @@ where async fn finalize_port_status_change( &self, local_port: LocalPortId, - status_event: PortStatusChanged, + status_event: PortStatusEventBitfield, status: PortStatus, ) -> Result<(), Error<::BusError>> { let global_port_id = self @@ -273,31 +275,22 @@ where .lookup_global_port(local_port) .map_err(Error::Pd)?; - let mut port_state = self - .ports + self.ports .get(local_port.0 as usize) .ok_or(Error::Pd(PdError::InvalidPort))? .state .lock() - .await; - - let mut events = port_state.pending_events; - events.status = events.status.union(status_event); - port_state.pending_events = events; - port_state.status = status; - - if events != PortEvent::none() { - let mut pending = PortPending::none(); - pending - .pend_port(global_port_id.0 as usize) - .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); - } + .await + .status = status; - Ok(()) + self.registration + .context + .send_port_event(ServicePortEvent { + port: global_port_id, + event: ServicePortEventData::StatusChanged(status_event, status), + }) + .await + .map_err(Error::Pd) } /// Finalize a PD alert output @@ -312,29 +305,14 @@ where .lookup_global_port(local_port) .map_err(Error::Pd)?; - let mut port_state = self - .ports - .get(local_port.0 as usize) - .ok_or(Error::Pd(PdError::InvalidPort))? - .state - .lock() - .await; - - // Buffer the alert - port_state.pd_alerts.0.publish_immediate(alert); - - // Pend the alert - port_state.pending_events.notification.set_alert(true); - - // Pend this port - let mut pending = PortPending::none(); - pending - .pend_port(global_port_id.0 as usize) - .map_err(|_| PdError::InvalidPort)?; self.registration - .pd_controller - .notify_ports(self.registration.context, pending); - Ok(()) + .context + .send_port_event(ServicePortEvent { + port: global_port_id, + event: ServicePortEventData::Alert(alert), + }) + .await + .map_err(Error::Pd) } /// Wait for a pending port event @@ -344,14 +322,14 @@ where &self, controller_state: &ControllerState, controller: &mut D::Inner, - ) -> Result::BusError>> { + ) -> Result>, Error<::BusError>> { if controller_state.fw_update_state.in_progress() { // Don't process events while firmware update is in progress debug!("Firmware update in progress, ignoring port events"); return pending().await; } - let streaming_state = controller_state.port_event_streaming_state; + let streaming_state = controller_state.port_event_streaming_state.clone(); if let Some(streamer) = streaming_state { // If we're converting the bitfields into an event stream yield first to prevent starving other tasks embassy_futures::yield_now().await; @@ -368,8 +346,7 @@ where Either::First(r) => r?, Either::Second(_) => (), }; - let pending: PortPending = FromIterator::from_iter(0..self.registration.num_ports()); - Ok(PortEventStreamer::new(pending.into_iter())) + Ok(PortEventStreamer::new(0..self.registration.num_ports())) } } @@ -410,33 +387,20 @@ where // No more awaits, modify state here for drop safety let sw_event = - core::mem::replace(&mut port_state.sw_status_event, PortStatusChanged::none()); + core::mem::replace(&mut port_state.sw_status_event, PortStatusEventBitfield::none()); Ok(hw_event.union(sw_event.into())) }) .await? { let port_id = LocalPortId(port_index as u8); self.controller_state.lock().await.port_event_streaming_state = Some(stream); - match event { - PortEventVariant::StatusChanged(status_event) => { - return Ok(Event::PortStatusChanged(EventPortStatusChanged { - port: port_id, - status_event, - })); - } - PortEventVariant::Notification(notification) => { - return Ok(Event::PortNotification(EventPortNotification { - port: port_id, - notification, - })); - } - } + return Ok(Event::PortEvent(LocalPortEvent { port: port_id, event })); } else { self.controller_state.lock().await.port_event_streaming_state = None; } } Either5::Second((port, request)) => { - return Ok(Event::PowerPolicyCommand(EventPowerPolicyCommand { port, request })); + return Ok(Event::PowerPolicyCommand(PowerPolicyCommand { port, request })); } Either5::Third(request) => return Ok(Event::ControllerCommand(request)), Either5::Fourth(event) => return Ok(Event::CfuEvent(event)), @@ -450,42 +414,49 @@ where .lock() .await .sink_ready_deadline = None; - let mut status_event = PortStatusChanged::none(); + let mut status_event = PortStatusEventBitfield::none(); status_event.set_sink_ready(true); - return Ok(Event::PortStatusChanged(EventPortStatusChanged { port, status_event })); + return Ok(Event::PortEvent(LocalPortEvent { + port, + event: type_c_interface::port::event::PortEvent::StatusChanged(status_event), + })); } } } } /// Process a port notification - async fn process_port_notification<'b>( + async fn process_port_event<'b>( &self, controller: &mut D::Inner, - port: LocalPortId, - notification: PortNotificationSingle, + event: LocalPortEvent, ) -> Result, Error<::BusError>> { - match notification { - PortNotificationSingle::Alert => { - let ado = controller.get_pd_alert(port).await?; - trace!("Port{}: PD alert: {:#?}", port.0, ado); + match event.event { + InterfacePortEvent::StatusChanged(status_event) => { + self.process_port_status_changed(controller, event.port, status_event) + .await + } + InterfacePortEvent::Alert => { + let ado = controller.get_pd_alert(event.port).await?; + trace!("Port{}: PD alert: {:#?}", event.port.0, ado); if let Some(ado) = ado { - Ok(Output::PdAlert(OutputPdAlert { port, ado })) + Ok(Output::PdAlert(OutputPdAlert { port: event.port, ado })) } else { // For some reason we didn't read an alert, nothing to do Ok(Output::Nop) } } - PortNotificationSingle::Vdm(event) => { - self.process_vdm_event(controller, port, event).await.map(Output::Vdm) - } - PortNotificationSingle::DpStatusUpdate => self - .process_dp_status_update(controller, port) + InterfacePortEvent::Vdm(vdm_event) => self + .process_vdm_event(controller, event.port, vdm_event) + .await + .map(Output::Vdm), + InterfacePortEvent::DpStatusUpdate => self + .process_dp_status_update(controller, event.port) .await .map(Output::DpStatusUpdate), rest => { // Nothing currently implemented for these - trace!("Port{}: Notification: {:#?}", port.0, rest); + trace!("Port{}: Notification: {:#?}", event.port.0, rest); Ok(Output::Nop) } } @@ -500,11 +471,8 @@ where let mut controller = self.controller.lock().await; let mut controller_state = self.controller_state.lock().await; match event { - Event::PortStatusChanged(EventPortStatusChanged { port, status_event }) => { - self.process_port_status_changed(&mut controller, port, status_event) - .await - } - Event::PowerPolicyCommand(EventPowerPolicyCommand { port, request }) => { + Event::PortEvent(port_event) => self.process_port_event(&mut controller, port_event).await, + Event::PowerPolicyCommand(PowerPolicyCommand { port, request }) => { let response = self .process_power_command(&mut controller_state, &mut controller, port, &request) .await; @@ -529,10 +497,6 @@ where Ok(Output::CfuRecovery) } }, - Event::PortNotification(EventPortNotification { port, notification }) => { - self.process_port_notification(&mut controller, port, notification) - .await - } } } diff --git a/type-c-service/src/wrapper/pd.rs b/type-c-service/src/wrapper/pd.rs index a789f3b3..41a938d7 100644 --- a/type-c-service/src/wrapper/pd.rs +++ b/type-c-service/src/wrapper/pd.rs @@ -1,13 +1,10 @@ use crate::wrapper::backing::ControllerState; -use embassy_futures::yield_now; -use embassy_sync::pubsub::WaitResult; use embassy_time::{Duration, Timer}; use embedded_services::debug; use embedded_usb_pd::constants::{T_PS_TRANSITION_EPR_MS, T_PS_TRANSITION_SPR_MS}; use embedded_usb_pd::ucsi::{self, lpm}; use power_policy_interface::psu::{self, PsuState}; use type_c_interface::port; -use type_c_interface::port::Cached; use type_c_interface::port::{InternalResponseData, Response}; use super::*; @@ -22,24 +19,6 @@ impl< where D::Inner: Controller, { - async fn process_get_pd_alert( - &self, - port_state: &mut PortState<'_, S>, - local_port: LocalPortId, - ) -> Result, PdError> { - loop { - match port_state.pd_alerts.1.try_next_message() { - Some(WaitResult::Message(alert)) => return Ok(Some(alert)), - None => return Ok(None), - Some(WaitResult::Lagged(count)) => { - warn!("Port{}: Lagged PD alert channel: {}", local_port.0, count); - // Yield to avoid starving other tasks since we're in a loop and try_next_message isn't async - yield_now().await; - } - } - } - } - /// Check the sink ready timeout /// /// After accepting a sink contract (new contract as consumer), the PD spec guarantees that the @@ -47,7 +26,7 @@ where /// even for controllers that might not always broadcast sink ready events. pub(super) fn check_sink_ready_timeout( &self, - port_state: &mut PortState<'_, S>, + port_state: &mut PortState, status: &PortStatus, port: LocalPortId, new_contract: bool, @@ -107,7 +86,7 @@ where async fn process_set_max_sink_voltage( &self, controller: &mut D::Inner, - port_state: &mut PortState<'_, S>, + port_state: &mut PortState, state: &psu::State, local_port: LocalPortId, voltage_mv: Option, @@ -139,26 +118,6 @@ where } } - async fn process_get_port_status( - &self, - controller: &mut D::Inner, - port_state: &mut PortState<'_, S>, - local_port: LocalPortId, - cached: Cached, - ) -> Result { - if cached.0 { - Ok(port::PortResponseData::PortStatus(port_state.status)) - } else { - match controller.get_port_status(local_port).await { - Ok(status) => Ok(port::PortResponseData::PortStatus(status)), - Err(e) => match e { - Error::Bus(_) => Err(PdError::Failed), - Error::Pd(e) => Err(e), - }, - } - } - } - /// Handle a port command async fn process_port_command( &self, @@ -185,14 +144,6 @@ where let mut port_state = port.state.lock().await; port::Response::Port(match command.data { - port::PortCommandData::PortStatus(cached) => { - self.process_get_port_status(controller, &mut port_state, local_port, cached) - .await - } - port::PortCommandData::ClearEvents => { - let event = core::mem::take(&mut port_state.pending_events); - Ok(port::PortResponseData::ClearEvents(event)) - } port::PortCommandData::RetimerFwUpdateGetState => { match controller.get_rt_fw_update_status(local_port).await { Ok(status) => Ok(port::PortResponseData::RtFwUpdateStatus(status)), @@ -234,10 +185,6 @@ where Error::Pd(e) => Err(e), }, }, - port::PortCommandData::GetPdAlert => match self.process_get_pd_alert(&mut port_state, local_port).await { - Ok(alert) => Ok(port::PortResponseData::PdAlert(alert)), - Err(e) => Err(e), - }, port::PortCommandData::SetMaxSinkVoltage(voltage_mv) => { match self.registration.pd_controller.lookup_local_port(command.port) { Ok(local_port) => { diff --git a/type-c-service/src/wrapper/power.rs b/type-c-service/src/wrapper/power.rs index fa9c33d3..bc683ab0 100644 --- a/type-c-service/src/wrapper/power.rs +++ b/type-c-service/src/wrapper/power.rs @@ -27,7 +27,7 @@ where /// Handle a new contract as consumer pub(super) async fn process_new_consumer_contract( &self, - port_state: &mut PortState<'_, S>, + port_state: &mut PortState, status: &PortStatus, ) -> Result<(), Error<::BusError>> { info!("Process new consumer contract"); @@ -53,7 +53,7 @@ where /// Handle a new contract as provider pub(super) async fn process_new_provider_contract( &self, - port_state: &mut PortState<'_, S>, + port_state: &mut PortState, status: &PortStatus, ) -> Result<(), Error<::BusError>> { info!("Process New provider contract"); diff --git a/type-c-service/src/wrapper/vdm.rs b/type-c-service/src/wrapper/vdm.rs index 896c2b2a..5cedbf04 100644 --- a/type-c-service/src/wrapper/vdm.rs +++ b/type-c-service/src/wrapper/vdm.rs @@ -2,10 +2,9 @@ use embassy_sync::blocking_mutex::raw::RawMutex; use embedded_services::{event, sync::Lockable, trace}; use embedded_usb_pd::{Error, LocalPortId, PdError}; -use crate::wrapper::message::vdm::OutputKind; - -use type_c_interface::port::Controller; -use type_c_interface::port::event::{PortPending, VdmNotification}; +use type_c_interface::port::event::VdmNotification; +use type_c_interface::port::{Controller, event::VdmData}; +use type_c_interface::service::event::{PortEvent, PortEventData}; use super::{ControllerWrapper, FwOfferValidator, message::vdm::Output}; @@ -28,42 +27,26 @@ where ) -> Result::BusError>> { trace!("Processing VDM event: {:?} on port {}", event, port.0); let kind = match event { - VdmNotification::Entered => OutputKind::Entered(controller.get_other_vdm(port).await?), - VdmNotification::Exited => OutputKind::Exited(controller.get_other_vdm(port).await?), - VdmNotification::OtherReceived => OutputKind::ReceivedOther(controller.get_other_vdm(port).await?), - VdmNotification::AttentionReceived => OutputKind::ReceivedAttn(controller.get_attn_vdm(port).await?), + VdmNotification::Entered => VdmData::Entered(controller.get_other_vdm(port).await?), + VdmNotification::Exited => VdmData::Exited(controller.get_other_vdm(port).await?), + VdmNotification::OtherReceived => VdmData::ReceivedOther(controller.get_other_vdm(port).await?), + VdmNotification::AttentionReceived => VdmData::ReceivedAttn(controller.get_attn_vdm(port).await?), }; - Ok(Output { port, kind }) + Ok(Output { port, vdm_data: kind }) } /// Finalize a VDM output by notifying the service. pub(super) async fn finalize_vdm(&self, output: Output) -> Result<(), PdError> { trace!("Finalizing VDM output: {:?}", output); - let Output { port, kind } = output; + let Output { port, vdm_data } = output; let global_port_id = self.registration.pd_controller.lookup_global_port(port)?; - let mut port_state = self - .ports - .get(port.0 as usize) - .ok_or(PdError::InvalidPort)? - .state - .lock() - .await; - let notification = &mut port_state.pending_events.notification; - match kind { - OutputKind::Entered(_) => notification.set_custom_mode_entered(true), - OutputKind::Exited(_) => notification.set_custom_mode_exited(true), - OutputKind::ReceivedOther(_) => notification.set_custom_mode_other_vdm_received(true), - OutputKind::ReceivedAttn(_) => notification.set_custom_mode_attention_received(true), - } - - let mut pending = PortPending::none(); - pending - .pend_port(global_port_id.0 as usize) - .map_err(|_| PdError::InvalidPort)?; self.registration - .pd_controller - .notify_ports(self.registration.context, pending); - Ok(()) + .context + .send_port_event(PortEvent { + port: global_port_id, + event: PortEventData::Vdm(vdm_data), + }) + .await } }