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