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, 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#[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 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, jtag_chunk_size: 0, 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 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 vec![WireProtocol::Jtag]
231 };
232
233 this.protocol = if this.supported_protocols.contains(&WireProtocol::Swd) {
234 WireProtocol::Swd
236 } else {
237 *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 this.jtag_chunk_size = match selector.product_id {
261 0x0101 => 65535,
263 0x1051 => 504,
265 _ => 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 let devices = [
288 (0x1366, 0x0101, Some("000000123456")), ];
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
355pub struct JLink {
357 handle: nusb::Interface,
358
359 read_ep: u8,
360 write_ep: u8,
361 max_read_ep_packet: usize,
362
363 caps: Capabilities,
365
366 interfaces: Interfaces,
368
369 interface: Interface,
372
373 config: JlinkConfig,
375 connection_handle: Option<u16>,
376
377 swo_config: Option<SwoConfig>,
378
379 supported_protocols: Vec<WireProtocol>,
381 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 jtag_chunk_size: usize,
394
395 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 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 reset_trst(&mut self) -> Result<(), JlinkError> {
672 self.write_cmd(&[Command::ResetTrst as u8])
673 }
674
675 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 let cmd = if self.caps.contains(Capability::SelectIf) {
720 has_status_byte = true;
722 Command::HwJtag3
723 } else {
724 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 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 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 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 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 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 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 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] = 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 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 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 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 self.read(&mut response[REG_MIN_SIZE..size])?;
940 }
941
942 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 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 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 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 tracing::debug!("Resetting JTAG chain using trst");
1047 self.reset_trst()?;
1048
1049 self.select_target(0)?;
1050 }
1051 WireProtocol::Swd => {
1052 }
1056 }
1057
1058 self.write_cmd(&[Command::HwReset1 as u8])?;
1059 self.write_cmd(&[Command::HwTrst1 as u8])?;
1060
1061 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 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 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 std::thread::sleep(Duration::from_micros(pin_wait as u64));
1196
1197 Ok(0xFFFF_FFFF)
1199 } else {
1200 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#[derive(Debug)]
1320struct HardwareVersion(u32);
1321
1322impl HardwareVersion {
1323 fn from_u32(raw: u32) -> Self {
1324 HardwareVersion(raw)
1325 }
1326
1327 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 fn major(&self) -> u8 {
1340 (self.0 / 10000) as u8
1342 }
1343
1344 fn minor(&self) -> u8 {
1346 ((self.0 % 10000) / 100) as u8
1347 }
1348
1349 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#[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
1387const LEGACY_JLINK_IDS: &[u16] = &[0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0107, 0x108];
1393
1394const PID_JLINK_FLAG: u16 = 0x1000;
1396
1397const PID_JLINK_SEGGER_DRV_FLAG: u16 = 1 << 4;
1399
1400const 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 assert!(!super::is_jlink_product_id(0x1008));
1427 }
1428
1429 #[test]
1430 fn jlink_pid_segger_driver() {
1431 assert!(super::is_jlink_product_id(0x1059));
1433 }
1434}