opentitanlib/transport/hyperdebug/
gpio.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use std::io::Cursor;
6use std::mem::size_of;
7use std::rc::Rc;
8use std::sync::LazyLock;
9use std::time::Duration;
10
11use anyhow::{Result, bail, ensure};
12use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
13use regex::Regex;
14use zerocopy::FromBytes;
15
16use crate::io::gpio::{
17    BitbangEntry, ClockNature, DacBangEntry, Edge, GpioBitbangOperation, GpioBitbanging,
18    GpioDacBangOperation, GpioError, GpioMonitoring, GpioPin, MonitoringEvent,
19    MonitoringReadResponse, MonitoringStartResponse, PinMode, PullMode,
20};
21use crate::transport::TransportError;
22use crate::transport::hyperdebug::{BulkInterface, Inner};
23
24pub struct HyperdebugGpioPin {
25    inner: Rc<Inner>,
26    pinname: String,
27}
28
29impl HyperdebugGpioPin {
30    pub fn open(inner: &Rc<Inner>, pinname: &str) -> Result<Self> {
31        let result = Self {
32            inner: Rc::clone(inner),
33            pinname: pinname.to_string(),
34        };
35        Ok(result)
36    }
37}
38
39impl GpioPin for HyperdebugGpioPin {
40    /// Reads the value of the GPIO pin `id`.
41    fn read(&self) -> Result<bool> {
42        let line = self
43            .inner
44            .cmd_one_line_output(&format!("gpioget {}", &self.pinname))?;
45        Ok(line.trim_start().starts_with('1'))
46    }
47
48    /// Sets the value of the GPIO pin `id` to `value`.
49    fn write(&self, value: bool) -> Result<()> {
50        self.inner
51            .cmd_no_output(&format!("gpioset {} {}", &self.pinname, u32::from(value)))
52    }
53
54    fn set_mode(&self, mode: PinMode) -> Result<()> {
55        self.inner.cmd_no_output(&format!(
56            "gpiomode {} {}",
57            &self.pinname,
58            match mode {
59                PinMode::Input => "input",
60                PinMode::OpenDrain => "opendrain",
61                PinMode::PushPull => "pushpull",
62                PinMode::AnalogInput => "adc",
63                PinMode::AnalogOutput => "dac",
64                PinMode::Alternate => "alternate",
65            }
66        ))
67    }
68
69    fn set_pull_mode(&self, mode: PullMode) -> Result<()> {
70        self.inner.cmd_no_output(&format!(
71            "gpiopullmode {} {}",
72            &self.pinname,
73            match mode {
74                PullMode::None => "none",
75                PullMode::PullUp => "up",
76                PullMode::PullDown => "down",
77            }
78        ))
79    }
80
81    fn analog_read(&self) -> Result<f32> {
82        let line = self
83            .inner
84            .cmd_one_line_output(&format!("adc {}", &self.pinname))
85            .map_err(|_| TransportError::CommunicationError("No output from adc".to_string()))?;
86        static ADC_REGEX: LazyLock<Regex> =
87            LazyLock::new(|| Regex::new("^ +([^ ])+ = ([0-9]+) mV").unwrap());
88        if let Some(captures) = ADC_REGEX.captures(&line) {
89            let milli_volts: u32 = captures.get(2).unwrap().as_str().parse()?;
90            Ok(milli_volts as f32 / 1000.0)
91        } else {
92            Err(TransportError::CommunicationError("Unrecognized adc output".to_string()).into())
93        }
94    }
95
96    fn analog_write(&self, volts: f32) -> Result<()> {
97        if !(0.0..=3.3).contains(&volts) {
98            return Err(GpioError::UnsupportedPinVoltage(volts).into());
99        }
100        let milli_volts = (volts * 1000.0) as u32;
101        self.inner.cmd_no_output(&format!(
102            "gpio analog-set {} {}",
103            &self.pinname, milli_volts,
104        ))
105    }
106
107    fn set(
108        &self,
109        mode: Option<PinMode>,
110        value: Option<bool>,
111        pull: Option<PullMode>,
112        volts: Option<f32>,
113    ) -> Result<()> {
114        if let Some(v) = volts
115            && !(0.0..=3.3).contains(&v)
116        {
117            return Err(GpioError::UnsupportedPinVoltage(v).into());
118        }
119        self.inner.cmd_no_output(&format!(
120            "gpio multiset {} {} {} {} {}",
121            &self.pinname,
122            match value {
123                Some(false) => "0",
124                Some(true) => "1",
125                None => "-",
126            },
127            match mode {
128                Some(PinMode::Input) => "input",
129                Some(PinMode::OpenDrain) => "opendrain",
130                Some(PinMode::PushPull) => "pushpull",
131                Some(PinMode::AnalogInput) => "adc",
132                Some(PinMode::AnalogOutput) => "dac",
133                Some(PinMode::Alternate) => "alternate",
134                None => "-",
135            },
136            match pull {
137                Some(PullMode::None) => "none",
138                Some(PullMode::PullUp) => "up",
139                Some(PullMode::PullDown) => "down",
140                None => "-",
141            },
142            if let Some(v) = volts {
143                format!("{}", (v * 1000.0) as u32)
144            } else {
145                "-".to_string()
146            },
147        ))
148    }
149
150    fn get_internal_pin_name(&self) -> Option<&str> {
151        Some(&self.pinname)
152    }
153}
154
155const USB_MAX_SIZE: usize = 64;
156
157/// HyperDebug supports retreiving a transcript of events on a set of monitored GPIO pins either
158/// through its textual console, or for improved performance, through a vendor extension to the
159/// binary CMSIS-DAP endpoint.
160///
161/// This struct describes the binary header of the response (following the one-byte CMSIS-DAP
162/// response header), after which the transcript data will follow.  The protocol is designed to
163/// allow HyperDebug to pretty much dump the contents of its internal buffer to the USB interface.
164///
165/// The data part consists of a sequence of integers in leb128 encoding.  Each integer contains
166/// the index of the signal that changed in the low bits, and the number of microseconds since
167/// last event in the high bits.  The number of bits used for encoding the signal depends on how
168/// many signals are monitored.
169///
170/// The source for the HyperDebug firmware generating these responses is here:
171/// https://chromium.googlesource.com/chromiumos/platform/ec/+/refs/heads/main/board/hyperdebug/gpio.c
172#[derive(FromBytes, Debug)]
173#[repr(C)]
174struct RspGpioMonitoringHeader {
175    /// Size of the header as sent by HyperDebug (excluding one byte CMSIS-DAP header), will be at
176    /// least `size_of::<RspGpioMonitoringHeader>`, but future versions could add more header
177    /// fields.
178    struct_size: u16,
179    /// Status/error code, zero means success.
180    status: u16,
181    /// Bitfield containing the levels of the monitored signals as of the begining of the
182    /// transcript about to be sent, starting from the lest significant bit.
183    start_levels: u16,
184    /// Number of data bytes following this header.
185    transcript_size: u16,
186    /// Timestamp when the monitoring was originally started (will be the same in subsequent
187    /// responses).
188    start_timestamp: u64,
189    /// Timestamp when the current transcript ends (will be different in subsequenct responses).
190    end_timestamp: u64,
191}
192
193pub struct HyperdebugGpioMonitoring {
194    inner: Rc<Inner>,
195    cmsis_interface: Option<BulkInterface>,
196}
197
198impl HyperdebugGpioMonitoring {
199    /// CMSIS extension for HyperDebug GPIO.
200    const CMSIS_DAP_CUSTOM_COMMAND_GPIO: u8 = 0x83;
201
202    /// Sub-command for reading list of GPIO edge events
203    const GPIO_MONITORING_READ: u8 = 0x00;
204
205    // Some of the possible values for RspGpioMonitoringHeader.status
206    const MON_SUCCESS: u16 = 0;
207    const MON_BUFFER_OVERRUN: u16 = 5;
208
209    pub fn open(inner: &Rc<Inner>, cmsis_interface: Option<BulkInterface>) -> Result<Self> {
210        Ok(Self {
211            inner: Rc::clone(inner),
212            cmsis_interface,
213        })
214    }
215}
216
217impl GpioMonitoring for HyperdebugGpioMonitoring {
218    fn get_clock_nature(&self) -> Result<ClockNature> {
219        Ok(ClockNature::Wallclock {
220            resolution: 1_000_000,
221            offset: None,
222        })
223    }
224
225    /// Set up edge trigger detection on the given set of pins, transport will buffer the list
226    /// internally.
227    fn monitoring_start(&self, pins: &[&dyn GpioPin]) -> Result<MonitoringStartResponse> {
228        let mut pin_names = Vec::new();
229        for pin in pins {
230            pin_names.push(
231                pin.get_internal_pin_name()
232                    .ok_or(TransportError::InvalidOperation)?,
233            );
234        }
235        static START_TIME_REGEX: LazyLock<Regex> =
236            LazyLock::new(|| Regex::new("^ +@([0-9]+)").unwrap());
237        static SIGNAL_REGEX: LazyLock<Regex> =
238            LazyLock::new(|| Regex::new("^ +([0-9]+) ([^ ])+ ([01])").unwrap());
239        let mut start_time: u64 = 0;
240        let mut signals = Vec::new();
241        let mut unexpected_output = false;
242        self.inner.execute_command(
243            &format!("gpio monitoring start {}", pin_names.join(" ")),
244            |line| {
245                if let Some(captures) = START_TIME_REGEX.captures(line) {
246                    start_time = captures.get(1).unwrap().as_str().parse().unwrap();
247                } else if let Some(captures) = SIGNAL_REGEX.captures(line) {
248                    signals.push(captures.get(3).unwrap().as_str() != "0");
249                } else {
250                    unexpected_output = true;
251                    log::error!("Unexpected HyperDebug output: {}\n", line);
252                };
253            },
254        )?;
255        if unexpected_output {
256            bail!(TransportError::CommunicationError(
257                "Unrecognized response".to_string()
258            ))
259        }
260        Ok(MonitoringStartResponse {
261            timestamp: start_time,
262            initial_levels: signals,
263        })
264    }
265
266    /// Retrieve list of events detected thus far, optionally stopping the possibly expensive edge
267    /// detection.  Buffer overrun will be reported as an `Err`, and result in the stopping of the
268    /// edge detection irrespective of the parameter value.
269    fn monitoring_read(
270        &self,
271        pins: &[&dyn GpioPin],
272        continue_monitoring: bool,
273    ) -> Result<MonitoringReadResponse> {
274        let mut pin_names = Vec::new();
275        for pin in pins {
276            pin_names.push(
277                pin.get_internal_pin_name()
278                    .ok_or(TransportError::InvalidOperation)?,
279            );
280        }
281
282        if let Some(cmsis_interface) = self.cmsis_interface {
283            // HyperDebug firmware supports binary protocol for retrieving list of events, use
284            // that for greatly improved performance.
285
286            let mut pkt = Vec::<u8>::new();
287            pkt.write_u8(Self::CMSIS_DAP_CUSTOM_COMMAND_GPIO)?;
288            pkt.write_u8(Self::GPIO_MONITORING_READ)?;
289            pkt.write_u8(pin_names.len().try_into()?)?;
290            for pin_name in &pin_names {
291                pkt.write_u8(pin_name.len().try_into()?)?;
292                pkt.extend_from_slice(pin_name.as_bytes());
293            }
294            self.inner
295                .usb_device
296                .borrow()
297                .write_bulk(cmsis_interface.out_endpoint, &pkt)?;
298
299            let mut databytes: Vec<u8> =
300                vec![0u8; 1 + size_of::<RspGpioMonitoringHeader>() + USB_MAX_SIZE];
301            let mut bytecount = 0;
302
303            while bytecount < 1 + size_of::<RspGpioMonitoringHeader>() {
304                let read_count = self.inner.usb_device.borrow().read_bulk(
305                    cmsis_interface.in_endpoint,
306                    &mut databytes[bytecount..][..USB_MAX_SIZE],
307                )?;
308                ensure!(
309                    read_count > 0,
310                    TransportError::CommunicationError("Truncated GPIO response".to_string())
311                );
312                bytecount += read_count;
313            }
314            ensure!(
315                databytes[0] == Self::CMSIS_DAP_CUSTOM_COMMAND_GPIO,
316                TransportError::CommunicationError(
317                    "Unrecognized CMSIS-DAP response to GPIO request".to_string()
318                )
319            );
320            let resp: RspGpioMonitoringHeader =
321                FromBytes::read_from_prefix(&databytes[1..]).unwrap().0;
322            ensure!(
323                resp.struct_size as usize >= size_of::<RspGpioMonitoringHeader>(),
324                TransportError::CommunicationError(
325                    "Short CMSIS-DAP response to GPIO request".to_string()
326                )
327            );
328            let header_bytes = resp.struct_size as usize + 1;
329            databytes.resize(header_bytes + resp.transcript_size as usize, 0u8);
330
331            while bytecount < databytes.len() {
332                let c = self
333                    .inner
334                    .usb_device
335                    .borrow()
336                    .read_bulk(cmsis_interface.in_endpoint, &mut databytes[bytecount..])?;
337                bytecount += c;
338            }
339
340            match resp.status {
341                Self::MON_SUCCESS => (),
342                Self::MON_BUFFER_OVERRUN => bail!(TransportError::CommunicationError(
343                    "HyperDebug GPIO monitoring buffer overrun".to_string()
344                )),
345                n => bail!(TransportError::CommunicationError(format!(
346                    "Unexpected HyperDebug GPIO error: {}",
347                    n
348                ))),
349            }
350
351            // Figure out how many of the low bits are used for storing the index of the signal
352            // hanving changed. (If only one signal, no bits are used, if two signals, then one
353            // bit is used, if three or four, then two bits are used, etc.)
354            let signal_bits = 32 - (pin_names.len() as u32 - 1).leading_zeros();
355            let signal_mask = (1u64 << signal_bits) - 1;
356
357            let mut cur_time: u64 = resp.start_timestamp;
358            let mut cur_levels = resp.start_levels;
359            let mut events = Vec::new();
360            let mut idx = header_bytes;
361
362            // Now decode the list of events, each consisting of a variable legnth encoded 64-bit
363            // integer.
364            while idx < databytes.len() {
365                let value = decode_leb128(&mut idx, &databytes)?;
366
367                // The 64-bit value consists of two parts, the lower `signal_bits` bits indicate
368                // which signal had an edge, the upper bits indicate the number of microseconds
369                // since the previous event (on any signal, not necessarily on that same one).
370                cur_time += value >> signal_bits;
371                let signal_index = (value & signal_mask) as u8;
372                cur_levels ^= 1 << signal_index;
373                events.push(MonitoringEvent {
374                    signal_index,
375                    edge: if cur_levels & (1 << signal_index) == 0 {
376                        Edge::Falling
377                    } else {
378                        Edge::Rising
379                    },
380                    timestamp: cur_time,
381                });
382            }
383
384            if !continue_monitoring {
385                self.inner
386                    .cmd_no_output(&format!("gpio monitoring stop {}", pin_names.join(" ")))?;
387            }
388            return Ok(MonitoringReadResponse {
389                events,
390                timestamp: resp.end_timestamp,
391            });
392        }
393
394        static START_TIME_REGEX: LazyLock<Regex> =
395            LazyLock::new(|| Regex::new("^ +@([0-9]+)").unwrap());
396        static EDGE_REGEX: LazyLock<Regex> =
397            LazyLock::new(|| Regex::new("^ +([0-9]+) (-?[0-9]+) ([RF])").unwrap());
398        let mut reference_time: u64 = 0;
399        let mut events = Vec::new();
400        loop {
401            let mut more_data = false;
402            let mut buffer_overrun = false;
403            let mut unexpected_output = false;
404            self.inner.execute_command(
405                &format!("gpio monitoring read {}", pin_names.join(" ")),
406                |line| {
407                    if let Some(captures) = START_TIME_REGEX.captures(line) {
408                        reference_time = captures.get(1).unwrap().as_str().parse().unwrap();
409                    } else if let Some(captures) = EDGE_REGEX.captures(line) {
410                        events.push(MonitoringEvent {
411                            signal_index: captures.get(1).unwrap().as_str().parse().unwrap(),
412                            edge: if captures.get(3).unwrap().as_str() == "R" {
413                                Edge::Rising
414                            } else {
415                                Edge::Falling
416                            },
417                            timestamp: (reference_time as i64
418                                + captures.get(2).unwrap().as_str().parse::<i64>().unwrap())
419                                as u64,
420                        });
421                    } else if line == "Warning: more data" {
422                        more_data = true;
423                    } else if line == "Error: Buffer overrun" {
424                        buffer_overrun = true;
425                    } else {
426                        unexpected_output = true;
427                        log::error!("Unexpected HyperDebug output: {}\n", line);
428                    }
429                },
430            )?;
431            if unexpected_output {
432                bail!(TransportError::CommunicationError(
433                    "Unrecognized response".to_string()
434                ))
435            }
436            if buffer_overrun {
437                bail!(TransportError::CommunicationError(
438                    "HyperDebug GPIO monitoring buffer overrun".to_string()
439                ))
440            }
441            if !more_data {
442                break;
443            }
444        }
445        if !continue_monitoring {
446            self.inner
447                .cmd_no_output(&format!("gpio monitoring stop {}", pin_names.join(" ")))?;
448        }
449        Ok(MonitoringReadResponse {
450            events,
451            timestamp: reference_time,
452        })
453    }
454}
455
456/// Read 7 bits from each byte, least significant byte first.  High bit of one indicates more
457/// bytes belong to the same value.
458fn decode_leb128(idx: &mut usize, databytes: &[u8]) -> Result<u64> {
459    let mut i = *idx;
460    let mut value = 0u64;
461    let mut shift = 0;
462    while i < databytes.len() {
463        let byte = databytes[i];
464        value |= ((byte & 0x7F) as u64) << shift;
465        shift += 7;
466        i += 1;
467        if (byte & 0x80) == 0 {
468            *idx = i;
469            return Ok(value);
470        }
471        if shift + 7 > 64 {
472            // Too many bytes in encoding of a single integer, could overflow 64 bit unsigned.
473            bail!(TransportError::CommunicationError(
474                "Corrupt data from HyperDebug GPIO monitoring".to_string(),
475            ));
476        }
477    }
478    // End of stream "in the middle" of a multi-byte integer encoding.
479    bail!(TransportError::CommunicationError(
480        "Corrupt data from HyperDebug GPIO monitoring".to_string(),
481    ));
482}
483
484pub struct HyperdebugGpioBitbanging {
485    inner: Rc<Inner>,
486    cmsis_interface: BulkInterface,
487}
488
489impl HyperdebugGpioBitbanging {
490    pub fn open(inner: &Rc<Inner>, cmsis_interface: BulkInterface) -> Result<Self> {
491        // Exclusively claim CMSIS-DAP interface, preparing for bulk transfers.
492        inner
493            .usb_device
494            .borrow_mut()
495            .claim_interface(cmsis_interface.interface)?;
496        Ok(Self {
497            inner: Rc::clone(inner),
498            cmsis_interface,
499        })
500    }
501}
502
503struct DacEncoder {
504    /// Conversion factor from volts to 12-bit unsigned DAC value.
505    factor: f32,
506}
507
508impl DacEncoder {
509    fn encode_dac_sample(&self, out: &mut Vec<u8>, voltage: f32) {
510        let count = voltage * self.factor;
511        let count: u16 = if count <= 0.0 {
512            0
513        } else if count >= 4095.0 {
514            4095
515        } else {
516            count as u16
517        };
518        out.push((count >> 8) as u8);
519        out.push((count & 0xFF) as u8);
520    }
521}
522
523static DAC_BANG_REGEX: LazyLock<Regex> =
524    LazyLock::new(|| Regex::new("^Calibration: ([0-9]+) ([0-9]+)").unwrap());
525
526impl GpioBitbanging for HyperdebugGpioBitbanging {
527    fn start<'a>(
528        &self,
529        pins: &[&dyn GpioPin],
530        clock_tick: Duration,
531        waveform: Box<[BitbangEntry<'a, 'a>]>,
532    ) -> Result<Box<dyn GpioBitbangOperation<'a, 'a> + 'a>> {
533        Ok(Box::new(HyperdebugGpioBitbangOperation::new(
534            Rc::clone(&self.inner),
535            self.cmsis_interface,
536            pins,
537            clock_tick,
538            waveform,
539        )?))
540    }
541
542    fn dac_start(
543        &self,
544        pins: &[&dyn GpioPin],
545        clock_tick: Duration,
546        waveform: Box<[DacBangEntry]>,
547    ) -> Result<Box<dyn GpioDacBangOperation>> {
548        Ok(Box::new(HyperdebugGpioDacBangOperation::new(
549            Rc::clone(&self.inner),
550            self.cmsis_interface,
551            pins,
552            clock_tick,
553            &waveform,
554        )?))
555    }
556}
557
558/// Represents a two-way streaming binary data transfer operation, as is used for bit-banging, or
559/// for outputting arbitrary waveforms via a DAC.
560pub struct HyperdebugDataOperation {
561    inner: Rc<Inner>,
562    cmsis_interface: BulkInterface,
563    encoded_waveform: Vec<u8>,
564    free_bytes: usize,
565    out_ptr: usize,
566    in_ptr: usize,
567}
568
569impl HyperdebugDataOperation {
570    /// CMSIS extension for HyperDebug GPIO.
571    const CMSIS_DAP_CUSTOM_COMMAND_GPIO: u8 = 0x83;
572
573    /// Sub-command for HyperDebug GPIO bitbanging.
574    const GPIO_BITBANG: u8 = 0x10;
575    const GPIO_BITBANG_STREAMING: u8 = 0x11;
576
577    /// Device status (whether there is an ongoing bitbang operation)
578    const STATUS_BITBANG_IDLE: u8 = 0x00;
579    const STATUS_BITBANG_ONGOING: u8 = 0x01;
580    const STATUS_BITBANG_ERROR_WAVEFORM: u8 = 0x80;
581
582    fn new(
583        inner: Rc<Inner>,
584        cmsis_interface: BulkInterface,
585        encoded_waveform: Vec<u8>,
586    ) -> Result<HyperdebugDataOperation> {
587        // Send an initial request, to ask how much buffer space HyperDebug has, so that we can
588        // fill the buffer, while avoiding overflows.
589        let free_bytes: usize = {
590            let usb = inner.usb_device.borrow();
591            let mut pkt = Vec::<u8>::new();
592            pkt.write_u8(Self::CMSIS_DAP_CUSTOM_COMMAND_GPIO)?;
593            pkt.write_u8(Self::GPIO_BITBANG)?;
594            pkt.write_u16::<LittleEndian>(0)?;
595            usb.write_bulk(cmsis_interface.out_endpoint, &pkt)?;
596
597            let mut databytes = [0u8; 64];
598
599            let c = usb.read_bulk(cmsis_interface.in_endpoint, &mut databytes)?;
600            let mut rdr = Cursor::new(&databytes[..c]);
601            ensure!(
602                rdr.read_u8()? == Self::CMSIS_DAP_CUSTOM_COMMAND_GPIO,
603                TransportError::CommunicationError(
604                    "Incorrect CMSIS-DAP header in response to GPIO request".to_string()
605                )
606            );
607            ensure!(
608                rdr.read_u8()? == Self::STATUS_BITBANG_IDLE,
609                TransportError::CommunicationError(
610                    "HyperDebug not responding correctly".to_string()
611                )
612            );
613            let free_bytes = rdr.read_u16::<LittleEndian>()?;
614            free_bytes as usize
615        };
616        Ok(Self {
617            inner,
618            cmsis_interface,
619            encoded_waveform,
620            free_bytes,
621            out_ptr: 0,
622            in_ptr: 0,
623        })
624    }
625
626    fn query(&mut self) -> Result<bool> {
627        if self.in_ptr >= self.encoded_waveform.len() {
628            return Ok(true);
629        }
630
631        let usb = self.inner.usb_device.borrow();
632        let chunk_size = std::cmp::min(self.encoded_waveform.len() - self.out_ptr, self.free_bytes);
633
634        let mut pkt = Vec::<u8>::new();
635        pkt.write_u8(Self::CMSIS_DAP_CUSTOM_COMMAND_GPIO)?;
636        if self.out_ptr + chunk_size < self.encoded_waveform.len() {
637            // We prefer partial response, in order to be able to fill up buffer before
638            // HyperDebug runs out of data to clock out.
639            pkt.write_u8(Self::GPIO_BITBANG_STREAMING)?;
640        } else {
641            // We want response only after every byte is transmitted
642            pkt.write_u8(Self::GPIO_BITBANG)?;
643        }
644        pkt.write_u16::<LittleEndian>(chunk_size as u16)?;
645        pkt.extend_from_slice(&self.encoded_waveform[self.out_ptr..self.out_ptr + chunk_size]);
646        usb.write_bulk(self.cmsis_interface.out_endpoint, &pkt)?;
647
648        let mut databytes = [0u8; 64];
649
650        let c = usb.read_bulk(self.cmsis_interface.in_endpoint, &mut databytes)?;
651        let mut rdr = Cursor::new(&databytes[..c]);
652        ensure!(
653            rdr.read_u8()? == Self::CMSIS_DAP_CUSTOM_COMMAND_GPIO,
654            TransportError::CommunicationError(
655                "Incorrect CMSIS-DAP header in response to GPIO request".to_string()
656            )
657        );
658        match rdr.read_u8()? {
659            Self::STATUS_BITBANG_ONGOING => (),
660            Self::STATUS_BITBANG_IDLE => bail!(TransportError::CommunicationError(
661                "GPIO request aborted".to_string()
662            )),
663            Self::STATUS_BITBANG_ERROR_WAVEFORM => bail!(TransportError::CommunicationError(
664                "HyperDebug reports encoding error".to_string()
665            )),
666            status => bail!(TransportError::CommunicationError(std::format!(
667                "Unrecognized status code: {}",
668                status
669            ))),
670        }
671
672        self.free_bytes = rdr.read_u16::<LittleEndian>()? as usize;
673        let response_size = rdr.read_u16::<LittleEndian>()? as usize;
674
675        let final_in_ptr = self.in_ptr + response_size;
676
677        // Copy any data in initial packet
678        let data_in_header = c - rdr.position() as usize;
679        self.encoded_waveform[self.in_ptr..self.in_ptr + data_in_header]
680            .copy_from_slice(&databytes[rdr.position() as usize..][..data_in_header]);
681        self.in_ptr += data_in_header;
682
683        while self.in_ptr < final_in_ptr {
684            self.in_ptr += usb.read_bulk(
685                self.cmsis_interface.in_endpoint,
686                &mut self.encoded_waveform[self.in_ptr..final_in_ptr],
687            )?;
688        }
689
690        self.out_ptr += chunk_size;
691
692        if self.in_ptr >= self.encoded_waveform.len() {
693            Ok(true)
694        } else {
695            Ok(false)
696        }
697    }
698}
699
700/// Represents an ongoing operation of bit-banging a number of GPIO pins.
701pub struct HyperdebugGpioBitbangOperation<'a> {
702    num_pins: usize,
703    waveform: Box<[BitbangEntry<'a, 'a>]>,
704    operation: HyperdebugDataOperation,
705}
706
707impl<'a> HyperdebugGpioBitbangOperation<'a> {
708    fn new(
709        inner: Rc<Inner>,
710        cmsis_interface: BulkInterface,
711        pins: &[&dyn GpioPin],
712        clock_tick: Duration,
713        waveform: Box<[BitbangEntry<'a, 'a>]>,
714    ) -> Result<Self> {
715        // Verify that `waveform` is valid, by converting into the binary representation to send
716        // to HyperDebug.
717        let encoded_waveform = encode_waveform(&waveform, pins.len())?;
718
719        // Tell HyperDebug about the set of pins to manipulate, and the clock speed, using the
720        // textual console protocol.
721        let mut pin_names = Vec::new();
722        for pin in pins {
723            pin_names.push(
724                pin.get_internal_pin_name()
725                    .ok_or(TransportError::InvalidOperation)?,
726            );
727        }
728        inner.cmd_no_output(&format!(
729            "gpio bit-bang {} {}",
730            clock_tick.as_nanos(),
731            pin_names.join(" ")
732        ))?;
733
734        // Here is the main two-way transfer logic.  At each iteration we send a number of bytes,
735        // capped at what HyperDebug has most recently indicated was available.  In response we
736        // will receive some number of bytes of samples taken as the data was clocked out (similar
737        // to how FTDI synchronous bitbanging works).  HyperDebug will send a response when it has
738        // half of its buffer full of sampled data to send, or when some amount of time has passed
739        // (relevant for slow clock speeds).  With this scheme, the bit-banging of waveforms
740        // longer than what fits in HyperDebug memory can be produced, without HyperDebug ever
741        // needing to "stop the clock" waiting for more data.
742        Ok(Self {
743            num_pins: pins.len(),
744            waveform,
745            operation: HyperdebugDataOperation::new(inner, cmsis_interface, encoded_waveform)?,
746        })
747    }
748}
749
750impl<'a> GpioBitbangOperation<'a, 'a> for HyperdebugGpioBitbangOperation<'a> {
751    fn query(&mut self) -> Result<bool> {
752        if self.operation.query()? {
753            // Decode the binary representation from HyperDebug into any `BitbangEntry::Both()`
754            // entries in `waveform`, allowing the caller to inspect data sampled at bitbanging
755            // clock ticks (useful with open drain or pure input pins).
756            decode_waveform(
757                &mut self.waveform,
758                &self.operation.encoded_waveform,
759                self.num_pins,
760            )?;
761            Ok(true)
762        } else {
763            Ok(false)
764        }
765    }
766
767    fn get_result(self: Box<Self>) -> Result<Box<[BitbangEntry<'a, 'a>]>> {
768        Ok(self.waveform)
769    }
770}
771
772/// Represents an ongoing operation of dac-banging a number of GPIO pins.  Since there is no
773/// incoming data to decode, this struct unlike the corresponding bit-banging one, does not need
774/// to maintain any additional records, besides the encoded binary data to be streamed to
775/// HyperDebug, in the `operation` field.
776pub struct HyperdebugGpioDacBangOperation {
777    operation: HyperdebugDataOperation,
778}
779
780impl HyperdebugGpioDacBangOperation {
781    fn new(
782        inner: Rc<Inner>,
783        cmsis_interface: BulkInterface,
784        pins: &[&dyn GpioPin],
785        clock_tick: Duration,
786        waveform: &[DacBangEntry],
787    ) -> Result<Self> {
788        // Tell HyperDebug about the set of pins to manipulate, and the clock speed, using the
789        // textual console protocol.
790        let mut pin_names = Vec::new();
791        for pin in pins {
792            pin_names.push(
793                pin.get_internal_pin_name()
794                    .ok_or(TransportError::InvalidOperation)?,
795            );
796        }
797
798        let mut buf = String::new();
799        let captures = inner.cmd_one_line_output_match(
800            &format!(
801                "gpio dac-bang {} {}",
802                clock_tick.as_nanos(),
803                pin_names.join(" ")
804            ),
805            &DAC_BANG_REGEX,
806            &mut buf,
807        )?;
808        let multiplier: u32 = captures.get(1).unwrap().as_str().parse().unwrap();
809        let divisor: u32 = captures.get(2).unwrap().as_str().parse().unwrap();
810
811        // Set up how to "encode" a voltage as 12-bit DAC value, using calibration factors from
812        // HyperDebug.
813        let encoder = DacEncoder {
814            factor: 1000.0 * multiplier as f32 / divisor as f32,
815        };
816
817        let mut encoded_waveform: Vec<u8> = Vec::new();
818
819        let mut last: Vec<f32> = Vec::new();
820        last.resize(pins.len(), 0.0);
821        let mut delay = 0u32;
822        let mut linear = 0u32;
823        for a in waveform.iter() {
824            match a {
825                DacBangEntry::Write(d) => {
826                    ensure!(
827                        !d.is_empty() && d.len() % pins.len() == 0,
828                        GpioError::InvalidDacBangData
829                    );
830                    ensure!(delay == 0 || linear == 0, GpioError::InvalidDacBangDelay);
831                    if linear > 1 {
832                        // Linear transition from one voltage to another over a given time is
833                        // encoded as a list of explicit voltage at the selected clock rate.
834                        // (Clock rates above 50k samples per second may not be able to sustain
835                        // the data throughput, if the sequence does not fit in HyperDebug buffer,
836                        // and must be streamed.)
837                        for sample_no in 1..linear {
838                            for i in 0..pins.len() {
839                                let val =
840                                    last[i] + (d[i] - last[i]) * sample_no as f32 / linear as f32;
841                                encoder.encode_dac_sample(&mut encoded_waveform, val);
842                            }
843                        }
844                    }
845                    if delay > 1 {
846                        // Delays are encoded using one or more bytes with the MSB set to one.  Each
847                        // containing 7 bits of the delay value, with the least significant bits in
848                        // the first byte.
849                        let encoded_delay = delay - 1;
850                        let mut shift = 0;
851                        while (encoded_delay >> shift) != 0 {
852                            encoded_waveform.push(0x80 | ((encoded_delay >> shift) & 0x7F) as u8);
853                            shift += 7;
854                        }
855                    }
856                    for voltage in *d {
857                        encoder.encode_dac_sample(&mut encoded_waveform, *voltage);
858                    }
859                    for i in 0..pins.len() {
860                        last[i] = d[d.len() - pins.len() + i];
861                    }
862                    delay = 0;
863                    linear = 0;
864                }
865                DacBangEntry::WriteOwned(d) => {
866                    ensure!(
867                        !d.is_empty() && d.len() % pins.len() == 0,
868                        GpioError::InvalidDacBangData
869                    );
870                    ensure!(delay == 0 || linear == 0, GpioError::InvalidDacBangDelay);
871                    if linear > 1 {
872                        // Linear transition from one voltage to another over a given time is
873                        // encoded as a list of explicit voltage at the selected clock rate.
874                        // (Clock rates above 50k samples per second may not be able to sustain
875                        // the data throughput, if the sequence does not fit in HyperDebug buffer,
876                        // and must be streamed.)
877                        for sample_no in 1..linear {
878                            for i in 0..pins.len() {
879                                let val =
880                                    last[i] + (d[i] - last[i]) * sample_no as f32 / linear as f32;
881                                encoder.encode_dac_sample(&mut encoded_waveform, val);
882                            }
883                        }
884                    }
885                    if delay > 1 {
886                        // Delays are encoded using one or more bytes with the MSB set to one.  Each
887                        // containing 7 bits of the delay value, with the least significant bits in
888                        // the first byte.
889                        let encoded_delay = delay - 1;
890                        let mut shift = 0;
891                        while (encoded_delay >> shift) != 0 {
892                            encoded_waveform.push(0x80 | ((encoded_delay >> shift) & 0x7F) as u8);
893                            shift += 7;
894                        }
895                    }
896                    for voltage in d.iter() {
897                        encoder.encode_dac_sample(&mut encoded_waveform, *voltage);
898                    }
899                    for i in 0..pins.len() {
900                        last[i] = d[d.len() - pins.len() + i];
901                    }
902                    delay = 0;
903                    linear = 0;
904                }
905                DacBangEntry::Delay(0) => bail!(GpioError::InvalidDacBangDelay),
906                DacBangEntry::Delay(n @ 1..) => {
907                    delay += *n;
908                }
909                DacBangEntry::Linear(0) => bail!(GpioError::InvalidDacBangDelay),
910                DacBangEntry::Linear(n @ 1..) => {
911                    linear += *n;
912                }
913            }
914        }
915
916        // Do not allow Delay or Linear as the final entry
917        ensure!(delay == 0, GpioError::InvalidBitbangDelay);
918        ensure!(linear == 0, GpioError::InvalidBitbangDelay);
919
920        Ok(Self {
921            operation: HyperdebugDataOperation::new(inner, cmsis_interface, encoded_waveform)?,
922        })
923    }
924}
925
926impl GpioDacBangOperation for HyperdebugGpioDacBangOperation {
927    fn query(&mut self) -> Result<bool> {
928        self.operation.query()
929    }
930}
931
932/// Produce binary encoding of waveform and delays, which can be sent to HyperDebug.
933fn encode_waveform(waveform: &[BitbangEntry], num_pins: usize) -> Result<Vec<u8>> {
934    ensure!(
935        (1..=7).contains(&num_pins),
936        GpioError::UnsupportedNumberOfPins(num_pins)
937    );
938
939    let mut encoded_waveform = Vec::<u8>::new();
940
941    let mut delay = 0u32;
942    for entry in waveform {
943        match entry {
944            BitbangEntry::Write(wbuf) | BitbangEntry::Both(wbuf, _) => {
945                if delay > 1 {
946                    // Delays are encoded using one or more bytes with the MSB set to one.  Each
947                    // containing 7 bits of the delay value, with the least significant bits in
948                    // the first byte.
949                    let encoded_delay = delay - 1;
950                    let mut shift = 0;
951                    while (encoded_delay >> shift) != 0 {
952                        encoded_waveform.push(0x80 | ((encoded_delay >> shift) & 0x7F) as u8);
953                        shift += 7;
954                    }
955                }
956                // A sequence of samples using up to 7 of the lowest bits, with the MSB set to
957                // zero.
958                for byte in *wbuf {
959                    ensure!(
960                        (byte >> num_pins) == 0,
961                        GpioError::InvalidBitbangData(num_pins)
962                    );
963                }
964                encoded_waveform.extend_from_slice(wbuf);
965                delay = 0;
966            }
967            BitbangEntry::WriteOwned(wbuf) | BitbangEntry::BothOwned(wbuf) => {
968                if delay > 1 {
969                    // Delays are encoded using one or more bytes with the MSB set to one.  Each
970                    // containing 7 bits of the delay value, with the least significant bits in
971                    // the first byte.
972                    let encoded_delay = delay - 1;
973                    let mut shift = 0;
974                    while (encoded_delay >> shift) != 0 {
975                        encoded_waveform.push(0x80 | ((encoded_delay >> shift) & 0x7F) as u8);
976                        shift += 7;
977                    }
978                }
979                // A sequence of samples using up to 7 of the lowest bits, with the MSB set to
980                // zero.
981                for byte in wbuf.iter() {
982                    ensure!(
983                        (byte >> num_pins) == 0,
984                        GpioError::InvalidBitbangData(num_pins)
985                    );
986                }
987                encoded_waveform.extend_from_slice(wbuf);
988                delay = 0;
989            }
990            BitbangEntry::Delay(0) => bail!(GpioError::InvalidBitbangDelay),
991            BitbangEntry::Delay(n @ 1..) => {
992                delay += *n;
993            }
994            BitbangEntry::Await { mask, pattern } => {
995                ensure!(delay == 0, GpioError::InvalidBitbangDelay);
996                encoded_waveform.extend_from_slice(&[0x80, 0x80, *mask, *pattern]);
997                delay = 0;
998            }
999        }
1000    }
1001
1002    // Do not allow Delay as the final entry
1003    ensure!(delay == 0, GpioError::InvalidBitbangDelay);
1004
1005    Ok(encoded_waveform)
1006}
1007
1008/// Decode the binary representation from HyperDebug into any `BitbangEntry::Both()` entries in
1009/// `waveform`, allowing the caller to inspect data sampled at bitbanging clock ticks (useful with
1010/// open drain or pure input pins).
1011fn decode_waveform(
1012    waveform: &mut [BitbangEntry],
1013    encoded_response: &[u8],
1014    num_pins: usize,
1015) -> Result<()> {
1016    ensure!(
1017        (1..=7).contains(&num_pins),
1018        GpioError::UnsupportedNumberOfPins(num_pins)
1019    );
1020
1021    let mut index = 0usize;
1022    for entry in waveform {
1023        match entry {
1024            BitbangEntry::Write(wbuf) => {
1025                index += wbuf.len();
1026            }
1027            BitbangEntry::WriteOwned(wbuf) => {
1028                index += wbuf.len();
1029            }
1030            BitbangEntry::Both(wbuf, rbuf) => {
1031                ensure!(
1032                    rbuf.len() == wbuf.len(),
1033                    GpioError::MismatchedDataLength(wbuf.len(), rbuf.len())
1034                );
1035                rbuf.copy_from_slice(&encoded_response[index..][..rbuf.len()]);
1036                index += wbuf.len();
1037            }
1038            BitbangEntry::BothOwned(rbuf) => {
1039                rbuf.copy_from_slice(&encoded_response[index..][..rbuf.len()]);
1040                index += rbuf.len();
1041            }
1042            BitbangEntry::Delay(_) => {
1043                while encoded_response[index] & 0x80 != 0 {
1044                    index += 1;
1045                }
1046            }
1047            BitbangEntry::Await { .. } => {
1048                index += 4;
1049            }
1050        }
1051    }
1052
1053    assert!(index == encoded_response.len());
1054
1055    Ok(())
1056}
1057
1058#[cfg(test)]
1059mod tests {
1060    use super::*;
1061
1062    #[test]
1063    fn test_encode_waveforms() {
1064        let encoding = encode_waveform(
1065            &[
1066                BitbangEntry::Write(&[0, 1, 0, 1]),
1067                BitbangEntry::Delay(0x0101),
1068                BitbangEntry::Write(&[0, 1, 0, 1]),
1069            ],
1070            1,
1071        )
1072        .unwrap();
1073
1074        assert_eq!(
1075            encoding,
1076            [0x00, 0x01, 0x00, 0x01, 0x80, 0x82, 0x00, 0x01, 0x00, 0x01]
1077        );
1078    }
1079}