Skip to main content

mcumgr_toolkit/commands/
mod.rs

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