1mod firmware_update;
3
4pub use firmware_update::{
5 FirmwareUpdateError, FirmwareUpdateParams, FirmwareUpdateProgressCallback, FirmwareUpdateStep,
6};
7
8use std::{
9 collections::HashMap,
10 io::{self, Read, Write},
11 net::SocketAddr,
12 sync::atomic::AtomicUsize,
13 time::Duration,
14};
15
16use miette::Diagnostic;
17use rand::distr::SampleString;
18use serde::Serialize;
19use sha2::{Digest, Sha256};
20use thiserror::Error;
21
22use crate::{
23 bootloader::BootloaderInfo,
24 commands::{
25 self, fs::file_upload_max_data_chunk_size, image::image_upload_max_data_chunk_size,
26 },
27 connection::{Connection, ExecuteError},
28 transport::{
29 ReceiveError,
30 serial::{ConfigurableTimeout, SerialTransport},
31 udp::UdpTransport,
32 },
33};
34
35const ZEPHYR_DEFAULT_SMP_FRAME_SIZE: usize = 384;
39
40pub struct MCUmgrClient {
44 connection: Connection,
45 smp_frame_size: AtomicUsize,
46}
47
48#[derive(Error, Debug, Diagnostic)]
50pub enum MCUmgrClientError {
51 #[error("Command execution failed")]
53 #[diagnostic(code(mcumgr_toolkit::client::execute))]
54 ExecuteError(#[from] ExecuteError),
55 #[error("Received an unexpected offset value")]
57 #[diagnostic(code(mcumgr_toolkit::client::unexpected_offset))]
58 UnexpectedOffset,
59 #[error("Writer returned an error")]
61 #[diagnostic(code(mcumgr_toolkit::client::writer))]
62 WriterError(#[source] io::Error),
63 #[error("Reader returned an error")]
65 #[diagnostic(code(mcumgr_toolkit::client::reader))]
66 ReaderError(#[source] io::Error),
67 #[error("Received data does not match reported size")]
69 #[diagnostic(code(mcumgr_toolkit::client::size_mismatch))]
70 SizeMismatch,
71 #[error("Received data is missing file size information")]
73 #[diagnostic(code(mcumgr_toolkit::client::missing_size))]
74 MissingSize,
75 #[error("Progress callback returned an error")]
77 #[diagnostic(code(mcumgr_toolkit::client::progress_cb_error))]
78 ProgressCallbackError,
79 #[error("SMP frame size too small for this command")]
81 #[diagnostic(code(mcumgr_toolkit::client::framesize_too_small))]
82 FrameSizeTooSmall(#[source] io::Error),
83 #[error("Device reported checksum mismatch")]
85 #[diagnostic(code(mcumgr_toolkit::client::checksum_mismatch_on_device))]
86 ChecksumMismatchOnDevice,
87 #[error("Firmware image does not match given checksum")]
89 #[diagnostic(code(mcumgr_toolkit::client::checksum_mismatch))]
90 ChecksumMismatch,
91 #[error("Failed to set the device timeout")]
93 #[diagnostic(code(mcumgr_toolkit::client::set_timeout))]
94 SetTimeoutFailed(#[source] Box<dyn std::error::Error + Send + Sync>),
95}
96
97impl MCUmgrClientError {
98 pub fn command_not_supported(&self) -> bool {
100 if let Self::ExecuteError(err) = self {
101 err.command_not_supported()
102 } else {
103 false
104 }
105 }
106}
107
108#[derive(Debug, Serialize, Clone, Eq, PartialEq)]
110pub struct UsbSerialPortInfo {
111 pub identifier: String,
113 pub port_name: String,
115 pub port_info: serialport::UsbPortInfo,
117}
118
119#[derive(Serialize, Clone, Eq, PartialEq)]
123#[serde(transparent)]
124pub struct UsbSerialPorts(pub Vec<UsbSerialPortInfo>);
125impl std::fmt::Display for UsbSerialPorts {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 if self.0.is_empty() {
128 writeln!(f)?;
129 write!(f, " - None -")?;
130 return Ok(());
131 }
132
133 for UsbSerialPortInfo {
134 identifier,
135 port_name,
136 port_info,
137 } in &self.0
138 {
139 writeln!(f)?;
140 write!(f, " - {identifier}")?;
141
142 let mut print_port_string = true;
143 let port_string = format!("({port_name})");
144
145 if port_info.manufacturer.is_some() || port_info.product.is_some() {
146 write!(f, " -")?;
147 if let Some(manufacturer) = &port_info.manufacturer {
148 let mut print_manufacturer = true;
149
150 if let Some(product) = &port_info.product {
151 if product.starts_with(manufacturer) {
152 print_manufacturer = false;
153 }
154 }
155
156 if print_manufacturer {
157 write!(f, " {manufacturer}")?;
158 }
159 }
160 if let Some(product) = &port_info.product {
161 write!(f, " {product}")?;
162
163 if product.ends_with(&port_string) {
164 print_port_string = false;
165 }
166 }
167 }
168
169 if print_port_string {
170 write!(f, " {port_string}")?;
171 }
172 }
173 Ok(())
174 }
175}
176impl std::fmt::Debug for UsbSerialPorts {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 std::fmt::Debug::fmt(&self.0, f)
179 }
180}
181
182#[derive(Error, Debug, Diagnostic)]
184pub enum UdpError {
185 #[error("Failed to open UDP socket")]
187 #[diagnostic(code(mcumgr_toolkit::udp::io_error))]
188 Io(#[from] io::Error),
189}
190
191#[derive(Error, Debug, Diagnostic)]
193pub enum UsbSerialError {
194 #[error("Serialport returned an error")]
196 #[diagnostic(code(mcumgr_toolkit::usb_serial::serialport_error))]
197 SerialPortError(#[from] serialport::Error),
198 #[error("No serial port matched the identifier '{identifier}'\nAvailable ports:\n{available}")]
200 #[diagnostic(code(mcumgr_toolkit::usb_serial::no_matches))]
201 NoMatchingPort {
202 identifier: String,
204 available: UsbSerialPorts,
206 },
207 #[error("Multiple serial ports matched the identifier '{identifier}'\n{ports}")]
209 #[diagnostic(code(mcumgr_toolkit::usb_serial::multiple_matches))]
210 MultipleMatchingPorts {
211 identifier: String,
213 ports: UsbSerialPorts,
215 },
216 #[error("An empty identifier was provided")]
219 #[diagnostic(code(mcumgr_toolkit::usb_serial::empty_identifier))]
220 IdentifierEmpty {
221 ports: UsbSerialPorts,
223 },
224 #[error("The given identifier was not a valid RegEx")]
226 #[diagnostic(code(mcumgr_toolkit::usb_serial::regex_error))]
227 RegexError(#[from] regex::Error),
228}
229
230impl MCUmgrClient {
231 pub fn new_from_serial<T: Send + Read + Write + ConfigurableTimeout + 'static>(
244 serial: T,
245 ) -> Self {
246 Self {
247 connection: Connection::new(SerialTransport::new(serial)),
248 smp_frame_size: ZEPHYR_DEFAULT_SMP_FRAME_SIZE.into(),
249 }
250 }
251
252 pub fn new_from_usb_serial(
270 identifier: impl AsRef<str>,
271 baud_rate: u32,
272 timeout: Duration,
273 ) -> Result<Self, UsbSerialError> {
274 let identifier = identifier.as_ref();
275
276 let ports = serialport::available_ports()?
277 .into_iter()
278 .filter_map(|port| {
279 if let serialport::SerialPortType::UsbPort(port_info) = port.port_type {
280 if let Some(interface) = port_info.interface {
281 Some(UsbSerialPortInfo {
282 identifier: format!(
283 "{:04x}:{:04x}:{}",
284 port_info.vid, port_info.pid, interface
285 ),
286 port_name: port.port_name,
287 port_info,
288 })
289 } else {
290 Some(UsbSerialPortInfo {
291 identifier: format!("{:04x}:{:04x}", port_info.vid, port_info.pid),
292 port_name: port.port_name,
293 port_info,
294 })
295 }
296 } else {
297 None
298 }
299 })
300 .collect::<Vec<_>>();
301
302 if identifier.is_empty() {
303 return Err(UsbSerialError::IdentifierEmpty {
304 ports: UsbSerialPorts(ports),
305 });
306 }
307
308 let port_regex = regex::RegexBuilder::new(identifier)
309 .case_insensitive(true)
310 .unicode(true)
311 .build()?;
312
313 let matches = ports
314 .iter()
315 .filter(|port| {
316 if let Some(m) = port_regex.find(&port.identifier) {
317 m.start() == 0
319 } else {
320 false
321 }
322 })
323 .cloned()
324 .collect::<Vec<_>>();
325
326 if matches.len() > 1 {
327 return Err(UsbSerialError::MultipleMatchingPorts {
328 identifier: identifier.to_string(),
329 ports: UsbSerialPorts(matches),
330 });
331 }
332
333 let port_name = match matches.into_iter().next() {
334 Some(port) => port.port_name,
335 None => {
336 return Err(UsbSerialError::NoMatchingPort {
337 identifier: identifier.to_string(),
338 available: UsbSerialPorts(ports),
339 });
340 }
341 };
342
343 let serial = serialport::new(port_name, baud_rate)
344 .timeout(timeout)
345 .open()?;
346
347 Ok(Self::new_from_serial(serial))
348 }
349
350 pub fn new_from_udp(addr: impl Into<SocketAddr>, timeout: Duration) -> Result<Self, UdpError> {
382 let addr = addr.into();
383 log::debug!("Connecting to {addr} ...");
384 Ok(Self {
385 connection: Connection::new(UdpTransport::new(addr, timeout)?),
386 smp_frame_size: ZEPHYR_DEFAULT_SMP_FRAME_SIZE.into(),
387 })
388 }
389
390 pub fn set_frame_size(&self, smp_frame_size: usize) {
395 self.smp_frame_size
396 .store(smp_frame_size, std::sync::atomic::Ordering::SeqCst);
397 }
398
399 pub fn use_auto_frame_size(&self) -> Result<(), MCUmgrClientError> {
403 let mcumgr_params = self
404 .connection
405 .execute_command(&commands::os::MCUmgrParameters)?;
406
407 let frame_size =
408 (mcumgr_params.buf_size as usize).min(self.connection.max_transport_frame_size());
409
410 log::debug!("Using frame size {}.", frame_size);
411
412 self.smp_frame_size
413 .store(frame_size, std::sync::atomic::Ordering::SeqCst);
414
415 Ok(())
416 }
417
418 pub fn set_timeout(&self, timeout: Duration) -> Result<(), MCUmgrClientError> {
423 self.connection
424 .set_timeout(timeout)
425 .map_err(MCUmgrClientError::SetTimeoutFailed)
426 }
427
428 pub fn set_retries(&self, retries: u8) {
433 self.connection.set_retries(retries)
434 }
435
436 pub fn check_connection(&self) -> Result<(), MCUmgrClientError> {
444 let random_message = rand::distr::Alphanumeric.sample_string(&mut rand::rng(), 16);
445 let response = self.os_echo(&random_message)?;
446 if random_message == response {
447 Ok(())
448 } else {
449 Err(
450 ExecuteError::ReceiveFailed(crate::transport::ReceiveError::UnexpectedResponse)
451 .into(),
452 )
453 }
454 }
455
456 pub fn firmware_update(
466 &self,
467 firmware: impl AsRef<[u8]>,
468 checksum: Option<[u8; 32]>,
469 params: FirmwareUpdateParams,
470 progress: Option<&mut FirmwareUpdateProgressCallback>,
471 ) -> Result<(), FirmwareUpdateError> {
472 firmware_update::firmware_update(self, firmware, checksum, params, progress)
473 }
474
475 pub fn os_echo(&self, msg: impl AsRef<str>) -> Result<String, MCUmgrClientError> {
479 self.connection
480 .execute_command(&commands::os::Echo { d: msg.as_ref() })
481 .map(|resp| resp.r)
482 .map_err(Into::into)
483 }
484
485 pub fn os_task_statistics(
496 &self,
497 ) -> Result<HashMap<String, commands::os::TaskStatisticsEntry>, MCUmgrClientError> {
498 self.connection
499 .execute_command(&commands::os::TaskStatistics)
500 .map(|resp| {
501 let mut tasks = resp.tasks;
502 for (_, stats) in tasks.iter_mut() {
503 stats.stkuse = stats.stkuse.map(|val| val * 4);
504 stats.stksiz = stats.stksiz.map(|val| val * 4);
505 }
506 tasks
507 })
508 .map_err(Into::into)
509 }
510
511 pub fn os_set_datetime(
513 &self,
514 datetime: chrono::NaiveDateTime,
515 ) -> Result<(), MCUmgrClientError> {
516 self.connection
517 .execute_command(&commands::os::DateTimeSet { datetime })
518 .map(Into::into)
519 .map_err(Into::into)
520 }
521
522 pub fn os_get_datetime(&self) -> Result<chrono::NaiveDateTime, MCUmgrClientError> {
524 self.connection
525 .execute_command(&commands::os::DateTimeGet)
526 .map(|val| val.datetime)
527 .map_err(Into::into)
528 }
529
530 pub fn os_system_reset(
544 &self,
545 force: bool,
546 boot_mode: Option<u8>,
547 ) -> Result<(), MCUmgrClientError> {
548 self.connection
549 .execute_command(&commands::os::SystemReset { force, boot_mode })
550 .map(Into::into)
551 .map_err(Into::into)
552 }
553
554 pub fn os_mcumgr_parameters(
556 &self,
557 ) -> Result<commands::os::MCUmgrParametersResponse, MCUmgrClientError> {
558 self.connection
559 .execute_command(&commands::os::MCUmgrParameters)
560 .map_err(Into::into)
561 }
562
563 pub fn os_application_info(&self, format: Option<&str>) -> Result<String, MCUmgrClientError> {
575 self.connection
576 .execute_command(&commands::os::ApplicationInfo { format })
577 .map(|resp| resp.output)
578 .map_err(Into::into)
579 }
580
581 pub fn os_bootloader_info(&self) -> Result<BootloaderInfo, MCUmgrClientError> {
583 Ok(
584 match self
585 .connection
586 .execute_command(&commands::os::BootloaderInfo)?
587 .bootloader
588 .as_str()
589 {
590 "MCUboot" => {
591 let mode_data = self
592 .connection
593 .execute_command(&commands::os::BootloaderInfoMcubootMode {})?;
594 BootloaderInfo::MCUboot {
595 mode: mode_data.mode,
596 no_downgrade: mode_data.no_downgrade,
597 }
598 }
599 name => BootloaderInfo::Unknown {
600 name: name.to_string(),
601 },
602 },
603 )
604 }
605
606 pub fn image_get_state(&self) -> Result<Vec<commands::image::ImageState>, MCUmgrClientError> {
608 self.connection
609 .execute_command(&commands::image::GetImageState)
610 .map(|val| val.images)
611 .map_err(Into::into)
612 }
613
614 pub fn image_set_state(
630 &self,
631 hash: Option<&[u8]>,
632 confirm: bool,
633 ) -> Result<Vec<commands::image::ImageState>, MCUmgrClientError> {
634 self.connection
635 .execute_command(&commands::image::SetImageState { hash, confirm })
636 .map(|val| val.images)
637 .map_err(Into::into)
638 }
639
640 pub fn image_upload(
658 &self,
659 data: impl AsRef<[u8]>,
660 image: Option<u32>,
661 checksum: Option<[u8; 32]>,
662 upgrade_only: bool,
663 mut progress: Option<&mut dyn FnMut(u64, u64) -> bool>,
664 ) -> Result<(), MCUmgrClientError> {
665 let chunk_size_max = image_upload_max_data_chunk_size(
666 self.smp_frame_size
667 .load(std::sync::atomic::Ordering::SeqCst),
668 )
669 .map_err(MCUmgrClientError::FrameSizeTooSmall)?;
670
671 let data = data.as_ref();
672
673 let actual_checksum: [u8; 32] = Sha256::digest(data).into();
674 if let Some(checksum) = checksum {
675 if actual_checksum != checksum {
676 return Err(MCUmgrClientError::ChecksumMismatch);
677 }
678 }
679
680 let mut offset = 0;
681 let size = data.len();
682
683 let mut checksum_matched = None;
684
685 while offset < size {
686 let current_chunk_size = (size - offset).min(chunk_size_max);
687 let chunk_data = &data[offset..offset + current_chunk_size];
688
689 let upload_response = if offset == 0 {
690 let result = self
691 .connection
692 .execute_command(&commands::image::ImageUpload {
693 image,
694 len: Some(size as u64),
695 off: offset as u64,
696 sha: Some(&actual_checksum),
697 data: chunk_data,
698 upgrade: Some(upgrade_only),
699 });
700
701 if let Err(ExecuteError::ReceiveFailed(ReceiveError::TransportError(e))) = &result {
702 if let io::ErrorKind::TimedOut = e.kind() {
703 log::warn!(
704 "Timed out during transfer of first chunk. Consider enabling CONFIG_IMG_ERASE_PROGRESSIVELY."
705 )
706 }
707 }
708
709 result?
710 } else {
711 self.connection
712 .execute_command(&commands::image::ImageUpload {
713 image: None,
714 len: None,
715 off: offset as u64,
716 sha: None,
717 data: chunk_data,
718 upgrade: None,
719 })?
720 };
721
722 offset = upload_response
723 .off
724 .try_into()
725 .map_err(|_| MCUmgrClientError::UnexpectedOffset)?;
726
727 if offset > size {
728 return Err(MCUmgrClientError::UnexpectedOffset);
729 }
730
731 if let Some(progress) = &mut progress {
732 if !progress(offset as u64, size as u64) {
733 return Err(MCUmgrClientError::ProgressCallbackError);
734 };
735 }
736
737 if let Some(is_match) = upload_response.r#match {
738 checksum_matched = Some(is_match);
739 }
740 }
741
742 if let Some(checksum_matched) = checksum_matched {
743 if !checksum_matched {
744 return Err(MCUmgrClientError::ChecksumMismatchOnDevice);
745 }
746 } else {
747 log::warn!("Device did not perform image checksum verification");
748 }
749
750 Ok(())
751 }
752
753 pub fn image_erase(&self, slot: Option<u32>) -> Result<(), MCUmgrClientError> {
760 self.connection
761 .execute_command(&commands::image::ImageErase { slot })
762 .map(Into::into)
763 .map_err(Into::into)
764 }
765
766 pub fn image_slot_info(
768 &self,
769 ) -> Result<Vec<commands::image::SlotInfoImage>, MCUmgrClientError> {
770 self.connection
771 .execute_command(&commands::image::SlotInfo)
772 .map(|val| val.images)
773 .map_err(Into::into)
774 }
775
776 pub fn stats_get_group_data(
783 &self,
784 name: impl AsRef<str>,
785 ) -> Result<HashMap<String, u64>, MCUmgrClientError> {
786 self.connection
787 .execute_command(&commands::stats::GroupData {
788 name: name.as_ref(),
789 })
790 .map(|val| val.fields)
791 .map_err(Into::into)
792 }
793
794 pub fn stats_list_groups(&self) -> Result<Vec<String>, MCUmgrClientError> {
796 self.connection
797 .execute_command(&commands::stats::ListGroups)
798 .map(|val| val.stat_list)
799 .map_err(Into::into)
800 }
801
802 pub fn settings_read(&self, name: impl AsRef<str>) -> Result<Vec<u8>, MCUmgrClientError> {
815 let name = name.as_ref();
816
817 self.settings_read_ext(name, None).map(|val| val.val)
818 }
819
820 pub fn settings_read_ext(
830 &self,
831 name: impl AsRef<str>,
832 max_size: Option<u32>,
833 ) -> Result<commands::settings::ReadSettingResponse, MCUmgrClientError> {
834 let name = name.as_ref();
835
836 self.connection
837 .execute_command(&commands::settings::ReadSetting { name, max_size })
838 .map_err(Into::into)
839 }
840
841 pub fn settings_write(
849 &self,
850 name: impl AsRef<str>,
851 value: &[u8],
852 ) -> Result<(), MCUmgrClientError> {
853 let name = name.as_ref();
854
855 self.connection
856 .execute_command(&commands::settings::WriteSetting { name, val: value })
857 .map(Into::into)
858 .map_err(Into::into)
859 }
860
861 pub fn settings_delete(&self, name: impl AsRef<str>) -> Result<(), MCUmgrClientError> {
868 let name = name.as_ref();
869
870 self.connection
871 .execute_command(&commands::settings::DeleteSetting { name })
872 .map(Into::into)
873 .map_err(Into::into)
874 }
875
876 pub fn settings_commit(&self) -> Result<(), MCUmgrClientError> {
879 self.connection
880 .execute_command(&commands::settings::CommitSettings)
881 .map(Into::into)
882 .map_err(Into::into)
883 }
884
885 pub fn settings_load(&self) -> Result<(), MCUmgrClientError> {
888 self.connection
889 .execute_command(&commands::settings::LoadSettings)
890 .map(Into::into)
891 .map_err(Into::into)
892 }
893
894 pub fn settings_save(&self, name: Option<impl AsRef<str>>) -> Result<(), MCUmgrClientError> {
901 let name = name.as_ref().map(|val| val.as_ref());
902
903 self.connection
904 .execute_command(&commands::settings::SaveSettings { name })
905 .map(Into::into)
906 .map_err(Into::into)
907 }
908
909 pub fn fs_file_download<T: Write>(
923 &self,
924 name: impl AsRef<str>,
925 mut writer: T,
926 mut progress: Option<&mut dyn FnMut(u64, u64) -> bool>,
927 ) -> Result<(), MCUmgrClientError> {
928 let name = name.as_ref();
929 let response = self
930 .connection
931 .execute_command(&commands::fs::FileDownload { name, off: 0 })?;
932
933 let file_len = response.len.ok_or(MCUmgrClientError::MissingSize)?;
934 if response.off != 0 {
935 return Err(MCUmgrClientError::UnexpectedOffset);
936 }
937
938 let mut offset = 0;
939
940 if let Some(progress) = &mut progress {
941 if !progress(offset, file_len) {
942 return Err(MCUmgrClientError::ProgressCallbackError);
943 };
944 }
945
946 writer
947 .write_all(&response.data)
948 .map_err(MCUmgrClientError::WriterError)?;
949 offset += response.data.len() as u64;
950
951 if let Some(progress) = &mut progress {
952 if !progress(offset, file_len) {
953 return Err(MCUmgrClientError::ProgressCallbackError);
954 };
955 }
956
957 while offset < file_len {
958 let response = self
959 .connection
960 .execute_command(&commands::fs::FileDownload { name, off: offset })?;
961
962 if response.off != offset {
963 return Err(MCUmgrClientError::UnexpectedOffset);
964 }
965
966 writer
967 .write_all(&response.data)
968 .map_err(MCUmgrClientError::WriterError)?;
969 offset += response.data.len() as u64;
970
971 if let Some(progress) = &mut progress {
972 if !progress(offset, file_len) {
973 return Err(MCUmgrClientError::ProgressCallbackError);
974 };
975 }
976 }
977
978 if offset != file_len {
979 return Err(MCUmgrClientError::SizeMismatch);
980 }
981
982 Ok(())
983 }
984
985 pub fn fs_file_upload<T: Read>(
1001 &self,
1002 name: impl AsRef<str>,
1003 mut reader: T,
1004 size: u64,
1005 mut progress: Option<&mut dyn FnMut(u64, u64) -> bool>,
1006 ) -> Result<(), MCUmgrClientError> {
1007 let name = name.as_ref();
1008
1009 let chunk_size_max = file_upload_max_data_chunk_size(
1010 self.smp_frame_size
1011 .load(std::sync::atomic::Ordering::SeqCst),
1012 name,
1013 )
1014 .map_err(MCUmgrClientError::FrameSizeTooSmall)?;
1015 let mut data_buffer = vec![0u8; chunk_size_max].into_boxed_slice();
1016
1017 let mut offset = 0;
1018
1019 while offset < size {
1020 let current_chunk_size = (size - offset).min(data_buffer.len() as u64) as usize;
1021
1022 let chunk_buffer = &mut data_buffer[..current_chunk_size];
1023 reader
1024 .read_exact(chunk_buffer)
1025 .map_err(MCUmgrClientError::ReaderError)?;
1026
1027 self.connection.execute_command(&commands::fs::FileUpload {
1028 off: offset,
1029 data: chunk_buffer,
1030 name,
1031 len: if offset == 0 { Some(size) } else { None },
1032 })?;
1033
1034 offset += chunk_buffer.len() as u64;
1035
1036 if let Some(progress) = &mut progress {
1037 if !progress(offset, size) {
1038 return Err(MCUmgrClientError::ProgressCallbackError);
1039 };
1040 }
1041 }
1042
1043 Ok(())
1044 }
1045
1046 pub fn fs_file_status(
1048 &self,
1049 name: impl AsRef<str>,
1050 ) -> Result<commands::fs::FileStatusResponse, MCUmgrClientError> {
1051 self.connection
1052 .execute_command(&commands::fs::FileStatus {
1053 name: name.as_ref(),
1054 })
1055 .map_err(Into::into)
1056 }
1057
1058 pub fn fs_file_checksum(
1070 &self,
1071 name: impl AsRef<str>,
1072 algorithm: Option<impl AsRef<str>>,
1073 offset: u64,
1074 length: Option<u64>,
1075 ) -> Result<commands::fs::FileChecksumResponse, MCUmgrClientError> {
1076 self.connection
1077 .execute_command(&commands::fs::FileChecksum {
1078 name: name.as_ref(),
1079 r#type: algorithm.as_ref().map(AsRef::as_ref),
1080 off: offset,
1081 len: length,
1082 })
1083 .map_err(Into::into)
1084 }
1085
1086 pub fn fs_supported_checksum_types(
1088 &self,
1089 ) -> Result<HashMap<String, commands::fs::FileChecksumProperties>, MCUmgrClientError> {
1090 self.connection
1091 .execute_command(&commands::fs::SupportedFileChecksumTypes)
1092 .map(|val| val.types)
1093 .map_err(Into::into)
1094 }
1095
1096 pub fn fs_file_close(&self) -> Result<(), MCUmgrClientError> {
1098 self.connection
1099 .execute_command(&commands::fs::FileClose)
1100 .map(Into::into)
1101 .map_err(Into::into)
1102 }
1103
1104 pub fn shell_execute(
1116 &self,
1117 argv: &[String],
1118 use_retries: bool,
1119 ) -> Result<(i32, String), MCUmgrClientError> {
1120 let command = commands::shell::ShellCommandLineExecute { argv };
1121
1122 if use_retries {
1123 self.connection.execute_command(&command)
1124 } else {
1125 self.connection.execute_command_without_retries(&command)
1126 }
1127 .map(|ret| (ret.ret, ret.o))
1128 .map_err(Into::into)
1129 }
1130
1131 pub fn enum_get_group_count(&self) -> Result<u16, MCUmgrClientError> {
1138 self.connection
1139 .execute_command(&commands::r#enum::GroupCount)
1140 .map(|ret| ret.count)
1141 .map_err(Into::into)
1142 }
1143
1144 pub fn enum_get_group_ids(&self) -> Result<Vec<u16>, MCUmgrClientError> {
1159 self.connection
1160 .execute_command(&commands::r#enum::ListGroups)
1161 .map(|ret| ret.groups)
1162 .map_err(Into::into)
1163 }
1164
1165 pub fn enum_get_group_id(&self, index: u16) -> Result<u16, MCUmgrClientError> {
1177 self.connection
1178 .execute_command(&commands::r#enum::GroupId { index: Some(index) })
1179 .map(|ret| ret.group)
1180 .map_err(Into::into)
1181 }
1182
1183 pub fn enum_iter_group_ids(&self) -> impl Iterator<Item = Result<u16, MCUmgrClientError>> {
1189 let mut i = 0;
1190 let mut num_elements = None;
1191
1192 std::iter::from_fn(move || -> Option<Result<u16, MCUmgrClientError>> {
1193 let mut num_elements_err = None;
1194 let num_elements =
1195 *num_elements.get_or_insert_with(|| match self.enum_get_group_count() {
1196 Ok(n) => n,
1197 Err(e) => {
1198 num_elements_err = Some(e);
1199 0
1200 }
1201 });
1202 if let Some(err) = num_elements_err {
1203 return Some(Err(err));
1204 }
1205
1206 if i >= num_elements {
1207 None
1208 } else {
1209 Some(match self.enum_get_group_id(i) {
1210 Ok(group_id) => {
1211 i += 1;
1212 Ok(group_id)
1213 }
1214 Err(e) => {
1215 i = num_elements;
1216 Err(e)
1217 }
1218 })
1219 }
1220 })
1221 }
1222
1223 pub fn enum_get_group_details(
1234 &self,
1235 groups: Option<&[u16]>,
1236 ) -> Result<Vec<commands::r#enum::GroupDetailsEntry>, MCUmgrClientError> {
1237 self.connection
1238 .execute_command(&commands::r#enum::GroupDetails { groups })
1239 .map(|ret| ret.groups)
1240 .map_err(Into::into)
1241 }
1242
1243 pub fn zephyr_erase_storage(&self) -> Result<(), MCUmgrClientError> {
1245 self.connection
1246 .execute_command(&commands::zephyr::EraseStorage)
1247 .map(Into::into)
1248 .map_err(Into::into)
1249 }
1250
1251 pub fn raw_command<T: commands::McuMgrCommand>(
1257 &self,
1258 command: &T,
1259 ) -> Result<T::Response, MCUmgrClientError> {
1260 self.connection.execute_command(command).map_err(Into::into)
1261 }
1262}