Skip to main content

driver_cp2130/
device.rs

1//! CP2130 Driver Device Definitions
2//!
3//!
4//! Copyright 2019 Ryan Kurte
5
6use std::str::FromStr;
7use std::time::{Duration, SystemTime};
8
9use bitflags::bitflags;
10use byteorder::{ByteOrder, BE, LE};
11use log::{debug, error, trace};
12
13use rusb::{
14    Context as UsbContext, Device as UsbDevice, DeviceDescriptor, DeviceHandle, Direction,
15    TransferType,
16};
17
18use embedded_hal::spi::{Mode as SpiMode, Phase, Polarity, MODE_0};
19
20use crate::Error;
21
22#[derive(Debug, Clone, PartialEq)]
23pub struct Info {
24    manufacturer: String,
25    product: String,
26    serial: String,
27}
28
29/// CP2130 command enumeration
30#[derive(Debug, PartialEq, Clone, Copy)]
31pub enum Commands {
32    GetClockDivider = 0x46,
33    GetEventCounter = 0x44,
34    GetFullThreshold = 0x34,
35    GetGpioChipSelect = 0x24,
36    GetGpioModeAndLevel = 0x22,
37    GetGpioValues = 0x20,
38    GetRtrState = 0x36,
39    GetSpiWord = 0x30,
40    GetSpiDelay = 0x32,
41    GetReadOnlyVersion = 0x11,
42    ResetDevice = 0x10,
43    SetClockDivider = 0x47,
44    SetEventCOunter = 0x45,
45    SetFullThreshold = 0x35,
46    SetGpioChipSelect = 0x25,
47    SetGpioModeAndLevel = 0x23,
48    SetGpioValues = 0x21,
49    SetRtrStop = 0x37,
50    SetSpiWord = 0x31,
51    SetSpiDelay = 0x33,
52}
53
54/// Default CP2130 VID
55pub const VID: u16 = 0x10c4;
56
57/// Default CP2130 PID
58pub const PID: u16 = 0x87a0;
59
60bitflags!(
61    /// USB request type flags
62    pub struct RequestType: u8 {
63        const HOST_TO_DEVICE = 0b0000_0000;
64        const DEVICE_TO_HOST = 0b1000_0000;
65
66        const TYPE_STANDARD = 0b0000_0000;
67        const TYPE_CLASS =    0b0010_0000;
68        const TYPE_VENDOR =   0b0100_0000;
69
70        const RECIPIENT_DEVICE =    0b0000_0000;
71        const RECIPIENT_INTERFACE = 0b0000_0001;
72        const RECIPIENT_ENDPOINT =  0b0000_0010;
73        const RECIPIENT_OTHER =     0b0000_0011;
74    }
75);
76
77bitflags!(
78    /// Gpio PIN masks for multiple pin operations
79    /// The endianness of this varies depending on where it is used...
80    pub struct GpioLevels: u16 {
81        const GPIO_10 = (1 << 14);
82        const GPIO_9  = (1 << 13);
83        const GPIO_8  = (1 << 12);
84        const GPIO_7  = (1 << 11);
85        const GPIO_6  = (1 << 10);
86        const GPIO_5  = (1 << 8);
87
88        const GPIO_4  = (1 << 7);
89        const GPIO_3  = (1 << 6);
90        const GPIO_2  = (1 << 5);
91        const GPIO_1  = (1 << 4);
92        const GPIO_0  = (1 << 3);
93    }
94);
95
96/// GPIO mode enumeration
97#[derive(Debug, PartialEq, Clone, Copy)]
98pub enum GpioMode {
99    Input = 0x00,
100    OpenDrain = 0x01,
101    PushPull = 0x02,
102}
103
104impl FromStr for GpioMode {
105    type Err = String;
106
107    fn from_str(s: &str) -> Result<Self, Self::Err> {
108        match s {
109            "input" => Ok(Self::Input),
110            "open-drain" => Ok(Self::OpenDrain),
111            "push-pull" => Ok(Self::PushPull),
112            _ => Err(format!(
113                "Unrecognised GPIO mode, try 'input', 'open-drain', or 'push-pull'"
114            )),
115        }
116    }
117}
118
119/// GPIO level enumeration
120#[derive(Debug, PartialEq, Clone, Copy)]
121pub enum GpioLevel {
122    Low = 0x00,
123    High = 0x01,
124}
125
126impl FromStr for GpioLevel {
127    type Err = String;
128
129    fn from_str(s: &str) -> Result<Self, Self::Err> {
130        match s {
131            "1" | "true" | "high" => Ok(Self::High),
132            "0" | "false" | "low" => Ok(Self::Low),
133            _ => Err(format!("Unrecognised GPIO level, try 'high' or 'low'")),
134        }
135    }
136}
137
138/// Transfer command enumeration
139#[derive(Debug, PartialEq, Clone)]
140pub enum TransferCommand {
141    Read = 0x00,
142    Write = 0x01,
143    WriteRead = 0x02,
144    ReadWithRTR = 0x04,
145}
146
147/// Inner struct contains CP2130 IO functions
148/// This is used to split SPI and GPIO components
149pub(crate) struct Inner {
150    _device: UsbDevice<UsbContext>,
151    handle: DeviceHandle<UsbContext>,
152    endpoints: Endpoints,
153
154    pub(crate) gpio_allocated: [bool; 11],
155    spi_clock: SpiClock,
156}
157
158/// Device specific endpoints
159/// TODO: given it's one device this could all be hard-coded
160#[derive(Debug)]
161struct Endpoints {
162    _control: Endpoint,
163    read: Endpoint,
164    write: Endpoint,
165}
166
167/// Internal endpoint representations
168#[derive(Debug, PartialEq, Clone)]
169struct Endpoint {
170    config: u8,
171    iface: u8,
172    setting: u8,
173    address: u8,
174}
175
176/// Options for creating a device instance
177#[derive(Debug, PartialEq, Clone)]
178#[cfg_attr(feature = "clap", derive(clap::Parser))]
179pub struct UsbOptions {
180    #[cfg_attr(feature = "clap", clap(long))]
181    /// Detach kernel driver if required
182    pub detach_kernel_driver: bool,
183
184    #[cfg_attr(feature = "clap", clap(long))]
185    /// Attempt to claim interface
186    pub claim_interface: bool,
187}
188
189impl Default for UsbOptions {
190    /// Generate os-specific defaults for connection options
191    fn default() -> Self {
192        Self {
193            #[cfg(target_os = "linux")]
194            detach_kernel_driver: true,
195            #[cfg(target_os = "windows")]
196            detach_kernel_driver: false,
197            #[cfg(target_os = "macos")]
198            detach_kernel_driver: true,
199
200            #[cfg(target_os = "linux")]
201            claim_interface: false,
202            #[cfg(target_os = "windows")]
203            claim_interface: true,
204            #[cfg(target_os = "macos")]
205            claim_interface: true,
206        }
207    }
208}
209
210impl Inner {
211    /// Create a new CP2130 instance from a libusb device and descriptor
212    pub fn new(
213        device: UsbDevice<UsbContext>,
214        descriptor: DeviceDescriptor,
215        opts: UsbOptions,
216    ) -> Result<(Self, Info), Error> {
217        let timeout = Duration::from_millis(200);
218
219        // Fetch device handle
220        let mut handle = match device.open() {
221            Ok(v) => v,
222            Err(e) => {
223                error!("Opening device: {}", e);
224                return Err(Error::Usb(e));
225            }
226        };
227
228        // Reset device
229        handle.reset()?;
230
231        // Fetch base configuration
232        let languages = handle.read_languages(timeout)?;
233        let active_config = handle.active_configuration()?;
234
235        trace!("Active configuration: {}", active_config);
236        trace!("Languages: {:?}", languages);
237
238        // Check a language is available
239        if languages.len() == 0 {
240            return Err(Error::NoLanguages);
241        }
242
243        // Fetch information
244        let language = languages[0];
245        let manufacturer = handle.read_manufacturer_string(language, &descriptor, timeout)?;
246        let product = handle.read_product_string(language, &descriptor, timeout)?;
247        let serial = handle.read_serial_number_string(language, &descriptor, timeout)?;
248        let info = Info {
249            manufacturer,
250            product,
251            serial,
252        };
253
254        // Check at least one configuration exists
255        if descriptor.num_configurations() != 1 {
256            error!("Unexpected number of configurations");
257            return Err(Error::Configurations);
258        }
259
260        // Connect to endpoints
261        let config_desc = device.config_descriptor(0)?;
262
263        let (mut write, mut read) = (None, None);
264
265        for interface in config_desc.interfaces() {
266            for interface_desc in interface.descriptors() {
267                for endpoint_desc in interface_desc.endpoint_descriptors() {
268                    // Create an endpoint container
269                    let e = Endpoint {
270                        config: config_desc.number(),
271                        iface: interface_desc.interface_number(),
272                        setting: interface_desc.setting_number(),
273                        address: endpoint_desc.address(),
274                    };
275
276                    trace!("Endpoint: {:?}", e);
277
278                    // Find the relevant endpoints
279                    match (endpoint_desc.transfer_type(), endpoint_desc.direction()) {
280                        (TransferType::Bulk, Direction::In) => read = Some(e),
281                        (TransferType::Bulk, Direction::Out) => write = Some(e),
282                        (_, _) => continue,
283                    }
284                }
285            }
286        }
287
288        // Configure endpoints
289        let control = Endpoint {
290            config: 1,
291            iface: 0,
292            setting: 0,
293            address: 0,
294        };
295        //control.configure(&mut handle)?;
296
297        // Detach kernel driver if required
298        // TODO: track this and re-enable kernel driver on closing?
299        if opts.detach_kernel_driver {
300            debug!("Checking for active kernel driver");
301            match handle.kernel_driver_active(control.iface)? {
302                true => {
303                    debug!("Detaching kernel driver");
304                    handle.detach_kernel_driver(control.iface)?;
305                }
306                false => {
307                    debug!("Kernel driver inactive");
308                }
309            }
310        } else {
311            debug!("Skipping kernel driver attach check");
312        }
313
314        // Claim interface
315        if opts.claim_interface {
316            debug!("Claiming device interface");
317            handle.claim_interface(control.iface)?;
318        } else {
319            debug!("Skipping claim device interface");
320        }
321
322        // Map endpoints
323        let write = match write {
324            Some(c) => c,
325            None => {
326                error!("No write endpoint found");
327                return Err(Error::Endpoint);
328            }
329        };
330        handle.set_active_configuration(write.config)?;
331
332        let read = match read {
333            Some(c) => c,
334            None => {
335                error!("No read endpoint found");
336                return Err(Error::Endpoint);
337            }
338        };
339        handle.set_active_configuration(read.config)?;
340
341        // Build endpoints
342        let endpoints = Endpoints {
343            _control: control,
344            write,
345            read,
346        };
347        Ok((
348            Inner {
349                _device: device,
350                handle,
351                endpoints,
352                gpio_allocated: [false; 11],
353                spi_clock: SpiClock::Clock12Mhz,
354            },
355            info,
356        ))
357    }
358}
359
360/// SPI clock configuration
361#[derive(Debug, PartialEq, Copy, Clone)]
362pub enum SpiClock {
363    Clock12Mhz,
364    Clock6MHz,
365    Clock3MHz,
366    Clock1_5MHz,
367    Clock750KHz,
368    Clock375MHz,
369}
370
371/// SPI operation delay added to transaction time to ensure we don't clobber previous SPI transactions
372pub const SPI_OP_DELAY_US: u64 = 100;
373
374impl SpiClock {
375    pub fn freq(&self) -> u64 {
376        match self {
377            SpiClock::Clock12Mhz => 12_000_000,
378            SpiClock::Clock6MHz => 6_000_000,
379            SpiClock::Clock3MHz => 3_000_000,
380            SpiClock::Clock1_5MHz => 1_500_000,
381            SpiClock::Clock750KHz => 750_000,
382            SpiClock::Clock375MHz => 375_000,
383        }
384    }
385
386    pub fn transfer_time(&self, len_bytes: u64) -> std::time::Duration {
387        let micros = len_bytes * 8 * 1_000_000 / self.freq();
388        Duration::from_micros(micros + SPI_OP_DELAY_US)
389    }
390}
391
392impl std::convert::TryFrom<usize> for SpiClock {
393    type Error = Error;
394
395    fn try_from(v: usize) -> Result<Self, Self::Error> {
396        match v {
397            12_000_000 => Ok(SpiClock::Clock12Mhz),
398            6_000_000 => Ok(SpiClock::Clock6MHz),
399            3_000_000 => Ok(SpiClock::Clock3MHz),
400            1_500_000 => Ok(SpiClock::Clock1_5MHz),
401            750_000 => Ok(SpiClock::Clock750KHz),
402            375_000 => Ok(SpiClock::Clock375MHz),
403            _ => Err(Error::InvalidBaud),
404        }
405    }
406}
407
408/// Chip select mode
409#[derive(Debug, PartialEq, Clone)]
410pub enum CsMode {
411    /// Auto chip select is disabled for the specified channel
412    Disabled = 0x00,
413    /// Auto chip select is enabled for the specified channel
414    Enabled = 0x01,
415    /// Auto chip select is enabled for the specified channel,
416    /// all other chip selects are disabled
417    Exclusive = 0x02,
418}
419
420pub const CPOL_TRAILING: u8 = 0 << 5;
421
422bitflags!(
423    /// Mask for delay configuration
424    pub struct DelayMask: u8 {
425        const CS_TOGGLE      = 1 << 3;
426        const PRE_DEASSERT   = 1 << 2;
427        const POST_ASSERT    = 1 << 1;
428        const INTER_BYE      = 1 << 0;
429    }
430);
431
432#[derive(Debug, PartialEq, Clone)]
433pub struct SpiDelays {
434    mask: DelayMask,
435    pre_deassert: u8,
436    post_assert: u8,
437    inter_byte: u8,
438}
439
440#[derive(PartialEq, Clone)]
441pub struct SpiConfig {
442    pub clock: SpiClock,
443    pub spi_mode: SpiMode,
444    pub cs_mode: CsMode,
445    pub cs_pin_mode: GpioMode,
446    pub delays: SpiDelays,
447}
448
449impl Default for SpiConfig {
450    fn default() -> Self {
451        Self {
452            clock: SpiClock::Clock3MHz,
453            spi_mode: MODE_0,
454            cs_mode: CsMode::Disabled,
455            cs_pin_mode: GpioMode::PushPull,
456            delays: SpiDelays {
457                mask: DelayMask::empty(),
458                pre_deassert: 0,
459                post_assert: 0,
460                inter_byte: 0,
461            },
462        }
463    }
464}
465
466impl Inner {
467    pub(crate) fn spi_configure(&mut self, channel: u8, config: SpiConfig) -> Result<(), Error> {
468        debug!(
469            "Setting SPI channel: {:?} clock: {:?} cs mode: {:?}",
470            channel, config.clock, config.cs_mode
471        );
472
473        // Set SPI channel configuration
474        self.set_spi_word(channel, config.clock, config.spi_mode, config.cs_pin_mode)?;
475
476        // Configure chip select
477        self.set_gpio_chip_select(channel, config.cs_mode)?;
478
479        // Configure delays
480        self.set_spi_delay(channel, config.delays)?;
481
482        Ok(())
483    }
484
485    pub(crate) fn set_spi_word(
486        &mut self,
487        channel: u8,
488        clock: SpiClock,
489        spi_mode: SpiMode,
490        cs_pin_mode: GpioMode,
491    ) -> Result<(), Error> {
492        let mut flags = 0;
493
494        if let Phase::CaptureOnSecondTransition = spi_mode.phase {
495            flags |= 1 << 5;
496        }
497
498        if let Polarity::IdleHigh = spi_mode.polarity {
499            flags |= 1 << 4;
500        };
501
502        if let GpioMode::PushPull = cs_pin_mode {
503            flags |= 1 << 3
504        }
505
506        flags |= (clock as u8) & 0b0111;
507
508        debug!("Set SPI word: 0x{:02x?}", flags);
509
510        let cmd = [channel, flags];
511
512        self.handle.write_control(
513            (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(),
514            Commands::SetSpiWord as u8,
515            0,
516            0,
517            &cmd,
518            Duration::from_millis(200),
519        )?;
520
521        self.spi_clock = clock;
522
523        Ok(())
524    }
525
526    pub(crate) fn reset(&mut self) -> Result<(), Error> {
527        self.handle.write_control(
528            (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(),
529            Commands::ResetDevice as u8,
530            0,
531            0,
532            &[],
533            Duration::from_millis(200),
534        )?;
535
536        Ok(())
537    }
538
539    pub(crate) fn set_spi_delay(&mut self, channel: u8, delays: SpiDelays) -> Result<(), Error> {
540        let cmd = [
541            channel,
542            delays.mask.bits(),
543            delays.inter_byte,
544            delays.post_assert,
545            delays.pre_deassert,
546        ];
547
548        self.handle.write_control(
549            (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(),
550            Commands::SetSpiDelay as u8,
551            0,
552            0,
553            &cmd,
554            Duration::from_millis(200),
555        )?;
556
557        Ok(())
558    }
559
560    pub(crate) fn set_gpio_chip_select(
561        &mut self,
562        channel: u8,
563        cs_mode: CsMode,
564    ) -> Result<(), Error> {
565        let cmd = [channel, cs_mode as u8];
566
567        self.handle.write_control(
568            (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(),
569            Commands::SetGpioChipSelect as u8,
570            0,
571            0,
572            &cmd,
573            Duration::from_millis(200),
574        )?;
575
576        Ok(())
577    }
578
579    /// Read from the SPI device
580    pub(crate) fn spi_read(&mut self, buff: &mut [u8]) -> Result<usize, Error> {
581        let mut cmd = [0u8; 8];
582        cmd[2] = TransferCommand::Read as u8;
583        LE::write_u32(&mut cmd[4..], buff.len() as u32);
584
585        trace!("SPI read (cmd: {:?})", cmd);
586
587        self.handle.write_bulk(
588            self.endpoints.write.address,
589            &cmd,
590            Duration::from_millis(200),
591        )?;
592
593        // TODO: loop for > 64-byte packets
594        let mut index = 0;
595
596        while index < buff.len() {
597            let remainder = if buff.len() > index + 64 {
598                64
599            } else {
600                buff.len() - index
601            };
602
603            debug!("SPI read (i: {}, rem: {})", index, remainder);
604
605            let n = self.handle.read_bulk(
606                self.endpoints.read.address,
607                &mut buff[index..index + remainder],
608                Duration::from_millis(200),
609            )?;
610
611            index += n;
612        }
613
614        trace!("SPI read done");
615
616        Ok(index)
617    }
618
619    /// Write to the SPI device
620    pub(crate) fn spi_write(&mut self, buff: &[u8]) -> Result<(), Error> {
621        let mut cmd = vec![0u8; buff.len() + 8];
622
623        cmd[2] = TransferCommand::Write as u8;
624        LE::write_u32(&mut cmd[4..], buff.len() as u32);
625        (&mut cmd[8..]).copy_from_slice(buff);
626
627        let t = self.spi_clock.transfer_time(buff.len() as u64);
628        trace!("SPI write (cmd: {:?} time: {} us)", cmd, t.as_micros());
629
630        self.handle.write_bulk(
631            self.endpoints.write.address,
632            &cmd,
633            Duration::from_millis(200),
634        )?;
635
636        // Wait for operation to complete so we don't confuse the device
637        // IMPORTANT NOTE: THIS IS A LOAD BEARING DELAY
638        self.delay(t);
639
640        //self.delay(Duration::from_millis(1));
641
642        trace!("SPI write done");
643
644        Ok(())
645    }
646
647    fn delay(&mut self, d: Duration) {
648        let n = SystemTime::now();
649        while n.elapsed().unwrap() < d {}
650    }
651
652    // Transfer (write-read) to and from the SPI device
653    pub(crate) fn spi_write_read(
654        &mut self,
655        buff_out: &[u8],
656        buff_in: &mut [u8],
657    ) -> Result<usize, Error> {
658        let mut cmd = vec![0u8; buff_out.len() + 8];
659
660        // TODO: split this into while loop so long packet writes work correctly
661        // At the moment the read buffer will probably be overwritten
662        cmd[2] = TransferCommand::WriteRead as u8;
663        LE::write_u32(&mut cmd[4..], buff_out.len() as u32);
664        (&mut cmd[8..]).copy_from_slice(buff_out);
665
666        let total_time = self.spi_clock.transfer_time(buff_out.len() as u64);
667        trace!(
668            "SPI transfer (cmd: {:?} time: {} us)",
669            cmd,
670            total_time.as_micros()
671        );
672
673        self.handle.write_bulk(
674            self.endpoints.write.address,
675            &cmd,
676            Duration::from_millis(200),
677        )?;
678
679        trace!("SPI transfer await resp");
680
681        let mut index = 0;
682
683        while index < buff_in.len() {
684            let remainder = if buff_in.len() > index + 64 {
685                64
686            } else {
687                buff_in.len() - index
688            };
689
690            let t = self.spi_clock.transfer_time(buff_out.len() as u64);
691
692            trace!(
693                "SPI read (len: {}, index: {}, rem: {}, time: {} us)",
694                buff_in.len(),
695                index,
696                remainder,
697                t.as_micros()
698            );
699
700            let n = self.handle.read_bulk(
701                self.endpoints.read.address,
702                &mut buff_in[index..index + remainder],
703                Duration::from_millis(200),
704            )?;
705
706            index += n;
707
708            // Wait for operation to complete before we continue
709            self.delay(t);
710        }
711
712        trace!("SPI transfer done");
713
714        Ok(index)
715    }
716
717    /// Fetch the CP2130 chip version
718    pub(crate) fn version(&mut self) -> Result<u16, Error> {
719        let mut buff = [0u8; 2];
720
721        self.handle.read_control(
722            (RequestType::DEVICE_TO_HOST | RequestType::TYPE_VENDOR).bits(),
723            Commands::GetReadOnlyVersion as u8,
724            0,
725            0,
726            &mut buff,
727            Duration::from_millis(200),
728        )?;
729
730        let version = LE::read_u16(&buff);
731
732        Ok(version)
733    }
734
735    /// Set the mode and level for a given GPIO pin
736    pub(crate) fn set_gpio_mode_level(
737        &mut self,
738        pin: u8,
739        mode: GpioMode,
740        level: GpioLevel,
741    ) -> Result<(), Error> {
742        assert!(pin <= 10);
743
744        let cmd = [pin, mode as u8, level as u8];
745
746        trace!(
747            "GPIO set pin: {} mode: {:?} level: {:?} (cmd: {:?})",
748            pin,
749            mode,
750            level,
751            cmd
752        );
753
754        self.handle.write_control(
755            (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(),
756            Commands::SetGpioModeAndLevel as u8,
757            0,
758            0,
759            &cmd,
760            Duration::from_millis(200),
761        )?;
762
763        Ok(())
764    }
765
766    /// Fetch the values for all GPIO pins
767    pub(crate) fn get_gpio_values(&mut self) -> Result<GpioLevels, Error> {
768        let mut buff = [0u8; 2];
769
770        self.handle.read_control(
771            (RequestType::DEVICE_TO_HOST | RequestType::TYPE_VENDOR).bits(),
772            Commands::GetGpioValues as u8,
773            0,
774            0,
775            &mut buff,
776            Duration::from_millis(200),
777        )?;
778
779        // Inexplicably big endian here
780        let values = GpioLevels::from_bits_truncate(BE::read_u16(&buff));
781
782        trace!("GPIO get pins (values: {:?})", values);
783
784        Ok(values)
785    }
786
787    /// Fetch the value for a given GPIO pin
788    pub(crate) fn get_gpio_level(&mut self, pin: u8) -> Result<bool, Error> {
789        assert!(pin <= 10);
790
791        let levels = self.get_gpio_values()?;
792
793        let v = match pin {
794            0 => levels.contains(GpioLevels::GPIO_0),
795            1 => levels.contains(GpioLevels::GPIO_1),
796            2 => levels.contains(GpioLevels::GPIO_2),
797            3 => levels.contains(GpioLevels::GPIO_3),
798            4 => levels.contains(GpioLevels::GPIO_4),
799            5 => levels.contains(GpioLevels::GPIO_5),
800            6 => levels.contains(GpioLevels::GPIO_6),
801            7 => levels.contains(GpioLevels::GPIO_7),
802            8 => levels.contains(GpioLevels::GPIO_8),
803            9 => levels.contains(GpioLevels::GPIO_9),
804            10 => levels.contains(GpioLevels::GPIO_10),
805            _ => panic!("invalid pin {}", pin),
806        };
807
808        Ok(v)
809    }
810}