probe_rs/probe/blackmagic/
mod.rs

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