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}