1mod 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#[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 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), interface: Interface::Spi, interfaces: Interfaces::from_bits_warn(0), handle,
187
188 supported_protocols: vec![], protocol: WireProtocol::Jtag, connection_handle: None,
191
192 swo_config: None,
193 speed_khz: 0, 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, jtag_chunk_size: 0, 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 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 vec![WireProtocol::Jtag]
230 };
231
232 this.protocol = if this.supported_protocols.contains(&WireProtocol::Swd) {
233 WireProtocol::Swd
235 } else {
236 *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 this.jtag_chunk_size = match selector.product_id {
260 0x0101 => 65535,
262 0x1051 => 504,
264 _ => 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 let devices = [
287 (0x1366, 0x0101, Some("000000123456")), ];
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
356pub struct JLink {
358 handle: nusb::Interface,
359
360 read_ep: u8,
361 write_ep: u8,
362 max_read_ep_packet: usize,
363
364 caps: Capabilities,
366
367 interfaces: Interfaces,
369
370 interface: Interface,
373
374 config: JlinkConfig,
376 connection_handle: Option<u16>,
377
378 swo_config: Option<SwoConfig>,
379
380 supported_protocols: Vec<WireProtocol>,
382 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 jtag_chunk_size: usize,
395
396 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 pub fn capabilities(&self) -> Capabilities {
413 self.caps
414 }
415
416 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 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 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 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 pub fn read_max_mem_block(&self) -> Result<u32, JlinkError> {
558 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 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 match buf.iter().position(|&b| b == 0) {
601 Some(pos) => &buf[..pos],
602 None => &buf,
603 },
604 )
605 .into_owned())
606 }
607
608 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 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 let _ = self.read_u32()?;
640
641 self.interface = intf;
642
643 if self.speed_khz != 0 {
644 self.set_interface_clock_speed(SpeedConfig::khz(self.speed_khz as u16).unwrap())?;
646 }
647
648 Ok(())
649 }
650
651 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 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 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 fn reset_trst(&mut self) -> Result<(), JlinkError> {
698 self.write_cmd(&[Command::ResetTrst as u8])
699 }
700
701 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 let cmd = if self.caps.contains(Capability::SelectIf) {
746 has_status_byte = true;
748 Command::HwJtag3
749 } else {
750 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 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 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 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 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 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 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 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] = 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 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 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 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 self.read(&mut response[REG_MIN_SIZE..size])?;
966 }
967
968 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 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 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 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 tracing::debug!("Resetting JTAG chain using trst");
1073 self.reset_trst()?;
1074
1075 self.select_target(0)?;
1076 }
1077 WireProtocol::Swd => {
1078 }
1082 }
1083
1084 self.write_cmd(&[Command::HwReset1 as u8])?;
1085 self.write_cmd(&[Command::HwTrst1 as u8])?;
1086
1087 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 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 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 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 std::thread::sleep(Duration::from_micros(pin_wait as u64));
1236
1237 Ok(0xFFFF_FFFF)
1239 } else {
1240 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#[derive(Debug)]
1360struct HardwareVersion(u32);
1361
1362impl HardwareVersion {
1363 fn from_u32(raw: u32) -> Self {
1364 HardwareVersion(raw)
1365 }
1366
1367 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 fn major(&self) -> u8 {
1380 (self.0 / 10000) as u8
1382 }
1383
1384 fn minor(&self) -> u8 {
1386 ((self.0 % 10000) / 100) as u8
1387 }
1388
1389 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#[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
1427const LEGACY_JLINK_IDS: &[u16] = &[0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0107, 0x108];
1433
1434const PID_JLINK_FLAG: u16 = 0x1000;
1436
1437const PID_JLINK_SEGGER_DRV_FLAG: u16 = 1 << 4;
1439
1440const 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 assert!(!super::is_jlink_product_id(0x1008));
1467 }
1468
1469 #[test]
1470 fn jlink_pid_segger_driver() {
1471 assert!(super::is_jlink_product_id(0x1059));
1473 }
1474}