1#[allow(unused_imports)]
42use log::{debug, error, info, trace, warn};
43use rusb::{Context, Device as RusbDevice, Error as RusbError, UsbContext};
44use std::time::Duration;
45
46const DEFAULT_USB_TIMEOUT: Duration = Duration::from_secs(30);
48
49const USB_CLASS_APPLICATION_SPECIFIC: u8 = 0xFE;
51const USB_SUBCLASS_DFU: u8 = 0x01;
52
53const DFU_BLOCK_SIZE: usize = 2048;
55
56const USB_CLASS_INTERFACE_OUT: u8 = 0x21; const USB_CLASS_INTERFACE_IN: u8 = 0xA1; const STM32_DFU_CMD_SET_ADDRESS: u8 = 0x21;
62const STM32_DFU_CMD_ERASE: u8 = 0x41;
63#[allow(dead_code)]
64const STM32_DFU_CMD_READ_UNPROTECT: u8 = 0x92;
65
66#[derive(Debug, Clone, PartialEq)]
68#[repr(u8)]
69#[allow(dead_code)]
70enum Request {
71 Detach = 0,
72 Download = 1,
73 Upload = 2,
74 GetStatus = 3,
75 ClearStatus = 4,
76 GetState = 5,
77 Abort = 6,
78}
79
80impl Into<u8> for Request {
81 fn into(self) -> u8 {
82 self as u8
83 }
84}
85
86impl Request {
87 const fn fixed_length(&self) -> usize {
89 match self {
90 Request::Detach => 0,
91 Request::Download => 0,
92 Request::Upload => 0,
93 Request::GetStatus => 6,
94 Request::ClearStatus => 0,
95 Request::GetState => 1,
96 Request::Abort => 0,
97 }
98 }
99}
100
101#[derive(Debug, Clone, PartialEq)]
103#[repr(u8)]
104pub enum Status {
105 Ok = 0,
106 ErrTarget = 1,
107 ErrFile = 2,
108 ErrWrite = 3,
109 ErrErase = 4,
110 ErrCheckErased = 5,
111 ErrProg = 6,
112 ErrVerify = 7,
113 ErrAddress = 8,
114 ErrNotDone = 9,
115 ErrFirmware = 10,
116 ErrVendor = 11,
117 ErrUsbReset = 12,
118 ErrPowerOnReset = 13,
119 ErrUnknown = 14,
120 ErrStalledPkt = 15,
121}
122
123impl std::fmt::Display for Status {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 match self {
126 Status::Ok => write!(f, "OK"),
127 Status::ErrTarget => write!(f, "Error: Target"),
128 Status::ErrFile => write!(f, "Error: File"),
129 Status::ErrWrite => write!(f, "Error: Write"),
130 Status::ErrErase => write!(f, "Error: Erase"),
131 Status::ErrCheckErased => write!(f, "Error: Check Erased"),
132 Status::ErrProg => write!(f, "Error: Program"),
133 Status::ErrVerify => write!(f, "Error: Verify"),
134 Status::ErrAddress => write!(f, "Error: Address"),
135 Status::ErrNotDone => write!(f, "Error: Not Done"),
136 Status::ErrFirmware => write!(f, "Error: Firmware"),
137 Status::ErrVendor => write!(f, "Error: Vendor"),
138 Status::ErrUsbReset => write!(f, "Error: USB Reset"),
139 Status::ErrPowerOnReset => write!(f, "Error: Power On Reset"),
140 Status::ErrUnknown => write!(f, "Error: Unknown"),
141 Status::ErrStalledPkt => write!(f, "Error: Stalled Packet"),
142 }
143 }
144}
145
146#[derive(Debug, Clone, PartialEq)]
148#[repr(u8)]
149pub enum State {
150 AppIdle = 0,
151 AppDetach = 1,
152 DfuIdle = 2,
153 DfuDnloadSync = 3,
154 DfuDnloadBusy = 4,
155 DfuDnloadIdle = 5,
156 DfuManifestSync = 6,
157 DfuManifest = 7,
158 DfuManifestWaitReset = 8,
159 DfuUploadIdle = 9,
160 DfuError = 10,
161}
162
163impl std::fmt::Display for State {
164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 match self {
166 State::AppIdle => write!(f, "App Idle"),
167 State::AppDetach => write!(f, "App Detach"),
168 State::DfuIdle => write!(f, "DFU Idle"),
169 State::DfuDnloadSync => write!(f, "DFU Download Sync"),
170 State::DfuDnloadBusy => write!(f, "DFU Download Busy"),
171 State::DfuDnloadIdle => write!(f, "DFU Download Idle"),
172 State::DfuManifestSync => write!(f, "DFU Manifest Sync"),
173 State::DfuManifest => write!(f, "DFU Manifest"),
174 State::DfuManifestWaitReset => write!(f, "DFU Manifest Wait Reset"),
175 State::DfuUploadIdle => write!(f, "DFU Upload Idle"),
176 State::DfuError => write!(f, "DFU Error"),
177 }
178 }
179}
180
181struct DeviceStatus {
183 status: Status,
184 state: State,
185 poll_time: u32,
186 string: u8,
187}
188
189#[allow(dead_code)]
190impl DeviceStatus {
191 fn status(&self) -> Status {
192 self.status.clone()
193 }
194
195 fn state(&self) -> State {
196 self.state.clone()
197 }
198
199 fn poll_time(&self) -> u32 {
200 self.poll_time
201 }
202
203 fn string_index(&self) -> u8 {
204 self.string
205 }
206
207 fn is_error(&self) -> bool {
208 self.is_status_error() || self.is_state_error()
209 }
210
211 fn is_status_error(&self) -> bool {
212 self.status != Status::Ok
213 }
214
215 fn is_state_error(&self) -> bool {
216 self.state == State::DfuError
217 }
218
219 fn is_state_dfu_idle(&self) -> bool {
220 self.state == State::DfuIdle
221 }
222
223 fn is_download_busy(&self) -> bool {
224 self.state == State::DfuDnloadBusy
225 }
226
227 fn is_download_manifest(&self) -> bool {
228 self.state == State::DfuManifest || self.state == State::DfuManifestSync
229 }
230
231 fn from_packet(data: &[u8]) -> Result<Self, Error> {
232 if data.len() < Request::GetStatus.fixed_length() {
233 warn!("Invalid DFU device status packet length: {}", data.len());
234 return Err(Error::DfuInvalidDeviceStatus);
235 }
236
237 let status = match data[0] {
238 0 => Status::Ok,
239 1 => Status::ErrTarget,
240 2 => Status::ErrFile,
241 3 => Status::ErrWrite,
242 4 => Status::ErrErase,
243 5 => Status::ErrCheckErased,
244 6 => Status::ErrProg,
245 7 => Status::ErrVerify,
246 8 => Status::ErrAddress,
247 9 => Status::ErrNotDone,
248 10 => Status::ErrFirmware,
249 11 => Status::ErrVendor,
250 12 => Status::ErrUsbReset,
251 13 => Status::ErrPowerOnReset,
252 14 => Status::ErrUnknown,
253 15 => Status::ErrStalledPkt,
254 _ => {
255 warn!("Unknown DFU status code: {}", data[0]);
256 return Err(Error::DfuInvalidDeviceStatus)
257 }
258 };
259
260 let state = match data[4] {
261 0 => State::AppIdle,
262 1 => State::AppDetach,
263 2 => State::DfuIdle,
264 3 => State::DfuDnloadSync,
265 4 => State::DfuDnloadBusy,
266 5 => State::DfuDnloadIdle,
267 6 => State::DfuManifestSync,
268 7 => State::DfuManifest,
269 8 => State::DfuManifestWaitReset,
270 9 => State::DfuUploadIdle,
271 10 => State::DfuError,
272 _ => {
273 warn!("Unknown DFU state code: {}", data[4]);
274 return Err(Error::DfuInvalidDeviceStatus);
275 }
276 };
277
278 let poll_time = u32::from_le_bytes([data[1], data[2], data[3], 0]);
279 let string = data[5];
280
281 trace!("Device Status: status={}, state={}, poll_time={}ms, string={}", status, state, poll_time, string);
282
283 Ok(DeviceStatus {
284 status,
285 state,
286 poll_time,
287 string,
288 })
289 }
290}
291
292#[derive(Debug, Clone, PartialEq)]
295pub enum DfuType {
296 InternalFlash,
297 OptionBytes,
298 SystemMemory,
299 Unknown(String),
300}
301
302#[derive(Debug, Clone, PartialEq)]
304pub struct DeviceInfo {
305 pub vid: u16,
307 pub pid: u16,
309 pub interface: u8,
311 pub bus: u8,
313 pub address: u8,
315 pub alt: u8,
317 pub desc: String,
319 pub dfu_type: DfuType,
321}
322
323impl std::fmt::Display for DeviceInfo {
324 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
325 write!(
326 f,
327 "{:0>4X}:{:0>4X}",
328 self.vid, self.pid,
329 )
330 }
331}
332
333#[derive(Debug, Clone, PartialEq)]
340pub enum Error {
341 DeviceNotFound,
343 DfuStatus{
345 status: Status,
346 state: State,
347 },
348 DfuInvalidDeviceStatus,
350 DfuSetAddressFailed(Status, State),
352 UsbContext(RusbError),
353 UsbDeviceEnumeration(RusbError),
354 UsbDeviceOpen(RusbError),
355 UsbKernelDriverDetach(RusbError),
356 UsbClaimInterface(RusbError),
357 UsbSetAltSetting(RusbError),
358 UsbControlTransfer(RusbError),
359}
360
361impl std::fmt::Display for Error {
362 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
363 match self {
364 Error::DeviceNotFound => write!(f, "DFU Device Not Found"),
365 Error::DfuStatus{ status, state } => write!(f, "DFU Status Error: status code {}, state {}", status, state),
366 Error::DfuInvalidDeviceStatus => write!(f, "DFU Invalid Device Status"),
367 Error::DfuSetAddressFailed(status, state) => write!(f, "DFU Set Address Failed: status code {}, state {}", status, state),
368 Error::UsbContext(e) => write!(f, "USB Context Error: {}", e),
369 Error::UsbDeviceEnumeration(e) => write!(f, "USB Device Enumeration Error: {}", e),
370 Error::UsbDeviceOpen(e) => write!(f, "Device Open Error: {}", e),
371 Error::UsbKernelDriverDetach(e) => write!(f, "Kernel Driver Detach Error: {}", e),
372 Error::UsbClaimInterface(e) => write!(f, "Claim Interface Error: {}", e),
373 Error::UsbSetAltSetting(e) => write!(f, "Set Alternate Setting Error: {}", e),
374 Error::UsbControlTransfer(e) => write!(f, "Control Transfer Error: {}", e),
375 }
376 }
377}
378
379impl Error {
380 pub fn usb_stack_error(&self) -> Option<&RusbError> {
382 match self {
383 Error::UsbContext(e) => Some(e),
384 Error::UsbDeviceEnumeration(e) => Some(e),
385 Error::UsbDeviceOpen(e) => Some(e),
386 Error::UsbKernelDriverDetach(e) => Some(e),
387 Error::UsbClaimInterface(e) => Some(e),
388 Error::UsbSetAltSetting(e) => Some(e),
389 Error::UsbControlTransfer(e) => Some(e),
390 _ => None,
391 }
392 }
393}
394
395#[derive(Debug, Clone, PartialEq)]
427pub struct Device {
428 info: DeviceInfo,
429 timeout: Duration,
430}
431
432impl std::fmt::Display for Device {
433 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
434 write!(
435 f,
436 "{} ({:04X}:{:04X})",
437 self.info.desc,
438 self.info.vid,
439 self.info.pid,
440 )
441 }
442}
443
444impl Device {
445 pub fn search(filter: Option<DfuType>) -> Result<Vec<Self>, Error> {
455 let context = Context::new()
456 .map_err(|e| Error::UsbContext(e))?;
457
458 let devices = context.devices()
459 .map_err(|e| Error::UsbDeviceEnumeration(e))?;
460
461 let mut dfu_devices = Vec::new();
462
463 for device in devices.iter() {
464 dfu_devices.extend(check_device_for_dfu(&device));
465 }
466
467 let dfu_devices = if let Some(filter) = filter {
468 check_device_for_dfu_type(filter, dfu_devices)
469 } else {
470 dfu_devices
471 };
472
473 let dfu_devices: Vec<Self> = dfu_devices.into_iter()
474 .map(|info| Device::new(info))
475 .collect();
476
477 Ok(dfu_devices)
478 }
479
480 pub fn new(info: DeviceInfo) -> Self {
493 Device {
494 info,
495 timeout: DEFAULT_USB_TIMEOUT,
496 }
497 }
498
499 pub fn set_timeout(&mut self, timeout: Duration) {
501 self.timeout = timeout;
502 }
503
504 pub fn info(&self) -> &DeviceInfo {
506 &self.info
507 }
508
509 pub fn upload(&self, address: u32, buf: &mut [u32]) -> Result<(), Error> {
519 trace!("Starting DFU upload from address 0x{:08X} for {} words", address, buf.len());
520 let byte_count = buf.len() * 4;
521 let mut bytes = vec![0u8; byte_count];
522
523 let (_context, handle) = self.open_device()?;
525 trace!("DFU device opened successfully");
526
527 self.set_address(&handle, address)?;
529 trace!("DFU address set successfully");
530
531 self.abort(&handle, false)?;
533 trace!("DFU aborted to enter dfuIDLE state");
534
535 let total_blocks = (byte_count + DFU_BLOCK_SIZE - 1) / DFU_BLOCK_SIZE;
537
538 for block in 0..total_blocks {
540 let block_data = self.read_block(&handle, block)?;
541 let offset = block * DFU_BLOCK_SIZE;
542 let end = (offset + block_data.len()).min(byte_count);
543 bytes[offset..end].copy_from_slice(&block_data[..end - offset]);
544 }
545 trace!("DFU upload completed successfully");
546
547 self.abort(&handle, true)?;
549 trace!("DFU session aborted successfully");
550
551 for (ii, chunk) in bytes.chunks_exact(4).enumerate() {
553 buf[ii] = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
554 }
555
556 Ok(())
557 }
558
559 pub fn erase(&self, address: u32, length: usize, page_size: usize) -> Result<(), Error> {
574 trace!("Starting DFU erase at address 0x{:08X} for {} bytes with page size {}",
575 address, length, page_size);
576
577 let (_context, handle) = self.open_device()?;
578 trace!("DFU device opened successfully");
579
580 let total_pages = (length + page_size - 1) / page_size;
582
583 for page in 0..total_pages {
585 let page_address = address + (page * page_size) as u32;
586 trace!("Erasing page {} at address 0x{:08X}", page, page_address);
587 self.erase_page(&handle, page_address)?;
588 }
589
590 trace!("DFU erase completed successfully");
591 Ok(())
592 }
593
594 pub fn mass_erase(&self) -> Result<(), Error> {
599 trace!("Starting DFU mass erase");
600
601 let (_context, handle) = self.open_device()?;
602 trace!("DFU device opened successfully");
603
604 let cmd = vec![STM32_DFU_CMD_ERASE];
606
607 handle.write_control(
608 USB_CLASS_INTERFACE_OUT,
609 Request::Download.into(),
610 0,
611 self.info.interface as u16,
612 &cmd,
613 self.timeout
614 ).map_err(|e| Error::UsbControlTransfer(e))?;
615
616 self.get_status_check_download_busy(&handle)?;
618 self.get_status(&handle)?;
619
620 trace!("DFU mass erase completed successfully");
621 Ok(())
622 }
623
624 pub fn download(&self, address: u32, data: &[u32]) -> Result<(), Error> {
633 trace!("Starting DFU download to address 0x{:08X} for {} words", address, data.len());
634
635 let mut bytes = Vec::with_capacity(data.len() * 4);
637 for word in data {
638 bytes.extend_from_slice(&word.to_le_bytes());
639 }
640
641 let (_context, handle) = self.open_device()?;
642 trace!("DFU device opened successfully");
643
644 self.set_address(&handle, address)?;
646 trace!("DFU address set successfully");
647
648 let total_blocks = (bytes.len() + DFU_BLOCK_SIZE - 1) / DFU_BLOCK_SIZE;
650
651 for block in 0..total_blocks {
653 let start = block * DFU_BLOCK_SIZE;
654 let end = (start + DFU_BLOCK_SIZE).min(bytes.len());
655
656 trace!("Writing block {} of {}", block + 1, total_blocks);
657 self.write_block(&handle, block, &bytes[start..end])?;
658 }
659 trace!("DFU download completed successfully");
660
661 handle.write_control(
663 USB_CLASS_INTERFACE_OUT,
664 Request::Download.into(),
665 0,
666 self.info.interface as u16,
667 &[],
668 self.timeout
669 ).map_err(|e| Error::UsbControlTransfer(e))?;
670
671 self.get_status(&handle)?;
673
674 trace!("DFU download session completed successfully");
675 Ok(())
676 }
677}
678
679impl Device {
681 fn clear_status(&self, handle: &rusb::DeviceHandle<Context>) -> Result<(), Error> {
682 trace!("Clearing DFU status");
683 handle.write_control(
684 USB_CLASS_INTERFACE_OUT,
685 Request::ClearStatus.into(),
686 0,
687 self.info.interface as u16,
688 &[],
689 self.timeout
690 ).map_err(|e| Error::UsbControlTransfer(e))?;
691
692 Ok(())
693 }
694
695 fn open_device(&self) -> Result<(Context, rusb::DeviceHandle<Context>), Error> {
696 trace!("Opening DFU device: {:?}", self.info);
697 let context = Context::new()
698 .map_err(|e| Error::UsbContext(e))?;
699
700 let devices = context.devices()
701 .map_err(|e| Error::UsbDeviceEnumeration(e))?;
702
703 let device = devices.iter()
704 .find(|d| d.bus_number() == self.info.bus && d.address() == self.info.address)
705 .ok_or_else(|| Error::DeviceNotFound)?;
706
707 #[allow(unused_mut)] let mut handle = device.open()
709 .map_err(|e| Error::UsbDeviceOpen(e))?;
710
711 handle.set_active_configuration(1)
713 .map_err(|e| Error::UsbDeviceOpen(e))?;
714
715 if let Ok(true) = handle.kernel_driver_active(self.info.interface) {
717 handle.detach_kernel_driver(self.info.interface)
718 .map_err(|e| Error::UsbKernelDriverDetach(e))?;
719 }
720
721 handle.claim_interface(self.info.interface)
722 .map_err(|e| Error::UsbClaimInterface(e))?;
723
724 handle.set_alternate_setting(self.info.interface, self.info.alt)
725 .map_err(|e| Error::UsbSetAltSetting(e))?;
726
727 let needs_clear = match self.get_status(&handle) {
729 Ok(status) => status.is_state_error(),
730 Err(_) => true, };
732
733 if needs_clear {
734 self.clear_status(&handle)?;
735 self.get_status(&handle)?;
736 }
737
738 self.abort(&handle, false)?;
740
741 Ok((context, handle))
742 }
743
744 fn set_address(&self, handle: &rusb::DeviceHandle<Context>, address: u32) -> Result<(), Error> {
745 trace!("Setting DFU address to 0x{:08X}", address);
746 trace!("DFU info {:?}", self.info);
747
748 let mut cmd = vec![STM32_DFU_CMD_SET_ADDRESS];
750 cmd.extend_from_slice(&address.to_le_bytes());
751
752 handle.write_control(
753 USB_CLASS_INTERFACE_OUT,
754 Request::Download.into(),
755 0, self.info.interface as u16,
757 &cmd,
758 self.timeout
759 )
760 .inspect_err(|e| warn!("Control transfer error during set_address: {e}"))
761 .map_err(|e| Error::UsbControlTransfer(e))?;
762
763 self.get_status_check_download_busy(handle)?;
765 self.get_status(handle)?;
766
767 Ok(())
768 }
769
770 fn erase_page(&self, handle: &rusb::DeviceHandle<Context>, address: u32) -> Result<(), Error> {
771 trace!("Erasing page at address 0x{:08X}", address);
772
773 let mut cmd = vec![STM32_DFU_CMD_ERASE];
775 cmd.extend_from_slice(&address.to_le_bytes());
776
777 handle.write_control(
778 USB_CLASS_INTERFACE_OUT,
779 Request::Download.into(),
780 0, self.info.interface as u16,
782 &cmd,
783 self.timeout
784 ).map_err(|e| Error::UsbControlTransfer(e))?;
785
786 self.get_status_check_download_busy(handle)?;
788 self.get_status(handle)?;
789
790 Ok(())
791 }
792
793 fn abort(&self, handle: &rusb::DeviceHandle<Context>, get_status: bool) -> Result<(), Error> {
794 trace!("Sending DFU abort");
795 handle.write_control(
796 USB_CLASS_INTERFACE_OUT,
797 Request::Abort.into(),
798 0,
799 self.info.interface as u16,
800 &[],
801 self.timeout
802 ).map_err(|e| Error::UsbControlTransfer(e))?;
803
804 if get_status {
805 self.get_status(handle)?;
806 }
807
808 Ok(())
809 }
810
811 fn read_block(&self, handle: &rusb::DeviceHandle<Context>, block: usize) -> Result<Vec<u8>, Error> {
812 trace!("Reading DFU block {}", block);
813 let mut buf = vec![0u8; DFU_BLOCK_SIZE];
814
815 let bytes_read = handle.read_control(
816 USB_CLASS_INTERFACE_IN,
817 Request::Upload.into(),
818 (2 + block) as u16, self.info.interface as u16,
820 &mut buf,
821 self.timeout
822 ).map_err(|e| Error::UsbControlTransfer(e))?;
823
824 buf.truncate(bytes_read);
825
826 self.get_status(handle)?;
827 self.get_status(handle)?;
828
829 Ok(buf)
830 }
831
832 fn write_block(&self, handle: &rusb::DeviceHandle<Context>, block: usize, data: &[u8]) -> Result<(), Error> {
833 trace!("Writing DFU block {} ({} bytes)", block, data.len());
834
835 let mut block_data = vec![0xFF; DFU_BLOCK_SIZE];
837 block_data[..data.len()].copy_from_slice(data);
838
839 handle.write_control(
840 USB_CLASS_INTERFACE_OUT,
841 Request::Download.into(),
842 (2 + block) as u16, self.info.interface as u16,
844 &block_data,
845 self.timeout
846 ).map_err(|e| Error::UsbControlTransfer(e))?;
847
848 self.get_status_check_download_busy(handle)?;
850 self.get_status(handle)?;
851
852 Ok(())
853 }
854
855 fn get_status_check_download_busy(&self, handle: &rusb::DeviceHandle<Context>) -> Result<(), Error> {
857 let status = self.get_status_error_flag(handle, true)?;
858 if !status.is_download_busy() {
859 return Err(Error::DfuStatus{ status: status.status(), state: status.state() })
860 }
861 Ok(())
862 }
863
864 fn get_status(&self, handle: &rusb::DeviceHandle<Context>) -> Result<DeviceStatus, Error> {
866 self.get_status_error_flag(handle, false)
867 }
868
869 fn get_status_error_flag(&self, handle: &rusb::DeviceHandle<Context>, error_ok: bool) -> Result<DeviceStatus, Error> {
870 trace!("Getting DFU status");
871 let mut data = [0u8; Request::GetStatus.fixed_length()];
872 handle.read_control(
873 USB_CLASS_INTERFACE_IN,
874 Request::GetStatus.into(),
875 0,
876 self.info.interface as u16,
877 &mut data,
878 self.timeout
879 ).map_err(|e| Error::UsbControlTransfer(e))?;
880
881 let status = DeviceStatus::from_packet(&data)?;
882
883 std::thread::sleep(std::time::Duration::from_millis(status.poll_time() as u64));
885
886 if !error_ok && status.is_error() {
887 return Err(Error::DfuStatus{ status: status.status(), state: status.state() });
888 }
889
890 Ok(status)
891 }
892}
893
894fn check_device_for_dfu_type(filter: DfuType, device_info: Vec<DeviceInfo>) -> Vec<DeviceInfo> {
895 device_info.into_iter()
896 .filter(|info| info.dfu_type == filter)
897 .collect()
898}
899
900fn check_device_for_dfu(device: &RusbDevice<Context>) -> Vec<DeviceInfo> {
901 let mut results = Vec::new();
902
903 let desc = match device.device_descriptor() {
904 Ok(d) => d,
905 Err(_) => return results,
906 };
907
908 let vid = desc.vendor_id();
909 let pid = desc.product_id();
910 let bus = device.bus_number();
911 let address = device.address();
912
913 for config_idx in 0..desc.num_configurations() {
914 let config = match device.config_descriptor(config_idx) {
915 Ok(c) => c,
916 Err(_) => continue,
917 };
918
919 for interface in config.interfaces() {
920 for iface_desc in interface.descriptors() {
921 if iface_desc.class_code() == USB_CLASS_APPLICATION_SPECIFIC && iface_desc.sub_class_code() == USB_SUBCLASS_DFU {
922 let alt = iface_desc.setting_number();
923 let desc_string = get_interface_string(device, &iface_desc);
924 let dfu_type = parse_dfu_type(&desc_string);
925
926 results.push(DeviceInfo {
927 vid,
928 pid,
929 interface: interface.number(),
930 bus,
931 address,
932 alt,
933 desc: desc_string,
934 dfu_type,
935 });
936 }
937 }
938 }
939 }
940
941 results
942}
943
944fn get_interface_string(
945 device: &RusbDevice<Context>,
946 iface_desc: &rusb::InterfaceDescriptor,
947) -> String {
948 device.open()
949 .and_then(|handle| {
950 handle.read_string_descriptor_ascii(iface_desc.description_string_index().unwrap_or(0))
951 })
952 .unwrap_or_else(|_| "Unknown".to_string())
953}
954
955fn parse_dfu_type(desc: &str) -> DfuType {
956 if let Some(at_pos) = desc.find('@') {
958 if let Some(slash_pos) = desc[at_pos..].find('/') {
959 let region = desc[at_pos + 1..at_pos + slash_pos].trim();
960
961 return match region {
962 s if s.contains("Internal Flash") => DfuType::InternalFlash,
963 s if s.contains("Option Bytes") => DfuType::OptionBytes,
964 s if s.contains("System Memory") || s.contains("Bootloader") => DfuType::SystemMemory,
965 _ => DfuType::Unknown(region.to_string()),
966 };
967 }
968 }
969
970 DfuType::Unknown(desc.to_string())
971}