From 263586e399945688ac34a772b8563607ac733b07 Mon Sep 17 00:00:00 2001 From: ryan kurte Date: Thu, 18 Apr 2024 16:18:53 +1200 Subject: [PATCH 1/2] add CS management to spi handles to avoid stacking mutexes --- src/cli.rs | 12 ++------- src/lib.rs | 79 ++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 7b93b10..0701ed0 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -160,28 +160,20 @@ fn main() { Command::SpiTransfer{data, spi_opts} => { info!("Transmit: {}", hex::encode(&data)); - let mut spi = cp2130.spi(spi_opts.channel, SpiConfig::default()).unwrap(); - - cp2130.set_gpio_mode_level(spi_opts.cs_pin, GpioMode::PushPull, GpioLevel::Low).unwrap(); + let mut spi = cp2130.spi(spi_opts.channel, SpiConfig::default(), Some(spi_opts.cs_pin)).unwrap(); let mut buff = data.clone(); spi.transfer_in_place(&mut buff).unwrap(); - cp2130.set_gpio_mode_level(spi_opts.cs_pin, GpioMode::PushPull, GpioLevel::High).unwrap(); - info!("Received: {}", hex::encode(buff)); }, Command::SpiWrite{data, spi_opts} => { info!("Transmit: {}", hex::encode(&data)); - let mut spi = cp2130.spi(spi_opts.channel, SpiConfig::default()).unwrap(); - - cp2130.set_gpio_mode_level(spi_opts.cs_pin, GpioMode::PushPull, GpioLevel::Low).unwrap(); + let mut spi = cp2130.spi(spi_opts.channel, SpiConfig::default(), Some(spi_opts.cs_pin)).unwrap(); spi.write(&data).unwrap(); - - cp2130.set_gpio_mode_level(spi_opts.cs_pin, GpioMode::PushPull, GpioLevel::High).unwrap(); }, Command::Test(opts) => { run_tests(&mut cp2130, &opts); diff --git a/src/lib.rs b/src/lib.rs index 2bc8ffe..2560626 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,14 +96,19 @@ impl Cp2130 { self.inner.lock().unwrap().reset() } - /// Create an SPI connector - pub fn spi(&self, channel: u8, config: SpiConfig) -> Result { + /// Create an SPI connector with an optional CS pin + pub fn spi(&self, channel: u8, config: SpiConfig, cs_pin: Option) -> Result { let mut inner = self.inner.lock().unwrap(); + // Configure CS pin if provided + if let Some(cs) = cs_pin { + inner.set_gpio_mode_level(cs, GpioMode::PushPull, GpioLevel::High)?; + } + // Configure SPI inner.spi_configure(channel, config)?; - Ok(Spi{inner: self.inner.clone(), _channel: channel}) + Ok(Spi{inner: self.inner.clone(), _channel: channel, cs: cs_pin}) } /// Create a GPIO OutputPin @@ -178,7 +183,10 @@ impl Device for Cp2130 { pub struct Spi { // TODO: use channel configuration _channel: u8, + // Handle for device singleton inner: Arc>, + // CS pin index + cs: Option, } use embedded_hal::spi::Operation as SpiOp; @@ -186,41 +194,54 @@ use embedded_hal::spi::Operation as SpiOp; impl embedded_hal::spi::SpiDevice for Spi { fn transaction(&mut self, operations: &mut [SpiOp<'_, u8>]) -> Result<(), Self::Error> { + let mut i = self.inner.lock().unwrap(); + + // Assert CS if available + if let Some(cs) = self.cs { + i.set_gpio_mode_level(cs, GpioMode::PushPull, GpioLevel::Low)?; + } + for o in operations { - match o { - SpiOp::Write(w) => self.write(w)?, - SpiOp::Transfer(r, w) => self.transfer(r, w)?, - SpiOp::TransferInPlace(b) => self.transfer_in_place(b)?, - SpiOp::Read(r) => self.read(r)?, + // Run operation and collect errors + let err = match o { + SpiOp::Write(w) => { + i.spi_write(w).err() + }, + SpiOp::Transfer(r, w) => { + i.spi_write_read(w, r).err() + }, + SpiOp::TransferInPlace(b) => { + let out = b.to_vec(); + i.spi_write_read(&out, b).err() + }, + SpiOp::Read(r) => { + let out = vec![0u8; r.len()]; + i.spi_write_read(&out, r).err() + }, SpiOp::DelayNs(ns) => { let now = Instant::now(); while now.elapsed() < Duration::from_nanos(*ns as u64) {} + None } - } - } + }; - Ok(()) - } - - fn read(&mut self, buff: &mut [u8] ) -> Result<(), Self::Error> { - let out = vec![0u8; buff.len()]; - let _n = self.inner.lock().unwrap().spi_write_read(&out, buff)?; - Ok(()) - } + // Check for errors + if let Some(e) = err { + // Deassert CS on failure + if let Some(cs) = self.cs { + i.set_gpio_mode_level(cs, GpioMode::PushPull, GpioLevel::High)?; + } - fn write(&mut self, words: &[u8] ) -> Result<(), Self::Error> { - let _n = self.inner.lock().unwrap().spi_write(words)?; - Ok(()) - } + // Return error + return Err(e) + } + } - fn transfer<'w>(&mut self, buff: &'w mut [u8], out: &'w [u8]) -> Result<(), Self::Error> { - let _n = self.inner.lock().unwrap().spi_write_read(&out, buff)?; - Ok(()) - } + // Clear CS if enabled + if let Some(cs) = self.cs { + i.set_gpio_mode_level(cs, GpioMode::PushPull, GpioLevel::Low)?; + } - fn transfer_in_place<'w>(&mut self, buff: &'w mut [u8]) -> Result<(), Self::Error> { - let out = buff.to_vec(); - let _n = self.inner.lock().unwrap().spi_write_read(&out, buff)?; Ok(()) } } From a1c8e7db06e9edb189d1435d1fbf088d18096961 Mon Sep 17 00:00:00 2001 From: ryan kurte Date: Thu, 18 Apr 2024 16:19:19 +1200 Subject: [PATCH 2/2] fmt run --- examples/ssd1306.rs | 4 +- src/cli.rs | 104 +++++++++++-------- src/device.rs | 237 +++++++++++++++++++++++++------------------ src/lib.rs | 101 ++++++++++-------- src/manager.rs | 46 +++++---- src/prelude.rs | 10 +- tests/integration.rs | 4 - 7 files changed, 293 insertions(+), 213 deletions(-) diff --git a/examples/ssd1306.rs b/examples/ssd1306.rs index ea91816..8bcc124 100644 --- a/examples/ssd1306.rs +++ b/examples/ssd1306.rs @@ -3,15 +3,15 @@ use driver_cp2130::prelude::*; use embedded_graphics::{ - text::Text, mono_font::{ascii::FONT_6X10, MonoTextStyle}, pixelcolor::BinaryColor, prelude::*, + text::Text, }; use linux_embedded_hal::Delay; +use embedded_hal_compat::ReverseCompat as _; use ssd1306::{prelude::*, Ssd1306}; -use embedded_hal_compat::{ReverseCompat as _}; fn main() { // Find matching devices diff --git a/src/cli.rs b/src/cli.rs index 0701ed0..283795f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,14 +1,15 @@ //! CP2130 Driver CLI -//! -//! +//! +//! //! Copyright 2019 Ryan Kurte extern crate clap; use clap::Parser; -#[macro_use] extern crate log; +#[macro_use] +extern crate log; extern crate simplelog; -use simplelog::{TermLogger, LevelFilter, TerminalMode}; +use simplelog::{LevelFilter, TermLogger, TerminalMode}; use driver_cp2130::prelude::*; @@ -19,12 +20,10 @@ extern crate hex; extern crate rand; use crate::rand::Rng; - #[derive(Debug, Parser)] #[clap(name = "cp2130-util")] /// CP2130 Utility pub struct Options { - #[clap(subcommand)] pub command: Command, @@ -34,11 +33,11 @@ pub struct Options { #[clap(flatten)] pub options: UsbOptions, - #[clap(long, default_value="0")] + #[clap(long, default_value = "0")] /// Device index (to select from multiple devices) pub index: usize, - #[clap(long = "log-level", default_value="info")] + #[clap(long = "log-level", default_value = "info")] /// Enable verbose logging pub level: LevelFilter, } @@ -51,21 +50,21 @@ pub enum Command { Info, /// Set a GPIO output SetOutput { - #[clap(long, default_value="6")] + #[clap(long, default_value = "6")] /// GPIO pin index pin: u8, - #[clap(long, default_value="push-pull")] + #[clap(long, default_value = "push-pull")] /// GPIO pin mode to set (input, open drain, push-pull) mode: GpioMode, - #[clap(default_value="high")] + #[clap(default_value = "high")] /// GPIO pin state (high, low) state: GpioLevel, }, /// Read a GPIO input ReadInput { - #[clap(long, default_value="6")] + #[clap(long, default_value = "6")] /// GPIO pin index pin: u8, @@ -92,27 +91,27 @@ pub enum Command { spi_opts: SpiOpts, }, /// Test interaction with the CP2130 device - Test(TestOpts) + Test(TestOpts), } #[derive(Clone, Debug, PartialEq, Parser)] pub struct SpiOpts { - #[clap(long, default_value="0")] + #[clap(long, default_value = "0")] /// SPI Channel channel: u8, - #[clap(long, default_value="0")] + #[clap(long, default_value = "0")] /// SPI CS gpio index cs_pin: u8, } #[derive(Debug, Parser)] pub struct TestOpts { - #[clap(long, default_value="0")] + #[clap(long, default_value = "0")] /// Pin for GPIO write write_pin: u8, - #[clap(long, default_value="1")] + #[clap(long, default_value = "1")] /// Pin for GPIO read read_pin: u8, } @@ -123,12 +122,16 @@ fn parse_hex_str(src: &str) -> Result, hex::FromHexError> { hex::decode(src) } - fn main() { let opts = Options::parse(); // Setup logging - TermLogger::init(opts.level, simplelog::Config::default(), TerminalMode::Mixed).unwrap(); + TermLogger::init( + opts.level, + simplelog::Config::default(), + TerminalMode::Mixed, + ) + .unwrap(); // Find matching devices let (device, descriptor) = Manager::device(opts.filter, opts.index).unwrap(); @@ -146,55 +149,71 @@ fn main() { Command::Version => { let v = cp2130.version().unwrap(); info!("Device version: {}", v); - }, - Command::SetOutput{pin, mode, state} => { + } + Command::SetOutput { pin, mode, state } => { cp2130.set_gpio_mode_level(pin, mode, state).unwrap() - }, - Command::ReadInput{pin, mode} => { + } + Command::ReadInput { pin, mode } => { if let Some(m) = mode { cp2130.set_gpio_mode_level(pin, m, GpioLevel::Low).unwrap(); } let v = cp2130.get_gpio_level(pin).unwrap(); info!("Pin: {} value: {}", pin, v); - }, - Command::SpiTransfer{data, spi_opts} => { + } + Command::SpiTransfer { data, spi_opts } => { info!("Transmit: {}", hex::encode(&data)); - let mut spi = cp2130.spi(spi_opts.channel, SpiConfig::default(), Some(spi_opts.cs_pin)).unwrap(); + let mut spi = cp2130 + .spi( + spi_opts.channel, + SpiConfig::default(), + Some(spi_opts.cs_pin), + ) + .unwrap(); let mut buff = data.clone(); - + spi.transfer_in_place(&mut buff).unwrap(); info!("Received: {}", hex::encode(buff)); - }, - Command::SpiWrite{data, spi_opts} => { + } + Command::SpiWrite { data, spi_opts } => { info!("Transmit: {}", hex::encode(&data)); - let mut spi = cp2130.spi(spi_opts.channel, SpiConfig::default(), Some(spi_opts.cs_pin)).unwrap(); + let mut spi = cp2130 + .spi( + spi_opts.channel, + SpiConfig::default(), + Some(spi_opts.cs_pin), + ) + .unwrap(); spi.write(&data).unwrap(); - }, + } Command::Test(opts) => { run_tests(&mut cp2130, &opts); } } - } - fn run_tests(cp2130: &mut Cp2130, opts: &TestOpts) { info!("Testing GPIO read/write"); - cp2130.set_gpio_mode_level(opts.read_pin, GpioMode::Input, GpioLevel::Low).unwrap(); + cp2130 + .set_gpio_mode_level(opts.read_pin, GpioMode::Input, GpioLevel::Low) + .unwrap(); - cp2130.set_gpio_mode_level(opts.write_pin, GpioMode::PushPull, GpioLevel::Low).unwrap(); + cp2130 + .set_gpio_mode_level(opts.write_pin, GpioMode::PushPull, GpioLevel::Low) + .unwrap(); let v = cp2130.get_gpio_level(opts.read_pin).unwrap(); if v != false { error!("GPIO read error"); } - cp2130.set_gpio_mode_level(opts.write_pin, GpioMode::PushPull, GpioLevel::High).unwrap(); + cp2130 + .set_gpio_mode_level(opts.write_pin, GpioMode::PushPull, GpioLevel::High) + .unwrap(); let v = cp2130.get_gpio_level(opts.read_pin).unwrap(); if v != true { error!("GPIO read error"); @@ -202,31 +221,28 @@ fn run_tests(cp2130: &mut Cp2130, opts: &TestOpts) { info!("GPIO read/write okay"); - info!("Testing SPI write (short)"); let mut rng = rand::thread_rng(); - let data: Vec = (0..34).map(|_| rng.gen() ).collect(); + let data: Vec = (0..34).map(|_| rng.gen()).collect(); cp2130.spi_write(&data).unwrap(); info!("SPI write (short) okay"); - info!("Testing SPI write (long)"); let mut rng = rand::thread_rng(); - let data: Vec = (0..300).map(|_| rng.gen() ).collect(); + let data: Vec = (0..300).map(|_| rng.gen()).collect(); cp2130.spi_write(&data).unwrap(); info!("SPI write (long) okay"); - info!("Testing SPI transfer (short)"); let mut rng = rand::thread_rng(); - let data: Vec = (0..34).map(|_| rng.gen() ).collect(); + let data: Vec = (0..34).map(|_| rng.gen()).collect(); let mut buff = vec![0u8; data.len()]; cp2130.spi_write_read(&data, &mut buff).unwrap(); @@ -237,11 +253,10 @@ fn run_tests(cp2130: &mut Cp2130, opts: &TestOpts) { info!("SPI transfer (short) okay"); - info!("Testing SPI transfer (long)"); let mut rng = rand::thread_rng(); - let data: Vec = (0..300).map(|_| rng.gen() ).collect(); + let data: Vec = (0..300).map(|_| rng.gen()).collect(); let mut buff = vec![0u8; data.len()]; cp2130.spi_write_read(&data, &mut buff).unwrap(); @@ -251,5 +266,4 @@ fn run_tests(cp2130: &mut Cp2130, opts: &TestOpts) { } info!("SPI transfer (long) okay"); - } diff --git a/src/device.rs b/src/device.rs index c1e157d..4ab31dd 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,16 +1,19 @@ //! CP2130 Driver Device Definitions -//! -//! +//! +//! //! Copyright 2019 Ryan Kurte -use std::time::{Duration, SystemTime}; use std::str::FromStr; +use std::time::{Duration, SystemTime}; -use byteorder::{LE, BE, ByteOrder}; use bitflags::bitflags; -use log::{trace, debug, error}; +use byteorder::{ByteOrder, BE, LE}; +use log::{debug, error, trace}; -use rusb::{Device as UsbDevice, Context as UsbContext, DeviceDescriptor, DeviceHandle, Direction, TransferType}; +use rusb::{ + Context as UsbContext, Device as UsbDevice, DeviceDescriptor, DeviceHandle, Direction, + TransferType, +}; use embedded_hal::spi::{Mode as SpiMode, Phase, Polarity, MODE_0}; @@ -23,7 +26,6 @@ pub struct Info { serial: String, } - /// CP2130 command enumeration #[derive(Debug, PartialEq, Clone, Copy)] pub enum Commands { @@ -72,7 +74,6 @@ bitflags!( } ); - bitflags!( /// Gpio PIN masks for multiple pin operations /// The endianness of this varies depending on where it is used... @@ -108,7 +109,9 @@ impl FromStr for GpioMode { "input" => Ok(Self::Input), "open-drain" => Ok(Self::OpenDrain), "push-pull" => Ok(Self::PushPull), - _ => Err(format!("Unrecognised GPIO mode, try 'input', 'open-drain', or 'push-pull'")), + _ => Err(format!( + "Unrecognised GPIO mode, try 'input', 'open-drain', or 'push-pull'" + )), } } } @@ -135,9 +138,9 @@ impl FromStr for GpioLevel { /// Transfer command enumeration #[derive(Debug, PartialEq, Clone)] pub enum TransferCommand { - Read = 0x00, - Write = 0x01, - WriteRead = 0x02, + Read = 0x00, + Write = 0x01, + WriteRead = 0x02, ReadWithRTR = 0x04, } @@ -167,7 +170,7 @@ struct Endpoint { config: u8, iface: u8, setting: u8, - address: u8 + address: u8, } /// Options for creating a device instance @@ -206,15 +209,19 @@ impl Default for UsbOptions { impl Inner { /// Create a new CP2130 instance from a libusb device and descriptor - pub fn new(device: UsbDevice, descriptor: DeviceDescriptor, opts: UsbOptions) -> Result<(Self, Info), Error> { + pub fn new( + device: UsbDevice, + descriptor: DeviceDescriptor, + opts: UsbOptions, + ) -> Result<(Self, Info), Error> { let timeout = Duration::from_millis(200); - + // Fetch device handle let mut handle = match device.open() { Ok(v) => v, Err(e) => { error!("Opening device: {}", e); - return Err(Error::Usb(e)) + return Err(Error::Usb(e)); } }; @@ -230,7 +237,7 @@ impl Inner { // Check a language is available if languages.len() == 0 { - return Err(Error::NoLanguages) + return Err(Error::NoLanguages); } // Fetch information @@ -238,23 +245,26 @@ impl Inner { let manufacturer = handle.read_manufacturer_string(language, &descriptor, timeout)?; let product = handle.read_product_string(language, &descriptor, timeout)?; let serial = handle.read_serial_number_string(language, &descriptor, timeout)?; - let info = Info{manufacturer, product, serial}; + let info = Info { + manufacturer, + product, + serial, + }; // Check at least one configuration exists if descriptor.num_configurations() != 1 { error!("Unexpected number of configurations"); - return Err(Error::Configurations) + return Err(Error::Configurations); } // Connect to endpoints let config_desc = device.config_descriptor(0)?; - + let (mut write, mut read) = (None, None); for interface in config_desc.interfaces() { for interface_desc in interface.descriptors() { for endpoint_desc in interface_desc.endpoint_descriptors() { - // Create an endpoint container let e = Endpoint { config: config_desc.number(), @@ -292,10 +302,10 @@ impl Inner { true => { debug!("Detaching kernel driver"); handle.detach_kernel_driver(control.iface)?; - }, + } false => { debug!("Kernel driver inactive"); - }, + } } } else { debug!("Skipping kernel driver attach check"); @@ -314,7 +324,7 @@ impl Inner { Some(c) => c, None => { error!("No write endpoint found"); - return Err(Error::Endpoint) + return Err(Error::Endpoint); } }; handle.set_active_configuration(write.config)?; @@ -323,14 +333,27 @@ impl Inner { Some(c) => c, None => { error!("No read endpoint found"); - return Err(Error::Endpoint) + return Err(Error::Endpoint); } }; handle.set_active_configuration(read.config)?; - + // Build endpoints - let endpoints = Endpoints{_control: control, write, read}; - Ok((Inner{_device: device, handle, endpoints, gpio_allocated: [false; 11], spi_clock: SpiClock::Clock12Mhz}, info)) + let endpoints = Endpoints { + _control: control, + write, + read, + }; + Ok(( + Inner { + _device: device, + handle, + endpoints, + gpio_allocated: [false; 11], + spi_clock: SpiClock::Clock12Mhz, + }, + info, + )) } } @@ -351,9 +374,9 @@ pub const SPI_OP_DELAY_US: u64 = 100; impl SpiClock { pub fn freq(&self) -> u64 { match self { - SpiClock::Clock12Mhz => 12_000_000, - SpiClock::Clock6MHz => 6_000_000, - SpiClock::Clock3MHz => 3_000_000, + SpiClock::Clock12Mhz => 12_000_000, + SpiClock::Clock6MHz => 6_000_000, + SpiClock::Clock3MHz => 3_000_000, SpiClock::Clock1_5MHz => 1_500_000, SpiClock::Clock750KHz => 750_000, SpiClock::Clock375MHz => 375_000, @@ -369,7 +392,7 @@ impl SpiClock { impl std::convert::TryFrom for SpiClock { type Error = Error; - fn try_from(v: usize) -> Result { + fn try_from(v: usize) -> Result { match v { 12_000_000 => Ok(SpiClock::Clock12Mhz), 6_000_000 => Ok(SpiClock::Clock6MHz), @@ -416,8 +439,8 @@ pub struct SpiDelays { #[derive(PartialEq, Clone)] pub struct SpiConfig { - pub clock: SpiClock, - pub spi_mode: SpiMode, + pub clock: SpiClock, + pub spi_mode: SpiMode, pub cs_mode: CsMode, pub cs_pin_mode: GpioMode, pub delays: SpiDelays, @@ -435,17 +458,17 @@ impl Default for SpiConfig { pre_deassert: 0, post_assert: 0, inter_byte: 0, - } + }, } } } - - -impl Inner{ - +impl Inner { pub(crate) fn spi_configure(&mut self, channel: u8, config: SpiConfig) -> Result<(), Error> { - debug!("Setting SPI channel: {:?} clock: {:?} cs mode: {:?}", channel, config.clock, config.cs_mode); + debug!( + "Setting SPI channel: {:?} clock: {:?} cs mode: {:?}", + channel, config.clock, config.cs_mode + ); // Set SPI channel configuration self.set_spi_word(channel, config.clock, config.spi_mode, config.cs_pin_mode)?; @@ -459,8 +482,13 @@ impl Inner{ Ok(()) } - pub(crate) fn set_spi_word(&mut self, channel: u8, clock: SpiClock, spi_mode: SpiMode, cs_pin_mode: GpioMode) -> Result<(), Error> { - + pub(crate) fn set_spi_word( + &mut self, + channel: u8, + clock: SpiClock, + spi_mode: SpiMode, + cs_pin_mode: GpioMode, + ) -> Result<(), Error> { let mut flags = 0; if let Phase::CaptureOnSecondTransition = spi_mode.phase { @@ -479,17 +507,15 @@ impl Inner{ debug!("Set SPI word: 0x{:02x?}", flags); - let cmd = [ - channel, - flags - ]; + let cmd = [channel, flags]; self.handle.write_control( - (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(), + (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(), Commands::SetSpiWord as u8, - 0, 0, + 0, + 0, &cmd, - Duration::from_millis(200) + Duration::from_millis(200), )?; self.spi_clock = clock; @@ -498,20 +524,19 @@ impl Inner{ } pub(crate) fn reset(&mut self) -> Result<(), Error> { - self.handle.write_control( - (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(), + (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(), Commands::ResetDevice as u8, - 0, 0, + 0, + 0, &[], - Duration::from_millis(200) + Duration::from_millis(200), )?; Ok(()) } pub(crate) fn set_spi_delay(&mut self, channel: u8, delays: SpiDelays) -> Result<(), Error> { - let cmd = [ channel, delays.mask.bits(), @@ -521,29 +546,31 @@ impl Inner{ ]; self.handle.write_control( - (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(), + (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(), Commands::SetSpiDelay as u8, - 0, 0, + 0, + 0, &cmd, - Duration::from_millis(200) + Duration::from_millis(200), )?; Ok(()) } - pub(crate) fn set_gpio_chip_select(&mut self, channel: u8, cs_mode: CsMode) -> Result<(), Error> { - - let cmd = [ - channel, - cs_mode as u8, - ]; + pub(crate) fn set_gpio_chip_select( + &mut self, + channel: u8, + cs_mode: CsMode, + ) -> Result<(), Error> { + let cmd = [channel, cs_mode as u8]; self.handle.write_control( - (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(), + (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(), Commands::SetGpioChipSelect as u8, - 0, 0, + 0, + 0, &cmd, - Duration::from_millis(200) + Duration::from_millis(200), )?; Ok(()) @@ -555,7 +582,6 @@ impl Inner{ cmd[2] = TransferCommand::Read as u8; LE::write_u32(&mut cmd[4..], buff.len() as u32); - trace!("SPI read (cmd: {:?})", cmd); self.handle.write_bulk( @@ -578,7 +604,7 @@ impl Inner{ let n = self.handle.read_bulk( self.endpoints.read.address, - &mut buff[index..index+remainder], + &mut buff[index..index + remainder], Duration::from_millis(200), )?; @@ -592,7 +618,6 @@ impl Inner{ /// Write to the SPI device pub(crate) fn spi_write(&mut self, buff: &[u8]) -> Result<(), Error> { - let mut cmd = vec![0u8; buff.len() + 8]; cmd[2] = TransferCommand::Write as u8; @@ -625,8 +650,11 @@ impl Inner{ } // Transfer (write-read) to and from the SPI device - pub(crate) fn spi_write_read(&mut self, buff_out: &[u8], buff_in: &mut [u8]) -> Result { - + pub(crate) fn spi_write_read( + &mut self, + buff_out: &[u8], + buff_in: &mut [u8], + ) -> Result { let mut cmd = vec![0u8; buff_out.len() + 8]; // TODO: split this into while loop so long packet writes work correctly @@ -636,7 +664,11 @@ impl Inner{ (&mut cmd[8..]).copy_from_slice(buff_out); let total_time = self.spi_clock.transfer_time(buff_out.len() as u64); - trace!("SPI transfer (cmd: {:?} time: {} us)", cmd, total_time.as_micros()); + trace!( + "SPI transfer (cmd: {:?} time: {} us)", + cmd, + total_time.as_micros() + ); self.handle.write_bulk( self.endpoints.write.address, @@ -656,13 +688,18 @@ impl Inner{ }; let t = self.spi_clock.transfer_time(buff_out.len() as u64); - - trace!("SPI read (len: {}, index: {}, rem: {}, time: {} us)", - buff_in.len(), index, remainder, t.as_micros()); + + trace!( + "SPI read (len: {}, index: {}, rem: {}, time: {} us)", + buff_in.len(), + index, + remainder, + t.as_micros() + ); let n = self.handle.read_bulk( self.endpoints.read.address, - &mut buff_in[index..index+remainder], + &mut buff_in[index..index + remainder], Duration::from_millis(200), )?; @@ -682,11 +719,12 @@ impl Inner{ let mut buff = [0u8; 2]; self.handle.read_control( - (RequestType::DEVICE_TO_HOST | RequestType::TYPE_VENDOR).bits(), + (RequestType::DEVICE_TO_HOST | RequestType::TYPE_VENDOR).bits(), Commands::GetReadOnlyVersion as u8, - 0, 0, + 0, + 0, &mut buff, - Duration::from_millis(200) + Duration::from_millis(200), )?; let version = LE::read_u16(&buff); @@ -695,23 +733,31 @@ impl Inner{ } /// Set the mode and level for a given GPIO pin - pub(crate) fn set_gpio_mode_level(&mut self, pin: u8, mode: GpioMode, level: GpioLevel) -> Result<(), Error> { + pub(crate) fn set_gpio_mode_level( + &mut self, + pin: u8, + mode: GpioMode, + level: GpioLevel, + ) -> Result<(), Error> { assert!(pin <= 10); - - let cmd = [ - pin, - mode as u8, - level as u8, - ]; - trace!("GPIO set pin: {} mode: {:?} level: {:?} (cmd: {:?})", pin, mode, level, cmd); + let cmd = [pin, mode as u8, level as u8]; + + trace!( + "GPIO set pin: {} mode: {:?} level: {:?} (cmd: {:?})", + pin, + mode, + level, + cmd + ); self.handle.write_control( - (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(), + (RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(), Commands::SetGpioModeAndLevel as u8, - 0, 0, + 0, + 0, &cmd, - Duration::from_millis(200) + Duration::from_millis(200), )?; Ok(()) @@ -722,11 +768,12 @@ impl Inner{ let mut buff = [0u8; 2]; self.handle.read_control( - (RequestType::DEVICE_TO_HOST | RequestType::TYPE_VENDOR).bits(), + (RequestType::DEVICE_TO_HOST | RequestType::TYPE_VENDOR).bits(), Commands::GetGpioValues as u8, - 0, 0, + 0, + 0, &mut buff, - Duration::from_millis(200) + Duration::from_millis(200), )?; // Inexplicably big endian here @@ -738,7 +785,7 @@ impl Inner{ } /// Fetch the value for a given GPIO pin - pub (crate) fn get_gpio_level(&mut self, pin: u8) -> Result { + pub(crate) fn get_gpio_level(&mut self, pin: u8) -> Result { assert!(pin <= 10); let levels = self.get_gpio_values()?; @@ -761,5 +808,3 @@ impl Inner{ Ok(v) } } - - diff --git a/src/lib.rs b/src/lib.rs index 2560626..3297786 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,24 +1,26 @@ //! CP2130 Driver -//! -//! +//! +//! //! Copyright 2019 Ryan Kurte -use std::{sync::{Arc, Mutex}, time::{Instant, Duration}}; +use std::{ + sync::{Arc, Mutex}, + time::{Duration, Instant}, +}; -pub use embedded_hal::spi::{Mode as SpiMode}; -use rusb::{Device as UsbDevice, Context as UsbContext, DeviceDescriptor}; +pub use embedded_hal::spi::Mode as SpiMode; +use rusb::{Context as UsbContext, Device as UsbDevice, DeviceDescriptor}; pub mod device; pub mod manager; pub mod prelude; -pub use crate::device::{UsbOptions, GpioMode, GpioLevel, SpiConfig, SpiClock}; use crate::device::*; - +pub use crate::device::{GpioLevel, GpioMode, SpiClock, SpiConfig, UsbOptions}; #[derive(Debug, thiserror::Error)] pub enum Error { -// Io(IoError), + // Io(IoError), #[error("USB error: {0}")] Usb(rusb::Error), @@ -43,8 +45,6 @@ impl From for Error { } } - - /// CP2130 provides methods to interact with the device, as well as create new spi and gpio connectors. pub struct Cp2130 { inner: Arc>, @@ -55,36 +55,39 @@ pub struct Cp2130 { pub trait Device { /// Read from the SPI device fn spi_read(&self, buff: &mut [u8]) -> Result; - + /// Write to the SPI device fn spi_write(&self, buff: &[u8]) -> Result<(), Error>; // Transfer (write-read) to and from the SPI device fn spi_write_read(&self, buff_out: &[u8], buff_in: &mut [u8]) -> Result; - + /// Fetch the CP2130 chip version - fn version(&self) -> Result ; + fn version(&self) -> Result; /// Set the mode and level for a given GPIO pin fn set_gpio_mode_level(&self, pin: u8, mode: GpioMode, level: GpioLevel) -> Result<(), Error>; - + /// Fetch the values for all GPIO pins fn get_gpio_values(&self) -> Result; - + /// Fetch the value for a given GPIO pin fn get_gpio_level(&self, pin: u8) -> Result; } impl Cp2130 { /// Create a new CP2130 instance from a libusb device and descriptor - pub fn new(device: UsbDevice, descriptor: DeviceDescriptor, options: UsbOptions) -> Result { - + pub fn new( + device: UsbDevice, + descriptor: DeviceDescriptor, + options: UsbOptions, + ) -> Result { // Connect to device let (inner, info) = Inner::new(device, descriptor, options)?; let inner = Arc::new(Mutex::new(inner)); // Create wrapper object - Ok(Self{info, inner}) + Ok(Self { info, inner }) } /// Fetch information for the connected device @@ -108,21 +111,34 @@ impl Cp2130 { // Configure SPI inner.spi_configure(channel, config)?; - Ok(Spi{inner: self.inner.clone(), _channel: channel, cs: cs_pin}) + Ok(Spi { + inner: self.inner.clone(), + _channel: channel, + cs: cs_pin, + }) } /// Create a GPIO OutputPin - pub fn gpio_out(&self, index: u8, mode: GpioMode, level: GpioLevel) -> Result { + pub fn gpio_out( + &self, + index: u8, + mode: GpioMode, + level: GpioLevel, + ) -> Result { let mut inner = self.inner.lock().unwrap(); if inner.gpio_allocated[index as usize] { - return Err(Error::GpioInUse) + return Err(Error::GpioInUse); } inner.set_gpio_mode_level(index, mode, level)?; inner.gpio_allocated[index as usize] = true; - Ok(OutputPin{index, mode, inner: self.inner.clone()}) + Ok(OutputPin { + index, + mode, + inner: self.inner.clone(), + }) } /// Create a GPIO InputPin @@ -130,15 +146,17 @@ impl Cp2130 { let mut inner = self.inner.lock().unwrap(); if inner.gpio_allocated[index as usize] { - return Err(Error::GpioInUse) + return Err(Error::GpioInUse); } inner.set_gpio_mode_level(index, GpioMode::Input, GpioLevel::Low)?; inner.gpio_allocated[index as usize] = true; - Ok(InputPin{index, inner: self.inner.clone()}) + Ok(InputPin { + index, + inner: self.inner.clone(), + }) } - } /// Underlying device functions @@ -158,7 +176,7 @@ impl Device for Cp2130 { inner.spi_write_read(buff_out, buff_in) } - fn version(&self) -> Result { + fn version(&self) -> Result { let mut inner = self.inner.lock().unwrap(); inner.version() } @@ -192,7 +210,6 @@ pub struct Spi { use embedded_hal::spi::Operation as SpiOp; impl embedded_hal::spi::SpiDevice for Spi { - fn transaction(&mut self, operations: &mut [SpiOp<'_, u8>]) -> Result<(), Self::Error> { let mut i = self.inner.lock().unwrap(); @@ -204,20 +221,16 @@ impl embedded_hal::spi::SpiDevice for Spi { for o in operations { // Run operation and collect errors let err = match o { - SpiOp::Write(w) => { - i.spi_write(w).err() - }, - SpiOp::Transfer(r, w) => { - i.spi_write_read(w, r).err() - }, + SpiOp::Write(w) => i.spi_write(w).err(), + SpiOp::Transfer(r, w) => i.spi_write_read(w, r).err(), SpiOp::TransferInPlace(b) => { let out = b.to_vec(); i.spi_write_read(&out, b).err() - }, + } SpiOp::Read(r) => { let out = vec![0u8; r.len()]; i.spi_write_read(&out, r).err() - }, + } SpiOp::DelayNs(ns) => { let now = Instant::now(); while now.elapsed() < Duration::from_nanos(*ns as u64) {} @@ -233,7 +246,7 @@ impl embedded_hal::spi::SpiDevice for Spi { } // Return error - return Err(e) + return Err(e); } } @@ -246,7 +259,6 @@ impl embedded_hal::spi::SpiDevice for Spi { } } - impl embedded_hal::spi::ErrorType for Spi { type Error = Error; } @@ -262,7 +274,7 @@ pub struct InputPin { inner: Arc>, } -impl embedded_hal::digital::InputPin for InputPin { +impl embedded_hal::digital::InputPin for InputPin { fn is_high(&mut self) -> Result { self.inner.lock().unwrap().get_gpio_level(self.index) } @@ -292,15 +304,20 @@ pub struct OutputPin { impl embedded_hal::digital::OutputPin for OutputPin { fn set_high(&mut self) -> Result<(), Self::Error> { - self.inner.lock().unwrap().set_gpio_mode_level(self.index, self.mode, GpioLevel::High) + self.inner + .lock() + .unwrap() + .set_gpio_mode_level(self.index, self.mode, GpioLevel::High) } fn set_low(&mut self) -> Result<(), Self::Error> { - self.inner.lock().unwrap().set_gpio_mode_level(self.index, self.mode, GpioLevel::Low) + self.inner + .lock() + .unwrap() + .set_gpio_mode_level(self.index, self.mode, GpioLevel::Low) } } - impl embedded_hal::digital::ErrorType for OutputPin { type Error = Error; -} \ No newline at end of file +} diff --git a/src/manager.rs b/src/manager.rs index a90f927..36a0c3c 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -1,9 +1,11 @@ //! CP2130 Driver Device Manager -//! -//! +//! +//! //! Copyright 2019 Ryan Kurte -pub use rusb::{Device as UsbDevice, UsbContext as _, Context as UsbContext, DeviceList, DeviceDescriptor}; +pub use rusb::{ + Context as UsbContext, Device as UsbDevice, DeviceDescriptor, DeviceList, UsbContext as _, +}; #[cfg(feature = "clap")] use std::num::ParseIntError; @@ -11,12 +13,12 @@ use std::num::ParseIntError; #[cfg(feature = "clap")] use clap::Parser; -use log::{trace, debug, error}; +use log::{debug, error, trace}; +use crate::device::{PID, VID}; use crate::Error; -use crate::device::{VID, PID}; -lazy_static::lazy_static!{ +lazy_static::lazy_static! { // LibUSB context created automagically static ref CONTEXT: UsbContext = { UsbContext::new().unwrap() @@ -48,7 +50,7 @@ fn parse_hex(src: &str) -> Result { impl Default for Filter { fn default() -> Self { - Filter{vid: VID, pid: PID} + Filter { vid: VID, pid: PID } } } @@ -62,14 +64,16 @@ impl Manager { Ok(v) => v, Err(e) => { error!("Fetching devices: {}", e); - return Err(Error::Usb(e)) + return Err(Error::Usb(e)); } }; Ok(devices) } - pub fn devices_filtered(filter: Filter) -> Result, DeviceDescriptor)>, Error> { + pub fn devices_filtered( + filter: Filter, + ) -> Result, DeviceDescriptor)>, Error> { let devices = Self::devices()?; let mut matches = vec![]; @@ -78,31 +82,37 @@ impl Manager { // Fetch descriptor let device_desc = match device.device_descriptor() { Ok(d) => d, - Err(_) => continue + Err(_) => continue, }; - + trace!("Device: {:?}", device_desc); - + // Check for VID/PID match if device_desc.vendor_id() == filter.vid && device_desc.product_id() == filter.pid { matches.push((device, device_desc)); } } - + debug!("Found {} matching devices", matches.len()); - + Ok(matches) } - pub fn device(filter: Filter, index: usize) -> Result<(UsbDevice, DeviceDescriptor), Error> { + pub fn device( + filter: Filter, + index: usize, + ) -> Result<(UsbDevice, DeviceDescriptor), Error> { // Find matching devices let mut matches = Self::devices_filtered(filter)?; // Check index is valid if matches.len() < index || matches.len() == 0 { - error!("Device index ({}) exceeds number of discovered devices ({})", - index, matches.len()); - return Err(Error::InvalidIndex) + error!( + "Device index ({}) exceeds number of discovered devices ({})", + index, + matches.len() + ); + return Err(Error::InvalidIndex); } // Return match diff --git a/src/prelude.rs b/src/prelude.rs index e948289..7595b01 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,9 +1,7 @@ +pub use embedded_hal::spi::Mode as SpiMode; -pub use embedded_hal::spi::{Mode as SpiMode}; +pub use crate::{Cp2130, Device, Error as Cp2130Error, InputPin, OutputPin, Spi}; -pub use crate::{Cp2130, Device, Spi, InputPin, OutputPin, Error as Cp2130Error}; - -pub use crate::device::{UsbOptions, GpioMode, GpioLevel, SpiConfig, SpiClock}; - -pub use crate::manager::{Manager, Filter}; +pub use crate::device::{GpioLevel, GpioMode, SpiClock, SpiConfig, UsbOptions}; +pub use crate::manager::{Filter, Manager}; diff --git a/tests/integration.rs b/tests/integration.rs index 644e794..c6f1e67 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,11 +1,9 @@ - extern crate driver_cp2130; use driver_cp2130::prelude::*; #[test] #[ignore] fn integration() { - // Find matching devices let (device, descriptor) = Manager::device(Filter::default(), 0).unwrap(); @@ -16,5 +14,3 @@ fn integration() { let _ = &mut cp2130; } - -