Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "embedded-cfu-protocol"
version = "0.1.0"
version = "0.2.0"
edition = "2021"

[dependencies]
Expand Down
34 changes: 6 additions & 28 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use super::*;
use core::future::Future;

use crate::components::CfuComponentTraits;

/// CfuReceiveContent trait defines behavior needed for a Cfu client (receiver) to process CFU commands
/// E is an error type that can be defined by the implementor
Expand All @@ -7,37 +9,13 @@ use super::*;
pub trait CfuReceiveContent<T, C, E: Default> {
/// receives a CFU command from a Host and processes the contents
/// Typestates here allow for flexible implementations
fn process_command(&self, args: Option<T>, cmd: C) -> impl Future<Output = Result<(), E>> {
default_process_command::<T, C, E>(args, cmd)
}
fn process_command(&self, args: Option<T>, cmd: C) -> impl Future<Output = Result<(), E>>;

/// For all components, run their storage_prepare() method
/// Typestates here allow for flexible implementations
fn prepare_components(
&self,
args: Option<T>,
primary_component: impl CfuComponentTraits,
) -> impl Future<Output = Result<(), E>> {
default_prepare_components::<T, E>(args, primary_component)
}
}

/// Helper function to provide a default implementation for process_command
async fn default_process_command<T, C, E: Default>(args: Option<T>, _cmd: C) -> Result<(), E> {
if args.is_some() {
trace!("unexpected args to default_process_command function");
trace!("potentially missing implementation of process_command in CfuReceiveContent trait")
}
Ok(())
}

/// Helper function to provide a default implementation for prepare_components
async fn default_prepare_components<T, E: Default>(
args: Option<T>,
_primary_component: impl CfuComponentTraits,
) -> Result<(), E> {
if args.is_some() {
trace!("unexpected args to default_prepare_components function");
trace!("potentially missing implementation of prepare_components in CfuReceiveContent trait")
}
Ok(())
) -> impl Future<Output = Result<(), E>>;
}
13 changes: 10 additions & 3 deletions src/components.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
use core::future::Future;

use crate::protocol_definitions::*;
use crate::{CfuWriter, CfuWriterError};
use crate::protocol_definitions::{
CfuProtocolError, ComponentId, FwVersion, OfferRejectReason, OfferStatus, MAX_SUBCMPT_COUNT,
};
use crate::writer::CfuWriterError;

pub trait CfuComponentInfo {
/// Gets the current fw version of the component
fn get_fw_version(&self) -> impl Future<Output = Result<FwVersion, CfuProtocolError>>;

/// Gets the component's id
/// Not async as this should be an element of struct that implements this trait
fn get_component_id(&self) -> ComponentId;

/// Validate the CFU offer for the component
/// returns an OfferStatus with additional info on Reject Reason in the Err case.
fn is_offer_valid(&self) -> impl Future<Output = Result<OfferStatus, (OfferStatus, OfferRejectReason)>>;

/// Returns whether or not this component is a primary component
/// Not async as this should be an element of struct that implements this trait
/// Default implementation returns false,
fn is_primary_component(&self) -> bool {
false
}

/// Returns whether or not this component has a dual-bank memory layout
/// Not async as this should be an element of struct that implements this trait
fn is_dual_bank(&self) -> bool;

/// Returns sub-component ids if this component has any
/// Not async as this should be an element of struct that implements this trait
fn get_subcomponents(&self) -> [Option<ComponentId>; MAX_SUBCMPT_COUNT];
}

