From 75e2fcf0c9dcfe2d2c88bbae1df9d2df94308cab Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Sun, 8 Oct 2023 01:20:12 +0100 Subject: [PATCH 1/4] Split Spidev into device and bus structs. Closes #99 --- src/lib.rs | 2 +- src/spi.rs | 115 ++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 102 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 17c75a4..ee52767 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,5 +53,5 @@ pub use crate::delay::Delay; pub use crate::i2c::{I2CError, I2cdev}; pub use crate::serial::{Serial, SerialError}; #[cfg(feature = "spi")] -pub use crate::spi::{SPIError, Spidev}; +pub use crate::spi::{SPIError, SpidevBus, SpidevDevice}; pub use crate::timer::{CountDown, Periodic, SysTimer}; diff --git a/src/spi.rs b/src/spi.rs index 10018fc..9e9a539 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -9,27 +9,96 @@ use std::io; use std::ops; use std::path::Path; -/// Newtype around [`spidev::Spidev`] that implements the `embedded-hal` traits +/// Spidev wrapper providing the embedded-hal [`SpiDevice`] trait. /// -/// [Delay operations][delay] are capped to 65535 microseconds. +/// Use this struct when you want a single spidev device, using a Linux-managed CS pin, +/// which is already defined in your devicetree. Linux will handle sharing the bus +/// between different SPI devices, even between different processes. /// -/// [`spidev::Spidev`]: https://docs.rs/spidev/0.5.2/spidev/struct.Spidev.html -/// [delay]: embedded_hal::spi::Operation::DelayUs -pub struct Spidev(pub spidev::Spidev); +/// You get an object that implements [`SpiDevice`], which is what most drivers require, +/// but does not implement [`SpiBus`]. In some rare cases, you may require [`SpiBus`] +/// instead; for that refer to [`SpidevBus`] below. You may also want to use [`SpiBus`] +/// if you want to handle all the CS pins yourself using GPIO. +/// +/// This struct wraps a [`spidev::Spidev`] struct, so it can be constructed directly +/// and the inner struct accessed if needed, for example to (re)configure the SPI settings. +/// +/// Note that [delay operations] on this device are capped to 65535 microseconds. +/// +/// [`SpiDevice`]: embedded_hal::spi::SpiDevice +/// [`SpiBus`]: embedded_hal::spi::SpiBus +/// [`spidev::Spidev`]: spidev::Spidev +/// [delay operations]: embedded_hal::spi::Operation::DelayUs +pub struct SpidevDevice(pub spidev::Spidev); + +/// Spidev wrapper providing the embedded-hal [`SpiBus`] trait. +/// +/// Use this struct when you require direct access to the underlying SPI bus, for +/// example when you want to use GPIOs as software-controlled CS pins to share the +/// bus with multiple devices, or because a driver requires the entire bus (for +/// example to drive smart LEDs). +/// +/// Do not use this struct if you're accessing SPI devices that already appear in your +/// device tree; you will not be able to drive CS pins that are already used by spidev +/// as GPIOs. Instead use [`SpidevDevice`]. +/// +/// This struct must still be created from a [`spidev::Spidev`] device, but there are two +/// important notes: +/// +/// 1. The CS pin associated with this spidev device will be driven whenever any device accesses +/// this bus, so it should be an unconnected or unused pin. +/// 2. No other spidev device on the same bus may be used as long as this `SpidevBus` exists, +/// as Linux will _not_ do anything to ensure this bus has exclusive access. +/// +/// It is recommended to use a dummy spidev device associated with an unused CS pin, and then use +/// regular GPIOs as CS pins if required. If you are planning to share this bus using GPIOs, the +/// [`embedded-hal-bus`] crate may be of interest. +/// +/// If necessary, you can [configure] the underlying [`spidev::Spidev`] instance with the +/// [`SPI_NO_CS`] flag set to prevent any CS pin activity. +/// +/// [`SpiDevice`]: embedded_hal::spi::SpiDevice +/// [`SpiBus`]: embedded_hal::spi::SpiBus +/// [`embedded-hal-bus`]: https://docs.rs/embedded-hal-bus/ +/// [`spidev::Spidev`]: spidev::Spidev +/// [delay operations]: embedded_hal::spi::Operation::DelayUs +/// [configure]: spidev::Spidev::configure +/// [`SPI_NO_CS`]: spidev::SpiModeFlags::SPI_NO_CS +pub struct SpidevBus(pub spidev::Spidev); + +impl SpidevDevice { + /// See [`spidev::Spidev::open`] for details. + /// + /// The provided `path` is for the specific device you wish to access. + /// Access to the bus is shared with other devices via the Linux kernel. + pub fn open

