driver_cp2130/
lib.rs

1//! CP2130 Driver
2//!
3//!
4//! Copyright 2019 Ryan Kurte
5
6use std::{
7    sync::{Arc, Mutex},
8    time::{Duration, Instant},
9};
10
11pub use embedded_hal::spi::Mode as SpiMode;
12use rusb::{Context as UsbContext, Device as UsbDevice, DeviceDescriptor};
13
14pub mod device;
15pub mod manager;
16pub mod prelude;
17
18use crate::device::*;
19pub use crate::device::{GpioLevel, GpioMode, SpiClock, SpiConfig, UsbOptions};
20
21#[derive(Debug, thiserror::Error)]
22pub enum Error {
23    //    Io(IoError),
24    #[error("USB error: {0}")]
25    Usb(rusb::Error),
26
27    #[error("No matching endpoint languages found")]
28    NoLanguages,
29
30    #[error("No valid endpoint configuration found")]
31    Configurations,
32    #[error("No matching endpoint found")]
33    Endpoint,
34    #[error("GPIO pin already in use")]
35    GpioInUse,
36    #[error("Invalid SPI index")]
37    InvalidIndex,
38    #[error("Invalid SPI baud rate")]
39    InvalidBaud,
40}
41
42impl From<rusb::Error> for Error {
43    fn from(e: rusb::Error) -> Self {
44        Error::Usb(e)
45    }
46}
47
48/// CP2130 provides methods to interact with the device, as well as create new spi and gpio connectors.
49pub struct Cp2130 {
50    inner: Arc<Mutex<Inner>>,
51    info: Info,
52}
53
54/// Device trait provides methods directly on the CP2130
55pub trait Device {
56    /// Read from the SPI device
57    fn spi_read(&self, buff: &mut [u8]) -> Result<usize, Error>;
58
59    /// Write to the SPI device
60    fn spi_write(&self, buff: &[u8]) -> Result<(), Error>;
61
62    // Transfer (write-read) to and from the SPI device
63    fn spi_write_read(&self, buff_out: &[u8], buff_in: &mut [u8]) -> Result<usize, Error>;
64
65    /// Fetch the CP2130 chip version
66    fn version(&self) -> Result<u16, Error>;
67
68    /// Set the mode and level for a given GPIO pin
69    fn set_gpio_mode_level(&self, pin: u8, mode: GpioMode, level: GpioLevel) -> Result<(), Error>;
70
71    /// Fetch the values for all GPIO pins
72    fn get_gpio_values(&self) -> Result<GpioLevels, Error>;
73
74    /// Fetch the value for a given GPIO pin
75    fn get_gpio_level(&self, pin: u8) -> Result<bool, Error>;
76}
77
78impl Cp2130 {
79    /// Create a new CP2130 instance from a libusb device and descriptor
80    pub fn new(
81        device: UsbDevice<UsbContext>,
82        descriptor: DeviceDescriptor,
83        options: UsbOptions,
84    ) -> Result<Self, Error> {
85        // Connect to device
86        let (inner, info) = Inner::new(device, descriptor, options)?;
87        let inner = Arc::new(Mutex::new(inner));
88
89        // Create wrapper object
90        Ok(Self { info, inner })
91    }
92
93    /// Fetch information for the connected device
94    pub fn info(&self) -> Info {
95        self.info.clone()
96    }
97
98    pub fn reset(&self) -> Result<(), Error> {
99        self.inner.lock().unwrap().reset()
100    }
101
102    /// Create an SPI connector with an optional CS pin
103    pub fn spi(&self, channel: u8, config: SpiConfig, cs_pin: Option<u8>) -> Result<Spi, Error> {
104        let mut inner = self.inner.lock().unwrap();
105
106        // Configure CS pin if provided
107        if let Some(cs) = cs_pin {
108            inner.set_gpio_mode_level(cs, GpioMode::PushPull, GpioLevel::High)?;
109        }
110
111        // Configure SPI
112        inner.spi_configure(channel, config)?;
113
114        Ok(Spi {
115            inner: self.inner.clone(),
116            _channel: channel,
117            cs: cs_pin,
118        })
119    }
120
121    /// Create a GPIO OutputPin
122    pub fn gpio_out(
123        &self,
124        index: u8,
125        mode: GpioMode,
126        level: GpioLevel,
127    ) -> Result<OutputPin, Error> {
128        let mut inner = self.inner.lock().unwrap();
129
130        if inner.gpio_allocated[index as usize] {
131            return Err(Error::GpioInUse);
132        }
133
134        inner.set_gpio_mode_level(index, mode, level)?;
135        inner.gpio_allocated[index as usize] = true;
136
137        Ok(OutputPin {
138            index,
139            mode,
140            inner: self.inner.clone(),
141        })
142    }
143
144    /// Create a GPIO InputPin
145    pub fn gpio_in(&self, index: u8) -> Result<InputPin, Error> {
146        let mut inner = self.inner.lock().unwrap();
147
148        if inner.gpio_allocated[index as usize] {
149            return Err(Error::GpioInUse);
150        }
151
152        inner.set_gpio_mode_level(index, GpioMode::Input, GpioLevel::Low)?;
153        inner.gpio_allocated[index as usize] = true;
154
155        Ok(InputPin {
156            index,
157            inner: self.inner.clone(),
158        })
159    }
160}
161
162/// Underlying device functions
163impl Device for Cp2130 {
164    fn spi_read(&self, buff: &mut [u8]) -> Result<usize, Error> {
165        let mut inner = self.inner.lock().unwrap();
166        inner.spi_read(buff)
167    }
168
169    fn spi_write(&self, buff: &[u8]) -> Result<(), Error> {
170        let mut inner = self.inner.lock().unwrap();
171        inner.spi_write(buff)
172    }
173
174    fn spi_write_read(&self, buff_out: &[u8], buff_in: &mut [u8]) -> Result<usize, Error> {
175        let mut inner = self.inner.lock().unwrap();
176        inner.spi_write_read(buff_out, buff_in)
177    }
178
179    fn version(&self) -> Result<u16, Error> {
180        let mut inner = self.inner.lock().unwrap();
181        inner.version()
182    }
183
184    fn set_gpio_mode_level(&self, pin: u8, mode: GpioMode, level: GpioLevel) -> Result<(), Error> {
185        let mut inner = self.inner.lock().unwrap();
186        inner.set_gpio_mode_level(pin, mode, level)
187    }
188
189    fn get_gpio_values(&self) -> Result<GpioLevels, Error> {
190        let mut inner = self.inner.lock().unwrap();
191        inner.get_gpio_values()
192    }
193
194    fn get_gpio_level(&self, pin: u8) -> Result<bool, Error> {
195        let mut inner = self.inner.lock().unwrap();
196        inner.get_gpio_level(pin)
197    }
198}
199
200/// Spi object implements embedded-hal SPI traits for the CP2130
201pub struct Spi {
202    // TODO: use channel configuration
203    _channel: u8,
204    // Handle for device singleton
205    inner: Arc<Mutex<Inner>>,
206    // CS pin index
207    cs: Option<u8>,
208}
209
210use embedded_hal::spi::Operation as SpiOp;
211
212impl embedded_hal::spi::SpiDevice<u8> for Spi {
213    fn transaction(&mut self, operations: &mut [SpiOp<'_, u8>]) -> Result<(), Self::Error> {
214        let mut i = self.inner.lock().unwrap();
215
216        // Assert CS if available
217        if let Some(cs) = self.cs {
218            i.set_gpio_mode_level(cs, GpioMode::PushPull, GpioLevel::Low)?;
219        }
220
221        for o in operations {
222            // Run operation and collect errors
223            let err = match o {
224                SpiOp::Write(w) => i.spi_write(w).err(),
225                SpiOp::Transfer(r, w) => i.spi_write_read(w, r).err(),
226                SpiOp::TransferInPlace(b) => {
227                    let out = b.to_vec();
228                    i.spi_write_read(&out, b).err()
229                }
230                SpiOp::Read(r) => {
231                    let out = vec![0u8; r.len()];
232                    i.spi_write_read(&out, r).err()
233                }
234                SpiOp::DelayNs(ns) => {
235                    let now = Instant::now();
236                    while now.elapsed() < Duration::from_nanos(*ns as u64) {}
237                    None
238                }
239            };
240
241            // Check for errors
242            if let Some(e) = err {
243                // Deassert CS on failure
244                if let Some(cs) = self.cs {
245                    i.set_gpio_mode_level(cs, GpioMode::PushPull, GpioLevel::High)?;
246                }
247
248                // Return error
249                return Err(e);
250            }
251        }
252
253        // Clear CS if enabled
254        if let Some(cs) = self.cs {
255            i.set_gpio_mode_level(cs, GpioMode::PushPull, GpioLevel::Low)?;
256        }
257
258        Ok(())
259    }
260}
261
262impl embedded_hal::spi::ErrorType for Spi {
263    type Error = Error;
264}
265
266impl embedded_hal::spi::Error for Error {
267    fn kind(&self) -> embedded_hal::spi::ErrorKind {
268        embedded_hal::spi::ErrorKind::Other
269    }
270}
271/// InputPin object implements embedded-hal InputPin traits for the CP2130
272pub struct InputPin {
273    index: u8,
274    inner: Arc<Mutex<Inner>>,
275}
276
277impl embedded_hal::digital::InputPin for InputPin {
278    fn is_high(&mut self) -> Result<bool, Self::Error> {
279        self.inner.lock().unwrap().get_gpio_level(self.index)
280    }
281
282    fn is_low(&mut self) -> Result<bool, Self::Error> {
283        let v = self.is_high()?;
284        Ok(!v)
285    }
286}
287
288impl embedded_hal::digital::ErrorType for InputPin {
289    type Error = Error;
290}
291
292impl embedded_hal::digital::Error for Error {
293    fn kind(&self) -> embedded_hal::digital::ErrorKind {
294        embedded_hal::digital::ErrorKind::Other
295    }
296}
297
298/// OutputPin object implements embedded-hal OutputPin traits for the CP2130
299pub struct OutputPin {
300    index: u8,
301    mode: GpioMode,
302    inner: Arc<Mutex<Inner>>,
303}
304
305impl embedded_hal::digital::OutputPin for OutputPin {
306    fn set_high(&mut self) -> Result<(), Self::Error> {
307        self.inner
308            .lock()
309            .unwrap()
310            .set_gpio_mode_level(self.index, self.mode, GpioLevel::High)
311    }
312
313    fn set_low(&mut self) -> Result<(), Self::Error> {
314        self.inner
315            .lock()
316            .unwrap()
317            .set_gpio_mode_level(self.index, self.mode, GpioLevel::Low)
318    }
319}
320
321impl embedded_hal::digital::ErrorType for OutputPin {
322    type Error = Error;
323}