pub trait CfuComponentStorage: CfuWriter {
pub trait CfuComponentStorage {
fn storage_prepare(&self) -> impl Future<Output = Result<(), CfuWriterError>>;
fn storage_write(&self) -> impl Future<Output = Result<(), CfuWriterError>>;
fn storage_finalize(&self) -> impl Future<Output = Result<(), CfuWriterError>>;
Expand Down
10 changes: 6 additions & 4 deletions src/fmt.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
//! Logging macro implementations and other formating functions
//! Logging macro implementations and other formatting functions

#[cfg(all(feature = "log", feature = "defmt", not(doc)))]
compile_error!("features `log` and `defmt` are mutually exclusive");

#[cfg(all(not(doc), feature = "defmt"))]
#[cfg(all(feature = "defmt", not(doc)))]
#[doc(hidden)]
mod defmt {
/// Logs a trace message using the underlying logger
#[macro_export]
Expand Down Expand Up @@ -61,7 +62,8 @@ mod defmt {
}
}

#[cfg(all(not(doc), feature = "log"))]
#[cfg(all(feature = "log", not(doc)))]
#[doc(hidden)]
mod log {
/// Logs a trace message using the underlying logger
#[macro_export]
Expand Down Expand Up @@ -120,7 +122,7 @@ mod log {
}

// Provide this implementation for `cargo doc`
#[cfg(any(doc, not(any(feature = "defmt", feature = "log"))))]
#[cfg(any(not(any(feature = "defmt", feature = "log")), doc))]
mod none {
/// Logs a trace message using the underlying logger
#[macro_export]
Expand Down
34 changes: 20 additions & 14 deletions src/host.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
use super::*;
use core::future::Future;

use crate::protocol_definitions::{
CfuProtocolError, CfuUpdateContentResponseStatus, ComponentId, FwUpdateContentCommand, FwUpdateContentHeader,
FwUpdateContentResponse, FwUpdateOfferResponse, DEFAULT_DATA_LENGTH, FW_UPDATE_FLAG_FIRST_BLOCK,
FW_UPDATE_FLAG_LAST_BLOCK,
};
use crate::writer::{CfuWriterAsync, CfuWriterError};
use crate::{trace, CfuImage, DataChunk};

/// CfuHostStates trait defines behavior needed for a Cfu Host to process available Cfu Offers
/// and send the appropriate commands to the Cfu Client to update the components
pub trait CfuHostStates {
pub trait CfuHostStates<W> {
/// Notifies that the host is now initialized and has identified the offers to send
fn start_transaction<W: CfuWriter>(
self,
writer: &mut W,
) -> impl Future<Output = Result<FwUpdateOfferResponse, CfuProtocolError>>;
fn start_transaction(self, writer: &mut W)
-> impl Future<Output = Result<FwUpdateOfferResponse, CfuProtocolError>>;
/// Notifies the primary component that the host is ready to start sending offers
fn notify_start_offer_list<W: CfuWriter>(
fn notify_start_offer_list(
self,
writer: &mut W,
) -> impl Future<Output = Result<FwUpdateOfferResponse, CfuProtocolError>>;
/// Notifies the primary component that the host has sent all offers
fn notify_end_offer_list<W: CfuWriter>(
fn notify_end_offer_list(
self,
writer: &mut W,
) -> impl Future<Output = Result<FwUpdateOfferResponse, CfuProtocolError>>;
Expand All @@ -25,10 +31,7 @@ pub trait CfuHostStates {
}

/// CfuUpdateContent trait defines behavior needed for a Cfu Host to send the contents of an accepted offer to a component via sending commands to a Cfu Client
pub trait CfuUpdateContent<W>
where
W: CfuWriter,
{
pub trait CfuUpdateContent<W> {
/// Write all chunks of an image
fn write_data_chunks(
&mut self,
Expand All @@ -37,19 +40,22 @@ where
cmpt_id: ComponentId,
base_offset: usize,
) -> impl Future<Output = Result<FwUpdateContentResponse, CfuProtocolError>>;

/// Build and send UpdateOfferContent command with first block flag
fn process_first_data_block(
&mut self,
w: &mut W,
chunk: DataChunk,
) -> impl Future<Output = Result<FwUpdateContentResponse, CfuWriterError>>;

/// Build and send UpdateOfferContent command, no special flags
fn process_middle_data_block(
&mut self,
w: &mut W,
chunk: DataChunk,
seq_num: usize,
) -> impl Future<Output = Result<FwUpdateContentResponse, CfuWriterError>>;

/// Build and send UpdateOfferContent command with last block flag
fn process_last_data_block(
&mut self,
Expand All @@ -60,9 +66,9 @@ where
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct CfuUpdater {}
pub struct CfuUpdater;

impl<W: CfuWriter> CfuUpdateContent<W> for CfuUpdater {
impl<W: CfuWriterAsync> CfuUpdateContent<W> for CfuUpdater {
/// Write all chunks of an image
async fn write_data_chunks(
&mut self,
Expand Down
91 changes: 1 addition & 90 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ use core::future::Future;

use embedded_io_async::{Read, ReadExactError, Seek, SeekFrom};

use crate::components::*;
use crate::protocol_definitions::*;

pub mod client;
pub mod components;
pub mod fmt;
pub mod host;
pub mod protocol_definitions;
pub mod writer;

// re-export the error enum
pub use CfuProtocolError::*;
Expand Down Expand Up @@ -42,93 +42,4 @@ pub async fn read_from_exact<I: CfuImage>(
image.read_exact(buf).await
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CfuWriterError {
StorageError,
ByteConversionError,
Other,
}

/// This is a temporary change and will be revisited later
pub trait CfuWriterMut {
/// writes a chunk of data to a component and reads back to another buffer
fn cfu_write_read(
&mut self,
mem_offset: Option<usize>,
data: &[u8],
read: &mut [u8],
) -> impl Future<Output = Result<(), CfuWriterError>>;
/// Fills a given buffer with data from the component
fn cfu_read(
&mut self,
mem_offset: Option<usize>,
read: &mut [u8],
) -> impl Future<Output = Result<(), CfuWriterError>>;
/// Writes a given buffer of data to a component
/// Note: we don't need cfu_write as cfu_storage may replace it.
fn cfu_write(&mut self, mem_offset: Option<usize>, data: &[u8])
-> impl Future<Output = Result<(), CfuWriterError>>;
/// Manages erasing sectors and writing pages into flash based on the CFU offset
fn cfu_storage(&mut self, mem_offset: usize, data: &[u8]) -> impl Future<Output = Result<(), CfuWriterError>>;
}

/// Trait to define R/W behavior for driver that can talk to a CFU component or client
pub trait CfuWriter {
/// writes a chunk of data to a component and reads back to another buffer
fn cfu_write_read(
&self,
mem_offset: Option<usize>,
data: &[u8],
read: &mut [u8],
) -> impl Future<Output = Result<(), CfuWriterError>>;
/// Fills a given buffer with data from the component
fn cfu_read(&self, mem_offset: Option<usize>, read: &mut [u8]) -> impl Future<Output = Result<(), CfuWriterError>>;
/// Writes a given buffer of data to a component
fn cfu_write(&self, mem_offset: Option<usize>, data: &[u8]) -> impl Future<Output = Result<(), CfuWriterError>>;
}

pub type DataChunk = [u8; DEFAULT_DATA_LENGTH];

#[derive(Copy, Clone)]
pub struct CfuWriterDefault {}

impl CfuWriterDefault {
/// Create new instance
pub fn new() -> Self {
Self {}
}
}

impl Default for CfuWriterDefault {
/// Creates default instance of CfuWriterDefault
fn default() -> Self {
Self::new()
}
}

impl CfuWriter for CfuWriterDefault {
/// writes a chunk of data to a component and reads back to another buffer
async fn cfu_write_read(&self, offset: Option<usize>, write: &[u8], read: &mut [u8]) -> Result<(), CfuWriterError> {
// TODO: add with_timeout to these calls
self.cfu_write(offset, write).await?;
self.cfu_read(offset, read).await?;
Ok(())
}
/// Fills a given buffer with 0xBE 0xEF alternating bytes
async fn cfu_read(&self, _offset: Option<usize>, read: &mut [u8]) -> Result<(), CfuWriterError> {
info!("Fake reading from component");
for (i, byte) in read.iter_mut().enumerate() {
if i % 2 == 0 {
*byte = 0xEF;
} else {
*byte = 0xBE;
}
}
Ok(())
}
async fn cfu_write(&self, _offset: Option<usize>, write: &[u8]) -> Result<(), CfuWriterError> {
info!("Fake writing to component: {:?}", write);
Ok(())
}
}
2 changes: 1 addition & 1 deletion src/protocol_definitions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use core::convert::TryFrom;

use crate::CfuWriterError;
use crate::writer::CfuWriterError;

// Max is 7 components in CfuUpdateOfferResponse, 1 primary and 6 subcomponents
pub const MAX_CMPT_COUNT: usize = 7;
Expand Down
Loading