From 9fb4c7b95780cb8e751d902147fb5be275363188 Mon Sep 17 00:00:00 2001 From: David Brown Date: Fri, 18 Oct 2024 14:08:06 -0600 Subject: [PATCH] zephyr: device: Split gpio and flash to own files Move this code out of the device.rs file, and into separate files for each module. Signed-off-by: David Brown --- zephyr/src/device.rs | 186 +------------------------------------ zephyr/src/device/flash.rs | 57 ++++++++++++ zephyr/src/device/gpio.rs | 120 ++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 183 deletions(-) create mode 100644 zephyr/src/device/flash.rs create mode 100644 zephyr/src/device/gpio.rs diff --git a/zephyr/src/device.rs b/zephyr/src/device.rs index dca7d38..08bf91a 100644 --- a/zephyr/src/device.rs +++ b/zephyr/src/device.rs @@ -7,6 +7,9 @@ use crate::sync::atomic::{AtomicUsize, Ordering}; +pub mod gpio; +pub mod flash; + // Allow dead code, because it isn't required for a given build to have any devices. /// Device uniqueness. /// @@ -35,186 +38,3 @@ impl Unique { self.0.fetch_add(1, Ordering::AcqRel) == 0 } } - -pub mod gpio { - //! Most devices in Zephyr operate on a `struct device`. This provides untyped access to - //! devices. We want to have stronger typing in the Zephyr interfaces, so most of these types - //! will be wrapped in another structure. This wraps a Gpio device, and provides methods to - //! most of the operations on gpios. - //! - //! Safey: In general, even just using gpio pins is unsafe in Zephyr. The gpio drivers are used - //! pervasively throughout Zephyr device drivers. As such, most of the calls in this module are - //! unsafe. - - use crate::raw; - use super::Unique; - - /// Global instance to help make gpio in Rust slightly safer. - /// - /// To help with safety, the rust types use a global instance of a gpio-token. Methods will - /// take a mutable reference to this, which will require either a single thread in the - /// application code, or something like a mutex or critical section to manage. The operation - /// methods are still unsafe, because we have no control over what happens with the gpio - /// operations outside of Rust code, but this will help make the Rust usage at least better. - pub struct GpioToken(()); - - static GPIO_TOKEN: Unique = Unique::new(); - - impl GpioToken { - /// Retrieves the gpio token. This is unsafe because lots of code in zephyr operates on the - /// gpio drivers. - pub unsafe fn get_instance() -> Option { - if !GPIO_TOKEN.once() { - return None; - } - Some(GpioToken(())) - } - } - - /// A single instance of a zephyr device to manage a gpio controller. A gpio controller - /// represents a set of gpio pins, that are generally operated on by the same hardware block. - pub struct Gpio { - /// The underlying device itself. - #[allow(dead_code)] - pub(crate) device: *const raw::device, - } - - impl Gpio { - /// Constructor, used by the devicetree generated code. - /// - /// TODO: Guarantee single instancing. - pub(crate) unsafe fn new(unique: &Unique, device: *const raw::device) -> Option { - if !unique.once() { - return None; - } - Some(Gpio { device }) - } - - /// Verify that the device is ready for use. At a minimum, this means the device has been - /// successfully initialized. - pub fn is_ready(&self) -> bool { - unsafe { - raw::device_is_ready(self.device) - } - } - } - - /// A GpioPin represents a single pin on a gpio device. - /// - /// This is a lightweight wrapper around the Zephyr `gpio_dt_spec` structure. Note that - /// multiple pins may share a gpio controller, and as such, all methods on this are both unsafe, - /// and require a mutable reference to the [`GpioToken`]. - #[allow(dead_code)] - pub struct GpioPin { - pub(crate) pin: raw::gpio_dt_spec, - } - - impl GpioPin { - /// Constructor, used by the devicetree generated code. - /// - /// TODO: Guarantee single instancing. - pub(crate) unsafe fn new(unique: &Unique, device: *const raw::device, pin: u32, dt_flags: u32) -> Option { - if !unique.once() { - return None; - } - Some(GpioPin { - pin: raw::gpio_dt_spec { - port: device, - pin: pin as raw::gpio_pin_t, - dt_flags: dt_flags as raw::gpio_dt_flags_t, - } - }) - } - - /// Verify that the device is ready for use. At a minimum, this means the device has been - /// successfully initialized. - pub fn is_ready(&self) -> bool { - self.get_gpio().is_ready() - } - - /// Get the underlying Gpio device. - pub fn get_gpio(&self) -> Gpio { - Gpio { - device: self.pin.port, - } - } - - /// Configure a single pin. - pub unsafe fn configure(&mut self, _token: &mut GpioToken, extra_flags: raw::gpio_flags_t) { - // TODO: Error? - unsafe { - raw::gpio_pin_configure(self.pin.port, - self.pin.pin, - self.pin.dt_flags as raw::gpio_flags_t | extra_flags); - } - } - - /// Toggle pin level. - pub unsafe fn toggle_pin(&mut self, _token: &mut GpioToken) { - // TODO: Error? - unsafe { - raw::gpio_pin_toggle_dt(&self.pin); - } - } - } -} - -pub mod flash { - //! Device wrappers for flash controllers, and flash partitions. - - use crate::raw; - use super::Unique; - - /// A flash controller - /// - /// This is a wrapper around the `struct device` in Zephyr that represents a flash controller. - /// Using the flash controller allows flash operations on the entire device. See - /// [`FlashPartition`] for a wrapper that limits the operation to a partition as defined in the - /// DT. - #[allow(dead_code)] - pub struct FlashController { - pub(crate) device: *const raw::device, - } - - impl FlashController { - /// Constructor, intended to be called by devicetree generated code. - pub(crate) unsafe fn new(unique: &Unique, device: *const raw::device) -> Option { - if !unique.once() { - return None; - } - - Some(FlashController { device }) - } - } - - /// A wrapper for flash partitions. There is no Zephyr struct that corresponds with this - /// information, which is typically used in a more direct underlying manner. - #[allow(dead_code)] - pub struct FlashPartition { - /// The underlying controller. - #[allow(dead_code)] - pub(crate) controller: FlashController, - #[allow(dead_code)] - pub(crate) offset: u32, - #[allow(dead_code)] - pub(crate) size: u32, - } - - impl FlashPartition { - /// Constructor, intended to be called by devicetree generated code. - pub(crate) unsafe fn new(unique: &Unique, device: *const raw::device, offset: u32, size: u32) -> Option { - if !unique.once() { - return None; - } - - // The `get_instance` on the flash controller would try to guarantee a unique instance, - // but in this case, we need one for each device, so just construct it here. - // TODO: This is not actually safe. - let controller = FlashController { device }; - Some(FlashPartition { controller, offset, size }) - } - } - - // Note that currently, the flash partition shares the controller, so the underlying operations - // are not actually safe. Need to rethink how to manage this. -} diff --git a/zephyr/src/device/flash.rs b/zephyr/src/device/flash.rs new file mode 100644 index 0000000..0fe0d21 --- /dev/null +++ b/zephyr/src/device/flash.rs @@ -0,0 +1,57 @@ +//! Device wrappers for flash controllers, and flash partitions. + +use crate::raw; +use super::Unique; + +/// A flash controller +/// +/// This is a wrapper around the `struct device` in Zephyr that represents a flash controller. +/// Using the flash controller allows flash operations on the entire device. See +/// [`FlashPartition`] for a wrapper that limits the operation to a partition as defined in the +/// DT. +#[allow(dead_code)] +pub struct FlashController { + pub(crate) device: *const raw::device, +} + +impl FlashController { + /// Constructor, intended to be called by devicetree generated code. + pub(crate) unsafe fn new(unique: &Unique, device: *const raw::device) -> Option { + if !unique.once() { + return None; + } + + Some(FlashController { device }) + } +} + +/// A wrapper for flash partitions. There is no Zephyr struct that corresponds with this +/// information, which is typically used in a more direct underlying manner. +#[allow(dead_code)] +pub struct FlashPartition { + /// The underlying controller. + #[allow(dead_code)] + pub(crate) controller: FlashController, + #[allow(dead_code)] + pub(crate) offset: u32, + #[allow(dead_code)] + pub(crate) size: u32, +} + +impl FlashPartition { + /// Constructor, intended to be called by devicetree generated code. + pub(crate) unsafe fn new(unique: &Unique, device: *const raw::device, offset: u32, size: u32) -> Option { + if !unique.once() { + return None; + } + + // The `get_instance` on the flash controller would try to guarantee a unique instance, + // but in this case, we need one for each device, so just construct it here. + // TODO: This is not actually safe. + let controller = FlashController { device }; + Some(FlashPartition { controller, offset, size }) + } +} + +// Note that currently, the flash partition shares the controller, so the underlying operations +// are not actually safe. Need to rethink how to manage this. diff --git a/zephyr/src/device/gpio.rs b/zephyr/src/device/gpio.rs new file mode 100644 index 0000000..be0ee2e --- /dev/null +++ b/zephyr/src/device/gpio.rs @@ -0,0 +1,120 @@ +//! Most devices in Zephyr operate on a `struct device`. This provides untyped access to +//! devices. We want to have stronger typing in the Zephyr interfaces, so most of these types +//! will be wrapped in another structure. This wraps a Gpio device, and provides methods to +//! most of the operations on gpios. +//! +//! Safey: In general, even just using gpio pins is unsafe in Zephyr. The gpio drivers are used +//! pervasively throughout Zephyr device drivers. As such, most of the calls in this module are +//! unsafe. + +use crate::raw; +use super::Unique; + +/// Global instance to help make gpio in Rust slightly safer. +/// +/// To help with safety, the rust types use a global instance of a gpio-token. Methods will +/// take a mutable reference to this, which will require either a single thread in the +/// application code, or something like a mutex or critical section to manage. The operation +/// methods are still unsafe, because we have no control over what happens with the gpio +/// operations outside of Rust code, but this will help make the Rust usage at least better. +pub struct GpioToken(()); + +static GPIO_TOKEN: Unique = Unique::new(); + +impl GpioToken { + /// Retrieves the gpio token. This is unsafe because lots of code in zephyr operates on the + /// gpio drivers. + pub unsafe fn get_instance() -> Option { + if !GPIO_TOKEN.once() { + return None; + } + Some(GpioToken(())) + } +} + +/// A single instance of a zephyr device to manage a gpio controller. A gpio controller +/// represents a set of gpio pins, that are generally operated on by the same hardware block. +pub struct Gpio { + /// The underlying device itself. + #[allow(dead_code)] + pub(crate) device: *const raw::device, +} + +impl Gpio { + /// Constructor, used by the devicetree generated code. + /// + /// TODO: Guarantee single instancing. + pub(crate) unsafe fn new(unique: &Unique, device: *const raw::device) -> Option { + if !unique.once() { + return None; + } + Some(Gpio { device }) + } + + /// Verify that the device is ready for use. At a minimum, this means the device has been + /// successfully initialized. + pub fn is_ready(&self) -> bool { + unsafe { + raw::device_is_ready(self.device) + } + } +} + +/// A GpioPin represents a single pin on a gpio device. +/// +/// This is a lightweight wrapper around the Zephyr `gpio_dt_spec` structure. Note that +/// multiple pins may share a gpio controller, and as such, all methods on this are both unsafe, +/// and require a mutable reference to the [`GpioToken`]. +#[allow(dead_code)] +pub struct GpioPin { + pub(crate) pin: raw::gpio_dt_spec, +} + +impl GpioPin { + /// Constructor, used by the devicetree generated code. + /// + /// TODO: Guarantee single instancing. + pub(crate) unsafe fn new(unique: &Unique, device: *const raw::device, pin: u32, dt_flags: u32) -> Option { + if !unique.once() { + return None; + } + Some(GpioPin { + pin: raw::gpio_dt_spec { + port: device, + pin: pin as raw::gpio_pin_t, + dt_flags: dt_flags as raw::gpio_dt_flags_t, + } + }) + } + + /// Verify that the device is ready for use. At a minimum, this means the device has been + /// successfully initialized. + pub fn is_ready(&self) -> bool { + self.get_gpio().is_ready() + } + + /// Get the underlying Gpio device. + pub fn get_gpio(&self) -> Gpio { + Gpio { + device: self.pin.port, + } + } + + /// Configure a single pin. + pub unsafe fn configure(&mut self, _token: &mut GpioToken, extra_flags: raw::gpio_flags_t) { + // TODO: Error? + unsafe { + raw::gpio_pin_configure(self.pin.port, + self.pin.pin, + self.pin.dt_flags as raw::gpio_flags_t | extra_flags); + } + } + + /// Toggle pin level. + pub unsafe fn toggle_pin(&mut self, _token: &mut GpioToken) { + // TODO: Error? + unsafe { + raw::gpio_pin_toggle_dt(&self.pin); + } + } +}