Skip to main content

probe_rs/probe/jlink/
mod.rs

1//! Support for J-Link Debug probes
2
3mod bits;
4pub mod capabilities;
5mod config;
6mod connection;
7mod error;
8mod interface;
9mod speed;
10pub mod swo;
11
12use std::fmt;
13use std::sync::Arc;
14use std::time::{Duration, Instant};
15
16use bitvec::prelude::*;
17
18use itertools::Itertools;
19use nusb::{DeviceInfo, MaybeFuture, descriptors::TransferType, transfer::Direction};
20
21use self::bits::BitIter;
22use self::capabilities::{Capabilities, Capability};
23use self::error::JlinkError;
24use self::interface::{Interface, Interfaces};
25use self::speed::SpeedConfig;
26use self::swo::SwoMode;
27use crate::architecture::arm::sequences::ArmDebugSequence;
28use crate::architecture::arm::{ArmDebugInterface, ArmError, Pins};
29use crate::architecture::riscv::communication_interface::RiscvError;
30use crate::architecture::xtensa::communication_interface::{
31    XtensaCommunicationInterface, XtensaDebugInterfaceState, XtensaError,
32};
33use crate::probe::jlink::bits::IteratorExt;
34use crate::probe::jlink::config::JlinkConfig;
35use crate::probe::jlink::connection::JlinkConnection;
36use crate::probe::usb_util::InterfaceExt;
37use crate::probe::{AutoImplementJtagAccess, JtagAccess};
38use crate::{
39    architecture::{
40        arm::{
41            ArmCommunicationInterface, SwoAccess, communication_interface::DapProbe, swo::SwoConfig,
42        },
43        riscv::{communication_interface::RiscvInterfaceBuilder, dtm::jtag_dtm::JtagDtmBuilder},
44    },
45    probe::{
46        DebugProbe, DebugProbeError, DebugProbeInfo, DebugProbeSelector, IoSequenceItem,
47        JtagDriverState, ProbeFactory, RawJtagIo, RawSwdIo, SwdSettings, WireProtocol,
48        list::{ProbeListItem, usb_probe_accessibility},
49    },
50};
51
52const SWO_BUFFER_SIZE: u16 = 128;
53const TIMEOUT_DEFAULT: Duration = Duration::from_millis(500);
54
55/// Factory to create [`JLink`] probes.
56#[derive(Debug)]
57pub struct JLinkFactory;
58
59impl std::fmt::Display for JLinkFactory {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        f.write_str("J-Link")
62    }
63}
64
65impl ProbeFactory for JLinkFactory {
66    fn open(&self, selector: &DebugProbeSelector) -> Result<Box<dyn DebugProbe>, DebugProbeError> {
67        fn open_error(e: std::io::Error, while_: &'static str) -> DebugProbeError {
68            let help = if cfg!(windows) {
69                "(this error may be caused by not having the WinUSB driver installed; use Zadig (https://zadig.akeo.ie/) to install it for the J-Link device; this will replace the SEGGER J-Link driver)"
70            } else {
71                ""
72            };
73
74            DebugProbeError::Usb(std::io::Error::other(format!(
75                "error while {while_}: {e}{help}",
76            )))
77        }
78
79        let mut jlinks = match nusb::list_devices().wait() {
80            Ok(devices) => devices
81                .filter(is_jlink)
82                .filter(|info| selector.matches(info))
83                .collect::<Vec<_>>(),
84            Err(e) => return Err(open_error(e.into(), "listing USB devices")),
85        };
86
87        if jlinks.is_empty() {
88            return Err(DebugProbeError::ProbeCouldNotBeCreated(
89                super::ProbeCreationError::NotFound,
90            ));
91        } else if jlinks.len() > 1 {
92            tracing::warn!("More than one matching J-Link was found. Opening the first one.")
93        }
94
95        let info = jlinks.pop().unwrap();
96
97        let handle = info
98            .open()
99            .wait()
100            .map_err(|e| open_error(e.into(), "opening the USB device"))?;
101
102        let configs: Vec<_> = handle.configurations().collect();
103
104        if configs.len() != 1 {
105            tracing::warn!("device has {} configurations, expected 1", configs.len());
106        }
107
108        let conf = &configs[0];
109        tracing::debug!("scanning {} interfaces", conf.interfaces().count());
110        tracing::trace!("active configuration descriptor: {:#x?}", conf);
111
112        let mut jlink_intf = None;
113        for intf in conf.interfaces() {
114            tracing::trace!("interface #{} descriptors:", intf.interface_number());
115
116            for descr in intf.alt_settings() {
117                tracing::trace!("{:#x?}", descr);
118
119                // We detect the proprietary J-Link interface using the vendor-specific class codes
120                // and the endpoint properties
121                if descr.class() == 0xff && descr.subclass() == 0xff && descr.protocol() == 0xff {
122                    if let Some((intf, _, _, _)) = jlink_intf {
123                        Err(JlinkError::Other(format!(
124                            "found multiple matching USB interfaces ({} and {})",
125                            intf,
126                            descr.interface_number()
127                        )))?;
128                    }
129
130                    let endpoints: Vec<_> = descr.endpoints().collect();
131                    tracing::trace!("endpoint descriptors: {:#x?}", endpoints);
132                    if endpoints.len() != 2 {
133                        tracing::warn!(
134                            "vendor-specific interface with {} endpoints, expected 2 (skipping interface)",
135                            endpoints.len()
136                        );
137                        continue;
138                    }
139
140                    if !endpoints
141                        .iter()
142                        .all(|ep| ep.transfer_type() == TransferType::Bulk)
143                    {
144                        tracing::warn!(
145                            "encountered non-bulk endpoints, skipping interface: {:#x?}",
146                            endpoints
147                        );
148                        continue;
149                    }
150
151                    let (read_ep, write_ep) = if endpoints[0].direction() == Direction::In {
152                        (&endpoints[0], &endpoints[1])
153                    } else {
154                        (&endpoints[1], &endpoints[0])
155                    };
156
157                    jlink_intf = Some((
158                        descr.interface_number(),
159                        read_ep.address(),
160                        write_ep.address(),
161                        read_ep.max_packet_size(),
162                    ));
163                    tracing::debug!("J-Link interface is #{}", descr.interface_number());
164                }
165            }
166        }
167
168        let Some((intf, read_ep, write_ep, max_read_ep_packet)) = jlink_intf else {
169            Err(JlinkError::Other(
170                "device is not a J-Link device".to_string(),
171            ))?
172        };
173
174        let handle = handle
175            .claim_interface(intf)
176            .wait()
177            .map_err(|e| open_error(e.into(), "taking control over USB device"))?;
178
179        let mut this = JLink {
180            read_ep,
181            write_ep,
182            max_read_ep_packet,
183            caps: Capabilities::from_raw_legacy(0), // dummy value
184            interface: Interface::Spi,              // dummy value, must not be JTAG
185            interfaces: Interfaces::from_bits_warn(0), // dummy value
186            handle,
187
188            supported_protocols: vec![],  // dummy value
189            protocol: WireProtocol::Jtag, // dummy value
190            connection_handle: None,
191
192            swo_config: None,
193            speed_khz: 0, // default is unknown
194            swd_settings: SwdSettings::default(),
195            jtag_state: JtagDriverState::default(),
196
197            jtag_tms_bits: vec![],
198            jtag_tdi_bits: vec![],
199            jtag_capture_tdo: vec![],
200            jtag_response: BitVec::new(),
201
202            max_mem_block_size: 0, // dummy value
203            jtag_chunk_size: 0,    // dummy value
204
205            config: JlinkConfig::default(),
206        };
207        this.fill_capabilities()?;
208        this.fill_interfaces()?;
209
210        this.supported_protocols = if this.caps.contains(Capability::SelectIf) {
211            this.interfaces
212                .into_iter()
213                .filter_map(|p| match WireProtocol::try_from(p) {
214                    Ok(protocol) => Some(protocol),
215                    Err(JlinkError::UnknownInterface(interface)) => {
216                        // We ignore unknown protocols.
217                        tracing::debug!(
218                            "J-Link returned interface {:?}, which is not supported by probe-rs.",
219                            interface
220                        );
221                        None
222                    }
223                    Err(_) => None,
224                })
225                .collect::<Vec<_>>()
226        } else {
227            // The J-Link cannot report which interfaces it supports, and cannot
228            // switch interfaces. We assume it just supports JTAG.
229            vec![WireProtocol::Jtag]
230        };
231
232        this.protocol = if this.supported_protocols.contains(&WireProtocol::Swd) {
233            // Default to SWD if supported, since it's the most commonly used.
234            WireProtocol::Swd
235        } else {
236            // Otherwise just pick the first supported.
237            *this.supported_protocols.first().unwrap()
238        };
239
240        if this.caps.contains(Capability::GetMaxBlockSize) {
241            this.max_mem_block_size = this.read_max_mem_block()? as usize;
242
243            tracing::debug!(
244                "J-Link max mem block size for SWD IO: {} byte",
245                this.max_mem_block_size
246            );
247        } else {
248            tracing::debug!(
249                "J-Link does not support GET_MAX_MEM_BLOCK, using default value of 65535"
250            );
251            this.max_mem_block_size = 65535;
252        }
253
254        // Some devices can't handle large transfers, so we limit the chunk size.
255        // While it would be nice to read this directly from the device,
256        // `read_max_mem_block`'s return value does not directly correspond to the
257        // maximum transfer size when performing JTAG IO, and it's not clear how to get the actual value.
258        // The number of *bits* is encoded as a u16, so the maximum value is 65535
259        this.jtag_chunk_size = match selector.product_id {
260            // 0x0101: J-Link EDU
261            0x0101 => 65535,
262            // 0x1051: J-Link OB-K22-SiFive: 504 bits
263            0x1051 => 504,
264            // Assume the lowest value is a safe default
265            _ => 504,
266        };
267        this.config = this.read_device_config()?;
268        this.connection_handle = if requires_connection_handle(selector) {
269            Some(this.register_connection()?)
270        } else {
271            None
272        };
273
274        Ok(Box::new(this))
275    }
276
277    fn list_probes(&self) -> Vec<ProbeListItem> {
278        list_jlink_devices()
279    }
280}
281
282fn requires_connection_handle(selector: &DebugProbeSelector) -> bool {
283    // These devices require a connection handle to be registered before they can be used.
284    // As some other devices can't handle the registration command, we only enable it for known
285    // devices.
286    let devices = [
287        (0x1366, 0x0101, Some("000000123456")), // Blue J-Link PRO clone
288    ];
289
290    devices.contains(&(
291        selector.vendor_id,
292        selector.product_id,
293        selector.serial_number.as_deref(),
294    ))
295}
296
297impl Drop for JLink {
298    fn drop(&mut self) {
299        self.unregister_connection().ok();
300    }
301}
302
303#[repr(u8)]
304#[expect(dead_code)]
305enum Command {
306    Version = 0x01,
307    Register = 0x09,
308    GetSpeeds = 0xC0,
309    GetMaxMemBlock = 0xD4,
310    GetCaps = 0xE8,
311    GetCapsEx = 0xED,
312    GetHwVersion = 0xF0,
313
314    GetState = 0x07,
315    GetHwInfo = 0xC1,
316    GetCounters = 0xC2,
317    MeasureRtckReact = 0xF6,
318
319    ResetTrst = 0x02,
320    SetSpeed = 0x05,
321    SelectIf = 0xC7,
322    SetKsPower = 0x08,
323    HwClock = 0xC8,
324    HwTms0 = 0xC9,
325    HwTms1 = 0xCA,
326    HwData0 = 0xCB,
327    HwData1 = 0xCC,
328    HwJtag = 0xCD,
329    HwJtag2 = 0xCE,
330    HwJtag3 = 0xCF,
331    HwJtagWrite = 0xD5,
332    HwJtagGetResult = 0xD6,
333    HwTrst0 = 0xDE,
334    HwTrst1 = 0xDF,
335    Swo = 0xEB,
336    WriteDcc = 0xF1,
337
338    ResetTarget = 0x03,
339    HwReleaseResetStopEx = 0xD0,
340    HwReleaseResetStopTimed = 0xD1,
341    HwTck0 = 0xDA,
342    HwTck1 = 0xDB,
343    HwReset0 = 0xDC,
344    HwReset1 = 0xDD,
345    GetCpuCaps = 0xE9,
346    ExecCpuCmd = 0xEA,
347    WriteMem = 0xF4,
348    ReadMem = 0xF5,
349    WriteMemArm79 = 0xF7,
350    ReadMemArm79 = 0xF8,
351
352    ReadConfig = 0xF2,
353    WriteConfig = 0xF3,
354}
355
356/// A J-Link probe.
357pub struct JLink {
358    handle: nusb::Interface,
359
360    read_ep: u8,
361    write_ep: u8,
362    max_read_ep_packet: usize,
363
364    /// The capabilities reported by the device. They're fetched once, when the device is opened.
365    caps: Capabilities,
366
367    /// The supported interfaces. Like `caps`, this is fetched once when opening the device.
368    interfaces: Interfaces,
369
370    /// The currently selected target interface. This is stored here to avoid unnecessary roundtrips
371    /// when performing target I/O operations.
372    interface: Interface,
373
374    /// Device configuration, fetched once when the device is opened.
375    config: JlinkConfig,
376    connection_handle: Option<u16>,
377
378    swo_config: Option<SwoConfig>,
379
380    /// Protocols supported by the probe.
381    supported_protocols: Vec<WireProtocol>,
382    /// Protocol chosen by the user
383    protocol: WireProtocol,
384
385    speed_khz: u32,
386
387    jtag_tms_bits: Vec<bool>,
388    jtag_tdi_bits: Vec<bool>,
389    jtag_capture_tdo: Vec<bool>,
390    jtag_response: BitVec,
391    jtag_state: JtagDriverState,
392
393    /// max number of bits in a transfer chunk, when using JTAG
394    jtag_chunk_size: usize,
395
396    /// Maximum memory block size, as report by the `GET_MAX_MEM_BLOCK` command.
397    ///
398    /// Used to determine maximum transfer length for SWD IO.
399    max_mem_block_size: usize,
400
401    swd_settings: SwdSettings,
402}
403
404impl fmt::Debug for JLink {
405    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406        f.debug_struct("JLink").finish()
407    }
408}
409
410impl JLink {
411    /// Returns the supported J-Link capabilities.
412    pub fn capabilities(&self) -> Capabilities {
413        self.caps
414    }
415
416    /// Reads the advertised capabilities from the device.
417    fn fill_capabilities(&mut self) -> Result<(), JlinkError> {
418        self.write_cmd(&[Command::GetCaps as u8])?;
419
420        let caps = self.read_u32().map(Capabilities::from_raw_legacy)?;
421
422        tracing::debug!("legacy caps: {:?}", caps);
423
424        // If the `GET_CAPS_EX` capability is set, use the extended capability command to fetch
425        // all the capabilities.
426        if caps.contains(Capability::GetCapsEx) {
427            self.write_cmd(&[Command::GetCapsEx as u8])?;
428
429            let real_caps = self.read_n::<32>().map(Capabilities::from_raw_ex)?;
430            if !real_caps.contains_all(caps) {
431                return Err(JlinkError::Other(format!(
432                    "ext. caps are not a superset of legacy caps (legacy: {caps:?}, ex: {real_caps:?})"
433                )));
434            }
435            tracing::debug!("extended caps: {:?}", real_caps);
436            self.caps = real_caps;
437        } else {
438            tracing::debug!("extended caps not supported");
439            self.caps = caps;
440        }
441
442        Ok(())
443    }
444
445    fn fill_interfaces(&mut self) -> Result<(), JlinkError> {
446        if !self.caps.contains(Capability::SelectIf) {
447            // Pre-SELECT_IF probes only support JTAG.
448            self.interfaces = Interfaces::single(Interface::Jtag);
449            self.interface = Interface::Jtag;
450
451            return Ok(());
452        }
453
454        self.write_cmd(&[Command::SelectIf as u8, 0xFF])?;
455
456        self.interfaces = self.read_u32().map(Interfaces::from_bits_warn)?;
457
458        Ok(())
459    }
460
461    fn write_cmd(&self, cmd: &[u8]) -> Result<(), JlinkError> {
462        tracing::trace!("write {} bytes: {:x?}", cmd.len(), cmd);
463
464        let n = self
465            .handle
466            .write_bulk(self.write_ep, cmd, TIMEOUT_DEFAULT)
467            .map_err(JlinkError::Usb)?;
468
469        if n != cmd.len() {
470            return Err(JlinkError::Other(format!(
471                "incomplete write (expected {} bytes, wrote {})",
472                cmd.len(),
473                n
474            )));
475        }
476        Ok(())
477    }
478
479    fn read(&self, buf: &mut [u8]) -> Result<(), JlinkError> {
480        let needs_workaround = buf.len().is_multiple_of(self.max_read_ep_packet);
481        let len = buf.len();
482
483        let mut tmp_buffer;
484        let dst = if needs_workaround {
485            // For some unknown reason, reading 256 bytes of config data leaves the interface in
486            // an unusable state. Force-reading one more byte works around this issue.
487            tmp_buffer = vec![0; len + 1];
488            &mut tmp_buffer
489        } else {
490            tmp_buffer = vec![];
491            &mut buf[..]
492        };
493
494        let mut total = 0;
495        while total < len {
496            let n = self
497                .handle
498                .read_bulk(self.read_ep, &mut dst[total..], TIMEOUT_DEFAULT)
499                .map_err(JlinkError::Usb)?;
500
501            total += n;
502        }
503
504        if needs_workaround {
505            buf.copy_from_slice(&tmp_buffer[..len]);
506        }
507
508        tracing::trace!("read {total} bytes: {buf:x?}");
509
510        Ok(())
511    }
512
513    fn read_n<const N: usize>(&self) -> Result<[u8; N], JlinkError> {
514        let mut buf = [0; N];
515        self.read(&mut buf)?;
516        Ok(buf)
517    }
518
519    fn read_u16(&self) -> Result<u16, JlinkError> {
520        self.read_n::<2>().map(u16::from_le_bytes)
521    }
522
523    fn read_u32(&self) -> Result<u32, JlinkError> {
524        self.read_n::<4>().map(u32::from_le_bytes)
525    }
526
527    fn require_capability(&self, cap: Capability) -> Result<(), JlinkError> {
528        if self.caps.contains(cap) {
529            Ok(())
530        } else {
531            Err(JlinkError::MissingCapability(cap))
532        }
533    }
534
535    fn require_interface_supported(&self, intf: Interface) -> Result<(), JlinkError> {
536        if self.interfaces.contains(intf) {
537            Ok(())
538        } else {
539            Err(JlinkError::InterfaceNotSupported(intf))
540        }
541    }
542
543    fn require_interface_selected(&self, intf: Interface) -> Result<(), JlinkError> {
544        if self.interface == intf {
545            Ok(())
546        } else {
547            Err(JlinkError::WrongInterfaceSelected {
548                selected: self.interface,
549                needed: intf,
550            })
551        }
552    }
553
554    /// Reads the maximum mem block size in Bytes.
555    ///
556    /// This requires the probe to support [`Capability::GetMaxBlockSize`].
557    pub fn read_max_mem_block(&self) -> Result<u32, JlinkError> {
558        // This cap refers to a nonexistent command `GET_MAX_BLOCK_SIZE`, but it probably means
559        // `GET_MAX_MEM_BLOCK`.
560        self.require_capability(Capability::GetMaxBlockSize)?;
561
562        self.write_cmd(&[Command::GetMaxMemBlock as u8])?;
563
564        self.read_u32()
565    }
566
567    fn read_device_config(&self) -> Result<JlinkConfig, JlinkError> {
568        if self.caps.contains(Capability::ReadConfig) {
569            self.write_cmd(&[Command::ReadConfig as u8])?;
570            let bytes = self.read_n::<256>()?;
571
572            let config = match JlinkConfig::parse(bytes) {
573                Ok(config) => {
574                    tracing::debug!("J-Link config: {:?}", config);
575                    config
576                }
577                Err(error) => {
578                    tracing::warn!("Failed to parse J-Link config: {error}");
579                    JlinkConfig::default()
580                }
581            };
582
583            Ok(config)
584        } else {
585            Ok(JlinkConfig::default())
586        }
587    }
588
589    /// Reads the firmware version string from the device.
590    fn read_firmware_version(&self) -> Result<String, JlinkError> {
591        self.write_cmd(&[Command::Version as u8])?;
592
593        let num_bytes = self.read_u16()?;
594        let mut buf = vec![0; num_bytes as usize];
595        self.read(&mut buf)?;
596
597        Ok(String::from_utf8_lossy(
598            // The firmware version string returned may contain null bytes. If
599            // this happens, only return the preceding bytes.
600            match buf.iter().position(|&b| b == 0) {
601                Some(pos) => &buf[..pos],
602                None => &buf,
603            },
604        )
605        .into_owned())
606    }
607
608    /// Reads the hardware version from the device.
609    ///
610    /// This requires the probe to support [`Capability::GetHwVersion`].
611    fn read_hardware_version(&self) -> Result<HardwareVersion, JlinkError> {
612        self.require_capability(Capability::GetHwVersion)?;
613
614        self.write_cmd(&[Command::GetHwVersion as u8])?;
615
616        self.read_u32().map(HardwareVersion::from_u32)
617    }
618
619    /// Selects the interface to use for talking to the target MCU.
620    ///
621    /// Switching interfaces will reset the configured transfer speed, so [`JLink::set_speed`]
622    /// needs to be called *after* `select_interface`.
623    ///
624    /// This requires the probe to support [`Capability::SelectIf`].
625    ///
626    /// **Note**: Selecting a different interface may cause the J-Link to perform target I/O!
627    fn select_interface(&mut self, intf: Interface) -> Result<(), JlinkError> {
628        if self.interface == intf {
629            return Ok(());
630        }
631
632        self.require_capability(Capability::SelectIf)?;
633
634        self.require_interface_supported(intf)?;
635
636        self.write_cmd(&[Command::SelectIf as u8, intf as u8])?;
637
638        // Returns the previous interface, ignore it
639        let _ = self.read_u32()?;
640
641        self.interface = intf;
642
643        if self.speed_khz != 0 {
644            // SelectIf resets the configured speed. Let's restore it.
645            self.set_interface_clock_speed(SpeedConfig::khz(self.speed_khz as u16).unwrap())?;
646        }
647
648        Ok(())
649    }
650
651    /// Changes the state of the RESET pin (pin 15).
652    ///
653    /// RESET is an open-collector / open-drain output. If `reset` is `true`, the output will float.
654    /// If `reset` is `false`, the output will be pulled to ground.
655    ///
656    /// **Note**: Some embedded J-Link probes may not expose this pin or may not allow controlling
657    /// it using this function.
658    fn set_reset(&mut self, reset: bool) -> Result<(), JlinkError> {
659        let cmd = if reset {
660            Command::HwReset1
661        } else {
662            Command::HwReset0
663        };
664        self.write_cmd(&[cmd as u8])
665    }
666
667    /// Changes the state of the TMS pin (pin 7).
668    ///
669    /// **Note**: Some embedded J-Link probes may not expose this pin or may not allow controlling
670    /// it using this function.
671    fn set_tms(&mut self, tms: bool) -> Result<(), JlinkError> {
672        let cmd = if tms {
673            Command::HwTms1
674        } else {
675            Command::HwTms0
676        };
677        self.write_cmd(&[cmd as u8])
678    }
679
680    /// Changes the state of the TCK pin (pin 9).
681    ///
682    /// **Note**: Some embedded J-Link probes may not expose this pin or may not allow controlling
683    /// it using this function.
684    fn set_tck(&mut self, tck: bool) -> Result<(), JlinkError> {
685        let cmd = if tck {
686            Command::HwTck1
687        } else {
688            Command::HwTck0
689        };
690        self.write_cmd(&[cmd as u8])
691    }
692
693    /// Resets the target's JTAG TAP controller by temporarily asserting (n)TRST (Pin 3).
694    ///
695    /// This might not do anything if the pin is not connected to the target. It does not affect
696    /// non-JTAG target interfaces.
697    fn reset_trst(&mut self) -> Result<(), JlinkError> {
698        self.write_cmd(&[Command::ResetTrst as u8])
699    }
700
701    /// Reads the target voltage measured on the `VTref` pin, in millivolts.
702    ///
703    /// In order to use the J-Link, this voltage must be present, since it will be used as the level
704    /// of the I/O signals to the target.
705    fn read_target_voltage(&self) -> Result<u16, JlinkError> {
706        self.write_cmd(&[Command::GetState as u8])?;
707
708        let buf = self.read_n::<8>()?;
709
710        let voltage = [buf[0], buf[1]];
711        Ok(u16::from_le_bytes(voltage))
712    }
713
714    fn shift_jtag_bit(
715        &mut self,
716        tms: bool,
717        tdi: bool,
718        capture: bool,
719    ) -> Result<(), DebugProbeError> {
720        self.jtag_state.state.update(tms);
721
722        self.jtag_tms_bits.push(tms);
723        self.jtag_tdi_bits.push(tdi);
724        self.jtag_capture_tdo.push(capture);
725
726        if self.jtag_tms_bits.len() >= self.jtag_chunk_size {
727            self.flush_jtag()?;
728        }
729
730        Ok(())
731    }
732
733    fn flush_jtag(&mut self) -> Result<(), JlinkError> {
734        if self.jtag_tms_bits.is_empty() {
735            return Ok(());
736        }
737
738        self.require_interface_selected(Interface::Jtag)?;
739
740        let mut has_status_byte = false;
741        // There's 3 commands for doing a JTAG transfer. The older 2 are obsolete with hardware
742        // version 5 and above, which adds the 3rd command. Unfortunately we cannot reliably use the
743        // HW version to determine this since some embedded J-Link probes have a HW version of
744        // 1.0.0, but still support SWD, so we use the `SELECT_IF` capability instead.
745        let cmd = if self.caps.contains(Capability::SelectIf) {
746            // Use the new JTAG3 command, make sure to select the JTAG interface mode
747            has_status_byte = true;
748            Command::HwJtag3
749        } else {
750            // Use the legacy JTAG2 command
751            // FIXME is HW_JTAG relevant at all?
752            Command::HwJtag2
753        };
754
755        let tms_bit_count = self.jtag_tms_bits.len();
756        let tdi_bit_count = self.jtag_tdi_bits.len();
757        assert_eq!(
758            tms_bit_count, tdi_bit_count,
759            "TMS and TDI must have the same number of bits"
760        );
761        let capacity = 1 + 1 + 2 + tms_bit_count.div_ceil(8) * 2;
762        let mut buf = Vec::with_capacity(capacity);
763        buf.resize(4, 0);
764        buf[0] = cmd as u8;
765        // JTAG3 and JTAG2 use the same format for JTAG operations
766        // buf[1] is dummy data for alignment
767        // buf[2..=3] is the bit count
768        let bit_count = u16::try_from(tms_bit_count).expect("too much data to transfer");
769        buf[2..=3].copy_from_slice(&bit_count.to_le_bytes());
770        buf.extend(self.jtag_tms_bits.drain(..).collapse_bytes());
771        buf.extend(self.jtag_tdi_bits.drain(..).collapse_bytes());
772
773        self.write_cmd(&buf)?;
774
775        // Round bit count up to multiple of 8 to get the number of response bytes.
776        let num_resp_bytes = tms_bit_count.div_ceil(8);
777        tracing::trace!(
778            "{} TMS/TDI bits sent; reading {} response bytes",
779            tms_bit_count,
780            num_resp_bytes
781        );
782
783        // Response is `num_resp_bytes` TDO data bytes and one status byte,
784        // if the JTAG3 command is used.
785        let mut read_len = num_resp_bytes;
786
787        if has_status_byte {
788            read_len += 1;
789        }
790
791        self.read(&mut buf[..read_len])?;
792
793        // Check the status if a JTAG3 command was used.
794        if has_status_byte && buf[read_len - 1] != 0 {
795            return Err(JlinkError::Other(format!(
796                "probe I/O command returned error code {:#x}",
797                buf[read_len - 1]
798            )));
799        }
800
801        let response = BitIter::new(&buf[..num_resp_bytes], tms_bit_count);
802
803        for (bit, capture) in response.zip(self.jtag_capture_tdo.drain(..)) {
804            if capture {
805                self.jtag_response.push(bit);
806            }
807        }
808
809        Ok(())
810    }
811
812    fn read_captured_bits(&mut self) -> Result<BitVec, DebugProbeError> {
813        self.flush_jtag()?;
814
815        Ok(std::mem::take(&mut self.jtag_response))
816    }
817
818    /// Perform a single SWDIO command
819    ///
820    /// The caller needs to ensure that the given iterators are not longer than the maximum transfer size
821    /// allowed. It seems that the maximum transfer size is determined by [`JLink::max_mem_block_size`].
822    fn perform_swdio_transfer<S>(&self, swdio: S) -> Result<Vec<bool>, DebugProbeError>
823    where
824        S: IntoIterator<Item = IoSequenceItem>,
825    {
826        self.require_interface_selected(Interface::Swd)?;
827
828        const COMMAND_OVERHEAD: usize = 4;
829
830        let max_bits = (self.max_mem_block_size - COMMAND_OVERHEAD) / 2 * 8;
831        let max_bits = std::cmp::min(max_bits, 65535);
832
833        let swdio_chunks = swdio.into_iter().chunks(max_bits);
834
835        let mut output = Vec::new();
836        let mut buf = Vec::with_capacity(self.max_mem_block_size);
837        buf.resize(COMMAND_OVERHEAD, 0);
838
839        for swdio in swdio_chunks.into_iter() {
840            buf.truncate(COMMAND_OVERHEAD);
841
842            const INPUT: bool = false;
843            const OUTPUT: bool = true;
844
845            let swdio: Vec<_> = swdio.collect();
846
847            //  Extend using the direction bits:
848            buf.extend(
849                swdio
850                    .iter()
851                    .map(|item| match item {
852                        IoSequenceItem::Input => INPUT,
853                        IoSequenceItem::Output { .. } => OUTPUT,
854                    })
855                    .collapse_bytes(),
856            );
857            //  Extend using the value bits:
858            buf.extend(
859                swdio
860                    .iter()
861                    .map(|item| match item {
862                        IoSequenceItem::Input => false,
863                        IoSequenceItem::Output(x) => *x,
864                    })
865                    .collapse_bytes(),
866            );
867
868            let num_bits = swdio.len() as u16;
869
870            buf[0] = Command::HwJtag3 as u8;
871            // buf[1] is dummy data for alignment
872            buf[1] = 0;
873            buf[2..=3].copy_from_slice(&num_bits.to_le_bytes());
874            let num_bytes = usize::from(num_bits.div_ceil(8));
875
876            tracing::trace!("Buffer length for j-link transfer: {}", buf.len());
877
878            self.write_cmd(&buf)?;
879
880            // Response is `num_bytes` SWDIO data bytes and one status byte
881            self.read(&mut buf[..num_bytes + 1])?;
882
883            if buf[num_bytes] != 0 {
884                return Err(JlinkError::Other(format!(
885                    "probe I/O command returned error code {:#x}",
886                    buf[num_bytes]
887                ))
888                .into());
889            }
890
891            output.extend(BitIter::new(&buf[..num_bytes], num_bits as usize));
892        }
893
894        Ok(output)
895    }
896
897    /// Enable/Disable the Target Power Supply of the probe.
898    ///
899    /// This is not available on all J-Links.
900    pub fn set_kickstart_power(&mut self, enable: bool) -> Result<(), JlinkError> {
901        self.require_capability(Capability::SetKsPower)?;
902        self.write_cmd(&[Command::SetKsPower as u8, if enable { 1 } else { 0 }])
903    }
904
905    fn register_connection(&mut self) -> Result<u16, JlinkError> {
906        if !self.caps.contains(Capability::Register) {
907            return Ok(0);
908        }
909
910        // Undocumented, taken from OpenOCD/libjaylink
911        let mut buf = vec![Command::Register as u8, 0x64];
912        buf.extend(JlinkConnection::usb(0).into_bytes());
913        self.write_cmd(&buf)?;
914
915        let handle = self.read_registration_response()?;
916
917        if handle == 0 {
918            return Err(JlinkError::Other("Invalid registration handle".to_string()));
919        }
920
921        Ok(handle)
922    }
923
924    fn unregister_connection(&mut self) -> Result<(), JlinkError> {
925        if !self.caps.contains(Capability::Register) {
926            return Ok(());
927        }
928
929        if let Some(handle) = self.connection_handle.take() {
930            let mut buf = vec![Command::Register as u8, 0x65];
931            buf.extend(JlinkConnection::usb(handle).into_bytes());
932            self.write_cmd(&buf)?;
933            self.read_registration_response()?;
934        }
935
936        Ok(())
937    }
938
939    fn read_registration_response(&mut self) -> Result<u16, JlinkError> {
940        const REG_HEADER_SIZE: usize = 8;
941        const REG_MIN_SIZE: usize = 76;
942        const REG_MAX_SIZE: usize = 512;
943
944        let mut response = [0; REG_MAX_SIZE];
945        self.read(&mut response[..REG_MIN_SIZE])?;
946
947        let handle = u16::from_le_bytes([response[0], response[1]]);
948        let num = u16::from_le_bytes([response[2], response[3]]) as usize;
949        let entry_size = u16::from_le_bytes([response[4], response[5]]) as usize;
950        let info_size = u16::from_le_bytes([response[6], response[7]]) as usize;
951
952        let table_size = num * entry_size;
953        let size = REG_HEADER_SIZE + table_size + info_size;
954
955        tracing::debug!("Registration response size: {size}");
956
957        if size > REG_MAX_SIZE {
958            return Err(JlinkError::Other(format!(
959                "Maximum registration size exceeded: {size} bytes",
960            )));
961        }
962
963        if size > REG_MIN_SIZE {
964            // Read the rest of the response.
965            self.read(&mut response[REG_MIN_SIZE..size])?;
966        }
967
968        // TODO: we should process the response, and return the list of connections.
969
970        Ok(handle)
971    }
972}
973
974impl DebugProbe for JLink {
975    fn select_protocol(&mut self, protocol: WireProtocol) -> Result<(), DebugProbeError> {
976        if self.caps.contains(Capability::SelectIf) {
977            let jlink_interface = match protocol {
978                WireProtocol::Swd => Interface::Swd,
979                WireProtocol::Jtag => Interface::Jtag,
980            };
981
982            if !self.interfaces.contains(jlink_interface) {
983                return Err(DebugProbeError::UnsupportedProtocol(protocol));
984            }
985        } else {
986            // Assume JTAG protocol if the probe does not support switching interfaces
987            if protocol != WireProtocol::Jtag {
988                return Err(DebugProbeError::UnsupportedProtocol(protocol));
989            }
990        }
991
992        self.protocol = protocol;
993
994        Ok(())
995    }
996
997    fn active_protocol(&self) -> Option<WireProtocol> {
998        Some(self.protocol)
999    }
1000
1001    fn get_name(&self) -> &'static str {
1002        "J-Link"
1003    }
1004
1005    fn speed_khz(&self) -> u32 {
1006        self.speed_khz
1007    }
1008
1009    fn set_speed(&mut self, speed_khz: u32) -> Result<u32, DebugProbeError> {
1010        if speed_khz == 0 || speed_khz >= 0xffff {
1011            return Err(DebugProbeError::UnsupportedSpeed(speed_khz));
1012        }
1013
1014        if let Ok(speeds) = self.read_interface_speeds() {
1015            tracing::debug!("Supported speeds: {:?}", speeds);
1016
1017            let max_speed_khz = speeds.max_speed_hz() / 1000;
1018
1019            if max_speed_khz < speed_khz {
1020                return Err(DebugProbeError::UnsupportedSpeed(speed_khz));
1021            }
1022        };
1023
1024        if let Some(expected_speed) = SpeedConfig::khz(speed_khz as u16) {
1025            self.set_interface_clock_speed(expected_speed)?;
1026            self.speed_khz = speed_khz;
1027        } else {
1028            return Err(DebugProbeError::UnsupportedSpeed(speed_khz));
1029        }
1030
1031        Ok(speed_khz)
1032    }
1033
1034    fn attach(&mut self) -> Result<(), DebugProbeError> {
1035        tracing::debug!("Attaching to J-Link");
1036
1037        tracing::debug!("Attaching with protocol '{}'", self.protocol);
1038
1039        if self.caps.contains(Capability::SelectIf) {
1040            let jlink_interface = match self.protocol {
1041                WireProtocol::Swd => Interface::Swd,
1042                WireProtocol::Jtag => Interface::Jtag,
1043            };
1044
1045            self.select_interface(jlink_interface)?;
1046        }
1047
1048        // Log some information about the probe
1049        tracing::debug!("J-Link: Capabilities: {:?}", self.caps);
1050        let fw_version = self.read_firmware_version().unwrap_or_else(|_| "?".into());
1051        tracing::info!("J-Link: Firmware version: {}", fw_version);
1052        match self.read_hardware_version() {
1053            Ok(hw_version) => tracing::info!("J-Link: Hardware version: {}", hw_version),
1054            Err(_) => tracing::info!("J-Link: Hardware version: ?"),
1055        };
1056
1057        // Check and report the target voltage.
1058        let target_voltage = self.get_target_voltage()?.expect("The J-Link returned None when it should only be able to return Some(f32) or an error. Please report this bug!");
1059        if target_voltage < crate::probe::LOW_TARGET_VOLTAGE_WARNING_THRESHOLD {
1060            tracing::warn!(
1061                "J-Link: Target voltage (VTref) is {:2.2} V. Is your target device powered?",
1062                target_voltage
1063            );
1064        } else {
1065            tracing::info!("J-Link: Target voltage: {:2.2} V", target_voltage);
1066        }
1067
1068        match self.protocol {
1069            WireProtocol::Jtag => {
1070                // try some JTAG stuff
1071
1072                tracing::debug!("Resetting JTAG chain using trst");
1073                self.reset_trst()?;
1074
1075                self.select_target(0)?;
1076            }
1077            WireProtocol::Swd => {
1078                // Attaching is handled in sequence
1079
1080                // We are ready to debug.
1081            }
1082        }
1083
1084        self.write_cmd(&[Command::HwReset1 as u8])?;
1085        self.write_cmd(&[Command::HwTrst1 as u8])?;
1086
1087        // Set a default speed if not already set
1088        if self.speed_khz == 0 {
1089            self.set_speed(400)?;
1090        }
1091
1092        tracing::debug!("Attached successfully");
1093
1094        Ok(())
1095    }
1096
1097    fn detach(&mut self) -> Result<(), crate::Error> {
1098        Ok(())
1099    }
1100
1101    fn target_reset(&mut self) -> Result<(), DebugProbeError> {
1102        self.write_cmd(&[Command::ResetTarget as u8])?;
1103        Ok(())
1104    }
1105
1106    fn target_reset_assert(&mut self) -> Result<(), DebugProbeError> {
1107        self.set_reset(false)?;
1108        Ok(())
1109    }
1110
1111    fn target_reset_deassert(&mut self) -> Result<(), DebugProbeError> {
1112        self.set_reset(true)?;
1113        Ok(())
1114    }
1115
1116    fn try_as_jtag_probe(&mut self) -> Option<&mut dyn JtagAccess> {
1117        Some(self)
1118    }
1119
1120    fn try_get_riscv_interface_builder<'probe>(
1121        &'probe mut self,
1122    ) -> Result<Box<dyn RiscvInterfaceBuilder<'probe> + 'probe>, RiscvError> {
1123        if self.supported_protocols.contains(&WireProtocol::Jtag) {
1124            self.select_protocol(WireProtocol::Jtag)?;
1125            Ok(Box::new(JtagDtmBuilder::new(self)))
1126        } else {
1127            Err(DebugProbeError::InterfaceNotAvailable {
1128                interface_name: "JTAG",
1129            }
1130            .into())
1131        }
1132    }
1133
1134    fn get_swo_interface(&self) -> Option<&dyn SwoAccess> {
1135        Some(self as _)
1136    }
1137
1138    fn get_swo_interface_mut(&mut self) -> Option<&mut dyn SwoAccess> {
1139        Some(self as _)
1140    }
1141
1142    fn has_arm_interface(&self) -> bool {
1143        true
1144    }
1145
1146    fn has_riscv_interface(&self) -> bool {
1147        self.supported_protocols.contains(&WireProtocol::Jtag)
1148    }
1149
1150    fn into_probe(self: Box<Self>) -> Box<dyn DebugProbe> {
1151        self
1152    }
1153
1154    fn try_as_dap_probe(&mut self) -> Option<&mut dyn DapProbe> {
1155        Some(self)
1156    }
1157
1158    fn try_get_arm_debug_interface<'probe>(
1159        self: Box<Self>,
1160        sequence: Arc<dyn ArmDebugSequence>,
1161    ) -> Result<Box<dyn ArmDebugInterface + 'probe>, (Box<dyn DebugProbe>, ArmError)> {
1162        Ok(ArmCommunicationInterface::create(self, sequence, true))
1163    }
1164
1165    fn get_target_voltage(&mut self) -> Result<Option<f32>, DebugProbeError> {
1166        // Convert the integer millivolts value from self.handle to volts as an f32.
1167        Ok(Some((self.read_target_voltage()? as f32) / 1000f32))
1168    }
1169
1170    fn try_get_xtensa_interface<'probe>(
1171        &'probe mut self,
1172        state: &'probe mut XtensaDebugInterfaceState,
1173    ) -> Result<XtensaCommunicationInterface<'probe>, XtensaError> {
1174        if self.supported_protocols.contains(&WireProtocol::Jtag) {
1175            self.select_protocol(WireProtocol::Jtag)?;
1176            Ok(XtensaCommunicationInterface::new(self, state))
1177        } else {
1178            Err(DebugProbeError::InterfaceNotAvailable {
1179                interface_name: "JTAG",
1180            }
1181            .into())
1182        }
1183    }
1184
1185    fn has_xtensa_interface(&self) -> bool {
1186        self.supported_protocols.contains(&WireProtocol::Jtag)
1187    }
1188}
1189
1190impl RawSwdIo for JLink {
1191    fn swd_io<S>(&mut self, swdio: S) -> Result<Vec<bool>, DebugProbeError>
1192    where
1193        S: IntoIterator<Item = IoSequenceItem>,
1194    {
1195        self.perform_swdio_transfer(swdio)
1196    }
1197
1198    fn swj_pins(
1199        &mut self,
1200        pin_out: u32,
1201        pin_select: u32,
1202        pin_wait: u32,
1203    ) -> Result<u32, DebugProbeError> {
1204        let mut unsupported_pins = Pins(0);
1205        unsupported_pins.set_ntrst(true);
1206        unsupported_pins.set_tdi(true);
1207        unsupported_pins.set_tdo(true);
1208        let unsupported_pins_mask = unsupported_pins.0 as u32;
1209
1210        // Only RESET, TCK and TMS are supported at the moment
1211        if pin_select & unsupported_pins_mask == 0 {
1212            let pin_select = Pins(pin_select as u8);
1213            let pin_out = Pins(pin_out as u8);
1214
1215            if pin_select.swclk_tck() {
1216                self.set_tck(pin_out.swclk_tck())?;
1217            }
1218
1219            if pin_select.swdio_tms() {
1220                self.set_tms(pin_out.swdio_tms())?;
1221            }
1222
1223            // Set reset as last of the pins as some chips might sample tms and tck on release of reset
1224            if pin_select.nreset() {
1225                if pin_out.nreset() {
1226                    self.target_reset_deassert()?;
1227                } else {
1228                    self.target_reset_assert()?;
1229                }
1230            }
1231
1232            // Normally this would be the timeout we pass to the probe to settle the pins.
1233            // The J-Link is not capable of this, so we just wait for this time on the host
1234            // and assume it has settled until then.
1235            std::thread::sleep(Duration::from_micros(pin_wait as u64));
1236
1237            // We signal that we cannot read the pin state.
1238            Ok(0xFFFF_FFFF)
1239        } else {
1240            // This is not supported for J-Links, unfortunately.
1241            Err(DebugProbeError::CommandNotSupportedByProbe {
1242                command_name: "swj_pins",
1243            })
1244        }
1245    }
1246
1247    fn swd_settings(&self) -> &SwdSettings {
1248        &self.swd_settings
1249    }
1250}
1251
1252impl RawJtagIo for JLink {
1253    fn state_mut(&mut self) -> &mut JtagDriverState {
1254        &mut self.jtag_state
1255    }
1256
1257    fn state(&self) -> &JtagDriverState {
1258        &self.jtag_state
1259    }
1260
1261    fn shift_bit(&mut self, tms: bool, tdi: bool, capture: bool) -> Result<(), DebugProbeError> {
1262        self.shift_jtag_bit(tms, tdi, capture)
1263    }
1264
1265    fn read_captured_bits(&mut self) -> Result<BitVec, DebugProbeError> {
1266        self.read_captured_bits()
1267    }
1268}
1269
1270impl AutoImplementJtagAccess for JLink {}
1271impl DapProbe for JLink {}
1272
1273impl SwoAccess for JLink {
1274    fn enable_swo(&mut self, config: &SwoConfig) -> Result<(), ArmError> {
1275        self.swo_config = Some(*config);
1276        self.swo_start(SwoMode::Uart, config.baud(), SWO_BUFFER_SIZE.into())
1277            .map_err(DebugProbeError::from)?;
1278        Ok(())
1279    }
1280
1281    fn disable_swo(&mut self) -> Result<(), ArmError> {
1282        self.swo_config = None;
1283        self.swo_stop().map_err(DebugProbeError::from)?;
1284        Ok(())
1285    }
1286
1287    fn swo_buffer_size(&mut self) -> Option<usize> {
1288        Some(SWO_BUFFER_SIZE.into())
1289    }
1290
1291    fn read_swo_timeout(&mut self, timeout: Duration) -> Result<Vec<u8>, ArmError> {
1292        let start = Instant::now();
1293        let mut buf = vec![0; SWO_BUFFER_SIZE.into()];
1294
1295        let poll_interval = self
1296            .swo_poll_interval_hint(&self.swo_config.unwrap())
1297            .unwrap();
1298
1299        let mut bytes = vec![];
1300        loop {
1301            let data = self.swo_read(&mut buf).map_err(DebugProbeError::from)?;
1302            bytes.extend(data.as_ref());
1303            if start.elapsed() > timeout {
1304                break;
1305            }
1306            std::thread::sleep(poll_interval);
1307        }
1308        Ok(bytes)
1309    }
1310}
1311
1312#[tracing::instrument]
1313fn list_jlink_devices() -> Vec<ProbeListItem> {
1314    let devices = match nusb::list_devices().wait() {
1315        Ok(devices) => devices,
1316        Err(e) => {
1317            tracing::warn!("error listing J-Link devices: {e}");
1318            return vec![];
1319        }
1320    };
1321
1322    devices
1323        .filter(is_jlink)
1324        .map(|info| {
1325            let debug_probe_info = DebugProbeInfo::new(
1326                info.product_string().unwrap_or("J-Link").to_string(),
1327                info.vendor_id(),
1328                info.product_id(),
1329                info.serial_number().map(|s| s.to_string()),
1330                &JLinkFactory,
1331                None,
1332                false,
1333            );
1334            ProbeListItem {
1335                info: debug_probe_info,
1336                accessibility: usb_probe_accessibility(&info),
1337            }
1338        })
1339        .collect()
1340}
1341
1342impl TryFrom<Interface> for WireProtocol {
1343    type Error = JlinkError;
1344
1345    fn try_from(interface: Interface) -> Result<Self, Self::Error> {
1346        match interface {
1347            Interface::Jtag => Ok(WireProtocol::Jtag),
1348            Interface::Swd => Ok(WireProtocol::Swd),
1349            unknown_interface => Err(JlinkError::UnknownInterface(unknown_interface)),
1350        }
1351    }
1352}
1353
1354/// A hardware version returned by [`JLink::read_hardware_version`].
1355///
1356/// Note that the reported hardware version does not allow reliable feature detection, since
1357/// embedded J-Link probes might return a hardware version of 1.0.0 despite supporting SWD and other
1358/// much newer features.
1359#[derive(Debug)]
1360struct HardwareVersion(u32);
1361
1362impl HardwareVersion {
1363    fn from_u32(raw: u32) -> Self {
1364        HardwareVersion(raw)
1365    }
1366
1367    /// Returns the type of hardware (or `None` if the hardware type is unknown).
1368    fn hardware_type(&self) -> Option<HardwareType> {
1369        Some(match (self.0 / 1000000) % 100 {
1370            0 => HardwareType::JLink,
1371            1 => HardwareType::JTrace,
1372            2 => HardwareType::Flasher,
1373            3 => HardwareType::JLinkPro,
1374            _ => return None,
1375        })
1376    }
1377
1378    /// The major version.
1379    fn major(&self) -> u8 {
1380        // Decimal coded Decimal, cool cool
1381        (self.0 / 10000) as u8
1382    }
1383
1384    /// The minor version.
1385    fn minor(&self) -> u8 {
1386        ((self.0 % 10000) / 100) as u8
1387    }
1388
1389    /// The hardware revision.
1390    fn revision(&self) -> u8 {
1391        (self.0 % 100) as u8
1392    }
1393}
1394
1395impl fmt::Display for HardwareVersion {
1396    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1397        if let Some(hw) = self.hardware_type() {
1398            write!(f, "{hw} ")?;
1399        }
1400        write!(f, "{}.{}.{}", self.major(), self.minor(), self.revision())
1401    }
1402}
1403
1404/// The hardware/product type of the device.
1405#[non_exhaustive]
1406#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1407enum HardwareType {
1408    JLink,
1409    JTrace,
1410    Flasher,
1411    JLinkPro,
1412}
1413
1414impl fmt::Display for HardwareType {
1415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1416        f.write_str(match self {
1417            HardwareType::JLink => "J-Link",
1418            HardwareType::JTrace => "J-Trace",
1419            HardwareType::Flasher => "J-Flash",
1420            HardwareType::JLinkPro => "J-Link Pro",
1421        })
1422    }
1423}
1424
1425const VID_SEGGER: u16 = 0x1366;
1426
1427// Information about the product IDs is taken from the udev rules file provided by Segger.
1428
1429/// Product IDS for J-Link devices using the old format
1430///
1431/// 0x106 is not included on purpose, it is supposed to be serial (CDC) only
1432const LEGACY_JLINK_IDS: &[u16] = &[0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0107, 0x108];
1433
1434/// Flag indicating that the product ID is a J-Link product ID, using the new format
1435const PID_JLINK_FLAG: u16 = 0x1000;
1436
1437/// Indicates that the J-Link is using the (legacy) Segger driver.
1438const PID_JLINK_SEGGER_DRV_FLAG: u16 = 1 << 4;
1439
1440/// Indicates that the J-Link is using the WinUSB driver.
1441const PID_JLINK_WINUSB_DRV_FLAG: u16 = 1 << 5;
1442
1443fn is_jlink_product_id(product_id: u16) -> bool {
1444    let old_product_id = LEGACY_JLINK_IDS.contains(&product_id);
1445
1446    let new_product_id = (product_id & PID_JLINK_FLAG) != 0;
1447
1448    let jlink_using_segger_driver = (product_id & PID_JLINK_SEGGER_DRV_FLAG) != 0;
1449
1450    let jlink_using_winusb_driver = (product_id & PID_JLINK_WINUSB_DRV_FLAG) != 0;
1451
1452    old_product_id || (new_product_id && (jlink_using_segger_driver || jlink_using_winusb_driver))
1453}
1454
1455fn is_jlink(info: &DeviceInfo) -> bool {
1456    let matching_vendor_id = info.vendor_id() == VID_SEGGER;
1457
1458    matching_vendor_id && is_jlink_product_id(info.product_id())
1459}
1460
1461#[cfg(test)]
1462mod test {
1463    #[test]
1464    fn jlink_pid_cmsisdap() {
1465        // J-Link devices configured as CMSIS-DAP should not be detected as J-Link devices.
1466        assert!(!super::is_jlink_product_id(0x1008));
1467    }
1468
1469    #[test]
1470    fn jlink_pid_segger_driver() {
1471        // J-Link device using the legacy Segger driver.
1472        assert!(super::is_jlink_product_id(0x1059));
1473    }
1474}