1use async_io::Timer;
49#[allow(unused_imports)]
50use log::{debug, error, info, trace, warn};
51use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient, TransferError};
52use nusb::{Device as NusbDevice, DeviceInfo as NusbDeviceInfo, Error as NusbError, Interface};
53use std::time::Duration;
54
55pub const DEFAULT_USB_TIMEOUT: Duration = Duration::from_secs(30);
57
58const USB_CLASS_APPLICATION_SPECIFIC: u8 = 0xFE;
60const USB_SUBCLASS_DFU: u8 = 0x01;
61
62const DFU_BLOCK_SIZE: usize = 2048;
64
65const STM32_DFU_CMD_SET_ADDRESS: u8 = 0x21;
67const STM32_DFU_CMD_ERASE: u8 = 0x41;
68#[allow(dead_code)]
69const STM32_DFU_CMD_READ_UNPROTECT: u8 = 0x92;
70
71const LANGUAGE_ID: u16 = 0x0409; #[derive(Debug, Clone, PartialEq)]
76#[repr(u8)]
77#[allow(dead_code)]
78enum Request {
79 Detach = 0,
80 Download = 1,
81 Upload = 2,
82 GetStatus = 3,
83 ClearStatus = 4,
84 GetState = 5,
85 Abort = 6,
86}
87
88impl From<Request> for u8 {
89 fn from(val: Request) -> Self {
90 val as u8
91 }
92}
93
94impl Request {
95 const fn fixed_length(&self) -> usize {
97 match self {
98 Request::Detach => 0,
99 Request::Download => 0,
100 Request::Upload => 0,
101 Request::GetStatus => 6,
102 Request::ClearStatus => 0,
103 Request::GetState => 1,
104 Request::Abort => 0,
105 }
106 }
107}
108
109#[derive(Debug, Clone, PartialEq)]
111#[repr(u8)]
112pub enum Status {
113 Ok = 0,
114 ErrTarget = 1,
115 ErrFile = 2,
116 ErrWrite = 3,
117 ErrErase = 4,
118 ErrCheckErased = 5,
119 ErrProg = 6,
120 ErrVerify = 7,
121 ErrAddress = 8,
122 ErrNotDone = 9,
123 ErrFirmware = 10,
124 ErrVendor = 11,
125 ErrUsbReset = 12,
126 ErrPowerOnReset = 13,
127 ErrUnknown = 14,
128 ErrStalledPkt = 15,
129}
130
131impl std::fmt::Display for Status {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 match self {
134 Status::Ok => write!(f, "OK"),
135 Status::ErrTarget => write!(f, "Error: Target"),
136 Status::ErrFile => write!(f, "Error: File"),
137 Status::ErrWrite => write!(f, "Error: Write"),
138 Status::ErrErase => write!(f, "Error: Erase"),
139 Status::ErrCheckErased => write!(f, "Error: Check Erased"),
140 Status::ErrProg => write!(f, "Error: Program"),
141 Status::ErrVerify => write!(f, "Error: Verify"),
142 Status::ErrAddress => write!(f, "Error: Address"),
143 Status::ErrNotDone => write!(f, "Error: Not Done"),
144 Status::ErrFirmware => write!(f, "Error: Firmware"),
145 Status::ErrVendor => write!(f, "Error: Vendor"),
146 Status::ErrUsbReset => write!(f, "Error: USB Reset"),
147 Status::ErrPowerOnReset => write!(f, "Error: Power On Reset"),
148 Status::ErrUnknown => write!(f, "Error: Unknown"),
149 Status::ErrStalledPkt => write!(f, "Error: Stalled Packet"),
150 }
151 }
152}
153
154#[derive(Debug, Clone, PartialEq)]
156#[repr(u8)]
157pub enum State {
158 AppIdle = 0,
159 AppDetach = 1,
160 DfuIdle = 2,
161 DfuDnloadSync = 3,
162 DfuDnloadBusy = 4,
163 DfuDnloadIdle = 5,
164 DfuManifestSync = 6,
165 DfuManifest = 7,
166 DfuManifestWaitReset = 8,
167 DfuUploadIdle = 9,
168 DfuError = 10,
169}
170
171impl std::fmt::Display for State {
172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173 match self {
174 State::AppIdle => write!(f, "App Idle"),
175 State::AppDetach => write!(f, "App Detach"),
176 State::DfuIdle => write!(f, "DFU Idle"),
177 State::DfuDnloadSync => write!(f, "DFU Download Sync"),
178 State::DfuDnloadBusy => write!(f, "DFU Download Busy"),
179 State::DfuDnloadIdle => write!(f, "DFU Download Idle"),
180 State::DfuManifestSync => write!(f, "DFU Manifest Sync"),
181 State::DfuManifest => write!(f, "DFU Manifest"),
182 State::DfuManifestWaitReset => write!(f, "DFU Manifest Wait Reset"),
183 State::DfuUploadIdle => write!(f, "DFU Upload Idle"),
184 State::DfuError => write!(f, "DFU Error"),
185 }
186 }
187}
188
189struct DeviceStatus {
191 status: Status,
192 state: State,
193 poll_time: u32,
194 string: u8,
195}
196
197#[allow(dead_code)]
198impl DeviceStatus {
199 fn status(&self) -> Status {
200 self.status.clone()
201 }
202
203 fn state(&self) -> State {
204 self.state.clone()
205 }
206
207 fn poll_time(&self) -> u32 {
208 self.poll_time
209 }
210
211 fn string_index(&self) -> u8 {
212 self.string
213 }
214
215 fn is_error(&self) -> bool {
216 self.is_status_error() || self.is_state_error()
217 }
218
219 fn is_status_error(&self) -> bool {
220 self.status != Status::Ok
221 }
222
223 fn is_state_error(&self) -> bool {
224 self.state == State::DfuError
225 }
226
227 fn is_state_dfu_idle(&self) -> bool {
228 self.state == State::DfuIdle
229 }
230
231 fn is_download_busy(&self) -> bool {
232 self.state == State::DfuDnloadBusy
233 }
234
235 fn is_download_manifest(&self) -> bool {
236 self.state == State::DfuManifest || self.state == State::DfuManifestSync
237 }
238
239 fn from_packet(data: &[u8]) -> Result<Self, Error> {
240 if data.len() < Request::GetStatus.fixed_length() {
241 warn!("Invalid DFU device status packet length: {}", data.len());
242 return Err(Error::DfuInvalidDeviceStatus);
243 }
244
245 let status = match data[0] {
246 0 => Status::Ok,
247 1 => Status::ErrTarget,
248 2 => Status::ErrFile,
249 3 => Status::ErrWrite,
250 4 => Status::ErrErase,
251 5 => Status::ErrCheckErased,
252 6 => Status::ErrProg,
253 7 => Status::ErrVerify,
254 8 => Status::ErrAddress,
255 9 => Status::ErrNotDone,
256 10 => Status::ErrFirmware,
257 11 => Status::ErrVendor,
258 12 => Status::ErrUsbReset,
259 13 => Status::ErrPowerOnReset,
260 14 => Status::ErrUnknown,
261 15 => Status::ErrStalledPkt,
262 _ => {
263 warn!("Unknown DFU status code: {}", data[0]);
264 return Err(Error::DfuInvalidDeviceStatus);
265 }
266 };
267
268 let state = match data[4] {
269 0 => State::AppIdle,
270 1 => State::AppDetach,
271 2 => State::DfuIdle,
272 3 => State::DfuDnloadSync,
273 4 => State::DfuDnloadBusy,
274 5 => State::DfuDnloadIdle,
275 6 => State::DfuManifestSync,
276 7 => State::DfuManifest,
277 8 => State::DfuManifestWaitReset,
278 9 => State::DfuUploadIdle,
279 10 => State::DfuError,
280 _ => {
281 warn!("Unknown DFU state code: {}", data[4]);
282 return Err(Error::DfuInvalidDeviceStatus);
283 }
284 };
285
286 let poll_time = u32::from_le_bytes([data[1], data[2], data[3], 0]);
287 let string = data[5];
288
289 trace!(
290 "Device Status: status={}, state={}, poll_time={}ms, string={}",
291 status, state, poll_time, string
292 );
293
294 Ok(DeviceStatus {
295 status,
296 state,
297 poll_time,
298 string,
299 })
300 }
301}
302
303#[derive(Debug, Clone, PartialEq)]
306pub enum DfuType {
307 InternalFlash,
308 OptionBytes,
309 SystemMemory,
310 Unknown(String),
311}
312
313impl std::fmt::Display for DfuType {
314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315 match self {
316 DfuType::InternalFlash => write!(f, "Internal Flash"),
317 DfuType::OptionBytes => write!(f, "Option Bytes"),
318 DfuType::SystemMemory => write!(f, "System Memory"),
319 DfuType::Unknown(desc) => write!(f, "Unknown ({})", desc),
320 }
321 }
322}
323
324#[derive(Debug, Clone, PartialEq)]
326pub struct DeviceInfo {
327 pub vid: u16,
329 pub pid: u16,
331 pub bus: String,
333 pub address: u8,
335 pub dfu: DfuInfo,
337}
338
339impl std::fmt::Display for DeviceInfo {
340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341 write!(f, "{:0>4X}:{:0>4X}", self.vid, self.pid)
342 }
343}
344
345impl DeviceInfo {
346 pub fn from_nusb(info: &NusbDeviceInfo, dfu: DfuInfo) -> Self {
347 let vid = info.vendor_id();
348 let pid = info.product_id();
349 let bus = info.bus_id().to_string();
350 let address = info.device_address();
351
352 DeviceInfo {
353 vid,
354 pid,
355 bus,
356 address,
357 dfu,
358 }
359 }
360
361 pub fn dfu_type(&self) -> &DfuType {
362 &self.dfu.dfu_type
363 }
364
365 pub fn is_dfu_type(&self, dfu_type: &DfuType) -> bool {
366 self.dfu.dfu_type == *dfu_type
367 }
368
369 pub fn interface(&self) -> u8 {
370 self.dfu.interface
371 }
372
373 pub fn vid(&self) -> u16 {
374 self.vid
375 }
376
377 pub fn pid(&self) -> u16 {
378 self.pid
379 }
380}
381
382#[derive(Debug, Clone, PartialEq)]
384pub struct DfuInfo {
385 pub interface: u8,
387 pub alt: u8,
389 pub desc: String,
391 pub dfu_type: DfuType,
393}
394
395impl std::fmt::Display for DfuInfo {
396 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
397 write!(
398 f,
399 "Interface {}, Alt {}, Type: {}, Desc: {}",
400 self.interface, self.alt, self.dfu_type, self.desc
401 )
402 }
403}
404
405#[derive(Debug, Clone)]
413pub enum Error {
414 DeviceNotFound,
416 DfuStatus {
418 status: Status,
419 state: State,
420 },
421 DfuInvalidDeviceStatus,
423 DfuSetAddressFailed(Status, State),
425 UsbContext(NusbError),
426 UsbDeviceEnumeration(NusbError),
427 UsbDeviceOpen(NusbError),
428 UsbKernelDriverDetach(NusbError),
429 UsbClaimInterface(NusbError),
430 UsbSetAltSetting(NusbError),
431 UsbControlTransfer(TransferError),
432}
433
434impl std::fmt::Display for Error {
435 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
436 match self {
437 Error::DeviceNotFound => write!(f, "DFU Device Not Found"),
438 Error::DfuStatus { status, state } => write!(
439 f,
440 "DFU Status Error: status code {}, state {}",
441 status, state
442 ),
443 Error::DfuInvalidDeviceStatus => write!(f, "DFU Invalid Device Status"),
444 Error::DfuSetAddressFailed(status, state) => write!(
445 f,
446 "DFU Set Address Failed: status code {}, state {}",
447 status, state
448 ),
449 Error::UsbContext(e) => write!(f, "USB Context Error: {}", e),
450 Error::UsbDeviceEnumeration(e) => write!(f, "USB Device Enumeration Error: {}", e),
451 Error::UsbDeviceOpen(e) => write!(f, "Device Open Error: {}", e),
452 Error::UsbKernelDriverDetach(e) => write!(f, "Kernel Driver Detach Error: {}", e),
453 Error::UsbClaimInterface(e) => write!(f, "Claim Interface Error: {}", e),
454 Error::UsbSetAltSetting(e) => write!(f, "Set Alternate Setting Error: {}", e),
455 Error::UsbControlTransfer(e) => write!(f, "Control Transfer Error: {}", e),
456 }
457 }
458}
459
460#[derive(Debug, Clone)]
461pub enum UsbStackError {
462 Nusb(NusbError),
463 Transfer(TransferError),
464}
465
466impl Error {
467 pub fn usb_stack_error(&self) -> Option<UsbStackError> {
469 match self {
470 Error::UsbContext(e) => Some(UsbStackError::Nusb(e.clone())),
471 Error::UsbDeviceEnumeration(e) => Some(UsbStackError::Nusb(e.clone())),
472 Error::UsbDeviceOpen(e) => Some(UsbStackError::Nusb(e.clone())),
473 Error::UsbKernelDriverDetach(e) => Some(UsbStackError::Nusb(e.clone())),
474 Error::UsbClaimInterface(e) => Some(UsbStackError::Nusb(e.clone())),
475 Error::UsbSetAltSetting(e) => Some(UsbStackError::Nusb(e.clone())),
476 Error::UsbControlTransfer(e) => Some(UsbStackError::Transfer(*e)),
477 _ => None,
478 }
479 }
480}
481
482#[derive(Debug)]
484#[allow(dead_code)]
485struct Handle {
486 device: NusbDevice,
487 interface: Interface,
488}
489
490#[derive(Debug, Clone)]
525pub struct Device {
526 info: DeviceInfo,
527 nusb_info: NusbDeviceInfo,
528 timeout: Duration,
529}
530
531impl PartialEq for Device {
532 fn eq(&self, other: &Self) -> bool {
533 self.info == other.info
534 }
535}
536
537impl std::fmt::Display for Device {
538 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
539 write!(
540 f,
541 "{} ({:04X}:{:04X})",
542 self.nusb_info.product_string().unwrap_or("Unknown Device"),
543 self.nusb_info.vendor_id(),
544 self.nusb_info.product_id(),
545 )
546 }
547}
548
549impl Device {
550 pub fn from_nusb(nusb_info: NusbDeviceInfo, dfu_info: DfuInfo) -> Self {
564 let info = DeviceInfo::from_nusb(&nusb_info, dfu_info);
565
566 Device {
567 info,
568 nusb_info,
569 timeout: DEFAULT_USB_TIMEOUT,
570 }
571 }
572
573 pub async fn from_device_info(info: DeviceInfo) -> Result<Self, Error> {
574 let devices = nusb::list_devices()
576 .await
577 .map_err(Error::UsbDeviceEnumeration)?;
578
579 let nusb_info = devices
581 .into_iter()
582 .find(|d| {
583 d.vendor_id() == info.vid
584 && d.product_id() == info.pid
585 && d.bus_id() == info.bus
586 && d.device_address() == info.address
587 })
588 .ok_or(Error::DeviceNotFound)?;
589
590 Ok(Device {
591 info,
592 nusb_info,
593 timeout: DEFAULT_USB_TIMEOUT,
594 })
595 }
596
597 pub fn set_timeout(&mut self, timeout: Duration) {
599 debug!("Setting USB timeout to {:?}", timeout);
600 self.timeout = timeout;
601 }
602
603 pub fn info(&self) -> &DeviceInfo {
605 &self.info
606 }
607
608 pub async fn upload(&self, address: u32, length: usize) -> Result<Vec<u8>, Error> {
618 trace!(
619 "Starting DFU upload from address 0x{:08X} for {} bytes",
620 address, length
621 );
622 let mut bytes = vec![0u8; length];
623
624 let handle = self.open().await?;
626 trace!("DFU device opened successfully");
627
628 self.set_address(&handle, address).await?;
630 trace!("DFU address set successfully");
631
632 self.abort(&handle, false).await?;
634 trace!("DFU aborted to enter dfuIDLE state");
635
636 let total_blocks = length.div_ceil(DFU_BLOCK_SIZE);
638
639 for block in 0..total_blocks {
641 let block_data = self.read_block(&handle, block).await?;
642 let offset = block * DFU_BLOCK_SIZE;
643 let end = (offset + block_data.len()).min(length);
644 bytes[offset..end].copy_from_slice(&block_data);
645 }
646 trace!("DFU upload completed successfully");
647
648 self.abort(&handle, true).await?;
650 trace!("DFU session aborted successfully");
651
652 Ok(bytes)
653 }
654
655 pub async fn erase(&self, address: u32, length: usize, page_size: usize) -> Result<(), Error> {
670 trace!(
671 "Starting DFU erase at address 0x{:08X} for {} bytes with page size {}",
672 address, length, page_size
673 );
674
675 let handle = self.open().await?;
676 trace!("DFU device opened successfully");
677
678 let total_pages = length.div_ceil(page_size);
680
681 for page in 0..total_pages {
683 let page_address = address + (page * page_size) as u32;
684 trace!("Erasing page {} at address 0x{:08X}", page, page_address);
685 self.erase_page(&handle, page_address).await?;
686 }
687
688 trace!("DFU erase completed successfully");
689 Ok(())
690 }
691
692 pub async fn mass_erase(&self) -> Result<(), Error> {
697 trace!("Starting DFU mass erase");
698
699 let handle = self.open().await?;
700 trace!("DFU device opened successfully");
701
702 let cmd = vec![STM32_DFU_CMD_ERASE];
704 self.control_out(&handle, Request::Download, 0, &cmd)
705 .await?;
706
707 self.get_status_check_download_busy(&handle).await?;
709
710 self.get_status(&handle).await?;
712
713 trace!("DFU mass erase completed successfully");
714 Ok(())
715 }
716
717 pub async fn download(&self, address: u32, data: &[u8]) -> Result<(), Error> {
726 trace!(
727 "Starting DFU download to address 0x{:08X} for {} bytes",
728 address,
729 data.len()
730 );
731
732 let handle = self.open().await?;
733 trace!("DFU device opened successfully");
734
735 self.set_address(&handle, address).await?;
737 trace!("DFU address set successfully");
738
739 let total_blocks = data.len().div_ceil(DFU_BLOCK_SIZE);
741
742 for block in 0..total_blocks {
744 let start = block * DFU_BLOCK_SIZE;
745 let end = (start + DFU_BLOCK_SIZE).min(data.len());
746
747 trace!("Writing block {} of {}", block + 1, total_blocks);
748 self.write_block(&handle, block, &data[start..end]).await?;
749 }
750 trace!("DFU download completed successfully");
751
752 self.control_out(&handle, Request::Download, 0, &[]).await?;
754
755 self.get_status(&handle).await?;
757
758 trace!("DFU download session completed successfully");
759 Ok(())
760 }
761}
762
763impl Device {
765 fn is_dfu_type(&self, dfu_type: &DfuType) -> bool {
766 self.info.is_dfu_type(dfu_type)
767 }
768
769 fn interface(&self) -> u8 {
770 self.info.interface()
771 }
772
773 async fn open(&self) -> Result<Handle, Error> {
774 trace!("Opening DFU device: {:?}", self.info);
775 let nusb_device = self.nusb_info.open().await.map_err(Error::UsbDeviceOpen)?;
776
777 let _ = nusb_device
779 .active_configuration()
780 .map_err(|e| Error::UsbDeviceOpen(e.into()))?;
781
782 let interface = self.info.interface();
784 let interface = nusb_device
785 .detach_and_claim_interface(interface)
786 .await
787 .map_err(Error::UsbClaimInterface)?;
788
789 let handle = Handle {
791 device: nusb_device,
792 interface,
793 };
794
795 let needs_clear = match self.get_status(&handle).await {
797 Ok(status) => status.is_state_error(),
798 Err(_) => true,
799 };
800
801 if needs_clear {
802 self.clear_status(&handle).await?;
803 self.get_status(&handle).await?;
804 }
805
806 self.abort(&handle, false).await?;
808
809 Ok(handle)
810 }
811
812 #[cfg(not(target_os = "windows"))]
813 async fn control_out(
814 &self,
815 handle: &Handle,
816 request: Request,
817 value: u16,
818 data: &[u8],
819 ) -> Result<(), Error> {
820 trace!("Sending DFU control out: {:?}", request);
821 handle
822 .device
823 .control_out(
824 ControlOut {
825 control_type: ControlType::Class,
826 recipient: Recipient::Interface,
827 request: request.into(),
828 value,
829 index: self.interface() as u16,
830 data,
831 },
832 self.timeout,
833 )
834 .await
835 .map_err(Error::UsbControlTransfer)
836 }
837
838 #[cfg(target_os = "windows")]
839 async fn control_out(
840 &self,
841 handle: &Handle,
842 request: Request,
843 value: u16,
844 data: &[u8],
845 ) -> Result<(), Error> {
846 trace!("Sending DFU control out: {:?}", request);
847 handle
848 .interface
849 .control_out(
850 ControlOut {
851 control_type: ControlType::Class,
852 recipient: Recipient::Interface,
853 request: request.into(),
854 value,
855 index: self.interface() as u16,
856 data,
857 },
858 self.timeout,
859 )
860 .await
861 .map_err(Error::UsbControlTransfer)
862 }
863
864 #[cfg(not(target_os = "windows"))]
865 async fn control_in(
866 &self,
867 handle: &Handle,
868 request: Request,
869 value: u16,
870 length: u16,
871 ) -> Result<Vec<u8>, Error> {
872 trace!("Sending DFU control in: {:?}", request);
873 handle
874 .device
875 .control_in(
876 ControlIn {
877 control_type: ControlType::Class,
878 recipient: Recipient::Interface,
879 request: request.into(),
880 value,
881 index: self.interface() as u16,
882 length,
883 },
884 self.timeout,
885 )
886 .await
887 .map_err(Error::UsbControlTransfer)
888 }
889
890 #[cfg(target_os = "windows")]
891 async fn control_in(
892 &self,
893 handle: &Handle,
894 request: Request,
895 value: u16,
896 length: u16,
897 ) -> Result<Vec<u8>, Error> {
898 trace!("Sending DFU control in: {:?}", request);
899 handle
900 .interface
901 .control_in(
902 ControlIn {
903 control_type: ControlType::Class,
904 recipient: Recipient::Interface,
905 request: request.into(),
906 value,
907 index: self.interface() as u16,
908 length,
909 },
910 self.timeout,
911 )
912 .await
913 .map_err(Error::UsbControlTransfer)
914 }
915
916 async fn clear_status(&self, handle: &Handle) -> Result<(), Error> {
917 trace!("Clearing DFU status");
918 self.control_out(handle, Request::ClearStatus, 0, &[]).await
919 }
920
921 async fn set_address(&self, handle: &Handle, address: u32) -> Result<(), Error> {
922 trace!("Setting DFU address to 0x{:08X}", address);
923 trace!("DFU info {:?}", self.info);
924
925 let mut cmd = vec![STM32_DFU_CMD_SET_ADDRESS];
927 cmd.extend_from_slice(&address.to_le_bytes());
928
929 self.control_out(handle, Request::Download, 0, &cmd).await?;
930
931 self.get_status_check_download_busy(handle).await?;
933 self.get_status(handle).await?;
934
935 Ok(())
936 }
937
938 async fn erase_page(&self, handle: &Handle, address: u32) -> Result<(), Error> {
939 trace!("Erasing page at address 0x{:08X}", address);
940
941 let mut cmd = vec![STM32_DFU_CMD_ERASE];
943 cmd.extend_from_slice(&address.to_le_bytes());
944
945 self.control_out(handle, Request::Download, 0, &cmd).await?;
946
947 self.get_status_check_download_busy(handle).await?;
949 self.get_status(handle).await?;
950
951 Ok(())
952 }
953
954 async fn abort(&self, handle: &Handle, get_status: bool) -> Result<(), Error> {
955 trace!("Sending DFU abort");
956 self.control_out(handle, Request::Abort, 0, &[]).await?;
957
958 if get_status {
959 self.get_status(handle).await?;
960 }
961
962 Ok(())
963 }
964
965 async fn read_block(&self, handle: &Handle, block: usize) -> Result<Vec<u8>, Error> {
966 trace!("Reading DFU block {}", block);
967
968 let data = self
969 .control_in(
970 handle,
971 Request::Upload,
972 (2 + block) as u16,
973 DFU_BLOCK_SIZE as u16,
974 )
975 .await?;
976
977 self.get_status(handle).await?;
978 self.get_status(handle).await?;
979
980 Ok(data)
981 }
982
983 async fn write_block(&self, handle: &Handle, block: usize, data: &[u8]) -> Result<(), Error> {
984 trace!("Writing DFU block {} ({} bytes)", block, data.len());
985
986 let mut block_data = vec![0xFF; DFU_BLOCK_SIZE];
988 block_data[..data.len()].copy_from_slice(data);
989
990 self.control_out(handle, Request::Download, (2 + block) as u16, &block_data)
991 .await?;
992
993 self.get_status_check_download_busy(handle).await?;
995 self.get_status(handle).await?;
996
997 Ok(())
998 }
999
1000 async fn get_status_check_download_busy(&self, handle: &Handle) -> Result<(), Error> {
1002 let status = self.get_status_error_flag(handle, true).await?;
1003 if !status.is_download_busy() {
1004 return Err(Error::DfuStatus {
1005 status: status.status(),
1006 state: status.state(),
1007 });
1008 }
1009 Ok(())
1010 }
1011
1012 async fn get_status(&self, handle: &Handle) -> Result<DeviceStatus, Error> {
1014 self.get_status_error_flag(handle, false).await
1015 }
1016
1017 async fn get_status_error_flag(
1018 &self,
1019 handle: &Handle,
1020 error_ok: bool,
1021 ) -> Result<DeviceStatus, Error> {
1022 trace!("Getting DFU status");
1023 let data = self
1024 .control_in(
1025 handle,
1026 Request::GetStatus,
1027 0,
1028 Request::GetStatus.fixed_length() as u16,
1029 )
1030 .await?;
1031
1032 let status = DeviceStatus::from_packet(&data)?;
1033
1034 trace!("Waiting for DFU poll time: {} ms", status.poll_time());
1036 Timer::after(Duration::from_millis(status.poll_time() as u64)).await;
1037
1038 if !error_ok && status.is_error() {
1039 return Err(Error::DfuStatus {
1040 status: status.status(),
1041 state: status.state(),
1042 });
1043 }
1044
1045 Ok(status)
1046 }
1047}
1048
1049fn parse_dfu_type(desc: &str) -> DfuType {
1051 if let Some(at_pos) = desc.find('@')
1053 && let Some(slash_pos) = desc[at_pos..].find('/')
1054 {
1055 let region = desc[at_pos + 1..at_pos + slash_pos].trim();
1056
1057 return match region {
1058 s if s.contains("Internal Flash") => DfuType::InternalFlash,
1059 s if s.contains("Option Bytes") => DfuType::OptionBytes,
1060 s if s.contains("System Memory") || s.contains("Bootloader") => DfuType::SystemMemory,
1061 _ => DfuType::Unknown(region.to_string()),
1062 };
1063 }
1064
1065 DfuType::Unknown(desc.to_string())
1066}
1067
1068async fn check_device_for_dfu(
1070 timeout: Duration,
1071 device_info: &NusbDeviceInfo,
1072) -> Option<Vec<Device>> {
1073 let mut dfu_device = false;
1075 for interface in device_info.interfaces() {
1076 trace!("Checking {device_info:?} interface {interface:?} for DFU class/subclass");
1077 let class = interface.class();
1078 let subclass = interface.subclass();
1079 if class == USB_CLASS_APPLICATION_SPECIFIC && subclass == USB_SUBCLASS_DFU {
1080 trace!("Found DFU interface");
1081 dfu_device = true;
1082 break;
1083 }
1084 }
1085
1086 if !dfu_device {
1087 return None;
1088 }
1089
1090 let vid = device_info.vendor_id();
1092 let pid = device_info.product_id();
1093 let device = match device_info.open().await {
1094 Ok(dev) => dev,
1095 Err(e) => {
1096 warn!("Failed to open USB device {vid:04X}:{pid:04X} for DFU interface check: {e}");
1097 return None;
1098 }
1099 };
1100
1101 let config = match device.active_configuration() {
1103 Ok(cfg) => cfg,
1104 Err(e) => {
1105 warn!("Failed to get active configuration for USB device {vid:04X}:{pid:04X}: {e}");
1106 return None;
1107 }
1108 };
1109
1110 let mut results = Vec::new();
1112 for interface in config.interface_alt_settings() {
1113 let string_index = interface.string_index();
1114 if let Some(index) = string_index {
1115 let desc_str = match device
1117 .get_string_descriptor(index, LANGUAGE_ID, timeout)
1118 .await
1119 {
1120 Ok(s) => s,
1121 Err(e) => {
1122 warn!(
1123 "Failed to read interface string for USB device {vid:04X}:{pid:04X}: {e}"
1124 );
1125 "Unknown".to_string();
1126 return None;
1127 }
1128 };
1129
1130 let dfu_type = parse_dfu_type(&desc_str);
1131 let dfu_info = DfuInfo {
1132 interface: interface.interface_number(),
1133 alt: interface.alternate_setting(),
1134 desc: desc_str,
1135 dfu_type,
1136 };
1137
1138 let device = Device::from_nusb(device_info.clone(), dfu_info);
1139 trace!("Found DFU-capable device: {device}");
1140 results.push(device);
1141 }
1142 }
1143
1144 Some(results)
1145}
1146
1147pub async fn search_for_dfu(
1157 timeout: Duration,
1158 filter: Option<DfuType>,
1159) -> Result<Vec<Device>, Error> {
1160 let devices = nusb::list_devices()
1162 .await
1163 .map_err(Error::UsbDeviceEnumeration)?;
1164
1165 let mut dfu_devices = Vec::new();
1167 for device in devices {
1168 if let Some(info) = check_device_for_dfu(timeout, &device).await {
1169 dfu_devices.extend(info);
1170 }
1171 }
1172
1173 let filtered_dfu_devices = if let Some(filter) = &filter {
1175 dfu_devices
1176 .iter()
1177 .filter(|device| {
1178 trace!("Checking device {} for DFU type {:?}", device, filter);
1179 let is_match = device.is_dfu_type(filter);
1180 if is_match {
1181 trace!("Device {} matches DFU type {:?}", device, filter);
1182 }
1183 is_match
1184 })
1185 .cloned()
1186 .collect()
1187 } else {
1188 dfu_devices
1189 };
1190
1191 Ok(filtered_dfu_devices)
1192}