1use crate::{
3 architecture::{
4 arm::{
5 ArmCommunicationInterface, ArmDebugInterface, ArmError,
6 communication_interface::DapProbe, sequences::ArmDebugSequence,
7 },
8 riscv::{
9 communication_interface::{RiscvError, RiscvInterfaceBuilder},
10 dtm::jtag_dtm::JtagDtmBuilder,
11 },
12 xtensa::communication_interface::{
13 XtensaCommunicationInterface, XtensaDebugInterfaceState, XtensaError,
14 },
15 },
16 probe::{
17 AutoImplementJtagAccess, DebugProbe, DebugProbeError, DebugProbeInfo, DebugProbeSelector,
18 IoSequenceItem, JtagAccess, JtagDriverState, ProbeCreationError, ProbeFactory, RawJtagIo,
19 RawSwdIo, SwdSettings, WireProtocol,
20 list::{ProbeListItem, usb_probe_accessibility},
21 },
22};
23use bitvec::prelude::*;
24use nusb::{DeviceInfo, MaybeFuture};
25use std::{
26 io::{Read, Write},
27 sync::Arc,
28 time::{Duration, Instant},
29};
30
31mod command_compacter;
32mod ftdaye;
33
34use command_compacter::Command;
35use ftdaye::{ChipType, error::FtdiError};
36
37#[derive(Debug)]
38struct JtagAdapter {
39 device: ftdaye::Device,
40 speed_khz: u32,
41
42 command: Command,
43 commands: Vec<u8>,
44
45 in_bit_counts: Vec<usize>,
47 in_bits: BitVec,
48 ftdi: FtdiProperties,
49}
50
51impl JtagAdapter {
52 fn map_interface(usb_interface: Option<u8>) -> ftdaye::Interface {
59 match usb_interface {
60 Some(0) | None => ftdaye::Interface::A,
61 Some(1) => ftdaye::Interface::B,
62 Some(2) => ftdaye::Interface::C,
63 Some(3) => ftdaye::Interface::D,
64 Some(n) => {
65 tracing::warn!(
66 "FTDI interface number {n} is out of range (0..=3), defaulting to Channel A"
67 );
68 ftdaye::Interface::A
69 }
70 }
71 }
72
73 fn open(
74 ftdi: FtdiDevice,
75 usb_device: DeviceInfo,
76 usb_interface: Option<u8>,
77 ) -> Result<Self, DebugProbeError> {
78 let interface = Self::map_interface(usb_interface);
79 let device = ftdaye::Builder::new()
80 .with_interface(interface)
81 .with_read_timeout(Duration::from_secs(5))
82 .with_write_timeout(Duration::from_secs(5))
83 .usb_open(usb_device)?;
84
85 let ftdi = FtdiProperties::try_from((ftdi, device.chip_type()))?;
86
87 Ok(Self {
88 device,
89 speed_khz: 1000,
90 command: Command::default(),
91 commands: vec![],
92 in_bit_counts: vec![],
93 in_bits: BitVec::new(),
94 ftdi,
95 })
96 }
97
98 pub fn attach(&mut self) -> Result<(), FtdiError> {
99 self.device.usb_reset()?;
100 self.device.set_bitmode(0x0b, ftdaye::BitMode::Mpsse)?;
102 self.device.set_latency_timer(1)?;
103 self.device.usb_purge_buffers()?;
104
105 let mut junk = vec![];
106 let _ = self.device.read_to_end(&mut junk);
107
108 let (output, direction) = self.pin_layout();
109 self.device.set_pins(output, direction)?;
110
111 self.apply_clock_speed(self.speed_khz)?;
112
113 self.device.disable_loopback()?;
114
115 Ok(())
116 }
117
118 fn pin_layout(&self) -> (u16, u16) {
119 let (output, direction) = match (
120 self.device.vendor_id(),
121 self.device.product_id(),
122 self.device.product_string().unwrap_or(""),
123 ) {
124 (0x0403, 0x6014, "Digilent USB Device") => (0x2088, 0x308b),
126 (0x0403, 0x6014, "Digilent Adept USB Device") => (0x00e8, 0x60eb),
128 (0x0403, 0x6010, "Digilent Adept USB Device") => (0x0088, 0x008b),
130 (0x0403, 0x6010, "Digilent USB Device") => (0x0088, 0x008b),
132 _ => (0x0008, 0x000b),
136 };
137 (output, direction)
138 }
139
140 fn speed_khz(&self) -> u32 {
141 self.speed_khz
142 }
143
144 fn set_speed_khz(&mut self, speed_khz: u32) -> u32 {
145 self.speed_khz = speed_khz;
146 self.speed_khz
147 }
148
149 fn apply_clock_speed(&mut self, speed_khz: u32) -> Result<u32, FtdiError> {
150 if self.ftdi.has_divide_by_5 {
152 self.device.disable_divide_by_5()?;
153 } else {
154 self.device.enable_divide_by_5()?;
156 }
157
158 let is_exact = self.ftdi.max_clock.is_multiple_of(speed_khz);
160
161 let divisor =
163 (self.ftdi.max_clock.checked_div(speed_khz).unwrap_or(1) - is_exact as u32).min(0xFFFF);
164
165 let actual_speed = self.ftdi.max_clock / (divisor + 1);
166
167 tracing::info!(
168 "Setting speed to {} kHz (divisor: {}, actual speed: {} kHz)",
169 speed_khz,
170 divisor,
171 actual_speed
172 );
173
174 self.device.configure_clock_divider(divisor as u16)?;
175
176 self.speed_khz = actual_speed;
177 Ok(actual_speed)
178 }
179
180 fn read_response(&mut self) -> Result<(), DebugProbeError> {
181 if self.in_bit_counts.is_empty() {
182 return Ok(());
183 }
184
185 let mut t0 = Instant::now();
186 let timeout = Duration::from_millis(100);
187
188 let expected_bits = std::mem::take(&mut self.in_bit_counts);
189 let reply_slots = expected_bits.len();
190 let mut reply = Vec::with_capacity(reply_slots);
191 while reply.len() < reply_slots {
192 let read = self
193 .device
194 .read_to_end(&mut reply)
195 .map_err(FtdiError::from)?;
196
197 if read > 0 {
198 t0 = Instant::now();
199 }
200
201 if t0.elapsed() > timeout {
202 tracing::warn!("Read {} bytes, expected {}", reply.len(), reply_slots);
203 return Err(DebugProbeError::Timeout);
204 }
205 }
206
207 if reply.len() != reply_slots {
208 return Err(DebugProbeError::Other(format!(
209 "Read more data than expected. Expected {} bytes, got {} bytes",
210 reply_slots,
211 reply.len()
212 )));
213 }
214
215 for (byte, count) in reply.into_iter().zip(expected_bits) {
216 let bits = byte >> (8 - count);
217 self.in_bits
218 .extend_from_bitslice(&bits.view_bits::<Lsb0>()[..count]);
219 }
220
221 Ok(())
222 }
223
224 fn flush(&mut self) -> Result<(), DebugProbeError> {
225 self.finalize_command()?;
226 self.send_buffer()?;
227 self.read_response()?;
228
229 Ok(())
230 }
231
232 fn append_command(&mut self, command: Command) -> Result<(), DebugProbeError> {
233 tracing::trace!("Appending {:?}", command);
234 if self.commands.len() + command.len() + 1 >= self.ftdi.buffer_size {
236 self.send_buffer()?;
237 self.read_response()?;
238 }
239
240 command.add_captured_bits(&mut self.in_bit_counts);
241 command.encode(&mut self.commands);
242
243 Ok(())
244 }
245
246 fn finalize_command(&mut self) -> Result<(), DebugProbeError> {
247 if let Some(command) = self.command.take() {
248 self.append_command(command)?;
249 }
250
251 Ok(())
252 }
253
254 fn shift_bit(&mut self, tms: bool, tdi: bool, capture: bool) -> Result<(), DebugProbeError> {
255 if let Some(command) = self.command.append_jtag_bit(tms, tdi, capture) {
256 self.append_command(command)?;
257 }
258
259 Ok(())
260 }
261
262 fn send_buffer(&mut self) -> Result<(), DebugProbeError> {
263 if self.commands.is_empty() {
264 return Ok(());
265 }
266
267 self.commands.push(0x87);
271
272 tracing::trace!("Sending buffer: {:X?}", self.commands);
273
274 self.device
275 .write_all(&self.commands)
276 .map_err(FtdiError::from)?;
277
278 self.commands.clear();
279
280 Ok(())
281 }
282
283 fn read_captured_bits(&mut self) -> Result<BitVec, DebugProbeError> {
284 self.flush()?;
285
286 Ok(std::mem::take(&mut self.in_bits))
287 }
288}
289
290#[derive(Debug)]
292pub struct FtdiProbeFactory;
293
294impl std::fmt::Display for FtdiProbeFactory {
295 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296 f.write_str("FTDI")
297 }
298}
299
300impl ProbeFactory for FtdiProbeFactory {
301 fn open(&self, selector: &DebugProbeSelector) -> Result<Box<dyn DebugProbe>, DebugProbeError> {
302 let Some(ftdi) = FTDI_COMPAT_DEVICES
304 .iter()
305 .find(|ftdi| ftdi.id == (selector.vendor_id, selector.product_id))
306 .copied()
307 else {
308 return Err(DebugProbeError::ProbeCouldNotBeCreated(
309 ProbeCreationError::NotFound,
310 ));
311 };
312
313 let devices = nusb::list_devices()
314 .wait()
315 .map_err(|e| DebugProbeError::from(FtdiError::Usb(e.into())))?;
316
317 let mut probes = devices
318 .filter(|usb_info| selector.matches(usb_info))
319 .collect::<Vec<_>>();
320
321 if probes.is_empty() {
322 return Err(DebugProbeError::ProbeCouldNotBeCreated(
323 ProbeCreationError::NotFound,
324 ));
325 } else if probes.len() > 1 {
326 tracing::warn!("More than one matching FTDI probe was found. Opening the first one.");
327 }
328
329 let probe = FtdiProbe {
330 adapter: JtagAdapter::open(ftdi, probes.pop().unwrap(), selector.interface)?,
331 jtag_state: JtagDriverState::default(),
332 swd_settings: SwdSettings::default(),
333 };
334 tracing::debug!("opened probe: {:?}", probe);
335 Ok(Box::new(probe))
336 }
337
338 fn list_probes(&self) -> Vec<ProbeListItem> {
339 list_ftdi_devices()
340 }
341
342 fn list_probes_filtered(&self, selector: Option<&DebugProbeSelector>) -> Vec<ProbeListItem> {
343 self.list_probes()
352 .into_iter()
353 .filter(|probe| {
354 selector.as_ref().is_none_or(|s| {
355 probe.info.vendor_id == s.vendor_id
356 && probe.info.product_id == s.product_id
357 && s.serial_number.as_ref().is_none_or(|sn| {
358 if let Some(probe_sn) = &probe.info.serial_number {
359 probe_sn == sn
360 } else {
361 sn.is_empty()
362 }
363 })
364 })
365 })
366 .collect()
367 }
368}
369
370#[derive(Debug)]
372pub struct FtdiProbe {
373 adapter: JtagAdapter,
374 jtag_state: JtagDriverState,
375 swd_settings: SwdSettings,
376}
377
378impl DebugProbe for FtdiProbe {
379 fn get_name(&self) -> &str {
380 "FTDI"
381 }
382
383 fn speed_khz(&self) -> u32 {
384 self.adapter.speed_khz()
385 }
386
387 fn set_speed(&mut self, speed_khz: u32) -> Result<u32, DebugProbeError> {
388 Ok(self.adapter.set_speed_khz(speed_khz))
389 }
390
391 fn attach(&mut self) -> Result<(), DebugProbeError> {
392 tracing::debug!("Attaching...");
393
394 self.adapter.attach()?;
395 self.select_target(0)
396 }
397
398 fn detach(&mut self) -> Result<(), crate::Error> {
399 Ok(())
400 }
401
402 fn target_reset(&mut self) -> Result<(), DebugProbeError> {
403 Err(DebugProbeError::NotImplemented {
406 function_name: "target_reset",
407 })
408 }
409
410 fn target_reset_assert(&mut self) -> Result<(), DebugProbeError> {
411 Err(DebugProbeError::NotImplemented {
412 function_name: "target_reset_assert",
413 })
414 }
415
416 fn target_reset_deassert(&mut self) -> Result<(), DebugProbeError> {
417 Err(DebugProbeError::NotImplemented {
418 function_name: "target_reset_deassert",
419 })
420 }
421
422 fn select_protocol(&mut self, protocol: WireProtocol) -> Result<(), DebugProbeError> {
423 if protocol != WireProtocol::Jtag {
424 Err(DebugProbeError::UnsupportedProtocol(protocol))
425 } else {
426 Ok(())
427 }
428 }
429
430 fn active_protocol(&self) -> Option<WireProtocol> {
431 Some(WireProtocol::Jtag)
433 }
434
435 fn try_as_jtag_probe(&mut self) -> Option<&mut dyn JtagAccess> {
436 Some(self)
437 }
438
439 fn try_get_riscv_interface_builder<'probe>(
440 &'probe mut self,
441 ) -> Result<Box<dyn RiscvInterfaceBuilder<'probe> + 'probe>, RiscvError> {
442 Ok(Box::new(JtagDtmBuilder::new(self)))
443 }
444
445 fn has_riscv_interface(&self) -> bool {
446 true
447 }
448
449 fn into_probe(self: Box<Self>) -> Box<dyn DebugProbe> {
450 self
451 }
452
453 fn try_get_arm_debug_interface<'probe>(
454 self: Box<Self>,
455 sequence: Arc<dyn ArmDebugSequence>,
456 ) -> Result<Box<dyn ArmDebugInterface + 'probe>, (Box<dyn DebugProbe>, ArmError)> {
457 Ok(ArmCommunicationInterface::create(self, sequence, true))
458 }
459
460 fn has_arm_interface(&self) -> bool {
461 true
462 }
463
464 fn try_get_xtensa_interface<'probe>(
465 &'probe mut self,
466 state: &'probe mut XtensaDebugInterfaceState,
467 ) -> Result<XtensaCommunicationInterface<'probe>, XtensaError> {
468 Ok(XtensaCommunicationInterface::new(self, state))
469 }
470
471 fn has_xtensa_interface(&self) -> bool {
472 true
473 }
474}
475
476impl AutoImplementJtagAccess for FtdiProbe {}
477impl DapProbe for FtdiProbe {}
478
479impl RawSwdIo for FtdiProbe {
480 fn swd_io<S>(&mut self, _swdio: S) -> Result<Vec<bool>, DebugProbeError>
481 where
482 S: IntoIterator<Item = IoSequenceItem>,
483 {
484 Err(DebugProbeError::NotImplemented {
485 function_name: "swd_io",
486 })
487 }
488
489 fn swj_pins(
490 &mut self,
491 _pin_out: u32,
492 _pin_select: u32,
493 _pin_wait: u32,
494 ) -> Result<u32, DebugProbeError> {
495 Err(DebugProbeError::CommandNotSupportedByProbe {
496 command_name: "swj_pins",
497 })
498 }
499
500 fn swd_settings(&self) -> &SwdSettings {
501 &self.swd_settings
502 }
503}
504
505impl RawJtagIo for FtdiProbe {
506 fn shift_bit(
507 &mut self,
508 tms: bool,
509 tdi: bool,
510 capture_tdo: bool,
511 ) -> Result<(), DebugProbeError> {
512 self.jtag_state.state.update(tms);
513 self.adapter.shift_bit(tms, tdi, capture_tdo)?;
514 Ok(())
515 }
516
517 fn read_captured_bits(&mut self) -> Result<BitVec, DebugProbeError> {
518 self.adapter.read_captured_bits()
519 }
520
521 fn state_mut(&mut self) -> &mut JtagDriverState {
522 &mut self.jtag_state
523 }
524
525 fn state(&self) -> &JtagDriverState {
526 &self.jtag_state
527 }
528}
529
530#[derive(Debug)]
532struct FtdiProperties {
533 buffer_size: usize,
537
538 max_clock: u32,
540
541 has_divide_by_5: bool,
546}
547
548impl TryFrom<(FtdiDevice, Option<ChipType>)> for FtdiProperties {
549 type Error = FtdiError;
550
551 fn try_from((ftdi, chip_type): (FtdiDevice, Option<ChipType>)) -> Result<Self, Self::Error> {
552 let chip_type = match chip_type {
553 Some(ty) => ty,
554 None => {
555 tracing::warn!("Unknown FTDI chip. Assuming {:?}", ftdi.fallback_chip_type);
556 ftdi.fallback_chip_type
557 }
558 };
559
560 let properties = match chip_type {
561 ChipType::FT2232H | ChipType::FT4232H => Self {
562 buffer_size: 4096,
563 max_clock: 30_000,
564 has_divide_by_5: true,
565 },
566 ChipType::FT232H => Self {
567 buffer_size: 1024,
568 max_clock: 30_000,
569 has_divide_by_5: true,
570 },
571 ChipType::FT2232C => Self {
572 buffer_size: 128,
573 max_clock: 6_000,
574 has_divide_by_5: false,
575 },
576 not_mpsse => {
577 tracing::warn!("Unsupported FTDI chip: {:?}", not_mpsse);
578 return Err(FtdiError::UnsupportedChipType(not_mpsse));
579 }
580 };
581
582 Ok(properties)
583 }
584}
585
586#[derive(Debug, Clone, Copy)]
587struct FtdiDevice {
588 id: (u16, u16),
590
591 fallback_chip_type: ChipType,
598}
599
600impl FtdiDevice {
601 fn matches(&self, device: &DeviceInfo) -> bool {
602 self.id == (device.vendor_id(), device.product_id())
603 }
604}
605
606static FTDI_COMPAT_DEVICES: &[FtdiDevice] = &[
608 FtdiDevice {
613 id: (0x0403, 0x6010),
614 fallback_chip_type: ChipType::FT2232C,
615 },
616 FtdiDevice {
618 id: (0x0403, 0x6011),
619 fallback_chip_type: ChipType::FT4232H,
620 },
621 FtdiDevice {
623 id: (0x0403, 0x6014),
624 fallback_chip_type: ChipType::FT232H,
625 },
626 FtdiDevice {
631 id: (0x15ba, 0x0003),
632 fallback_chip_type: ChipType::FT2232C,
633 },
634 FtdiDevice {
636 id: (0x15ba, 0x0004),
637 fallback_chip_type: ChipType::FT2232C,
638 },
639 FtdiDevice {
641 id: (0x15ba, 0x002a),
642 fallback_chip_type: ChipType::FT2232H,
643 },
644 FtdiDevice {
646 id: (0x15ba, 0x002b),
647 fallback_chip_type: ChipType::FT2232H,
648 },
649];
650
651fn get_device_info(device: &DeviceInfo) -> Option<ProbeListItem> {
652 FTDI_COMPAT_DEVICES.iter().find_map(|ftdi| {
653 ftdi.matches(device).then(|| ProbeListItem {
654 info: DebugProbeInfo {
655 identifier: device.product_string().unwrap_or("FTDI").to_string(),
656 vendor_id: device.vendor_id(),
657 product_id: device.product_id(),
658 serial_number: device.serial_number().map(|s| s.to_string()),
659 probe_factory: &FtdiProbeFactory,
660 is_hid_interface: false,
661 interface: None,
662 },
663 accessibility: usb_probe_accessibility(device),
664 })
665 })
666}
667
668#[tracing::instrument(skip_all)]
669fn list_ftdi_devices() -> Vec<ProbeListItem> {
670 match nusb::list_devices().wait() {
671 Ok(devices) => devices
672 .filter_map(|device| get_device_info(&device))
673 .collect(),
674 Err(e) => {
675 tracing::warn!("error listing FTDI devices: {e}");
676 vec![]
677 }
678 }
679}