1use std::any::Any;
6use std::cell::{Cell, RefCell};
7use std::collections::HashMap;
8use std::collections::hash_map::Entry;
9use std::fs;
10use std::io::{Read, Write};
11use std::marker::PhantomData;
12use std::path::{Path, PathBuf};
13use std::rc::{Rc, Weak};
14use std::sync::LazyLock;
15use std::time::Duration;
16
17use anyhow::{Context, Result, bail, ensure};
18use regex::Regex;
19use serde_annotate::Annotate;
20use serialport::TTYPort;
21
22use crate::debug::openocd::OpenOcdJtagChain;
23use crate::io::gpio::{GpioBitbanging, GpioMonitoring, GpioPin};
24use crate::io::i2c::Bus;
25use crate::io::jtag::{JtagChain, JtagParams};
26use crate::io::spi::Target;
27use crate::io::uart::Uart;
28use crate::transport::MaintainConnection;
29use crate::transport::chip_whisperer::ChipWhisperer;
30use crate::transport::chip_whisperer::board::Board;
31use crate::transport::common::fpga::{ClearBitstream, FpgaProgram};
32use crate::transport::common::uart::flock_serial;
33use crate::transport::{
34 Capabilities, Capability, SetJtagPins, Transport, TransportError, TransportInterfaceType,
35 UpdateFirmware,
36};
37use crate::util::usb::UsbBackend;
38
39pub mod c2d2;
40pub mod dfu;
41pub mod gpio;
42pub mod i2c;
43pub mod servo_micro;
44pub mod spi;
45pub mod ti50;
46pub mod uart;
47
48pub use c2d2::C2d2Flavor;
49pub use dfu::HyperdebugDfu;
50pub use servo_micro::ServoMicroFlavor;
51pub use ti50::Ti50Flavor;
52
53pub struct Hyperdebug<T: Flavor> {
56 spi_interface: BulkInterface,
57 i2c_interface: Option<BulkInterface>,
58 cmsis_interface: Option<BulkInterface>,
59 uart_interfaces: HashMap<String, UartInterface>,
60 cached_io_interfaces: CachedIo,
61 inner: Rc<Inner>,
62 current_firmware_version: Option<String>,
63 cmsis_google_capabilities: Cell<Option<u16>>,
64 phantom: PhantomData<T>,
65}
66
67pub trait Flavor {
70 fn gpio_pin(inner: &Rc<Inner>, pinname: &str) -> Result<Rc<dyn GpioPin>>;
71 fn spi_index(_inner: &Rc<Inner>, instance: &str) -> Result<(u8, u8)> {
72 bail!(TransportError::InvalidInstance(
73 TransportInterfaceType::Spi,
74 instance.to_string()
75 ))
76 }
77 fn i2c_index(_inner: &Rc<Inner>, instance: &str) -> Result<(u8, i2c::Mode)> {
78 bail!(TransportError::InvalidInstance(
79 TransportInterfaceType::I2c,
80 instance.to_string()
81 ))
82 }
83 fn get_default_usb_vid() -> u16;
84 fn get_default_usb_pid() -> u16;
85 fn load_bitstream(_fpga_program: &FpgaProgram) -> Result<()> {
86 Err(TransportError::UnsupportedOperation.into())
87 }
88 fn clear_bitstream(_clear: &ClearBitstream) -> Result<()> {
89 Err(TransportError::UnsupportedOperation.into())
90 }
91 fn perform_initial_fw_check() -> bool {
92 true
93 }
94}
95
96pub const VID_GOOGLE: u16 = 0x18d1;
97pub const PID_HYPERDEBUG: u16 = 0x520e;
98
99#[derive(Copy, Clone)]
102pub struct BulkInterface {
103 interface: u8,
104 in_endpoint: u8,
105 out_endpoint: u8,
106}
107
108pub struct UartInterface {
109 interface: u8,
110 tty: PathBuf,
111}
112
113impl UartInterface {
114 pub fn new(interface: u8, tty: PathBuf) -> Self {
115 Self { interface, tty }
116 }
117}
118
119impl<T: Flavor> Hyperdebug<T> {
120 const USB_CLASS_VENDOR: u8 = 255;
121 const USB_SUBCLASS_UART: u8 = 80;
122 const USB_SUBCLASS_SPI: u8 = 81;
123 const USB_SUBCLASS_I2C: u8 = 82;
124 const USB_PROTOCOL_UART: u8 = 1;
125 const USB_PROTOCOL_SPI: u8 = 2;
126 const USB_PROTOCOL_I2C: u8 = 1;
127
128 const CMSIS_DAP_CUSTOM_COMMAND_GOOGLE_INFO: u8 = 0x80;
130
131 const GOOGLE_INFO_CAPABILITIES: u8 = 0x00;
133
134 const GOOGLE_CAP_I2C: u16 = 0x0001;
136 const GOOGLE_CAP_I2C_DEVICE: u16 = 0x0002;
137 const GOOGLE_CAP_GPIO_MONITORING: u16 = 0x0004;
138 const GOOGLE_CAP_GPIO_BITBANGING: u16 = 0x0008;
139 const GOOGLE_CAP_UART_QUEUE_CLEAR: u16 = 0x0010;
140 const GOOGLE_CAP_TPM_POLL: u16 = 0x0020;
141
142 pub fn open(
144 usb_vid: Option<u16>,
145 usb_pid: Option<u16>,
146 usb_serial: Option<&str>,
147 ) -> Result<Self> {
148 let mut device = UsbBackend::new(
149 usb_vid.unwrap_or_else(T::get_default_usb_vid),
150 usb_pid.unwrap_or_else(T::get_default_usb_pid),
151 usb_serial,
152 )?;
153
154 let path = PathBuf::from("/sys/bus/usb/devices");
155
156 let mut console_tty: Option<PathBuf> = None;
157 let mut spi_interface: Option<BulkInterface> = None;
158 let mut i2c_interface: Option<BulkInterface> = None;
159 let mut cmsis_interface: Option<BulkInterface> = None;
160 let mut uart_interfaces: HashMap<String, UartInterface> = HashMap::new();
161
162 let config_desc = device.active_config_descriptor()?;
163 let current_firmware_version = if let Some(idx) = config_desc.description_string_index()
164 && let Ok(current_firmware_version) = device.read_string_descriptor_ascii(idx)
165 {
166 if let Some(released_firmware_version) = dfu::official_firmware_version()?
167 && T::perform_initial_fw_check()
168 && current_firmware_version != released_firmware_version
169 {
170 log::warn!(
171 "Current HyperDebug firmware version is {}, newest release is {}, Consider running `opentitantool transport update-firmware`",
172 current_firmware_version,
173 released_firmware_version,
174 );
175 }
176 Some(current_firmware_version)
177 } else {
178 None
179 };
180 for interface in config_desc.interfaces() {
182 for interface_desc in interface.descriptors() {
183 let ports = device
184 .port_numbers()?
185 .iter()
186 .map(|id| id.to_string())
187 .collect::<Vec<String>>()
188 .join(".");
189 let interface_path = path
190 .join(format!("{}-{}", device.bus_number(), ports))
191 .join(format!(
192 "{}-{}:{}.{}",
193 device.bus_number(),
194 ports,
195 config_desc.number(),
196 interface.number()
197 ));
198 if interface_desc.class_code() == Self::USB_CLASS_VENDOR
200 && interface_desc.sub_class_code() == Self::USB_SUBCLASS_UART
201 && interface_desc.protocol_code() == Self::USB_PROTOCOL_UART
202 {
203 let idx = match interface_desc.description_string_index() {
206 Some(idx) => idx,
207 None => continue,
208 };
209 let interface_name = match device.read_string_descriptor_ascii(idx) {
210 Ok(interface_name) => interface_name,
211 _ => continue,
212 };
213
214 if !device.kernel_driver_active(interface.number())? {
215 device.attach_kernel_driver(interface.number())?;
216 std::thread::sleep(Duration::from_millis(100));
218 }
219
220 if interface_name.ends_with("Shell") {
221 console_tty = Some(Self::find_tty(&interface_path)?);
224 } else {
225 let uart = UartInterface {
227 interface: interface.number(),
228 tty: Self::find_tty(&interface_path)?,
229 };
230 uart_interfaces.insert(interface_name.to_string(), uart);
231 }
232 continue;
233 }
234 if interface_desc.class_code() == Self::USB_CLASS_VENDOR
235 && interface_desc.sub_class_code() == Self::USB_SUBCLASS_SPI
236 && interface_desc.protocol_code() == Self::USB_PROTOCOL_SPI
237 {
238 Self::find_endpoints_for_interface(
241 &mut spi_interface,
242 &interface,
243 &interface_desc,
244 )?;
245 continue;
246 }
247 if interface_desc.class_code() == Self::USB_CLASS_VENDOR
248 && interface_desc.sub_class_code() == Self::USB_SUBCLASS_I2C
249 && interface_desc.protocol_code() == Self::USB_PROTOCOL_I2C
250 {
251 Self::find_endpoints_for_interface(
254 &mut i2c_interface,
255 &interface,
256 &interface_desc,
257 )?;
258 continue;
259 }
260 if interface_desc.class_code() == Self::USB_CLASS_VENDOR {
261 let idx = match interface_desc.description_string_index() {
264 Some(idx) => idx,
265 None => continue,
266 };
267 let interface_name = match device.read_string_descriptor_ascii(idx) {
268 Ok(interface_name) => interface_name,
269 _ => continue,
270 };
271 if interface_name.ends_with("CMSIS-DAP") {
272 Self::find_endpoints_for_interface(
275 &mut cmsis_interface,
276 &interface,
277 &interface_desc,
278 )?;
279 continue;
280 }
281 }
282 }
283 }
284 let result = Hyperdebug::<T> {
285 spi_interface: spi_interface.ok_or_else(|| {
286 TransportError::CommunicationError("Missing SPI interface".to_string())
287 })?,
288 i2c_interface,
289 cmsis_interface,
290 uart_interfaces,
291 cached_io_interfaces: CachedIo {
292 gpio: Default::default(),
293 spis: Default::default(),
294 i2cs_by_name: Default::default(),
295 i2cs_by_index: Default::default(),
296 uarts: Default::default(),
297 },
298 inner: Rc::new(Inner {
299 console_tty: console_tty.ok_or_else(|| {
300 TransportError::CommunicationError("Missing console interface".to_string())
301 })?,
302 conn: RefCell::new(Weak::new()),
303 usb_device: RefCell::new(device),
304 selected_spi: Cell::new(0),
305 }),
306 current_firmware_version,
307 cmsis_google_capabilities: Cell::new(None),
308 phantom: PhantomData,
309 };
310 Ok(result)
311 }
312
313 fn find_tty(path: &Path) -> Result<PathBuf> {
316 for entry in fs::read_dir(path).context(format!("find TTY: read_dir({:?})", path))? {
317 let entry = entry.context(format!("find TTY: entity {:?}", path))?;
318 if let Ok(filename) = entry.file_name().into_string()
319 && filename.starts_with("tty")
320 {
321 return Ok(PathBuf::from("/dev").join(entry.file_name()));
322 }
323 }
324 Err(TransportError::CommunicationError("Did not find ttyUSBn device".to_string()).into())
325 }
326
327 fn find_endpoints_for_interface(
328 interface_variable_output: &mut Option<BulkInterface>,
329 interface: &rusb::Interface,
330 interface_desc: &rusb::InterfaceDescriptor,
331 ) -> Result<()> {
332 let mut in_endpoint: Option<u8> = None;
333 let mut out_endpoint: Option<u8> = None;
334 for endpoint_desc in interface_desc.endpoint_descriptors() {
335 if endpoint_desc.transfer_type() != rusb::TransferType::Bulk {
336 continue;
337 }
338 match endpoint_desc.direction() {
339 rusb::Direction::In => {
340 ensure!(
341 in_endpoint.is_none(),
342 TransportError::CommunicationError("Multiple IN endpoints".to_string())
343 );
344 in_endpoint.replace(endpoint_desc.address());
345 }
346 rusb::Direction::Out => {
347 ensure!(
348 out_endpoint.is_none(),
349 TransportError::CommunicationError("Multiple OUT endpoints".to_string())
350 );
351 out_endpoint.replace(endpoint_desc.address());
352 }
353 }
354 }
355 match (in_endpoint, out_endpoint) {
356 (Some(in_endpoint), Some(out_endpoint)) => {
357 ensure!(
358 interface_variable_output.is_none(),
359 TransportError::CommunicationError("Multiple identical interfaces".to_string())
360 );
361 interface_variable_output.replace(BulkInterface {
362 interface: interface.number(),
363 in_endpoint,
364 out_endpoint,
365 });
366 Ok(())
367 }
368 _ => bail!(TransportError::CommunicationError(
369 "Missing one or more endpoints".to_string()
370 )),
371 }
372 }
373
374 fn get_cmsis_google_capabilities(&self) -> Result<u16> {
375 let Some(cmsis_interface) = self.cmsis_interface else {
376 return Ok(0);
379 };
380 if let Some(capabilities) = self.cmsis_google_capabilities.get() {
381 return Ok(capabilities);
383 }
384 self.inner
385 .usb_device
386 .borrow_mut()
387 .claim_interface(cmsis_interface.interface)?;
388 let cmd = [
389 Self::CMSIS_DAP_CUSTOM_COMMAND_GOOGLE_INFO,
390 Self::GOOGLE_INFO_CAPABILITIES,
391 ];
392 self.inner
393 .usb_device
394 .borrow()
395 .write_bulk(cmsis_interface.out_endpoint, &cmd)?;
396 let mut resp = [0u8; 64];
397 let bytecount = self
398 .inner
399 .usb_device
400 .borrow()
401 .read_bulk(cmsis_interface.in_endpoint, &mut resp)?;
402 let resp = &resp[..bytecount];
403 ensure!(
406 bytecount >= 4 && resp[0] == Self::CMSIS_DAP_CUSTOM_COMMAND_GOOGLE_INFO && resp[1] >= 2,
407 TransportError::CommunicationError("Unrecognized CMSIS-DAP response".to_string())
408 );
409 let capabilities = u16::from_le_bytes([resp[2], resp[3]]);
410 self.cmsis_google_capabilities.set(Some(capabilities));
411 self.inner
412 .usb_device
413 .borrow_mut()
414 .release_interface(cmsis_interface.interface)?;
415 Ok(capabilities)
416 }
417}
418
419pub struct Inner {
423 console_tty: PathBuf,
424 conn: RefCell<Weak<Conn>>,
425 usb_device: RefCell<UsbBackend>,
426 selected_spi: Cell<u8>,
427}
428
429pub struct CachedIo {
433 gpio: RefCell<HashMap<String, Rc<dyn GpioPin>>>,
434 spis: RefCell<HashMap<u8, Rc<dyn Target>>>,
435 i2cs_by_name: RefCell<HashMap<String, Rc<dyn Bus>>>,
436 i2cs_by_index: RefCell<HashMap<u8, Rc<dyn Bus>>>,
437 uarts: RefCell<HashMap<PathBuf, Rc<dyn Uart>>>,
438}
439
440pub struct Conn {
441 console_port: RefCell<TTYPort>,
442 first_use: Cell<bool>,
443}
444
445impl MaintainConnection for Conn {}
449
450impl Inner {
451 const COMMAND_TIMEOUT: Duration = Duration::from_millis(3000);
453
454 pub fn connect(&self) -> Result<Rc<Conn>> {
456 if let Some(conn) = self.conn.borrow().upgrade() {
457 return Ok(conn);
459 }
460 let port_name = self
462 .console_tty
463 .to_str()
464 .ok_or(TransportError::UnicodePathError)?;
465 let port = TTYPort::open(
466 &serialport::new(port_name, 115_200)
467 .preserve_dtr_on_open()
468 .timeout(Self::COMMAND_TIMEOUT),
469 )
470 .context("Failed to open HyperDebug console")?;
471 flock_serial(&port, port_name)?;
472 let conn = Rc::new(Conn {
473 console_port: RefCell::new(port),
474 first_use: Cell::new(true),
475 });
476 *self.conn.borrow_mut() = Rc::downgrade(&conn);
483 Ok(conn)
484 }
485
486 pub fn cmd_no_output(&self, cmd: &str) -> Result<()> {
489 let mut unexpected_output: bool = false;
490 self.execute_command(cmd, |line| {
491 log::warn!("Unexpected HyperDebug output: {}", line);
492 unexpected_output = true;
493 })?;
494 if unexpected_output {
495 bail!(TransportError::CommunicationError(format!(
496 "Unexpected output to {}",
497 cmd
498 )));
499 }
500 Ok(())
501 }
502
503 pub fn cmd_one_line_output(&self, cmd: &str) -> Result<String> {
506 let mut result: Option<String> = None;
507 let mut unexpected_output: bool = false;
508 self.execute_command(cmd, |line| {
509 if unexpected_output {
510 log::warn!("Unexpected HyperDebug output: {}", line);
512 } else if result.is_none() {
513 result = Some(line.to_string());
515 } else {
516 log::warn!("Unexpected HyperDebug output: {}", result.as_ref().unwrap());
518 log::warn!("Unexpected HyperDebug output: {}", line);
519 unexpected_output = true;
520 }
521 })?;
522 if unexpected_output {
523 bail!(TransportError::CommunicationError(
524 "Unexpected output".to_string()
525 ));
526 }
527 match result {
528 None => bail!(TransportError::CommunicationError(format!(
529 "No response to command {}",
530 cmd
531 ))),
532 Some(str) => Ok(str),
533 }
534 }
535
536 pub fn cmd_one_line_output_match<'a>(
539 &self,
540 cmd: &str,
541 regex: &Regex,
542 buf: &'a mut String,
543 ) -> Result<regex::Captures<'a>> {
544 *buf = self.cmd_one_line_output(cmd)?;
545 let Some(captures) = regex.captures(buf) else {
546 log::warn!("Unexpected HyperDebug output: {}", buf);
547 bail!(TransportError::CommunicationError(
548 "Unexpected output".to_string()
549 ));
550 };
551 Ok(captures)
552 }
553
554 fn execute_command(&self, cmd: &str, callback: impl FnMut(&str)) -> Result<()> {
556 let conn = self.connect()?;
558 conn.execute_command(cmd, callback)
560 }
561}
562
563impl Conn {
564 fn execute_command(&self, cmd: &str, mut callback: impl FnMut(&str)) -> Result<()> {
566 let port: &mut TTYPort = &mut self.console_port.borrow_mut();
567
568 if self.first_use.get() {
569 port.write(format!("\x03{}\n", cmd).as_bytes())
572 .context("writing to HyperDebug console")?;
573 self.first_use.set(false);
574 } else {
575 port.write(format!("{}\n", cmd).as_bytes())
576 .context("writing to HyperDebug console")?;
577 }
578
579 let mut buf = [0u8; 128];
584 let mut seen_echo = false;
585 let mut len: usize = 0;
586 loop {
587 match port.read(&mut buf[len..]) {
589 Ok(rc) => {
590 len += rc;
591 let mut line_start = 0;
595 for i in 0..len {
596 if buf[i] == b'\n' {
597 let mut line_end = i;
599 while line_end > line_start && buf[line_end - 1] == 13 {
600 line_end -= 1;
601 }
602 let line = std::str::from_utf8(&buf[line_start..line_end])
603 .context("utf8 decoding from HyperDebug console")?;
604 if seen_echo {
605 callback(line);
606 } else if line.len() >= 2 && line[line.len() - 2..] == *"^C" {
607 } else if line.len() >= cmd.len()
609 && line[line.len() - cmd.len()..] == *cmd
610 {
611 seen_echo = true;
614 } else if !line.is_empty() {
615 log::info!("Unexpected output: {:?}", line)
617 }
618 line_start = i + 1;
619 }
620 }
621 if line_start > 0 {
623 buf.rotate_left(line_start);
624 len -= line_start;
625 }
626 if seen_echo && buf[0..len] == [b'>', b' '] {
627 return Ok(());
631 }
632 }
633 Err(error) => return Err(error).context("reading from HyperDebug console"),
634 }
635 }
636 }
637}
638
639impl<T: Flavor> Transport for Hyperdebug<T> {
640 fn capabilities(&self) -> Result<Capabilities> {
641 Ok(Capabilities::new(
642 Capability::UART
643 | Capability::UART_NONBLOCKING
644 | Capability::GPIO
645 | Capability::GPIO_MONITORING
646 | Capability::GPIO_BITBANGING
647 | Capability::SPI
648 | Capability::SPI_DUAL
649 | Capability::SPI_QUAD
650 | Capability::I2C
651 | Capability::JTAG,
652 ))
653 }
654
655 fn apply_default_configuration(&self) -> Result<()> {
656 let mut error: Option<String> = None;
657 self.inner.execute_command("reinit", |line| {
658 log::warn!("Unexpected HyperDebug output: {}", line);
659 if line.starts_with("Error: ") {
660 error = Some(line.to_string());
661 }
662 })?;
663 if let Some(err) = error {
664 bail!(TransportError::CommunicationError(err));
665 }
666 Ok(())
667 }
668
669 fn spi(&self, instance: &str) -> Result<Rc<dyn Target>> {
671 let (enable_cmd, idx) = T::spi_index(&self.inner, instance)?;
672 if let Some(instance) = self.cached_io_interfaces.spis.borrow().get(&idx) {
673 return Ok(Rc::clone(instance));
674 }
675 let instance: Rc<dyn Target> = Rc::new(spi::HyperdebugSpiTarget::open(
676 &self.inner,
677 &self.spi_interface,
678 enable_cmd,
679 idx,
680 self.get_cmsis_google_capabilities()? & Self::GOOGLE_CAP_TPM_POLL != 0,
681 )?);
682 self.cached_io_interfaces
683 .spis
684 .borrow_mut()
685 .insert(idx, Rc::clone(&instance));
686 Ok(instance)
687 }
688
689 fn i2c(&self, name: &str) -> Result<Rc<dyn Bus>> {
691 if let Some(instance) = self.cached_io_interfaces.i2cs_by_name.borrow().get(name) {
692 return Ok(Rc::clone(instance));
693 }
694 let (idx, mode) = T::i2c_index(&self.inner, name)?;
695 if let Some(instance) = self.cached_io_interfaces.i2cs_by_index.borrow().get(&idx) {
696 self.cached_io_interfaces
697 .i2cs_by_name
698 .borrow_mut()
699 .insert(name.to_string(), Rc::clone(instance));
700 return Ok(Rc::clone(instance));
701 }
702 let cmsis_google_capabilities = self.get_cmsis_google_capabilities()?;
703 let instance: Rc<dyn Bus> = Rc::new(
704 match (
705 cmsis_google_capabilities & Self::GOOGLE_CAP_I2C != 0,
706 self.cmsis_interface.as_ref(),
707 self.i2c_interface.as_ref(),
708 ) {
709 (true, Some(cmsis_interface), _) => i2c::HyperdebugI2cBus::open(
710 &self.inner,
711 cmsis_interface,
712 true, cmsis_google_capabilities & Self::GOOGLE_CAP_I2C_DEVICE != 0,
714 idx,
715 mode,
716 )?,
717 (_, _, Some(i2c_interface)) => i2c::HyperdebugI2cBus::open(
718 &self.inner,
719 i2c_interface,
720 false, false, idx,
723 mode,
724 )?,
725 _ => bail!(TransportError::InvalidInstance(
726 TransportInterfaceType::I2c,
727 name.to_string()
728 )),
729 },
730 );
731 self.cached_io_interfaces
732 .i2cs_by_index
733 .borrow_mut()
734 .insert(idx, Rc::clone(&instance));
735 self.cached_io_interfaces
736 .i2cs_by_name
737 .borrow_mut()
738 .insert(name.to_string(), Rc::clone(&instance));
739 Ok(instance)
740 }
741
742 fn uart(&self, instance: &str) -> Result<Rc<dyn Uart>> {
744 match self.uart_interfaces.get(instance) {
745 Some(uart_interface) => {
746 if let Some(instance) = self
747 .cached_io_interfaces
748 .uarts
749 .borrow()
750 .get(&uart_interface.tty)
751 {
752 return Ok(Rc::clone(instance));
753 }
754 let supports_clearing_queues =
755 self.get_cmsis_google_capabilities()? & Self::GOOGLE_CAP_UART_QUEUE_CLEAR != 0;
756 let instance: Rc<dyn Uart> = Rc::new(uart::HyperdebugUart::open(
757 &self.inner,
758 uart_interface,
759 supports_clearing_queues,
760 )?);
761 self.cached_io_interfaces
762 .uarts
763 .borrow_mut()
764 .insert(uart_interface.tty.clone(), Rc::clone(&instance));
765 Ok(instance)
766 }
767 _ => Err(TransportError::InvalidInstance(
768 TransportInterfaceType::Uart,
769 instance.to_string(),
770 )
771 .into()),
772 }
773 }
774
775 fn gpio_pin(&self, pinname: &str) -> Result<Rc<dyn GpioPin>> {
777 Ok(
778 match self
779 .cached_io_interfaces
780 .gpio
781 .borrow_mut()
782 .entry(pinname.to_string())
783 {
784 Entry::Vacant(v) => {
785 let u = v.insert(T::gpio_pin(&self.inner, pinname)?);
786 Rc::clone(u)
787 }
788 Entry::Occupied(o) => Rc::clone(o.get()),
789 },
790 )
791 }
792
793 fn gpio_monitoring(&self) -> Result<Rc<dyn GpioMonitoring>> {
795 if self.get_cmsis_google_capabilities()? & Self::GOOGLE_CAP_GPIO_MONITORING != 0 {
798 Ok(Rc::new(gpio::HyperdebugGpioMonitoring::open(
799 &self.inner,
800 self.cmsis_interface,
801 )?))
802 } else {
803 Ok(Rc::new(gpio::HyperdebugGpioMonitoring::open(
807 &self.inner,
808 None,
809 )?))
810 }
811 }
812
813 fn gpio_bitbanging(&self) -> Result<Rc<dyn GpioBitbanging>> {
814 ensure!(
815 self.get_cmsis_google_capabilities()? & Self::GOOGLE_CAP_GPIO_BITBANGING != 0,
816 TransportError::InvalidInterface(TransportInterfaceType::GpioBitbanging),
817 );
818 let Some(cmsis_interface) = self.cmsis_interface else {
821 bail!(TransportError::InvalidInterface(
822 TransportInterfaceType::GpioBitbanging
823 ));
824 };
825 Ok(Rc::new(gpio::HyperdebugGpioBitbanging::open(
826 &self.inner,
827 cmsis_interface,
828 )?))
829 }
830
831 fn dispatch(&self, action: &dyn Any) -> Result<Option<Box<dyn Annotate>>> {
832 if let Some(update_firmware_action) = action.downcast_ref::<UpdateFirmware>() {
833 let usb_vid = self.inner.usb_device.borrow().get_vendor_id();
834 let usb_pid = self.inner.usb_device.borrow().get_product_id();
835 dfu::update_firmware(
836 &mut self.inner.usb_device.borrow_mut(),
837 self.current_firmware_version.as_deref(),
838 &update_firmware_action.firmware,
839 update_firmware_action.progress.as_ref(),
840 update_firmware_action.force,
841 usb_vid,
842 usb_pid,
843 )
844 } else if let Some(jtag_set_pins) = action.downcast_ref::<SetJtagPins>() {
845 match (
846 &jtag_set_pins.tclk,
847 &jtag_set_pins.tms,
848 &jtag_set_pins.tdi,
849 &jtag_set_pins.tdo,
850 &jtag_set_pins.trst,
851 ) {
852 (Some(tclk), Some(tms), Some(tdi), Some(tdo), Some(trst)) => {
853 self.inner.cmd_no_output(&format!(
854 "jtag set-pins {} {} {} {} {}",
855 tclk.get_internal_pin_name()
856 .ok_or(TransportError::InvalidOperation)?,
857 tms.get_internal_pin_name()
858 .ok_or(TransportError::InvalidOperation)?,
859 tdi.get_internal_pin_name()
860 .ok_or(TransportError::InvalidOperation)?,
861 tdo.get_internal_pin_name()
862 .ok_or(TransportError::InvalidOperation)?,
863 trst.get_internal_pin_name()
864 .ok_or(TransportError::InvalidOperation)?,
865 ))?;
866 Ok(None)
867 }
868 _ => Err(TransportError::UnsupportedOperation.into()),
869 }
870 } else if let Some(fpga_program) = action.downcast_ref::<FpgaProgram>() {
871 T::load_bitstream(fpga_program).map(|_| None)
872 } else if let Some(clear) = action.downcast_ref::<ClearBitstream>() {
873 T::clear_bitstream(clear).map(|_| None)
874 } else {
875 Err(TransportError::UnsupportedOperation.into())
876 }
877 }
878
879 fn jtag(&self, opts: &JtagParams) -> Result<Box<dyn JtagChain + '_>> {
880 ensure!(
881 self.cmsis_interface.is_some(),
882 TransportError::InvalidInterface(TransportInterfaceType::Jtag),
883 );
884 let usb_device = self.inner.usb_device.borrow();
887 let new_jtag = Box::new(OpenOcdJtagChain::new(
888 &format!(
889 "{}; cmsis_dap_vid_pid 0x{:04x} 0x{:04x}; adapter serial \"{}\";",
890 include_str!(env!("openocd_cmsis_dap_adapter_cfg")),
891 usb_device.get_vendor_id(),
892 usb_device.get_product_id(),
893 usb_device.get_serial_number(),
894 ),
895 opts,
896 )?);
897 Ok(new_jtag)
898 }
899
900 fn maintain_connection(&self) -> Result<Rc<dyn MaintainConnection>> {
907 Ok(self.inner.connect()?)
908 }
909}
910
911pub struct StandardFlavor;
913
914impl Flavor for StandardFlavor {
915 fn gpio_pin(inner: &Rc<Inner>, pinname: &str) -> Result<Rc<dyn GpioPin>> {
916 Ok(Rc::new(gpio::HyperdebugGpioPin::open(inner, pinname)?))
917 }
918
919 fn spi_index(inner: &Rc<Inner>, instance: &str) -> Result<(u8, u8)> {
920 match instance.parse() {
921 Err(_) => {
922 let mut buf = String::new();
925 let captures = inner
926 .cmd_one_line_output_match(
927 &format!("spi info {}", instance),
928 &SPI_REGEX,
929 &mut buf,
930 )
931 .map_err(|_| {
932 TransportError::InvalidInstance(
933 TransportInterfaceType::Spi,
934 instance.to_string(),
935 )
936 })?;
937 Ok((
938 spi::USB_SPI_REQ_ENABLE,
939 captures.get(1).unwrap().as_str().parse().unwrap(),
940 ))
941 }
942 Ok(n) => Ok((spi::USB_SPI_REQ_ENABLE, n)),
943 }
944 }
945
946 fn i2c_index(inner: &Rc<Inner>, instance: &str) -> Result<(u8, i2c::Mode)> {
947 let mut buf = String::new();
950 let captures = inner
951 .cmd_one_line_output_match(&format!("i2c info {}", instance), &SPI_REGEX, &mut buf)
952 .map_err(|_| {
953 TransportError::InvalidInstance(TransportInterfaceType::I2c, instance.to_string())
954 })?;
955 let mode = match captures.get(4) {
956 Some(c) if c.as_str().starts_with('d') => i2c::Mode::Device,
957 _ => i2c::Mode::Host,
958 };
959 Ok((captures.get(1).unwrap().as_str().parse().unwrap(), mode))
960 }
961
962 fn get_default_usb_vid() -> u16 {
963 VID_GOOGLE
964 }
965
966 fn get_default_usb_pid() -> u16 {
967 PID_HYPERDEBUG
968 }
969}
970
971pub struct ChipWhispererFlavor<B: Board> {
976 _phantom: PhantomData<B>,
977}
978
979impl<B: Board> Flavor for ChipWhispererFlavor<B> {
980 fn gpio_pin(inner: &Rc<Inner>, pinname: &str) -> Result<Rc<dyn GpioPin>> {
981 StandardFlavor::gpio_pin(inner, pinname)
982 }
983 fn spi_index(inner: &Rc<Inner>, instance: &str) -> Result<(u8, u8)> {
984 StandardFlavor::spi_index(inner, instance)
985 }
986 fn i2c_index(inner: &Rc<Inner>, instance: &str) -> Result<(u8, i2c::Mode)> {
987 StandardFlavor::i2c_index(inner, instance)
988 }
989 fn get_default_usb_vid() -> u16 {
990 StandardFlavor::get_default_usb_vid()
991 }
992 fn get_default_usb_pid() -> u16 {
993 StandardFlavor::get_default_usb_pid()
994 }
995 fn load_bitstream(fpga_program: &FpgaProgram) -> Result<()> {
996 let board = ChipWhisperer::<B>::new(None, None, None, &[])?;
999
1000 log::info!("Programming the FPGA bitstream.");
1002 let usb = board.device.borrow();
1003 usb.spi1_enable(false)?;
1004 usb.fpga_program(&fpga_program.bitstream, fpga_program.progress.as_ref())?;
1005 Ok(())
1006 }
1007 fn clear_bitstream(_clear: &ClearBitstream) -> Result<()> {
1008 let board = ChipWhisperer::<B>::new(None, None, None, &[])?;
1009 let usb = board.device.borrow();
1010 usb.spi1_enable(false)?;
1011 usb.clear_bitstream()?;
1012 Ok(())
1013 }
1014}
1015
1016static SPI_REGEX: LazyLock<Regex> =
1017 LazyLock::new(|| Regex::new("^ +([0-9]+) ([^ ]+) ([0-9]+) bps(?: ([hd])[^ ]*)?").unwrap());