diff --git a/src/asynchronous/embassy/mod.rs b/src/asynchronous/embassy/mod.rs index a7671be..26a00cb 100644 --- a/src/asynchronous/embassy/mod.rs +++ b/src/asynchronous/embassy/mod.rs @@ -1,3 +1,4 @@ +//! This module contains a high-level API uses embassy synchronization types use core::iter::zip; use core::sync::atomic::AtomicBool; @@ -26,14 +27,20 @@ pub mod controller { use super::*; + /// Controller struct. This struct is meant to be created and then immediately broken into its parts pub struct Controller { + /// Low-level TPS6699x driver pub(super) inner: Mutex>, + /// Signal for awaiting an interrupt pub(super) interrupt_waker: Signal, + /// Current interrupt state pub(super) interrupts_enabled: [AtomicBool; MAX_SUPPORTED_PORTS], + /// Number of active ports pub(super) num_ports: usize, } impl Controller { + /// Private constructor pub fn new(bus: B, addr: [u8; MAX_SUPPORTED_PORTS], num_ports: usize) -> Result> { Ok(Self { inner: Mutex::new(internal::Tps6699x::new(bus, addr, num_ports)), @@ -43,26 +50,31 @@ pub mod controller { }) } + /// Create a new controller for the TPS66993 pub fn new_tps66993(bus: B, addr: u8) -> Result> { Self::new(bus, [addr, 0], TPS66993_NUM_PORTS) } + /// Create a new controller for the TPS66994 pub fn new_tps66994(bus: B, addr: [u8; TPS66994_NUM_PORTS]) -> Result> { Self::new(bus, addr, TPS66994_NUM_PORTS) } + /// Breaks the controller into its parts pub fn make_parts(&mut self) -> (Tps6699x<'_, M, B>, Interrupt<'_, M, B>) { let tps = Tps6699x { controller: self }; let interrupt = Interrupt { controller: self }; (tps, interrupt) } + /// Enable or disable interrupts for the given ports pub(super) fn enable_interrupts(&self, enabled: [bool; MAX_SUPPORTED_PORTS]) { for (enabled, s) in zip(enabled.iter(), self.interrupts_enabled.iter()) { s.store(*enabled, core::sync::atomic::Ordering::SeqCst); } } + /// Returns current interrupt state pub(super) fn interrupts_enabled(&self) -> [bool; MAX_SUPPORTED_PORTS] { let mut interrupts_enabled = [false; MAX_SUPPORTED_PORTS]; for (copy, enabled) in zip(interrupts_enabled.iter_mut(), self.interrupts_enabled.iter()) { @@ -74,11 +86,13 @@ pub mod controller { } } +/// Struct for controlling a TP6699x device pub struct Tps6699x<'a, M: RawMutex, B: I2c> { controller: &'a controller::Controller, } impl<'a, M: RawMutex, B: I2c> Tps6699x<'a, M, B> { + /// Locks the inner device async fn lock_inner(&mut self) -> MutexGuard<'_, M, internal::Tps6699x> { self.controller.inner.lock().await } @@ -119,6 +133,7 @@ impl<'a, M: RawMutex, B: I2c> Tps6699x<'a, M, B> { self.lock_inner().await.get_customer_use().await } + /// Returns the number of ports pub fn num_ports(&self) -> usize { self.controller.num_ports } @@ -166,7 +181,6 @@ impl<'a, M: RawMutex, B: I2c> Tps6699x<'a, M, B> { } /// Execute the given command with a timeout - #[allow(dead_code)] async fn execute_command( &mut self, port: PortId, @@ -224,6 +238,7 @@ impl<'a, M: RawMutex, B: I2c> Interrupt<'a, M, B> { self.controller.inner.lock().await } + /// Process interrupts pub async fn process_interrupt( &mut self, int: &mut impl InputPin, @@ -240,13 +255,13 @@ impl<'a, M: RawMutex, B: I2c> Interrupt<'a, M, B> { continue; } - // Early exit if checking the last port cleared the interrupt let result = int.is_high(); if result.is_err() { error!("Failed to read interrupt line"); return PdError::Failed.into(); } + // Early exit if checking the last port cleared the interrupt if result.unwrap() { continue; } diff --git a/src/asynchronous/embassy/task.rs b/src/asynchronous/embassy/task.rs index 7d404c8..42ca8f2 100644 --- a/src/asynchronous/embassy/task.rs +++ b/src/asynchronous/embassy/task.rs @@ -6,6 +6,7 @@ use embedded_hal_async::i2c::I2c; use super::Interrupt; use crate::{error, warn}; +/// Task to process all given interrupts pub async fn interrupt_task( int: &mut INT, mut interrupts: [&mut Interrupt<'_, M, B>; N], diff --git a/src/asynchronous/fw_update.rs b/src/asynchronous/fw_update.rs index e3047e3..e5fe5fc 100644 --- a/src/asynchronous/fw_update.rs +++ b/src/asynchronous/fw_update.rs @@ -13,33 +13,46 @@ use crate::{error, info, warn, PORT0}; /// Trait for updating the firmware of a target device pub trait UpdateTarget: InterruptController { + /// Enter FW update mode fn fw_update_mode_enter( &mut self, delay: &mut impl DelayNs, ) -> impl Future>>; + + /// Start FW update fn fw_update_init( &mut self, delay: &mut impl DelayNs, args: &TfuiArgs, ) -> impl Future>>; + + /// Abort FW update fn fw_update_mode_exit( &mut self, delay: &mut impl DelayNs, ) -> impl Future>>; + + /// Validate the most recent block supplied to the device fn fw_update_validate_stream( &mut self, delay: &mut impl DelayNs, block_index: usize, ) -> impl Future>>; + + /// Stream a block to the device fn fw_update_stream_data( &mut self, delay: &mut impl DelayNs, args: &TfudArgs, ) -> impl Future>>; + + /// Complete the FW update process fn fw_update_complete( &mut self, delay: &mut impl DelayNs, ) -> impl Future>>; + + /// Write data to all supplied devices fn fw_update_burst_write( &mut self, address: u8, @@ -50,6 +63,7 @@ pub trait UpdateTarget: InterruptController { /// Trait for anything that can be used as an image for firmware update pub trait Image: Read + Seek {} +/// Error type for the firmware update process #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -134,6 +148,7 @@ async fn fw_update_init( let (args, _) = bincode::decode_from_slice(&arg_bytes, config::standard().with_fixed_int_encoding()) .map_err(|_| Error::Pd(PdError::Serialize))?; + // Initialize FW update on all controllers for (i, controller) in controllers.iter_mut().enumerate() { info!("Controller {}: Initializing FW update", i); @@ -158,6 +173,7 @@ async fn fw_update_init( .await .map_err(Error::Io)?; + // Supply the header block to all controllers info!("Broadcasting header block"); for _ in 0..HEADER_BLOCK_LEN / BURST_WRITE_SIZE { image.read_exact(&mut buf).await.map_err(Error::ReadExact)?; @@ -169,6 +185,7 @@ async fn fw_update_init( delay.delay_ms(TFUI_BURST_WRITE_DELAY_MS).await; + // Validate the block on all controllers for (i, controller) in controllers.iter_mut().enumerate() { info!("Controller {}: Validating header block", i); match controller.fw_update_validate_stream(delay, HEADER_BLOCK_INDEX).await { @@ -189,25 +206,29 @@ async fn fw_update_init( Ok(args) } +/// Computes the offset of a data block's metadata const fn data_block_metadata_offset(block: usize) -> usize { HEADER_BLOCK_OFFSET + HEADER_BLOCK_LEN + (block * (DATA_BLOCK_LEN + DATA_BLOCK_METADATA_LEN)) } +/// Computes the offset of a data block's data const fn block_offset(metadata_offset: usize) -> usize { metadata_offset + DATA_BLOCK_METADATA_LEN } +/// Computes the offset of the app config block's metadata const fn app_config_block_metadata_offset(num_data_blocks: usize, app_size: usize) -> usize { app_size + IMAGE_ID_LEN + HEADER_METADATA_LEN + HEADER_BLOCK_LEN + num_data_blocks * DATA_BLOCK_METADATA_LEN } +/// Get the size of the image async fn get_image_size(image: &mut I) -> Result> { let mut image_size_data = [0; 4]; read_from_exact(image, APP_IMAGE_SIZE_OFFSET, &mut image_size_data).await?; Ok(u32::from_le_bytes(image_size_data) as usize) } -// Data block indices start at 1 +/// Converts a data block into a block index fn data_block_index_to_block_index(block_index: usize) -> usize { block_index + DATA_BLOCK_START_INDEX } @@ -227,6 +248,7 @@ async fn fw_update_stream_data( let mut arg_bytes = [0u8; MAX_METADATA_LEN]; + // Get TFUd args from image read_from_exact(image, metadata_offset, &mut arg_bytes[..metadata_size]) .await .map_err(Error::ReadExact)?; @@ -262,6 +284,7 @@ async fn fw_update_stream_data( delay.delay_ms(TFUD_BURST_WRITE_DELAY_MS).await; + // Validate the block on all controllers for (i, controller) in controllers.iter_mut().enumerate() { info!("Controller {}: Validating block {}", i, block_index); match controller.fw_update_validate_stream(delay, block_index).await { diff --git a/src/asynchronous/internal/command.rs b/src/asynchronous/internal/command.rs index 354d661..9f383fe 100644 --- a/src/asynchronous/internal/command.rs +++ b/src/asynchronous/internal/command.rs @@ -58,6 +58,7 @@ impl Tps6699x { Ok(()) } + /// Check if the command has completed pub async fn check_command_complete(&mut self, port: PortId) -> Result> { let mut registers = self.borrow_port(port)?.into_registers(); let status = registers.cmd_1().read_async().await?.command(); @@ -65,6 +66,7 @@ impl Tps6699x { Ok(Command::Success == status) } + /// Read the result of a command pub async fn read_command_result( &mut self, port: PortId, diff --git a/src/asynchronous/internal/mod.rs b/src/asynchronous/internal/mod.rs index 2685718..6899515 100644 --- a/src/asynchronous/internal/mod.rs +++ b/src/asynchronous/internal/mod.rs @@ -1,4 +1,4 @@ -//! Asynchronous TPS6699x driver +//! Asynchronous, low-level TPS6699x driver. This module provides a low-level interface use embedded_hal_async::i2c::I2c; use embedded_usb_pd::{Error, PdError, PortId}; diff --git a/src/command.rs b/src/command.rs index 3d9e8a6..c3da2ba 100644 --- a/src/command.rs +++ b/src/command.rs @@ -4,6 +4,7 @@ use bincode::error::{DecodeError, EncodeError}; use bincode::{Decode, Encode}; use embedded_usb_pd::PdError; +/// Length of a command const CMD_LEN: usize = 4; /// Converts a 4-byte string into a u32 @@ -132,15 +133,20 @@ impl Into> for ReturnValue { } } +/// Delay to wait for the device to restart pub(crate) const RESET_DELAY_MS: u32 = 1600; +/// Length of arguments for the reset command pub(crate) const RESET_ARGS_LEN: usize = 2; +/// Constant to enable a feature in the command args pub(crate) const RESET_FEATURE_ENABLE: u8 = 0xAC; /// Arugments to reset-like commands #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetArgs { + /// True to swap banks on reset pub switch_banks: bool, + /// True to copy the backup bank to the active bank pub copy_bank: bool, } impl Encode for ResetArgs { @@ -157,20 +163,26 @@ pub(crate) const TFUS_DELAY_MS: u32 = 500; /// Timeout for completion of TFUs command #[allow(dead_code)] pub(crate) const TFUS_TIMEOUT_MS: u32 = TFUS_DELAY_MS + 100; +/// Timeout for completion of TFUi command #[allow(dead_code)] pub(crate) const TFUI_TIMEOUT_MS: u32 = 100; +/// Timeout for completion of TFUe command #[allow(dead_code)] pub(crate) const TFUE_TIMEOUT_MS: u32 = 100; +/// Timeout for completion of TFUd command #[allow(dead_code)] pub(crate) const TFUD_TIMEOUT_MS: u32 = 100; +/// Timeout for completion of TFUq command #[allow(dead_code)] pub(crate) const TFUQ_TIMEOUT_MS: u32 = 100; +/// Timeout for completion of reset #[allow(dead_code)] pub(crate) const RESET_TIMEOUT_MS: u32 = RESET_DELAY_MS + 100; - +/// Length of TFUi arguments #[allow(dead_code)] pub(crate) const TFUI_ARGS_LEN: usize = 8; +/// Arguments for TFUi command #[derive(Debug, Clone, Copy, Decode, Encode, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TfuiArgs { @@ -180,6 +192,7 @@ pub struct TfuiArgs { pub broadcast_u16_address: u16, } +/// Command type for TFUq command #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] @@ -194,6 +207,7 @@ impl Encode for TfuqCommandType { } } +/// Status we're checking for in the TFUq command #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] @@ -211,6 +225,7 @@ impl Encode for TfuqStatusQuery { } } +/// Status of a block supplied to device #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] @@ -288,9 +303,11 @@ impl Decode for TfuqBlockStatus { } } +/// Length of TFUq args #[allow(dead_code)] pub(crate) const TFUQ_ARGS_LEN: usize = 2; +/// Arguments for TFUq command #[derive(Debug, Encode, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TfuqArgs { @@ -298,11 +315,14 @@ pub struct TfuqArgs { pub command: TfuqCommandType, } +/// Length of return data from TFUq command #[allow(dead_code)] pub(crate) const TFUQ_RETURN_LEN: usize = 40; +/// Number of block statuses present in TFUq return data #[allow(dead_code)] pub(crate) const TFUQ_RETURN_BLOCK_STATUS_LEN: usize = 13; +/// Return data from TFUq command #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TfuqReturnValue { @@ -346,7 +366,7 @@ impl Decode for TfuqReturnValue { } } -#[allow(dead_code)] +/// Arguments for TFUd command pub(crate) const TFUD_ARGS_LEN: usize = 8; #[derive(Debug, Decode, Encode, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/src/fw_update.rs b/src/fw_update.rs index f9f13ea..62c9f65 100644 --- a/src/fw_update.rs +++ b/src/fw_update.rs @@ -1,21 +1,37 @@ +/// Header block index pub const HEADER_BLOCK_INDEX: usize = 0; +/// Data block start index pub const DATA_BLOCK_START_INDEX: usize = 1; +/// App config block index pub const APP_CONFIG_BLOCK_INDEX: usize = 12; +/// Image ID length pub const IMAGE_ID_LEN: usize = 4; +/// App image size offset pub const APP_IMAGE_SIZE_OFFSET: usize = 0x4F8; +/// Header metadata offset pub const HEADER_METADATA_OFFSET: usize = IMAGE_ID_LEN; +/// Header metadata length pub const HEADER_METADATA_LEN: usize = 8; +/// Header block data offset pub const HEADER_BLOCK_OFFSET: usize = HEADER_METADATA_OFFSET + HEADER_METADATA_LEN; +/// Header block length pub const HEADER_BLOCK_LEN: usize = 0x800; +/// Data block length pub const DATA_BLOCK_LEN: usize = 0x4000; +/// Data block metadata length pub const DATA_BLOCK_METADATA_LEN: usize = 8; +/// App config metadata length pub const APP_CONFIG_METADATA_LEN: usize = 8; +/// Maximum metadata length pub const MAX_METADATA_LEN: usize = 8; +/// Delay after sending burst write for TFUi command pub const TFUI_BURST_WRITE_DELAY_MS: u32 = 250; +/// Delay after sending burst write for TFUd command pub const TFUD_BURST_WRITE_DELAY_MS: u32 = 150; +/// Max data to send in a single burst write pub const BURST_WRITE_SIZE: usize = 256; diff --git a/src/lib.rs b/src/lib.rs index 7ae5974..dd0d6eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,11 +12,16 @@ pub const ADDR0: [u8; 2] = [0x20, 0x24]; /// I2C address set 1 pub const ADDR1: [u8; 2] = [0x21, 0x25]; +/// Number of ports present on the TPS66994 pub const TPS66994_NUM_PORTS: usize = 2; +/// Number of ports present on the TPS66993 pub const TPS66993_NUM_PORTS: usize = 1; +/// Maximum number of ports supported by any device pub const MAX_SUPPORTED_PORTS: usize = 2; +/// Port 0 constant pub const PORT0: PortId = PortId(0); +/// Port 1 constant pub const PORT1: PortId = PortId(1); pub mod registers {