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, ProbeStatistics, RawJtagIo, RawSwdIo, SwdSettings,
48        WireProtocol,
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            probe_statistics: ProbeStatistics::default(),
196            jtag_state: JtagDriverState::default(),
197
198            jtag_tms_bits: vec![],
199            jtag_tdi_bits: vec![],
200            jtag_capture_tdo: vec![],
201            jtag_response: BitVec::new(),
202
203            max_mem_block_size: 0, // dummy value
204            jtag_chunk_size: 0,    // dummy value
205
206            config: JlinkConfig::default(),
207        };
208        this.fill_capabilities()?;
209        this.fill_interfaces()?;
210
211        this.supported_protocols = if this.caps.contains(Capability::SelectIf) {
212            this.interfaces
213                .into_iter()
214                .filter_map(|p| match WireProtocol::try_from(p) {
215                    Ok(protocol) => Some(protocol),
216                    Err(JlinkError::UnknownInterface(interface)) => {
217                        // We ignore unknown protocols.
218                        tracing::debug!(
219                            "J-Link returned interface {:?}, which is not supported by probe-rs.",
220                            interface
221                        );
222                        None
223                    }
224                    Err(_) => None,
225                })
226                .collect::<Vec<_>>()
227        } else {
228            // The J-Link cannot report which interfaces it supports, and cannot
229            // switch interfaces. We assume it just supports JTAG.
230            vec![WireProtocol::Jtag]
231        };
232
233        this.protocol = if this.supported_protocols.contains(&WireProtocol::Swd) {
234            // Default to SWD if supported, since it's the most commonly used.
235            WireProtocol::Swd
236        } else {
237            // Otherwise just pick the first supported.
238            *this.supported_protocols.first().unwrap()
239        };
240
241        if this.caps.contains(Capability::GetMaxBlockSize) {
242            this.max_mem_block_size = this.read_max_mem_block()? as usize;
243
244            tracing::debug!(
245                "J-Link max mem block size for SWD IO: {} byte",
246                this.max_mem_block_size
247            );
248        } else {
249            tracing::debug!(
250                "J-Link does not support GET_MAX_MEM_BLOCK, using default value of 65535"
251            );
252            this.max_mem_block_size = 65535;
253        }
254
255        // Some devices can't handle large transfers, so we limit the chunk size.
256        // While it would be nice to read this directly from the device,
257        // `read_max_mem_block`'s return value does not directly correspond to the
258        // maximum transfer size when performing JTAG IO, and it's not clear how to get the actual value.
259        // The number of *bits* is encoded as a u16, so the maximum value is 65535
260        this.jtag_chunk_size = match selector.product_id {
261            // 0x0101: J-Link EDU
262            0x0101 => 65535,
263            // 0x1051: J-Link OB-K22-SiFive: 504 bits
264            0x1051 => 504,
265            // Assume the lowest value is a safe default
266            _ => 504,
267        };
268        this.config = this.read_device_config()?;
269        this.connection_handle = if requires_connection_handle(selector) {
270            Some(this.register_connection()?)
271        } else {
272            None
273        };
274
275        Ok(Box::new(this))
276    }
277
278    fn list_probes(&self) -> Vec<DebugProbeInfo> {
279        list_jlink_devices()
280    }
281}
282
283fn requires_connection_handle(selector: &DebugProbeSelector) -> bool {
284    // These devices require a connection handle to be registered before they can be used.
285    // As some other devices can't handle the registration command, we only enable it for known
286    // devices.
287    let devices = [
288        (0x1366, 0x0101, Some("000000123456")), // Blue J-Link PRO clone
289    ];
290
291    devices.contains(&(
292        selector.vendor_id,
293        selector.product_id,
294        selector.serial_number.as_deref(),
295    ))
296}
297
298impl Drop for JLink {
299    fn drop(&mut self) {
300        self.unregister_connection().ok();
301    }
302}
303
304#[repr(u8)]
305#[expect(dead_code)]
306enum Command {
307    Version = 0x01,
308    Register = 0x09,
309    GetSpeeds = 0xC0,
310    GetMaxMemBlock = 0xD4,
311    GetCaps = 0xE8,
312    GetCapsEx = 0xED,
313    GetHwVersion = 0xF0,
314
315    GetState = 0x07,
316    GetHwInfo = 0xC1,
317    GetCounters = 0xC2,
318    MeasureRtckReact = 0xF6,
319
320    ResetTrst = 0x02,
321    SetSpeed = 0x05,
322    SelectIf = 0xC7,
323    SetKsPower = 0x08,
324    HwClock = 0xC8,
325    HwTms0 = 0xC9,
326    HwTms1 = 0xCA,
327    HwData0 = 0xCB,
328    HwData1 = 0xCC,
329    HwJtag = 0xCD,
330    HwJtag2 = 0xCE,
331    HwJtag3 = 0xCF,
332    HwJtagWrite = 0xD5,
333    HwJtagGetResult = 0xD6,
334    HwTrst0 = 0xDE,
335    HwTrst1 = 0xDF,
336    Swo = 0xEB,
337    WriteDcc = 0xF1,
338
339    ResetTarget = 0x03,
340    HwReleaseResetStopEx = 0xD0,
341    HwReleaseResetStopTimed = 0xD1,
342    HwReset0 = 0xDC,
343    HwReset1 = 0xDD,
344    GetCpuCaps = 0xE9,
345    ExecCpuCmd = 0xEA,
346    WriteMem = 0xF4,
347    ReadMem = 0xF5,
348    WriteMemArm79 = 0xF7,
349    ReadMemArm79 = 0xF8,
350
351    ReadConfig = 0xF2,
352    WriteConfig = 0xF3,
353}
354
355/// A J-Link probe.
356pub struct JLink {
357    handle: nusb::Interface,
358
359    read_ep: u8,
360    write_ep: u8,
361    max_read_ep_packet: usize,
362
363    /// The capabilities reported by the device. They're fetched once, when the device is opened.
364    caps: Capabilities,
365
366    /// The supported interfaces. Like `caps`, this is fetched once when opening the device.
367    interfaces: Interfaces,
368
369    /// The currently selected target interface. This is stored here to avoid unnecessary roundtrips
370    /// when performing target I/O operations.
371    interface: Interface,
372
373    /// Device configuration, fetched once when the device is opened.
374    config: JlinkConfig,
375    connection_handle: Option<u16>,
376
377    swo_config: Option<SwoConfig>,
378
379    /// Protocols supported by the probe.
380    supported_protocols: Vec<WireProtocol>,
381    /// Protocol chosen by the user
382    protocol: WireProtocol,
383
384    speed_khz: u32,
385
386    jtag_tms_bits: Vec<bool>,
387    jtag_tdi_bits: Vec<bool>,
388    jtag_capture_tdo: Vec<bool>,
389    jtag_response: BitVec,
390    jtag_state: JtagDriverState,
391
392    /// max number of bits in a transfer chunk, when using JTAG
393    jtag_chunk_size: usize,
394
395    /// Maximum memory block size, as report by the `GET_MAX_MEM_BLOCK` command.
396    ///
397    /// Used to determine maximum transfer length for SWD IO.
398    max_mem_block_size: usize,
399
400    probe_statistics: ProbeStatistics,
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    /// Resets the target's JTAG TAP controller by temporarily asserting (n)TRST (Pin 3).
668    ///
669    /// This might not do anything if the pin is not connected to the target. It does not affect
670    /// non-JTAG target interfaces.
671    fn reset_trst(&mut self) -> Result<(), JlinkError> {
672        self.write_cmd(&[Command::ResetTrst as u8])
673    }
674
675    /// Reads the target voltage measured on the `VTref` pin, in millivolts.
676    ///
677    /// In order to use the J-Link, this voltage must be present, since it will be used as the level
678    /// of the I/O signals to the target.
679    fn read_target_voltage(&self) -> Result<u16, JlinkError> {
680        self.write_cmd(&[Command::GetState as u8])?;
681
682        let buf = self.read_n::<8>()?;
683
684        let voltage = [buf[0], buf[1]];
685        Ok(u16::from_le_bytes(voltage))
686    }
687
688    fn shift_jtag_bit(
689        &mut self,
690        tms: bool,
691        tdi: bool,
692        capture: bool,
693    ) -> Result<(), DebugProbeError> {
694        self.jtag_state.state.update(tms);
695
696        self.jtag_tms_bits.push(tms);
697        self.jtag_tdi_bits.push(tdi);
698        self.jtag_capture_tdo.push(capture);
699
700        if self.jtag_tms_bits.len() >= self.jtag_chunk_size {
701            self.flush_jtag()?;
702        }
703
704        Ok(())
705    }
706
707    fn flush_jtag(&mut self) -> Result<(), JlinkError> {
708        if self.jtag_tms_bits.is_empty() {
709            return Ok(());
710        }
711
712        self.require_interface_selected(Interface::Jtag)?;
713
714        let mut has_status_byte = false;
715        // There's 3 commands for doing a JTAG transfer. The older 2 are obsolete with hardware
716        // version 5 and above, which adds the 3rd command. Unfortunately we cannot reliably use the
717        // HW version to determine this since some embedded J-Link probes have a HW version of
718        // 1.0.0, but still support SWD, so we use the `SELECT_IF` capability instead.
719        let cmd = if self.caps.contains(Capability::SelectIf) {
720            // Use the new JTAG3 command, make sure to select the JTAG interface mode
721            has_status_byte = true;
722            Command::HwJtag3
723        } else {
724            // Use the legacy JTAG2 command
725            // FIXME is HW_JTAG relevant at all?
726            Command::HwJtag2
727        };
728
729        let tms_bit_count = self.jtag_tms_bits.len();
730        let tdi_bit_count = self.jtag_tdi_bits.len();
731        assert_eq!(
732            tms_bit_count, tdi_bit_count,
733            "TMS and TDI must have the same number of bits"
734        );
735        let capacity = 1 + 1 + 2 + tms_bit_count.div_ceil(8) * 2;
736        let mut buf = Vec::with_capacity(capacity);
737        buf.resize(4, 0);
738        buf[0] = cmd as u8;
739        // JTAG3 and JTAG2 use the same format for JTAG operations
740        // buf[1] is dummy data for alignment
741        // buf[2..=3] is the bit count
742        let bit_count = u16::try_from(tms_bit_count).expect("too much data to transfer");
743        buf[2..=3].copy_from_slice(&bit_count.to_le_bytes());
744        buf.extend(self.jtag_tms_bits.drain(..).collapse_bytes());
745        buf.extend(self.jtag_tdi_bits.drain(..).collapse_bytes());
746
747        self.write_cmd(&buf)?;
748
749        // Round bit count up to multple of 8 to get the number of response bytes.
750        let num_resp_bytes = tms_bit_count.div_ceil(8);
751        tracing::trace!(
752            "{} TMS/TDI bits sent; reading {} response bytes",
753            tms_bit_count,
754            num_resp_bytes
755        );
756
757        // Response is `num_resp_bytes` TDO data bytes and one status byte,
758        // if the JTAG3 command is used.
759        let mut read_len = num_resp_bytes;
760
761        if has_status_byte {
762            read_len += 1;
763        }
764
765        self.read(&mut buf[..read_len])?;
766
767        // Check the status if a JTAG3 command was used.
768        if has_status_byte && buf[read_len - 1] != 0 {
769            return Err(JlinkError::Other(format!(
770                "probe I/O command returned error code {:#x}",
771                buf[read_len - 1]
772            )));
773        }
774
775        let response = BitIter::new(&buf[..num_resp_bytes], tms_bit_count);
776
777        for (bit, capture) in response.zip(self.jtag_capture_tdo.drain(..)) {
778            if capture {
779                self.jtag_response.push(bit);
780            }
781        }
782
783        Ok(())
784    }
785
786    fn read_captured_bits(&mut self) -> Result<BitVec, DebugProbeError> {
787        self.flush_jtag()?;
788
789        Ok(std::mem::take(&mut self.jtag_response))
790    }
791
792    /// Perform a single SWDIO command
793    ///
794    /// The caller needs to ensure that the given iterators are not longer than the maximum transfer size
795    /// allowed. It seems that the maximum transfer size is determined by [`JLink::max_mem_block_size`].
796    fn perform_swdio_transfer<S>(&self, swdio: S) -> Result<Vec<bool>, DebugProbeError>
797    where
798        S: IntoIterator<Item = IoSequenceItem>,
799    {
800        self.require_interface_selected(Interface::Swd)?;
801
802        const COMMAND_OVERHEAD: usize = 4;
803
804        let max_bits = (self.max_mem_block_size - COMMAND_OVERHEAD) / 2 * 8;
805        let max_bits = std::cmp::min(max_bits, 65535);
806
807        let swdio_chunks = swdio.into_iter().chunks(max_bits);
808
809        let mut output = Vec::new();
810        let mut buf = Vec::with_capacity(self.max_mem_block_size);
811        buf.resize(COMMAND_OVERHEAD, 0);
812
813        for swdio in swdio_chunks.into_iter() {
814            buf.truncate(COMMAND_OVERHEAD);
815
816            const INPUT: bool = false;
817            const OUTPUT: bool = true;
818
819            let swdio: Vec<_> = swdio.collect();
820
821            //  Extend using the direction bits:
822            buf.extend(
823                swdio
824                    .iter()
825                    .map(|item| match item {
826                        IoSequenceItem::Input => INPUT,
827                        IoSequenceItem::Output { .. } => OUTPUT,
828                    })
829                    .collapse_bytes(),
830            );
831            //  Extend using the value bits:
832            buf.extend(
833                swdio
834                    .iter()
835                    .map(|item| match item {
836                        IoSequenceItem::Input => false,
837                        IoSequenceItem::Output(x) => *x,
838                    })
839                    .collapse_bytes(),
840            );
841
842            let num_bits = swdio.len() as u16;
843
844            buf[0] = Command::HwJtag3 as u8;
845            // buf[1] is dummy data for alignment
846            buf[1] = 0;
847            buf[2..=3].copy_from_slice(&num_bits.to_le_bytes());
848            let num_bytes = usize::from(num_bits.div_ceil(8));
849
850            tracing::trace!("Buffer length for j-link transfer: {}", buf.len());
851
852            self.write_cmd(&buf)?;
853
854            // Response is `num_bytes` SWDIO data bytes and one status byte
855            self.read(&mut buf[..num_bytes + 1])?;
856
857            if buf[num_bytes] != 0 {
858                return Err(JlinkError::Other(format!(
859                    "probe I/O command returned error code {:#x}",
860                    buf[num_bytes]
861                ))
862                .into());
863            }
864
865            output.extend(BitIter::new(&buf[..num_bytes], num_bits as usize));
866        }
867
868        Ok(output)
869    }
870
871    /// Enable/Disable the Target Power Supply of the probe.
872    ///
873    /// This is not available on all J-Links.
874    pub fn set_kickstart_power(&mut self, enable: bool) -> Result<(), JlinkError> {
875        self.require_capability(Capability::SetKsPower)?;
876        self.write_cmd(&[Command::SetKsPower as u8, if enable { 1 } else { 0 }])
877    }
878
879    fn register_connection(&mut self) -> Result<u16, JlinkError> {
880        if !self.caps.contains(Capability::Register) {
881            return Ok(0);
882        }
883
884        // Undocumented, taken from OpenOCD/libjaylink
885        let mut buf = vec![Command::Register as u8, 0x64];
886        buf.extend(JlinkConnection::usb(0).into_bytes());
887        self.write_cmd(&buf)?;
888
889        let handle = self.read_registration_response()?;
890
891        if handle == 0 {
892            return Err(JlinkError::Other("Invalid registration handle".to_string()));
893        }
894
895        Ok(handle)
896    }
897
898    fn unregister_connection(&mut self) -> Result<(), JlinkError> {
899        if !self.caps.contains(Capability::Register) {
900            return Ok(());
901        }
902
903        if let Some(handle) = self.connection_handle.take() {
904            let mut buf = vec![Command::Register as u8, 0x65];
905            buf.extend(JlinkConnection::usb(handle).into_bytes());
906            self.write_cmd(&buf)?;
907            self.read_registration_response()?;
908        }
909
910        Ok(())
911    }
912
913    fn read_registration_response(&mut self) -> Result<u16, JlinkError> {
914        const REG_HEADER_SIZE: usize = 8;
915        const REG_MIN_SIZE: usize = 76;
916        const REG_MAX_SIZE: usize = 512;
917
918        let mut response = [0; REG_MAX_SIZE];
919        self.read(&mut response[..REG_MIN_SIZE])?;
920
921        let handle = u16::from_le_bytes([response[0], response[1]]);
922        let num = u16::from_le_bytes([response[2], response[3]]) as usize;
923        let entry_size = u16::from_le_bytes([response[4], response[5]]) as usize;
924        let info_size = u16::from_le_bytes([response[6], response[7]]) as usize;
925
926        let table_size = num * entry_size;
927        let size = REG_HEADER_SIZE + table_size + info_size;
928
929        tracing::debug!("Registration response size: {size}");
930
931        if size > REG_MAX_SIZE {
932            return Err(JlinkError::Other(format!(
933                "Maximum registration size exceeded: {size} bytes",
934            )));
935        }
936
937        if size > REG_MIN_SIZE {
938            // Read the rest of the response.
939            self.read(&mut response[REG_MIN_SIZE..size])?;
940        }
941
942        // TODO: we should process the response, and return the list of connections.
943
944        Ok(handle)
945    }
946}
947
948impl DebugProbe for JLink {
949    fn select_protocol(&mut self, protocol: WireProtocol) -> Result<(), DebugProbeError> {
950        if self.caps.contains(Capability::SelectIf) {
951            let jlink_interface = match protocol {
952                WireProtocol::Swd => Interface::Swd,
953                WireProtocol::Jtag => Interface::Jtag,
954            };
955
956            if !self.interfaces.contains(jlink_interface) {
957                return Err(DebugProbeError::UnsupportedProtocol(protocol));
958            }
959        } else {
960            // Assume JTAG protocol if the probe does not support switching interfaces
961            if protocol != WireProtocol::Jtag {
962                return Err(DebugProbeError::UnsupportedProtocol(protocol));
963            }
964        }
965
966        self.protocol = protocol;
967
968        Ok(())
969    }
970
971    fn active_protocol(&self) -> Option<WireProtocol> {
972        Some(self.protocol)
973    }
974
975    fn get_name(&self) -> &'static str {
976        "J-Link"
977    }
978
979    fn speed_khz(&self) -> u32 {
980        self.speed_khz
981    }
982
983    fn set_speed(&mut self, speed_khz: u32) -> Result<u32, DebugProbeError> {
984        if speed_khz == 0 || speed_khz >= 0xffff {
985            return Err(DebugProbeError::UnsupportedSpeed(speed_khz));
986        }
987
988        if let Ok(speeds) = self.read_interface_speeds() {
989            tracing::debug!("Supported speeds: {:?}", speeds);
990
991            let max_speed_khz = speeds.max_speed_hz() / 1000;
992
993            if max_speed_khz < speed_khz {
994                return Err(DebugProbeError::UnsupportedSpeed(speed_khz));
995            }
996        };
997
998        if let Some(expected_speed) = SpeedConfig::khz(speed_khz as u16) {
999            self.set_interface_clock_speed(expected_speed)?;
1000            self.speed_khz = speed_khz;
1001        } else {
1002            return Err(DebugProbeError::UnsupportedSpeed(speed_khz));
1003        }
1004
1005        Ok(speed_khz)
1006    }
1007
1008    fn attach(&mut self) -> Result<(), DebugProbeError> {
1009        tracing::debug!("Attaching to J-Link");
1010
1011        tracing::debug!("Attaching with protocol '{}'", self.protocol);
1012
1013        if self.caps.contains(Capability::SelectIf) {
1014            let jlink_interface = match self.protocol {
1015                WireProtocol::Swd => Interface::Swd,
1016                WireProtocol::Jtag => Interface::Jtag,
1017            };
1018
1019            self.select_interface(jlink_interface)?;
1020        }
1021
1022        // Log some information about the probe
1023        tracing::debug!("J-Link: Capabilities: {:?}", self.caps);
1024        let fw_version = self.read_firmware_version().unwrap_or_else(|_| "?".into());
1025        tracing::info!("J-Link: Firmware version: {}", fw_version);
1026        match self.read_hardware_version() {
1027            Ok(hw_version) => tracing::info!("J-Link: Hardware version: {}", hw_version),
1028            Err(_) => tracing::info!("J-Link: Hardware version: ?"),
1029        };
1030
1031        // Check and report the target voltage.
1032        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!");
1033        if target_voltage < crate::probe::LOW_TARGET_VOLTAGE_WARNING_THRESHOLD {
1034            tracing::warn!(
1035                "J-Link: Target voltage (VTref) is {:2.2} V. Is your target device powered?",
1036                target_voltage
1037            );
1038        } else {
1039            tracing::info!("J-Link: Target voltage: {:2.2} V", target_voltage);
1040        }
1041
1042        match self.protocol {
1043            WireProtocol::Jtag => {
1044                // try some JTAG stuff
1045
1046                tracing::debug!("Resetting JTAG chain using trst");
1047                self.reset_trst()?;
1048
1049                self.select_target(0)?;
1050            }
1051            WireProtocol::Swd => {
1052                // Attaching is handled in sequence
1053
1054                // We are ready to debug.
1055            }
1056        }
1057
1058        self.write_cmd(&[Command::HwReset1 as u8])?;
1059        self.write_cmd(&[Command::HwTrst1 as u8])?;
1060
1061        // Set a default speed if not already set
1062        if self.speed_khz == 0 {
1063            self.set_speed(400)?;
1064        }
1065
1066        tracing::debug!("Attached succesfully");
1067
1068        Ok(())
1069    }
1070
1071    fn detach(&mut self) -> Result<(), crate::Error> {
1072        Ok(())
1073    }
1074
1075    fn target_reset(&mut self) -> Result<(), DebugProbeError> {
1076        self.write_cmd(&[Command::ResetTarget as u8])?;
1077        Ok(())
1078    }
1079
1080    fn target_reset_assert(&mut self) -> Result<(), DebugProbeError> {
1081        self.set_reset(false)?;
1082        Ok(())
1083    }
1084
1085    fn target_reset_deassert(&mut self) -> Result<(), DebugProbeError> {
1086        self.set_reset(true)?;
1087        Ok(())
1088    }
1089
1090    fn try_as_jtag_probe(&mut self) -> Option<&mut dyn JtagAccess> {
1091        Some(self)
1092    }
1093
1094    fn try_get_riscv_interface_builder<'probe>(
1095        &'probe mut self,
1096    ) -> Result<Box<dyn RiscvInterfaceBuilder<'probe> + 'probe>, RiscvError> {
1097        if self.supported_protocols.contains(&WireProtocol::Jtag) {
1098            self.select_protocol(WireProtocol::Jtag)?;
1099            Ok(Box::new(JtagDtmBuilder::new(self)))
1100        } else {
1101            Err(DebugProbeError::InterfaceNotAvailable {
1102                interface_name: "JTAG",
1103            }
1104            .into())
1105        }
1106    }
1107
1108    fn get_swo_interface(&self) -> Option<&dyn SwoAccess> {
1109        Some(self as _)
1110    }
1111
1112    fn get_swo_interface_mut(&mut self) -> Option<&mut dyn SwoAccess> {
1113        Some(self as _)
1114    }
1115
1116    fn has_arm_interface(&self) -> bool {
1117        true
1118    }
1119
1120    fn has_riscv_interface(&self) -> bool {
1121        self.supported_protocols.contains(&WireProtocol::Jtag)
1122    }
1123
1124    fn into_probe(self: Box<Self>) -> Box<dyn DebugProbe> {
1125        self
1126    }
1127
1128    fn try_as_dap_probe(&mut self) -> Option<&mut dyn DapProbe> {
1129        Some(self)
1130    }
1131
1132    fn try_get_arm_debug_interface<'probe>(
1133        self: Box<Self>,
1134        sequence: Arc<dyn ArmDebugSequence>,
1135    ) -> Result<Box<dyn ArmDebugInterface + 'probe>, (Box<dyn DebugProbe>, ArmError)> {
1136        Ok(ArmCommunicationInterface::create(self, sequence, true))
1137    }
1138
1139    fn get_target_voltage(&mut self) -> Result<Option<f32>, DebugProbeError> {
1140        // Convert the integer millivolts value from self.handle to volts as an f32.
1141        Ok(Some((self.read_target_voltage()? as f32) / 1000f32))
1142    }
1143
1144    fn try_get_xtensa_interface<'probe>(
1145        &'probe mut self,
1146        state: &'probe mut XtensaDebugInterfaceState,
1147    ) -> Result<XtensaCommunicationInterface<'probe>, XtensaError> {
1148        if self.supported_protocols.contains(&WireProtocol::Jtag) {
1149            self.select_protocol(WireProtocol::Jtag)?;
1150            Ok(XtensaCommunicationInterface::new(self, state))
1151        } else {
1152            Err(DebugProbeError::InterfaceNotAvailable {
1153                interface_name: "JTAG",
1154            }
1155            .into())
1156        }
1157    }
1158
1159    fn has_xtensa_interface(&self) -> bool {
1160        self.supported_protocols.contains(&WireProtocol::Jtag)
1161    }
1162}
1163
1164impl RawSwdIo for JLink {
1165    fn swd_io<S>(&mut self, swdio: S) -> Result<Vec<bool>, DebugProbeError>
1166    where
1167        S: IntoIterator<Item = IoSequenceItem>,
1168    {
1169        self.probe_statistics.report_io();
1170        self.perform_swdio_transfer(swdio)
1171    }
1172
1173    fn swj_pins(
1174        &mut self,
1175        pin_out: u32,
1176        pin_select: u32,
1177        pin_wait: u32,
1178    ) -> Result<u32, DebugProbeError> {
1179        let mut nreset = Pins(0);
1180        nreset.set_nreset(true);
1181        let nreset_mask = nreset.0 as u32;
1182
1183        // If only the reset pin is selected we perform the reset.
1184        // If something else is selected return an error as this is not supported on J-Links.
1185        if pin_select == nreset_mask {
1186            if Pins(pin_out as u8).nreset() {
1187                self.target_reset_deassert()?;
1188            } else {
1189                self.target_reset_assert()?;
1190            }
1191
1192            // Normally this would be the timeout we pass to the probe to settle the pins.
1193            // The J-Link is not capable of this, so we just wait for this time on the host
1194            // and assume it has settled until then.
1195            std::thread::sleep(Duration::from_micros(pin_wait as u64));
1196
1197            // We signal that we cannot read the pin state.
1198            Ok(0xFFFF_FFFF)
1199        } else {
1200            // This is not supported for J-Links, unfortunately.
1201            Err(DebugProbeError::CommandNotSupportedByProbe {
1202                command_name: "swj_pins",
1203            })
1204        }
1205    }
1206
1207    fn swd_settings(&self) -> &SwdSettings {
1208        &self.swd_settings
1209    }
1210
1211    fn probe_statistics(&mut self) -> &mut ProbeStatistics {
1212        &mut self.probe_statistics
1213    }
1214}
1215
1216impl RawJtagIo for JLink {
1217    fn state_mut(&mut self) -> &mut JtagDriverState {
1218        &mut self.jtag_state
1219    }
1220
1221    fn state(&self) -> &JtagDriverState {
1222        &self.jtag_state
1223    }
1224
1225    fn shift_bit(&mut self, tms: bool, tdi: bool, capture: bool) -> Result<(), DebugProbeError> {
1226        self.shift_jtag_bit(tms, tdi, capture)
1227    }
1228
1229    fn read_captured_bits(&mut self) -> Result<BitVec, DebugProbeError> {
1230        self.read_captured_bits()
1231    }
1232}
1233
1234impl AutoImplementJtagAccess for JLink {}
1235impl DapProbe for JLink {}
1236
1237impl SwoAccess for JLink {
1238    fn enable_swo(&mut self, config: &SwoConfig) -> Result<(), ArmError> {
1239        self.swo_config = Some(*config);
1240        self.swo_start(SwoMode::Uart, config.baud(), SWO_BUFFER_SIZE.into())
1241            .map_err(DebugProbeError::from)?;
1242        Ok(())
1243    }
1244
1245    fn disable_swo(&mut self) -> Result<(), ArmError> {
1246        self.swo_config = None;
1247        self.swo_stop().map_err(DebugProbeError::from)?;
1248        Ok(())
1249    }
1250
1251    fn swo_buffer_size(&mut self) -> Option<usize> {
1252        Some(SWO_BUFFER_SIZE.into())
1253    }
1254
1255    fn read_swo_timeout(&mut self, timeout: Duration) -> Result<Vec<u8>, ArmError> {
1256        let start = Instant::now();
1257        let mut buf = vec![0; SWO_BUFFER_SIZE.into()];
1258
1259        let poll_interval = self
1260            .swo_poll_interval_hint(&self.swo_config.unwrap())
1261            .unwrap();
1262
1263        let mut bytes = vec![];
1264        loop {
1265            let data = self.swo_read(&mut buf).map_err(DebugProbeError::from)?;
1266            bytes.extend(data.as_ref());
1267            if start.elapsed() > timeout {
1268                break;
1269            }
1270            std::thread::sleep(poll_interval);
1271        }
1272        Ok(bytes)
1273    }
1274}
1275
1276#[tracing::instrument]
1277fn list_jlink_devices() -> Vec<DebugProbeInfo> {
1278    let devices = match nusb::list_devices().wait() {
1279        Ok(devices) => devices,
1280        Err(e) => {
1281            tracing::warn!("error listing J-Link devices: {e}");
1282            return vec![];
1283        }
1284    };
1285
1286    devices
1287        .filter(is_jlink)
1288        .map(|info| {
1289            DebugProbeInfo::new(
1290                info.product_string().unwrap_or("J-Link").to_string(),
1291                info.vendor_id(),
1292                info.product_id(),
1293                info.serial_number().map(|s| s.to_string()),
1294                &JLinkFactory,
1295                None,
1296                false,
1297            )
1298        })
1299        .collect()
1300}
1301
1302impl TryFrom<Interface> for WireProtocol {
1303    type Error = JlinkError;
1304
1305    fn try_from(interface: Interface) -> Result<Self, Self::Error> {
1306        match interface {
1307            Interface::Jtag => Ok(WireProtocol::Jtag),
1308            Interface::Swd => Ok(WireProtocol::Swd),
1309            unknown_interface => Err(JlinkError::UnknownInterface(unknown_interface)),
1310        }
1311    }
1312}
1313
1314/// A hardware version returned by [`JLink::read_hardware_version`].
1315///
1316/// Note that the reported hardware version does not allow reliable feature detection, since
1317/// embedded J-Link probes might return a hardware version of 1.0.0 despite supporting SWD and other
1318/// much newer features.
1319#[derive(Debug)]
1320struct HardwareVersion(u32);
1321
1322impl HardwareVersion {
1323    fn from_u32(raw: u32) -> Self {
1324        HardwareVersion(raw)
1325    }
1326
1327    /// Returns the type of hardware (or `None` if the hardware type is unknown).
1328    fn hardware_type(&self) -> Option<HardwareType> {
1329        Some(match (self.0 / 1000000) % 100 {
1330            0 => HardwareType::JLink,
1331            1 => HardwareType::JTrace,
1332            2 => HardwareType::Flasher,
1333            3 => HardwareType::JLinkPro,
1334            _ => return None,
1335        })
1336    }
1337
1338    /// The major version.
1339    fn major(&self) -> u8 {
1340        // Decimal coded Decimal, cool cool
1341        (self.0 / 10000) as u8
1342    }
1343
1344    /// The minor version.
1345    fn minor(&self) -> u8 {
1346        ((self.0 % 10000) / 100) as u8
1347    }
1348
1349    /// The hardware revision.
1350    fn revision(&self) -> u8 {
1351        (self.0 % 100) as u8
1352    }
1353}
1354
1355impl fmt::Display for HardwareVersion {
1356    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1357        if let Some(hw) = self.hardware_type() {
1358            write!(f, "{hw} ")?;
1359        }
1360        write!(f, "{}.{}.{}", self.major(), self.minor(), self.revision())
1361    }
1362}
1363
1364/// The hardware/product type of the device.
1365#[non_exhaustive]
1366#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1367enum HardwareType {
1368    JLink,
1369    JTrace,
1370    Flasher,
1371    JLinkPro,
1372}
1373
1374impl fmt::Display for HardwareType {
1375    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1376        f.write_str(match self {
1377            HardwareType::JLink => "J-Link",
1378            HardwareType::JTrace => "J-Trace",
1379            HardwareType::Flasher => "J-Flash",
1380            HardwareType::JLinkPro => "J-Link Pro",
1381        })
1382    }
1383}
1384
1385const VID_SEGGER: u16 = 0x1366;
1386
1387// Information about the product IDs is taken from the udev rules file provided by Segger.
1388
1389/// Product IDS for J-Link devices using the old format
1390///
1391/// 0x106 is not included on purpose, it is supposed to be serial (CDC) only
1392const LEGACY_JLINK_IDS: &[u16] = &[0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0107, 0x108];
1393
1394/// Flag indicating that the product ID is a J-Link product ID, using the new format
1395const PID_JLINK_FLAG: u16 = 0x1000;
1396
1397/// Indicates that the J-Link is using the (legacy) Segger driver.
1398const PID_JLINK_SEGGER_DRV_FLAG: u16 = 1 << 4;
1399
1400/// Indicates that the J-Link is using the WinUSB driver.
1401const PID_JLINK_WINUSB_DRV_FLAG: u16 = 1 << 5;
1402
1403fn is_jlink_product_id(product_id: u16) -> bool {
1404    let old_product_id = LEGACY_JLINK_IDS.contains(&product_id);
1405
1406    let new_product_id = (product_id & PID_JLINK_FLAG) != 0;
1407
1408    let jlink_using_segger_driver = (product_id & PID_JLINK_SEGGER_DRV_FLAG) != 0;
1409
1410    let jlink_using_winusb_driver = (product_id & PID_JLINK_WINUSB_DRV_FLAG) != 0;
1411
1412    old_product_id || (new_product_id && (jlink_using_segger_driver || jlink_using_winusb_driver))
1413}
1414
1415fn is_jlink(info: &DeviceInfo) -> bool {
1416    let matching_vendor_id = info.vendor_id() == VID_SEGGER;
1417
1418    matching_vendor_id && is_jlink_product_id(info.product_id())
1419}
1420
1421#[cfg(test)]
1422mod test {
1423    #[test]
1424    fn jlink_pid_cmsisdap() {
1425        // J-Link devices configured as CMSIS-DAP should not be detected as J-Link devices.
1426        assert!(!super::is_jlink_product_id(0x1008));
1427    }
1428
1429    #[test]
1430    fn jlink_pid_segger_driver() {
1431        // J-Link device using the legacy Segger driver.
1432        assert!(super::is_jlink_product_id(0x1059));
1433    }
1434}