Skip to main content

mcumgr_toolkit/commands/
mod.rs

1/// [File management](https://docs.zephyrproject.org/latest/services/device_mgmt/smp_groups/smp_group_8.html) group commands
2pub mod fs;
3/// [Application/software image management](https://docs.zephyrproject.org/latest/services/device_mgmt/smp_groups/smp_group_1.html) group commands
4pub mod image;
5/// [Default/OS management](https://docs.zephyrproject.org/latest/services/device_mgmt/smp_groups/smp_group_0.html) group commands
6pub mod os;
7/// [Shell management](https://docs.zephyrproject.org/latest/services/device_mgmt/smp_groups/smp_group_9.html) group commands
8pub mod shell;
9/// [Statistics management](https://docs.zephyrproject.org/latest/services/device_mgmt/smp_groups/smp_group_2.html) group commands
10pub mod stats;
11/// [Zephyr management](https://docs.zephyrproject.org/latest/services/device_mgmt/smp_groups/smp_group_63.html) group commands
12pub mod zephyr;
13
14mod macros;
15use macros::impl_mcumgr_command;
16
17use serde::{Deserialize, Serialize};
18
19/// SMP version 2 group based error message
20#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
21pub struct ErrResponseV2 {
22    /// group of the group-based error code
23    pub group: u16,
24    /// contains the index of the group-based error code
25    pub rc: i32,
26}
27
28/// [SMP error message](https://docs.zephyrproject.org/latest/services/device_mgmt/smp_protocol.html#minimal-response-smp-data)
29#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
30pub struct ErrResponse {
31    /// SMP version 1 error code
32    pub rc: Option<i32>,
33    /// SMP version 1 error string
34    pub rsn: Option<String>,
35    /// SMP version 2 error message
36    pub err: Option<ErrResponseV2>,
37}
38
39/// An MCUmgr command that can be executed through [`Connection::execute_command`](crate::connection::Connection::execute_command).
40pub trait McuMgrCommand {
41    /// the data payload type
42    type Payload: Serialize;
43    /// the response type of the command
44    type Response: for<'a> Deserialize<'a>;
45    /// whether this command is a read or write operation
46    fn is_write_operation(&self) -> bool;
47    /// the group ID of the command
48    fn group_id(&self) -> u16;
49    /// the command ID
50    fn command_id(&self) -> u8;
51    /// the data
52    fn data(&self) -> &Self::Payload;
53}
54
55/// Checks if a value is the default value
56fn is_default<T: Default + PartialEq>(val: &T) -> bool {
57    val == &T::default()
58}
59
60fn data_too_large_error() -> std::io::Error {
61    std::io::Error::other(Box::<dyn std::error::Error + Send + Sync>::from(
62        "Serialized data too large".to_string(),
63    ))
64}
65
66// A writer that simply counts the number of bytes written to it
67struct CountingWriter {
68    bytes_written: usize,
69}
70impl CountingWriter {
71    fn new() -> Self {
72        Self { bytes_written: 0 }
73    }
74}
75impl std::io::Write for CountingWriter {
76    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
77        let len = buf.len();
78        self.bytes_written = self
79            .bytes_written
80            .checked_add(len)
81            .ok_or_else(data_too_large_error)?;
82        Ok(len)
83    }
84
85    fn flush(&mut self) -> std::io::Result<()> {
86        Ok(())
87    }
88}
89
90impl_mcumgr_command!((read,  MGMT_GROUP_ID_OS, 0): os::Echo<'_> => os::EchoResponse);
91impl_mcumgr_command!((read,  MGMT_GROUP_ID_OS, 2): os::TaskStatistics => os::TaskStatisticsResponse);
92impl_mcumgr_command!((read,  MGMT_GROUP_ID_OS, 4): os::DateTimeGet => os::DateTimeGetResponse);
93impl_mcumgr_command!((write, MGMT_GROUP_ID_OS, 4): os::DateTimeSet => os::DateTimeSetResponse);
94impl_mcumgr_command!((write, MGMT_GROUP_ID_OS, 5): os::SystemReset => os::SystemResetResponse);
95impl_mcumgr_command!((read,  MGMT_GROUP_ID_OS, 6): os::MCUmgrParameters => os::MCUmgrParametersResponse);
96impl_mcumgr_command!((read,  MGMT_GROUP_ID_OS, 7): os::ApplicationInfo<'_> => os::ApplicationInfoResponse);
97impl_mcumgr_command!((read,  MGMT_GROUP_ID_OS, 8): os::BootloaderInfo => os::BootloaderInfoResponse);
98impl_mcumgr_command!((read,  MGMT_GROUP_ID_OS, 8): os::BootloaderInfoMcubootMode => os::BootloaderInfoMcubootModeResponse);
99
100impl_mcumgr_command!((read,  MGMT_GROUP_ID_IMAGE, 0): image::GetImageState => image::ImageStateResponse);
101impl_mcumgr_command!((write,  MGMT_GROUP_ID_IMAGE, 0): image::SetImageState<'_> => image::ImageStateResponse);
102impl_mcumgr_command!((write,  MGMT_GROUP_ID_IMAGE, 1): image::ImageUpload<'_, '_> => image::ImageUploadResponse);
103impl_mcumgr_command!((write,  MGMT_GROUP_ID_IMAGE, 5): image::ImageErase => image::ImageEraseResponse);
104impl_mcumgr_command!((read,  MGMT_GROUP_ID_IMAGE, 6): image::SlotInfo => image::SlotInfoResponse);
105
106impl_mcumgr_command!((read, MGMT_GROUP_ID_STAT, 0): stats::GroupData<'_> => stats::GroupDataResponse);
107impl_mcumgr_command!((read, MGMT_GROUP_ID_STAT, 1): stats::ListGroups => stats::ListGroupsResponse);
108
109impl_mcumgr_command!((write, MGMT_GROUP_ID_FS, 0): fs::FileUpload<'_, '_> => fs::FileUploadResponse);
110impl_mcumgr_command!((read,  MGMT_GROUP_ID_FS, 0): fs::FileDownload<'_> => fs::FileDownloadResponse);
111impl_mcumgr_command!((read,  MGMT_GROUP_ID_FS, 1): fs::FileStatus<'_> => fs::FileStatusResponse);
112impl_mcumgr_command!((read,  MGMT_GROUP_ID_FS, 2): fs::FileChecksum<'_, '_> => fs::FileChecksumResponse);
113impl_mcumgr_command!((read,  MGMT_GROUP_ID_FS, 3): fs::SupportedFileChecksumTypes => fs::SupportedFileChecksumTypesResponse);
114impl_mcumgr_command!((write, MGMT_GROUP_ID_FS, 4): fs::FileClose => fs::FileCloseResponse);
115
116impl_mcumgr_command!((write, MGMT_GROUP_ID_SHELL, 0): shell::ShellCommandLineExecute<'_> => shell::ShellCommandLineExecuteResponse);
117
118impl_mcumgr_command!((write, ZEPHYR_MGMT_GRP_BASIC, 0): zephyr::EraseStorage => zephyr::EraseStorageResponse);
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use ciborium::cbor;
124
125    #[test]
126    fn decode_error_none() {
127        let mut cbor_data = vec![];
128        ciborium::into_writer(
129            &cbor!({
130                "foo" => 42,
131            })
132            .unwrap(),
133            &mut cbor_data,
134        )
135        .unwrap();
136        let err: ErrResponse = ciborium::from_reader(cbor_data.as_slice()).unwrap();
137        assert_eq!(
138            err,
139            ErrResponse {
140                rc: None,
141                rsn: None,
142                err: None,
143            }
144        );
145    }
146
147    #[test]
148    fn decode_error_v1() {
149        let mut cbor_data = vec![];
150        ciborium::into_writer(
151            &cbor!({
152                "rc" => 10,
153            })
154            .unwrap(),
155            &mut cbor_data,
156        )
157        .unwrap();
158        let err: ErrResponse = ciborium::from_reader(cbor_data.as_slice()).unwrap();
159        assert_eq!(
160            err,
161            ErrResponse {
162                rc: Some(10),
163                rsn: None,
164                err: None,
165            }
166        );
167    }
168
169    #[test]
170    fn decode_error_v1_with_msg() {
171        let mut cbor_data = vec![];
172        ciborium::into_writer(
173            &cbor!({
174                "rc" => 1,
175                "rsn" => "Test Reason!",
176            })
177            .unwrap(),
178            &mut cbor_data,
179        )
180        .unwrap();
181        let err: ErrResponse = ciborium::from_reader(cbor_data.as_slice()).unwrap();
182        assert_eq!(
183            err,
184            ErrResponse {
185                rc: Some(1),
186                rsn: Some("Test Reason!".to_string()),
187                err: None,
188            }
189        );
190    }
191
192    #[test]
193    fn decode_error_v2() {
194        let mut cbor_data = vec![];
195        ciborium::into_writer(
196            &cbor!({
197                "err" => {
198                    "group" => 4,
199                    "rc" => 20,
200                }
201            })
202            .unwrap(),
203            &mut cbor_data,
204        )
205        .unwrap();
206        let err: ErrResponse = ciborium::from_reader(cbor_data.as_slice()).unwrap();
207        assert_eq!(
208            err,
209            ErrResponse {
210                rc: None,
211                rsn: None,
212                err: Some(ErrResponseV2 { group: 4, rc: 20 })
213            }
214        );
215    }
216
217    #[test]
218    fn is_default() {
219        assert!(super::is_default(&0));
220        assert!(!super::is_default(&5));
221    }
222}