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