Skip to main content

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        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/// A factory for creating [`BlackMagicProbe`] instances.
45#[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    /// Return the buffer from the payload of the specified value where the
296    /// response should be written.
297    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    /// Return `true` if the resulting buffer should have hex decoded.
310    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// Implement `ToString` instead of `Display` as this is for generating
323// strings to send over the network, and is not meant for human consumption.
324#[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
705/// A Black Magic Probe.
706pub 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        // Strip off leading `0x` if present
833        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        // Convert the hex in the buffer to a u64.
865        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        // Responses begin with `&`
875        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 there was no incoming buffer, then we will read up to 64 bits of data and
891        // return a response based on that.
892
893        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                // Read the first nibble
904                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                // Add one byte to the resulting output value. This is the case whether we
912                // get one or two nibbles.
913                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                    // Read the second nibble, if present.
920                    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    /// Perform a single SWDIO command
1008    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                // Inputs are off-by-one due to input latency. Remove one bit
1024                // from the accumulator and store the turnaround bit at the end of
1025                // the transaction.
1026                if self.swd_direction == SwdDirection::Input && dir == SwdDirection::Output {
1027                    accumulator_length -= 2;
1028                }
1029
1030                // Drain the accumulator to the BMP, either writing bits to the device
1031                // or reading bits from the device.
1032                self.drain_swd_accumulator(&mut output, accumulator, accumulator_length)?;
1033
1034                // Input -> Output transition
1035                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 this is the last bit, then `cap` may be false.
1075            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        // Enable output on the clock pin (if supported)
1139        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        // TODO we could add this by using a GPIO. However, different probes may connect
1181        // different pins (if any) to the reset line, so we would need to make this configurable.
1182        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    /// Turn this probe into an ARM probe
1236    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)) // TODO: Fixup the error type here
1266        }
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        // The Black Magic Probe doesn't support setting TCK/TMS/TDI/TDO directly,
1303        // and has no separate nTRST.
1304        if pin_select & 0x2f != 0 {
1305            return Err(DebugProbeError::CommandNotSupportedByProbe {
1306                command_name: "swj_pins",
1307            });
1308        }
1309        // Set the nRST pin according to the specified value
1310        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        // A strange number of bits are captured, such as including the last bit or
1362        // including a smattering of bits in the middle of the transaction.
1363        if (cap_count != 0 && (cap_count + 1 != transaction.len())) || last_cap {
1364            special_transaction = true;
1365        }
1366
1367        // The TMS value is `true` for a field other than the last bit
1368        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
1398/// Determine if a given serial port is a Black Magic Probe GDB interface.
1399/// The BMP has at least two serial ports, and we want to make sure we get
1400/// the correct one.
1401fn black_magic_debug_port_info(
1402    port_type: SerialPortType,
1403    port_name: &str,
1404) -> Option<DebugProbeInfo> {
1405    // Only accept /dev/cu.* values on macos, to avoid having two
1406    // copies of the port (both /dev/tty.* and /dev/cu.*)
1407    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    // The `interface` property has been observed on Mac to occasionally be `None`.
1455    // This shouldn't happen on any known devices. If this happens, derive the
1456    // interface number from the last character of the filename.
1457    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    // Mac specifies the interface as the CDC Data interface, whereas Linux and
1466    // Windows use the CDC Communications interface. Accept either one here.
1467    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        // Ensure the VID and PID match Black Magic Probes
1500        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 the serial number is a valid "address:port" string, attempt to
1516        // connect to it via TCP.
1517        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        // Otherwise, treat it as a serial port and iterate through all ports.
1529        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            // Open with the baud rate 115200. This baud rate is arbitrary, since it's
1554            // a soft USB device and will run at the same speed regardless of the baud rate.
1555            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            // Set DTR, indicating we're ready to communicate.
1563            port.write_data_terminal_ready(true).map_err(|_| {
1564                DebugProbeError::ProbeCouldNotBeCreated(ProbeCreationError::CouldNotOpen)
1565            })?;
1566            // A delay appears necessary to allow the BMP to recognize the DTR signal.
1567            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            // Black Magic probes are accessed over a serial port; check that node, not usbfs.
1597            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        // No selector - list probes as usual
1608        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                // Filter is not for black magic probes, skip listing.
1617                return vec![];
1618            }
1619            // Since there is no serial specified, we can list all probes.
1620            return self.list_probes();
1621        };
1622
1623        // If the selector refers to an IP:port pair, return that as the list of probes.
1624        let Ok(ip_port) = serial.parse::<SocketAddr>() else {
1625            if vid_pid != BLACK_MAGIC_PROBE {
1626                // Filter is not for black magic probes, skip listing.
1627                return vec![];
1628            }
1629            // The selector is not an IP:port pair, so we can list all probes, applying the requested filter.
1630            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            // Filter is not for black magic probes, skip listing.
1639            return vec![];
1640        }
1641
1642        // Filter is a valid probe, and VID:PID is either a BMP or the "not specified" convention.
1643        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}