diff --git a/Cargo.lock b/Cargo.lock index e9877c6e8..54eb00893 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -495,9 +495,9 @@ dependencies = [ [[package]] name = "device-driver" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af0e43acfcbb0bb3b7435cc1b1dbb33596cacfec1eb243336b74a398e0bd6cbf" +checksum = "c2e4547bd66511372d2a38ac3c1b2892c7ebf83cf0d5411c3406e496c85a1d96" dependencies = [ "defmt 0.3.100", "embedded-io 0.6.1", @@ -862,7 +862,7 @@ dependencies = [ [[package]] name = "embedded-usb-pd" version = "0.1.0" -source = "git+https://github.com/OpenDevicePartnership/embedded-usb-pd#1a8e79d3a2ac0d2837a34b045087cf0863146f7d" +source = "git+https://github.com/OpenDevicePartnership/embedded-usb-pd#21d0e228d21ddc6ccaeffc01d98ef9a5b87941ef" dependencies = [ "aquamarine", "bincode", @@ -2175,7 +2175,7 @@ dependencies = [ [[package]] name = "tps6699x" version = "0.1.0" -source = "git+https://github.com/OpenDevicePartnership/tps6699x?branch=v0.2.0#c908a50747e8fcce831d4e53026072b5b6916a7b" +source = "git+https://github.com/OpenDevicePartnership/tps6699x?branch=v0.2.0#abe5568183bfe5fb2ea81806dded6cb60f3f9b58" dependencies = [ "bincode", "bitfield 0.19.2", diff --git a/examples/rt685s-evk/Cargo.lock b/examples/rt685s-evk/Cargo.lock index 34391866e..24458c167 100644 --- a/examples/rt685s-evk/Cargo.lock +++ b/examples/rt685s-evk/Cargo.lock @@ -334,9 +334,9 @@ dependencies = [ [[package]] name = "device-driver" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aa3d97b2acf349b9d52c75470e2ccfc7224c49597ec12c2fb0e28826e910495" +checksum = "c2e4547bd66511372d2a38ac3c1b2892c7ebf83cf0d5411c3406e496c85a1d96" dependencies = [ "defmt 0.3.100", "embedded-io 0.6.1", @@ -676,7 +676,7 @@ dependencies = [ [[package]] name = "embedded-usb-pd" version = "0.1.0" -source = "git+https://github.com/OpenDevicePartnership/embedded-usb-pd#1a8e79d3a2ac0d2837a34b045087cf0863146f7d" +source = "git+https://github.com/OpenDevicePartnership/embedded-usb-pd#21d0e228d21ddc6ccaeffc01d98ef9a5b87941ef" dependencies = [ "aquamarine", "bincode", @@ -1427,7 +1427,7 @@ dependencies = [ [[package]] name = "tps6699x" version = "0.1.0" -source = "git+https://github.com/OpenDevicePartnership/tps6699x?branch=v0.2.0#c908a50747e8fcce831d4e53026072b5b6916a7b" +source = "git+https://github.com/OpenDevicePartnership/tps6699x?branch=v0.2.0#abe5568183bfe5fb2ea81806dded6cb60f3f9b58" dependencies = [ "bincode", "bitfield 0.19.4", diff --git a/examples/rt685s-evk/src/bin/type_c.rs b/examples/rt685s-evk/src/bin/type_c.rs index 4f6981990..cba3c6c60 100644 --- a/examples/rt685s-evk/src/bin/type_c.rs +++ b/examples/rt685s-evk/src/bin/type_c.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] - -use ::tps6699x::{ADDR1, TPS66994_NUM_PORTS}; +use ::tps6699x::ADDR1; +use ::tps6699x::asynchronous::embassy::interrupt::InterruptReceiver; use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice; use embassy_executor::Spawner; use embassy_imxrt::gpio::{Input, Inverter, Pull}; @@ -15,58 +15,65 @@ use embassy_time::{self as _, Delay}; use embedded_services::GlobalRawMutex; use embedded_services::event::MapSender; use embedded_services::{error, info}; -use embedded_usb_pd::GlobalPortId; +use embedded_usb_pd::{GlobalPortId, LocalPortId}; use power_policy_interface::psu; use power_policy_service::psu::PsuEventReceivers; use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use tps6699x::asynchronous::embassy as tps6699x; -use type_c_interface::port::ControllerId; use type_c_interface::port::PortRegistration; +use type_c_interface::port::event::PortEventBitfield; +use type_c_interface::port::{ControllerId, Device}; use type_c_interface::service::event::PortEvent as ServicePortEvent; use type_c_service::bridge::Bridge; use type_c_service::bridge::event_receiver::EventReceiver as BridgeEventReceiver; -use type_c_service::driver::tps6699x::{self as tps6699x_drv, InterruptReceiver}; -use type_c_service::service::{EventReceiver, Service}; -use type_c_service::wrapper::ControllerWrapper; -use type_c_service::wrapper::backing::{IntermediateStorage, ReferencedStorage, Storage}; -use type_c_service::wrapper::event_receiver::ArrayPortEventReceivers; -use type_c_service::wrapper::proxy::PowerProxyDevice; +use type_c_service::controller::Port; +use type_c_service::controller::event_receiver::{ + EventReceiver as PortEventReceiver, InterruptReceiver as _, PortEventSplitter, +}; +use type_c_service::controller::macros::PortComponents; +use type_c_service::controller::state::SharedState; +use type_c_service::define_controller_port_static_cell_channel; +use type_c_service::driver::tps6699x::{self as tps6699x_drv}; +use type_c_service::service::{EventReceiver as ServiceEventReceiver, Service}; 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); const PORT1_ID: GlobalPortId = GlobalPortId(1); -type DeviceType = Mutex>; -type ChargerType = power_policy_interface::charger::mock::ChargerType; - bind_interrupts!(struct Irqs { FLEXCOMM2 => embassy_imxrt::i2c::InterruptHandler; }); +type SharedStateType = Mutex; +type PortType = Mutex< + GlobalRawMutex, + Port< + 'static, + Tps6699xMutex<'static>, + SharedStateType, + DynamicSender<'static, power_policy_interface::psu::event::EventData>, + DynamicSender<'static, type_c_service::controller::event::Loopback>, + >, +>; +type ChargerType = power_policy_interface::charger::mock::ChargerType; + type BusMaster<'a> = I2cMaster<'a, Async>; type BusDevice<'a> = I2cDevice<'a, GlobalRawMutex, BusMaster<'a>>; type Tps6699xMutex<'a> = Mutex>>; -type Wrapper<'a> = ControllerWrapper< - 'a, - GlobalRawMutex, - Tps6699xMutex<'a>, - DynamicSender<'a, power_policy_interface::psu::event::EventData>, ->; type Controller<'a> = tps6699x::controller::Controller>; type InterruptProcessor<'a> = tps6699x::interrupt::InterruptProcessor<'a, GlobalRawMutex, BusDevice<'a>>; type PowerPolicySenderType = MapSender< - power_policy_interface::service::event::Event<'static, DeviceType>, + power_policy_interface::service::event::Event<'static, PortType>, power_policy_interface::service::event::EventData, DynImmediatePublisher<'static, power_policy_interface::service::event::EventData>, fn( - power_policy_interface::service::event::Event<'static, DeviceType>, + power_policy_interface::service::event::Event<'static, PortType>, ) -> power_policy_interface::service::event::EventData, >; @@ -76,11 +83,17 @@ type PowerPolicyServiceType = Mutex< GlobalRawMutex, power_policy_service::service::Service< 'static, - ArrayRegistration<'static, DeviceType, 2, PowerPolicySenderType, 1, ChargerType, 0>, + ArrayRegistration<'static, PortType, 2, PowerPolicySenderType, 1, ChargerType, 0>, >, >; type ServiceType = Service<'static>; +type PortEventReceiverType = PortEventReceiver< + 'static, + SharedStateType, + DynamicReceiver<'static, PortEventBitfield>, + DynamicReceiver<'static, type_c_service::controller::event::Loopback>, +>; #[embassy_executor::task] async fn bridge_task( @@ -94,29 +107,15 @@ async fn bridge_task( } } -#[embassy_executor::task] -async fn pd_controller_task( - mut event_receiver: ArrayPortEventReceivers< - 'static, - 2, - InterruptReceiver<'static, GlobalRawMutex, BusDevice<'static>>, - >, - wrapper: &'static Wrapper<'static>, -) { +#[embassy_executor::task(pool_size = 2)] +async fn port_task(mut event_receiver: PortEventReceiverType, port: &'static PortType) { + port.lock().await.sync_state().await.unwrap(); + loop { let event = event_receiver.wait_event().await; - - let output = wrapper - .process_event(&mut event_receiver.sink_ready_timeout, event) - .await; + let output = port.lock().await.process_event(event).await; if let Err(e) = output { error!("Error processing event: {:?}", e); - continue; - } - - let output = output.unwrap(); - if let Err(e) = wrapper.finalize(&mut event_receiver.power_proxies, output).await { - error!("Error finalizing output: {:?}", e); } } } @@ -126,9 +125,20 @@ async fn interrupt_task(mut int_in: Input<'static>, mut interrupt: InterruptProc tps6699x::task::interrupt_task(&mut int_in, &mut [&mut interrupt]).await; } +#[embassy_executor::task] +async fn interrupt_splitter_task( + mut interrupt_receiver: InterruptReceiver<'static, GlobalRawMutex, BusDevice<'static>>, + mut interrupt_splitter: PortEventSplitter<2, DynamicSender<'static, PortEventBitfield>>, +) -> ! { + loop { + let interrupts = interrupt_receiver.wait_interrupt().await; + interrupt_splitter.process_interrupts(interrupts).await; + } +} + #[embassy_executor::task] async fn power_policy_task( - psu_events: PsuEventReceivers<'static, 2, DeviceType, DynamicReceiver<'static, psu::event::EventData>>, + psu_events: PsuEventReceivers<'static, 2, PortType, DynamicReceiver<'static, psu::event::EventData>>, power_policy: &'static PowerPolicyServiceType, ) { power_policy_service::service::task::psu_task(psu_events, power_policy).await; @@ -137,10 +147,9 @@ async fn power_policy_task( #[embassy_executor::task] async fn type_c_service_task( service: &'static Mutex, - event_receiver: EventReceiver<'static, PowerPolicyReceiverType>, - wrappers: [&'static Wrapper<'static>; NUM_PD_CONTROLLERS], + event_receiver: ServiceEventReceiver<'static, PowerPolicyReceiverType>, ) { - type_c_service::task::task(service, event_receiver, wrappers).await; + type_c_service::task::task(service, event_receiver).await; } #[embassy_executor::main] @@ -186,53 +195,6 @@ 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, - [ - PortRegistration { - id: PORT0_ID, - sender: PORT0_CHANNEL.dyn_sender(), - receiver: PORT0_CHANNEL.dyn_receiver(), - }, - PortRegistration { - id: PORT1_ID, - sender: PORT1_CHANNEL.dyn_sender(), - receiver: PORT1_CHANNEL.dyn_receiver(), - }, - ], - )); - - static POLICY_CHANNEL0: StaticCell> = StaticCell::new(); - let policy_channel0 = POLICY_CHANNEL0.init(Channel::new()); - let policy_sender0 = policy_channel0.dyn_sender(); - let policy_receiver0 = policy_channel0.dyn_receiver(); - - static POLICY_CHANNEL1: StaticCell> = StaticCell::new(); - let policy_channel1 = POLICY_CHANNEL1.init(Channel::new()); - let policy_sender1 = policy_channel1.dyn_sender(); - let policy_receiver1 = policy_channel1.dyn_receiver(); - - let (intermediate, power_event_receivers) = storage - .try_create_intermediate([("Pd0", policy_sender0), ("Pd1", policy_sender1)]) - .expect("Failed to create intermediate storage"); - static INTERMEDIATE: StaticCell< - IntermediateStorage>, - > = StaticCell::new(); - let intermediate = INTERMEDIATE.init(intermediate); - - static REFERENCED: StaticCell< - ReferencedStorage>, - > = StaticCell::new(); - let referenced = REFERENCED.init( - intermediate - .try_create_referenced() - .expect("Failed to create referenced storage"), - ); - info!("Spawining PD controller task"); static CONTROLLER_MUTEX: StaticCell> = StaticCell::new(); let controller_mutex = CONTROLLER_MUTEX.init(Mutex::new(tps6699x_drv::tps66994( @@ -242,14 +204,62 @@ async fn main(spawner: Spawner) { "tps6699x_0", ))); - static WRAPPER: StaticCell = StaticCell::new(); - let wrapper = WRAPPER.init(ControllerWrapper::new( + static PORT0_CHANNEL: Channel = Channel::new(); + static PORT1_CHANNEL: Channel = Channel::new(); + + static PORT_REGISTRATION: StaticCell<[PortRegistration; 2]> = StaticCell::new(); + let port_registration = PORT_REGISTRATION.init([ + PortRegistration { + id: PORT0_ID, + sender: PORT0_CHANNEL.dyn_sender(), + receiver: PORT0_CHANNEL.dyn_receiver(), + }, + PortRegistration { + id: PORT1_ID, + sender: PORT1_CHANNEL.dyn_sender(), + receiver: PORT1_CHANNEL.dyn_receiver(), + }, + ]); + + static PD_REGISTRATION: StaticCell> = StaticCell::new(); + let pd_registration = PD_REGISTRATION.init(Device::new(CONTROLLER0_ID, port_registration)); + + controller_context.register_controller(pd_registration).unwrap(); + + define_controller_port_static_cell_channel!(pub(self), port0, GlobalRawMutex, Tps6699xMutex<'static>); + let PortComponents { + port: port0, + power_policy_receiver: policy_receiver0, + event_receiver: event_receiver0, + interrupt_sender: port0_interrupt_sender, + } = port0::create( + "PD0", + LocalPortId(0), + PORT0_ID, + Default::default(), controller_mutex, + controller_context, + ); + + let bridge_receiver = BridgeEventReceiver::new(pd_registration); + let bridge = Bridge::new(controller_mutex, pd_registration); + + define_controller_port_static_cell_channel!(pub(self), port1, GlobalRawMutex, Tps6699xMutex<'static>); + let PortComponents { + port: port1, + power_policy_receiver: policy_receiver1, + event_receiver: event_receiver1, + interrupt_sender: port1_interrupt_sender, + } = port1::create( + "PD1", + LocalPortId(0), + PORT1_ID, Default::default(), - referenced, - )); - let bridge_receiver = BridgeEventReceiver::new(&referenced.pd_controller); - let bridge = Bridge::new(controller_mutex, &referenced.pd_controller); + controller_mutex, + controller_context, + ); + + let port_event_splitter = PortEventSplitter::new([port0_interrupt_sender, port1_interrupt_sender]); // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot static POWER_POLICY_CHANNEL: StaticCell< @@ -264,7 +274,7 @@ async fn main(spawner: Spawner) { // Create power policy service let power_policy_registration = ArrayRegistration { - psus: [&wrapper.ports[0].proxy, &wrapper.ports[1].proxy], + psus: [port0, port1], chargers: [], service_senders: [power_policy_sender], }; @@ -282,8 +292,7 @@ async fn main(spawner: Spawner) { spawner.spawn( type_c_service_task( type_c_service, - EventReceiver::new(controller_context, power_policy_subscriber), - [wrapper], + ServiceEventReceiver::new(controller_context, power_policy_subscriber), ) .expect("Failed to create type-c service task"), ); @@ -291,24 +300,19 @@ async fn main(spawner: Spawner) { info!("Spawining power policy task"); spawner.spawn( power_policy_task( - PsuEventReceivers::new( - [&wrapper.ports[0].proxy, &wrapper.ports[1].proxy], - [policy_receiver0, policy_receiver1], - ), + PsuEventReceivers::new([port0, port1], [policy_receiver0, policy_receiver1]), power_service, ) .expect("Failed to create power policy task"), ); spawner.spawn(bridge_task(bridge_receiver, bridge).expect("Failed to create bridge task")); + spawner.spawn(port_task(event_receiver0, port0).expect("Failed to create controller0 task")); + + spawner.spawn(port_task(event_receiver1, port1).expect("Failed to create controller1 task")); + spawner.spawn( - pd_controller_task( - ArrayPortEventReceivers::new( - InterruptReceiver::new(interrupt_receiver), - power_event_receivers, - ), - wrapper, - ) - .expect("Failed to create pd controller task"), + interrupt_splitter_task(interrupt_receiver, port_event_splitter) + .expect("Failed to spawn interrupt splitter task"), ); } diff --git a/examples/rt685s-evk/src/bin/type_c_cfu.rs b/examples/rt685s-evk/src/bin/type_c_cfu.rs index d308229df..1a9355bf4 100644 --- a/examples/rt685s-evk/src/bin/type_c_cfu.rs +++ b/examples/rt685s-evk/src/bin/type_c_cfu.rs @@ -1,7 +1,8 @@ #![no_std] #![no_main] -use ::tps6699x::{ADDR1, TPS66994_NUM_PORTS}; +use ::tps6699x::ADDR1; +use ::tps6699x::asynchronous::embassy::interrupt::InterruptReceiver; use cfu_service::CfuClient; use cfu_service::component::{CfuDevice, InternalResponseData, RequestData}; use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice; @@ -21,22 +22,26 @@ use embedded_cfu_protocol::protocol_definitions::{FwUpdateOffer, FwUpdateOfferRe use embedded_services::GlobalRawMutex; use embedded_services::event::MapSender; use embedded_services::{error, info}; -use embedded_usb_pd::GlobalPortId; +use embedded_usb_pd::{GlobalPortId, LocalPortId}; use power_policy_interface::psu; use power_policy_service::psu::PsuEventReceivers; use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; use tps6699x::asynchronous::embassy as tps6699x; -use type_c_interface::port::{ControllerId, PortRegistration}; +use type_c_interface::port::event::PortEventBitfield; +use type_c_interface::port::{ControllerId, Device, PortRegistration}; use type_c_interface::service::event::PortEvent as ServicePortEvent; use type_c_service::bridge::Bridge; use type_c_service::bridge::event_receiver::EventReceiver as BridgeEventReceiver; -use type_c_service::driver::tps6699x::{self as tps6699x_drv, InterruptReceiver}; -use type_c_service::service::{EventReceiver, Service}; -use type_c_service::wrapper::ControllerWrapper; -use type_c_service::wrapper::backing::{IntermediateStorage, ReferencedStorage, Storage}; -use type_c_service::wrapper::event_receiver::ArrayPortEventReceivers; -use type_c_service::wrapper::proxy::PowerProxyDevice; +use type_c_service::controller::Port; +use type_c_service::controller::event_receiver::{ + EventReceiver as PortEventReceiver, InterruptReceiver as _, PortEventSplitter, +}; +use type_c_service::controller::macros::PortComponents; +use type_c_service::controller::state::SharedState as PortSharedState; +use type_c_service::define_controller_port_static_cell_channel; +use type_c_service::driver::tps6699x::{self as tps6699x_drv}; +use type_c_service::service::{EventReceiver as ServiceEventReceiver, Service}; extern crate rt685s_evk_example; @@ -55,27 +60,31 @@ impl cfu_service::customization::Customization for CfuCustomization { } } -type DeviceType = Mutex>; +type PortSharedStateType = Mutex; +type PortType = Mutex< + GlobalRawMutex, + Port< + 'static, + Tps6699xMutex<'static>, + PortSharedStateType, + DynamicSender<'static, power_policy_interface::psu::event::EventData>, + DynamicSender<'static, type_c_service::controller::event::Loopback>, + >, +>; type ChargerType = power_policy_interface::charger::mock::ChargerType; type BusMaster<'a> = I2cMaster<'a, Async>; type BusDevice<'a> = I2cDevice<'a, GlobalRawMutex, BusMaster<'a>>; type Tps6699xMutex<'a> = Mutex>>; -type Wrapper<'a> = ControllerWrapper< - 'a, - GlobalRawMutex, - Tps6699xMutex<'a>, - DynamicSender<'a, power_policy_interface::psu::event::EventData>, ->; type Controller<'a> = tps6699x::controller::Controller>; type InterruptProcessor<'a> = tps6699x::interrupt::InterruptProcessor<'a, GlobalRawMutex, BusDevice<'a>>; type PowerPolicySenderType = MapSender< - power_policy_interface::service::event::Event<'static, DeviceType>, + power_policy_interface::service::event::Event<'static, PortType>, power_policy_interface::service::event::EventData, DynImmediatePublisher<'static, power_policy_interface::service::event::EventData>, fn( - power_policy_interface::service::event::Event<'static, DeviceType>, + power_policy_interface::service::event::Event<'static, PortType>, ) -> power_policy_interface::service::event::EventData, >; @@ -85,16 +94,21 @@ type PowerPolicyServiceType = Mutex< GlobalRawMutex, power_policy_service::service::Service< 'static, - ArrayRegistration<'static, DeviceType, 2, PowerPolicySenderType, 1, ChargerType, 0>, + ArrayRegistration<'static, PortType, 2, PowerPolicySenderType, 1, ChargerType, 0>, >, >; type ServiceType = Service<'static>; +type PortEventReceiverType = PortEventReceiver< + 'static, + PortSharedStateType, + DynamicReceiver<'static, PortEventBitfield>, + DynamicReceiver<'static, type_c_service::controller::event::Loopback>, +>; type CfuUpdaterSharedStateType = Mutex; type CfuUpdaterType<'a> = cfu_service::basic::Updater<'a, Tps6699xMutex<'a>, CfuUpdaterSharedStateType, CfuCustomization>; -const NUM_PD_CONTROLLERS: usize = 1; const CONTROLLER0_ID: ControllerId = ControllerId(0); const CONTROLLER0_CFU_ID: ComponentId = 0x12; const PORT0_ID: GlobalPortId = GlobalPortId(0); @@ -112,28 +126,15 @@ async fn bridge_task( } } -#[embassy_executor::task] -async fn pd_controller_task( - mut event_receiver: ArrayPortEventReceivers< - 'static, - 2, - InterruptReceiver<'static, GlobalRawMutex, BusDevice<'static>>, - >, - wrapper: &'static Wrapper<'static>, -) { +#[embassy_executor::task(pool_size = 2)] +async fn port_task(mut event_receiver: PortEventReceiverType, port: &'static PortType) { + port.lock().await.sync_state().await.unwrap(); + loop { let event = event_receiver.wait_event().await; - - let output = wrapper - .process_event(&mut event_receiver.sink_ready_timeout, event) - .await; + let output = port.lock().await.process_event(event).await; if let Err(e) = output { error!("Error processing event: {:?}", e); - continue; - } - let output = output.unwrap(); - if let Err(e) = wrapper.finalize(&mut event_receiver.power_proxies, output).await { - error!("Error finalizing output: {:?}", e); } } } @@ -155,6 +156,17 @@ async fn interrupt_task(mut int_in: Input<'static>, mut interrupt: InterruptProc tps6699x::task::interrupt_task(&mut int_in, &mut [&mut interrupt]).await; } +#[embassy_executor::task] +async fn interrupt_splitter_task( + mut interrupt_receiver: InterruptReceiver<'static, GlobalRawMutex, BusDevice<'static>>, + mut interrupt_splitter: PortEventSplitter<2, DynamicSender<'static, PortEventBitfield>>, +) -> ! { + loop { + let interrupts = interrupt_receiver.wait_interrupt().await; + interrupt_splitter.process_interrupts(interrupts).await; + } +} + #[embassy_executor::task] async fn fw_update_task(cfu_client: &'static CfuClient) { Timer::after_millis(1000).await; @@ -238,7 +250,7 @@ async fn fw_update_task(cfu_client: &'static CfuClient) { #[embassy_executor::task] async fn power_policy_task( - psu_events: PsuEventReceivers<'static, 2, DeviceType, DynamicReceiver<'static, psu::event::EventData>>, + psu_events: PsuEventReceivers<'static, 2, PortType, DynamicReceiver<'static, psu::event::EventData>>, power_policy: &'static PowerPolicyServiceType, ) { power_policy_service::service::task::psu_task(psu_events, power_policy).await; @@ -247,10 +259,9 @@ async fn power_policy_task( #[embassy_executor::task] async fn type_c_service_task( service: &'static Mutex, - event_receiver: EventReceiver<'static, PowerPolicyReceiverType>, - wrappers: [&'static Wrapper<'static>; NUM_PD_CONTROLLERS], + event_receiver: ServiceEventReceiver<'static, PowerPolicyReceiverType>, ) { - type_c_service::task::task(service, event_receiver, wrappers).await; + type_c_service::task::task(service, event_receiver).await; } #[embassy_executor::main] @@ -296,57 +307,6 @@ 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, - [ - PortRegistration { - id: PORT0_ID, - sender: PORT0_CHANNEL.dyn_sender(), - receiver: PORT0_CHANNEL.dyn_receiver(), - }, - PortRegistration { - id: PORT1_ID, - sender: PORT1_CHANNEL.dyn_sender(), - receiver: PORT1_CHANNEL.dyn_receiver(), - }, - ], - )); - - static POLICY_CHANNEL0: StaticCell> = StaticCell::new(); - let policy_channel0 = POLICY_CHANNEL0.init(Channel::new()); - let policy_sender0 = policy_channel0.dyn_sender(); - let policy_receiver0 = policy_channel0.dyn_receiver(); - - static POLICY_CHANNEL1: StaticCell> = StaticCell::new(); - let policy_channel1 = POLICY_CHANNEL1.init(Channel::new()); - let policy_sender1 = policy_channel1.dyn_sender(); - let policy_receiver1 = policy_channel1.dyn_receiver(); - - let (intermediate, power_event_receivers) = storage - .try_create_intermediate([("Pd0", policy_sender0), ("Pd1", policy_sender1)]) - .expect("Failed to create intermediate storage"); - static INTERMEDIATE: StaticCell< - IntermediateStorage>, - > = StaticCell::new(); - let intermediate = INTERMEDIATE.init(intermediate); - - static REFERENCED: StaticCell< - ReferencedStorage< - TPS66994_NUM_PORTS, - GlobalRawMutex, - DynamicSender<'_, power_policy_interface::psu::event::EventData>, - >, - > = StaticCell::new(); - let referenced = REFERENCED.init( - intermediate - .try_create_referenced() - .expect("Failed to create referenced storage"), - ); - info!("Spawining PD controller task"); static CONTROLLER_MUTEX: StaticCell> = StaticCell::new(); let controller_mutex = CONTROLLER_MUTEX.init(Mutex::new(tps6699x_drv::tps66994( @@ -356,6 +316,27 @@ async fn main(spawner: Spawner) { "tps6699x_0", ))); + static PORT0_CHANNEL: Channel = Channel::new(); + static PORT1_CHANNEL: Channel = Channel::new(); + static PORT_REGISTRATION: StaticCell<[PortRegistration; 2]> = StaticCell::new(); + let port_registration = PORT_REGISTRATION.init([ + PortRegistration { + id: PORT0_ID, + sender: PORT0_CHANNEL.dyn_sender(), + receiver: PORT0_CHANNEL.dyn_receiver(), + }, + PortRegistration { + id: PORT1_ID, + sender: PORT1_CHANNEL.dyn_sender(), + receiver: PORT1_CHANNEL.dyn_receiver(), + }, + ]); + + static PD_REGISTRATION: StaticCell> = StaticCell::new(); + let pd_registration = PD_REGISTRATION.init(Device::new(CONTROLLER0_ID, port_registration)); + + controller_context.register_controller(pd_registration).unwrap(); + // Create controller CFU device and updater static CFU_DEVICE: StaticCell = StaticCell::new(); let cfu_device = CFU_DEVICE.init(CfuDevice::new(CONTROLLER0_CFU_ID)); @@ -373,17 +354,45 @@ async fn main(spawner: Spawner) { CONTROLLER0_CFU_ID, CfuCustomization, ); + let bridge_receiver = BridgeEventReceiver::new(pd_registration); + let bridge = Bridge::new(controller_mutex, pd_registration); // Create CFU client static CFU_CLIENT: OnceLock = OnceLock::new(); let cfu_client = CfuClient::new(&CFU_CLIENT).await; cfu_client.register_device(cfu_device).unwrap(); - let bridge_receiver = BridgeEventReceiver::new(&referenced.pd_controller); - let bridge = Bridge::new(controller_mutex, &referenced.pd_controller); + define_controller_port_static_cell_channel!(pub(self), port0, GlobalRawMutex, Tps6699xMutex<'static>); + let PortComponents { + port: port0, + power_policy_receiver: policy_receiver0, + event_receiver: event_receiver0, + interrupt_sender: port0_interrupt_sender, + } = port0::create( + "PD0", + LocalPortId(0), + PORT0_ID, + Default::default(), + controller_mutex, + controller_context, + ); - static WRAPPER: StaticCell = StaticCell::new(); - let wrapper = WRAPPER.init(ControllerWrapper::new(controller_mutex, Default::default(), referenced)); + define_controller_port_static_cell_channel!(pub(self),port1, GlobalRawMutex, Tps6699xMutex<'static>); + let PortComponents { + port: port1, + power_policy_receiver: policy_receiver1, + event_receiver: event_receiver1, + interrupt_sender: port1_interrupt_sender, + } = port1::create( + "PD1", + LocalPortId(0), + PORT1_ID, + Default::default(), + controller_mutex, + controller_context, + ); + + let port_event_splitter = PortEventSplitter::new([port0_interrupt_sender, port1_interrupt_sender]); // Create power policy service // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot @@ -398,7 +407,7 @@ async fn main(spawner: Spawner) { let power_policy_subscriber = power_policy_channel.dyn_subscriber().unwrap(); let power_policy_registration = ArrayRegistration { - psus: [&wrapper.ports[0].proxy, &wrapper.ports[1].proxy], + psus: [port0, port1], chargers: [], service_senders: [power_policy_sender], }; @@ -416,8 +425,7 @@ async fn main(spawner: Spawner) { spawner.spawn( type_c_service_task( type_c_service, - EventReceiver::new(controller_context, power_policy_subscriber), - [wrapper], + ServiceEventReceiver::new(controller_context, power_policy_subscriber), ) .expect("Failed to spawn type-c service task"), ); @@ -425,25 +433,20 @@ async fn main(spawner: Spawner) { info!("Spawining power policy task"); spawner.spawn( power_policy_task( - PsuEventReceivers::new( - [&wrapper.ports[0].proxy, &wrapper.ports[1].proxy], - [policy_receiver0, policy_receiver1], - ), + PsuEventReceivers::new([port0, port1], [policy_receiver0, policy_receiver1]), power_service, ) .expect("Failed to create power policy task"), ); spawner.spawn(bridge_task(bridge_receiver, bridge).expect("Failed to create bridge task")); + spawner.spawn(port_task(event_receiver0, port0).expect("Failed to create controller0 task")); + + spawner.spawn(port_task(event_receiver1, port1).expect("Failed to create controller1 task")); + spawner.spawn( - pd_controller_task( - ArrayPortEventReceivers::new( - InterruptReceiver::new(interrupt_receiver), - power_event_receivers, - ), - wrapper, - ) - .expect("Failed to create PD controller task"), + interrupt_splitter_task(interrupt_receiver, port_event_splitter) + .expect("Failed to spawn interrupt splitter task"), ); spawner.spawn(cfu_updater_task(cfu_event_receiver, cfu_updater).expect("Failed to create CFU updater task")); diff --git a/examples/std/src/bin/power_policy.rs b/examples/std/src/bin/power_policy.rs index 9df1dfbb5..763e106fd 100644 --- a/examples/std/src/bin/power_policy.rs +++ b/examples/std/src/bin/power_policy.rs @@ -60,18 +60,21 @@ impl<'a> ExampleDevice<'a> { } pub async fn simulate_attach(&mut self) { + self.state.attach().unwrap(); self.sender .send(power_policy_interface::psu::event::EventData::Attached) .await; } pub async fn simulate_update_consumer_power_capability(&mut self, capability: Option) { + self.state.update_consumer_power_capability(capability).unwrap(); self.sender .send(power_policy_interface::psu::event::EventData::UpdatedConsumerCapability(capability)) .await; } pub async fn simulate_detach(&mut self) { + self.state.detach(); self.sender .send(power_policy_interface::psu::event::EventData::Detached) .await; @@ -81,6 +84,9 @@ impl<'a> ExampleDevice<'a> { &mut self, capability: Option, ) { + self.state + .update_requested_provider_power_capability(capability) + .unwrap(); self.sender .send(power_policy_interface::psu::event::EventData::RequestedProviderCapability(capability)) .await @@ -90,17 +96,17 @@ impl<'a> ExampleDevice<'a> { impl Psu for ExampleDevice<'_> { async fn disconnect(&mut self) -> Result<(), Error> { debug!("ExampleDevice disconnect"); - Ok(()) + self.state.disconnect(false) } async fn connect_provider(&mut self, capability: ProviderPowerCapability) -> Result<(), Error> { debug!("ExampleDevice connect_provider with {capability:?}"); - Ok(()) + self.state.connect_provider(capability) } async fn connect_consumer(&mut self, capability: ConsumerPowerCapability) -> Result<(), Error> { debug!("ExampleDevice connect_consumer with {capability:?}"); - Ok(()) + self.state.connect_consumer(capability) } fn state(&self) -> &psu::State { diff --git a/examples/std/src/bin/type_c/service.rs b/examples/std/src/bin/type_c/service.rs index 6d24fbe6b..e05b2f18e 100644 --- a/examples/std/src/bin/type_c/service.rs +++ b/examples/std/src/bin/type_c/service.rs @@ -5,43 +5,46 @@ use embassy_sync::pubsub::{DynImmediatePublisher, DynSubscriber, PubSubChannel}; use embassy_time::Timer; use embedded_services::GlobalRawMutex; use embedded_services::event::MapSender; -use embedded_usb_pd::GlobalPortId; use embedded_usb_pd::ado::Ado; use embedded_usb_pd::type_c::Current; +use embedded_usb_pd::{GlobalPortId, LocalPortId}; use log::*; use power_policy_interface::charger::mock::ChargerType; use power_policy_interface::psu; use power_policy_service::psu::PsuEventReceivers; 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, PortRegistration}; +use std_examples::type_c::mock_controller::Port; +use std_examples::type_c::mock_controller::{self, InterruptReceiver}; +use type_c_interface::port::event::PortEventBitfield; +use type_c_interface::port::{ControllerId, Device, PortRegistration}; use type_c_interface::service::event::PortEvent as ServicePortEvent; +use type_c_interface::service::event::PortEventData as ServicePortEventData; use type_c_service::bridge::Bridge; use type_c_service::bridge::event_receiver::EventReceiver as BridgeEventReceiver; +use type_c_service::controller::event_receiver::InterruptReceiver as _; +use type_c_service::controller::event_receiver::{EventReceiver as PortEventReceiver, PortEventSplitter}; +use type_c_service::controller::macros::PortComponents; +use type_c_service::controller::state::SharedState; +use type_c_service::define_controller_port_static_cell_channel; use type_c_service::service::config::Config; -use type_c_service::service::{EventReceiver, Service}; +use type_c_service::service::{EventReceiver as ServiceEventReceiver, Service}; use type_c_service::util::power_capability_from_current; -use type_c_service::wrapper::backing::Storage; -use type_c_service::wrapper::event_receiver::ArrayPortEventReceivers; -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; -type DeviceType = Mutex>; +type ControllerType = Mutex>; +type PortType = Mutex>; type PowerPolicySenderType = MapSender< - power_policy_interface::service::event::Event<'static, DeviceType>, + power_policy_interface::service::event::Event<'static, PortType>, power_policy_interface::service::event::EventData, DynImmediatePublisher<'static, power_policy_interface::service::event::EventData>, fn( - power_policy_interface::service::event::Event<'static, DeviceType>, + power_policy_interface::service::event::Event<'static, PortType>, ) -> power_policy_interface::service::event::EventData, >; @@ -51,38 +54,43 @@ type PowerPolicyServiceType = Mutex< GlobalRawMutex, power_policy_service::service::Service< 'static, - ArrayRegistration<'static, DeviceType, 1, PowerPolicySenderType, 1, ChargerType, 0>, + ArrayRegistration<'static, PortType, 1, PowerPolicySenderType, 1, ChargerType, 0>, >, >; type ServiceType = Service<'static>; +type SharedStateType = Mutex; +type PortEventReceiverType = PortEventReceiver< + 'static, + SharedStateType, + DynamicReceiver<'static, PortEventBitfield>, + DynamicReceiver<'static, type_c_service::controller::event::Loopback>, +>; #[embassy_executor::task] -async fn controller_task( - mut event_receiver: ArrayPortEventReceivers<'static, 1, mock_controller::InterruptReceiver<'static>>, - wrapper: &'static Wrapper<'static>, - controller: &'static Mutex>, -) { - controller.lock().await.custom_function(); - +async fn port_task(mut event_receiver: PortEventReceiverType, port: &'static PortType) { loop { let event = event_receiver.wait_event().await; - - let output = wrapper - .process_event(&mut event_receiver.sink_ready_timeout, event) - .await; + let output = port.lock().await.process_event(event).await; if let Err(e) = output { error!("Error processing event: {e:?}"); } let output = output.unwrap(); - if let Output::PdAlert(OutputPdAlert { port, ado }) = &output { - info!("Port{}: PD alert received: {:?}", port.0, ado); + if let Some(ServicePortEventData::Alert(ado)) = &output { + info!("PD alert received: {:?}", ado); } + } +} - if let Err(e) = wrapper.finalize(&mut event_receiver.power_proxies, output).await { - error!("Error finalizing output: {e:?}"); - } +#[embassy_executor::task] +async fn interrupt_splitter_task( + mut interrupt_receiver: InterruptReceiver<'static>, + mut interrupt_splitter: PortEventSplitter<1, DynamicSender<'static, PortEventBitfield>>, +) -> ! { + loop { + let interrupts = interrupt_receiver.wait_interrupt().await; + interrupt_splitter.process_interrupts(interrupts).await; } } @@ -109,70 +117,40 @@ async fn task(spawner: Spawner) { 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( - controller_context, - CONTROLLER0_ID, - [PortRegistration { - id: PORT0_ID, - sender: PORT0_CHANNEL.dyn_sender(), - receiver: PORT0_CHANNEL.dyn_receiver(), - }], - )); - - static POLICY_CHANNEL: StaticCell> = - StaticCell::new(); - let policy_channel = POLICY_CHANNEL.init(Channel::new()); - - let policy_sender = policy_channel.dyn_sender(); - let policy_receiver = policy_channel.dyn_receiver(); - - let (intermediate, power_event_receivers) = storage - .try_create_intermediate([("Pd0", policy_sender)]) - .expect("Failed to create intermediate storage"); - - static INTERMEDIATE: StaticCell< - type_c_service::wrapper::backing::IntermediateStorage< - 1, - GlobalRawMutex, - DynamicSender<'static, psu::event::EventData>, - >, - > = StaticCell::new(); - let intermediate = INTERMEDIATE.init(intermediate); - - static REFERENCED: StaticCell< - type_c_service::wrapper::backing::ReferencedStorage< - 1, - GlobalRawMutex, - DynamicSender<'_, psu::event::EventData>, - >, - > = StaticCell::new(); - let referenced = REFERENCED.init( - intermediate - .try_create_referenced() - .expect("Failed to create referenced storage"), - ); - - let event_receiver = ArrayPortEventReceivers::new( - state.create_interrupt_receiver(), - power_event_receivers, - ); - - static CONTROLLER: StaticCell> = StaticCell::new(); + 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::new( - controller, + static PORT_CHANNEL: Channel = Channel::new(); + + static PORT_REGISTRATION: StaticCell<[PortRegistration; 1]> = StaticCell::new(); + let port_registration = PORT_REGISTRATION.init([PortRegistration { + id: PORT0_ID, + sender: PORT_CHANNEL.dyn_sender(), + receiver: PORT_CHANNEL.dyn_receiver(), + }]); + + static PD_REGISTRATION: StaticCell> = StaticCell::new(); + let pd_registration = PD_REGISTRATION.init(Device::new(CONTROLLER0_ID, port_registration)); + + controller_context.register_controller(pd_registration).unwrap(); + + let bridge_receiver = BridgeEventReceiver::new(pd_registration); + let bridge = Bridge::new(controller, pd_registration); + + define_controller_port_static_cell_channel!(pub(self), port, GlobalRawMutex, Mutex>); + let PortComponents { + port, + power_policy_receiver, + event_receiver, + interrupt_sender: port_interrupt_sender, + } = port::create( + "PD0", + LocalPortId(0), + PORT0_ID, Default::default(), - referenced, - )); - - let bridge_receiver = BridgeEventReceiver::new(&referenced.pd_controller); - let bridge = Bridge::new(controller, &referenced.pd_controller); + controller, + controller_context, + ); // Create type-c service // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot @@ -187,7 +165,7 @@ async fn task(spawner: Spawner) { let power_policy_subscriber = power_policy_channel.dyn_subscriber().unwrap(); let power_policy_registration = ArrayRegistration { - psus: [&wrapper.ports[0].proxy], + psus: [port], service_senders: [power_policy_sender], chargers: [], }; @@ -203,23 +181,27 @@ async fn task(spawner: Spawner) { // Spin up power policy service spawner.spawn( - power_policy_psu_task( - PsuEventReceivers::new([&wrapper.ports[0].proxy], [policy_receiver]), - power_service, - ) - .expect("Failed to create power policy task"), + power_policy_psu_task(PsuEventReceivers::new([port], [power_policy_receiver]), power_service) + .expect("Failed to create power policy task"), ); spawner.spawn( type_c_service_task( type_c_service, - EventReceiver::new(controller_context, power_policy_subscriber), - [wrapper], + ServiceEventReceiver::new(controller_context, power_policy_subscriber), ) .expect("Failed to create type-c service task"), ); spawner.spawn(bridge_task(bridge_receiver, bridge).expect("Failed to create bridge task")); - spawner.spawn(controller_task(event_receiver, wrapper, controller).expect("Failed to create controller task")); + spawner.spawn(port_task(event_receiver, port).expect("Failed to create controller task")); + + spawner.spawn( + interrupt_splitter_task( + state.create_interrupt_receiver(), + PortEventSplitter::new([port_interrupt_sender]), + ) + .expect("Failed to create interrupt splitter task"), + ); Timer::after_millis(1000).await; info!("Simulating connection"); @@ -247,7 +229,7 @@ async fn task(spawner: Spawner) { #[embassy_executor::task] async fn power_policy_psu_task( - psu_events: PsuEventReceivers<'static, 1, DeviceType, DynamicReceiver<'static, psu::event::EventData>>, + psu_events: PsuEventReceivers<'static, 1, PortType, DynamicReceiver<'static, psu::event::EventData>>, power_policy: &'static PowerPolicyServiceType, ) { power_policy_service::service::task::psu_task(psu_events, power_policy).await; @@ -256,11 +238,10 @@ async fn power_policy_psu_task( #[embassy_executor::task] async fn type_c_service_task( service: &'static Mutex, - event_receiver: EventReceiver<'static, PowerPolicyReceiverType>, - wrappers: [&'static Wrapper<'static>; NUM_PD_CONTROLLERS], + event_receiver: ServiceEventReceiver<'static, PowerPolicyReceiverType>, ) { info!("Starting type-c task"); - type_c_service::task::task(service, event_receiver, wrappers).await; + type_c_service::task::task(service, event_receiver).await; } fn main() { diff --git a/examples/std/src/bin/type_c/ucsi.rs b/examples/std/src/bin/type_c/ucsi.rs index 2e7ad82a1..04f126293 100644 --- a/examples/std/src/bin/type_c/ucsi.rs +++ b/examples/std/src/bin/type_c/ucsi.rs @@ -1,20 +1,19 @@ #![allow(unused_imports)] -use crate::mock_controller::Wrapper; use cfu_service::CfuClient; use embassy_executor::{Executor, Spawner}; use embassy_sync::channel::{Channel, DynamicReceiver, DynamicSender}; use embassy_sync::mutex::Mutex; use embassy_sync::once_lock::OnceLock; use embassy_sync::pubsub::{DynImmediatePublisher, DynSubscriber, PubSubChannel}; -use embedded_services::GlobalRawMutex; use embedded_services::IntrusiveList; use embedded_services::event::MapSender; -use embedded_usb_pd::GlobalPortId; +use embedded_services::{GlobalRawMutex, event}; use embedded_usb_pd::ucsi::lpm::get_connector_capability::OperationModeFlags; use embedded_usb_pd::ucsi::ppm::ack_cc_ci::Ack; use embedded_usb_pd::ucsi::ppm::get_capability::ResponseData as UcsiCapabilities; use embedded_usb_pd::ucsi::ppm::set_notification_enable::NotificationEnable; use embedded_usb_pd::ucsi::{Command, lpm, ppm}; +use embedded_usb_pd::{GlobalPortId, LocalPortId}; use log::*; use power_policy_interface::capability::PowerCapability; use power_policy_interface::charger::mock::ChargerType; @@ -22,17 +21,21 @@ use power_policy_interface::psu; use power_policy_service::psu::PsuEventReceivers; use power_policy_service::service::registration::ArrayRegistration; use static_cell::StaticCell; -use std_examples::type_c::mock_controller; -use type_c_interface::port::{ControllerId, PortRegistration}; +use std_examples::type_c::mock_controller::{self, InterruptReceiver, Port}; +use type_c_interface::port::event::PortEventBitfield; +use type_c_interface::port::{ControllerId, Device, PortRegistration}; use type_c_interface::service::context::Context; -use type_c_interface::service::event::PortEvent as ServicePortEvent; +use type_c_interface::service::event::{PortEvent as ServicePortEvent, PortEventData as ServicePortEventData}; use type_c_service::bridge::Bridge; use type_c_service::bridge::event_receiver::EventReceiver as BridgeEventReceiver; +use type_c_service::controller::event::Event as PortEvent; +use type_c_service::controller::event_receiver::InterruptReceiver as _; +use type_c_service::controller::event_receiver::{EventReceiver as PortEventReceiver, PortEventSplitter}; +use type_c_service::controller::macros::PortComponents; +use type_c_service::controller::state::SharedState; +use type_c_service::define_controller_port_static_cell_channel; use type_c_service::service::config::Config; -use type_c_service::service::{EventReceiver, Service}; -use type_c_service::wrapper::backing::Storage; -use type_c_service::wrapper::event_receiver::ArrayPortEventReceivers; -use type_c_service::wrapper::proxy::PowerProxyDevice; +use type_c_service::service::{EventReceiver as ServiceEventReceiver, Service}; const CHANNEL_CAPACITY: usize = 4; const NUM_PD_CONTROLLERS: usize = 2; @@ -41,14 +44,15 @@ const CONTROLLER1_ID: ControllerId = ControllerId(1); const PORT0_ID: GlobalPortId = GlobalPortId(0); const PORT1_ID: GlobalPortId = GlobalPortId(1); -type DeviceType = Mutex>; +type ControllerType = Mutex>; +type PortType = Mutex>; type PowerPolicySenderType = MapSender< - power_policy_interface::service::event::Event<'static, DeviceType>, + power_policy_interface::service::event::Event<'static, PortType>, power_policy_interface::service::event::EventData, DynImmediatePublisher<'static, power_policy_interface::service::event::EventData>, fn( - power_policy_interface::service::event::Event<'static, DeviceType>, + power_policy_interface::service::event::Event<'static, PortType>, ) -> power_policy_interface::service::event::EventData, >; @@ -58,11 +62,18 @@ type PowerPolicyServiceType = Mutex< GlobalRawMutex, power_policy_service::service::Service< 'static, - ArrayRegistration<'static, DeviceType, 2, PowerPolicySenderType, 1, ChargerType, 0>, + ArrayRegistration<'static, PortType, 2, PowerPolicySenderType, 1, ChargerType, 0>, >, >; type ServiceType = Service<'static>; +type SharedStateType = Mutex; +type PortEventReceiverType = PortEventReceiver< + 'static, + SharedStateType, + DynamicReceiver<'static, PortEventBitfield>, + DynamicReceiver<'static, type_c_service::controller::event::Loopback>, +>; #[embassy_executor::task] async fn opm_task(_context: &'static Context, _state: [&'static mock_controller::ControllerState; NUM_PD_CONTROLLERS]) { @@ -209,29 +220,30 @@ async fn bridge_task( } #[embassy_executor::task(pool_size = 2)] -async fn wrapper_task( - mut event_receiver: ArrayPortEventReceivers<'static, 1, mock_controller::InterruptReceiver<'static>>, - wrapper: &'static mock_controller::Wrapper<'static>, -) { +async fn port_task(mut event_receiver: PortEventReceiverType, port: &'static PortType) { loop { let event = event_receiver.wait_event().await; - - let output = wrapper - .process_event(&mut event_receiver.sink_ready_timeout, event) - .await; + let output = port.lock().await.process_event(event).await; if let Err(e) = output { error!("Error processing event: {e:?}"); } - let output = output.unwrap(); - if let Err(e) = wrapper.finalize(&mut event_receiver.power_proxies, output).await { - error!("Error finalizing output: {e:#?}"); - } + } +} + +#[embassy_executor::task(pool_size = 2)] +async fn interrupt_splitter_task( + mut interrupt_receiver: InterruptReceiver<'static>, + mut interrupt_splitter: PortEventSplitter<1, DynamicSender<'static, PortEventBitfield>>, +) -> ! { + loop { + let interrupts = interrupt_receiver.wait_interrupt().await; + interrupt_splitter.process_interrupts(interrupts).await; } } #[embassy_executor::task] async fn power_policy_task( - psu_events: PsuEventReceivers<'static, 2, DeviceType, DynamicReceiver<'static, psu::event::EventData>>, + psu_events: PsuEventReceivers<'static, 2, PortType, DynamicReceiver<'static, psu::event::EventData>>, power_policy: &'static PowerPolicyServiceType, ) { power_policy_service::service::task::psu_task(psu_events, power_policy).await; @@ -240,11 +252,9 @@ async fn power_policy_task( #[embassy_executor::task] async fn type_c_service_task( service: &'static Mutex, - event_receiver: EventReceiver<'static, PowerPolicyReceiverType>, - wrappers: [&'static Wrapper<'static>; NUM_PD_CONTROLLERS], + event_receiver: ServiceEventReceiver<'static, PowerPolicyReceiverType>, ) { - info!("Starting type-c task"); - type_c_service::task::task(service, event_receiver, wrappers).await; + type_c_service::task::task(service, event_receiver).await; } #[embassy_executor::task] @@ -256,122 +266,77 @@ 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, - [PortRegistration { - id: PORT0_ID, - sender: PORT0_CHANNEL.dyn_sender(), - receiver: PORT0_CHANNEL.dyn_receiver(), - }], - )); - - static POLICY_CHANNEL0: StaticCell> = StaticCell::new(); - let policy_channel0 = POLICY_CHANNEL0.init(Channel::new()); - let policy_sender0 = policy_channel0.dyn_sender(); - let policy_receiver0 = policy_channel0.dyn_receiver(); - - static INTERMEDIATE0: StaticCell< - type_c_service::wrapper::backing::IntermediateStorage< - 1, - GlobalRawMutex, - DynamicSender<'_, psu::event::EventData>, - >, - > = StaticCell::new(); - let (intermediate0, power_event_receivers0) = storage0 - .try_create_intermediate([("Pd0", policy_sender0)]) - .expect("Failed to create intermediate storage"); - let intermediate0 = INTERMEDIATE0.init(intermediate0); - - static REFERENCED0: StaticCell< - type_c_service::wrapper::backing::ReferencedStorage< - 1, - GlobalRawMutex, - DynamicSender<'_, psu::event::EventData>, - >, - > = StaticCell::new(); - let referenced0 = REFERENCED0.init( - intermediate0 - .try_create_referenced() - .expect("Failed to create referenced storage"), - ); - static STATE0: StaticCell = StaticCell::new(); let state0 = STATE0.init(mock_controller::ControllerState::new()); - let event_receiver0 = ArrayPortEventReceivers::new( - state0.create_interrupt_receiver(), - power_event_receivers0, - ); - static CONTROLLER0: StaticCell> = StaticCell::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::new( - controller0, - Default::default(), - referenced0, - )); - let bridge_receiver0 = BridgeEventReceiver::new(&referenced0.pd_controller); - let bridge0 = Bridge::new(controller0, &referenced0.pd_controller); - static POLICY_CHANNEL1: StaticCell> = StaticCell::new(); - let policy_channel1 = POLICY_CHANNEL1.init(Channel::new()); - 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( + static PORT_CHANNEL0: Channel = Channel::new(); + static PORT_REGISTRATION0: StaticCell<[PortRegistration; 1]> = StaticCell::new(); + let port_registration0 = PORT_REGISTRATION0.init([PortRegistration { + id: PORT0_ID, + sender: PORT_CHANNEL0.dyn_sender(), + receiver: PORT_CHANNEL0.dyn_receiver(), + }]); + + static PD_REGISTRATION0: StaticCell> = StaticCell::new(); + let pd_registration0 = PD_REGISTRATION0.init(Device::new(CONTROLLER0_ID, port_registration0)); + + controller_context.register_controller(pd_registration0).unwrap(); + + define_controller_port_static_cell_channel!(pub(self), port0, GlobalRawMutex, Mutex>); + let PortComponents { + port: port0, + power_policy_receiver: policy_receiver0, + event_receiver: event_receiver0, + interrupt_sender: port0_interrupt_sender, + } = port0::create( + "PD0", + LocalPortId(0), + PORT0_ID, + Default::default(), + controller0, controller_context, - CONTROLLER1_ID, - [PortRegistration { - id: PORT1_ID, - sender: PORT1_CHANNEL.dyn_sender(), - receiver: PORT1_CHANNEL.dyn_receiver(), - }], - )); - static INTERMEDIATE1: StaticCell< - type_c_service::wrapper::backing::IntermediateStorage< - 1, - GlobalRawMutex, - DynamicSender<'_, psu::event::EventData>, - >, - > = StaticCell::new(); - let (intermediate1, power_event_receivers1) = storage1 - .try_create_intermediate([("Pd1", policy_sender1)]) - .expect("Failed to create intermediate storage"); - let intermediate1 = INTERMEDIATE1.init(intermediate1); - - static REFERENCED1: StaticCell< - type_c_service::wrapper::backing::ReferencedStorage< - 1, - GlobalRawMutex, - DynamicSender<'_, psu::event::EventData>, - >, - > = StaticCell::new(); - let referenced1 = REFERENCED1.init( - intermediate1 - .try_create_referenced() - .expect("Failed to create referenced storage"), ); + let bridge_receiver0 = BridgeEventReceiver::new(pd_registration0); + let bridge0 = Bridge::new(controller0, pd_registration0); + static STATE1: StaticCell = StaticCell::new(); let state1 = STATE1.init(mock_controller::ControllerState::new()); - let event_receiver1 = ArrayPortEventReceivers::new( - state1.create_interrupt_receiver(), - power_event_receivers1, - ); - static CONTROLLER1: StaticCell> = StaticCell::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::new( - controller1, + + static PORT1_CHANNEL: Channel = Channel::new(); + static PORT_REGISTRATION1: StaticCell<[PortRegistration; 1]> = StaticCell::new(); + let port_registration1 = PORT_REGISTRATION1.init([PortRegistration { + id: PORT1_ID, + sender: PORT1_CHANNEL.dyn_sender(), + receiver: PORT1_CHANNEL.dyn_receiver(), + }]); + + static PD_REGISTRATION1: StaticCell> = StaticCell::new(); + let pd_registration1 = PD_REGISTRATION1.init(Device::new(CONTROLLER1_ID, port_registration1)); + + controller_context.register_controller(pd_registration1).unwrap(); + + define_controller_port_static_cell_channel!(pub(self), port1, GlobalRawMutex, Mutex>); + let PortComponents { + port: port1, + power_policy_receiver: policy_receiver1, + event_receiver: event_receiver1, + interrupt_sender: port1_interrupt_sender, + } = port1::create( + "PD1", + LocalPortId(0), + PORT1_ID, Default::default(), - referenced1, - )); - let bridge_receiver1 = BridgeEventReceiver::new(&referenced1.pd_controller); - let bridge1 = Bridge::new(controller1, &referenced1.pd_controller); + controller1, + controller_context, + ); + + let bridge_receiver1 = BridgeEventReceiver::new(pd_registration1); + let bridge1 = Bridge::new(controller1, pd_registration1); // Create power policy service // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot @@ -386,7 +351,7 @@ async fn task(spawner: Spawner) { let power_policy_subscriber = power_policy_channel.dyn_subscriber().unwrap(); let power_policy_registration = ArrayRegistration { - psus: [&wrapper0.ports[0].proxy, &wrapper1.ports[0].proxy], + psus: [port0, port1], service_senders: [power_policy_sender], chargers: [], }; @@ -429,10 +394,7 @@ async fn task(spawner: Spawner) { spawner.spawn( power_policy_task( - PsuEventReceivers::new( - [&wrapper0.ports[0].proxy, &wrapper1.ports[0].proxy], - [policy_receiver0, policy_receiver1], - ), + PsuEventReceivers::new([port0, port1], [policy_receiver0, policy_receiver1]), power_service, ) .expect("Failed to create power policy task"), @@ -441,15 +403,28 @@ async fn task(spawner: Spawner) { spawner.spawn( type_c_service_task( type_c_service, - EventReceiver::new(controller_context, power_policy_subscriber), - [wrapper0, wrapper1], + ServiceEventReceiver::new(controller_context, power_policy_subscriber), ) .expect("Failed to create type-c service task"), ); spawner.spawn(bridge_task(bridge_receiver0, bridge0).expect("Failed to create bridge0 task")); spawner.spawn(bridge_task(bridge_receiver1, bridge1).expect("Failed to create bridge1 task")); - spawner.spawn(wrapper_task(event_receiver0, wrapper0).expect("Failed to create wrapper0 task")); - spawner.spawn(wrapper_task(event_receiver1, wrapper1).expect("Failed to create wrapper1 task")); + spawner.spawn(port_task(event_receiver0, port0).expect("Failed to create wrapper0 task")); + spawner.spawn( + interrupt_splitter_task( + state0.create_interrupt_receiver(), + PortEventSplitter::new([port0_interrupt_sender]), + ) + .expect("Failed to create interrupt splitter 0 task"), + ); + spawner.spawn(port_task(event_receiver1, port1).expect("Failed to create wrapper1 task")); + spawner.spawn( + interrupt_splitter_task( + state1.create_interrupt_receiver(), + PortEventSplitter::new([port1_interrupt_sender]), + ) + .expect("Failed to create interrupt splitter 1 task"), + ); spawner.spawn(opm_task(controller_context, [state0, state1]).expect("Failed to create opm task")); } diff --git a/examples/std/src/bin/type_c/unconstrained.rs b/examples/std/src/bin/type_c/unconstrained.rs index c83d15bec..43b1b686c 100644 --- a/examples/std/src/bin/type_c/unconstrained.rs +++ b/examples/std/src/bin/type_c/unconstrained.rs @@ -1,4 +1,3 @@ -use crate::mock_controller::Wrapper; use embassy_executor::{Executor, Spawner}; use embassy_sync::channel::Channel; use embassy_sync::channel::DynamicReceiver; @@ -8,7 +7,7 @@ use embassy_sync::pubsub::{DynImmediatePublisher, DynSubscriber, PubSubChannel}; use embassy_time::Timer; use embedded_services::GlobalRawMutex; use embedded_services::event::MapSender; -use embedded_usb_pd::GlobalPortId; +use embedded_usb_pd::{GlobalPortId, LocalPortId}; use log::*; use power_policy_interface::capability::PowerCapability; use power_policy_interface::charger::mock::ChargerType; @@ -16,21 +15,24 @@ use power_policy_interface::psu; use power_policy_service::psu::PsuEventReceivers; 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::Port; +use std_examples::type_c::mock_controller::{self, InterruptReceiver}; use type_c_interface::port::ControllerId; +use type_c_interface::port::Device; use type_c_interface::port::PortRegistration; +use type_c_interface::port::event::PortEventBitfield; use type_c_interface::service::event::PortEvent as ServicePortEvent; use type_c_service::bridge::Bridge; use type_c_service::bridge::event_receiver::EventReceiver as BridgeEventReceiver; -use type_c_service::service::{EventReceiver, Service}; -use type_c_service::wrapper::backing::{IntermediateStorage, ReferencedStorage, Storage}; -use type_c_service::wrapper::event_receiver::ArrayPortEventReceivers; -use type_c_service::wrapper::proxy::PowerProxyDevice; +use type_c_service::controller::event_receiver::InterruptReceiver as _; +use type_c_service::controller::event_receiver::{EventReceiver as PortEventReceiver, PortEventSplitter}; +use type_c_service::controller::macros::PortComponents; +use type_c_service::controller::state::SharedState; +use type_c_service::define_controller_port_static_cell_channel; +use type_c_service::service::{EventReceiver as ServiceEventReceiver, Service}; const CHANNEL_CAPACITY: usize = 4; -const NUM_PD_CONTROLLERS: usize = 3; - const CONTROLLER0_ID: ControllerId = ControllerId(0); const PORT0_ID: GlobalPortId = GlobalPortId(0); @@ -42,14 +44,15 @@ const PORT2_ID: GlobalPortId = GlobalPortId(2); const DELAY_MS: u64 = 1000; -type DeviceType = Mutex>; +type ControllerType = Mutex>; +type PortType = Mutex>; type PowerPolicySenderType = MapSender< - power_policy_interface::service::event::Event<'static, DeviceType>, + power_policy_interface::service::event::Event<'static, PortType>, power_policy_interface::service::event::EventData, DynImmediatePublisher<'static, power_policy_interface::service::event::EventData>, fn( - power_policy_interface::service::event::Event<'static, DeviceType>, + power_policy_interface::service::event::Event<'static, PortType>, ) -> power_policy_interface::service::event::EventData, >; @@ -59,11 +62,18 @@ type PowerPolicyServiceType = Mutex< GlobalRawMutex, power_policy_service::service::Service< 'static, - ArrayRegistration<'static, DeviceType, 3, PowerPolicySenderType, 1, ChargerType, 0>, + ArrayRegistration<'static, PortType, 3, PowerPolicySenderType, 1, ChargerType, 0>, >, >; type ServiceType = Service<'static>; +type SharedStateType = Mutex; +type PortEventReceiverType = PortEventReceiver< + 'static, + SharedStateType, + DynamicReceiver<'static, PortEventBitfield>, + DynamicReceiver<'static, type_c_service::controller::event::Loopback>, +>; #[embassy_executor::task(pool_size = 3)] async fn bridge_task( @@ -78,23 +88,24 @@ async fn bridge_task( } #[embassy_executor::task(pool_size = 3)] -async fn controller_task( - mut event_receiver: ArrayPortEventReceivers<'static, 1, mock_controller::InterruptReceiver<'static>>, - wrapper: &'static mock_controller::Wrapper<'static>, -) { +async fn port_task(mut event_receiver: PortEventReceiverType, port: &'static PortType) { loop { let event = event_receiver.wait_event().await; - - let output = wrapper - .process_event(&mut event_receiver.sink_ready_timeout, event) - .await; + let output = port.lock().await.process_event(event).await; if let Err(e) = output { error!("Error processing event: {e:?}"); } - let output = output.unwrap(); - if let Err(e) = wrapper.finalize(&mut event_receiver.power_proxies, output).await { - error!("Error finalizing output: {e:#?}"); - } + } +} + +#[embassy_executor::task(pool_size = 3)] +async fn interrupt_splitter_task( + mut interrupt_receiver: InterruptReceiver<'static>, + mut interrupt_splitter: PortEventSplitter<1, DynamicSender<'static, PortEventBitfield>>, +) -> ! { + loop { + let interrupts = interrupt_receiver.wait_interrupt().await; + interrupt_splitter.process_interrupts(interrupts).await; } } @@ -106,152 +117,110 @@ async fn task(spawner: Spawner) { static CONTROLLER_CONTEXT: StaticCell = StaticCell::new(); let controller_context = CONTROLLER_CONTEXT.init(type_c_interface::service::context::Context::new()); - static POLICY_CHANNEL0: StaticCell> = StaticCell::new(); - let policy_channel0 = POLICY_CHANNEL0.init(Channel::new()); - 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, - [PortRegistration { - id: PORT0_ID, - sender: PORT0_CHANNEL.dyn_sender(), - receiver: PORT0_CHANNEL.dyn_receiver(), - }], - )); - static INTERMEDIATE0: StaticCell< - IntermediateStorage<1, GlobalRawMutex, DynamicSender<'static, psu::event::EventData>>, - > = StaticCell::new(); - let (intermediate0, power_event_receivers0) = storage0 - .try_create_intermediate([("Pd0", policy_sender0)]) - .expect("Failed to create intermediate storage"); - let intermediate0 = INTERMEDIATE0.init(intermediate0); - - static REFERENCED0: StaticCell>> = - StaticCell::new(); - let referenced0 = REFERENCED0.init( - intermediate0 - .try_create_referenced() - .expect("Failed to create referenced storage"), - ); - static STATE0: StaticCell = StaticCell::new(); let state0 = STATE0.init(mock_controller::ControllerState::new()); - let event_receiver0 = ArrayPortEventReceivers::new( - state0.create_interrupt_receiver(), - power_event_receivers0, - ); - static CONTROLLER0: StaticCell> = StaticCell::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::new( - controller0, - Default::default(), - referenced0, - )); - let bridge_receiver0 = BridgeEventReceiver::new(&referenced0.pd_controller); - let bridge0 = Bridge::new(controller0, &referenced0.pd_controller); - static POLICY_CHANNEL1: StaticCell> = StaticCell::new(); - let policy_channel1 = POLICY_CHANNEL1.init(Channel::new()); - 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( + static PORT_CHANNEL0: Channel = Channel::new(); + static PORT_REGISTRATION0: StaticCell<[PortRegistration; 1]> = StaticCell::new(); + let port_registration0 = PORT_REGISTRATION0.init([PortRegistration { + id: PORT0_ID, + sender: PORT_CHANNEL0.dyn_sender(), + receiver: PORT_CHANNEL0.dyn_receiver(), + }]); + + static PD_REGISTRATION0: StaticCell> = StaticCell::new(); + let pd_registration0 = PD_REGISTRATION0.init(Device::new(CONTROLLER0_ID, port_registration0)); + + controller_context.register_controller(pd_registration0).unwrap(); + + define_controller_port_static_cell_channel!(pub(self), port0, GlobalRawMutex, Mutex>); + let PortComponents { + port: port0, + power_policy_receiver: policy_receiver0, + event_receiver: event_receiver0, + interrupt_sender: port0_interrupt_sender, + } = port0::create( + "PD0", + LocalPortId(0), + PORT0_ID, + Default::default(), + controller0, controller_context, - CONTROLLER1_ID, - [PortRegistration { - id: PORT1_ID, - sender: PORT1_CHANNEL.dyn_sender(), - receiver: PORT1_CHANNEL.dyn_receiver(), - }], - )); - static INTERMEDIATE1: StaticCell< - IntermediateStorage<1, GlobalRawMutex, DynamicSender<'static, psu::event::EventData>>, - > = StaticCell::new(); - let (intermediate1, power_event_receivers1) = storage1 - .try_create_intermediate([("Pd1", policy_sender1)]) - .expect("Failed to create intermediate storage"); - let intermediate1 = INTERMEDIATE1.init(intermediate1); - - static REFERENCED1: StaticCell>> = - StaticCell::new(); - let referenced1 = REFERENCED1.init( - intermediate1 - .try_create_referenced() - .expect("Failed to create referenced storage"), ); + let bridge_receiver0 = BridgeEventReceiver::new(pd_registration0); + let bridge0 = Bridge::new(controller0, pd_registration0); static STATE1: StaticCell = StaticCell::new(); let state1 = STATE1.init(mock_controller::ControllerState::new()); - let event_receiver1 = ArrayPortEventReceivers::new( - state1.create_interrupt_receiver(), - power_event_receivers1, - ); - static CONTROLLER1: StaticCell> = StaticCell::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::new( - controller1, - Default::default(), - referenced1, - )); - let bridge_receiver1 = BridgeEventReceiver::new(&referenced1.pd_controller); - let bridge1 = Bridge::new(controller1, &referenced1.pd_controller); - - static POLICY_CHANNEL2: StaticCell> = StaticCell::new(); - let policy_channel2 = POLICY_CHANNEL2.init(Channel::new()); - 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( + static PORT1_CHANNEL: Channel = Channel::new(); + static PORT_REGISTRATION1: StaticCell<[PortRegistration; 1]> = StaticCell::new(); + let port_registration1 = PORT_REGISTRATION1.init([PortRegistration { + id: PORT1_ID, + sender: PORT1_CHANNEL.dyn_sender(), + receiver: PORT1_CHANNEL.dyn_receiver(), + }]); + + static PD_REGISTRATION1: StaticCell> = StaticCell::new(); + let pd_registration1 = PD_REGISTRATION1.init(Device::new(CONTROLLER1_ID, port_registration1)); + + controller_context.register_controller(pd_registration1).unwrap(); + + define_controller_port_static_cell_channel!(pub(self), port1, GlobalRawMutex, Mutex>); + let PortComponents { + port: port1, + power_policy_receiver: policy_receiver1, + event_receiver: event_receiver1, + interrupt_sender: port1_interrupt_sender, + } = port1::create( + "PD1", + LocalPortId(0), + PORT1_ID, + Default::default(), + controller1, controller_context, - CONTROLLER2_ID, - [PortRegistration { - id: PORT2_ID, - sender: PORT2_CHANNEL.dyn_sender(), - receiver: PORT2_CHANNEL.dyn_receiver(), - }], - )); - static INTERMEDIATE2: StaticCell< - IntermediateStorage<1, GlobalRawMutex, DynamicSender<'static, psu::event::EventData>>, - > = StaticCell::new(); - let (intermediate2, power_event_receivers2) = storage2 - .try_create_intermediate([("Pd2", policy_sender2)]) - .expect("Failed to create intermediate storage"); - let intermediate2 = INTERMEDIATE2.init(intermediate2); - - static REFERENCED2: StaticCell>> = - StaticCell::new(); - let referenced2 = REFERENCED2.init( - intermediate2 - .try_create_referenced() - .expect("Failed to create referenced storage"), ); + let bridge_receiver1 = BridgeEventReceiver::new(pd_registration1); + let bridge1 = Bridge::new(controller1, pd_registration1); static STATE2: StaticCell = StaticCell::new(); let state2 = STATE2.init(mock_controller::ControllerState::new()); - let event_receiver2 = ArrayPortEventReceivers::new( - state2.create_interrupt_receiver(), - power_event_receivers2, - ); - static CONTROLLER2: StaticCell> = StaticCell::new(); + static CONTROLLER2: StaticCell = StaticCell::new(); let controller2 = CONTROLLER2.init(Mutex::new(mock_controller::Controller::new(state2))); - static WRAPPER2: StaticCell = StaticCell::new(); - let wrapper2 = WRAPPER2.init(mock_controller::Wrapper::new( - controller2, + + static PORT2_CHANNEL: Channel = Channel::new(); + static PORT_REGISTRATION2: StaticCell<[PortRegistration; 1]> = StaticCell::new(); + let port_registration2 = PORT_REGISTRATION2.init([PortRegistration { + id: PORT2_ID, + sender: PORT2_CHANNEL.dyn_sender(), + receiver: PORT2_CHANNEL.dyn_receiver(), + }]); + + static PD_REGISTRATION2: StaticCell> = StaticCell::new(); + let pd_registration2 = PD_REGISTRATION2.init(Device::new(CONTROLLER2_ID, port_registration2)); + + controller_context.register_controller(pd_registration2).unwrap(); + + define_controller_port_static_cell_channel!(pub(self), port2, GlobalRawMutex, Mutex>); + let PortComponents { + port: port2, + power_policy_receiver: policy_receiver2, + event_receiver: event_receiver2, + interrupt_sender: port2_interrupt_sender, + } = port2::create( + "PD2", + LocalPortId(0), + PORT2_ID, Default::default(), - referenced2, - )); - let bridge_receiver2 = BridgeEventReceiver::new(&referenced2.pd_controller); - let bridge2 = Bridge::new(controller2, &referenced2.pd_controller); + controller2, + controller_context, + ); + let bridge_receiver2 = BridgeEventReceiver::new(pd_registration2); + let bridge2 = Bridge::new(controller2, pd_registration2); // The service is the only receiver and we only use a DynImmediatePublisher, which doesn't take a publisher slot static POWER_POLICY_CHANNEL: StaticCell< @@ -265,11 +234,7 @@ async fn task(spawner: Spawner) { let power_policy_subscriber = power_policy_channel.dyn_subscriber().unwrap(); let power_policy_registration = ArrayRegistration { - psus: [ - &wrapper0.ports[0].proxy, - &wrapper1.ports[0].proxy, - &wrapper2.ports[0].proxy, - ], + psus: [port0, port1, port2], service_senders: [power_policy_sender], chargers: [], }; @@ -287,11 +252,7 @@ async fn task(spawner: Spawner) { spawner.spawn( power_policy_task( PsuEventReceivers::new( - [ - &wrapper0.ports[0].proxy, - &wrapper1.ports[0].proxy, - &wrapper2.ports[0].proxy, - ], + [port0, port1, port2], [policy_receiver0, policy_receiver1, policy_receiver2], ), power_service, @@ -301,8 +262,7 @@ async fn task(spawner: Spawner) { spawner.spawn( type_c_service_task( type_c_service, - EventReceiver::new(controller_context, power_policy_subscriber), - [wrapper0, wrapper1, wrapper2], + ServiceEventReceiver::new(controller_context, power_policy_subscriber), ) .expect("Failed to create type-c service task"), ); @@ -310,9 +270,30 @@ async fn task(spawner: Spawner) { spawner.spawn(bridge_task(bridge_receiver0, bridge0).expect("Failed to create bridge0 task")); spawner.spawn(bridge_task(bridge_receiver1, bridge1).expect("Failed to create bridge1 task")); spawner.spawn(bridge_task(bridge_receiver2, bridge2).expect("Failed to create bridge2 task")); - spawner.spawn(controller_task(event_receiver0, wrapper0).expect("Failed to create controller0 task")); - spawner.spawn(controller_task(event_receiver1, wrapper1).expect("Failed to create controller1 task")); - spawner.spawn(controller_task(event_receiver2, wrapper2).expect("Failed to create controller2 task")); + spawner.spawn(port_task(event_receiver0, port0).expect("Failed to create controller0 task")); + spawner.spawn( + interrupt_splitter_task( + state0.create_interrupt_receiver(), + PortEventSplitter::new([port0_interrupt_sender]), + ) + .expect("Failed to create interrupt splitter 0 task"), + ); + spawner.spawn(port_task(event_receiver1, port1).expect("Failed to create controller1 task")); + spawner.spawn( + interrupt_splitter_task( + state1.create_interrupt_receiver(), + PortEventSplitter::new([port1_interrupt_sender]), + ) + .expect("Failed to create interrupt splitter 1 task"), + ); + spawner.spawn(port_task(event_receiver2, port2).expect("Failed to create controller2 task")); + spawner.spawn( + interrupt_splitter_task( + state2.create_interrupt_receiver(), + PortEventSplitter::new([port2_interrupt_sender]), + ) + .expect("Failed to create interrupt splitter 2 task"), + ); const CAPABILITY: PowerCapability = PowerCapability { voltage_mv: 20000, @@ -365,7 +346,7 @@ async fn task(spawner: Spawner) { #[embassy_executor::task] async fn power_policy_task( - psu_events: PsuEventReceivers<'static, 3, DeviceType, DynamicReceiver<'static, psu::event::EventData>>, + psu_events: PsuEventReceivers<'static, 3, PortType, DynamicReceiver<'static, psu::event::EventData>>, power_policy: &'static PowerPolicyServiceType, ) { power_policy_service::service::task::psu_task(psu_events, power_policy).await; @@ -374,11 +355,10 @@ async fn power_policy_task( #[embassy_executor::task] async fn type_c_service_task( service: &'static Mutex, - event_receiver: EventReceiver<'static, PowerPolicyReceiverType>, - wrappers: [&'static Wrapper<'static>; NUM_PD_CONTROLLERS], + event_receiver: ServiceEventReceiver<'static, PowerPolicyReceiverType>, ) { info!("Starting type-c task"); - type_c_service::task::task(service, event_receiver, wrappers).await; + type_c_service::task::task(service, event_receiver).await; } fn main() { diff --git a/examples/std/src/lib/type_c/mock_controller.rs b/examples/std/src/lib/type_c/mock_controller.rs index 28c6a6556..9ba50dad1 100644 --- a/examples/std/src/lib/type_c/mock_controller.rs +++ b/examples/std/src/lib/type_c/mock_controller.rs @@ -14,6 +14,7 @@ use type_c_interface::port::{ AttnVdm, ControllerStatus, DpConfig, DpPinConfig, DpStatus, OtherVdm, PdStateMachineConfig, PortStatus, RetimerFwUpdateState, SendVdm, TbtConfig, TypeCStateMachineState, UsbControlConfig, event::PortEventBitfield, }; +use type_c_service::controller::state::SharedState; use type_c_service::util::power_capability_from_current; pub struct ControllerState { @@ -119,7 +120,7 @@ pub struct InterruptReceiver<'a> { events: &'a Signal, } -impl type_c_service::wrapper::event_receiver::InterruptReceiver for InterruptReceiver<'_> { +impl type_c_service::controller::event_receiver::InterruptReceiver for InterruptReceiver<'_> { async fn wait_interrupt(&mut self) -> [PortEventBitfield; N] { let events = self.events.wait().await; let mut result = [PortEventBitfield::none(); N]; @@ -321,9 +322,10 @@ impl type_c_interface::port::Controller for Controller<'_> { } } -pub type Wrapper<'a> = type_c_service::wrapper::ControllerWrapper< +pub type Port<'a> = type_c_service::controller::Port< 'a, - GlobalRawMutex, Mutex>, + Mutex, channel::DynamicSender<'a, power_policy_interface::psu::event::EventData>, + channel::DynamicSender<'a, type_c_service::controller::event::Loopback>, >; diff --git a/power-policy-interface/src/psu/event.rs b/power-policy-interface/src/psu/event.rs index 10b9fbdfe..aff48ef3d 100644 --- a/power-policy-interface/src/psu/event.rs +++ b/power-policy-interface/src/psu/event.rs @@ -34,28 +34,3 @@ where /// Event data pub event: EventData, } - -/// Data for a power policy response -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum ResponseData { - /// The request was completed successfully - Complete, -} - -impl ResponseData { - /// Returns an InvalidResponse error if the response is not complete - pub fn complete_or_err(self) -> Result<(), super::Error> { - match self { - ResponseData::Complete => Ok(()), - } - } -} - -/// Response from the power policy service -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Response { - /// Response data - pub data: ResponseData, -} diff --git a/power-policy-interface/src/psu/mod.rs b/power-policy-interface/src/psu/mod.rs index bae5ced52..987f70174 100644 --- a/power-policy-interface/src/psu/mod.rs +++ b/power-policy-interface/src/psu/mod.rs @@ -125,12 +125,16 @@ impl State { pub fn disconnect(&mut self, clear_caps: bool) -> Result<(), Error> { let result = if matches!( self.psu_state, - PsuState::ConnectedConsumer(_) | PsuState::ConnectedProvider(_) + PsuState::ConnectedConsumer(_) | PsuState::ConnectedProvider(_) | PsuState::Idle ) { Ok(()) } else { Err(Error::InvalidState( - &[StateKind::ConnectedConsumer, StateKind::ConnectedProvider], + &[ + StateKind::ConnectedConsumer, + StateKind::ConnectedProvider, + StateKind::Idle, + ], self.psu_state.kind(), )) }; @@ -235,52 +239,6 @@ impl State { } } -/// Data for a device request -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum CommandData { - /// Start consuming on this device - ConnectAsConsumer(ConsumerPowerCapability), - /// Start providing power to port partner on this device - ConnectAsProvider(ProviderPowerCapability), - /// Stop providing or consuming on this device - Disconnect, -} - -/// Request from power policy service to a device -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Command { - /// Request data - pub data: CommandData, -} - -/// Data for a device response -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum ResponseData { - /// The request was successful - Complete, -} - -impl ResponseData { - /// Returns an InvalidResponse error if the response is not complete - pub fn complete_or_err(self) -> Result<(), Error> { - match self { - ResponseData::Complete => Ok(()), - } - } -} - -/// Wrapper type to make code cleaner -pub type InternalResponseData = Result; - -/// Response from a device to the power policy service -pub struct Response { - /// Response data - pub data: ResponseData, -} - /// Trait for PSU devices pub trait Psu: Named { /// Disconnect power from this device diff --git a/power-policy-service/src/service/consumer.rs b/power-policy-service/src/service/consumer.rs index fa84aaa33..77fffd30f 100644 --- a/power-policy-service/src/service/consumer.rs +++ b/power-policy-service/src/service/consumer.rs @@ -196,12 +196,7 @@ impl<'device, Reg: Registration<'device>> Service<'device, Reg> { if matches!(current_psu.state().psu_state, PsuState::ConnectedConsumer(_)) { // Disconnect the current consumer if needed info!("({}): Disconnecting current consumer", current_psu.name()); - // disconnect current consumer and set idle current_psu.disconnect().await?; - if let Err(e) = current_psu.state_mut().disconnect(false) { - // This should never happen because we check the state above, log an error instead of a panic - error!("({}): Disconnect transition failed: {:#?}", current_psu.name(), e); - } } // If no chargers are registered, they won't receive the new power capability. @@ -228,8 +223,6 @@ impl<'device, Reg: Registration<'device>> Service<'device, Reg> { e } else { psu.connect_consumer(new_consumer.consumer_power_capability).await?; - psu.state_mut() - .connect_consumer(new_consumer.consumer_power_capability)?; self.post_consumer_connected(new_consumer).await } } diff --git a/power-policy-service/src/service/mod.rs b/power-policy-service/src/service/mod.rs index 822ccfc8d..701808347 100644 --- a/power-policy-service/src/service/mod.rs +++ b/power-policy-service/src/service/mod.rs @@ -8,8 +8,7 @@ pub mod registration; pub mod task; use embedded_services::named::Named; -use embedded_services::trace; -use embedded_services::{error, event::Sender, info, sync::Lockable}; +use embedded_services::{event::Sender, info, sync::Lockable, trace}; use power_policy_interface::charger::{Charger, PsuState}; use power_policy_interface::{ @@ -92,20 +91,11 @@ impl<'device, Reg: Registration<'device>> Service<'device, Reg> { } async fn process_notify_attach(&self, device: &'device Reg::Psu) { - let mut device = device.lock().await; - info!("({}): Received notify attached", device.name()); - if let Err(e) = device.state_mut().attach() { - error!("({}): Invalid state for attach: {:#?}", device.name(), e); - } + info!("({}): Received notify attached", device.lock().await.name()); } async fn process_notify_detach(&mut self, device: &'device Reg::Psu) -> Result<(), Error> { - { - let mut device = device.lock().await; - info!("({}): Received notify detached", device.name()); - device.state_mut().detach(); - } - + info!("({}): Received notify detached", device.lock().await.name()); self.post_provider_removed(device).await; self.update_current_consumer().await?; Ok(()) @@ -116,21 +106,11 @@ impl<'device, Reg: Registration<'device>> Service<'device, Reg> { device: &'device Reg::Psu, capability: Option, ) -> Result<(), Error> { - { - let mut device = device.lock().await; - info!( - "({}): Received notify consumer capability: {:#?}", - device.name(), - capability, - ); - if let Err(e) = device.state_mut().update_consumer_power_capability(capability) { - error!( - "({}): Invalid state for notify consumer capability, catching up: {:#?}", - device.name(), - e, - ); - } - } + info!( + "({}): Received notify consumer capability: {:#?}", + device.lock().await.name(), + capability, + ); self.update_current_consumer().await } @@ -140,42 +120,17 @@ impl<'device, Reg: Registration<'device>> Service<'device, Reg> { requester: &'device Reg::Psu, capability: Option, ) -> Result<(), Error> { - { - let mut requester = requester.lock().await; - info!( - "({}): Received request provider capability: {:#?}", - requester.name(), - capability, - ); - if let Err(e) = requester - .state_mut() - .update_requested_provider_power_capability(capability) - { - error!( - "({}): Invalid state for notify provider capability, catching up: {:#?}", - requester.name(), - e, - ); - } - } + info!( + "({}): Received request provider capability: {:#?}", + requester.lock().await.name(), + capability, + ); self.connect_provider(requester).await } async fn process_notify_disconnect(&mut self, device: &'device Reg::Psu) -> Result<(), Error> { - { - let mut locked_device = device.lock().await; - info!("({}): Received notify disconnect", locked_device.name()); - - if let Err(e) = locked_device.state_mut().disconnect(true) { - error!( - "({}): Invalid state for notify disconnect, catching up: {:#?}", - locked_device.name(), - e, - ); - } - } - + info!("({}): Received notify disconnect", device.lock().await.name()); self.post_provider_removed(device).await; self.update_current_consumer().await?; Ok(()) diff --git a/power-policy-service/src/service/provider.rs b/power-policy-service/src/service/provider.rs index 02029e61e..e41c15d6a 100644 --- a/power-policy-service/src/service/provider.rs +++ b/power-policy-service/src/service/provider.rs @@ -6,6 +6,7 @@ use core::ptr; use embedded_services::debug; +use embedded_services::error; use embedded_services::named::Named; use super::*; @@ -95,7 +96,6 @@ impl<'device, Reg: Registration<'device>> Service<'device, Reg> { e } else { locked_requester.connect_provider(target_power).await?; - locked_requester.state_mut().connect_provider(target_power)?; self.post_provider_connected(requester, target_power).await; Ok(()) } diff --git a/power-policy-service/tests/common/mock.rs b/power-policy-service/tests/common/mock.rs index 77b126e87..0b97d0f0c 100644 --- a/power-policy-service/tests/common/mock.rs +++ b/power-policy-service/tests/common/mock.rs @@ -46,29 +46,37 @@ impl<'a, S: Sender> Mock<'a, S> { } pub async fn simulate_consumer_connection(&mut self, capability: ConsumerPowerCapability) { + self.state.attach().unwrap(); self.sender.send(EventData::Attached).await; + self.state.update_consumer_power_capability(Some(capability)).unwrap(); self.sender .send(EventData::UpdatedConsumerCapability(Some(capability))) .await; } pub async fn simulate_detach(&mut self) { + self.state.detach(); self.sender.send(EventData::Detached).await; } pub async fn simulate_provider_connection(&mut self, capability: PowerCapability) { + self.state.attach().unwrap(); self.sender.send(EventData::Attached).await; let capability = Some(ProviderPowerCapability { capability, flags: ProviderFlags::none(), }); + self.state + .update_requested_provider_power_capability(capability) + .unwrap(); self.sender .send(EventData::RequestedProviderCapability(capability)) .await; } pub async fn simulate_disconnect(&mut self) { + self.state.disconnect(true).unwrap(); self.sender.send(EventData::Disconnected).await; } @@ -76,6 +84,9 @@ impl<'a, S: Sender> Mock<'a, S> { &mut self, capability: Option, ) { + self.state + .update_requested_provider_power_capability(capability) + .unwrap(); self.sender .send(power_policy_interface::psu::event::EventData::RequestedProviderCapability(capability)) .await @@ -86,19 +97,19 @@ impl<'a, S: Sender> Psu for Mock<'a, S> { async fn connect_consumer(&mut self, capability: ConsumerPowerCapability) -> Result<(), Error> { info!("Connect consumer {:#?}", capability); self.record_fn_call(FnCall::ConnectConsumer(capability)); - Ok(()) + self.state.connect_consumer(capability) } async fn connect_provider(&mut self, capability: ProviderPowerCapability) -> Result<(), Error> { info!("Connect provider: {:#?}", capability); self.record_fn_call(FnCall::ConnectProvider(capability)); - Ok(()) + self.state.connect_provider(capability) } async fn disconnect(&mut self) -> Result<(), Error> { info!("Disconnect"); self.record_fn_call(FnCall::Disconnect); - Ok(()) + self.state.disconnect(false) } fn state(&self) -> &State { diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index d03ff8e7e..0c001d01d 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -92,6 +92,16 @@ who = "Jerry Xie " criteria = "safe-to-deploy" version = "1.0.0" +[[audits.device-driver]] +who = "Felipe Balbi " +criteria = "safe-to-deploy" +version = "1.0.9" + +[[audits.device-driver]] +who = "Felipe Balbi " +criteria = "safe-to-run" +version = "1.0.9" + [[audits.embassy-embedded-hal]] who = "Billy Price " criteria = "safe-to-deploy" diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 7acb2d508..52d64d200 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -22,250 +22,10 @@ audit-as-crates-io = false [policy.keyberon] audit-as-crates-io = false -[[exemptions.ahash]] -version = "0.8.12" -criteria = "safe-to-deploy" - -[[exemptions.askama]] -version = "0.14.0" -criteria = "safe-to-deploy" - -[[exemptions.askama_derive]] -version = "0.14.0" -criteria = "safe-to-deploy" - -[[exemptions.askama_parser]] -version = "0.14.0" -criteria = "safe-to-deploy" - -[[exemptions.az]] -version = "1.2.1" -criteria = "safe-to-deploy" - -[[exemptions.bare-metal]] -version = "0.2.5" -criteria = "safe-to-deploy" - -[[exemptions.bbq2]] -version = "0.4.2" -criteria = "safe-to-deploy" - -[[exemptions.bincode]] -version = "2.0.1" -criteria = "safe-to-deploy" - -[[exemptions.bincode_derive]] -version = "2.0.1" -criteria = "safe-to-deploy" - -[[exemptions.bitfield]] -version = "0.13.2" -criteria = "safe-to-deploy" - -[[exemptions.bitfield]] -version = "0.15.0" -criteria = "safe-to-deploy" - -[[exemptions.bitfield]] -version = "0.17.0" -criteria = "safe-to-deploy" - -[[exemptions.bitfield]] -version = "0.19.2" -criteria = "safe-to-deploy" - -[[exemptions.bitfield-macros]] -version = "0.19.2" -criteria = "safe-to-deploy" - -[[exemptions.bitfield-struct]] -version = "0.10.1" -criteria = "safe-to-deploy" - -[[exemptions.bitvec]] -version = "1.0.1" -criteria = "safe-to-deploy" - -[[exemptions.bytemuck]] -version = "1.23.2" -criteria = "safe-to-deploy" - -[[exemptions.cfg-if]] -version = "1.0.3" -criteria = "safe-to-deploy" - -[[exemptions.chrono]] -version = "0.4.40" -criteria = "safe-to-deploy" - -[[exemptions.convert_case]] -version = "0.6.0" -criteria = "safe-to-deploy" - -[[exemptions.cordyceps]] -version = "0.3.4" -criteria = "safe-to-deploy" - -[[exemptions.cortex-m]] -version = "0.7.7" -criteria = "safe-to-deploy" - -[[exemptions.cortex-m-rt]] -version = "0.7.5" -criteria = "safe-to-deploy" - -[[exemptions.cortex-m-rt-macros]] -version = "0.7.5" -criteria = "safe-to-deploy" - -[[exemptions.crc]] -version = "3.3.0" -criteria = "safe-to-deploy" - -[[exemptions.crc-catalog]] -version = "2.4.0" -criteria = "safe-to-deploy" - -[[exemptions.critical-section]] -version = "1.2.0" -criteria = "safe-to-deploy" - -[[exemptions.darling]] -version = "0.20.11" -criteria = "safe-to-deploy" - -[[exemptions.darling_core]] -version = "0.20.11" -criteria = "safe-to-deploy" - -[[exemptions.darling_macro]] -version = "0.20.11" -criteria = "safe-to-deploy" - -[[exemptions.dd-manifest-tree]] -version = "1.0.0" -criteria = "safe-to-deploy" - -[[exemptions.defmt]] -version = "0.3.100" -criteria = "safe-to-deploy" - -[[exemptions.defmt]] -version = "1.0.1" -criteria = "safe-to-deploy" - -[[exemptions.defmt-macros]] -version = "1.0.1" -criteria = "safe-to-deploy" - -[[exemptions.defmt-parser]] -version = "1.0.0" -criteria = "safe-to-deploy" - -[[exemptions.device-driver]] -version = "1.0.7" -criteria = "safe-to-deploy" - -[[exemptions.device-driver-generation]] -version = "1.0.7" -criteria = "safe-to-deploy" - -[[exemptions.device-driver-macros]] -version = "1.0.7" -criteria = "safe-to-deploy" - [[exemptions.diff]] version = "0.1.13" criteria = "safe-to-run" -[[exemptions.embassy-embedded-hal]] -version = "0.5.0" -criteria = "safe-to-deploy" - -[[exemptions.embassy-executor]] -version = "0.9.1" -criteria = "safe-to-deploy" - -[[exemptions.embassy-executor-macros]] -version = "0.7.0" -criteria = "safe-to-deploy" - -[[exemptions.embassy-executor-timer-queue]] -version = "0.1.0" -criteria = "safe-to-deploy" - -[[exemptions.embassy-futures]] -version = "0.1.2" -criteria = "safe-to-deploy" - -[[exemptions.embassy-hal-internal]] -version = "0.3.0" -criteria = "safe-to-deploy" - -[[exemptions.embassy-sync]] -version = "0.7.2" -criteria = "safe-to-deploy" - -[[exemptions.embassy-sync]] -version = "0.8.0" -criteria = "safe-to-deploy" - -[[exemptions.embassy-time-driver]] -version = "0.2.1" -criteria = "safe-to-deploy" - -[[exemptions.embassy-time-queue-utils]] -version = "0.3.0" -criteria = "safe-to-deploy" - -[[exemptions.embedded-hal]] -version = "0.2.7" -criteria = "safe-to-deploy" - -[[exemptions.embedded-hal]] -version = "1.0.0" -criteria = "safe-to-deploy" - -[[exemptions.embedded-hal-async]] -version = "1.0.0" -criteria = "safe-to-deploy" - -[[exemptions.embedded-hal-nb]] -version = "1.0.0" -criteria = "safe-to-deploy" - -[[exemptions.embedded-io]] -version = "0.7.1" -criteria = "safe-to-deploy" - -[[exemptions.embedded-io-async]] -version = "0.6.1" -criteria = "safe-to-deploy" - -[[exemptions.embedded-io-async]] -version = "0.7.0" -criteria = "safe-to-deploy" - -[[exemptions.embedded-storage]] -version = "0.3.1" -criteria = "safe-to-deploy" - -[[exemptions.embedded-storage-async]] -version = "0.4.1" -criteria = "safe-to-deploy" - -[[exemptions.fixed]] -version = "1.29.0" -criteria = "safe-to-deploy" - -[[exemptions.funty]] -version = "2.0.0" -criteria = "safe-to-deploy" - -[[exemptions.futures-macro]] -version = "0.3.31" -criteria = "safe-to-run" - [[exemptions.futures-task]] version = "0.3.32" criteria = "safe-to-run" @@ -274,114 +34,18 @@ criteria = "safe-to-run" version = "3.0.3" criteria = "safe-to-run" -[[exemptions.futures-util]] -version = "0.3.31" -criteria = "safe-to-run" - [[exemptions.generator]] version = "0.8.5" criteria = "safe-to-deploy" -[[exemptions.hash32]] -version = "0.3.1" -criteria = "safe-to-deploy" - -[[exemptions.hashbrown]] -version = "0.14.5" -criteria = "safe-to-deploy" - [[exemptions.hashbrown]] version = "0.17.0" criteria = "safe-to-deploy" -[[exemptions.hashlink]] -version = "0.9.1" -criteria = "safe-to-deploy" - -[[exemptions.heapless]] -version = "0.8.0" -criteria = "safe-to-deploy" - -[[exemptions.heapless]] -version = "0.9.2" -criteria = "safe-to-deploy" - -[[exemptions.indexmap]] -version = "2.11.0" -criteria = "safe-to-deploy" - [[exemptions.indexmap]] version = "2.14.0" criteria = "safe-to-deploy" -[[exemptions.io-uring]] -version = "0.7.10" -criteria = "safe-to-run" - -[[exemptions.kdl]] -version = "6.3.4" -criteria = "safe-to-deploy" - -[[exemptions.litrs]] -version = "0.4.2" -criteria = "safe-to-deploy" - -[[exemptions.maitake-sync]] -version = "0.2.2" -criteria = "safe-to-deploy" - -[[exemptions.miette]] -version = "7.6.0" -criteria = "safe-to-deploy" - -[[exemptions.miette-derive]] -version = "7.6.0" -criteria = "safe-to-deploy" - -[[exemptions.mimxrt600-fcb]] -version = "0.2.2" -criteria = "safe-to-deploy" - -[[exemptions.mio]] -version = "1.0.4" -criteria = "safe-to-run" - -[[exemptions.mycelium-bitfield]] -version = "0.1.5" -criteria = "safe-to-deploy" - -[[exemptions.num]] -version = "0.4.3" -criteria = "safe-to-deploy" - -[[exemptions.num-bigint]] -version = "0.4.6" -criteria = "safe-to-deploy" - -[[exemptions.num-complex]] -version = "0.4.6" -criteria = "safe-to-deploy" - -[[exemptions.num-iter]] -version = "0.1.45" -criteria = "safe-to-deploy" - -[[exemptions.once_cell]] -version = "1.21.3" -criteria = "safe-to-deploy" - -[[exemptions.pin-project]] -version = "1.1.10" -criteria = "safe-to-deploy" - -[[exemptions.pin-project-internal]] -version = "1.1.10" -criteria = "safe-to-deploy" - -[[exemptions.pin-utils]] -version = "0.1.0" -criteria = "safe-to-run" - [[exemptions.pretty_assertions]] version = "1.4.1" criteria = "safe-to-run" @@ -390,66 +54,18 @@ criteria = "safe-to-run" version = "3.5.0" criteria = "safe-to-run" -[[exemptions.proc-macro-error-attr2]] -version = "2.0.0" -criteria = "safe-to-deploy" - -[[exemptions.proc-macro-error2]] -version = "2.0.1" -criteria = "safe-to-deploy" - -[[exemptions.radium]] -version = "0.7.0" -criteria = "safe-to-deploy" - [[exemptions.rustc-demangle]] version = "0.1.26" criteria = "safe-to-run" -[[exemptions.rustc_version]] -version = "0.2.3" -criteria = "safe-to-deploy" - -[[exemptions.semver]] -version = "0.9.0" -criteria = "safe-to-deploy" - [[exemptions.semver]] version = "1.0.28" criteria = "safe-to-run" -[[exemptions.semver-parser]] -version = "0.7.0" -criteria = "safe-to-deploy" - -[[exemptions.serde_spanned]] -version = "0.6.9" -criteria = "safe-to-deploy" - -[[exemptions.slab]] -version = "0.4.11" -criteria = "safe-to-run" - -[[exemptions.tokio]] -version = "1.47.1" -criteria = "safe-to-run" - -[[exemptions.toml]] -version = "0.8.23" -criteria = "safe-to-deploy" - -[[exemptions.toml_datetime]] -version = "0.6.11" -criteria = "safe-to-deploy" - [[exemptions.toml_datetime]] version = "1.1.1+spec-1.1.0" criteria = "safe-to-run" -[[exemptions.toml_edit]] -version = "0.22.27" -criteria = "safe-to-deploy" - [[exemptions.toml_edit]] version = "0.25.11+spec-1.1.0" criteria = "safe-to-run" @@ -458,54 +74,10 @@ criteria = "safe-to-run" version = "1.1.2+spec-1.1.0" criteria = "safe-to-run" -[[exemptions.typenum]] -version = "1.18.0" -criteria = "safe-to-deploy" - -[[exemptions.unty]] -version = "0.0.4" -criteria = "safe-to-deploy" - -[[exemptions.vcell]] -version = "0.1.3" -criteria = "safe-to-deploy" - -[[exemptions.version_check]] -version = "0.9.5" -criteria = "safe-to-deploy" - -[[exemptions.virtue]] -version = "0.0.18" -criteria = "safe-to-deploy" - -[[exemptions.volatile-register]] -version = "0.2.2" -criteria = "safe-to-deploy" - -[[exemptions.wasi]] -version = "0.11.1+wasi-snapshot-preview1" -criteria = "safe-to-run" - -[[exemptions.winnow]] -version = "0.6.24" -criteria = "safe-to-deploy" - -[[exemptions.winnow]] -version = "0.7.13" -criteria = "safe-to-deploy" - [[exemptions.winnow]] version = "1.0.2" criteria = "safe-to-run" -[[exemptions.wyz]] -version = "0.5.1" -criteria = "safe-to-deploy" - -[[exemptions.yaml-rust2]] -version = "0.9.0" -criteria = "safe-to-deploy" - [[exemptions.yansi]] version = "1.0.1" criteria = "safe-to-run" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index d185c6ac5..322196ff7 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -21,13 +21,6 @@ when = "2025-08-16" user-id = 55123 user-login = "rust-lang-owner" -[[publisher.encoding_rs]] -version = "0.8.35" -when = "2024-10-24" -user-id = 4484 -user-login = "hsivonen" -user-name = "Henri Sivonen" - [[publisher.libc]] version = "0.2.175" when = "2025-08-11" @@ -76,13 +69,6 @@ user-id = 3618 user-login = "dtolnay" user-name = "David Tolnay" -[[publisher.ryu]] -version = "1.0.20" -when = "2025-03-04" -user-id = 3618 -user-login = "dtolnay" -user-name = "David Tolnay" - [[publisher.scoped-tls]] version = "1.0.1" when = "2022-10-31" @@ -90,13 +76,6 @@ user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" -[[publisher.serde_json]] -version = "1.0.143" -when = "2025-08-19" -user-id = 3618 -user-login = "dtolnay" -user-name = "David Tolnay" - [[publisher.smallvec]] version = "1.15.1" when = "2025-06-06" @@ -118,13 +97,6 @@ user-id = 3618 user-login = "dtolnay" user-name = "David Tolnay" -[[publisher.thiserror]] -version = "1.0.69" -when = "2024-11-10" -user-id = 3618 -user-login = "dtolnay" -user-name = "David Tolnay" - [[publisher.thiserror]] version = "2.0.16" when = "2025-08-20" @@ -132,13 +104,6 @@ user-id = 3618 user-login = "dtolnay" user-name = "David Tolnay" -[[publisher.thiserror-impl]] -version = "1.0.69" -when = "2024-11-10" -user-id = 3618 -user-login = "dtolnay" -user-name = "David Tolnay" - [[publisher.thiserror-impl]] version = "2.0.16" when = "2025-08-20" @@ -146,20 +111,6 @@ user-id = 3618 user-login = "dtolnay" user-name = "David Tolnay" -[[publisher.unicode-segmentation]] -version = "1.12.0" -when = "2024-09-13" -user-id = 1139 -user-login = "Manishearth" -user-name = "Manish Goregaokar" - -[[publisher.unicode-width]] -version = "0.1.14" -when = "2024-09-19" -user-id = 1139 -user-login = "Manishearth" -user-name = "Manish Goregaokar" - [[publisher.windows]] version = "0.61.3" when = "2025-06-12" @@ -328,18 +279,102 @@ delta = "1.4.0 -> 1.5.0" notes = "No unsafe, no build.rs, no network access; delta adds edition-aware rustc probing and best-effort probe-file cleanup only. Assisted-by: copilot-cli:GPT-5.3-Codex cargo-vet" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" +[[audits.OpenDevicePartnership.audits.az]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "1.2.1" +notes = "No unsafe code. no_std library with only safe numeric cast traits. Build script probes for track_caller via rustc in OUT_DIR only. No network, no ambient capabilities. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + [[audits.OpenDevicePartnership.audits.bare-metal]] who = "Felipe Balbi " criteria = "safe-to-deploy" version = "0.2.5" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/mcxa-pac/refs/heads/main/supply-chain/audits.toml" +[[audits.OpenDevicePartnership.audits.bbq2]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.4.2" +notes = "no_std SPSC bip-buffer queue. Non-trivial unsafe for lock-free coordination and pointer arithmetic, all reviewed and sound. No build script, no proc macros, no I/O. Has Miri CI. Assisted-by: copilot-chat:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.bincode]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "2.0.1" +notes = "no_std binary serialization library. ~15 unsafe blocks for u8 type-specialization guarded by unty::type_equal and MaybeUninit patterns. No build script, no proc macros. std imports only for Encode/Decode trait impls. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.bincode_derive]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "2.0.1" +notes = "Proc-macro derive for bincode Encode/Decode. No unsafe, no build script, no I/O. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + [[audits.OpenDevicePartnership.audits.bitfield]] who = "Felipe Balbi " criteria = "safe-to-deploy" version = "0.13.2" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/mcxa-pac/refs/heads/main/supply-chain/audits.toml" +[[audits.OpenDevicePartnership.audits.bitfield]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "0.13.2 -> 0.15.0" +notes = "Delta audit: BitRange/Bit traits split into read-only and mutable variants (BitRangeMut/BitMut); added mask constant generation; clippy fixes; MSRV bump. No unsafe, no build script, no proc macros, no powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.bitfield]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "0.15.0 -> 0.17.0" +notes = "Delta: adds bitwise op derives, constructor derives, arbitrary visibility. Pure declarative macros. No unsafe, no build script. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.bitfield]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "0.15.0 -> 0.19.2" +notes = "Delta: refactored to proc macros in bitfield-macros, added BitAnd/BitOr/BitXor, signed types, bool arrays. No unsafe. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.bitfield-macros]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.19.2" +notes = "Proc-macro generating bitfield getters/setters/masks. No unsafe, no build script, no powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.bitfield-struct]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.10.1" +notes = "Proc-macro crate generating safe bitfield structs. No unsafe, no build script. Standard proc-macro deps only. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.bytemuck]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "1.22.0 -> 1.23.2" +notes = "Delta 1.22.0->1.23.2: new ZeroableInOption impls for function pointer types (sound, uses guaranteed niche optimization), core::error::Error impls behind feature flag, safe derive helper module. No new unsafe blocks, no build script, no I/O. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.cfg-if]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "1.0.0 -> 1.0.3" +notes = "Delta 1.0.0->1.0.3: formatting/readability refactor of macro identifiers, removed compiler_builtins dep, updated CI. No unsafe, no build script, no imports. Pure macro_rules crate. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.cordyceps]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.3.4" +notes = "Intrusive data structures crate (no_std). ~115 unsafe blocks, all necessary for intrusive linked list/queue/stack ops. Correct patterns: addr_of_mut, proper atomic orderings, Vyukov MPSC algorithm. No build script, no proc macros, no powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + [[audits.OpenDevicePartnership.audits.cortex-m]] who = "Felipe Balbi " criteria = "safe-to-deploy" @@ -358,6 +393,20 @@ criteria = "safe-to-deploy" version = "0.7.5" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/mcxa-pac/refs/heads/main/supply-chain/audits.toml" +[[audits.OpenDevicePartnership.audits.crc]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "3.3.0" +notes = "No unsafe (forbid(unsafe_code)), no build script, no I/O, no_std pure CRC computation. Assisted-by: copilot-chat:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.crc-catalog]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "2.4.0" +notes = "Pure no_std data-only crate. No unsafe, no build script, no dependencies, no I/O. Contains only const CRC algorithm parameter structs. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + [[audits.OpenDevicePartnership.audits.critical-section]] who = "Felipe Balbi " criteria = "safe-to-deploy" @@ -371,6 +420,13 @@ delta = "0.2.3 -> 0.2.4" notes = "Tiny diff to use newer core/std features via build.rs env var for path separator; no safety impact. Assisted-by: copilot-cli:GPT-5.3-Codex cargo-vet" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" +[[audits.OpenDevicePartnership.audits.defmt]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.3.100" +notes = "Compatibility shim: no_std crate that re-exports defmt 1.x items for 0.3 API compatibility. No unsafe code, no build script, no powerful imports, no logic - pure pub-use re-exports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + [[audits.OpenDevicePartnership.audits.defmt]] who = "Felipe Balbi " criteria = "safe-to-deploy" @@ -389,18 +445,53 @@ criteria = "safe-to-deploy" version = "1.0.0" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/mcxa-pac/refs/heads/main/supply-chain/audits.toml" +[[audits.OpenDevicePartnership.audits.embassy-embedded-hal]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.5.0" +notes = "No unsafe, no build script, no proc macros. no_std shared bus/flash partition utilities for embedded-hal traits. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + [[audits.OpenDevicePartnership.audits.embassy-executor-timer-queue]] who = "Felipe Balbi " criteria = "safe-to-deploy" version = "0.1.0" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embassy-imxrt/refs/heads/main/supply-chain/audits.toml" +[[audits.OpenDevicePartnership.audits.embassy-futures]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.1.2" +notes = "no_std future combinators. All unsafe is pin-projection and no-op RawWaker - reviewed and sound. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.embassy-hal-internal]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.3.0" +notes = "no_std HAL internals. Unsafe in atomic ring buffer (sound SPSC), peripheral singletons, cortex-m interrupt priority. Build script emits cfg flags only. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.embassy-sync]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.8.0" +notes = "no_std async sync primitives. Substantial unsafe for UnsafeCell-based interiors and Send/Sync impls -- all reviewed and sound, guarded by RawMutex/critical_section. Build script only reads TARGET env var. No proc macros, no powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + [[audits.OpenDevicePartnership.audits.embassy-time]] who = "Felipe Balbi " criteria = "safe-to-deploy" version = "0.5.0" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/tps6699x/refs/heads/main/supply-chain/audits.toml" +[[audits.OpenDevicePartnership.audits.embassy-time-driver]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.2.1" +notes = "no_std driver trait for embassy-time. Minimal unsafe for extern Rust FFI calls (sound via links key). Empty build.rs. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + [[audits.OpenDevicePartnership.audits.embassy-time-queue-utils]] who = "Felipe Balbi " criteria = "safe-to-deploy" @@ -413,6 +504,159 @@ criteria = "safe-to-deploy" version = "0.2.7" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/mcxa-pac/refs/heads/main/supply-chain/audits.toml" +[[audits.OpenDevicePartnership.audits.embedded-hal]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "0.2.7 -> 1.0.0" +notes = "Pure no_std trait crate. Complete API redesign for 1.0: removed nb-based traits, CAN module, all unsafe code. Only defines traits/enums/types for digital, I2C, SPI, PWM, delay. No build script, no proc macros, no powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.embedded-hal-async]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "1.0.0" +notes = "no_std async HAL trait definitions. No unsafe in library. Build script only runs rustc --version. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.embedded-hal-nb]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "1.0.0" +notes = "no_std trait-only crate. No unsafe, no build script, no proc macros, no powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.embedded-io]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "0.6.1 -> 0.7.1" +notes = "Add core::error::Error trait bound (MSRV 1.81). defmt 0.3->1.0. Implement ReadReady/WriteReady for slices and VecDeque. Add seek_relative(). Fix method forwardings. Trusted publisher (Dirbaio from Embedded WG)." +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embassy-imxrt/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.embedded-io-async]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.6.1" +notes = "No unsafe. Build script only detects nightly via rustc --version. Pure async trait definitions for embedded I/O. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.embedded-io-async]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "0.6.1 -> 0.7.0" +notes = "Delta 0.6.1->0.7.0: No unsafe. Build script removed (AFIT now stable). flush() made required, BufRead requires Read, new VecDeque impls. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.embedded-storage]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.3.1" +notes = "Pure no_std storage abstraction traits. deny(unsafe_code), no build script, no dependencies, no powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.embedded-storage-async]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.4.1" +notes = "Pure no_std async trait definitions for NOR flash storage. No unsafe code, no build script, no powerful imports. Only dependency is embedded-storage. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.fixed]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "1.29.0" +notes = "no_std fixed-point number library. Unsafe limited to: bytemuck Pod/Zeroable impls on repr(transparent) types, NonZero::new_unchecked after proven-nonzero guards, unreachable_unchecked in exhaustive remainder logic. Build script probes compiler features in OUT_DIR. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.hash32]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.3.1" +notes = "no_std 32-bit hashing (FNV, MurmurHash3). ~10 unsafe blocks in murmur3.rs for MaybeUninit buffer handling - all sound. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.heapless]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.8.0" +notes = "no_std fixed-capacity data structures. Extensive unsafe for MaybeUninit buffer management, lock-free queues (Vyukov MPMC, SPSC), and Treiber stack memory pools with ABA prevention. Patterns mirror std or published algorithms. Build script probes for atomic/LLSC support. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.heapless]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.9.2" +notes = "no_std fixed-capacity data structures. Extensive unsafe for MaybeUninit buffers, lock-free queues (Vyukov MPMC, SPSC), Treiber stack pools with ABA prevention (CAS tagged pointers + ARM LLSC). All Send/Sync bounds verified correct. Build script probes for ARM LLSC. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.io-uring]] +who = "Jerry Xie " +criteria = "safe-to-run" +delta = "0.5.13 -> 0.7.10" +notes = "Delta audit. Linux io_uring bindings. +15 hand-written unsafe (new from_fd, buffer/file registration APIs). SeqCst fence fix improves atomics correctness. Probe refactored to stack allocation. Build script adds cfg checks only. All unsafe for expected syscall/mmap/fd operations. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.litrs]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "0.4.1 -> 0.4.2" +notes = "Delta 0.4.1->0.4.2: Bug fixes for non-ASCII byte string escapes, removes CR LF normalization to align with spec, fixes error span for out-of-range Unicode escapes. No unsafe code, no build script, no powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.maitake-sync]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.2.2" +notes = "No-std async sync primitives. Extensive unsafe for Send/Sync impls, UnsafeCell access under locks/atomics, intrusive linked list nodes, spinlocks -- all follow standard patterns. Uses unreachable_unchecked! macro (panics in debug). No build script, no proc macros. Loom-tested. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.mimxrt600-fcb]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.2.1" +notes = "Pure no_std data-definition crate for MIMXRT600 flash config blocks. No unsafe, no build script. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.mimxrt600-fcb]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "0.2.1 -> 0.2.2" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embassy-imxrt/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.mio]] +who = "Jerry Xie " +criteria = "safe-to-run" +delta = "1.0.1 -> 1.0.4" +notes = "Delta 1.0.1->1.0.4: I/O safety trait impls, AIX poll(2) support, windows-sys 0.59 pointer fixes. Unsafe Send/Sync for CompletionPort/Inner sound. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.mycelium-bitfield]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.1.5" +notes = "Pure safe no_std bitfield macro crate. No unsafe code, no build script, no proc macros, no dependencies, no powerful imports. Only core:: types used. Assisted-by: copilot-chat:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.once_cell]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "1.20.1" +notes = "Single-assignment cells and lazy values. All unsafe reviewed: UnsafeCell access, Send/Sync impls, atomic waiter queue, strict provenance polyfill - all sound with correct bounds. No build script, no proc macros, no powerful imports beyond std::thread/atomic. Assisted-by: copilot-chat:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.pin-project]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "1.1.10" +notes = "no_std pin-projection helper. Re-exports proc macros from pin-project-internal. Minimal unsafe in __private module (drop guards, UnsafeUnpin forwarding) -- all sound with SAFETY comments. No build script, no powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.pin-project-internal]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "1.1.10" +notes = "Proc-macro for pin projection. forbid(unsafe_code) in macro itself. Generated unsafe is sound pin projection (Pin::new_unchecked, get_unchecked_mut) with compile-time safety enforced via trait tricks. No build script, no I/O. Deps: proc-macro2, quote, syn only. Assisted-by: copilot-chat:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + [[audits.OpenDevicePartnership.audits.proc-macro-error-attr2]] who = "Felipe Balbi " criteria = "safe-to-deploy" @@ -443,11 +687,18 @@ criteria = "safe-to-deploy" version = "0.7.0" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/mcxa-pac/refs/heads/main/supply-chain/audits.toml" -[[audits.OpenDevicePartnership.audits.tap]] +[[audits.OpenDevicePartnership.audits.serde_spanned]] who = "Jerry Xie " criteria = "safe-to-deploy" -version = "1.0.1" -notes = "No unsafe, no build.rs, no ambient I/O/process/network capabilities; behavior matches no_std tap/pipe/conv utility traits. Assisted-by: copilot-cli:GPT-5.3-Codex cargo-vet" +delta = "0.6.8 -> 0.6.9" +notes = "Trivial delta: metadata, lint config, and doc formatting only. No functional code changes, no unsafe, no build script, no I/O. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.slab]] +who = "Jerry Xie " +criteria = "safe-to-run" +delta = "0.4.8 -> 0.4.11" +notes = "Delta 0.4.8->0.4.11: new get_disjoint_mut uses unsafe (MaybeUninit + raw ptrs) with sound bounds/overlap checks. build.rs removed. No powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" [[audits.OpenDevicePartnership.audits.thread_local]] @@ -457,6 +708,48 @@ delta = "1.1.4 -> 1.1.9" notes = "No build script, no FS/net/process capability expansion; unsafe refactor to lock-free insertion and nightly TLS path appears sound on review. Assisted-by: copilot-cli:GPT-5.3-Codex cargo-vet" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" +[[audits.OpenDevicePartnership.audits.tokio]] +who = "Jerry Xie " +criteria = "safe-to-run" +delta = "1.45.0 -> 1.47.1" +notes = "Delta audit. New SetOnce sync primitive, OwnedNotified, spawn location tracking (tokio_unstable), experimental io_uring behind cfg gate, block_in_place hardening. All new unsafe follows existing patterns with safety comments. No build script, no proc macros. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.toml]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "0.8.22 -> 0.8.23" +notes = "Delta: adds TupleVariant/StructVariant serialization support. All new code is thin wrappers delegating to toml_edit. No unsafe (forbid(unsafe_code)), no build script, no powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.toml_datetime]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "0.6.9 -> 0.6.11" +notes = "Delta 0.6.9->0.6.11: parser refactored from char-by-char to lexer-based tokenizer with improved error messages; no unsafe (forbid(unsafe_code)), no build script, no powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.toml_edit]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "0.22.26 -> 0.22.27" +notes = "Delta: no changes to unsafe code (all pre-existing from_utf8_unchecked on ASCII-validated buffers). Visibility reductions on parser internals, serializer refactoring, new consuming accessors. No build script, no proc macros, no powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.typenum]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "1.18.0" +notes = "Pure no_std type-level numbers crate. forbid(unsafe_code) -- zero unsafe anywhere. Build script only writes generated test code to OUT_DIR. No proc macros, no FFI, no network/filesystem/process access in library. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.unty]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.0.4" +notes = "Tiny no_std crate (1 file, ~120 LOC, zero deps). Two unsafe blocks: transmute_copy guarded by TypeId check in unty(), and a dtolnay-pattern transmute in non_static_type_id(). Both documented; no build script, no powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + [[audits.OpenDevicePartnership.audits.valuable]] who = "Jerry Xie " criteria = "safe-to-deploy" @@ -470,18 +763,46 @@ criteria = "safe-to-deploy" version = "0.1.3" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/mcxa-pac/refs/heads/main/supply-chain/audits.toml" +[[audits.OpenDevicePartnership.audits.version_check]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "0.9.4 -> 0.9.5" +notes = "Delta 0.9.4->0.9.5: documentation-only changes (added feature detection guidance, doc cross-references) and Cargo.toml normalization. No code changes, no unsafe, no new imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + +[[audits.OpenDevicePartnership.audits.virtue]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +version = "0.0.18" +notes = "Proc-macro derive helper library. No unsafe code, no build script. Uses std::fs/std::env only in opt-in export_to_file() debug helper scoped to target/ dir. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + [[audits.OpenDevicePartnership.audits.volatile-register]] who = "Felipe Balbi " criteria = "safe-to-deploy" version = "0.2.2" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/mcxa-pac/refs/heads/main/supply-chain/audits.toml" +[[audits.OpenDevicePartnership.audits.wasi]] +who = "Jerry Xie " +criteria = "safe-to-run" +version = "0.11.1+wasi-snapshot-preview1" +notes = "Auto-generated WASI snapshot-preview1 bindings from Bytecode Alliance. no_std, no build script, zero runtime deps. Unsafe limited to FFI wrappers for WASI host calls and unreachable_unchecked in exhaustive enum match arms. Assisted-by: copilot-chat:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + [[audits.OpenDevicePartnership.audits.windows-sys]] who = "Felipe Balbi " criteria = "safe-to-run" version = "0.61.2" aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-mcu/refs/heads/main/supply-chain/audits.toml" +[[audits.OpenDevicePartnership.audits.winnow]] +who = "Jerry Xie " +criteria = "safe-to-deploy" +delta = "0.7.10 -> 0.7.13" +notes = "Delta adds Accumulate impls (Cow str, String, VecDeque), fixes macro PartialEq/PartialOrd, optimizes str::next_token, adds tests, improves docs. No unsafe changes, no build script, no new powerful imports. Assisted-by: copilot-cli:claude-opus-4.6 cargo-vet" +aggregated-from = "https://raw.githubusercontent.com/OpenDevicePartnership/embedded-services/refs/heads/main/supply-chain/audits.toml" + [[audits.bytecode-alliance.audits.adler2]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -522,6 +843,12 @@ criteria = "safe-to-deploy" delta = "2.7.0 -> 2.9.4" notes = "Tweaks to the macro, nothing out of order." +[[audits.bytecode-alliance.audits.cfg-if]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "1.0.0" +notes = "I am the author of this crate." + [[audits.bytecode-alliance.audits.embedded-io]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -545,6 +872,11 @@ who = "Pat Hickey " criteria = "safe-to-deploy" delta = "0.3.28 -> 0.3.31" +[[audits.bytecode-alliance.audits.futures-macro]] +who = "Joel Dice " +criteria = "safe-to-deploy" +version = "0.3.31" + [[audits.bytecode-alliance.audits.futures-sink]] who = "Pat Hickey " criteria = "safe-to-deploy" @@ -555,11 +887,6 @@ who = "Pat Hickey " criteria = "safe-to-deploy" delta = "0.3.28 -> 0.3.31" -[[audits.bytecode-alliance.audits.hashbrown]] -who = "Chris Fallin " -criteria = "safe-to-deploy" -delta = "0.14.5 -> 0.15.2" - [[audits.bytecode-alliance.audits.itertools]] who = "Nick Fitzgerald " criteria = "safe-to-deploy" @@ -578,11 +905,6 @@ Lots of new iterators and shuffling some things around. Some new unsafe code but it's well-documented and well-tested. Nothing suspicious. """ -[[audits.bytecode-alliance.audits.itoa]] -who = "Dan Gohman " -criteria = "safe-to-deploy" -delta = "1.0.11 -> 1.0.14" - [[audits.bytecode-alliance.audits.log]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -636,21 +958,10 @@ criteria = "safe-to-deploy" version = "0.46.0" notes = "one use of unsafe to call windows specific api to get console handle." -[[audits.bytecode-alliance.audits.percent-encoding]] -who = "Alex Crichton " -criteria = "safe-to-deploy" -version = "2.2.0" -notes = """ -This crate is a single-file crate that does what it says on the tin. There are -a few `unsafe` blocks related to utf-8 validation which are locally verifiable -as correct and otherwise this crate is good to go. -""" - -[[audits.bytecode-alliance.audits.semver]] +[[audits.bytecode-alliance.audits.pin-utils]] who = "Pat Hickey " criteria = "safe-to-deploy" -version = "1.0.17" -notes = "plenty of unsafe pointer and vec tricks, but in well-structured and commented code that appears to be correct" +version = "0.1.0" [[audits.bytecode-alliance.audits.sharded-slab]] who = "Pat Hickey " @@ -752,6 +1063,67 @@ Additional review comments can be found at https://crrev.com/c/4723145/31 """ aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.bytemuck]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.16.3" +notes = """ +Review notes from the original audit (of 1.14.3) may be found in +https://crrev.com/c/5362675. Note that this audit has initially missed UB risk +that was fixed in 1.16.2 - see https://github.com/Lokathor/bytemuck/pull/258. +Because of this, the original audit has been edited to certify version `1.16.3` +instead (see also https://crrev.com/c/5771867). +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.16.3 -> 1.17.1" +notes = "Unsafe review comments can be found in https://crrev.com/c/5813463" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.17.1 -> 1.18.0" +notes = "No code changes - just altering feature flag arrangements" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.18.0 -> 1.19.0" +notes = "No code changes - just comment changes and adding the track_caller attribute." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.19.0 -> 1.20.0" +notes = "`unsafe` review can be found at https://crrev.com/c/6096767" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.20.0 -> 1.21.0" +notes = "Unsafe review at https://chromium-review.googlesource.com/c/chromium/src/+/6111154/" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Daniel Cheng " +criteria = "safe-to-deploy" +delta = "1.21.0 -> 1.22.0" +notes = """ +This adds new instances of unsafe, but the uses are justified: +- BoxBytes is essentially a Box<[u8], which is Send + Sync, so also marking BoxBytes as Send + Sync is justified. +- core::num::Saturating meets the criteria for Zeroable + Pod, so marking it as such is justified. + +See https://crrev.com/c/6321863 for more audit notes. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + [[audits.google.audits.byteorder]] who = "danakj " criteria = "safe-to-deploy" @@ -800,55 +1172,44 @@ delta = "1.0.1 -> 1.0.2" notes = "No changes to any .rs files or Rust code." aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" -[[audits.google.audits.glob]] +[[audits.google.audits.futures-util]] who = "George Burgess IV " -criteria = "safe-to-deploy" -version = "0.3.1" +criteria = "safe-to-run" +version = "0.3.28" +notes = """ +There's a custom xorshift-based `random::shuffle` implementation in +src/async_await/random.rs. This is `doc(hidden)` and seems to exist just so +that `futures-macro::select` can be unbiased. Sicne xorshift is explicitly not +intended to be a cryptographically secure algorithm, it is not considered +crypto. +""" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.glob]] -who = "Dustin J. Mitchell " -criteria = "safe-to-deploy" -delta = "0.3.1 -> 0.3.2" -notes = "Still no unsafe" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.itoa]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -version = "1.0.10" -notes = ''' -I grepped for \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits. - -There are a few places where `unsafe` is used. Unsafe review notes can be found -in https://crrev.com/c/5350697. - -Version 1.0.1 of this crate has been added to Chromium in -https://crrev.com/c/3321896. -''' -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.futures-util]] +who = "George Burgess IV " +criteria = "safe-to-run" +delta = "0.3.28 -> 0.3.31" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.itoa]] -who = "Lukasz Anforowicz " +[[audits.google.audits.glob]] +who = "George Burgess IV " criteria = "safe-to-deploy" -delta = "1.0.10 -> 1.0.11" -notes = """ -Straightforward diff between 1.0.10 and 1.0.11 - only 3 commits: - -* Bumping up the version -* A touch up of comments -* And my own PR to make `unsafe` blocks more granular: - https://github.com/dtolnay/itoa/pull/42 -""" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +version = "0.3.1" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.itoa]] -who = "Daniel Cheng " +[[audits.google.audits.glob]] +who = "Dustin J. Mitchell " criteria = "safe-to-deploy" -delta = "1.0.14 -> 1.0.15" -notes = "Only minor rustdoc changes." +delta = "0.3.1 -> 0.3.2" +notes = "Still no unsafe" aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.io-uring]] +who = "George Burgess IV " +criteria = "safe-to-run" +version = "0.5.13" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + [[audits.google.audits.lazy_static]] who = "Lukasz Anforowicz " criteria = "safe-to-deploy" @@ -882,6 +1243,12 @@ describe in the review doc. """ aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.mio]] +who = "Vovo Yang " +criteria = "safe-to-run" +version = "0.8.8" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + [[audits.google.audits.nb]] who = "George Burgess IV " criteria = "safe-to-deploy" @@ -900,20 +1267,6 @@ criteria = "safe-to-deploy" delta = "1.0.0 -> 1.1.0" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.num-integer]] -who = "Manish Goregaokar " -criteria = "safe-to-deploy" -version = "0.1.46" -notes = "Contains no unsafe" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.num-rational]] -who = "Manish Goregaokar " -criteria = "safe-to-deploy" -version = "0.4.2" -notes = "Contains no unsafe" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - [[audits.google.audits.num-traits]] who = "Manish Goregaokar " criteria = "safe-to-deploy" @@ -1074,247 +1427,11 @@ delta = "0.4.0 -> 0.4.1" notes = "No unsafe, net or fs." aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" -[[audits.google.audits.semver]] -who = "Daniel Cheng " +[[audits.google.audits.slab]] +who = "Android Legacy" criteria = "safe-to-run" -delta = "1.0.25 -> 1.0.26" -notes = "Only minor documentation updates." -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -version = "1.0.197" -notes = """ -Grepped for `-i cipher`, `-i crypto`, `'\bfs\b'`, `'\bnet\b'`, `'\bunsafe\b'`. - -There were some hits for `net`, but they were related to serialization and -not actually opening any connections or anything like that. - -There were 2 hits of `unsafe` when grepping: -* In `fn as_str` in `impl Buf` -* In `fn serialize` in `impl Serialize for net::Ipv4Addr` - -Unsafe review comments can be found in https://crrev.com/c/5350573/2 (this -review also covered `serde_json_lenient`). - -Version 1.0.130 of the crate has been added to Chromium in -https://crrev.com/c/3265545. The CL description contains a link to a -(Google-internal, sorry) document with a mini security review. -""" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Dustin J. Mitchell " -criteria = "safe-to-deploy" -delta = "1.0.197 -> 1.0.198" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "danakj " -criteria = "safe-to-deploy" -delta = "1.0.198 -> 1.0.201" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Dustin J. Mitchell " -criteria = "safe-to-deploy" -delta = "1.0.201 -> 1.0.202" -notes = "Trivial changes" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -delta = "1.0.202 -> 1.0.203" -notes = "s/doc_cfg/docsrs/ + tuple_impls/tuple_impl_body-related changes" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Adrian Taylor " -criteria = "safe-to-deploy" -delta = "1.0.203 -> 1.0.204" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -delta = "1.0.204 -> 1.0.207" -notes = "The small change in `src/private/ser.rs` should have no impact on `ub-risk-2`." -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -delta = "1.0.207 -> 1.0.209" -notes = """ -The delta carries fairly small changes in `src/private/de.rs` and -`src/private/ser.rs` (see https://crrev.com/c/5812194/2..5). AFAICT the -delta has no impact on the `unsafe`, `from_utf8_unchecked`-related parts -of the crate (in `src/de/format.rs` and `src/ser/impls.rs`). -""" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Adrian Taylor " -criteria = "safe-to-deploy" -delta = "1.0.209 -> 1.0.210" -notes = "Almost no new code - just feature rearrangement" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Liza Burakova " -criteria = "safe-to-deploy" -delta = "1.0.210 -> 1.0.213" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Dustin J. Mitchell " -criteria = "safe-to-deploy" -delta = "1.0.213 -> 1.0.214" -notes = "No unsafe, no crypto" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Adrian Taylor " -criteria = "safe-to-deploy" -delta = "1.0.214 -> 1.0.215" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -delta = "1.0.215 -> 1.0.216" -notes = "The delta makes minor changes in `build.rs` - switching to the `?` syntax sugar." -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Dustin J. Mitchell " -criteria = "safe-to-deploy" -delta = "1.0.216 -> 1.0.217" -notes = "Minimal changes, nothing unsafe" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Daniel Cheng " -criteria = "safe-to-deploy" -delta = "1.0.217 -> 1.0.218" -notes = "No changes outside comments and documentation." -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -delta = "1.0.218 -> 1.0.219" -notes = "Just allowing `clippy::elidable_lifetime_names`." -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -version = "1.0.197" -notes = 'Grepped for "unsafe", "crypt", "cipher", "fs", "net" - there were no hits' -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "danakj " -criteria = "safe-to-deploy" -delta = "1.0.197 -> 1.0.201" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Dustin J. Mitchell " -criteria = "safe-to-deploy" -delta = "1.0.201 -> 1.0.202" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -delta = "1.0.202 -> 1.0.203" -notes = 'Grepped for "unsafe", "crypt", "cipher", "fs", "net" - there were no hits' -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Adrian Taylor " -criteria = "safe-to-deploy" -delta = "1.0.203 -> 1.0.204" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -delta = "1.0.204 -> 1.0.207" -notes = 'Grepped for \"unsafe\", \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits' -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -delta = "1.0.207 -> 1.0.209" -notes = ''' -There are no code changes in this delta - see https://crrev.com/c/5812194/2..5 - -I've neverthless also grepped for `-i cipher`, `-i crypto`, `\bfs\b`, -`\bnet\b`, and `\bunsafe\b`. There were no hits. -''' -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Adrian Taylor " -criteria = "safe-to-deploy" -delta = "1.0.209 -> 1.0.210" -notes = "Almost no new code - just feature rearrangement" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Liza Burakova " -criteria = "safe-to-deploy" -delta = "1.0.210 -> 1.0.213" -notes = "Grepped for 'unsafe', 'crypt', 'cipher', 'fs', 'net' - there were no hits" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Dustin J. Mitchell " -criteria = "safe-to-deploy" -delta = "1.0.213 -> 1.0.214" -notes = "No changes to unsafe, no crypto" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Adrian Taylor " -criteria = "safe-to-deploy" -delta = "1.0.214 -> 1.0.215" -notes = "Minor changes should not impact UB risk" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -delta = "1.0.215 -> 1.0.216" -notes = "The delta adds `#[automatically_derived]` in a few places. Still no `unsafe`." -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Dustin J. Mitchell " -criteria = "safe-to-deploy" -delta = "1.0.216 -> 1.0.217" -notes = "No changes" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Daniel Cheng " -criteria = "safe-to-deploy" -delta = "1.0.217 -> 1.0.218" -notes = "No changes outside comments and documentation." -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_derive]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -delta = "1.0.218 -> 1.0.219" -notes = "Minor changes (clippy tweaks, using `mem::take` instead of `mem::replace`)." -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +version = "0.4.7" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" [[audits.google.audits.stable_deref_trait]] who = "Manish Goregaokar " @@ -1323,17 +1440,6 @@ version = "1.2.0" notes = "Purely a trait, crates using this should be carefully vetted since self-referential stuff can be super tricky around various unsafe rust edges." aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" -[[audits.google.audits.strsim]] -who = "danakj@chromium.org" -criteria = "safe-to-deploy" -version = "0.10.0" -notes = """ -Reviewed in https://crrev.com/c/5171063 - -Previously reviewed during security review and the audit is grandparented in. -""" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - [[audits.google.audits.unicode-ident]] who = "Lukasz Anforowicz " criteria = "safe-to-deploy" @@ -1390,38 +1496,17 @@ criteria = "safe-to-run" version = "0.2.1" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.void]] +[[audits.google.audits.version_check]] who = "George Burgess IV " criteria = "safe-to-deploy" -version = "1.0.2" +version = "0.9.4" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.mozilla.wildcard-audits.encoding_rs]] -who = "Henri Sivonen " -criteria = "safe-to-deploy" -user-id = 4484 # Henri Sivonen (hsivonen) -start = "2019-02-26" -end = "2025-10-23" -notes = "I, Henri Sivonen, wrote encoding_rs for Gecko and have reviewed contributions by others. There are two caveats to the certification: 1) The crate does things that are documented to be UB but that do not appear to actually be UB due to integer types differing from the general rule; https://github.com/hsivonen/encoding_rs/issues/79 . 2) It would be prudent to re-review the code that reinterprets buffers of integers as SIMD vectors; see https://github.com/hsivonen/encoding_rs/issues/87 ." -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - -[[audits.mozilla.wildcard-audits.unicode-segmentation]] -who = "Manish Goregaokar " -criteria = "safe-to-deploy" -user-id = 1139 # Manish Goregaokar (Manishearth) -start = "2019-05-15" -end = "2026-02-01" -notes = "All code written or reviewed by Manish" -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - -[[audits.mozilla.wildcard-audits.unicode-width]] -who = "Manish Goregaokar " +[[audits.google.audits.void]] +who = "George Burgess IV " criteria = "safe-to-deploy" -user-id = 1139 # Manish Goregaokar (Manishearth) -start = "2019-12-05" -end = "2026-02-01" -notes = "All code written or reviewed by Manish" -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +version = "1.0.2" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" [[audits.mozilla.audits.adler2]] who = "Erich Gubler " @@ -1435,67 +1520,6 @@ criteria = "safe-to-deploy" version = "0.5.1" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.mozilla.audits.askama]] -who = "Ben Dean-Kawamura " -criteria = "safe-to-deploy" -version = "0.13.1" -notes = """ -Template crate. This is only used to generate the Rust/JS code for UniFFI. - -We used to use askama, then we switched to rinja which was a fork. Now rinja and -askama have merged again. - -The differences from askama 0.12, are pretty straightforward and don't seem risky to me. There's -some unsafe code and macros, but nothing that complicated. -""" -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - -[[audits.mozilla.audits.askama]] -who = "Jan-Erik Rediger " -criteria = "safe-to-deploy" -delta = "0.13.1 -> 0.14.0" -aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" - -[[audits.mozilla.audits.askama_derive]] -who = "Ben Dean-Kawamura " -criteria = "safe-to-deploy" -version = "0.13.1" -notes = """ -Template crate. This is only used to generate the Rust/JS code for UniFFI. - -We used to use askama, then we switched to rinja which was a fork. Now rinja and -askama have merged again. - -I did a quick scan of the current code and couldn't find any issues. -""" -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - -[[audits.mozilla.audits.askama_derive]] -who = "Jan-Erik Rediger " -criteria = "safe-to-deploy" -delta = "0.13.1 -> 0.14.0" -aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" - -[[audits.mozilla.audits.askama_parser]] -who = "Ben Dean-Kawamura " -criteria = "safe-to-deploy" -version = "0.13.0" -notes = """ -Template crate. This is only used to generate the Rust/JS code for UniFFI. - -We used to use askama, then we switched to rinja which was a fork. Now rinja and -askama have merged again. - -I did a quick scan of the current code and couldn't find any issues. -""" -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - -[[audits.mozilla.audits.askama_parser]] -who = "Jan-Erik Rediger " -criteria = "safe-to-deploy" -delta = "0.13.0 -> 0.14.0" -aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" - [[audits.mozilla.audits.bitflags]] who = "Alex Franchuk " criteria = "safe-to-deploy" @@ -1567,13 +1591,6 @@ criteria = "safe-to-deploy" delta = "0.2.10 -> 0.2.11" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.mozilla.audits.fnv]] -who = "Bobby Holley " -criteria = "safe-to-deploy" -version = "1.0.7" -notes = "Simple hasher implementation with no unsafe code." -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - [[audits.mozilla.audits.futures-core]] who = "Mike Hommey " criteria = "safe-to-deploy" @@ -1609,28 +1626,41 @@ criteria = "safe-to-deploy" delta = "1.8.3 -> 2.5.0" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.mozilla.audits.hashbrown]] +[[audits.mozilla.audits.litrs]] +who = "Erich Gubler " +criteria = "safe-to-deploy" +version = "0.4.1" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.mio]] +who = "Mike Hommey " +criteria = "safe-to-deploy" +delta = "0.8.8 -> 1.0.1" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.once_cell]] who = "Erich Gubler " criteria = "safe-to-deploy" -delta = "0.15.2 -> 0.15.5" +delta = "1.20.1 -> 1.20.2" +notes = "This update works around a Cargo bug that forces the addition of `portable-atomic` into a lockfile, which we have never needed to use." aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.mozilla.audits.percent-encoding]] -who = "Valentin Gosu " +[[audits.mozilla.audits.once_cell]] +who = "Erich Gubler " criteria = "safe-to-deploy" -delta = "2.2.0 -> 2.3.0" +delta = "1.20.2 -> 1.20.3" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.mozilla.audits.percent-encoding]] -who = "Valentin Gosu " +[[audits.mozilla.audits.once_cell]] +who = "Erich Gubler " criteria = "safe-to-deploy" -delta = "2.3.0 -> 2.3.1" +delta = "1.20.3 -> 1.21.1" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.mozilla.audits.percent-encoding]] -who = "edgul " +[[audits.mozilla.audits.once_cell]] +who = "Erich Gubler " criteria = "safe-to-deploy" -delta = "2.3.1 -> 2.3.2" +delta = "1.21.1 -> 1.21.3" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" [[audits.mozilla.audits.pin-project-lite]] @@ -1649,26 +1679,6 @@ Only functional change is to work around a bug in the negative_impls feature """ aggregated-from = "https://raw.githubusercontent.com/mozilla/cargo-vet/main/supply-chain/audits.toml" -[[audits.mozilla.audits.rustc-hash]] -who = "Bobby Holley " -criteria = "safe-to-deploy" -version = "1.1.0" -notes = "Straightforward crate with no unsafe code, does what it says on the tin." -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - -[[audits.mozilla.audits.rustc-hash]] -who = "Ben Dean-Kawamura " -criteria = "safe-to-deploy" -delta = "1.1.0 -> 2.1.1" -notes = "Simple hashing crate, no unsafe code." -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - -[[audits.mozilla.audits.semver]] -who = "Jan-Erik Rediger " -criteria = "safe-to-deploy" -delta = "1.0.17 -> 1.0.25" -aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" - [[audits.mozilla.audits.serde_core]] who = "Erich Gubler " criteria = "safe-to-deploy" @@ -1693,10 +1703,10 @@ criteria = "safe-to-deploy" delta = "1.1.0 -> 1.3.0" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.mozilla.audits.strsim]] -who = "Ben Dean-Kawamura " +[[audits.mozilla.audits.slab]] +who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.10.0 -> 0.11.1" +delta = "0.4.7 -> 0.4.8" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" [[audits.mozilla.audits.tracing]] diff --git a/type-c-interface/src/service/event.rs b/type-c-interface/src/service/event.rs index 08ac63cae..88b4e7ea6 100644 --- a/type-c-interface/src/service/event.rs +++ b/type-c-interface/src/service/event.rs @@ -3,16 +3,28 @@ use embedded_usb_pd::{GlobalPortId, ado::Ado}; use crate::port::{ - PortStatus, + DpStatus, PortStatus, event::{PortStatusEventBitfield, VdmData}, }; +/// Struct containing data for a [`PortEventData::StatusChanged`] event +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct StatusChangedData { + /// Status changed event + pub status_event: PortStatusEventBitfield, + /// Previous port status + pub previous_status: PortStatus, + /// Current port status + pub current_status: PortStatus, +} + /// 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), + StatusChanged(StatusChangedData), /// PD alert Alert(Ado), /// VDM @@ -22,7 +34,7 @@ pub enum PortEventData { /// USB mux error recovery UsbMuxErrorRecovery, /// DP status update - DpStatusUpdate, + DpStatusUpdate(DpStatus), } /// Struct containing a complete port event diff --git a/type-c-service/src/wrapper/config.rs b/type-c-service/src/controller/config.rs similarity index 100% rename from type-c-service/src/wrapper/config.rs rename to type-c-service/src/controller/config.rs diff --git a/type-c-service/src/controller/event.rs b/type-c-service/src/controller/event.rs new file mode 100644 index 000000000..40c91bc38 --- /dev/null +++ b/type-c-service/src/controller/event.rs @@ -0,0 +1,22 @@ +//! Port event types + +use type_c_interface::port::event::PortEventBitfield; + +/// Top-level port event type +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Event { + /// Port event + PortEvent(type_c_interface::port::event::PortEvent), +} + +/// Loopback event to allow `sync_state` and similar functions +/// to generate events that can be processed by the same code as real events. +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Loopback { + /// Port event + PortEvent(PortEventBitfield), +} diff --git a/type-c-service/src/controller/event_receiver.rs b/type-c-service/src/controller/event_receiver.rs new file mode 100644 index 000000000..8a17f93a0 --- /dev/null +++ b/type-c-service/src/controller/event_receiver.rs @@ -0,0 +1,140 @@ +//! This module contains event receiver types for the controller wrapper. +use core::array; +use core::future::pending; +use embassy_futures::select::{Either, select}; +use embassy_time::Timer; +use embedded_services::event::{Receiver, Sender}; +use embedded_services::sync::Lockable; + +use crate::PortEventStreamer; +use crate::controller::event::{Event, Loopback}; +use crate::controller::state::SharedState; +use type_c_interface::port::event::{PortEvent, PortEventBitfield, PortStatusEventBitfield}; + +/// Trait used for receiving interrupt from the controller. +pub trait InterruptReceiver { + /// Wait for the next interrupt event. + fn wait_interrupt(&mut self) -> impl Future; +} + +/// Struct to send received interrupts to their corresponding port receivers +pub struct PortEventSplitter> { + /// Senders to forward port events to their corresponding port receivers + sender: [S; N], +} + +impl> PortEventSplitter { + /// Create a new instance + pub fn new(sender: [S; N]) -> Self { + Self { sender } + } + + /// Wait for the next interrupt event and forward it to the corresponding port receiver. + pub async fn process_interrupts(&mut self, interrupts: [PortEventBitfield; N]) { + for (interrupt, sender) in interrupts.into_iter().zip(self.sender.iter_mut()) { + if interrupt != PortEventBitfield::none() { + sender.send(interrupt).await; + } + } + } +} + +/// Struct to receive and stream port events from the controller. +pub struct PortEventReceiver, LoopbackReceiver: Receiver> { + /// Receiver for the controller's interrupt events + receiver: R, + /// Port event streaming state + streaming_state: Option>>, + /// Loopback receiver for software-generated events + loopback_receiver: LoopbackReceiver, +} + +impl, LoopbackReceiver: Receiver> PortEventReceiver { + /// Create a new instance + pub fn new(receiver: R, loopback_receiver: LoopbackReceiver) -> Self { + Self { + receiver, + streaming_state: None, + loopback_receiver, + } + } + + /// Wait for the next port event + pub async fn wait_next(&mut self) -> type_c_interface::port::event::PortEvent { + loop { + let streaming_state = if let Some(streaming_state) = &mut self.streaming_state { + // Yield to ensure we don't monopolize the executor + embassy_futures::yield_now().await; + streaming_state + } else { + let (Either::First(Loopback::PortEvent(events)) | Either::Second(events)) = + select(self.loopback_receiver.wait_next(), self.receiver.wait_next()).await; + self.streaming_state + .insert(PortEventStreamer::new([events].into_iter())) + }; + + if let Some((_, event)) = streaming_state.next() { + return event; + } else { + self.streaming_state = None; + } + } + } +} + +/// Struct used for containing controller event receivers. +pub struct EventReceiver< + 'a, + State: Lockable, + InterruptReceiver: Receiver, + LoopbackReceiver: Receiver, +> { + /// Port event receiver + port_event_receiver: PortEventReceiver, + /// Shared state + shared_state: &'a State, +} + +impl< + 'a, + State: Lockable, + InterruptReceiver: Receiver, + LoopbackReceiver: Receiver, +> EventReceiver<'a, State, InterruptReceiver, LoopbackReceiver> +{ + /// Create a new instance + pub fn new( + shared_state: &'a State, + port_event_receiver: InterruptReceiver, + loopback_receiver: LoopbackReceiver, + ) -> Self { + Self { + shared_state, + port_event_receiver: PortEventReceiver::new(port_event_receiver, loopback_receiver), + } + } + + /// Wait for the next port event from any port. + /// + /// Returns the local port ID and the event bitfield. + pub async fn wait_event(&mut self) -> Event { + let timeout = self.shared_state.lock().await.sink_ready_timeout; + match select(self.port_event_receiver.wait_next(), async move { + if let Some(timeout) = timeout { + Timer::at(timeout).await; + } else { + pending::<()>().await; + } + }) + .await + { + Either::First(event) => Event::PortEvent(event), + Either::Second(_) => { + let mut status_event = PortStatusEventBitfield::none(); + status_event.set_sink_ready(true); + self.shared_state.lock().await.sink_ready_timeout = None; + Event::PortEvent(PortEvent::StatusChanged(status_event)) + } + } + } +} diff --git a/type-c-service/src/controller/macros.rs b/type-c-service/src/controller/macros.rs new file mode 100644 index 000000000..8d27f94e4 --- /dev/null +++ b/type-c-service/src/controller/macros.rs @@ -0,0 +1,170 @@ +use embedded_services::{ + event::{Receiver, Sender}, + sync::Lockable, +}; +use type_c_interface::port::event::PortEventBitfield; + +use crate::controller::{event_receiver::EventReceiver, state}; + +pub const DEFAULT_POWER_POLICY_CHANNEL_SIZE: usize = 2; +pub const DEFAULT_LOOPBACK_CHANNEL_SIZE: usize = 1; +pub const DEFAULT_INTERRUPT_CHANNEL_SIZE: usize = 4; + +/// Components returned from port creation +pub struct PortComponents< + 'a, + Port, + SharedState: Lockable, + PowerPolicyReceveiver: Receiver, + LoopbackReceiver: Receiver, + InterruptReceiver: Receiver, + InterruptSender: Sender, +> { + /// Port instance + pub port: &'a Port, + /// Power policy event receiver + pub power_policy_receiver: PowerPolicyReceveiver, + /// Port event receiver + pub event_receiver: EventReceiver<'a, SharedState, InterruptReceiver, LoopbackReceiver>, + /// Interrupt sender + pub interrupt_sender: InterruptSender, +} + +/// Creates a module containing all state for a controller port, based on static cells and channels. +#[macro_export] +macro_rules! define_controller_port_static_cell_channel { + ($vis:vis, $name:ident, $mutex:ty, $controller:ty) => { + $vis mod $name { + use super::*; + + // We prefix all aliases with 'Inner' to avoid potential name conflicts with user code when this macro is invoked + // Unfortunately, super::$ty is not valid syntax in a macro, so we have to pull in everything with super::*. + /// Type alias for the power policy sender + pub type InnerPowerPolicySenderType = + ::embassy_sync::channel::DynamicSender<'static, ::power_policy_interface::psu::event::EventData>; + /// Type alias for the power policy receiver + pub type InnerPowerPolicyReceiverType = + ::embassy_sync::channel::DynamicReceiver<'static, ::power_policy_interface::psu::event::EventData>; + + /// Type alias for the loopback sender + pub type InnerLoopbackSenderType = + ::embassy_sync::channel::DynamicSender<'static, $crate::controller::event::Loopback>; + /// Type alias for the loopback receiver + pub type InnerLoopbackReceiverType = + ::embassy_sync::channel::DynamicReceiver<'static, $crate::controller::event::Loopback>; + + /// Type alias for the interrupt sender + pub type InnerInterruptReceiverType = + ::embassy_sync::channel::DynamicReceiver<'static, ::type_c_interface::port::event::PortEventBitfield>; + /// Type alias for the interrupt receiver + pub type InnerInterruptSenderType = + ::embassy_sync::channel::DynamicSender<'static, ::type_c_interface::port::event::PortEventBitfield>; + + /// Type alias for the shared state mutex + pub type InnerSharedStateType = + ::embassy_sync::mutex::Mutex<$mutex, $crate::controller::state::SharedState>; + /// Type alias for the port + pub type InnerPortType = ::embassy_sync::mutex::Mutex< + $mutex, + $crate::controller::Port< + 'static, + // Controller type + $controller, + // Shared state type + InnerSharedStateType, + // Power policy event sender type + InnerPowerPolicySenderType, + // Loopback event sender type + InnerLoopbackSenderType, + >, + >; + + /// Channel to send events to the power policy service + static POWER_POLICY_CHANNEL: ::static_cell::StaticCell< + ::embassy_sync::channel::Channel< + $mutex, + ::power_policy_interface::psu::event::EventData, + { $crate::controller::macros::DEFAULT_POWER_POLICY_CHANNEL_SIZE }, + >, + > = ::static_cell::StaticCell::new(); + /// Loopback channel + static LOOPBACK_CHANNEL: ::static_cell::StaticCell< + ::embassy_sync::channel::Channel< + $mutex, + $crate::controller::event::Loopback, + { $crate::controller::macros::DEFAULT_LOOPBACK_CHANNEL_SIZE }, + >, + > = ::static_cell::StaticCell::new(); + /// Interrupt channel + static INTERRUPT_CHANNEL: ::static_cell::StaticCell< + ::embassy_sync::channel::Channel< + $mutex, + ::type_c_interface::port::event::PortEventBitfield, + { $crate::controller::macros::DEFAULT_INTERRUPT_CHANNEL_SIZE }, + >, + > = ::static_cell::StaticCell::new(); + /// State shared between the port and event receiver + static SHARED_STATE: ::static_cell::StaticCell< + ::embassy_sync::mutex::Mutex<$mutex, $crate::controller::state::SharedState>, + > = ::static_cell::StaticCell::new(); + /// Port instance + static PORT: ::static_cell::StaticCell = ::static_cell::StaticCell::new(); + + pub fn create( + name: &'static str, + port: ::embedded_usb_pd::LocalPortId, + global_port: ::embedded_usb_pd::GlobalPortId, + config: $crate::controller::config::Config, + controller: &'static $controller, + context: &'static type_c_interface::service::context::Context, + ) -> $crate::controller::macros::PortComponents< + 'static, + InnerPortType, + InnerSharedStateType, + InnerPowerPolicyReceiverType, + InnerLoopbackReceiverType, + InnerInterruptReceiverType, + InnerInterruptSenderType, + > { + let shared_state = SHARED_STATE.init(::embassy_sync::mutex::Mutex::new( + $crate::controller::state::SharedState::new(), + )); + + let power_policy_channel = POWER_POLICY_CHANNEL.init(::embassy_sync::channel::Channel::new()); + let power_policy_sender = power_policy_channel.dyn_sender(); + let power_policy_receiver = power_policy_channel.dyn_receiver(); + + let loopback_channel = LOOPBACK_CHANNEL.init(::embassy_sync::channel::Channel::new()); + let loopback_sender = loopback_channel.dyn_sender(); + let loopback_receiver = loopback_channel.dyn_receiver(); + + let interrupt_channel = INTERRUPT_CHANNEL.init(::embassy_sync::channel::Channel::new()); + let interrupt_sender = interrupt_channel.dyn_sender(); + let interrupt_receiver = interrupt_channel.dyn_receiver(); + + let port = PORT.init(::embassy_sync::mutex::Mutex::new($crate::controller::Port::new( + name, + config, + port, + global_port, + controller, + shared_state, + power_policy_sender, + loopback_sender, + context, + ))); + let event_receiver = $crate::controller::event_receiver::EventReceiver::new( + shared_state, + interrupt_receiver, + loopback_receiver, + ); + $crate::controller::macros::PortComponents { + port, + power_policy_receiver, + event_receiver, + interrupt_sender, + } + } + } + }; +} diff --git a/type-c-service/src/controller/mod.rs b/type-c-service/src/controller/mod.rs new file mode 100644 index 000000000..dc6bf0d84 --- /dev/null +++ b/type-c-service/src/controller/mod.rs @@ -0,0 +1,242 @@ +//! Struct that manages per-port state, interfacing with a controller object that exposes multiple ports. +use embedded_services::{debug, error, event::Sender, info, named::Named, sync::Lockable}; +use embedded_usb_pd::{Error, GlobalPortId, LocalPortId, PdError}; +use power_policy_interface::psu::PsuState; +use type_c_interface::port::event::PortEventBitfield; +use type_c_interface::port::{ + Controller, PortStatus, event::PortEvent as InterfacePortEvent, event::PortStatusEventBitfield, +}; +use type_c_interface::service::event::{ + PortEvent as ServicePortEvent, PortEventData as ServicePortEventData, StatusChangedData, +}; + +use crate::controller::event::{Event, Loopback}; +use crate::controller::state::SharedState; + +pub mod config; +pub mod event; +pub mod event_receiver; +pub mod macros; +mod pd; +mod power; +pub mod state; + +pub struct Port< + 'device, + C: Lockable, + Shared: Lockable, + PowerSender: Sender, + LoopbackSender: Sender, +> { + /// Local port + port: LocalPortId, + /// Global port + global_port: GlobalPortId, + /// Controller + controller: &'device C, + /// Per-port PSU state + psu_state: power_policy_interface::psu::State, + /// Name for this port + name: &'static str, + /// Cached port status + status: PortStatus, + /// Sender for power policy events + power_policy_sender: PowerSender, + /// Configuration + config: config::Config, + /// Shared state + shared_state: &'device Shared, + /// Type-C service context + context: &'device type_c_interface::service::context::Context, + /// Loopback sender + loopback_sender: LoopbackSender, +} + +impl< + 'device, + C: Lockable, + Shared: Lockable, + PowerSender: Sender, + LoopbackSender: Sender, +> Port<'device, C, Shared, PowerSender, LoopbackSender> +{ + /// Create new Port instance + // Argument count will be reduced as the last bit of refactoring is done + #[allow(clippy::too_many_arguments)] + pub fn new( + name: &'static str, + config: config::Config, + port: LocalPortId, + global_port: GlobalPortId, + controller: &'device C, + shared_state: &'device Shared, + power_policy_sender: PowerSender, + loopback_sender: LoopbackSender, + context: &'device type_c_interface::service::context::Context, + ) -> Self { + Self { + name, + controller, + port, + global_port, + status: PortStatus::default(), + psu_state: power_policy_interface::psu::State::default(), + power_policy_sender, + config, + shared_state, + context, + loopback_sender, + } + } + + /// Top-level processing function + pub async fn process_event( + &mut self, + event: Event, + ) -> Result, Error<::BusError>> { + match event { + Event::PortEvent(port_event) => self.process_port_event(port_event).await, + } + } + + /// Process a port notification + async fn process_port_event( + &mut self, + event: InterfacePortEvent, + ) -> Result, Error<::BusError>> { + match event { + InterfacePortEvent::StatusChanged(status_event) => { + self.process_port_status_changed(status_event).await.map(Some) + } + InterfacePortEvent::Alert => self.process_pd_alert().await, + InterfacePortEvent::Vdm(vdm_event) => self.process_vdm_event(vdm_event).await.map(Some), + InterfacePortEvent::DpStatusUpdate => self.process_dp_status_update().await.map(Some), + rest => { + // Nothing currently implemented for these + debug!("({}): Notification: {:#?}", self.name, rest); + Ok(None) + } + } + } + + /// Process port status changed events + async fn process_port_status_changed( + &mut self, + status_event: PortStatusEventBitfield, + ) -> Result::BusError>> { + let new_status = self.controller.lock().await.get_port_status(self.port).await?; + debug!("({}) status: {:#?}", self.name, new_status); + debug!("({}) status events: {:#?}", self.name, status_event); + + if status_event.plug_inserted_or_removed() { + self.process_plug_event(&new_status).await?; + } + + // Only notify power policy of a contract after Sink Ready event (always after explicit or implicit contract) + if status_event.sink_ready() { + self.process_new_consumer_contract(&new_status).await?; + } + + if status_event.new_power_contract_as_provider() { + self.process_new_provider_contract(&new_status).await?; + } + + self.check_sink_ready_timeout( + &new_status, + status_event.new_power_contract_as_consumer(), + status_event.sink_ready(), + ) + .await?; + + let event = ServicePortEventData::StatusChanged(StatusChangedData { + status_event, + previous_status: self.status, + current_status: new_status, + }); + self.status = new_status; + self.context + .send_port_event(ServicePortEvent { + port: self.global_port, + event, + }) + .await + .map_err(Error::Pd)?; + Ok(event) + } + + /// Handle a plug event + async fn process_plug_event( + &mut self, + new_status: &PortStatus, + ) -> Result<(), Error<::BusError>> { + info!("Plug event"); + if new_status.is_connected() { + info!("Plug inserted"); + if self.psu_state.psu_state != PsuState::Detached { + info!("Device not in detached state, recovering"); + self.psu_state.detach(); + } + + if let Err(e) = self.psu_state.attach() { + // This should never happen because we should have detached above + error!("Failed to attach PSU: {:?}", e); + return Err(Error::Pd(PdError::Failed)); + } + + self.power_policy_sender + .send(power_policy_interface::psu::event::EventData::Attached) + .await; + } else { + info!("Plug removed"); + self.psu_state.detach(); + self.power_policy_sender + .send(power_policy_interface::psu::event::EventData::Detached) + .await; + } + + Ok(()) + } + + /// Get the cached port status, returns None if the port is invalid + pub fn get_cached_port_status(&self) -> PortStatus { + self.status + } + + /// Synchronize the state between the controller and the internal state + pub async fn sync_state(&mut self) -> Result<(), Error<::BusError>> { + let status = self.controller.lock().await.get_port_status(self.port).await?; + + let mut event = PortEventBitfield::none(); + let previous_status = self.status; + + if previous_status.is_connected() != status.is_connected() { + event.status.set_plug_inserted_or_removed(true); + } + + if previous_status.available_sink_contract != status.available_sink_contract { + event.status.set_new_power_contract_as_consumer(true); + } + + if previous_status.available_source_contract != status.available_source_contract { + event.status.set_new_power_contract_as_provider(true); + } + + if event != PortEventBitfield::none() { + self.loopback_sender.send(Loopback::PortEvent(event)).await; + } + Ok(()) + } +} + +impl< + 'device, + C: Lockable, + Shared: Lockable, + PowerSender: Sender, + LoopbackSender: Sender, +> Named for Port<'device, C, Shared, PowerSender, LoopbackSender> +{ + fn name(&self) -> &'static str { + self.name + } +} diff --git a/type-c-service/src/controller/pd.rs b/type-c-service/src/controller/pd.rs new file mode 100644 index 000000000..07d71c9f4 --- /dev/null +++ b/type-c-service/src/controller/pd.rs @@ -0,0 +1,84 @@ +//! PD functionality unrelated to power contracts and general port status +use embedded_services::{event::Sender, sync::Lockable}; +use type_c_interface::port::{ + Controller, + event::{VdmData, VdmNotification}, +}; +use type_c_interface::service::event::{PortEvent as ServicePortEvent, PortEventData as ServicePortEventData}; + +use super::*; +use crate::controller::state::SharedState; + +impl< + 'device, + C: Lockable, + Shared: Lockable, + PowerSender: Sender, + LoopbackSender: Sender, +> Port<'device, C, Shared, PowerSender, LoopbackSender> +{ + /// Process a VDM event by retrieving the relevant VDM data from the `controller` for the appropriate `port`. + pub(super) async fn process_vdm_event( + &mut self, + event: VdmNotification, + ) -> Result::BusError>> { + debug!("({}): Processing VDM event: {:?}", self.name, event); + let vdm_data = { + let mut controller = self.controller.lock().await; + match event { + VdmNotification::Entered => VdmData::Entered(controller.get_other_vdm(self.port).await?), + VdmNotification::Exited => VdmData::Exited(controller.get_other_vdm(self.port).await?), + VdmNotification::OtherReceived => VdmData::ReceivedOther(controller.get_other_vdm(self.port).await?), + VdmNotification::AttentionReceived => VdmData::ReceivedAttn(controller.get_attn_vdm(self.port).await?), + } + }; + + let event = ServicePortEventData::Vdm(vdm_data); + let _ = self + .context + .send_port_event(ServicePortEvent { + port: self.global_port, + event: ServicePortEventData::Vdm(vdm_data), + }) + .await; + Ok(event) + } + + /// Process a DisplayPort status update by retrieving the current DP status from the `controller` for the appropriate `port`. + pub(super) async fn process_dp_status_update( + &mut self, + ) -> Result::BusError>> { + debug!("({}): Processing DP status update event", self.name); + let status = self.controller.lock().await.get_dp_status(self.port).await?; + let event = ServicePortEventData::DpStatusUpdate(status); + let _ = self + .context + .send_port_event(ServicePortEvent { + port: self.global_port, + event, + }) + .await; + Ok(event) + } + + pub(super) async fn process_pd_alert( + &mut self, + ) -> Result, Error<::BusError>> { + let ado = self.controller.lock().await.get_pd_alert(self.port).await?; + debug!("({}): PD alert: {:#?}", self.name, ado); + if let Some(ado) = ado { + let event = ServicePortEventData::Alert(ado); + let _ = self + .context + .send_port_event(ServicePortEvent { + port: self.global_port, + event, + }) + .await; + Ok(Some(event)) + } else { + // For some reason we didn't read an alert, nothing to do + Ok(None) + } + } +} diff --git a/type-c-service/src/controller/power.rs b/type-c-service/src/controller/power.rs new file mode 100644 index 000000000..8fd0bd6e0 --- /dev/null +++ b/type-c-service/src/controller/power.rs @@ -0,0 +1,172 @@ +//! Module for power policy related functionality +use embassy_time::{Duration, Instant}; +use embedded_services::{debug, error, event::Sender, info, sync::Lockable}; +use embedded_usb_pd::{ + PdError, + constants::{T_PS_TRANSITION_EPR_MS, T_PS_TRANSITION_SPR_MS}, +}; +use power_policy_interface::{ + capability::{ConsumerPowerCapability, ProviderPowerCapability, PsuType}, + psu::{Error as PsuError, Psu, State}, +}; +use type_c_interface::port::Controller; + +use crate::{controller::config::UnconstrainedSink, util::power_policy_error_from_pd_bus_error}; + +use super::*; + +impl< + 'device, + C: Lockable, + Shared: Lockable, + PowerSender: Sender, + LoopbackSender: Sender, +> Port<'device, C, Shared, PowerSender, LoopbackSender> +{ + /// Handle a new contract as consumer + pub(super) async fn process_new_consumer_contract( + &mut self, + new_status: &PortStatus, + ) -> Result<(), Error<::BusError>> { + info!("Process new consumer contract"); + let available_sink_contract = new_status.available_sink_contract.map(|c| { + let mut c: ConsumerPowerCapability = c.into(); + let unconstrained = match self.config.unconstrained_sink { + UnconstrainedSink::Auto => new_status.unconstrained_power, + UnconstrainedSink::PowerThresholdMilliwatts(threshold) => c.capability.max_power_mw() >= threshold, + UnconstrainedSink::Never => false, + }; + c.flags.set_unconstrained_power(unconstrained); + c.flags.set_psu_type(PsuType::TypeC); + c + }); + + if let Err(e) = self.psu_state.update_consumer_power_capability(available_sink_contract) { + error!("Failed to update consumer power capability: {:?}", e); + return Err(Error::Pd(PdError::Failed)); + } + self.power_policy_sender + .send(power_policy_interface::psu::event::EventData::UpdatedConsumerCapability(available_sink_contract)) + .await; + Ok(()) + } + + /// Handle a new contract as provider + pub(super) async fn process_new_provider_contract( + &mut self, + new_status: &PortStatus, + ) -> Result<(), Error<::BusError>> { + info!("Process New provider contract"); + let capability = new_status.available_source_contract.map(|caps| { + let mut caps = ProviderPowerCapability::from(caps); + caps.flags.set_psu_type(PsuType::TypeC); + caps + }); + if let Err(e) = self.psu_state.update_requested_provider_power_capability(capability) { + error!("Failed to update requested provider power capability: {:?}", e); + return Err(Error::Pd(PdError::Failed)); + } + self.power_policy_sender + .send(power_policy_interface::psu::event::EventData::RequestedProviderCapability(capability)) + .await; + Ok(()) + } + + /// Check the sink ready timeout + /// + /// After accepting a sink contract (new contract as consumer), the PD spec guarantees that the + /// source will be available to provide power after `tPSTransition`. This allows us to handle transitions + /// even for controllers that might not always broadcast sink ready events. + pub(super) async fn check_sink_ready_timeout( + &mut self, + new_status: &PortStatus, + new_contract: bool, + sink_ready: bool, + ) -> Result<(), PdError> { + let contract_changed = self.status.available_sink_contract != new_status.available_sink_contract; + let mut shared_state = self.shared_state.lock().await; + let timeout = &mut shared_state.sink_ready_timeout; + + // Don't start the timeout if the sink has signaled it's ready or if the contract didn't change. + // The latter ensures that soft resets won't continually reset the ready timeout + debug!( + "({}): Check sink ready: new_contract={:?}, sink_ready={:?}, contract_changed={:?}, deadline={:?}", + self.name, new_contract, sink_ready, contract_changed, timeout, + ); + if new_contract && !sink_ready && contract_changed { + // Start the timeout + // Double the spec maximum transition time to provide a safety margin for hardware/controller delays or out-of-spec controllers. + let timeout_ms = if new_status.epr { + T_PS_TRANSITION_EPR_MS + } else { + T_PS_TRANSITION_SPR_MS + } + .maximum + .0 * 2; + + debug!("({}): Sink ready timeout started for {}ms", self.name, timeout_ms); + *timeout = Some(Instant::now() + Duration::from_millis(timeout_ms as u64)); + } else if timeout.is_some() + && (!new_status.is_connected() || new_status.available_sink_contract.is_none() || sink_ready) + { + debug!("({}): Sink ready timeout cleared", self.name); + *timeout = None; + } + Ok(()) + } +} + +impl< + 'device, + C: Lockable, + Shared: Lockable, + PowerSender: Sender, + LoopbackSender: Sender, +> Psu for Port<'device, C, Shared, PowerSender, LoopbackSender> +{ + async fn disconnect(&mut self) -> Result<(), PsuError> { + self.controller + .lock() + .await + .enable_sink_path(self.port, false) + .await + .map_err(|e| { + error!("({}): Error disabling sink path", self.name); + power_policy_error_from_pd_bus_error(e) + })?; + self.psu_state.disconnect(false) + } + + async fn connect_provider(&mut self, capability: ProviderPowerCapability) -> Result<(), PsuError> { + info!("({}): Connect as provider: {:#?}", self.name, capability); + // TODO: Implement controller over provider enablement + self.psu_state.connect_provider(capability).inspect_err(|e| { + error!("({}): Failed to transition to provider state: {:#?}", self.name, e); + }) + } + + async fn connect_consumer(&mut self, capability: ConsumerPowerCapability) -> Result<(), PsuError> { + info!( + "({}): Connect as consumer: {:?}, enable input switch", + self.name, capability + ); + self.controller + .lock() + .await + .enable_sink_path(self.port, true) + .await + .map_err(|e| { + error!("({}): Error enabling sink path", self.name); + power_policy_error_from_pd_bus_error(e) + })?; + self.psu_state.connect_consumer(capability) + } + + fn state(&self) -> &State { + &self.psu_state + } + + fn state_mut(&mut self) -> &mut State { + &mut self.psu_state + } +} diff --git a/type-c-service/src/controller/state.rs b/type-c-service/src/controller/state.rs new file mode 100644 index 000000000..c19fd3a6f --- /dev/null +++ b/type-c-service/src/controller/state.rs @@ -0,0 +1,23 @@ +use embassy_time::Instant; + +/// State shared between the port and event receiver +#[derive(Copy, Clone)] +pub struct SharedState { + /// Sink ready timeout + pub(crate) sink_ready_timeout: Option, +} + +impl SharedState { + /// Create a new instance with default values + pub fn new() -> Self { + Self { + sink_ready_timeout: None, + } + } +} + +impl Default for SharedState { + fn default() -> Self { + Self::new() + } +} diff --git a/type-c-service/src/driver/tps6699x.rs b/type-c-service/src/driver/tps6699x.rs index 6a17a43df..906a298ea 100644 --- a/type-c-service/src/driver/tps6699x.rs +++ b/type-c-service/src/driver/tps6699x.rs @@ -871,21 +871,11 @@ bitfield! { pub u32, ti_fw_version, set_ti_fw_version: 63, 32; } -pub struct InterruptReceiver<'a, M: RawMutex, BUS: I2c> { - interrupt_receiver: interrupt::InterruptReceiver<'a, M, BUS>, -} - -impl<'a, M: RawMutex, BUS: I2c> InterruptReceiver<'a, M, BUS> { - pub fn new(interrupt_receiver: interrupt::InterruptReceiver<'a, M, BUS>) -> Self { - Self { interrupt_receiver } - } -} - -impl<'a, M: RawMutex, BUS: I2c> crate::wrapper::event_receiver::InterruptReceiver - for InterruptReceiver<'a, M, BUS> +impl<'a, M: RawMutex, BUS: I2c> crate::controller::event_receiver::InterruptReceiver + for interrupt::InterruptReceiver<'a, M, BUS> { async fn wait_interrupt(&mut self) -> [PortEventBitfield; MAX_SUPPORTED_PORTS] { - let interrupts = self.interrupt_receiver.wait_any(false).await; + let interrupts = self.wait_any(false).await; let mut port_events = [PortEventBitfield::none(); MAX_SUPPORTED_PORTS]; for (interrupt, event) in zip(interrupts.iter(), port_events.iter_mut()) { if *interrupt == IntEventBus1::new_zero() { diff --git a/type-c-service/src/lib.rs b/type-c-service/src/lib.rs index 3ccaa27d6..9e42b2000 100644 --- a/type-c-service/src/lib.rs +++ b/type-c-service/src/lib.rs @@ -1,10 +1,10 @@ #![no_std] pub mod bridge; +pub mod controller; pub mod driver; pub mod service; pub mod task; pub mod util; -pub mod wrapper; use core::iter::Enumerate; diff --git a/type-c-service/src/service/mod.rs b/type-c-service/src/service/mod.rs index 1e1db4b18..70e7f11e6 100644 --- a/type-c-service/src/service/mod.rs +++ b/type-c-service/src/service/mod.rs @@ -133,8 +133,9 @@ impl<'a> Service<'a> { 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 + PortEventData::StatusChanged(status_event) => { + self.process_port_status_event(event.port, status_event.status_event, status_event.current_status) + .await } unhandled => { // Currently just log notifications, but may want to do more in the future diff --git a/type-c-service/src/task.rs b/type-c-service/src/task.rs index cd1ed7dd9..98dbe4f18 100644 --- a/type-c-service/src/task.rs +++ b/type-c-service/src/task.rs @@ -1,36 +1,15 @@ -use embedded_services::{ - error, - event::{self, Receiver}, - info, - sync::Lockable, -}; +use embedded_services::{error, event::Receiver, info, sync::Lockable}; use power_policy_interface::service::event::EventData as PowerPolicyEventData; -use crate::{ - service::{EventReceiver, Service}, - wrapper::ControllerWrapper, -}; +use crate::service::{EventReceiver, Service}; /// Task to run the Type-C service, running the default event loop -pub async fn task, const N: usize>( +pub async fn task>( service: &'static impl Lockable>, mut event_receiver: EventReceiver<'static, PowerReceiver>, - wrappers: [&'static ControllerWrapper<'static, M, D, S>; N], -) where - M: embassy_sync::blocking_mutex::raw::RawMutex, - D: embedded_services::sync::Lockable, - S: event::Sender, - ::Inner: type_c_interface::port::Controller, -{ +) { info!("Starting type-c task"); - for controller_wrapper in wrappers { - if controller_wrapper.register().is_err() { - error!("Failed to register a controller"); - return; - } - } - loop { let event = event_receiver.wait_next().await; if let Err(e) = service.lock().await.process_event(event).await { diff --git a/type-c-service/src/wrapper/backing.rs b/type-c-service/src/wrapper/backing.rs deleted file mode 100644 index 4d287ebf6..000000000 --- a/type-c-service/src/wrapper/backing.rs +++ /dev/null @@ -1,189 +0,0 @@ -//! Various types of state and objects required for [`crate::wrapper::ControllerWrapper`]. -//! -//! TODO: update this documentation when the type-C service is refactored -//! -use core::array::from_fn; - -use embassy_sync::{blocking_mutex::raw::RawMutex, mutex::Mutex}; - -use embedded_services::event; - -use type_c_interface::port::{ControllerId, PortRegistration, PortStatus, event::PortStatusEventBitfield}; - -use crate::wrapper::proxy::{PowerProxyChannel, PowerProxyDevice, PowerProxyReceiver}; - -/// Service registration objects -pub struct Registration<'a, M: RawMutex> { - pub context: &'a type_c_interface::service::context::Context, - pub pd_controller: &'a type_c_interface::port::Device<'a>, - pub power_devices: &'a [&'a Mutex>], -} - -impl<'a, M: RawMutex> Registration<'a, M> { - pub fn num_ports(&self) -> usize { - self.power_devices.len() - } -} - -/// 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: [PortRegistration; N], - power_proxy_channels: [PowerProxyChannel; N], -} - -impl<'a, const N: usize, M: RawMutex> Storage<'a, N, M> { - pub fn new( - context: &'a type_c_interface::service::context::Context, - controller_id: ControllerId, - pd_ports: [PortRegistration; N], - ) -> Self { - Self { - context, - controller_id, - pd_ports, - power_proxy_channels: from_fn(|_| PowerProxyChannel::new()), - } - } - - /// Create intermediate storage from this storage - pub fn try_create_intermediate>( - &self, - power_policy_init: [(&'static str, S); N], - ) -> Option<(IntermediateStorage<'_, N, M, S>, [PowerProxyReceiver<'_>; N])> { - IntermediateStorage::try_from_storage(self, power_policy_init) - } -} - -pub struct Port<'a, M: RawMutex, S: event::Sender> { - pub proxy: Mutex>, - pub state: Mutex>, -} - -pub struct PortState> { - /// Cached port status - pub(crate) status: PortStatus, - /// Software status event - pub(crate) sw_status_event: PortStatusEventBitfield, - /// Sender to send events to the power policy service - pub(crate) power_policy_sender: S, -} - -impl> PortState { - pub fn new(power_policy_sender: S) -> Self { - Self { - status: PortStatus::default(), - sw_status_event: PortStatusEventBitfield::default(), - power_policy_sender, - } - } -} - -/// Intermediate storage that holds power proxy devices -pub struct IntermediateStorage< - 'a, - const N: usize, - M: RawMutex, - S: event::Sender, -> { - storage: &'a Storage<'a, N, M>, - ports: [Port<'a, M, S>; N], -} - -impl<'a, const N: usize, M: RawMutex, S: event::Sender> - IntermediateStorage<'a, N, M, S> -{ - fn try_from_storage( - storage: &'a Storage<'a, N, M>, - power_policy_init: [(&'static str, S); N], - ) -> Option<(Self, [PowerProxyReceiver<'a>; N])> { - let mut ports = heapless::Vec::<_, N>::new(); - let mut power_proxy_receivers = heapless::Vec::<_, N>::new(); - - 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(policy_sender)), - }) - .ok()?; - power_proxy_receivers.push(power_proxy_channel.get_receiver()).ok()?; - } - - Some(( - Self { - storage, - ports: ports.into_array().ok()?, - }, - power_proxy_receivers.into_array().ok()?, - )) - } - - /// Create referenced storage from this intermediate storage - pub fn try_create_referenced<'b>(&'b self) -> Option> - where - 'b: 'a, - { - ReferencedStorage::try_from_intermediate(self) - } -} - -/// Contains any values that need to reference [`Storage`] -/// -/// To simplify usage, we use interior mutability through a ref cell to avoid having to declare the state -/// completely separately. -pub struct ReferencedStorage< - 'a, - const N: usize, - M: RawMutex, - S: event::Sender, -> { - intermediate: &'a IntermediateStorage<'a, N, M, S>, - pub pd_controller: type_c_interface::port::Device<'a>, - power_devices: [&'a Mutex>; N], -} - -impl<'a, const N: usize, M: RawMutex, S: event::Sender> - ReferencedStorage<'a, N, M, S> -{ - /// Create a new referenced storage from the given intermediate storage - fn try_from_intermediate(intermediate: &'a IntermediateStorage<'a, N, M, S>) -> Option { - Some(Self { - intermediate, - pd_controller: type_c_interface::port::Device::new( - intermediate.storage.controller_id, - intermediate.storage.pd_ports.as_slice(), - ), - // Panic safety: will not panic because array length is fixed by generic argument - #[allow(clippy::indexing_slicing)] - power_devices: from_fn(|i| &intermediate.ports[i].proxy), - }) - } - - /// Creates the backing, returns `None` if a backing has already been created - pub fn create_backing<'b>(&'b self) -> Backing<'b, M, S> - where - 'b: 'a, - { - Backing { - registration: Registration { - context: self.intermediate.storage.context, - pd_controller: &self.pd_controller, - power_devices: &self.power_devices, - }, - ports: &self.intermediate.ports, - } - } -} - -/// Wrapper around registration and type-erased state -pub struct Backing<'a, M: RawMutex, S: event::Sender> { - pub(crate) registration: Registration<'a, M>, - pub(crate) ports: &'a [Port<'a, M, S>], -} diff --git a/type-c-service/src/wrapper/dp.rs b/type-c-service/src/wrapper/dp.rs deleted file mode 100644 index a5c6740f7..000000000 --- a/type-c-service/src/wrapper/dp.rs +++ /dev/null @@ -1,24 +0,0 @@ -use super::ControllerWrapper; -use crate::wrapper::message::OutputDpStatusChanged; -use embassy_sync::blocking_mutex::raw::RawMutex; -use embedded_services::{event, sync::Lockable, trace}; -use embedded_usb_pd::{Error, LocalPortId}; -use type_c_interface::port::Controller; - -impl<'device, M: RawMutex, D: Lockable, S: event::Sender> - ControllerWrapper<'device, M, D, S> -where - D::Inner: Controller, -{ - /// Process a DisplayPort status update by retrieving the current DP status from the `controller` for the appropriate `port`. - pub(super) async fn process_dp_status_update( - &self, - controller: &mut D::Inner, - port: LocalPortId, - ) -> Result::BusError>> { - trace!("Processing DP status update event on port {}", port.0); - - let status = controller.get_dp_status(port).await?; - Ok(OutputDpStatusChanged { port, status }) - } -} diff --git a/type-c-service/src/wrapper/event_receiver.rs b/type-c-service/src/wrapper/event_receiver.rs deleted file mode 100644 index dff283dea..000000000 --- a/type-c-service/src/wrapper/event_receiver.rs +++ /dev/null @@ -1,211 +0,0 @@ -//! This module contains event receiver types for the controller wrapper. -use core::array; -use core::future::pending; -use core::pin::pin; -use embassy_futures::select::{Either3, select_slice, select3}; -use embassy_time::{Instant, Timer}; -use embedded_services::{debug, trace}; -use embedded_usb_pd::LocalPortId; - -use crate::PortEventStreamer; -use crate::wrapper::message::{Event, LocalPortEvent, PowerPolicyCommand}; -use crate::wrapper::proxy::PowerProxyReceiver; -use type_c_interface::port::event::{PortEvent, PortEventBitfield, PortStatusEventBitfield}; - -/// Trait used for receiving interrupt from the controller. -pub trait InterruptReceiver { - /// Wait for the next interrupt event. - fn wait_interrupt(&mut self) -> impl Future; -} - -/// Struct to receive and stream port events from the controller. -pub struct PortEventReceiver> { - /// Receiver for the controller's interrupt events - receiver: Receiver, - /// Port event streaming state - streaming_state: Option>>, -} - -impl> PortEventReceiver { - /// Create a new instance - pub fn new(receiver: Receiver) -> Self { - Self { - receiver, - streaming_state: None, - } - } - - /// Wait for the next port event - pub async fn wait_next(&mut self) -> LocalPortEvent { - loop { - let streaming_state = if let Some(streaming_state) = &mut self.streaming_state { - // Yield to ensure we don't monopolize the executor - embassy_futures::yield_now().await; - streaming_state - } else { - let events = self.receiver.wait_interrupt().await; - self.streaming_state.insert(PortEventStreamer::new(events.into_iter())) - }; - - if let Some((port_index, event)) = streaming_state.next() { - return LocalPortEvent { - port: LocalPortId(port_index as u8), - event, - }; - } else { - self.streaming_state = None; - } - } - } -} - -/// Struct to receive power policy messages. -pub struct ArrayPowerProxyEventReceiver<'device, const N: usize> { - receivers: [PowerProxyReceiver<'device>; N], -} - -impl<'device, const N: usize> ArrayPowerProxyEventReceiver<'device, N> { - /// Create a new array power proxy event receiver - pub fn new(receivers: [PowerProxyReceiver<'device>; N]) -> Self { - Self { receivers } - } - - /// Wait for the next power policy command - pub async fn wait_next(&mut self) -> PowerPolicyCommand { - let mut futures = heapless::Vec::<_, N>::new(); - for receiver in self.receivers.iter_mut() { - // Size is fixed at compile time, so no chance of overflow - let _ = futures.push(async { receiver.receive().await }); - } - - // DROP SAFETY: Select over drop safe futures - let (request, local_id) = select_slice(pin!(futures.as_mut_slice())).await; - trace!("Power command: device{} {:#?}", local_id, request); - PowerPolicyCommand { - port: LocalPortId(local_id as u8), - request, - } - } - - /// Temporary function until the conversion to direct function calls is complete - pub async fn send_response( - &mut self, - port: LocalPortId, - response: power_policy_interface::psu::InternalResponseData, - ) -> Result<(), ()> { - self.receivers.get_mut(port.0 as usize).ok_or(())?.send(response).await; - Ok(()) - } -} - -/// Struct to receive sink ready timeout events. -pub struct SinkReadyTimeoutEvent { - timeouts: [Option; N], -} - -impl SinkReadyTimeoutEvent { - /// Create a new instance - pub fn new() -> Self { - Self { timeouts: [None; N] } - } - - /// Set a timeout for a specific port - pub fn set_timeout(&mut self, port: LocalPortId, new_timeout: Instant) { - let index = port.0 as usize; - if let Some(timeout) = self.timeouts.get_mut(index) { - *timeout = Some(new_timeout); - } - } - - /// Clear the timeout for a specific port - pub fn clear_timeout(&mut self, port: LocalPortId) { - let index = port.0 as usize; - if let Some(timeout) = self.timeouts.get_mut(index) { - *timeout = None; - } - } - - pub fn get_timeout(&self, port: LocalPortId) -> Option { - let index = port.0 as usize; - self.timeouts.get(index).copied().flatten() - } - - /// Wait for a sink ready timeout and return the port that has timed out. - /// - /// DROP SAFETY: No state to restore - pub async fn wait_next(&mut self) -> LocalPortId { - let mut futures = heapless::Vec::<_, N>::new(); - for (i, timeout) in self.timeouts.iter().enumerate() { - let timeout = *timeout; - // Size is fixed at compile time, so no chance of overflow - let _ = futures.push(async move { - if let Some(timeout) = timeout { - Timer::at(timeout).await; - debug!("Port{}: Sink ready timeout reached", i); - } else { - pending::<()>().await; - } - }); - } - - // DROP SAFETY: Select over drop safe futures - let (_, port_index) = select_slice(pin!(futures.as_mut_slice())).await; - if let Some(timeout) = self.timeouts.get_mut(port_index) { - *timeout = None; - } - LocalPortId(port_index as u8) - } -} - -impl Default for SinkReadyTimeoutEvent { - fn default() -> Self { - Self::new() - } -} - -/// Struct used for containing controller event receivers. -pub struct ArrayPortEventReceivers<'device, const N: usize, PortInterrupts: InterruptReceiver> { - /// Port event receiver - pub port_events: PortEventReceiver, - /// Power proxy event receiver - pub power_proxies: ArrayPowerProxyEventReceiver<'device, N>, - /// Sink ready timeout event receiver - pub sink_ready_timeout: SinkReadyTimeoutEvent, -} - -impl<'device, const N: usize, PortInterrupts: InterruptReceiver> - ArrayPortEventReceivers<'device, N, PortInterrupts> -{ - /// Create a new instance - pub fn new(port_interrupts: PortInterrupts, power_proxies: [PowerProxyReceiver<'device>; N]) -> Self { - Self { - port_events: PortEventReceiver::new(port_interrupts), - power_proxies: ArrayPowerProxyEventReceiver::new(power_proxies), - sink_ready_timeout: SinkReadyTimeoutEvent::new(), - } - } - - /// Wait for the next port event from any port. - /// - /// Returns the local port ID and the event bitfield. - pub async fn wait_event(&mut self) -> Event { - match select3( - self.port_events.wait_next(), - self.power_proxies.wait_next(), - self.sink_ready_timeout.wait_next(), - ) - .await - { - Either3::First(event) => Event::PortEvent(event), - Either3::Second(command) => Event::PowerPolicyCommand(command), - Either3::Third(port) => { - let mut status_event = PortStatusEventBitfield::none(); - status_event.set_sink_ready(true); - Event::PortEvent(LocalPortEvent { - port, - event: PortEvent::StatusChanged(status_event), - }) - } - } - } -} diff --git a/type-c-service/src/wrapper/message.rs b/type-c-service/src/wrapper/message.rs deleted file mode 100644 index f0c8152b7..000000000 --- a/type-c-service/src/wrapper/message.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! [`crate::wrapper::ControllerWrapper`] message types -use embedded_usb_pd::{LocalPortId, ado::Ado}; - -use type_c_interface::{ - port::event::PortStatusEventBitfield, - port::{DpStatus, PortStatus}, -}; - -/// Port event -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct LocalPortEvent { - /// Port ID - pub port: LocalPortId, - /// Port event - pub event: type_c_interface::port::event::PortEvent, -} - -/// Power policy command event data -pub struct PowerPolicyCommand { - /// Port ID - pub port: LocalPortId, - /// Power policy request - pub request: power_policy_interface::psu::CommandData, -} - -/// Wrapper events -pub enum Event { - /// Port status changed - PortEvent(LocalPortEvent), - /// Power policy command received - PowerPolicyCommand(PowerPolicyCommand), -} - -/// Port status changed output data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct OutputPortStatusChanged { - /// Port ID - pub port: LocalPortId, - /// Status changed event - pub status_event: PortStatusEventBitfield, - /// Port status - pub status: PortStatus, -} - -/// PD alert output data -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct OutputPdAlert { - /// Port ID - pub port: LocalPortId, - /// ADO data - pub ado: Ado, -} - -/// Power policy command output data -pub struct OutputPowerPolicyCommand { - /// Port ID - pub port: LocalPortId, - /// Response - pub response: power_policy_interface::psu::InternalResponseData, -} - -pub mod vdm { - //! Events and output for vendor-defined messaging. - use type_c_interface::port::event::VdmData; - - use super::LocalPortId; - - /// Output from processing a vendor-defined message. - #[derive(Copy, Clone, Debug)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub struct Output { - /// The port that the VDM message is associated with. - pub port: LocalPortId, - /// VDM data - pub vdm_data: VdmData, - } -} - -/// DP status changed output data -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct OutputDpStatusChanged { - /// Port ID - pub port: LocalPortId, - /// Port status - pub status: DpStatus, -} - -/// [`crate::wrapper::ControllerWrapper`] output -pub enum Output { - /// No-op when nothing specific is needed - Nop, - /// Port status changed - PortStatusChanged(OutputPortStatusChanged), - /// PD alert - PdAlert(OutputPdAlert), - /// Vendor-defined messaging. - Vdm(vdm::Output), - /// Power policy command received - PowerPolicyCommand(OutputPowerPolicyCommand), - /// Dp status update - DpStatusUpdate(OutputDpStatusChanged), -} diff --git a/type-c-service/src/wrapper/mod.rs b/type-c-service/src/wrapper/mod.rs deleted file mode 100644 index 9a757059d..000000000 --- a/type-c-service/src/wrapper/mod.rs +++ /dev/null @@ -1,402 +0,0 @@ -//! This module contains the [`ControllerWrapper`] struct. This struct serves as a bridge between various service messages -//! and the actual controller functions provided by [`type_c_interface::port::Controller`]. -//! # Supported service messaging -//! This struct currently supports messages from the following services: -//! * Type-C: [`type_c_interface::port::Command`] -//! # Event loop -//! This struct follows a standard process/finalize event loop. -//! -//! [`ControllerWrapper::process_event`] reads any additional data relevant to the event and returns [`message::Output`]. -//! e.g. port status for a port status changed event, VDM data for a VDM event -//! -//! [`ControllerWrapper::finalize`] consumes [`message::Output`] and responds to any deferred requests, performs -//! any caching/buffering of data, and notifies the type-C service implementation of the event if needed. -use core::ops::DerefMut; - -use crate::wrapper::backing::PortState; -use crate::wrapper::event_receiver::{ArrayPowerProxyEventReceiver, SinkReadyTimeoutEvent}; -use embassy_sync::blocking_mutex::raw::RawMutex; -use embassy_sync::signal::Signal; -use embassy_time::Instant; -use embedded_services::event; -use embedded_services::sync::Lockable; -use embedded_services::{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::wrapper::message::*; - -pub mod backing; -pub mod config; -mod dp; -pub mod event_receiver; -pub mod message; -mod pd; -mod power; -pub mod proxy; -mod vdm; - -use type_c_interface::port::event::PortStatusEventBitfield; -use type_c_interface::port::{Controller, PortStatus}; - -/// Maximum number of supported ports -pub const MAX_SUPPORTED_PORTS: usize = 2; - -/// Common functionality implemented on top of [`type_c_interface::port::Controller`] -pub struct ControllerWrapper< - 'device, - M: RawMutex, - D: Lockable, - S: event::Sender, -> where - D::Inner: Controller, -{ - controller: &'device D, - /// Registration information for services - pub registration: backing::Registration<'device, M>, - /// SW port status event signal - sw_status_event: Signal, - /// General config - config: config::Config, - /// Port proxies - pub ports: &'device [backing::Port<'device, M, S>], -} - -impl<'device, M: RawMutex, D: Lockable, S: event::Sender> - ControllerWrapper<'device, M, D, S> -where - D::Inner: Controller, -{ - /// Create a new controller wrapper - pub fn new( - controller: &'device D, - config: config::Config, - storage: &'device backing::ReferencedStorage<'device, N, M, S>, - ) -> Self { - const { - assert!(N > 0 && N <= MAX_SUPPORTED_PORTS, "Invalid number of ports"); - }; - - let backing = storage.create_backing(); - Self { - controller, - config, - registration: backing.registration, - sw_status_event: Signal::new(), - ports: backing.ports, - } - } - - /// Get the cached port status, returns None if the port is invalid - pub async fn get_cached_port_status(&self, local_port: LocalPortId) -> Option { - let port = self.ports.get(local_port.0 as usize)?; - Some(port.state.lock().await.status) - } - - /// Synchronize the state between the controller and the internal state - pub async fn sync_state(&self) -> Result<(), Error<::BusError>> { - let mut controller = self.controller.lock().await; - self.sync_state_internal(&mut controller).await - } - - /// Synchronize the state between the controller and the internal state - async fn sync_state_internal( - &self, - controller: &mut D::Inner, - ) -> Result<(), Error<::BusError>> { - // Sync the controller state with the PD controller - for (i, port) in self.ports.iter().enumerate() { - let mut port_state = port.state.lock().await; - - let mut status_changed = port_state.sw_status_event; - let local_port = LocalPortId(i as u8); - let status = controller.get_port_status(local_port).await?; - trace!("Port{} status: {:#?}", i, status); - - let previous_status = port_state.status; - - if previous_status.is_connected() != status.is_connected() { - status_changed.set_plug_inserted_or_removed(true); - } - - if previous_status.available_sink_contract != status.available_sink_contract { - status_changed.set_new_power_contract_as_consumer(true); - } - - if previous_status.available_source_contract != status.available_source_contract { - status_changed.set_new_power_contract_as_provider(true); - } - - port_state.sw_status_event = status_changed; - 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(()); - } - } - Ok(()) - } - - /// Handle a plug event - async fn process_plug_event( - &self, - port_state: &mut PortState, - status: &PortStatus, - ) -> Result<(), Error<::BusError>> { - info!("Plug event"); - if status.is_connected() { - info!("Plug inserted"); - port_state - .power_policy_sender - .send(power_policy_interface::psu::event::EventData::Attached) - .await; - } else { - info!("Plug removed"); - port_state - .power_policy_sender - .send(power_policy_interface::psu::event::EventData::Detached) - .await; - } - - Ok(()) - } - - /// Process port status changed events - async fn process_port_status_changed( - &self, - sink_ready_timeout: &mut SinkReadyTimeoutEvent, - controller: &mut D::Inner, - local_port_id: LocalPortId, - status_event: PortStatusEventBitfield, - ) -> Result::BusError>> { - let global_port_id = self - .registration - .pd_controller - .lookup_global_port(local_port_id) - .map_err(Error::Pd)?; - - let mut port_state = self - .ports - .get(local_port_id.0 as usize) - .ok_or(Error::Pd(PdError::InvalidPort))? - .state - .lock() - .await; - - let status = controller.get_port_status(local_port_id).await?; - trace!("Port{} status: {:#?}", global_port_id.0, status); - trace!("Port{} status events: {:#?}", global_port_id.0, status_event); - - if status_event.plug_inserted_or_removed() { - self.process_plug_event(&mut port_state, &status).await?; - } - - // Only notify power policy of a contract after Sink Ready event (always after explicit or implicit contract) - if status_event.sink_ready() { - self.process_new_consumer_contract(&mut port_state, &status).await?; - } - - if status_event.new_power_contract_as_provider() { - self.process_new_provider_contract(&mut port_state, &status).await?; - } - - self.check_sink_ready_timeout( - sink_ready_timeout, - &port_state.status, - &status, - local_port_id, - status_event.new_power_contract_as_consumer(), - status_event.sink_ready(), - )?; - - Ok(Output::PortStatusChanged(OutputPortStatusChanged { - port: local_port_id, - status_event, - status, - })) - } - - /// Finalize a port status change output - async fn finalize_port_status_change( - &self, - local_port: LocalPortId, - status_event: PortStatusEventBitfield, - status: PortStatus, - ) -> Result<(), Error<::BusError>> { - let global_port_id = self - .registration - .pd_controller - .lookup_global_port(local_port) - .map_err(Error::Pd)?; - - self.ports - .get(local_port.0 as usize) - .ok_or(Error::Pd(PdError::InvalidPort))? - .state - .lock() - .await - .status = status; - - 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 - async fn finalize_pd_alert( - &self, - local_port: LocalPortId, - alert: Ado, - ) -> Result<(), Error<::BusError>> { - let global_port_id = self - .registration - .pd_controller - .lookup_global_port(local_port) - .map_err(Error::Pd)?; - - self.registration - .context - .send_port_event(ServicePortEvent { - port: global_port_id, - event: ServicePortEventData::Alert(alert), - }) - .await - .map_err(Error::Pd) - } - - /// Process a port notification - async fn process_port_event( - &self, - sink_ready_timeout: &mut SinkReadyTimeoutEvent, - controller: &mut D::Inner, - event: LocalPortEvent, - ) -> Result::BusError>> { - match event.event { - InterfacePortEvent::StatusChanged(status_event) => { - self.process_port_status_changed(sink_ready_timeout, 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: event.port, ado })) - } else { - // For some reason we didn't read an alert, nothing to do - Ok(Output::Nop) - } - } - 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: {:#?}", event.port.0, rest); - Ok(Output::Nop) - } - } - } - - /// Top-level processing function - /// Only call this fn from one place in a loop. Otherwise a deadlock could occur. - pub async fn process_event( - &self, - sink_ready_timeout: &mut SinkReadyTimeoutEvent, - event: Event, - ) -> Result::BusError>> { - let mut controller = self.controller.lock().await; - match event { - Event::PortEvent(port_event) => { - self.process_port_event(sink_ready_timeout, &mut controller, port_event) - .await - } - Event::PowerPolicyCommand(PowerPolicyCommand { port, request }) => { - let response = self.process_power_command(&mut controller, port, &request).await; - Ok(Output::PowerPolicyCommand(OutputPowerPolicyCommand { port, response })) - } - } - } - - /// Event loop finalize - pub async fn finalize( - &self, - event_receiver: &mut ArrayPowerProxyEventReceiver<'device, N>, - output: Output, - ) -> Result<(), Error<::BusError>> { - match output { - Output::Nop => Ok(()), - Output::PortStatusChanged(OutputPortStatusChanged { - port, - status_event, - status, - }) => self.finalize_port_status_change(port, status_event, status).await, - Output::PdAlert(OutputPdAlert { port, ado }) => self.finalize_pd_alert(port, ado).await, - Output::Vdm(vdm) => self.finalize_vdm(vdm).await.map_err(Error::Pd), - Output::PowerPolicyCommand(OutputPowerPolicyCommand { port, response }) => { - event_receiver - .send_response(port, response) - .await - .map_err(|_| Error::Pd(PdError::Failed))?; - Ok(()) - } - Output::DpStatusUpdate(_) => { - // Nothing to do here - Ok(()) - } - } - } - - /// Combined processing and finialization function - pub async fn process_and_finalize_event( - &self, - sink_ready_timeout: &mut SinkReadyTimeoutEvent, - power_event_receiver: &mut ArrayPowerProxyEventReceiver<'device, N>, - event: Event, - ) -> Result<(), Error<::BusError>> { - let output = self.process_event(sink_ready_timeout, event).await?; - self.finalize(power_event_receiver, output).await - } - - /// Register all devices with their respective services - pub fn register(&'static self) -> Result<(), Error<::BusError>> { - self.registration - .context - .register_controller(self.registration.pd_controller) - .map_err(|_| { - error!( - "Controller{}: Failed to register PD controller", - self.registration.pd_controller.id().0 - ); - Error::Pd(PdError::Failed) - })?; - Ok(()) - } -} - -impl<'device, M: RawMutex, C: Lockable, S: event::Sender> Lockable - for ControllerWrapper<'device, M, C, S> -where - ::Inner: Controller, -{ - type Inner = C::Inner; - - fn try_lock(&self) -> Option> { - self.controller.try_lock() - } - - fn lock(&self) -> impl Future> { - self.controller.lock() - } -} diff --git a/type-c-service/src/wrapper/pd.rs b/type-c-service/src/wrapper/pd.rs deleted file mode 100644 index 641608cd2..000000000 --- a/type-c-service/src/wrapper/pd.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::wrapper::event_receiver::SinkReadyTimeoutEvent; -use embassy_time::Duration; -use embedded_services::debug; -use embedded_usb_pd::constants::{T_PS_TRANSITION_EPR_MS, T_PS_TRANSITION_SPR_MS}; - -use super::*; - -impl<'device, M: RawMutex, D: Lockable, S: event::Sender> - ControllerWrapper<'device, M, D, S> -where - D::Inner: Controller, -{ - /// Check the sink ready timeout - /// - /// After accepting a sink contract (new contract as consumer), the PD spec guarantees that the - /// source will be available to provide power after `tPSTransition`. This allows us to handle transitions - /// even for controllers that might not always broadcast sink ready events. - pub(super) fn check_sink_ready_timeout( - &self, - sink_ready_timeout: &mut SinkReadyTimeoutEvent, - previous_status: &PortStatus, - new_status: &PortStatus, - port: LocalPortId, - new_contract: bool, - sink_ready: bool, - ) -> Result<(), PdError> { - let contract_changed = previous_status.available_sink_contract != new_status.available_sink_contract; - let timeout = sink_ready_timeout.get_timeout(port); - - // Don't start the timeout if the sink has signaled it's ready or if the contract didn't change. - // The latter ensures that soft resets won't continually reset the ready timeout - debug!( - "Port{}: Check sink ready: new_contract={:?}, sink_ready={:?}, contract_changed={:?}, deadline={:?}", - port.0, new_contract, sink_ready, contract_changed, timeout, - ); - if new_contract && !sink_ready && contract_changed { - // Start the timeout - // Double the spec maximum transition time to provide a safety margin for hardware/controller delays or out-of-spec controllers. - let timeout_ms = if new_status.epr { - T_PS_TRANSITION_EPR_MS - } else { - T_PS_TRANSITION_SPR_MS - } - .maximum - .0 * 2; - - debug!("Port{}: Sink ready timeout started for {}ms", port.0, timeout_ms); - sink_ready_timeout.set_timeout(port, Instant::now() + Duration::from_millis(timeout_ms as u64)); - } else if timeout.is_some() - && (!new_status.is_connected() || new_status.available_sink_contract.is_none() || sink_ready) - { - debug!("Port{}: Sink ready timeout cleared", port.0); - sink_ready_timeout.clear_timeout(port); - } - Ok(()) - } -} diff --git a/type-c-service/src/wrapper/power.rs b/type-c-service/src/wrapper/power.rs deleted file mode 100644 index b52b7942c..000000000 --- a/type-c-service/src/wrapper/power.rs +++ /dev/null @@ -1,126 +0,0 @@ -//! Module contain power-policy related message handling -use crate::util::power_policy_error_from_pd_bus_error; -use crate::wrapper::config::UnconstrainedSink; -use power_policy_interface::capability::{ConsumerPowerCapability, ProviderPowerCapability, PsuType}; -use power_policy_interface::psu::CommandData as PowerCommand; -use power_policy_interface::psu::{CommandData, InternalResponseData, ResponseData}; - -use super::*; - -impl<'device, M: RawMutex, D: Lockable, S: event::Sender> - ControllerWrapper<'device, M, D, S> -where - D::Inner: Controller, -{ - /// Handle a new contract as consumer - pub(super) async fn process_new_consumer_contract( - &self, - port_state: &mut PortState, - status: &PortStatus, - ) -> Result<(), Error<::BusError>> { - info!("Process new consumer contract"); - let available_sink_contract = status.available_sink_contract.map(|c| { - let mut c: ConsumerPowerCapability = c.into(); - let unconstrained = match self.config.unconstrained_sink { - UnconstrainedSink::Auto => status.unconstrained_power, - UnconstrainedSink::PowerThresholdMilliwatts(threshold) => c.capability.max_power_mw() >= threshold, - UnconstrainedSink::Never => false, - }; - c.flags.set_unconstrained_power(unconstrained); - c.flags.set_psu_type(PsuType::TypeC); - c - }); - - port_state - .power_policy_sender - .send(power_policy_interface::psu::event::EventData::UpdatedConsumerCapability(available_sink_contract)) - .await; - Ok(()) - } - - /// Handle a new contract as provider - pub(super) async fn process_new_provider_contract( - &self, - port_state: &mut PortState, - status: &PortStatus, - ) -> Result<(), Error<::BusError>> { - info!("Process New provider contract"); - port_state - .power_policy_sender - .send( - power_policy_interface::psu::event::EventData::RequestedProviderCapability( - status.available_source_contract.map(|caps| { - let mut caps = ProviderPowerCapability::from(caps); - caps.flags.set_psu_type(PsuType::TypeC); - caps - }), - ), - ) - .await; - Ok(()) - } - - /// Handle a disconnect command - async fn process_disconnect( - &self, - port: LocalPortId, - controller: &mut D::Inner, - ) -> Result<(), Error<::BusError>> { - if controller.enable_sink_path(port, false).await.is_err() { - error!("Error disabling sink path"); - return PdError::Failed.into(); - } - Ok(()) - } - - /// Handle a connect as provider command - fn process_connect_as_provider( - &self, - port: LocalPortId, - capability: ProviderPowerCapability, - _controller: &mut D::Inner, - ) -> Result<(), Error<::BusError>> { - info!("Port{}: Connect as provider: {:#?}", port.0, capability); - // TODO: double check explicit contract handling - Ok(()) - } - - /// Process a power command - /// Returns no error because this is a top-level function - pub(super) async fn process_power_command( - &self, - controller: &mut D::Inner, - port: LocalPortId, - command: &CommandData, - ) -> InternalResponseData { - trace!("Processing power command: device{} {:#?}", port.0, command); - - match command { - PowerCommand::ConnectAsConsumer(capability) => { - info!( - "Port{}: Connect as consumer: {:?}, enable input switch", - port.0, capability - ); - controller.enable_sink_path(port, true).await.map_err(|e| { - error!("Error enabling sink path"); - power_policy_error_from_pd_bus_error(e) - })?; - } - PowerCommand::ConnectAsProvider(capability) => { - self.process_connect_as_provider(port, *capability, controller) - .map_err(|e| { - error!("Error processing connect provider"); - power_policy_error_from_pd_bus_error(e) - })?; - } - PowerCommand::Disconnect => { - self.process_disconnect(port, controller).await.map_err(|e| { - error!("Error processing disconnect"); - power_policy_error_from_pd_bus_error(e) - })?; - } - } - - Ok(ResponseData::Complete) - } -} diff --git a/type-c-service/src/wrapper/proxy.rs b/type-c-service/src/wrapper/proxy.rs deleted file mode 100644 index 170fd55d3..000000000 --- a/type-c-service/src/wrapper/proxy.rs +++ /dev/null @@ -1,128 +0,0 @@ -use embassy_sync::blocking_mutex::raw::RawMutex; -use embassy_sync::channel::{Channel, DynamicReceiver, DynamicSender}; -use embedded_services::named::Named; -use power_policy_interface::psu::{CommandData as PolicyCommandData, InternalResponseData as PolicyResponseData, Psu}; - -pub struct PowerProxyChannel { - command_channel: Channel, - response_channel: Channel, -} - -impl PowerProxyChannel { - pub fn new() -> Self { - Self { - command_channel: Channel::new(), - response_channel: Channel::new(), - } - } - - pub fn get_device_components( - &self, - ) -> ( - DynamicSender<'_, PolicyCommandData>, - DynamicReceiver<'_, PolicyResponseData>, - ) { - (self.command_channel.dyn_sender(), self.response_channel.dyn_receiver()) - } - - pub fn get_receiver(&self) -> PowerProxyReceiver<'_> { - PowerProxyReceiver { - receiver: self.command_channel.dyn_receiver(), - sender: self.response_channel.dyn_sender(), - } - } -} - -pub struct PowerProxyReceiver<'a> { - sender: DynamicSender<'a, PolicyResponseData>, - receiver: DynamicReceiver<'a, PolicyCommandData>, -} - -impl<'a> PowerProxyReceiver<'a> { - pub fn new( - receiver: DynamicReceiver<'a, PolicyCommandData>, - sender: DynamicSender<'a, PolicyResponseData>, - ) -> Self { - Self { receiver, sender } - } - - pub async fn receive(&mut self) -> PolicyCommandData { - self.receiver.receive().await - } - - pub async fn send(&mut self, response: PolicyResponseData) { - self.sender.send(response).await; - } -} - -pub struct PowerProxyDevice<'a> { - sender: DynamicSender<'a, PolicyCommandData>, - receiver: DynamicReceiver<'a, PolicyResponseData>, - /// Per-port PSU state - pub(crate) psu_state: power_policy_interface::psu::State, - name: &'static str, -} - -impl<'a> PowerProxyDevice<'a> { - pub fn new( - name: &'static str, - sender: DynamicSender<'a, PolicyCommandData>, - receiver: DynamicReceiver<'a, PolicyResponseData>, - ) -> Self { - Self { - name, - sender, - receiver, - psu_state: power_policy_interface::psu::State::default(), - } - } - - async fn execute(&mut self, command: PolicyCommandData) -> PolicyResponseData { - self.sender.send(command).await; - self.receiver.receive().await - } -} - -impl<'a> Psu for PowerProxyDevice<'a> { - async fn disconnect(&mut self) -> Result<(), power_policy_interface::psu::Error> { - self.execute(PolicyCommandData::Disconnect).await?.complete_or_err() - } - - async fn connect_provider( - &mut self, - capability: power_policy_interface::capability::ProviderPowerCapability, - ) -> Result<(), power_policy_interface::psu::Error> { - self.execute(PolicyCommandData::ConnectAsProvider(capability)) - .await? - .complete_or_err() - } - - async fn connect_consumer( - &mut self, - capability: power_policy_interface::capability::ConsumerPowerCapability, - ) -> Result<(), power_policy_interface::psu::Error> { - self.execute(PolicyCommandData::ConnectAsConsumer(capability)) - .await? - .complete_or_err() - } - - fn state(&self) -> &power_policy_interface::psu::State { - &self.psu_state - } - - fn state_mut(&mut self) -> &mut power_policy_interface::psu::State { - &mut self.psu_state - } -} - -impl<'a> Named for PowerProxyDevice<'a> { - fn name(&self) -> &'static str { - self.name - } -} - -impl Default for PowerProxyChannel { - fn default() -> Self { - Self::new() - } -} diff --git a/type-c-service/src/wrapper/vdm.rs b/type-c-service/src/wrapper/vdm.rs deleted file mode 100644 index 2f6baebfa..000000000 --- a/type-c-service/src/wrapper/vdm.rs +++ /dev/null @@ -1,47 +0,0 @@ -use embassy_sync::blocking_mutex::raw::RawMutex; -use embedded_services::{event, sync::Lockable, trace}; -use embedded_usb_pd::{Error, LocalPortId, PdError}; - -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, message::vdm::Output}; - -impl<'device, M: RawMutex, D: Lockable, S: event::Sender> - ControllerWrapper<'device, M, D, S> -where - D::Inner: Controller, -{ - /// Process a VDM event by retrieving the relevant VDM data from the `controller` for the appropriate `port`. - pub(super) async fn process_vdm_event( - &self, - controller: &mut D::Inner, - port: LocalPortId, - event: VdmNotification, - ) -> Result::BusError>> { - trace!("Processing VDM event: {:?} on port {}", event, port.0); - let kind = match event { - 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, 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, vdm_data } = output; - let global_port_id = self.registration.pd_controller.lookup_global_port(port)?; - self.registration - .context - .send_port_event(PortEvent { - port: global_port_id, - event: PortEventData::Vdm(vdm_data), - }) - .await - } -}