(path: P) -> Result + where + P: AsRef, + { + spidev::Spidev::open(path) + .map(SpidevDevice) + .map_err(|e| e.into()) + } +} -impl Spidev { - /// See [`spidev::Spidev::open`][0] for details. +impl SpidevBus { + /// See [`spidev::Spidev::open`] for details. /// - /// [0]: https://docs.rs/spidev/0.5.2/spidev/struct.Spidev.html#method.open + /// The provided `path` must be the _only_ device in use on its bus, + /// and note its own CS pin will be asserted for all device access, + /// so the path should be to a dummy device used only to access + /// the underlying bus. pub fn open

(path: P) -> Result where P: AsRef, { - spidev::Spidev::open(path).map(Spidev).map_err(|e| e.into()) + spidev::Spidev::open(path) + .map(SpidevBus) + .map_err(|e| e.into()) } } -impl ops::Deref for Spidev { +impl ops::Deref for SpidevDevice { type Target = spidev::Spidev; fn deref(&self) -> &Self::Target { @@ -37,7 +106,21 @@ impl ops::Deref for Spidev { } } -impl ops::DerefMut for Spidev { +impl ops::DerefMut for SpidevDevice { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl ops::Deref for SpidevBus { + type Target = spidev::Spidev; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl ops::DerefMut for SpidevBus { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } @@ -51,11 +134,15 @@ mod embedded_hal_impl { use std::convert::TryInto; use std::io::{Read, Write}; - impl ErrorType for Spidev { + impl ErrorType for SpidevDevice { + type Error = SPIError; + } + + impl ErrorType for SpidevBus { type Error = SPIError; } - impl SpiBus for Spidev { + impl SpiBus for SpidevBus { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.0.read_exact(words).map_err(|err| SPIError { err }) } @@ -97,7 +184,7 @@ mod embedded_hal_impl { } } - impl SpiDevice for Spidev { + impl SpiDevice for SpidevDevice { /// Perform a transaction against the device. [Read more][transaction] /// /// [Delay operations][delay] are capped to 65535 microseconds. From 4a69ba4b62004fd8c39d00b080805a58770bfc60 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Sun, 8 Oct 2023 21:10:46 +0100 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: Diego Barrios Romero --- src/spi.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/spi.rs b/src/spi.rs index 9e9a539..59fba7a 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -11,8 +11,8 @@ use std::path::Path; /// Spidev wrapper providing the embedded-hal [`SpiDevice`] trait. /// -/// Use this struct when you want a single spidev device, using a Linux-managed CS pin, -/// which is already defined in your devicetree. Linux will handle sharing the bus +/// Use this struct when you want a single spidev device, using a Linux-managed CS (chip-select) pin, +/// which is already defined in your device tree. Linux will handle sharing the bus /// between different SPI devices, even between different processes. /// /// You get an object that implements [`SpiDevice`], which is what most drivers require, @@ -34,23 +34,23 @@ pub struct SpidevDevice(pub spidev::Spidev); /// Spidev wrapper providing the embedded-hal [`SpiBus`] trait. /// /// Use this struct when you require direct access to the underlying SPI bus, for -/// example when you want to use GPIOs as software-controlled CS pins to share the +/// example when you want to use GPIOs as software-controlled CS (chip-select) pins to share the /// bus with multiple devices, or because a driver requires the entire bus (for /// example to drive smart LEDs). /// /// Do not use this struct if you're accessing SPI devices that already appear in your -/// device tree; you will not be able to drive CS pins that are already used by spidev +/// device tree; you will not be able to drive CS pins that are already used by `spidev` /// as GPIOs. Instead use [`SpidevDevice`]. /// /// This struct must still be created from a [`spidev::Spidev`] device, but there are two /// important notes: /// -/// 1. The CS pin associated with this spidev device will be driven whenever any device accesses +/// 1. The CS pin associated with this `spidev` device will be driven whenever any device accesses /// this bus, so it should be an unconnected or unused pin. -/// 2. No other spidev device on the same bus may be used as long as this `SpidevBus` exists, +/// 2. No other `spidev` device on the same bus may be used as long as this `SpidevBus` exists, /// as Linux will _not_ do anything to ensure this bus has exclusive access. /// -/// It is recommended to use a dummy spidev device associated with an unused CS pin, and then use +/// It is recommended to use a dummy `spidev` device associated with an unused CS pin, and then use /// regular GPIOs as CS pins if required. If you are planning to share this bus using GPIOs, the /// [`embedded-hal-bus`] crate may be of interest. /// From 4ffd221c4ad90d842db6bd27d3cc2797f406b393 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Sun, 8 Oct 2023 21:11:37 +0100 Subject: [PATCH 3/4] Add CHANGELOG entry for #100 --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbad00f..cd3f0eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.4.0-alpha.4] - 2022-10-08 + ### Changed - [breaking-change] Replace serial-rs with the serialport-rs crate. `Serial::open` now needs a baud-rate argument as well. +- [breaking-change] Split `Spidev` into `SpidevDevice` and `SpidevBus`, implementing the respective `SpiDevice` and `SpiBus` traits (#100) - Updated to `embedded-hal` `1.0.0-rc.1` release ([API changes](https://github.com/rust-embedded/embedded-hal/blob/master/embedded-hal/CHANGELOG.md#v100-rc1---2023-08-15)) - Updated to `embedded-hal-nb` `1.0.0-rc.1` release ([API changes](https://github.com/rust-embedded/embedded-hal/blob/master/embedded-hal-nb/CHANGELOG.md#v100-rc1---2023-08-15)) - Updated to `spidev` `0.6.0` release([API changes](https://github.com/rust-embedded/rust-spidev/blob/master/CHANGELOG.md#060--2023-08-03)) @@ -141,7 +144,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Initial release -[Unreleased]: https://github.com/rust-embedded/linux-embedded-hal/compare/v0.4.0-alpha.3...HEAD +[Unreleased]: https://github.com/rust-embedded/linux-embedded-hal/compare/v0.4.0-alpha.4...HEAD +[v0.4.0-alpha.4]: https://github.com/rust-embedded/linux-embedded-hal/compare/v0.4.0-alpha.3...v0.4.0-alpha.4 [v0.4.0-alpha.3]: https://github.com/rust-embedded/linux-embedded-hal/compare/v0.4.0-alpha.2...v0.4.0-alpha.3 [v0.4.0-alpha.2]: https://github.com/rust-embedded/linux-embedded-hal/compare/v0.4.0-alpha.1...v0.4.0-alpha.2 [v0.4.0-alpha.1]: https://github.com/rust-embedded/linux-embedded-hal/compare/v0.3.0...v0.4.0-alpha.1 From a0e0a8dba070de204cf724a41ec18fc3fd68a080 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Fri, 10 Nov 2023 09:32:27 +0100 Subject: [PATCH 4/4] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd3f0eb..fd45637 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -## [v0.4.0-alpha.4] - 2022-10-08 +## [v0.4.0-alpha.4] - 2023-11-10 ### Changed - [breaking-change] Replace serial-rs with the serialport-rs crate. `Serial::open` now needs a baud-rate argument as well.