ftdi_mpsse/
lib.rs

1//! Multi-protocol synchronous serial engine utilities for FTDI devices.
2#![deny(unsafe_code)]
3
4use std::time::Duration;
5
6/// MPSSE opcodes.
7///
8/// Exported for use by [`mpsse`] macro. May also be used for manual command array construction.
9///
10/// Data clocking MPSSE commands are broken out into separate enums for API ergonomics:
11/// * [`ClockDataOut`]
12/// * [`ClockBitsOut`]
13/// * [`ClockDataIn`]
14/// * [`ClockBitsIn`]
15/// * [`ClockData`]
16#[derive(Debug, Copy, Clone, Eq, PartialEq)]
17#[repr(u8)]
18#[non_exhaustive]
19pub enum MpsseCmd {
20    /// Used by [`set_gpio_lower`][`MpsseCmdBuilder::set_gpio_lower`].
21    SetDataBitsLowbyte = 0x80,
22    /// Used by [`gpio_lower`][`MpsseCmdBuilder::gpio_lower`].
23    GetDataBitsLowbyte = 0x81,
24    /// Used by [`set_gpio_upper`][`MpsseCmdBuilder::set_gpio_upper`].
25    SetDataBitsHighbyte = 0x82,
26    /// Used by [`gpio_upper`][`MpsseCmdBuilder::gpio_upper`].
27    GetDataBitsHighbyte = 0x83,
28    /// Used by [`enable_loopback`][`MpsseCmdBuilder::enable_loopback`].
29    EnableLoopback = 0x84,
30    /// Used by [`disable_loopback`][`MpsseCmdBuilder::disable_loopback`].
31    DisableLoopback = 0x85,
32    /// Used by [`set_clock`][`MpsseCmdBuilder::set_clock`].
33    SetClockFrequency = 0x86,
34    /// Used by [`send_immediate`][`MpsseCmdBuilder::send_immediate`].
35    SendImmediate = 0x87,
36    /// Used by [`wait_on_io_high`][`MpsseCmdBuilder::wait_on_io_high`].
37    WaitOnIOHigh = 0x88,
38    /// Used by [`wait_on_io_low`][`MpsseCmdBuilder::wait_on_io_low`].
39    WaitOnIOLow = 0x89,
40    /// Used by [`set_clock`][`MpsseCmdBuilder::set_clock`].
41    DisableClockDivide = 0x8A,
42    /// Used by [`set_clock`][`MpsseCmdBuilder::set_clock`].
43    EnableClockDivide = 0x8B,
44    /// Used by [`enable_3phase_data_clocking`][`MpsseCmdBuilder::enable_3phase_data_clocking`].
45    Enable3PhaseClocking = 0x8C,
46    /// Used by [`disable_3phase_data_clocking`][`MpsseCmdBuilder::disable_3phase_data_clocking`].
47    Disable3PhaseClocking = 0x8D,
48    /// Used by [`disable_adaptive_data_clocking`][`MpsseCmdBuilder::disable_adaptive_data_clocking`].
49    EnableAdaptiveClocking = 0x96,
50    /// Used by [`enable_adaptive_data_clocking`][`MpsseCmdBuilder::enable_adaptive_data_clocking`].
51    DisableAdaptiveClocking = 0x97,
52    // EnableDriveOnlyZero = 0x9E,
53}
54
55/// Modes for clocking data out of the FTDI device.
56///
57/// This is an argument to the [`clock_data_out`] method.
58///
59/// [`clock_data_out`]: MpsseCmdBuilder::clock_data_out
60#[repr(u8)]
61#[derive(Debug, Copy, Clone, Eq, PartialEq)]
62pub enum ClockDataOut {
63    /// Positive clock edge MSB first.
64    ///
65    /// The data is sent MSB first.
66    ///
67    /// The data will change to the next bit on the rising edge of the CLK pin.
68    MsbPos = 0x10,
69    /// Negative clock edge MSB first.
70    ///
71    /// The data is sent MSB first.
72    ///
73    /// The data will change to the next bit on the falling edge of the CLK pin.
74    MsbNeg = 0x11,
75    /// Positive clock edge LSB first.
76    ///
77    /// The first bit in will be the LSB of the first byte and so on.
78    ///
79    /// The data will change to the next bit on the rising edge of the CLK pin.
80    LsbPos = 0x18,
81    /// Negative clock edge LSB first.
82    ///
83    /// The first bit in will be the LSB of the first byte and so on.
84    ///
85    /// The data will change to the next bit on the falling edge of the CLK pin.
86    LsbNeg = 0x19,
87}
88
89impl From<ClockDataOut> for u8 {
90    fn from(value: ClockDataOut) -> u8 {
91        value as u8
92    }
93}
94
95/// Modes for clocking bits out of the FTDI device.
96///
97/// This is an argument to the [`clock_bits_out`] method.
98///
99/// [`clock_bits_out`]: MpsseCmdBuilder::clock_bits_out
100#[repr(u8)]
101#[derive(Debug, Copy, Clone, Eq, PartialEq)]
102pub enum ClockBitsOut {
103    /// Positive clock edge MSB first.
104    ///
105    /// The data is sent MSB first (bit 7 first).
106    ///
107    /// The data will change to the next bit on the rising edge of the CLK pin.
108    MsbPos = 0x12,
109    /// Negative clock edge MSB first.
110    ///
111    /// The data is sent MSB first (bit 7 first).
112    ///
113    /// The data will change to the next bit on the falling edge of the CLK pin.
114    MsbNeg = 0x13,
115    /// Positive clock edge LSB first (bit 0 first).
116    ///
117    /// The first bit in will be the LSB of the first byte and so on.
118    ///
119    /// The data will change to the next bit on the rising edge of the CLK pin.
120    LsbPos = 0x1A,
121    /// Negative clock edge LSB first (bit 0 first).
122    ///
123    /// The first bit in will be the LSB of the first byte and so on.
124    ///
125    /// The data will change to the next bit on the falling edge of the CLK pin.
126    LsbNeg = 0x1B,
127}
128
129impl From<ClockBitsOut> for u8 {
130    fn from(value: ClockBitsOut) -> u8 {
131        value as u8
132    }
133}
134
135/// Modes for clocking data into the FTDI device.
136///
137/// This is an argument to the [`clock_data_in`] method.
138///
139/// [`clock_data_in`]: MpsseCmdBuilder::clock_data_in
140#[repr(u8)]
141#[derive(Debug, Copy, Clone, Eq, PartialEq)]
142pub enum ClockDataIn {
143    /// Positive clock edge MSB first.
144    ///
145    /// The first bit in will be the MSB of the first byte and so on.
146    ///
147    /// The data will be sampled on the rising edge of the CLK pin.
148    MsbPos = 0x20,
149    /// Negative clock edge MSB first.
150    ///
151    /// The first bit in will be the MSB of the first byte and so on.
152    ///
153    /// The data will be sampled on the falling edge of the CLK pin.
154    MsbNeg = 0x24,
155    /// Positive clock edge LSB first.
156    ///
157    /// The first bit in will be the LSB of the first byte and so on.
158    ///
159    /// The data will be sampled on the rising edge of the CLK pin.
160    LsbPos = 0x28,
161    /// Negative clock edge LSB first.
162    ///
163    /// The first bit in will be the LSB of the first byte and so on.
164    ///
165    /// The data will be sampled on the falling edge of the CLK pin.
166    LsbNeg = 0x2C,
167}
168
169impl From<ClockDataIn> for u8 {
170    fn from(value: ClockDataIn) -> u8 {
171        value as u8
172    }
173}
174
175/// Modes for clocking data bits into the FTDI device.
176///
177/// This is an argument to the [`clock_bits_in`] method.
178///
179/// [`clock_bits_in`]: MpsseCmdBuilder::clock_bits_in
180#[repr(u8)]
181#[derive(Debug, Copy, Clone, Eq, PartialEq)]
182pub enum ClockBitsIn {
183    /// Positive clock edge MSB first.
184    ///
185    /// The data will be shifted up so that the first bit in may not be in bit 7
186    /// but from 6 downwards depending on the number of bits to shift
187    /// (i.e. a length of 1 bit will have the data bit sampled in bit 0 of the
188    /// byte sent back to the PC).
189    ///
190    /// The data will be sampled on the rising edge of the CLK pin.
191    MsbPos = 0x22,
192    /// Negative clock edge MSB first.
193    ///
194    /// The data will be shifted up so that the first bit in may not be in bit 7
195    /// but from 6 downwards depending on the number of bits to shift
196    /// (i.e. a length of 1 bit will have the data bit sampled in bit 0 of the
197    /// byte sent back to the PC).
198    ///
199    /// The data will be sampled on the falling edge of the CLK pin.
200    MsbNeg = 0x26,
201    /// Positive clock edge LSB first.
202    ///
203    /// The data will be shifted down so that the first bit in may not be in bit
204    /// 0 but from 1 upwards depending on the number of bits to shift
205    /// (i.e. a length of 1 bit will have the data bit sampled in bit 7 of the
206    /// byte sent back to the PC).
207    ///
208    /// The data will be sampled on the rising edge of the CLK pin.
209    LsbPos = 0x2A,
210    /// Negative clock edge LSB first.
211    ///
212    /// The data will be shifted down so that the first bit in may not be in bit
213    /// 0 but from 1 upwards depending on the number of bits to shift
214    /// (i.e. a length of 1 bit will have the data bit sampled in bit 7 of the
215    /// byte sent back to the PC).
216    ///
217    /// The data will be sampled on the falling edge of the CLK pin.
218    LsbNeg = 0x2E,
219}
220
221impl From<ClockBitsIn> for u8 {
222    fn from(value: ClockBitsIn) -> u8 {
223        value as u8
224    }
225}
226
227/// Modes for clocking data in and out of the FTDI device.
228///
229/// This is an argument to the [`clock_data`] method.
230///
231/// [`clock_data`]: MpsseCmdBuilder::clock_data
232#[repr(u8)]
233#[derive(Debug, Copy, Clone, Eq, PartialEq)]
234pub enum ClockData {
235    /// MSB first, data in on positive edge, data out on negative edge.
236    MsbPosIn = 0x31,
237    /// MSB first, data in on negative edge, data out on positive edge.
238    MsbNegIn = 0x34,
239    /// LSB first, data in on positive edge, data out on negative edge.
240    LsbPosIn = 0x39,
241    /// LSB first, data in on negative edge, data out on positive edge.
242    LsbNegIn = 0x3C,
243}
244
245impl From<ClockData> for u8 {
246    fn from(value: ClockData) -> u8 {
247        value as u8
248    }
249}
250
251/// Modes for clocking data bits in and out of the FTDI device.
252///
253/// This is an argument to the [`clock_bits`] method.
254///
255/// [`clock_bits`]: MpsseCmdBuilder::clock_bits
256#[repr(u8)]
257#[derive(Debug, Copy, Clone, Eq, PartialEq)]
258pub enum ClockBits {
259    /// MSB first, data in on positive edge, data out on negative edge.
260    MsbPosIn = 0x33,
261    /// MSB first, data in on negative edge, data out on positive edge.
262    MsbNegIn = 0x36,
263    /// LSB first, data in on positive edge, data out on negative edge.
264    LsbPosIn = 0x3B,
265    /// LSB first, data in on negative edge, data out on positive edge.
266    LsbNegIn = 0x3E,
267}
268
269impl From<ClockBits> for u8 {
270    fn from(value: ClockBits) -> u8 {
271        value as u8
272    }
273}
274
275impl From<MpsseCmd> for u8 {
276    fn from(value: MpsseCmd) -> Self {
277        value as u8
278    }
279}
280
281/// Modes for clocking bits out on TMS for JTAG mode.
282///
283/// This is an argument to the [`clock_tms_out`] method.
284///
285/// [`clock_tms_out`]: MpsseCmdBuilder::clock_tms_out
286#[repr(u8)]
287#[derive(Debug, Copy, Clone, Eq, PartialEq)]
288pub enum ClockTMSOut {
289    /// LSB first, TMS out on positive edge
290    PosEdge = 0x4A,
291    /// LSB first, TMS out on negative edge
292    NegEdge = 0x4B,
293}
294
295impl From<ClockTMSOut> for u8 {
296    fn from(value: ClockTMSOut) -> u8 {
297        value as u8
298    }
299}
300
301/// Modes for clocking bits out on TMS for JTAG mode while reading TDO.
302///
303/// This is an argument to the [`clock_tms`] method.
304///
305/// [`clock_tms`]: MpsseCmdBuilder::clock_tms
306#[repr(u8)]
307#[derive(Debug, Copy, Clone, Eq, PartialEq)]
308pub enum ClockTMS {
309    /// LSB first, TMS out on positive edge, TDO in on positive edge.
310    PosTMSPosTDO = 0x6A,
311    /// LSB first, TMS out on positive edge, TDO in on negative edge.
312    PosTMSNegTDO = 0x6E,
313    /// LSB first, TMS out on negative edge, TDO in on positive edge.
314    NegTMSPosTDO = 0x6B,
315    /// LSB first, TMS out on negative edge, TDO in on negative edge.
316    NegTMSNegTDO = 0x6F,
317}
318
319impl From<ClockTMS> for u8 {
320    fn from(value: ClockTMS) -> u8 {
321        value as u8
322    }
323}
324
325/// Initialization settings for the MPSSE.
326///
327/// Settings can be written to the device with the appropriate
328/// implementation of [`init`] method.
329///
330/// [`init`]: MpsseCmdExecutor::init
331#[derive(Debug, Copy, Clone, Eq, PartialEq)]
332pub struct MpsseSettings {
333    /// Reset the MPSSE on initialization.
334    pub reset: bool,
335    /// USB in transfer size in bytes.
336    pub in_transfer_size: u32,
337    /// Read timeout.
338    pub read_timeout: Duration,
339    /// Write timeout.
340    pub write_timeout: Duration,
341    /// Latency timer.
342    pub latency_timer: Duration,
343    /// Bitmode mask.
344    ///
345    /// * A bit value of `0` sets the corresponding pin to an input.
346    /// * A bit value of `1` sets the corresponding pin to an output.
347    pub mask: u8,
348    /// Clock frequency.
349    ///
350    /// If `None`, then no frequency changes will be applied.
351    pub clock_frequency: Option<u32>,
352}
353
354impl std::default::Default for MpsseSettings {
355    fn default() -> Self {
356        MpsseSettings {
357            reset: true,
358            in_transfer_size: 4096,
359            read_timeout: Duration::from_secs(1),
360            write_timeout: Duration::from_secs(1),
361            latency_timer: Duration::from_millis(16),
362            mask: 0x00,
363            clock_frequency: None,
364        }
365    }
366}
367
368/// FTDI MPSSE configurator and executor
369pub trait MpsseCmdExecutor {
370    /// Error type
371    type Error;
372
373    /// Configure FTDI MPSSE mode
374    fn init(&mut self, settings: &MpsseSettings) -> Result<(), Self::Error>;
375
376    /// Execute MPSSE write command sequence
377    fn send(&mut self, data: &[u8]) -> Result<(), Self::Error>;
378
379    /// Execute MPSSE read command sequence
380    fn recv(&mut self, data: &mut [u8]) -> Result<(), Self::Error>;
381
382    /// Execute MPSSE command and read response
383    fn xfer(&mut self, txdata: &[u8], rxdata: &mut [u8]) -> Result<(), Self::Error> {
384        self.send(txdata)?;
385        self.recv(rxdata)
386    }
387}
388
389/// FTDI Multi-Protocol Synchronous Serial Engine (MPSSE) command builder.
390///
391/// For details about the MPSSE read the [FTDI MPSSE Basics].
392///
393/// This structure is a `Vec<u8>` that the methods push bytewise commands onto.
394/// These commands can then be written to the device with the appropriate
395/// implementations of [`send`] and [`xfer`] methods.
396///
397/// This is useful for creating commands that need to do multiple operations
398/// quickly, since individual write calls can be expensive. For example,
399/// this can be used to set a GPIO low and clock data out for SPI operations.
400///
401/// If dynamic command layout is not required, the [`mpsse`] macro can build
402/// command `[u8; N]` arrays at compile-time.
403///
404/// [FTDI MPSSE Basics]: https://www.ftdichip.com/Support/Documents/AppNotes/AN_135_MPSSE_Basics.pdf
405/// [`send`]: MpsseCmdExecutor::send
406/// [`xfer`]: MpsseCmdExecutor::xfer
407pub struct MpsseCmdBuilder(pub Vec<u8>);
408
409impl Default for MpsseCmdBuilder {
410    fn default() -> Self {
411        Self::new()
412    }
413}
414
415impl MpsseCmdBuilder {
416    /// Create a new command builder.
417    ///
418    /// # Example
419    ///
420    /// ```
421    /// use ftdi_mpsse::MpsseCmdBuilder;
422    ///
423    /// MpsseCmdBuilder::new();
424    /// ```
425    pub const fn new() -> MpsseCmdBuilder {
426        MpsseCmdBuilder(Vec::new())
427    }
428
429    /// Create a new command builder from a vector.
430    ///
431    /// # Example
432    ///
433    /// ```
434    /// use ftdi_mpsse::MpsseCmdBuilder;
435    ///
436    /// MpsseCmdBuilder::with_vec(Vec::new());
437    /// ```
438    pub const fn with_vec(vec: Vec<u8>) -> MpsseCmdBuilder {
439        MpsseCmdBuilder(vec)
440    }
441
442    /// Get the MPSSE command as a slice.
443    ///
444    /// # Example
445    ///
446    /// ```no_run
447    /// use ftdi_mpsse::MpsseCmdBuilder;
448    /// use libftd2xx::{Ft232h, FtdiCommon, FtdiMpsse};
449    ///
450    /// let cmd = MpsseCmdBuilder::new().enable_loopback();
451    ///
452    /// let mut ft = Ft232h::with_serial_number("FT5AVX6B")?;
453    /// ft.initialize_mpsse_default()?;
454    /// ft.set_clock(100_000);
455    /// ft.write_all(cmd.as_slice())?;
456    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
457    /// ```
458    pub fn as_slice(&self) -> &[u8] {
459        self.0.as_slice()
460    }
461
462    /// Set the MPSSE clock frequency using provided
463    /// divisor value and clock divider configuration.
464    /// Both parameters are device dependent.
465    ///
466    /// # Example
467    ///
468    /// ```no_run
469    /// use ftdi_mpsse::MpsseCmdBuilder;
470    ///
471    /// let cmd = MpsseCmdBuilder::new().set_clock(9, Some(false));
472    ///
473    /// ```
474    pub fn set_clock(mut self, divisor: u32, clkdiv: Option<bool>) -> Self {
475        match clkdiv {
476            Some(true) => self.0.push(MpsseCmd::EnableClockDivide.into()),
477            Some(false) => self.0.push(MpsseCmd::DisableClockDivide.into()),
478            None => {}
479        };
480
481        self.0.push(MpsseCmd::SetClockFrequency.into());
482        self.0.push((divisor & 0xFF) as u8);
483        self.0.push(((divisor >> 8) & 0xFF) as u8);
484
485        self
486    }
487
488    /// Enable the MPSSE loopback state.
489    ///
490    /// # Example
491    ///
492    /// ```no_run
493    /// use ftdi_mpsse::MpsseCmdBuilder;
494    /// use libftd2xx::{Ft232h, FtdiCommon, FtdiMpsse};
495    ///
496    /// let cmd = MpsseCmdBuilder::new().enable_loopback();
497    ///
498    /// let mut ft = Ft232h::with_serial_number("FT5AVX6B")?;
499    /// ft.initialize_mpsse_default()?;
500    /// ft.write_all(cmd.as_slice())?;
501    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
502    /// ```
503    pub fn enable_loopback(mut self) -> Self {
504        self.0.push(MpsseCmd::EnableLoopback.into());
505        self
506    }
507
508    /// Disable the MPSSE loopback state.
509    ///
510    /// # Example
511    ///
512    /// ```no_run
513    /// use ftdi_mpsse::MpsseCmdBuilder;
514    /// use libftd2xx::{Ft232h, FtdiCommon, FtdiMpsse};
515    ///
516    /// let cmd = MpsseCmdBuilder::new().disable_loopback();
517    ///
518    /// let mut ft = Ft232h::with_serial_number("FT5AVX6B")?;
519    /// ft.initialize_mpsse_default()?;
520    /// ft.write_all(cmd.as_slice())?;
521    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
522    /// ```
523    pub fn disable_loopback(mut self) -> Self {
524        self.0.push(MpsseCmd::DisableLoopback.into());
525        self
526    }
527
528    /// Disable 3 phase data clocking.
529    ///
530    /// This is only available on FTx232H devices.
531    ///
532    /// This will give a 2 stage data shift which is the default state.
533    ///
534    /// It will appears as:
535    ///
536    /// 1. Data setup for 1/2 clock period
537    /// 2. Pulse clock for 1/2 clock period
538    ///
539    /// # Example
540    ///
541    /// ```no_run
542    /// use ftdi_mpsse::MpsseCmdBuilder;
543    /// use libftd2xx::{Ft232h, FtdiCommon, FtdiMpsse};
544    ///
545    /// let cmd = MpsseCmdBuilder::new().disable_3phase_data_clocking();
546    ///
547    /// let mut ft = Ft232h::with_serial_number("FT5AVX6B")?;
548    /// ft.initialize_mpsse_default()?;
549    /// ft.write_all(cmd.as_slice())?;
550    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
551    /// ```
552    pub fn disable_3phase_data_clocking(mut self) -> Self {
553        self.0.push(MpsseCmd::Disable3PhaseClocking.into());
554        self
555    }
556
557    /// Enable 3 phase data clocking.
558    ///
559    /// This is only available on FTx232H devices.
560    ///
561    /// This will give a 3 stage data shift for the purposes of supporting
562    /// interfaces such as I2C which need the data to be valid on both edges of
563    /// the clock.
564    ///
565    /// It will appears as:
566    ///
567    /// 1. Data setup for 1/2 clock period
568    /// 2. Pulse clock for 1/2 clock period
569    /// 3. Data hold for 1/2 clock period
570    ///
571    /// # Example
572    ///
573    /// ```no_run
574    /// use ftdi_mpsse::MpsseCmdBuilder;
575    /// use libftd2xx::{Ft232h, FtdiCommon, FtdiMpsse};
576    ///
577    /// let cmd = MpsseCmdBuilder::new().enable_3phase_data_clocking();
578    ///
579    /// let mut ft = Ft232h::with_serial_number("FT5AVX6B")?;
580    /// ft.initialize_mpsse_default()?;
581    /// ft.write_all(cmd.as_slice())?;
582    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
583    /// ```
584    pub fn enable_3phase_data_clocking(mut self) -> Self {
585        self.0.push(MpsseCmd::Enable3PhaseClocking.into());
586        self
587    }
588
589    /// Enable adaptive data clocking.
590    ///
591    /// This is only available on FTx232H devices.
592    pub fn enable_adaptive_data_clocking(mut self) -> Self {
593        self.0.push(MpsseCmd::EnableAdaptiveClocking.into());
594        self
595    }
596
597    /// Enable adaptive data clocking.
598    ///
599    /// This is only available on FTx232H devices.
600    pub fn disable_adaptive_data_clocking(mut self) -> Self {
601        self.0.push(MpsseCmd::DisableAdaptiveClocking.into());
602        self
603    }
604
605    /// Set the pin direction and state of the lower byte (0-7) GPIO pins on the
606    /// MPSSE interface.
607    ///
608    /// The pins that this controls depends on the device.
609    ///
610    /// * On the FT232H this will control the AD0-AD7 pins.
611    ///
612    /// # Arguments
613    ///
614    /// * `state` - GPIO state mask, `0` is low (or input pin), `1` is high.
615    /// * `direction` - GPIO direction mask, `0` is input, `1` is output.
616    ///
617    /// # Example
618    ///
619    /// ```no_run
620    /// use ftdi_mpsse::MpsseCmdBuilder;
621    /// use libftd2xx::{Ft232h, FtdiCommon, FtdiMpsse};
622    ///
623    /// let cmd = MpsseCmdBuilder::new()
624    ///     .set_gpio_lower(0xFF, 0xFF)
625    ///     .set_gpio_lower(0x00, 0xFF);
626    ///
627    /// let mut ft = Ft232h::with_serial_number("FT5AVX6B")?;
628    /// ft.initialize_mpsse_default()?;
629    /// ft.write_all(cmd.as_slice())?;
630    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
631    /// ```
632    pub fn set_gpio_lower(mut self, state: u8, direction: u8) -> Self {
633        self.0
634            .extend_from_slice(&[MpsseCmd::SetDataBitsLowbyte.into(), state, direction]);
635        self
636    }
637
638    /// Set the pin direction and state of the upper byte (8-15) GPIO pins on
639    /// the MPSSE interface.
640    ///
641    /// The pins that this controls depends on the device.
642    /// This method may do nothing for some devices, such as the FT4232H that
643    /// only have 8 pins per port.
644    ///
645    /// # Arguments
646    ///
647    /// * `state` - GPIO state mask, `0` is low (or input pin), `1` is high.
648    /// * `direction` - GPIO direction mask, `0` is input, `1` is output.
649    ///
650    /// # FT232H Corner Case
651    ///
652    /// On the FT232H only CBUS5, CBUS6, CBUS8, and CBUS9 can be controlled.
653    /// These pins confusingly map to the first four bits in the direction and
654    /// state masks.
655    ///
656    /// # Example
657    ///
658    /// ```no_run
659    /// use ftdi_mpsse::MpsseCmdBuilder;
660    /// use libftd2xx::{Ft232h, FtdiCommon, FtdiMpsse};
661    ///
662    /// let cmd = MpsseCmdBuilder::new()
663    ///     .set_gpio_upper(0xFF, 0xFF)
664    ///     .set_gpio_upper(0x00, 0xFF);
665    ///
666    /// let mut ft = Ft232h::with_serial_number("FT5AVX6B")?;
667    /// ft.initialize_mpsse_default()?;
668    /// ft.write_all(cmd.as_slice())?;
669    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
670    /// ```
671    pub fn set_gpio_upper(mut self, state: u8, direction: u8) -> Self {
672        self.0
673            .extend_from_slice(&[MpsseCmd::SetDataBitsHighbyte.into(), state, direction]);
674        self
675    }
676
677    /// Get the pin state state of the lower byte (0-7) GPIO pins on the MPSSE
678    /// interface.
679    ///
680    /// # Example
681    ///
682    /// ```no_run
683    /// use ftdi_mpsse::MpsseCmdBuilder;
684    /// use libftd2xx::{Ft232h, FtdiCommon, FtdiMpsse};
685    ///
686    /// let cmd = MpsseCmdBuilder::new().gpio_lower().send_immediate();
687    ///
688    /// let mut ft = Ft232h::with_serial_number("FT5AVX6B")?;
689    /// ft.initialize_mpsse_default()?;
690    /// ft.write_all(cmd.as_slice())?;
691    /// let mut buf: [u8; 1] = [0; 1];
692    /// ft.read_all(&mut buf)?;
693    /// println!("GPIO lower state: 0x{:02X}", buf[0]);
694    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
695    /// ```
696    pub fn gpio_lower(mut self) -> Self {
697        self.0.push(MpsseCmd::GetDataBitsLowbyte.into());
698        self
699    }
700
701    /// Get the pin state state of the upper byte (8-15) GPIO pins on the MPSSE
702    /// interface.
703    ///
704    /// See [`set_gpio_upper`] for additional information about physical pin
705    /// mappings.
706    ///
707    /// # Example
708    ///
709    /// ```no_run
710    /// use ftdi_mpsse::MpsseCmdBuilder;
711    /// use libftd2xx::{Ft232h, FtdiCommon, FtdiMpsse};
712    ///
713    /// let cmd = MpsseCmdBuilder::new().gpio_upper().send_immediate();
714    ///
715    /// let mut ft = Ft232h::with_serial_number("FT5AVX6B")?;
716    /// ft.initialize_mpsse_default()?;
717    /// ft.write_all(cmd.as_slice())?;
718    /// let mut buf: [u8; 1] = [0; 1];
719    /// ft.read_all(&mut buf)?;
720    /// println!("GPIO upper state: 0x{:02X}", buf[0]);
721    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
722    /// ```
723    ///
724    /// [`set_gpio_upper`]: MpsseCmdBuilder::set_gpio_upper
725    pub fn gpio_upper(mut self) -> Self {
726        self.0.push(MpsseCmd::GetDataBitsHighbyte.into());
727        self
728    }
729
730    /// Send the preceding commands immediately.
731    ///
732    /// # Example
733    ///
734    /// ```
735    /// use ftdi_mpsse::MpsseCmdBuilder;
736    ///
737    /// let cmd = MpsseCmdBuilder::new()
738    ///     .set_gpio_upper(0xFF, 0xFF)
739    ///     .set_gpio_upper(0x00, 0xFF)
740    ///     .send_immediate();
741    /// ```
742    pub fn send_immediate(mut self) -> Self {
743        self.0.push(MpsseCmd::SendImmediate.into());
744        self
745    }
746
747    /// Make controller wait until GPIOL1 or I/O1 is high before running further commands.
748    ///
749    /// # Example
750    ///
751    /// ```
752    /// use ftdi_mpsse::{ClockData, MpsseCmdBuilder};
753    ///
754    /// // Assume a "chip ready" signal is connected to GPIOL1. This signal is pulled high
755    /// // shortly after AD3 (chip select) is pulled low. Data will not be clocked out until
756    /// // the chip is ready.
757    /// let cmd = MpsseCmdBuilder::new()
758    ///     .set_gpio_lower(0x0, 0xb)
759    ///     .wait_on_io_high()
760    ///     .clock_data(ClockData::MsbPosIn, &[0x12, 0x34, 0x56])
761    ///     .set_gpio_lower(0x8, 0xb)
762    ///     .send_immediate();
763    /// ```
764    pub fn wait_on_io_high(mut self) -> Self {
765        self.0.push(MpsseCmd::WaitOnIOHigh.into());
766        self
767    }
768
769    /// Make controller wait until GPIOL1 or I/O1 is low before running further commands.
770    ///
771    /// # Example
772    ///
773    /// ```
774    /// use ftdi_mpsse::{ClockData, MpsseCmdBuilder};
775    ///
776    /// // Assume a "chip ready" signal is connected to GPIOL1. This signal is pulled low
777    /// // shortly after AD3 (chip select) is pulled low. Data will not be clocked out until
778    /// // the chip is ready.
779    /// let cmd = MpsseCmdBuilder::new()
780    ///     .set_gpio_lower(0x0, 0xb)
781    ///     .wait_on_io_low()
782    ///     .clock_data(ClockData::MsbPosIn, &[0x12, 0x34, 0x56])
783    ///     .set_gpio_lower(0x8, 0xb)
784    ///     .send_immediate();
785    /// ```
786    pub fn wait_on_io_low(mut self) -> Self {
787        self.0.push(MpsseCmd::WaitOnIOLow.into());
788        self
789    }
790
791    /// Clock data out.
792    ///
793    /// This will clock out bytes on TDI/DO.
794    /// No data is clocked into the device on TDO/DI.
795    ///
796    /// This will panic for data lengths greater than `u16::MAX + 1`.
797    pub fn clock_data_out(mut self, mode: ClockDataOut, data: &[u8]) -> Self {
798        let mut len = data.len();
799        assert!(len <= 65536, "data length cannot exceed u16::MAX + 1");
800        len = match len.checked_sub(1) {
801            Some(l) => l,
802            None => return self,
803        };
804        self.0
805            .extend_from_slice(&[mode.into(), (len & 0xFF) as u8, ((len >> 8) & 0xFF) as u8]);
806        self.0.extend_from_slice(data);
807        self
808    }
809
810    /// Clock data in.
811    ///
812    /// This will clock in bytes on TDO/DI.
813    /// No data is clocked out of the device on TDI/DO.
814    ///
815    /// # Arguments
816    ///
817    /// * `mode` - Data clocking mode.
818    /// * `len` - Number of bytes to clock in.
819    ///           This will panic for values greater than `u16::MAX + 1`.
820    pub fn clock_data_in(mut self, mode: ClockDataIn, mut len: usize) -> Self {
821        assert!(len <= 65536, "data length cannot exceed u16::MAX + 1");
822        len = match len.checked_sub(1) {
823            Some(l) => l,
824            None => return self,
825        };
826        self.0
827            .extend_from_slice(&[mode.into(), (len & 0xFF) as u8, ((len >> 8) & 0xFF) as u8]);
828        self
829    }
830
831    /// Clock data in and out simultaneously.
832    ///
833    /// This will panic for data lengths greater than `u16::MAX + 1`.
834    pub fn clock_data(mut self, mode: ClockData, data: &[u8]) -> Self {
835        let mut len = data.len();
836        assert!(len <= 65536, "data length cannot exceed u16::MAX + 1");
837        len = match len.checked_sub(1) {
838            Some(l) => l,
839            None => return self,
840        };
841        self.0
842            .extend_from_slice(&[mode.into(), (len & 0xFF) as u8, ((len >> 8) & 0xFF) as u8]);
843        self.0.extend_from_slice(data);
844        self
845    }
846
847    /// Clock data bits out.
848    ///
849    /// # Arguments
850    ///
851    /// * `mode` - Bit clocking mode.
852    /// * `data` - Data bits.
853    /// * `len` - Number of bits to clock out.
854    ///           This will panic for values greater than 8.
855    pub fn clock_bits_out(mut self, mode: ClockBitsOut, data: u8, mut len: u8) -> Self {
856        assert!(len <= 8, "data length cannot exceed 8");
857        len = match len.checked_sub(1) {
858            Some(l) => l,
859            None => return self,
860        };
861        self.0.extend_from_slice(&[mode.into(), len, data]);
862        self
863    }
864
865    /// Clock data bits in.
866    ///
867    /// # Arguments
868    ///
869    /// * `mode` - Bit clocking mode.
870    /// * `len` - Number of bits to clock in.
871    ///           This will panic for values greater than 8.
872    pub fn clock_bits_in(mut self, mode: ClockBitsIn, mut len: u8) -> Self {
873        assert!(len <= 8, "data length cannot exceed 8");
874        len = match len.checked_sub(1) {
875            Some(l) => l,
876            None => return self,
877        };
878        self.0.extend_from_slice(&[mode.into(), len]);
879        self
880    }
881
882    /// Clock data bits in and out simultaneously.
883    ///
884    /// # Arguments
885    ///
886    /// * `mode` - Bit clocking mode.
887    /// * `len` - Number of bits to clock in.
888    ///           This will panic for values greater than 8.
889    pub fn clock_bits(mut self, mode: ClockBits, data: u8, mut len: u8) -> Self {
890        assert!(len <= 8, "data length cannot exceed 8");
891        len = match len.checked_sub(1) {
892            Some(l) => l,
893            None => return self,
894        };
895        self.0.extend_from_slice(&[mode.into(), len, data]);
896        self
897    }
898
899    /// Clock TMS bits out.
900    ///
901    /// # Arguments
902    ///
903    /// * `mode` - TMS clocking mode.
904    /// * `data` - TMS bits.
905    /// * `tdi` - Value to place on TDI while clocking.
906    /// * `len` - Number of bits to clock out.
907    ///           This will panic for values greater than 7.
908    pub fn clock_tms_out(
909        mut self,
910        mode: ClockTMSOut,
911        mut data: u8,
912        tdi: bool,
913        mut len: u8,
914    ) -> Self {
915        assert!(len <= 7, "data length cannot exceed 7");
916        len = match len.checked_sub(1) {
917            Some(l) => l,
918            None => return self,
919        };
920        if tdi {
921            data |= 0x80;
922        }
923        self.0.extend_from_slice(&[mode.into(), len, data]);
924        self
925    }
926
927    /// Clock TMS bits out while clocking TDO bits in.
928    ///
929    /// # Arguments
930    ///
931    /// * `mode` - TMS clocking mode.
932    /// * `data` - TMS bits.
933    /// * `tdi` - Value to place on TDI while clocking.
934    /// * `len` - Number of bits to clock out.
935    ///           This will panic for values greater than 7.
936    pub fn clock_tms(mut self, mode: ClockTMS, mut data: u8, tdi: bool, mut len: u8) -> Self {
937        assert!(len <= 7, "data length cannot exceed 7");
938        len = match len.checked_sub(1) {
939            Some(l) => l,
940            None => return self,
941        };
942        if tdi {
943            data |= 0x80;
944        }
945        self.0.extend_from_slice(&[mode.into(), len, data]);
946        self
947    }
948}
949
950/// Construct an MPSSE command array at compile-time.
951///
952/// Alternative to [`MpsseCmdBuilder`]. Parses a specialized grammar that gathers MPSSE commands
953/// into pseudo-statements contained within zero or more assigned blocks. The pseudo-assignment
954/// syntax of each block creates a fixed-length `[u8; N]` array that is bound with `let` or
955/// `const`[^const_note].
956///
957/// [^const_note]: In `const` bindings, all values used as command parameters and data must be const.
958///
959/// # Syntax
960///
961/// ```compile_fail
962/// mpsse! { let command_data = { command1(); command2(); /* ... */ commandN(); }; }
963/// ```
964/// or
965/// ```compile_fail
966/// mpsse! { let (command_data, READ_LEN) = { command1(); command2(); /* ... */ commandN(); }; }
967/// ```
968/// The second form provides the caller with a constant size value of the expected data length to
969/// read after writing the commands to the device.
970///
971/// # Commands
972///
973/// * [`enable_loopback()`][`MpsseCmdBuilder::enable_loopback`]
974/// * [`disable_loopback()`][`MpsseCmdBuilder::disable_loopback`]
975/// * [`enable_3phase_data_clocking()`][`MpsseCmdBuilder::enable_3phase_data_clocking`]
976/// * [`disable_3phase_data_clocking()`][`MpsseCmdBuilder::disable_3phase_data_clocking`]
977/// * [`set_gpio_lower(state: u8, direction: u8)`][`MpsseCmdBuilder::set_gpio_lower`]
978/// * [`set_gpio_upper(state: u8, direction: u8)`][`MpsseCmdBuilder::set_gpio_upper`]
979/// * [`gpio_lower() -> usize`][`MpsseCmdBuilder::gpio_lower`]
980/// * [`gpio_upper() -> usize`][`MpsseCmdBuilder::gpio_upper`]
981/// * [`send_immediate()`][`MpsseCmdBuilder::send_immediate`]
982/// * [`wait_on_io_high()`][`MpsseCmdBuilder::wait_on_io_high`]
983/// * [`wait_on_io_low()`][`MpsseCmdBuilder::wait_on_io_low`]
984/// * [`clock_data_out(mode: ClockDataOut, data: [u8])`][`MpsseCmdBuilder::clock_data_out`]
985/// * [`clock_data_in(mode: ClockDataIn, len: u16) -> std::ops::Range<usize>`][`MpsseCmdBuilder::clock_data_in`]
986/// * [`clock_data(mode: ClockData, data: [u8]) -> std::ops::Range<usize>`][`MpsseCmdBuilder::clock_data`]
987/// * [`clock_bits_out(mode: ClockBitsOut, data: u8, len: u8)`][`MpsseCmdBuilder::clock_bits_out`]
988/// * [`clock_bits_in(mode: ClockBitsIn, len: u8) -> usize`][`MpsseCmdBuilder::clock_bits_in`]
989/// * [`clock_bits(mode: ClockBits, data: u8, len: u8) -> usize`][`MpsseCmdBuilder::clock_bits`]
990/// * [`clock_tms_out(mode: ClockTMSOut, data: u8, tdi: bool, len: u8)`][`MpsseCmdBuilder::clock_tms_out`]
991/// * [`clock_tms(mode: ClockTMS, data: u8, tdi: bool, len: u8) -> usize`][`MpsseCmdBuilder::clock_tms`]
992///
993/// Command pseudo-statements that read data from the device may optionally have the form:
994/// ```
995/// # use ftdi_mpsse::{mpsse, ClockDataIn};
996/// mpsse! {
997///     // command_data and DATA_IN_RANGE are both declared in the scope of the macro expansion.
998///     let command_data = {
999///         const DATA_IN_RANGE = clock_data_in(ClockDataIn::MsbNeg, 3);
1000///     };
1001/// }
1002/// ```
1003/// This provides a constant [`Range`][`std::ops::Range`] or [`usize`] index value that may be used
1004/// to subscript the data read from the device.
1005///
1006/// `clock_data` and `clock_data_out` require that the second argument is a fixed-length, square
1007/// bracketed list of `u8` values. Compile-time limitations make arbitrary array concatenation or
1008/// coercion infeasible.
1009///
1010/// # Asserts
1011///
1012/// For `let` bindings, the standard [`assert`] macro is used for validating parameter size inputs.
1013/// For `const` bindings, [`const_assert`][`static_assertions::const_assert`] is used instead.
1014///
1015/// `const_assert` lacks the ability to provide meaningful compile errors, so it may be useful
1016/// to temporarily use a `let` binding within function scope to diagnose failing macro expansions.
1017///
1018/// # User Abstractions
1019///
1020/// With macro shadowing, it is possible to extend the macro with additional rules for abstract,
1021/// device-specific commands.
1022///
1023/// Comments within the implementation of this macro contain hints on how to implement these rules.
1024///
1025/// For example, a SPI device typically delineates transfers with the CS line. Fundamental
1026/// commands like `cs_high` and `cs_low` can be implemented this way, along with other
1027/// device-specific abstractions.
1028///
1029/// ```
1030/// # use ftdi_mpsse::mpsse;
1031/// macro_rules! mpsse {
1032///     // Practical abstraction of CS line for SPI devices.
1033///     ($passthru:tt {cs_low(); $($tail:tt)*} -> [$($out:tt)*]) => {
1034///         mpsse!($passthru {
1035///             set_gpio_lower(0x0, 0xb);
1036///             $($tail)*
1037///         } -> [$($out)*]);
1038///     };
1039///     ($passthru:tt {cs_high(); $($tail:tt)*} -> [$($out:tt)*]) => {
1040///         mpsse!($passthru {
1041///             set_gpio_lower(0x8, 0xb);
1042///             $($tail)*
1043///         } -> [$($out)*]);
1044///     };
1045///
1046///     // Hypothetical device-specific command. Leverages both user and libftd2xx commands.
1047///     ($passthru:tt
1048///      {const $idx_id:ident = command_42([$($data:expr),* $(,)*]); $($tail:tt)*} ->
1049///      [$($out:tt)*]) => {
1050///         mpsse!($passthru {
1051///             cs_low();
1052///             const $idx_id = clock_data(::ftdi_mpsse::ClockData::MsbPosIn, [0x42, $($data,)*]);
1053///             cs_high();
1054///             $($tail)*
1055///         } -> [$($out)*]);
1056///     };
1057///
1058///     // Everything else handled by libftd2xx crate implementation.
1059///     ($($tokens:tt)*) => {
1060///         ::ftdi_mpsse::mpsse!($($tokens)*);
1061///     };
1062/// }
1063///
1064/// mpsse! {
1065///     const (COMMAND_DATA, READ_LEN) = {
1066///         wait_on_io_high();
1067///         const COMMAND_42_RESULT_RANGE = command_42([11, 22, 33]);
1068///         send_immediate();
1069///     };
1070/// }
1071/// ```
1072///
1073/// # Example
1074///
1075/// ```no_run
1076/// use ftdi_mpsse::{mpsse, ClockDataIn, ClockDataOut};
1077/// use libftd2xx::{Ft232h, FtdiCommon, FtdiMpsse};
1078///
1079/// mpsse! {
1080///     const (COMMAND_DATA, READ_LEN) = {
1081///         set_gpio_lower(0xFA, 0xFB);
1082///         set_gpio_lower(0xF2, 0xFB);
1083///         clock_data_out(ClockDataOut::MsbNeg, [0x12, 0x34, 0x56]);
1084///         const DATA_IN_RANGE = clock_data_in(ClockDataIn::MsbNeg, 3);
1085///         set_gpio_lower(0xFA, 0xFB);
1086///         send_immediate();
1087///     };
1088/// }
1089///
1090/// let mut ft = Ft232h::with_serial_number("FT5AVX6B")?;
1091/// ft.initialize_mpsse_default()?;
1092/// ft.write_all(&COMMAND_DATA)?;
1093/// let mut buf: [u8; READ_LEN] = [0; READ_LEN];
1094/// ft.read_all(&mut buf)?;
1095/// println!("Data slice in: {:?}", &buf[DATA_IN_RANGE]);
1096/// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
1097/// ```
1098#[macro_export]
1099macro_rules! mpsse {
1100    // Replacement method for counting comma-separated expressions.
1101    // https://danielkeep.github.io/tlborm/book/blk-counting.html#repetition-with-replacement
1102    (@replace_expr $_t:tt $sub:expr) => {$sub};
1103    (@count_elements $($tts:expr),* $(,)*) => {(0usize $(+ mpsse!(@replace_expr $tts 1usize))*)};
1104
1105    // Assert that is selectively compile-time depending on let vs. const expansion.
1106    //
1107    // Unfortunately, the compile-time error is not very helpful due to the lack of message and
1108    // macro depth, but still ensures safe command construction.
1109    //
1110    // Temporarily running a let expansion can be helpful to diagnose errors.
1111    (@assert ((let, $_user_passthru:tt), $_read_len:expr), $e:expr, $msg:expr) => {
1112        ::std::assert!($e, $msg);
1113    };
1114    (@assert ((const, $_user_passthru:tt), $_read_len:expr), $e:expr, $_msg:expr) => {
1115        ::static_assertions::const_assert!($e);
1116    };
1117
1118    // Unit rule
1119    () => {};
1120
1121    // let command_data = { command1(); command2(); ... commandN(); };
1122    (let $id:ident = {$($commands:tt)*}; $($tail:tt)*) => {
1123        mpsse!(((let, ($id, _)), 0) {$($commands)*} -> []);
1124        mpsse!($($tail)*);
1125    };
1126
1127    // const COMMAND_DATA = { command1(); command2(); ... commandN(); };
1128    (const $id:ident = {$($commands:tt)*}; $($tail:tt)*) => {
1129        mpsse!(((const, ($id, _)), 0) {$($commands)*} -> []);
1130        mpsse!($($tail)*);
1131    };
1132
1133    // let (command_data, READ_LEN) = { command1(); command2(); ... commandN(); };
1134    (let ($id:ident, $read_len_id:ident) = {$($commands:tt)*}; $($tail:tt)*) => {
1135        mpsse!(((let, ($id, $read_len_id)), 0) {$($commands)*} -> []);
1136        mpsse!($($tail)*);
1137    };
1138
1139    // const (COMMAND_DATA, READ_LEN) = { command1(); command2(); ... commandN(); };
1140    (const ($id:ident, $read_len_id:ident) = {$($commands:tt)*}; $($tail:tt)*) => {
1141        mpsse!(((const, ($id, $read_len_id)), 0) {$($commands)*} -> []);
1142        mpsse!($($tail)*);
1143    };
1144
1145    // Rules generally follow a structure based on three root token trees:
1146    // (<passthru>) {<input>} -> [<output>]
1147    //
1148    // "Statements" are recursively shifted off the front of the input and the resulting u8 tokens
1149    // are appended to the output. Recursion ends when the input token tree is empty.
1150    //
1151    // Rules have the following form:
1152    // ($passthru:tt {<FUNCTION NAME>(); $($tail:tt)*} -> [$($out:tt)*])
1153    //
1154    // For functions that perform data reads, cumulative read_len can be accessed with this form:
1155    // (($passthru:tt, $read_len:tt) {<FUNCTION NAME>(); $($tail:tt)*} -> [$($out:tt)*])
1156    //
1157    // Additionally, the following form is used to provide the invoker with a usize index or
1158    // range to later access a specific data read `const READ_INDEX = <FUNCTION NAME>();`:
1159    // (($passthru:tt, $read_len:tt) {const $idx_id:ident = <FUNCTION NAME>(); $($tail:tt)*} -> [$($out:tt)*])
1160
1161    ($passthru:tt {enable_loopback(); $($tail:tt)*} -> [$($out:tt)*]) => {
1162        mpsse!($passthru {$($tail)*} -> [$($out)* $crate::MpsseCmd::EnableLoopback as u8,]);
1163    };
1164    ($passthru:tt {disable_loopback(); $($tail:tt)*} -> [$($out:tt)*]) => {
1165        mpsse!($passthru {$($tail)*} -> [$($out)* $crate::MpsseCmd::DisableLoopback as u8,]);
1166    };
1167    ($passthru:tt {enable_3phase_data_clocking(); $($tail:tt)*} -> [$($out:tt)*]) => {
1168        mpsse!($passthru {$($tail)*} -> [$($out)* $crate::MpsseCmd::Enable3PhaseClocking as u8,]);
1169    };
1170    ($passthru:tt {disable_3phase_data_clocking(); $($tail:tt)*} -> [$($out:tt)*]) => {
1171        mpsse!($passthru {$($tail)*} -> [$($out)* $crate::MpsseCmd::Disable3PhaseClocking as u8,]);
1172    };
1173    ($passthru:tt {enable_adaptive_data_clocking(); $($tail:tt)*} -> [$($out:tt)*]) => {
1174        mpsse!($passthru {$($tail)*} -> [$($out)* $crate::MpsseCmd::EnableAdaptiveClocking as u8,]);
1175    };
1176    ($passthru:tt {disable_adaptive_data_clocking(); $($tail:tt)*} -> [$($out:tt)*]) => {
1177        mpsse!($passthru {$($tail)*} -> [$($out)* $crate::MpsseCmd::DisableAdaptiveClocking as u8,]);
1178    };
1179    ($passthru:tt {set_gpio_lower($state:expr, $direction:expr); $($tail:tt)*} -> [$($out:tt)*]) => {
1180        mpsse!($passthru {$($tail)*} -> [$($out)* $crate::MpsseCmd::SetDataBitsLowbyte as u8, $state as u8, $direction as u8,]);
1181    };
1182    ($passthru:tt {set_gpio_upper($state:expr, $direction:expr); $($tail:tt)*} -> [$($out:tt)*]) => {
1183        mpsse!($passthru {$($tail)*} -> [$($out)* $crate::MpsseCmd::SetDataBitsHighbyte as u8, $state as u8, $direction as u8,]);
1184    };
1185    (($passthru:tt, $read_len:tt) {gpio_lower(); $($tail:tt)*} -> [$($out:tt)*]) => {
1186        mpsse!(($passthru, ($read_len + 1)) {$($tail)*} -> [$($out)* $crate::MpsseCmd::GetDataBitsLowbyte as u8,]);
1187    };
1188    (($passthru:tt, $read_len:tt) {const $idx_id:ident = gpio_lower(); $($tail:tt)*} -> [$($out:tt)*]) => {
1189        const $idx_id: usize = $read_len;
1190        mpsse!(($passthru, $read_len) {gpio_lower(); $($tail)*} -> [$($out)*]);
1191    };
1192    (($passthru:tt, $read_len:tt) {gpio_upper(); $($tail:tt)*} -> [$($out:tt)*]) => {
1193        mpsse!(($passthru, ($read_len + 1)) {$($tail)*} -> [$($out)* $crate::MpsseCmd::GetDataBitsHighbyte as u8,]);
1194    };
1195    (($passthru:tt, $read_len:tt) {const $idx_id:ident = gpio_upper(); $($tail:tt)*} -> [$($out:tt)*]) => {
1196        const $idx_id: usize = $read_len;
1197        mpsse!(($passthru, $read_len) {gpio_upper(); $($tail)*} -> [$($out)*]);
1198    };
1199    ($passthru:tt {send_immediate(); $($tail:tt)*} -> [$($out:tt)*]) => {
1200        mpsse!($passthru {$($tail)*} -> [$($out)* $crate::MpsseCmd::SendImmediate as u8,]);
1201    };
1202    ($passthru:tt {wait_on_io_high(); $($tail:tt)*} -> [$($out:tt)*]) => {
1203        mpsse!($passthru {$($tail)*} -> [$($out)* $crate::MpsseCmd::WaitOnIOHigh as u8,]);
1204    };
1205    ($passthru:tt {wait_on_io_low(); $($tail:tt)*} -> [$($out:tt)*]) => {
1206        mpsse!($passthru {$($tail)*} -> [$($out)* $crate::MpsseCmd::WaitOnIOLow as u8,]);
1207    };
1208    ($passthru:tt {clock_data_out($mode:expr, [$($data:expr),* $(,)*]); $($tail:tt)*} -> [$($out:tt)*]) => {
1209        mpsse!(@assert $passthru, (mpsse!(@count_elements $($data,)*) as usize > 0_usize && mpsse!(@count_elements $($data,)*) as usize <= 65536_usize), "data length must be in 1..=(u16::MAX + 1)");
1210        mpsse!($passthru {$($tail)*} -> [$($out)* $mode as $crate::ClockDataOut as u8,
1211        ((mpsse!(@count_elements $($data,)*) - 1) & 0xFF_usize) as u8,
1212        (((mpsse!(@count_elements $($data,)*) - 1) >> 8) & 0xFF_usize) as u8,
1213        $($data as u8,)*]);
1214    };
1215    (($passthru:tt, $read_len:tt) {clock_data_in($mode:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => {
1216        mpsse!(@assert ($passthru, $read_len), (($len) as usize > 0_usize && ($len) as usize <= 65536_usize), "data length must be in 1..=(u16::MAX + 1)");
1217        mpsse!(($passthru, ($read_len + ($len))) {$($tail)*} -> [$($out)* $mode as $crate::ClockDataIn as u8,
1218        ((($len) - 1) & 0xFF_usize) as u8,
1219        (((($len) - 1) >> 8) & 0xFF_usize) as u8,]);
1220    };
1221    (($passthru:tt, $read_len:tt) {const $range_id:ident = clock_data_in($mode:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => {
1222        const $range_id: ::std::ops::Range<usize> = $read_len..$read_len + ($len);
1223        mpsse!(($passthru, $read_len) {clock_data_in($mode, $len); $($tail)*} -> [$($out)*]);
1224    };
1225    (($passthru:tt, $read_len:tt) {clock_data($mode:expr, [$($data:expr),* $(,)*]); $($tail:tt)*} -> [$($out:tt)*]) => {
1226        mpsse!(@assert ($passthru, $read_len), (mpsse!(@count_elements $($data,)*) as usize > 0_usize && mpsse!(@count_elements $($data,)*) as usize <= 65536_usize), "data length must be in 1..=(u16::MAX + 1)");
1227        mpsse!(($passthru, ($read_len + mpsse!(@count_elements $($data,)*))) {$($tail)*} -> [$($out)* $mode as $crate::ClockData as u8,
1228        ((mpsse!(@count_elements $($data,)*) - 1) & 0xFF_usize) as u8,
1229        (((mpsse!(@count_elements $($data,)*) - 1) >> 8) & 0xFF_usize) as u8,
1230        $($data as u8,)*]);
1231    };
1232    (($passthru:tt, $read_len:tt) {const $range_id:ident = clock_data($mode:expr, [$($data:expr),* $(,)*]); $($tail:tt)*} -> [$($out:tt)*]) => {
1233        const $range_id: ::std::ops::Range<usize> = $read_len..$read_len + mpsse!(@count_elements $($data,)*);
1234        mpsse!(($passthru, $read_len) {clock_data($mode, [$($data,)*]); $($tail)*} -> [$($out)*]);
1235    };
1236    ($passthru:tt {clock_bits_out($mode:expr, $data:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => {
1237        mpsse!(@assert $passthru, ($len as u8 > 0_u8 && $len as u8 <= 8_u8), "data length must be in 1..=8");
1238        mpsse!($passthru {$($tail)*} -> [$($out)* $mode as $crate::ClockBitsOut as u8, (($len) - 1) as u8, $data as u8,]);
1239    };
1240    (($passthru:tt, $read_len:tt) {clock_bits_in($mode:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => {
1241        mpsse!(@assert ($passthru, $read_len), ($len as u8 > 0_u8 && $len as u8 <= 8_u8), "data length must be in 1..=8");
1242        mpsse!(($passthru, ($read_len + 1)) {$($tail)*} -> [$($out)* $mode as $crate::ClockBitsIn as u8, (($len) - 1) as u8,]);
1243    };
1244    (($passthru:tt, $read_len:tt) {const $idx_id:ident = clock_bits_in($mode:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => {
1245        const $idx_id: usize = $read_len;
1246        mpsse!(($passthru, $read_len) {clock_bits_in($mode, $len); $($tail)*} -> [$($out)*]);
1247    };
1248    (($passthru:tt, $read_len:tt) {clock_bits($mode:expr, $data:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => {
1249        mpsse!(@assert ($passthru, $read_len), ($len as u8 > 0_u8 && $len as u8 <= 8_u8), "data length must be in 1..=8");
1250        mpsse!(($passthru, ($read_len + 1)) {$($tail)*} -> [$($out)* $mode as $crate::ClockBits as u8, (($len) - 1) as u8, $data as u8,]);
1251    };
1252    (($passthru:tt, $read_len:tt) {const $idx_id:ident = clock_bits($mode:expr, $data:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => {
1253        const $idx_id: usize = $read_len;
1254        mpsse!(($passthru, $read_len) {clock_bits($mode, $data, $len); $($tail)*} -> [$($out)*]);
1255    };
1256    ($passthru:tt {clock_tms_out($mode:expr, $data:expr, $tdi:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => {
1257        mpsse!(@assert $passthru, ($len as u8 > 0_u8 && $len as u8 <= 7_u8), "data length must be in 1..=7");
1258        mpsse!($passthru {$($tail)*} -> [$($out)* $mode as $crate::ClockTMSOut as u8, (($len) - 1) as u8, ($data as u8) | if $tdi { 0x80 } else { 0 },]);
1259    };
1260    (($passthru:tt, $read_len:tt) {clock_tms($mode:expr, $data:expr, $tdi:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => {
1261        mpsse!(@assert ($passthru, $read_len), ($len as u8 > 0_u8 && $len as u8 <= 7_u8), "data length must be in 1..=7");
1262        mpsse!(($passthru, ($read_len + 1)) {$($tail)*} -> [$($out)* $mode as $crate::ClockTMS as u8, (($len) - 1) as u8, ($data as u8) | if $tdi { 0x80 } else { 0 },]);
1263    };
1264    (($passthru:tt, $read_len:tt) {const $idx_id:ident = clock_tms($mode:expr, $data:expr, $tdi:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => {
1265        const $idx_id: usize = $read_len;
1266        mpsse!(($passthru, $read_len) {clock_tms($mode, $data, $tdi, $len); $($tail)*} -> [$($out)*]);
1267    };
1268
1269    // Emit command_data
1270    ((($const_let:tt, ($id:tt, _)), $read_len:expr) {} -> [$($out:tt)*]) => {
1271        $const_let $id: [u8; mpsse!(@count_elements $($out)*)] = [$($out)*];
1272    };
1273
1274    // Emit command_data, READ_LEN
1275    ((($const_let:tt, ($id:tt, $read_len_id:tt)), $read_len:expr) {} -> [$($out:tt)*]) => {
1276        $const_let $id: [u8; mpsse!(@count_elements $($out)*)] = [$($out)*];
1277        const $read_len_id: usize = $read_len;
1278    };
1279}