1use std::{
3 char,
4 io::{BufReader, BufWriter, Read, Write},
5 net::SocketAddr,
6 sync::Arc,
7 time::Duration,
8};
9
10use crate::{
11 architecture::{
12 arm::{
13 ArmCommunicationInterface, ArmDebugInterface, ArmError,
14 communication_interface::DapProbe, sequences::ArmDebugSequence,
15 },
16 riscv::{
17 communication_interface::{RiscvError, RiscvInterfaceBuilder},
18 dtm::jtag_dtm::JtagDtmBuilder,
19 },
20 xtensa::communication_interface::{
21 XtensaCommunicationInterface, XtensaDebugInterfaceState, XtensaError,
22 },
23 },
24 probe::{
25 AutoImplementJtagAccess, DebugProbe, DebugProbeError, DebugProbeInfo, DebugProbeSelector,
26 IoSequenceItem, JtagAccess, JtagDriverState, ProbeCreationError, ProbeError, ProbeFactory,
27 RawJtagIo, RawSwdIo, SwdSettings, WireProtocol, blackmagic::arm::BlackMagicProbeArmDebug,
28 list::ProbeListItem,
29 },
30};
31use bitfield::bitfield;
32use bitvec::vec::BitVec;
33use serialport::{SerialPortType, available_ports};
34
35const BLACK_MAGIC_PROBE_VID: u16 = 0x1d50;
36const BLACK_MAGIC_PROBE_PID: u16 = 0x6018;
37const BLACK_MAGIC_PROBE: (u16, u16) = (BLACK_MAGIC_PROBE_VID, BLACK_MAGIC_PROBE_PID);
38const BLACK_MAGIC_PROTOCOL_RESPONSE_START: u8 = b'&';
39const BLACK_MAGIC_PROTOCOL_RESPONSE_END: u8 = b'#';
40pub(crate) const BLACK_MAGIC_REMOTE_SIZE_MAX: usize = 1024;
41
42mod arm;
43
44#[derive(Debug)]
46pub struct BlackMagicProbeFactory;
47
48#[derive(PartialEq)]
49enum ProtocolVersion {
50 V0,
51 V0P,
52 V1,
53 V2,
54 V3,
55 V4,
56}
57
58#[derive(Debug, Copy, Clone)]
59pub(crate) enum Align {
60 U8 = 0,
61 U16 = 1,
62 U32 = 2,
63 U64 = 3,
64}
65
66impl core::fmt::Display for ProtocolVersion {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 write!(
69 f,
70 "{}",
71 match self {
72 Self::V0 => "V0",
73 Self::V0P => "V0P",
74 Self::V1 => "V1",
75 Self::V2 => "V2",
76 Self::V3 => "V3",
77 Self::V4 => "V4",
78 }
79 )
80 }
81}
82
83bitfield! {
84 #[derive(Copy, Clone)]
85 struct Accelerators(u64);
86 impl Debug;
87
88 bool, has_adiv5, set_has_adiv5: 0;
89 bool, has_cortex_ar, _: 1;
90 bool, has_riscv, _: 2;
91 bool, has_adiv6, _: 3;
92}
93
94#[expect(dead_code)]
95#[derive(Debug)]
96enum RemoteCommand<'a> {
97 Handshake(&'a mut [u8]),
98 GetAccelerators,
99 HighLevelCheck,
100 GetVoltage,
101 GetSpeedKhz,
102 SetNrst(bool),
103 SetPower(bool),
104 TargetClockOutput {
105 enable: bool,
106 },
107 SetSpeedHz(u32),
108 SpeedKhz,
109 TargetReset(bool),
110 RawAccessV0P {
111 rnw: u8,
112 addr: u8,
113 value: u32,
114 },
115 ReadDpV0P {
116 addr: u8,
117 },
118 ReadApV0P {
119 apsel: u8,
120 addr: u8,
121 },
122 WriteApV0P {
123 apsel: u8,
124 addr: u8,
125 value: u32,
126 },
127 MemReadV0P {
128 apsel: u8,
129 csw: u32,
130 offset: u32,
131 data: &'a mut [u8],
132 },
133 MemWriteV0P {
134 apsel: u8,
135 csw: u32,
136 align: Align,
137 offset: u32,
138 data: &'a [u8],
139 },
140 RawAccessV1 {
141 index: u8,
142 rnw: u8,
143 addr: u8,
144 value: u32,
145 },
146 ReadDpV1 {
147 index: u8,
148 addr: u8,
149 },
150 ReadApV1 {
151 index: u8,
152 apsel: u8,
153 addr: u8,
154 },
155 WriteApV1 {
156 index: u8,
157 apsel: u8,
158 addr: u8,
159 value: u32,
160 },
161 MemReadV1 {
162 index: u8,
163 apsel: u8,
164 csw: u32,
165 offset: u32,
166 data: &'a mut [u8],
167 },
168 MemWriteV1 {
169 index: u8,
170 apsel: u8,
171 csw: u32,
172 align: Align,
173 offset: u32,
174 data: &'a [u8],
175 },
176 RawAccessV3 {
177 index: u8,
178 rnw: u8,
179 addr: u8,
180 value: u32,
181 },
182 ReadDpV3 {
183 index: u8,
184 addr: u8,
185 },
186 ReadApV3 {
187 index: u8,
188 apsel: u8,
189 addr: u8,
190 },
191 WriteApV3 {
192 index: u8,
193 apsel: u8,
194 addr: u8,
195 value: u32,
196 },
197 MemReadV3 {
198 index: u8,
199 apsel: u8,
200 csw: u32,
201 offset: u32,
202 data: &'a mut [u8],
203 },
204 MemWriteV3 {
205 index: u8,
206 apsel: u8,
207 csw: u32,
208 align: Align,
209 offset: u32,
210 data: &'a [u8],
211 },
212 MemReadV4 {
213 index: u8,
214 apsel: u8,
215 csw: u32,
216 offset: u64,
217 data: &'a mut [u8],
218 },
219 MemWriteV4 {
220 index: u8,
221 apsel: u8,
222 csw: u32,
223 align: Align,
224 offset: u64,
225 data: &'a [u8],
226 },
227 AdiV6ReadApV4 {
228 index: u8,
229 apsel: u64,
230 addr: u16,
231 },
232 AdiV6WriteApV4 {
233 index: u8,
234 apsel: u64,
235 addr: u16,
236 value: u32,
237 },
238 AdiV6MemReadV4 {
239 index: u8,
240 apsel: u64,
241 csw: u32,
242 offset: u64,
243 data: &'a mut [u8],
244 },
245 AdiV6MemWriteV4 {
246 index: u8,
247 apsel: u64,
248 csw: u32,
249 align: Align,
250 offset: u64,
251 data: &'a [u8],
252 },
253 JtagNext {
254 tms: bool,
255 tdi: bool,
256 },
257 JtagTms {
258 bits: u32,
259 length: usize,
260 },
261 JtagTdi {
262 bits: u32,
263 length: usize,
264 tms: bool,
265 },
266 JtagInit,
267 JtagReset,
268 JtagAddDevice {
269 index: u8,
270 dr_prescan: u8,
271 dr_postscan: u8,
272 ir_len: u8,
273 ir_prescan: u8,
274 ir_postscan: u8,
275 current_ir: u32,
276 },
277 SwdInit,
278 SwdIn {
279 length: usize,
280 },
281 SwdInParity {
282 length: usize,
283 },
284 SwdOut {
285 value: u32,
286 length: usize,
287 },
288 SwdOutParity {
289 value: u32,
290 length: usize,
291 },
292}
293
294impl RemoteCommand<'_> {
295 fn response_buffer(&mut self) -> Option<&mut [u8]> {
298 match self {
299 RemoteCommand::Handshake(data) => Some(data),
300 RemoteCommand::MemReadV0P { data, .. } => Some(data),
301 RemoteCommand::MemReadV1 { data, .. } => Some(data),
302 RemoteCommand::MemReadV3 { data, .. } => Some(data),
303 RemoteCommand::MemReadV4 { data, .. } => Some(data),
304 RemoteCommand::AdiV6MemReadV4 { data, .. } => Some(data),
305 _ => None,
306 }
307 }
308
309 fn decode_hex(&self) -> bool {
311 matches!(
312 self,
313 RemoteCommand::MemReadV0P { .. }
314 | RemoteCommand::MemReadV1 { .. }
315 | RemoteCommand::MemReadV3 { .. }
316 | RemoteCommand::MemReadV4 { .. }
317 | RemoteCommand::AdiV6MemReadV4 { .. }
318 )
319 }
320}
321
322#[expect(clippy::to_string_trait_impl)]
325impl std::string::ToString for RemoteCommand<'_> {
326 fn to_string(&self) -> String {
327 match self {
328 RemoteCommand::Handshake(_) => "+#!GA#".to_string(),
329 RemoteCommand::GetVoltage => " !GV#".to_string(),
330 RemoteCommand::GetSpeedKhz => "!Gf#".to_string(),
331 RemoteCommand::SetSpeedHz(speed) => {
332 format!("!GF{speed:08x}#")
333 }
334 RemoteCommand::HighLevelCheck => "!HC#".to_string(),
335 RemoteCommand::SetNrst(set) => format!("!GZ{}#", if *set { '1' } else { '0' }),
336 RemoteCommand::SetPower(set) => format!("!GP{}#", if *set { '1' } else { '0' }),
337 RemoteCommand::TargetClockOutput { enable } => {
338 format!("!GE{}#", if *enable { '1' } else { '0' })
339 }
340 RemoteCommand::SpeedKhz => "!Gf#".to_string(),
341 RemoteCommand::RawAccessV0P { rnw, addr, value } => {
342 format!("!HL{rnw:02x}{addr:04x}{value:08x}#")
343 }
344 RemoteCommand::ReadDpV0P { addr } => {
345 format!("!Hdff{addr:04x}#")
346 }
347 RemoteCommand::ReadApV0P { apsel, addr } => {
348 format!("!Ha{:02x}{:04x}#", apsel, 0x100 | *addr as u16)
349 }
350 RemoteCommand::WriteApV0P { apsel, addr, value } => {
351 format!("!HA{:02x}{:04x}{:08x}#", apsel, 0x100 | *addr as u16, value)
352 }
353 RemoteCommand::MemReadV0P {
354 apsel,
355 csw,
356 offset,
357 data,
358 } => format!(
359 "!HM{:02x}{:08x}{:08x}{:08x}#",
360 apsel,
361 csw,
362 offset,
363 data.len()
364 ),
365 RemoteCommand::MemWriteV0P {
366 apsel,
367 csw,
368 align,
369 offset,
370 data,
371 } => {
372 let mut s = format!(
373 "!Hm{:02x}{:08x}{:02x}{:08x}{:08x}",
374 apsel,
375 csw,
376 *align as u8,
377 offset,
378 data.len(),
379 );
380 for b in data.iter() {
381 s.push_str(&format!("{b:02x}"));
382 }
383 s.push('#');
384 s
385 }
386
387 RemoteCommand::RawAccessV1 {
388 index,
389 rnw,
390 addr,
391 value,
392 } => {
393 format!("!HL{index:02x}{rnw:02x}{addr:04x}{value:08x}#")
394 }
395 RemoteCommand::ReadDpV1 { index, addr } => {
396 format!("!Hd{index:02x}ff{addr:04x}#")
397 }
398 RemoteCommand::ReadApV1 { index, apsel, addr } => {
399 format!("!Ha{:02x}{:02x}{:04x}#", index, apsel, 0x100 | *addr as u16)
400 }
401 RemoteCommand::WriteApV1 {
402 index,
403 apsel,
404 addr,
405 value,
406 } => format!(
407 "!HA{:02x}{:02x}{:04x}{:08x}#",
408 index,
409 apsel,
410 0x100 | *addr as u16,
411 value
412 ),
413 RemoteCommand::MemReadV1 {
414 index,
415 apsel,
416 csw,
417 offset,
418 data,
419 } => format!(
420 "!HM{:02x}{:02x}{:08x}{:08x}{:08x}#",
421 index,
422 apsel,
423 csw,
424 offset,
425 data.len()
426 ),
427 RemoteCommand::MemWriteV1 {
428 index,
429 apsel,
430 csw,
431 align,
432 offset,
433 data,
434 } => {
435 let mut s = format!(
436 "!Hm{:02x}{:02x}{:08x}{:02x}{:08x}{:08x}",
437 index,
438 apsel,
439 csw,
440 *align as u8,
441 offset,
442 data.len()
443 );
444 for b in data.iter() {
445 s.push_str(&format!("{b:02x}"));
446 }
447 s.push('#');
448 s
449 }
450
451 RemoteCommand::RawAccessV3 {
452 index,
453 rnw,
454 addr,
455 value,
456 } => {
457 format!("!AR{index:02x}{rnw:02x}{addr:04x}{value:08x}#")
458 }
459 RemoteCommand::ReadDpV3 { index, addr } => {
460 format!("!Ad{index:02x}ff{addr:04x}#")
461 }
462 RemoteCommand::ReadApV3 { index, apsel, addr } => {
463 format!("!Aa{:02x}{:02x}{:04x}#", index, apsel, 0x100 | *addr as u16)
464 }
465 RemoteCommand::WriteApV3 {
466 index,
467 apsel,
468 addr,
469 value,
470 } => format!(
471 "!AA{:02x}{:02x}{:04x}{:08x}#",
472 index,
473 apsel,
474 0x100 | *addr as u16,
475 value.to_be()
476 ),
477 RemoteCommand::MemReadV3 {
478 index,
479 apsel,
480 csw,
481 offset,
482 data,
483 } => format!(
484 "!Am{:02x}{:02x}{:08x}{:08x}{:08x}#",
485 index,
486 apsel,
487 csw,
488 offset,
489 data.len()
490 ),
491 RemoteCommand::MemWriteV3 {
492 index,
493 apsel,
494 csw,
495 align,
496 offset,
497 data,
498 } => {
499 let mut s = format!(
500 "!AM{:02x}{:02x}{:08x}{:02x}{:08x}{:08x}",
501 index,
502 apsel,
503 csw,
504 *align as u8,
505 offset,
506 data.len()
507 );
508 for b in data.iter() {
509 s.push_str(&format!("{b:02x}"));
510 }
511 s.push('#');
512 s
513 }
514
515 RemoteCommand::MemReadV4 {
516 index,
517 apsel,
518 csw,
519 offset,
520 data,
521 } => format!(
522 "!Am{:02x}{:02x}{:08x}{:016x}{:08x}#",
523 index,
524 apsel,
525 csw,
526 offset,
527 data.len()
528 ),
529 RemoteCommand::MemWriteV4 {
530 index,
531 apsel,
532 csw,
533 align,
534 offset,
535 data,
536 } => {
537 let mut s = format!(
538 "!AM{:02x}{:02x}{:08x}{:02x}{:016x}{:08x}",
539 index,
540 apsel,
541 csw,
542 *align as u8,
543 offset,
544 data.len()
545 );
546 for b in data.iter() {
547 s.push_str(&format!("{b:02x}"));
548 }
549 s.push('#');
550 s
551 }
552
553 RemoteCommand::AdiV6ReadApV4 { index, apsel, addr } => {
554 format!("!A6a{:02x}{:016x}{:04x}#", index, apsel, 0x1000 | addr)
555 }
556 RemoteCommand::AdiV6WriteApV4 {
557 index,
558 apsel,
559 addr,
560 value,
561 } => format!(
562 "!A6A{:02x}{:016x}{:04x}{:08x}#",
563 index,
564 apsel,
565 0x1000 | addr,
566 value.to_be()
567 ),
568 RemoteCommand::AdiV6MemReadV4 {
569 index,
570 apsel,
571 csw,
572 offset,
573 data,
574 } => format!(
575 "!A6m{:02x}{:016x}{:08x}{:016x}{:08x}#",
576 index,
577 apsel,
578 csw,
579 offset,
580 data.len()
581 ),
582 RemoteCommand::AdiV6MemWriteV4 {
583 index,
584 apsel,
585 csw,
586 align,
587 offset,
588 data,
589 } => {
590 let mut s = format!(
591 "!A6M{:02x}{:016x}{:08x}{:02x}{:016x}{:08x}",
592 index,
593 apsel,
594 csw,
595 *align as u8,
596 offset,
597 data.len()
598 );
599 for b in data.iter() {
600 s.push_str(&format!("{b:02x}"));
601 }
602 s.push('#');
603 s
604 }
605
606 RemoteCommand::JtagNext { tms, tdi } => format!(
607 "!JN{}{}#",
608 if *tms { '1' } else { '0' },
609 if *tdi { '1' } else { '0' }
610 ),
611 RemoteCommand::JtagInit => "+#!JS#".to_string(),
612 RemoteCommand::JtagReset => "+#!JR#".to_string(),
613 RemoteCommand::JtagTms { bits, length } => {
614 format!("!JT{:02x}{:x}#", *length, *bits)
615 }
616 RemoteCommand::JtagTdi { bits, length, tms } => format!(
617 "!J{}{:02x}{:x}#",
618 if *tms { 'D' } else { 'd' },
619 *length,
620 *bits
621 ),
622 RemoteCommand::JtagAddDevice {
623 index,
624 dr_prescan,
625 dr_postscan,
626 ir_len,
627 ir_prescan,
628 ir_postscan,
629 current_ir,
630 } => format!(
631 "!HJ{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:08x}#",
632 *index, *dr_prescan, *dr_postscan, *ir_len, *ir_prescan, *ir_postscan, *current_ir
633 ),
634 RemoteCommand::SwdInit => "!SS#".to_string(),
635 RemoteCommand::SwdIn { length: bits } => {
636 format!("!Si{:02x}#", *bits)
637 }
638 RemoteCommand::SwdInParity { length } => {
639 format!("!SI{:02x}#", *length)
640 }
641 RemoteCommand::SwdOut { value, length } => {
642 format!("!So{:02x}{:x}#", *length, *value)
643 }
644 RemoteCommand::SwdOutParity { value, length } => {
645 format!("!SO{:02x}{:x}#", *length, *value)
646 }
647 RemoteCommand::TargetReset(reset) => {
648 format!("!GZ{}#", if *reset { '1' } else { '0' })
649 }
650 RemoteCommand::GetAccelerators => "!HA#".to_string(),
651 }
652 }
653}
654
655#[derive(Debug, thiserror::Error)]
656enum RemoteError {
657 ParameterError(u64),
658 Error(u64),
659 Unsupported(u64),
660 ProbeError(std::io::Error),
661 UnsupportedVersion(u64),
662}
663
664impl core::fmt::Display for RemoteError {
665 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
666 match self {
667 Self::ParameterError(e) => write!(f, "Remote parameter error with result {:016x}", *e),
668 Self::Error(e) => write!(f, "Remote error with result {:016x}", *e),
669 Self::Unsupported(e) => write!(f, "Remote command unsupported with result {:016x}", *e),
670 Self::ProbeError(e) => write!(f, "Probe error {e}"),
671 Self::UnsupportedVersion(e) => write!(f, "Only versions 0-4 are supported, not {e}"),
672 }
673 }
674}
675
676impl ProbeError for RemoteError {}
677
678struct RemoteResponse(u64);
679
680#[derive(PartialEq, Copy, Clone)]
681enum SwdDirection {
682 Input,
683 Output,
684}
685
686impl From<bool> for SwdDirection {
687 fn from(value: bool) -> Self {
688 if value {
689 SwdDirection::Output
690 } else {
691 SwdDirection::Input
692 }
693 }
694}
695
696impl From<IoSequenceItem> for SwdDirection {
697 fn from(value: IoSequenceItem) -> Self {
698 match value {
699 IoSequenceItem::Input => SwdDirection::Input,
700 IoSequenceItem::Output(_) => SwdDirection::Output,
701 }
702 }
703}
704
705pub struct BlackMagicProbe {
707 reader: BufReader<Box<dyn Read + Send>>,
708 writer: BufWriter<Box<dyn Write + Send>>,
709 protocol: Option<WireProtocol>,
710 version: String,
711 remote_protocol: ProtocolVersion,
712 speed_khz: u32,
713 jtag_state: JtagDriverState,
714 swd_settings: SwdSettings,
715 in_bits: BitVec,
716 swd_direction: SwdDirection,
717}
718
719impl core::fmt::Debug for BlackMagicProbe {
720 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
721 write!(
722 f,
723 "Black Magic Probe {} with remote protocol {}",
724 self.version, self.remote_protocol
725 )
726 }
727}
728
729impl core::fmt::Display for BlackMagicProbeFactory {
730 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
731 write!(f, "Black Magic Probe")
732 }
733}
734
735impl BlackMagicProbe {
736 fn new(
737 reader: Box<dyn Read + Send>,
738 writer: Box<dyn Write + Send>,
739 ) -> Result<Self, DebugProbeError> {
740 let mut reader = BufReader::new(reader);
741 let mut writer = BufWriter::new(writer);
742
743 let mut handshake_response = [0u8; 1024];
744 Self::send(
745 &mut writer,
746 &RemoteCommand::Handshake(&mut handshake_response),
747 )?;
748 let response_len = Self::recv(&mut reader, Some(&mut handshake_response), false)
749 .map_err(|e| {
750 tracing::error!("Unable to receive command: {:?}", e);
751 DebugProbeError::ProbeCouldNotBeCreated(ProbeCreationError::CouldNotOpen)
752 })?
753 .0;
754 let version =
755 String::from_utf8_lossy(&handshake_response[0..response_len as usize]).to_string();
756 tracing::info!("Probe version {}", version);
757
758 Self::send(&mut writer, &RemoteCommand::HighLevelCheck)?;
759 let remote_protocol = if let Ok(response) = Self::recv(&mut reader, None, false) {
760 match response.0 {
761 0 => ProtocolVersion::V0P,
762 1 => ProtocolVersion::V1,
763 2 => ProtocolVersion::V2,
764 3 => ProtocolVersion::V3,
765 4 => ProtocolVersion::V4,
766 version => {
767 return Err(DebugProbeError::ProbeCouldNotBeCreated(
768 ProbeCreationError::ProbeSpecific(
769 RemoteError::UnsupportedVersion(version).into(),
770 ),
771 ));
772 }
773 }
774 } else {
775 ProtocolVersion::V0
776 };
777
778 tracing::info!("Using BMP protocol {}", remote_protocol);
779
780 let mut probe = Self {
781 reader,
782 writer,
783 protocol: None,
784 version,
785 speed_khz: 0,
786 remote_protocol,
787 jtag_state: JtagDriverState::default(),
788 swd_settings: SwdSettings::default(),
789 in_bits: BitVec::new(),
790 swd_direction: SwdDirection::Output,
791 };
792
793 probe.command(RemoteCommand::SetNrst(false)).ok();
794 probe.command(RemoteCommand::GetVoltage).ok();
795 probe.command(RemoteCommand::SetSpeedHz(400_0000)).ok();
796 probe.command(RemoteCommand::GetSpeedKhz).ok();
797
798 Ok(probe)
799 }
800
801 fn command(&mut self, mut command: RemoteCommand) -> Result<RemoteResponse, RemoteError> {
802 let result = Self::send(&mut self.writer, &command);
803 if let Err(e) = result {
804 tracing::error!("Error sending command: {:?}", e);
805 return Err(e);
806 }
807 let should_decode = command.decode_hex();
808
809 Self::recv(&mut self.reader, command.response_buffer(), should_decode)
810 }
811
812 fn send(
813 writer: &mut BufWriter<Box<dyn Write + Send>>,
814 command: &RemoteCommand,
815 ) -> Result<(), RemoteError> {
816 let s = command.to_string();
817 tracing::debug!(" > {}", s);
818 write!(writer, "{s}").map_err(RemoteError::ProbeError)?;
819 writer.flush().map_err(RemoteError::ProbeError)
820 }
821
822 fn hex_val(c: u8) -> Result<u8, char> {
823 match c {
824 b'A'..=b'F' => Ok(c - b'A' + 10),
825 b'a'..=b'f' => Ok(c - b'a' + 10),
826 b'0'..=b'9' => Ok(c - b'0'),
827 _ => Err(c as char),
828 }
829 }
830
831 fn from_hex_u64(hex: &[u8]) -> Result<u64, ()> {
832 let hex = if hex.first() == Some(&b'0')
834 && (hex.get(1) == Some(&b'x') || hex.get(1) == Some(&b'X'))
835 {
836 &hex[2..]
837 } else {
838 hex
839 };
840
841 let mut val = 0u64;
842
843 for (index, c) in hex.iter().rev().enumerate() {
844 let decoded_val: u64 = Self::hex_val(*c).or(Err(()))?.into();
845 val += decoded_val << (index * 4);
846 }
847 Ok(val)
848 }
849
850 fn recv_u64(reader: &mut BufReader<Box<dyn Read + Send>>) -> Result<u64, RemoteError> {
851 let mut response_buffer = [0u8; 16];
852 let mut response_len = 0;
853 for dest in response_buffer.iter_mut() {
854 let mut byte = [0u8; 1];
855 reader
856 .read_exact(&mut byte)
857 .map_err(RemoteError::ProbeError)?;
858 if byte[0] == BLACK_MAGIC_PROTOCOL_RESPONSE_END {
859 break;
860 }
861 *dest = byte[0];
862 response_len += 1;
863 }
864 Self::from_hex_u64(&response_buffer[0..response_len])
866 .or(Err(RemoteError::ParameterError(0)))
867 }
868
869 fn recv(
870 reader: &mut BufReader<Box<dyn Read + Send>>,
871 buffer: Option<&mut [u8]>,
872 decode_hex: bool,
873 ) -> Result<RemoteResponse, RemoteError> {
874 loop {
876 let mut byte = [0u8; 1];
877 reader
878 .read_exact(&mut byte)
879 .map_err(RemoteError::ProbeError)?;
880 if byte[0] == BLACK_MAGIC_PROTOCOL_RESPONSE_START {
881 break;
882 }
883 }
884 let mut response_code = [0u8; 1];
885 reader
886 .read_exact(&mut response_code)
887 .map_err(RemoteError::ProbeError)?;
888 let response_code = response_code[0];
889
890 if response_code == b'K' {
894 let Some(buffer) = buffer else {
895 let response = Self::recv_u64(reader)?;
896 tracing::trace!(" < K{:x}", response);
897 return Ok(RemoteResponse(response));
898 };
899 let mut output_count = 0;
900 for dest in buffer.iter_mut() {
901 let mut byte = [0u8; 1];
902
903 reader
905 .read_exact(&mut byte)
906 .map_err(RemoteError::ProbeError)?;
907 if byte[0] == BLACK_MAGIC_PROTOCOL_RESPONSE_END {
908 break;
909 }
910
911 output_count += 1;
914
915 if decode_hex {
916 *dest = Self::hex_val(byte[0])
917 .or(Err(RemoteError::ParameterError(byte[0] as _)))?;
918
919 reader
921 .read_exact(&mut byte)
922 .map_err(RemoteError::ProbeError)?;
923 if byte[0] == BLACK_MAGIC_PROTOCOL_RESPONSE_END {
924 break;
925 }
926
927 *dest = (*dest << 4)
928 | Self::hex_val(byte[0])
929 .or(Err(RemoteError::ParameterError(byte[0] as _)))?;
930 } else {
931 *dest = byte[0];
932 }
933 }
934 tracing::trace!(" < K{:x?}", &buffer[0..output_count as usize]);
935 Ok(RemoteResponse(output_count))
936 } else {
937 let response = Self::recv_u64(reader)?;
938 tracing::trace!(" < {}{:x}", char::from(response_code), response);
939 if response_code == b'E' {
940 Err(RemoteError::Error(response))
941 } else if response_code == b'P' {
942 Err(RemoteError::ParameterError(response))
943 } else {
944 Err(RemoteError::Unsupported(response))
945 }
946 }
947 }
948
949 fn get_speed(&mut self) -> Result<u32, DebugProbeError> {
950 let speed = self.command(RemoteCommand::SpeedKhz)?.0.try_into().unwrap();
951 Ok(speed)
952 }
953
954 fn drain_swd_accumulator(
955 &mut self,
956 output: &mut Vec<bool>,
957 accumulator: u32,
958 accumulator_length: usize,
959 ) -> Result<(), DebugProbeError> {
960 if self.swd_direction == SwdDirection::Output {
961 match self.command(RemoteCommand::SwdOut {
962 value: accumulator,
963 length: accumulator_length,
964 }) {
965 Ok(response) => tracing::debug!(
966 "Doing SWD out of {} bits: {:x} -- {}",
967 accumulator_length,
968 accumulator,
969 response.0
970 ),
971 Err(e) => tracing::error!(
972 "Error doing SWD OUT of {} bits ({:x}) -- {}",
973 accumulator_length,
974 accumulator,
975 e
976 ),
977 }
978 for bit in 0..accumulator_length {
979 output.push(accumulator & (1 << bit) != 0);
980 }
981 } else {
982 let result = self.command(RemoteCommand::SwdIn {
983 length: accumulator_length,
984 });
985 match &result {
986 Ok(response) => {
987 let response = response.0;
988 tracing::debug!(
989 "Doing SWD in of {} bits: {:x}",
990 accumulator_length,
991 response
992 );
993 for bit in 0..accumulator_length {
994 output.push(response & (1 << bit) != 0);
995 }
996 }
997 Err(e) => tracing::error!(
998 "Error doing SWD IN operation of {} bits: {}",
999 accumulator_length,
1000 e
1001 ),
1002 }
1003 }
1004 Ok(())
1005 }
1006
1007 fn perform_swdio_transfer<S>(&mut self, swdio: S) -> Result<Vec<bool>, DebugProbeError>
1009 where
1010 S: IntoIterator<Item = IoSequenceItem>,
1011 {
1012 let swdio_sequence = swdio.into_iter();
1013 let mut output = vec![];
1014
1015 let mut accumulator = 0u32;
1016 let mut accumulator_length = 0;
1017
1018 for swdio in swdio_sequence {
1019 let dir: SwdDirection = swdio.into();
1020 if dir != self.swd_direction
1021 || accumulator_length >= core::mem::size_of_val(&accumulator) * 8
1022 {
1023 if self.swd_direction == SwdDirection::Input && dir == SwdDirection::Output {
1027 accumulator_length -= 2;
1028 }
1029
1030 self.drain_swd_accumulator(&mut output, accumulator, accumulator_length)?;
1033
1034 if self.swd_direction == SwdDirection::Input && dir == SwdDirection::Output {
1036 output.push(false);
1037 output.push(false);
1038 }
1039
1040 accumulator = 0;
1041 accumulator_length = 0;
1042 }
1043 self.swd_direction = dir;
1044 accumulator |= if let IoSequenceItem::Output(true) = swdio {
1045 1 << accumulator_length
1046 } else {
1047 0
1048 };
1049 accumulator_length += 1;
1050 }
1051
1052 if accumulator_length > 0 {
1053 self.drain_swd_accumulator(&mut output, accumulator, accumulator_length)?;
1054 }
1055
1056 Ok(output)
1057 }
1058
1059 fn drain_jtag_accumulator(
1060 &mut self,
1061 accumulator: u32,
1062 mut accumulator_length: usize,
1063 final_tms: bool,
1064 capture: bool,
1065 final_transaction: bool,
1066 ) -> Result<(), DebugProbeError> {
1067 let response = self.command(RemoteCommand::JtagTdi {
1068 bits: accumulator,
1069 length: accumulator_length,
1070 tms: final_tms && final_transaction,
1071 })?;
1072
1073 if capture {
1074 if capture && final_transaction {
1076 accumulator_length -= 1;
1077 }
1078 let value = response.0;
1079 for bit in 0..accumulator_length {
1080 self.in_bits.push(value & (1 << bit) != 0);
1081 }
1082 }
1083 Ok(())
1084 }
1085
1086 fn perform_jtag_transfer(
1087 &mut self,
1088 transaction: Vec<(bool, bool, bool)>,
1089 final_tms: bool,
1090 capture: bool,
1091 ) -> Result<(), DebugProbeError> {
1092 let mut accumulator = 0;
1093 let mut accumulator_length = 0;
1094 let bit_count = transaction.len();
1095
1096 for (index, (_, tdi, _)) in transaction.into_iter().enumerate() {
1097 accumulator |= if tdi { 1 << accumulator_length } else { 0 };
1098 accumulator_length += 1;
1099 if accumulator_length >= core::mem::size_of_val(&accumulator) * 8 {
1100 let is_final = index + 1 >= bit_count;
1101 self.drain_jtag_accumulator(
1102 accumulator,
1103 accumulator_length,
1104 final_tms,
1105 capture,
1106 is_final,
1107 )?;
1108 accumulator = 0;
1109 accumulator_length = 0;
1110 }
1111 }
1112
1113 if accumulator_length > 0 {
1114 self.drain_jtag_accumulator(accumulator, accumulator_length, final_tms, capture, true)?;
1115 }
1116 Ok(())
1117 }
1118}
1119
1120impl DebugProbe for BlackMagicProbe {
1121 fn get_name(&self) -> &str {
1122 "Black Magic probe"
1123 }
1124
1125 fn speed_khz(&self) -> u32 {
1126 self.speed_khz
1127 }
1128
1129 fn set_speed(&mut self, speed_khz: u32) -> Result<u32, DebugProbeError> {
1130 self.command(RemoteCommand::SetSpeedHz(speed_khz * 1000))?;
1131 self.speed_khz = self.get_speed()?;
1132 Ok(self.speed_khz)
1133 }
1134
1135 fn attach(&mut self) -> Result<(), DebugProbeError> {
1136 tracing::debug!("Attaching with protocol '{:?}'", self.protocol);
1137
1138 if let ProtocolVersion::V2 | ProtocolVersion::V3 | ProtocolVersion::V4 =
1140 self.remote_protocol
1141 {
1142 self.command(RemoteCommand::TargetClockOutput { enable: true })
1143 .ok();
1144 }
1145
1146 match self.protocol {
1147 Some(WireProtocol::Jtag) => {
1148 self.select_target(0)?;
1149
1150 if let ProtocolVersion::V1
1151 | ProtocolVersion::V2
1152 | ProtocolVersion::V3
1153 | ProtocolVersion::V4 = self.remote_protocol
1154 {
1155 let sc = &self.jtag_state.chain_params;
1156 self.command(RemoteCommand::JtagAddDevice {
1157 index: 0,
1158 dr_prescan: sc.drpre.try_into().unwrap(),
1159 dr_postscan: sc.drpost.try_into().unwrap(),
1160 ir_len: sc.irlen.try_into().unwrap(),
1161 ir_prescan: sc.irpre.try_into().unwrap(),
1162 ir_postscan: sc.irpost.try_into().unwrap(),
1163 current_ir: u32::MAX,
1164 })?;
1165 }
1166 Ok(())
1167 }
1168 Some(WireProtocol::Swd) => Ok(()),
1169 _ => Err(DebugProbeError::InterfaceNotAvailable {
1170 interface_name: "no protocol specified",
1171 }),
1172 }
1173 }
1174
1175 fn detach(&mut self) -> Result<(), crate::Error> {
1176 Ok(())
1177 }
1178
1179 fn target_reset(&mut self) -> Result<(), DebugProbeError> {
1180 Err(DebugProbeError::NotImplemented {
1183 function_name: "target_reset",
1184 })
1185 }
1186
1187 fn target_reset_assert(&mut self) -> Result<(), DebugProbeError> {
1188 self.command(RemoteCommand::TargetReset(true))?;
1189 Ok(())
1190 }
1191
1192 fn target_reset_deassert(&mut self) -> Result<(), DebugProbeError> {
1193 self.command(RemoteCommand::TargetReset(false))?;
1194 Ok(())
1195 }
1196
1197 fn select_protocol(&mut self, protocol: WireProtocol) -> Result<(), DebugProbeError> {
1198 self.protocol = Some(protocol);
1199
1200 tracing::debug!("Switching to protocol {}", protocol);
1201 match protocol {
1202 WireProtocol::Jtag => {
1203 self.command(RemoteCommand::JtagInit)?;
1204 self.command(RemoteCommand::JtagReset)?;
1205 }
1206 WireProtocol::Swd => {
1207 self.command(RemoteCommand::SwdInit)?;
1208 }
1209 }
1210 Ok(())
1211 }
1212
1213 fn active_protocol(&self) -> Option<WireProtocol> {
1214 self.protocol
1215 }
1216
1217 fn try_as_jtag_probe(&mut self) -> Option<&mut dyn JtagAccess> {
1218 Some(self)
1219 }
1220
1221 fn try_get_riscv_interface_builder<'probe>(
1222 &'probe mut self,
1223 ) -> Result<Box<dyn RiscvInterfaceBuilder<'probe> + 'probe>, RiscvError> {
1224 Ok(Box::new(JtagDtmBuilder::new(self)))
1225 }
1226
1227 fn has_riscv_interface(&self) -> bool {
1228 true
1229 }
1230
1231 fn into_probe(self: Box<Self>) -> Box<dyn DebugProbe> {
1232 self
1233 }
1234
1235 fn try_get_arm_debug_interface<'probe>(
1237 mut self: Box<Self>,
1238 sequence: Arc<dyn ArmDebugSequence>,
1239 ) -> Result<Box<dyn ArmDebugInterface + 'probe>, (Box<dyn DebugProbe>, ArmError)> {
1240 let accelerators = match self.remote_protocol {
1241 ProtocolVersion::V0 => Accelerators(0),
1242 ProtocolVersion::V0P
1243 | ProtocolVersion::V1
1244 | ProtocolVersion::V2
1245 | ProtocolVersion::V3 => {
1246 let mut accelerators = Accelerators(0);
1247 accelerators.set_has_adiv5(true);
1248 accelerators
1249 }
1250 ProtocolVersion::V4 => {
1251 if let Ok(response) = self.command(RemoteCommand::GetAccelerators) {
1252 Accelerators(response.0)
1253 } else {
1254 Accelerators(0)
1255 }
1256 }
1257 };
1258
1259 if accelerators.has_adiv5() {
1260 match BlackMagicProbeArmDebug::new(self, sequence, accelerators) {
1261 Ok(interface) => Ok(Box::new(interface)),
1262 Err((probe, err)) => Err((probe.into_probe(), err)),
1263 }
1264 } else {
1265 Ok(ArmCommunicationInterface::create(self, sequence, true)) }
1267 }
1268
1269 fn has_arm_interface(&self) -> bool {
1270 true
1271 }
1272
1273 fn try_get_xtensa_interface<'probe>(
1274 &'probe mut self,
1275 state: &'probe mut XtensaDebugInterfaceState,
1276 ) -> Result<XtensaCommunicationInterface<'probe>, XtensaError> {
1277 Ok(XtensaCommunicationInterface::new(self, state))
1278 }
1279
1280 fn has_xtensa_interface(&self) -> bool {
1281 true
1282 }
1283}
1284
1285impl AutoImplementJtagAccess for BlackMagicProbe {}
1286impl DapProbe for BlackMagicProbe {}
1287
1288impl RawSwdIo for BlackMagicProbe {
1289 fn swd_io<S>(&mut self, swdio: S) -> Result<Vec<bool>, DebugProbeError>
1290 where
1291 S: IntoIterator<Item = IoSequenceItem>,
1292 {
1293 self.perform_swdio_transfer(swdio)
1294 }
1295
1296 fn swj_pins(
1297 &mut self,
1298 pin_out: u32,
1299 pin_select: u32,
1300 _pin_wait: u32,
1301 ) -> Result<u32, DebugProbeError> {
1302 if pin_select & 0x2f != 0 {
1305 return Err(DebugProbeError::CommandNotSupportedByProbe {
1306 command_name: "swj_pins",
1307 });
1308 }
1309 if pin_select & 0x80 != 0 {
1311 self.command(RemoteCommand::TargetReset(pin_out & 0x80 == 0))?;
1312 }
1313 Ok(pin_out)
1314 }
1315
1316 fn swd_settings(&self) -> &SwdSettings {
1317 &self.swd_settings
1318 }
1319}
1320
1321impl RawJtagIo for BlackMagicProbe {
1322 fn shift_bit(
1323 &mut self,
1324 tms: bool,
1325 tdi: bool,
1326 capture_tdo: bool,
1327 ) -> Result<(), DebugProbeError> {
1328 self.jtag_state.state.update(tms);
1329 let response = self.command(RemoteCommand::JtagNext { tms, tdi }).unwrap();
1330 if capture_tdo {
1331 self.in_bits.push(response.0 != 0);
1332 }
1333 Ok(())
1334 }
1335
1336 fn shift_bits(
1337 &mut self,
1338 tms: impl IntoIterator<Item = bool>,
1339 tdi: impl IntoIterator<Item = bool>,
1340 cap: impl IntoIterator<Item = bool>,
1341 ) -> Result<(), DebugProbeError> {
1342 let mut transaction = vec![];
1343 let mut last_tms = false;
1344 let mut last_cap = false;
1345
1346 let mut special_transaction = false;
1347 let mut tms_true_count = 0;
1348 let mut cap_count = 0;
1349 for (tms, (tdi, cap)) in tms.into_iter().zip(tdi.into_iter().zip(cap)) {
1350 if tms {
1351 tms_true_count += 1;
1352 }
1353 if cap {
1354 cap_count += 1;
1355 }
1356 last_tms = tms;
1357 last_cap = cap;
1358 transaction.push((tms, tdi, cap));
1359 }
1360
1361 if (cap_count != 0 && (cap_count + 1 != transaction.len())) || last_cap {
1364 special_transaction = true;
1365 }
1366
1367 if tms_true_count > 1 || (tms_true_count == 1 && !last_tms) {
1369 special_transaction = true;
1370 }
1371
1372 if special_transaction {
1373 for (tms, tdi, cap) in transaction {
1374 self.shift_bit(tms, tdi, cap)?;
1375 }
1376 } else {
1377 self.jtag_state.state.update(tms_true_count > 0);
1378 self.perform_jtag_transfer(transaction, tms_true_count > 0, cap_count > 0)?;
1379 }
1380
1381 Ok(())
1382 }
1383
1384 fn read_captured_bits(&mut self) -> Result<BitVec, DebugProbeError> {
1385 tracing::trace!("reading captured bits");
1386 Ok(std::mem::take(&mut self.in_bits))
1387 }
1388
1389 fn state_mut(&mut self) -> &mut JtagDriverState {
1390 &mut self.jtag_state
1391 }
1392
1393 fn state(&self) -> &JtagDriverState {
1394 &self.jtag_state
1395 }
1396}
1397
1398fn black_magic_debug_port_info(
1402 port_type: SerialPortType,
1403 port_name: &str,
1404) -> Option<DebugProbeInfo> {
1405 if cfg!(target_os = "macos") && !port_name.contains("/cu.") {
1408 tracing::trace!(
1409 "{}: port name doesn't contain `/cu.` -- skipping",
1410 port_name
1411 );
1412 return None;
1413 }
1414
1415 let (vendor_id, product_id, serial_number, mut interface, identifier) = match port_type {
1416 SerialPortType::UsbPort(info) => (
1417 info.vid,
1418 info.pid,
1419 info.serial_number.map(|s| s.to_string()),
1420 info.interface,
1421 info.product
1422 .unwrap_or_else(|| "Black Magic Probe".to_string()),
1423 ),
1424 _ => {
1425 tracing::trace!(
1426 "{}: serial port {:?} is not USB -- skipping",
1427 port_name,
1428 port_type,
1429 );
1430 return None;
1431 }
1432 };
1433
1434 if vendor_id != BLACK_MAGIC_PROBE_VID {
1435 tracing::trace!(
1436 "{}: vid is {:04x}, not {:04x} -- skipping",
1437 port_name,
1438 vendor_id,
1439 BLACK_MAGIC_PROBE_VID
1440 );
1441 return None;
1442 }
1443
1444 if product_id != BLACK_MAGIC_PROBE_PID {
1445 tracing::trace!(
1446 "{}: pid is {:04x}, not {:04x} -- skipping",
1447 port_name,
1448 product_id,
1449 BLACK_MAGIC_PROBE_PID
1450 );
1451 return None;
1452 }
1453
1454 if cfg!(target_os = "macos") && interface.is_none() {
1458 tracing::warn!(
1459 "{}: interface number is `None` -- applying interface number workaround",
1460 port_name
1461 );
1462 interface = port_name.as_bytes().last().map(|v| *v - b'0');
1463 }
1464
1465 if interface != Some(0) && interface != Some(1) {
1468 tracing::trace!(
1469 "{}: interface is {:?}, not Some(0) or Some(1) -- skipping",
1470 port_name,
1471 interface
1472 );
1473 return None;
1474 }
1475
1476 tracing::debug!(
1477 "{}: returning port {}:{}:{:?}",
1478 port_name,
1479 vendor_id,
1480 product_id,
1481 serial_number
1482 );
1483 Some(DebugProbeInfo {
1484 identifier,
1485 vendor_id,
1486 product_id,
1487 serial_number,
1488 probe_factory: &BlackMagicProbeFactory,
1489 interface,
1490 is_hid_interface: false,
1491 })
1492}
1493
1494impl ProbeFactory for BlackMagicProbeFactory {
1495 fn open(
1496 &self,
1497 selector: &super::DebugProbeSelector,
1498 ) -> Result<Box<dyn DebugProbe>, DebugProbeError> {
1499 if selector.vendor_id != BLACK_MAGIC_PROBE_VID
1501 || selector.product_id != BLACK_MAGIC_PROBE_PID
1502 {
1503 tracing::trace!(
1504 "{:04x}:{:04x} doesn't match BMP VID/PID {:04x}:{:04x}",
1505 selector.vendor_id,
1506 selector.product_id,
1507 BLACK_MAGIC_PROBE_VID,
1508 BLACK_MAGIC_PROBE_PID
1509 );
1510 return Err(DebugProbeError::ProbeCouldNotBeCreated(
1511 ProbeCreationError::NotFound,
1512 ));
1513 }
1514
1515 if let Some(serial_number) = &selector.serial_number
1518 && let Ok(connection) = std::net::TcpStream::connect(serial_number)
1519 {
1520 let reader = connection;
1521 let writer = reader
1522 .try_clone()
1523 .map_err(|e| DebugProbeError::ProbeCouldNotBeCreated(ProbeCreationError::Usb(e)))?;
1524 return BlackMagicProbe::new(Box::new(reader), Box::new(writer))
1525 .map(|p| Box::new(p) as Box<dyn DebugProbe>);
1526 }
1527
1528 let Ok(ports) = available_ports() else {
1530 tracing::trace!("unable to get available serial ports");
1531 return Err(DebugProbeError::ProbeCouldNotBeCreated(
1532 ProbeCreationError::CouldNotOpen,
1533 ));
1534 };
1535
1536 for port_description in ports {
1537 let Some(info) = black_magic_debug_port_info(
1538 port_description.port_type,
1539 &port_description.port_name,
1540 ) else {
1541 continue;
1542 };
1543
1544 if selector.serial_number.is_some() && selector.serial_number != info.serial_number {
1545 tracing::trace!(
1546 "serial number {:?} doesn't match requested number {:?}",
1547 info.serial_number,
1548 selector.serial_number
1549 );
1550 continue;
1551 }
1552
1553 let mut port = serialport::new(port_description.port_name, 115200)
1556 .timeout(std::time::Duration::from_secs(1))
1557 .open()
1558 .map_err(|_| {
1559 DebugProbeError::ProbeCouldNotBeCreated(ProbeCreationError::CouldNotOpen)
1560 })?;
1561
1562 port.write_data_terminal_ready(true).map_err(|_| {
1564 DebugProbeError::ProbeCouldNotBeCreated(ProbeCreationError::CouldNotOpen)
1565 })?;
1566 std::thread::sleep(Duration::from_millis(250));
1568 let reader = port;
1569 let writer = reader.try_clone().map_err(|_| {
1570 DebugProbeError::ProbeCouldNotBeCreated(ProbeCreationError::CouldNotOpen)
1571 })?;
1572 return BlackMagicProbe::new(Box::new(reader), Box::new(writer))
1573 .map(|p| Box::new(p) as Box<dyn DebugProbe>);
1574 }
1575
1576 tracing::trace!("unable to find port {:?}", selector);
1577 Err(DebugProbeError::ProbeCouldNotBeCreated(
1578 ProbeCreationError::NotFound,
1579 ))
1580 }
1581
1582 fn list_probes(&self) -> Vec<ProbeListItem> {
1583 let mut probes = vec![];
1584 let ports = match available_ports() {
1585 Ok(ports) => ports,
1586 Err(e) => {
1587 tracing::trace!("Unable to enumerate serial ports: {}", e);
1588 return probes;
1589 }
1590 };
1591
1592 for port in ports {
1593 let Some(info) = black_magic_debug_port_info(port.port_type, &port.port_name) else {
1594 continue;
1595 };
1596 let accessibility = crate::probe::list::device_node_accessibility(&port.port_name);
1598 probes.push(ProbeListItem {
1599 info,
1600 accessibility,
1601 });
1602 }
1603 probes
1604 }
1605
1606 fn list_probes_filtered(&self, selector: Option<&DebugProbeSelector>) -> Vec<ProbeListItem> {
1607 let Some(selector) = selector else {
1609 return self.list_probes();
1610 };
1611
1612 let vid_pid = (selector.vendor_id, selector.product_id);
1613
1614 let Some(serial) = selector.serial_number.as_deref() else {
1615 if vid_pid != BLACK_MAGIC_PROBE {
1616 return vec![];
1618 }
1619 return self.list_probes();
1621 };
1622
1623 let Ok(ip_port) = serial.parse::<SocketAddr>() else {
1625 if vid_pid != BLACK_MAGIC_PROBE {
1626 return vec![];
1628 }
1629 return self
1631 .list_probes()
1632 .into_iter()
1633 .filter(|probe| selector.matches_probe(&probe.info))
1634 .collect();
1635 };
1636
1637 if vid_pid != BLACK_MAGIC_PROBE && vid_pid != (0, 0) {
1638 return vec![];
1640 }
1641
1642 vec![ProbeListItem::accessible(DebugProbeInfo {
1644 identifier: format!("{}:{}", ip_port.ip(), ip_port.port()),
1645 vendor_id: BLACK_MAGIC_PROBE_VID,
1646 product_id: BLACK_MAGIC_PROBE_PID,
1647 serial_number: Some(ip_port.to_string()),
1648 probe_factory: &BlackMagicProbeFactory,
1649 interface: None,
1650 is_hid_interface: false,
1651 })]
1652 }
1653}