zencan_node/
storage.rs

1//! Handling for persistent storage control objects
2//!
3//!
4
5use core::{convert::Infallible, sync::atomic::Ordering};
6
7use portable_atomic::AtomicBool;
8use zencan_common::{
9    constants::values::SAVE_CMD,
10    objects::{ObjectCode, SubInfo},
11    sdo::AbortCode,
12};
13
14use crate::object_dict::ObjectAccess;
15
16/// A callback function type for handling a store objects event
17pub type StoreObjectsCallback =
18    dyn Fn(&mut dyn embedded_io::Read<Error = Infallible>, usize) + Sync;
19
20#[derive(Default)]
21#[allow(missing_debug_implementations)]
22/// Shared state for supporting object storage
23pub struct StorageContext {
24    /// A flag set by the storage command object when a store command is received
25    pub(crate) store_flag: AtomicBool,
26    /// Indicates to storage command object if storage is supported by the application
27    pub(crate) store_supported: AtomicBool,
28}
29
30impl StorageContext {
31    /// Create a new StorageContext
32    pub const fn new() -> Self {
33        Self {
34            store_flag: AtomicBool::new(false),
35            store_supported: AtomicBool::new(false),
36        }
37    }
38}
39
40/// Implements the storage command object (0x1010)
41#[allow(missing_debug_implementations)]
42pub struct StorageCommandObject {
43    storage_context: &'static StorageContext,
44}
45
46impl StorageCommandObject {
47    /// Create a new storage context object
48    pub const fn new(storage_context: &'static StorageContext) -> Self {
49        Self { storage_context }
50    }
51}
52
53impl ObjectAccess for StorageCommandObject {
54    fn read(&self, sub: u8, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
55        match sub {
56            0 => {
57                if offset != 0 || buf.len() != 1 {
58                    Err(AbortCode::DataTypeMismatch)
59                } else {
60                    buf[0] = 1;
61                    Ok(1)
62                }
63            }
64            1 => {
65                // Bit 0 indicates the node is capable of saving objects. Set it if a callback has
66                // been registered.
67                let mut value = 0u32;
68                if self.storage_context.store_supported.load(Ordering::Relaxed) {
69                    value |= 1;
70                }
71                let value_bytes = value.to_le_bytes();
72                if offset < value_bytes.len() {
73                    let read_len = buf.len().min(value_bytes.len() - offset);
74                    buf[..read_len].copy_from_slice(&value_bytes[offset..offset + read_len]);
75                    Ok(read_len)
76                } else {
77                    Ok(0)
78                }
79            }
80            _ => Err(AbortCode::NoSuchSubIndex),
81        }
82    }
83
84    fn read_size(&self, sub: u8) -> Result<usize, AbortCode> {
85        match sub {
86            0 => Ok(1),
87            1 => Ok(4),
88            _ => Err(AbortCode::NoSuchSubIndex),
89        }
90    }
91
92    fn write(&self, sub: u8, data: &[u8]) -> Result<(), AbortCode> {
93        match sub {
94            0 => Err(AbortCode::ReadOnly),
95            1 => {
96                if data.len() != 4 {
97                    Err(AbortCode::DataTypeMismatch)
98                } else {
99                    let value = u32::from_le_bytes(data[0..4].try_into().unwrap());
100                    // Magic value ('save') triggering a save
101                    if value == SAVE_CMD {
102                        if self.storage_context.store_supported.load(Ordering::Relaxed) {
103                            self.storage_context
104                                .store_flag
105                                .store(true, Ordering::Relaxed);
106                            Ok(())
107                        } else {
108                            Err(AbortCode::ResourceNotAvailable)
109                        }
110                    } else {
111                        Err(AbortCode::IncompatibleParameter)
112                    }
113                }
114            }
115            _ => Err(AbortCode::NoSuchSubIndex),
116        }
117    }
118
119    fn object_code(&self) -> ObjectCode {
120        ObjectCode::Record
121    }
122
123    fn sub_info(&self, sub: u8) -> Result<SubInfo, AbortCode> {
124        match sub {
125            0 => Ok(SubInfo::MAX_SUB_NUMBER),
126            1 => Ok(SubInfo::new_u32().rw_access()),
127            _ => Err(AbortCode::NoSuchSubIndex),
128        }
129    }
130}