diff --git a/Cargo.toml b/Cargo.toml index e8dde23c..718a5c8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "riot-wrappers" -version = "0.8.4" +version = "0.8.999" # really 0.9.0-alpha.1, but we also try hard to not break realistic 0.8 users who fixed all deprecation warnings and don't hold things wrong. authors = ["Christian Amsüss "] edition = "2021" rust-version = "1.75" @@ -30,7 +30,7 @@ bare-metal = "1" cstr = "^0.2.11" -heapless = "^0.7" +heapless = "^0.8" rand_core_06 = { package = "rand_core", version = "^0.6" } # For nimble UUID parsing and some debug implementations @@ -40,18 +40,20 @@ coap-numbers = "^0.2.0" embedded-graphics = "0.6" -coap-message-0-2 = { package = "coap-message", version = "^0.2.3" } coap-message-0-3 = { package = "coap-message", version = "^0.3.0" } -coap-handler-0-1 = { package = "coap-handler", version = "^0.1.4" } coap-handler-0-2 = { package = "coap-handler", version = "^0.2.0" } embedded-nal = { version = "0.6.0", optional = true } embedded-nal-tcpextensions = { version = "0.1", optional = true } -embedded-nal-async = { version = "0.6", optional = true } embedded-nal-async-0-7 = { package = "embedded-nal-async", version = "0.7.1", optional = true } embedded-io-async = { version = "0.6", optional = true } pin-utils = "0.1" pin-project = "1.0.11" +# as used in embedded-nal 0.6 +no-std-net-0-5 = { package = "no-std-net", version = "0.5", optional = true } +# as used in embedded-nal-async +no-std-net-0-6 = { package = "no-std-net", version = "0.6", optional = true } + embedded-hal-async = { version = "1", optional = true } critical-section = { version = "1.0", optional = true } @@ -84,8 +86,8 @@ provide_critical_section_1_0 = ["critical-section/restore-state-u32"] with_coap_message = [] with_coap_handler = [] -with_embedded_nal = ["embedded-nal", "embedded-nal-tcpextensions"] -with_embedded_nal_async = [ "embedded-nal-async", "embedded-io-async", "embedded-nal-async-0-7" ] +with_embedded_nal = ["embedded-nal", "embedded-nal-tcpextensions", "no-std-net-0-5"] +with_embedded_nal_async = [ "embedded-io-async", "embedded-nal-async-0-7", "no-std-net-0-6" ] with_embedded_hal_async = [ "embedded-hal-async" ] diff --git a/src/adc.rs b/src/adc.rs index d3567917..febc92c5 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -1,4 +1,4 @@ -use crate::Never; +use core::convert::Infallible; pub struct ADCLine(riot_sys::adc_t); @@ -43,9 +43,9 @@ impl embedded_hal_0_2::adc::Channel for ADCLine { } impl embedded_hal_0_2::adc::OneShot for ADC { - type Error = Never; + type Error = Infallible; - fn read(&mut self, pin: &mut ADCLine) -> nb::Result { + fn read(&mut self, pin: &mut ADCLine) -> nb::Result { // Sorry, blocking still Ok(unsafe { riot_sys::adc_sample(pin.0, self.resolution) }) } diff --git a/src/coap_handler/mod.rs b/src/coap_handler/mod.rs index 65d33a7f..91999242 100644 --- a/src/coap_handler/mod.rs +++ b/src/coap_handler/mod.rs @@ -1,8 +1,4 @@ //! This module provides a wrappers around a coap_handler::Handler in different versions, all of //! which can be registered at a RIOT GcoapHandler. -pub mod v0_1; pub mod v0_2; - -#[deprecated(note = "Use through the v0_1 module.")] -pub use v0_1::*; diff --git a/src/coap_handler/v0_1.rs b/src/coap_handler/v0_1.rs deleted file mode 100644 index e64bfbbe..00000000 --- a/src/coap_handler/v0_1.rs +++ /dev/null @@ -1,115 +0,0 @@ -//! This module provides a wrapper around a coap_handler::Handler that can be registered at a RIOT -//! GcoapHandler. - -use core::convert::TryInto; - -use coap_handler_0_1::{Attribute, Handler, Record, Reporting}; - -use coap_message_0_2::{MutableWritableMessage, ReadableMessage}; - -use crate::coap_message::ResponseMessage; -use crate::gcoap::PacketBuffer; - -/// Adapter to get a [crate::gcoap::Handler] from a more generic [coap_handler::Handler], typically -/// to register it through a [crate::gcoap::SingleHandlerListener]. -pub struct GcoapHandler(pub H) -where - H: Handler; - -impl crate::gcoap::Handler for GcoapHandler -where - H: Handler, -{ - fn handle(&mut self, pkt: &mut PacketBuffer) -> isize { - let request_data = self.0.extract_request_data(pkt); - let mut lengthwrapped = ResponseMessage::new(pkt); - self.0.build_response(&mut lengthwrapped, request_data); - lengthwrapped.finish() - } -} - -impl crate::gcoap::WithLinkEncoder for GcoapHandler -where - H: Handler + Reporting, -{ - fn encode(&self, writer: &mut crate::gcoap::LinkEncoder) { - for record in self.0.report() { - writer.write_comma_maybe(); - writer.write(b"<"); - for pathelement in record.path() { - writer.write(b"/"); - writer.write(pathelement.as_ref().as_bytes()); - } - writer.write(b">"); - if let Some(rel) = record.rel() { - // Not trying to be smart about whether or not we need the quotes - writer.write(b";rel=\""); - writer.write(rel.as_bytes()); - writer.write(b"\""); - } - for attr in record.attributes() { - use Attribute::*; - match attr { - Observable => writer.write(b";obs"), - Interface(i) => { - writer.write(b";if=\""); - writer.write(i.as_bytes()); - writer.write(b"\""); - } - ResourceType(r) => { - writer.write(b";rt=\""); - writer.write(r.as_bytes()); - writer.write(b"\""); - } - // FIXME: deduplicate with what's somewhere in coap-handler-implementations; - // implement remaining items - _ => (), - } - } - } - } -} - -/// Blanket implementation for mutex wrapped resources -/// -/// This is useful in combination with the defauilt implementation for Option as well. -impl<'b, H> Handler for &'b crate::mutex::Mutex -where - H: Handler, -{ - type RequestData = Option; - - fn extract_request_data<'a>(&mut self, request: &'a impl ReadableMessage) -> Self::RequestData { - self.try_lock().map(|mut h| h.extract_request_data(request)) - } - - fn estimate_length(&mut self, request: &Self::RequestData) -> usize { - if let Some(r) = request { - if let Some(mut s) = self.try_lock() { - return s.estimate_length(r); - } - } - - 1 - } - - fn build_response( - &mut self, - response: &mut impl MutableWritableMessage, - request: Self::RequestData, - ) { - if let Some(r) = request { - if let Some(mut s) = self.try_lock() { - return s.build_response(response, r); - } - } - - response.set_code( - coap_numbers::code::SERVICE_UNAVAILABLE - .try_into() - .map_err(|_| "Message type can't even exprss Service Unavailable") - .unwrap(), - ); - response.set_payload(b""); - } -} diff --git a/src/coap_handler/v0_2.rs b/src/coap_handler/v0_2.rs index cc90c603..e8c60a0b 100644 --- a/src/coap_handler/v0_2.rs +++ b/src/coap_handler/v0_2.rs @@ -22,8 +22,8 @@ impl crate::gcoap::Handler for GcoapHandler where H: Handler, { - fn handle(&mut self, pkt: &mut PacketBuffer) -> isize { - let request_data = self.0.extract_request_data(pkt); + fn handle(&mut self, pkt: PacketBuffer) -> isize { + let request_data = self.0.extract_request_data(&pkt); let mut lengthwrapped = ResponseMessage::new(pkt); match request_data { Ok(r) => { diff --git a/src/coap_message/impl_0_2.rs b/src/coap_message/impl_0_2.rs deleted file mode 100644 index c908cf09..00000000 --- a/src/coap_message/impl_0_2.rs +++ /dev/null @@ -1,85 +0,0 @@ -use coap_message_0_2::{ - MessageOption, MinimalWritableMessage, MutableWritableMessage, ReadableMessage, - WithSortedOptions, -}; - -impl<'a> MessageOption for super::MessageOption<'a> { - fn number(&self) -> u16 { - self.number - } - - fn value(&self) -> &[u8] { - self.value - } -} - -impl WithSortedOptions for super::PacketBuffer { - // valid because gcoap just reads options from the message where they are stored in sequence -} - -impl ReadableMessage for super::PacketBuffer { - type Code = u8; - type OptionsIter<'a> = super::OptionsIterator<'a>; - type MessageOption<'a> = super::MessageOption<'a>; - - fn code(&self) -> Self::Code { - self.get_code_raw() - } - - fn payload(&self) -> &[u8] { - self.payload() - } - - fn options(&self) -> Self::OptionsIter<'_> { - super::OptionsIterator(self.opt_iter()) - } -} - -impl<'a> MinimalWritableMessage for super::ResponseMessage<'a> { - type Code = u8; - type OptionNumber = u16; - - fn set_code(&mut self, code: Self::Code) { - self.message.set_code_raw(code); - } - - fn add_option(&mut self, number: Self::OptionNumber, value: &[u8]) { - if self.payload_written.is_some() { - panic!("Options can not be added after payload was added"); - } - self.message - .opt_add_opaque(number.into(), value) - .expect("Options exceed allocated buffer"); - } - - fn set_payload(&mut self, data: &[u8]) { - self.payload_mut_with_len(data.len()).copy_from_slice(data); - self.truncate(data.len()); - } -} - -impl<'a> MutableWritableMessage for super::ResponseMessage<'a> { - fn available_space(&self) -> usize { - self.message.payload().len() - } - - fn payload_mut(&mut self) -> &mut [u8] { - self.payload_written = Some(0); - let payload = self.message.payload_mut(); - payload[0] = 0xff; - &mut payload[1..] - } - - fn truncate(&mut self, len: usize) { - self.payload_written = Some(len); - } - - fn mutate_options(&mut self, mut callback: F) - where - F: FnMut(Self::OptionNumber, &mut [u8]), - { - for (opt_num, slice) in self.message.opt_iter_mut() { - callback(opt_num.into(), slice); - } - } -} diff --git a/src/coap_message/impl_0_3.rs b/src/coap_message/impl_0_3.rs index 80dacd38..bc7df531 100644 --- a/src/coap_message/impl_0_3.rs +++ b/src/coap_message/impl_0_3.rs @@ -39,14 +39,14 @@ impl<'a> MessageOption for super::MessageOption<'a> { } } -impl WithSortedOptions for super::PacketBuffer { +impl<'b> WithSortedOptions for super::PacketBuffer<'b> { // valid because gcoap just reads options from the message where they are stored in sequence } -impl ReadableMessage for super::PacketBuffer { +impl<'b> ReadableMessage for super::PacketBuffer<'b> { type Code = u8; - type OptionsIter<'a> = super::OptionsIterator<'a>; - type MessageOption<'a> = super::MessageOption<'a>; + type OptionsIter<'a> = super::OptionsIterator<'a, 'b> where Self: 'a; + type MessageOption<'a> = super::MessageOption<'a> where Self: 'a; fn code(&self) -> Self::Code { self.get_code_raw() diff --git a/src/coap_message/mod.rs b/src/coap_message/mod.rs index 5140e54a..ea9258f5 100644 --- a/src/coap_message/mod.rs +++ b/src/coap_message/mod.rs @@ -1,7 +1,6 @@ //! This module implements [coap_message::ReadableMessage] for, and a wrapper that provides //! [coap_message::WritableMessage] around RIOT's coap_pkt_t. -mod impl_0_2; mod impl_0_3; use crate::gcoap::{PacketBuffer, PacketBufferOptIter}; @@ -11,8 +10,8 @@ pub struct MessageOption<'a> { value: &'a [u8], } -pub struct OptionsIterator<'a>(PacketBufferOptIter<'a>); -impl<'a> Iterator for OptionsIterator<'a> { +pub struct OptionsIterator<'a, 'b>(PacketBufferOptIter<'a, 'b>); +impl<'a, 'b> Iterator for OptionsIterator<'a, 'b> { type Item = MessageOption<'a>; fn next(&mut self) -> Option { @@ -24,15 +23,15 @@ impl<'a> Iterator for OptionsIterator<'a> { } } -pub struct ResponseMessage<'a> { +pub struct ResponseMessage<'b> { /// Note that this is a slightly weird version of PacketBuffer, where opt_finish is never /// called, and .payload() perpetually reports the payload marker as part of the payload. - message: &'a mut PacketBuffer, + message: PacketBuffer<'b>, payload_written: Option, } -impl<'a> ResponseMessage<'a> { - pub fn new(buf: &'a mut PacketBuffer) -> Self { +impl<'b> ResponseMessage<'b> { + pub fn new(mut buf: PacketBuffer<'b>) -> Self { // Can't really err; FIXME ensure that such a check won't affect ROM too much buf.resp_init(5 << 5).unwrap(); diff --git a/src/error.rs b/src/error.rs index dd0d4284..10252dbf 100644 --- a/src/error.rs +++ b/src/error.rs @@ -25,8 +25,7 @@ pub trait NegativeErrorExt { /// manually implemented newtype around isize that'd be used to represent the Result. #[derive(Debug, PartialEq, Eq)] pub struct NumericError { - #[deprecated(note = "Use the .number() method")] - pub number: isize, + number: isize, } impl NumericError { @@ -56,13 +55,11 @@ impl NumericError { name > 0, "Error names are expected to be positive for conversion into negative error numbers." ); - #[allow(deprecated)] // it's deprecated *pub* NumericError { number: -name } } /// Numeric value of the error pub const fn number(&self) -> isize { - #[allow(deprecated)] // it's deprecated *pub* self.number } @@ -94,7 +91,6 @@ where if self >= Self::zero() { Ok(self) } else { - #[allow(deprecated)] // it's deprecated *pub* Err(NumericError { number: self.try_into().unwrap_or(-(riot_sys::EOVERFLOW as isize)), }) diff --git a/src/gcoap.rs b/src/gcoap.rs index f2043590..e8a0f133 100644 --- a/src/gcoap.rs +++ b/src/gcoap.rs @@ -180,11 +180,11 @@ where let h = &mut *h; let mut pb = PacketBuffer { - pkt, + pkt: &mut *pkt, buf, len: len.try_into().unwrap(), }; - H::handle(h, &mut pb).try_into().unwrap() + H::handle(h, pb).try_into().unwrap() } } @@ -277,7 +277,7 @@ where // Can be implemented by application code that'd then need to call some gcoap response functions, // but preferably using the coap_handler module (behind the with-coap-handler feature). pub trait Handler { - fn handle(&mut self, pkt: &mut PacketBuffer) -> isize; + fn handle(&mut self, pkt: PacketBuffer) -> isize; } /// The message buffer of a .well-known/core file in appication/link-format, as it is passed to a @@ -347,20 +347,17 @@ use riot_sys::{ /// messages are created. (For example, it does not keep the user from adding options after the /// payload marker). Use CoAP generalization for that. #[derive(Debug)] -pub struct PacketBuffer { - pkt: *mut coap_pkt_t, +pub struct PacketBuffer<'b> { + pkt: &'b mut coap_pkt_t, buf: *mut u8, len: usize, } -impl PacketBuffer { +impl<'b> PacketBuffer<'b> { /// Wrapper for coap_get_code_raw pub fn get_code_raw(&self) -> u8 { - (unsafe { - riot_sys::coap_get_code_raw( - self.pkt as *mut _, // missing const in C - ) - }) as u8 // odd return type in C + (unsafe { riot_sys::coap_get_code_raw(crate::inline_cast_ref(self.pkt)) }) as u8 + // odd return type in C } /// Wrapper for gcoap_resp_init @@ -446,14 +443,14 @@ impl PacketBuffer { .map(|_| ()) } - pub fn opt_iter<'a>(&'a self) -> PacketBufferOptIter<'a> { + pub fn opt_iter<'a>(&'a self) -> PacketBufferOptIter<'a, 'b> { PacketBufferOptIter { buffer: self, state: None, } } - pub fn opt_iter_mut<'a>(&'a mut self) -> PacketBufferOptIterMut<'a> { + pub fn opt_iter_mut<'a>(&'a mut self) -> PacketBufferOptIterMut<'a, 'b> { PacketBufferOptIterMut { buffer: self, state: None, @@ -461,12 +458,12 @@ impl PacketBuffer { } } -pub struct PacketBufferOptIter<'a> { - buffer: &'a PacketBuffer, +pub struct PacketBufferOptIter<'a, 'b> { + buffer: &'a PacketBuffer<'b>, state: Option, } -impl<'a> Iterator for PacketBufferOptIter<'a> { +impl<'a, 'b> Iterator for PacketBufferOptIter<'a, 'b> { type Item = (u16, &'a [u8]); fn next(&mut self) -> Option { @@ -512,12 +509,12 @@ impl<'a> Iterator for PacketBufferOptIter<'a> { } } -pub struct PacketBufferOptIterMut<'a> { - buffer: &'a mut PacketBuffer, +pub struct PacketBufferOptIterMut<'a, 'b> { + buffer: &'a mut PacketBuffer<'b>, state: Option, } -impl<'a> Iterator for PacketBufferOptIterMut<'a> { +impl<'a, 'b> Iterator for PacketBufferOptIterMut<'a, 'b> { type Item = (u16, &'a mut [u8]); fn next(&mut self) -> Option { diff --git a/src/gnrc.rs b/src/gnrc.rs index 53839700..088edb80 100644 --- a/src/gnrc.rs +++ b/src/gnrc.rs @@ -5,8 +5,8 @@ pub mod ipv6; pub mod netapi; pub mod netreg; -#[deprecated(note = "moved to gnrc_pktbuf toplevel module")] -pub use crate::gnrc_pktbuf as pktbuf; +#[deprecated(note = "Internally, use gnrc_pktbuf directly")] +pub(crate) use crate::gnrc_pktbuf as pktbuf; use riot_sys::{gnrc_netif_iter, gnrc_netif_t}; diff --git a/src/gnrc/netreg.rs b/src/gnrc/netreg.rs index 573bf1b2..ad6bdaf9 100644 --- a/src/gnrc/netreg.rs +++ b/src/gnrc/netreg.rs @@ -25,7 +25,7 @@ type PktsnipPort = crate::msg::v2::SendPort< /// used once more (with the risk that messages from the old registration arrive in the new one, /// which is wrong correctness-wise but safe because it'll still be a pointer to a pktsnip). #[cfg(feature = "with_msg_v2")] -pub fn register_for_messages crate::Never>( +pub fn register_for_messages crate::never::Never>( grant: PktsnipPort, nettype: riot_sys::gnrc_nettype_t, demux_ctx: u32, diff --git a/src/gpio/impl_0_2.rs b/src/gpio/impl_0_2.rs deleted file mode 100644 index 0381aa35..00000000 --- a/src/gpio/impl_0_2.rs +++ /dev/null @@ -1,73 +0,0 @@ -use super::*; - -use embedded_hal_0_2::digital::v2::{InputPin, OutputPin, ToggleableOutputPin}; - -impl InputPin for InputGPIO { - type Error = Never; - - fn is_high(&self) -> Result { - Ok(unsafe { gpio_read(self.to_c()) } != 0) - } - - fn is_low(&self) -> Result { - Ok(unsafe { gpio_read(self.to_c()) } == 0) - } -} - -impl OutputPin for OutputGPIO { - type Error = Never; - - fn set_high(&mut self) -> Result<(), Never> { - unsafe { gpio_set(self.to_c()) }; - Ok(()) - } - - fn set_low(&mut self) -> Result<(), Never> { - unsafe { gpio_clear(self.to_c()) }; - Ok(()) - } -} - -impl ToggleableOutputPin for OutputGPIO { - type Error = Never; - - fn toggle(&mut self) -> Result<(), Never> { - unsafe { gpio_toggle(self.to_c()) }; - Ok(()) - } -} - -impl InputPin for InOutGPIO { - type Error = Never; - - fn is_high(&self) -> Result { - Ok(unsafe { gpio_read(self.to_c()) } != 0) - } - - fn is_low(&self) -> Result { - Ok(unsafe { gpio_read(self.to_c()) } == 0) - } -} - -impl OutputPin for InOutGPIO { - type Error = Never; - - fn set_high(&mut self) -> Result<(), Never> { - unsafe { gpio_set(self.to_c()) }; - Ok(()) - } - - fn set_low(&mut self) -> Result<(), Never> { - unsafe { gpio_clear(self.to_c()) }; - Ok(()) - } -} - -impl ToggleableOutputPin for InOutGPIO { - type Error = Never; - - fn toggle(&mut self) -> Result<(), Never> { - unsafe { gpio_toggle(self.to_c()) }; - Ok(()) - } -} diff --git a/src/gpio/mod.rs b/src/gpio/mod.rs index 41bf5e92..6e0bc4dc 100644 --- a/src/gpio/mod.rs +++ b/src/gpio/mod.rs @@ -4,13 +4,12 @@ //! the [embedded_hal::digital::v2] traits. As recommended for infallible types, they also //! provide identically named direct methods, which (for input pins) also work on shared reference. -mod impl_0_2; mod impl_1; use riot_sys::{gpio_clear, gpio_mode_t, gpio_read, gpio_set, gpio_t, gpio_toggle, gpio_write}; use crate::error::NegativeErrorExt; -use crate::Never; +use core::convert::Infallible; /// A Rust representation of RIOT's gpio_t, representing a single pin in no particular /// configuration. diff --git a/src/i2c/impl_0_2.rs b/src/i2c/impl_0_2.rs deleted file mode 100644 index b3c0e98e..00000000 --- a/src/i2c/impl_0_2.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! Implementation of embedded-hal 0.2's I2C for [I2CDevice] -//! -//! As the implementation is on the [I2CDevice directly], all that is in this module is the -//! suitable [Error] type. - -use embedded_hal_0_2::blocking; - -use super::*; - -use riot_sys::libc; -use riot_sys::{i2c_acquire, i2c_read_bytes, i2c_release, i2c_write_bytes}; - -#[derive(Debug)] -#[non_exhaustive] -pub enum Error { - AcquireError, - WriteError(i32), - ReadError(i32), -} - -impl blocking::i2c::WriteRead for I2CDevice { - type Error = Error; - - fn write_read( - &mut self, - address: u8, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - unsafe { i2c_acquire(self.dev) }; - let err = unsafe { - i2c_write_bytes( - self.dev, - address as u16, - bytes.as_ptr() as *const libc::c_void, - bytes.len() as _, - 0, - ) - }; - if err != 0 { - unsafe { i2c_release(self.dev) }; - return Err(Error::WriteError(err)); - } - let err = unsafe { - i2c_read_bytes( - self.dev, - address as u16, - buffer.as_ptr() as *mut libc::c_void, - buffer.len() as _, - 0, - ) - }; - if err != 0 { - unsafe { i2c_release(self.dev) }; - return Err(Error::ReadError(err)); - } - unsafe { i2c_release(self.dev) }; - Ok(()) - } -} - -impl blocking::i2c::Write for I2CDevice { - type Error = Error; - - fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { - unsafe { i2c_acquire(self.dev) }; - let err = unsafe { - i2c_write_bytes( - self.dev, - address as u16, - bytes.as_ptr() as *const libc::c_void, - bytes.len() as _, - 0, - ) - }; - if err != 0 { - unsafe { i2c_release(self.dev) }; - return Err(Error::WriteError(err)); - } - unsafe { i2c_release(self.dev) }; - Ok(()) - } -} - -impl blocking::i2c::Read for I2CDevice { - type Error = Error; - - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - unsafe { i2c_acquire(self.dev) }; - let err = unsafe { - i2c_read_bytes( - self.dev, - address as u16, - buffer.as_ptr() as *mut libc::c_void, - buffer.len() as _, - 0, - ) - }; - if err != 0 { - unsafe { i2c_release(self.dev) }; - return Err(Error::ReadError(err)); - } - unsafe { i2c_release(self.dev) }; - Ok(()) - } -} diff --git a/src/i2c/mod.rs b/src/i2c/mod.rs index 7bd09683..93afc04a 100644 --- a/src/i2c/mod.rs +++ b/src/i2c/mod.rs @@ -1,6 +1,5 @@ //! Controlling the I²C bus -pub mod impl_0_2; pub mod impl_1; use riot_sys::i2c_t; @@ -25,8 +24,3 @@ impl I2CDevice { I2CDevice { dev } } } - -#[deprecated( - note = "This error type applies to embedded-hal 0.2 only, use it through the impl_0_2 module." -)] -pub use impl_0_2::Error; diff --git a/src/interrupt.rs b/src/interrupt.rs index a458a232..d4034a62 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -27,8 +27,7 @@ /// [`irq_is_in`](https://doc.riot-os.org/group__core__irq.html#ga83decbeef665d955290f730125ef0e3f) /// /// Returns true when called from an interrupt service routine -#[deprecated(note = "Use crate::thread::InThread::new() instead")] -pub fn irq_is_in() -> bool { +pub(crate) fn irq_is_in() -> bool { unsafe { riot_sys::irq_is_in() } } @@ -38,8 +37,7 @@ pub fn irq_is_in() -> bool { /// Returns true if interrupts are currently enabled /// /// Note that this only returns reliable values when called from a thread context. -#[deprecated(note = "use crate::thread::InThread::irq_is_enabled() instead")] -pub fn irq_is_enabled() -> bool { +pub(crate) fn irq_is_enabled() -> bool { unsafe { riot_sys::irq_is_enabled() } } @@ -77,25 +75,3 @@ pub fn free R>(f: F) -> R { unsafe { riot_sys::irq_restore(stored) }; ret } - -/// Wrap a Rust interrupt handler in an extern "C" wrapper that does the post-return cleaups. -/// -/// As with all code executed in interrupt contexts, the wrapped function should not panic. -/// -/// ## Caveats -/// -/// This is Cortex-M specific. -#[deprecated( - note = "See module documentation: This needs to be done manually per platform; it is incomplete as riot-wrappers provides no method of enabling platform specific interrupts, and provides no other access to configure the peripheral through registers. If it is re-introduced, it will likely carry an `InIsr` token into the function." -)] -#[macro_export] -macro_rules! interrupt { - ($isr_name:ident, $rust_handler:expr) => { - #[no_mangle] - pub extern "C" fn $isr_name() -> () { - $rust_handler(); - - unsafe { riot_sys::inline::cortexm_isr_end() }; - } - }; -} diff --git a/src/led.rs b/src/led.rs index 4282ebb7..c61cd1bb 100644 --- a/src/led.rs +++ b/src/led.rs @@ -1,16 +1,12 @@ //! Wrappers for the `LEDn_{ON,OFF,TOGGLE}` macros -use crate::Never; +use core::convert::Infallible; /// The Ith LED (calling the `LED_{ON,OFF,TOGGLE}` macros). /// /// The preferred interface for turning a LED on and off is [switch_hal::OutputSwitch]. /// /// LEDs are accessible safely; any not implemented on a board are silently ignored. -/// -/// LEDs are wrapped into embedded-hal 0.2 GPIOs for compatibility reasons (but that integration is -/// discontinued with embedded-hal 1.0); GPIO is interpreted such that "high" is having the LED on, -/// and "low" is off. pub struct LED(()); impl LED { @@ -21,31 +17,9 @@ impl LED { } impl switch_hal::OutputSwitch for LED { - type Error = Never; + type Error = Infallible; fn on(&mut self) -> Result<(), Self::Error> { - use embedded_hal_0_2::digital::v2::OutputPin; - self.set_high() - } - - fn off(&mut self) -> Result<(), Self::Error> { - use embedded_hal_0_2::digital::v2::OutputPin; - self.set_low() - } -} - -impl switch_hal::ToggleableOutputSwitch for LED { - type Error = Never; - - fn toggle(&mut self) -> Result<(), Self::Error> { - ::toggle(self) - } -} - -impl embedded_hal_0_2::digital::v2::OutputPin for LED { - type Error = Never; - - fn set_high(&mut self) -> Result<(), Never> { // unsafe: RIOT's LED functions can be called any time (and no-op on undefined LEDs) unsafe { match I { @@ -63,7 +37,7 @@ impl embedded_hal_0_2::digital::v2::OutputPin for LED { Ok(()) } - fn set_low(&mut self) -> Result<(), Never> { + fn off(&mut self) -> Result<(), Self::Error> { // unsafe: RIOT's LED functions can be called any time (and no-op on undefined LEDs) unsafe { match I { @@ -82,10 +56,10 @@ impl embedded_hal_0_2::digital::v2::OutputPin for LED { } } -impl embedded_hal_0_2::digital::v2::ToggleableOutputPin for LED { - type Error = Never; +impl switch_hal::ToggleableOutputSwitch for LED { + type Error = Infallible; - fn toggle(&mut self) -> Result<(), Never> { + fn toggle(&mut self) -> Result<(), Self::Error> { // unsafe: RIOT's LED functions can be called any time (and no-op on undefined LEDs) unsafe { match I { diff --git a/src/lib.rs b/src/lib.rs index b08230f6..a1616cbf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,7 +38,6 @@ pub mod error; mod helpers; mod never; -use never::Never; /// The identifier of the RIOT board the program is being built for (`RIOT_BOARD` in C). #[doc(alias = "RIOT_BOARD")] @@ -55,12 +54,6 @@ pub const BOARD: &'static str = { b }; -/// Name of the RIOT board that is being used -#[deprecated(note = "Access BOARD instead")] -pub const fn board() -> &'static str { - BOARD -} - /// Cast pointers around before passing them in to functions; this is sometimes needed when a /// struct is used from bindgen (`riot_sys::*`) but passed to a C2Rust function that uses its own /// definition (`riot_sys::inline::*`). diff --git a/src/main_module.rs b/src/main_module.rs index c0943517..7c8196d9 100644 --- a/src/main_module.rs +++ b/src/main_module.rs @@ -121,17 +121,6 @@ macro_rules! riot_main { }; } -#[deprecated(note = "Use `riot_main` instead, which takes multiple signatures")] -#[macro_export] -macro_rules! riot_main_with_tokens { - ($main:ident) => { - #[export_name = "main"] - pub extern "C" fn c_main() -> i32 { - unsafe { <_ as $crate::main::UsableAsMain<_>>::call_main(&$main) } - } - }; -} - /// A result trait for main methods, analogous to std::process::Termination pub trait Termination { fn report(self) -> i32; @@ -155,12 +144,12 @@ impl Termination for Result<(), E> { fn report(self) -> i32 { match self { Ok(()) => ().report(), - Err(err) => Err::(err).report(), + Err(err) => Err::(err).report(), } } } -impl Termination for crate::Never { +impl Termination for crate::never::Never { fn report(self) -> i32 { self } @@ -172,7 +161,7 @@ impl Termination for core::convert::Infallible { } } -impl Termination for Result { +impl Termination for Result { fn report(self) -> i32 { match self { Err(err) => { diff --git a/src/microbit.rs b/src/microbit.rs index f451917e..a0e4a924 100644 --- a/src/microbit.rs +++ b/src/microbit.rs @@ -6,7 +6,7 @@ use embedded_graphics::{ drawable::Pixel, geometry::Point, geometry::Size, pixelcolor::BinaryColor, DrawTarget, }; -use crate::Never; +use core::convert::Infallible; /// The 5x5 LED matrix of the micro:bit boards /// @@ -24,9 +24,9 @@ impl LEDs { } impl DrawTarget for LEDs { - type Error = Never; + type Error = Infallible; - fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Never> { + fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Infallible> { let Pixel(Point { x, y }, color) = pixel; let setter = match color { diff --git a/src/saul.rs b/src/saul.rs index 275812fd..92c8607d 100644 --- a/src/saul.rs +++ b/src/saul.rs @@ -353,6 +353,7 @@ pub enum SensorClass { #[derive(Copy, Clone, Debug)] /// Unit of measurement required to interpret numeric values in a [Phydat] exchanged with a SAUL /// device +#[non_exhaustive] pub enum Unit { /// Note that this means "data has no physical unit", and is distinct from "No unit given", /// which is `Option::::None` as opposed to `Some(Unit::None)`. @@ -393,18 +394,6 @@ pub enum Unit { } impl Unit { - // Note that on the C side the code still uses the aliases on the C side -- they're deprecated, - // but we'd need to introduce a marker, and given they'll stay deprecated on C for a release, - // we can just switch over before they go. - #[deprecated(note = "Use the GForce variant instead")] - pub const G: Self = Unit::GForce; - #[allow(non_upper_case_globals)] - #[deprecated(note = "Use the Gram variant instead")] - pub const Gr: Self = Unit::Gram; - #[allow(non_upper_case_globals)] - #[deprecated(note = "Use the Gauss variant instead")] - pub const Gs: Self = Unit::Gauss; - fn from_c(input: u8) -> Option { match input as _ { riot_sys::UNIT_NONE => Some(Unit::None), @@ -485,22 +474,6 @@ impl Unit { }) as _ } - /// String representation of a given unit (e.g. `V` or `m`) - #[deprecated( - note = "RIOT's mechanism changed; this returns None unconditionally, use .name_owned() instead" - )] - pub fn name(self) -> Option<&'static str> { - None - } - - /// Like [`.name()`](Unit::name), but with additional names like "none" or "time". - #[deprecated( - note = "RIOT's mechanism changed; this returns None unconditionally, use .name_owned() instead" - )] - pub fn name_verbose(self) -> Option<&'static str> { - None - } - /// String representation of a given unit (e.g. `V`, `m`, `none` or `time`) #[doc(alias = "phydat_unit_write")] pub fn name_owned(self) -> Option> { diff --git a/src/saul/registration.rs b/src/saul/registration.rs index 74c264cd..2993ecb9 100644 --- a/src/saul/registration.rs +++ b/src/saul/registration.rs @@ -14,7 +14,7 @@ use riot_sys::libc; use super::{Class, Phydat}; use crate::error::NegativeErrorExt; -use crate::Never; +use core::convert::Infallible; /// The single error read and write operations may produce; corresponds to an `-ECANCELED`. /// (-ENOTSUP is expressed by not having support for the operation in the first place, indicated by @@ -229,7 +229,7 @@ pub fn register_and_then( driver: &Driver, device: &DEV, name: Option<&CStr>, - f: impl FnOnce() -> Never, + f: impl FnOnce() -> crate::never::Never, ) -> ! where DEV: Sized + Sync + 'static, diff --git a/src/shell.rs b/src/shell.rs index 03c5d6a4..7d15cf02 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -136,13 +136,6 @@ pub trait CommandList ! { // unsafe: See unsafe in run_any where it's called self.run_any(linebuffer, |built, buf, len| unsafe { @@ -151,13 +144,6 @@ pub trait CommandList ! { - self.run_forever_with_buf(linebuffer) - } - /// Run the shell prompt on stdio /// /// See [shell_run_forever] for details. @@ -165,10 +151,9 @@ pub trait CommandList()` to alter that. The method will be renamed to - /// `run_forever` once that name is free. + /// trait type; use `.with_buffer_size::<>()` to alter that. #[doc(alias = "shell_run_forever")] - fn run_forever_providing_buf(&mut self) -> ! { + fn run_forever(&mut self) -> ! { let mut linebuffer = [0; BUFSIZE]; self.run_forever_with_buf(&mut linebuffer) } @@ -180,14 +165,23 @@ pub trait CommandList()` to alter that. The method will be renamed to - /// `run_once` once that name is free. + /// trait type; use `.with_buffer_size::<>()` to alter that. #[doc(alias = "shell_run_once")] - fn run_once_providing_buf(&mut self) { + fn run_once(&mut self) { let mut linebuffer = [0; BUFSIZE]; self.run_once_with_buf(&mut linebuffer) } + #[deprecated(note = "Renamed to run_forever", since = "0.9")] + fn run_forever_providing_buf(&mut self) -> ! { + self.run_forever() + } + + #[deprecated(note = "Renamed to run_once", since = "0.9")] + fn run_once_providing_buf(&mut self) { + self.run_once() + } + /// Extend the list of commands by an additional one. /// /// The handler will be called every time the command is entered, and is passed the arguments diff --git a/src/socket.rs b/src/socket.rs index af7ef6b6..a1207e65 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -64,145 +64,84 @@ impl Into for UdpEp { } } -#[cfg(feature = "with_embedded_nal")] -mod nal_impls { - use super::*; - use embedded_nal::SocketAddr; - - impl Into for &SocketAddr { - fn into(self) -> UdpEp { - use SocketAddr::*; - - // Constructing via default avoids using the volatile names of the union types - let mut ep: riot_sys::sock_udp_ep_t = Default::default(); - - ep.family = match self { - V4(_) => riot_sys::AF_INET as _, - V6(_) => riot_sys::AF_INET6 as _, - }; - ep.netif = match self { - V4(_) => 0, - V6(a) => a.scope_id() as _, - }; - ep.port = self.port(); - match self { - V4(a) => { - ep.addr.ipv4 = a.ip().octets(); - } - V6(a) => { - ep.addr.ipv6 = a.ip().octets(); +macro_rules! implementation_no_std_net { + ($nsn_crate:ident) => { + use super::*; + use $nsn_crate::SocketAddr; + + impl Into for &SocketAddr { + fn into(self) -> UdpEp { + use SocketAddr::*; + + // Constructing via default avoids using the volatile names of the union types + let mut ep: riot_sys::sock_udp_ep_t = Default::default(); + + ep.family = match self { + V4(_) => riot_sys::AF_INET as _, + V6(_) => riot_sys::AF_INET6 as _, + }; + ep.netif = match self { + V4(_) => 0, + V6(a) => a.scope_id() as _, + }; + ep.port = self.port(); + match self { + V4(a) => { + ep.addr.ipv4 = a.ip().octets(); + } + V6(a) => { + ep.addr.ipv6 = a.ip().octets(); + } } - } - - UdpEp(ep) - } - } - - impl Into for SocketAddr { - fn into(self) -> UdpEp { - (&self).into() - } - } - impl Into for &UdpEp { - fn into(self) -> SocketAddr { - match self.0.family as _ { - riot_sys::AF_INET6 => embedded_nal::SocketAddrV6::new( - // unsafe: Access to C union whose type was just checked - unsafe { self.0.addr.ipv6.into() }, - self.0.port, - 0, - self.0.netif.into(), - ) - .into(), - - riot_sys::AF_INET => embedded_nal::SocketAddrV4::new( - // unsafe: Access to C union whose type was just checked - unsafe { self.0.addr.ipv4.into() }, - self.0.port, - ) - .into(), - - _ => panic!("Endpoint not expressible in embedded_nal"), + UdpEp(ep) } } - } - impl Into for UdpEp { - fn into(self) -> SocketAddr { - (&self).into() + impl Into for SocketAddr { + fn into(self) -> UdpEp { + (&self).into() + } } - } -} -// FIXME: This is literally copied from above, just with another module -#[cfg(feature = "with_embedded_nal_async")] -mod nal_impls_async { - use super::*; - use embedded_nal_async::SocketAddr; - - impl Into for &SocketAddr { - fn into(self) -> UdpEp { - use SocketAddr::*; - - // Constructing via default avoids using the volatile names of the union types - let mut ep: riot_sys::sock_udp_ep_t = Default::default(); - - ep.family = match self { - V4(_) => riot_sys::AF_INET as _, - V6(_) => riot_sys::AF_INET6 as _, - }; - ep.netif = match self { - V4(_) => 0, - V6(a) => a.scope_id() as _, - }; - ep.port = self.port(); - match self { - V4(a) => { - ep.addr.ipv4 = a.ip().octets(); - } - V6(a) => { - ep.addr.ipv6 = a.ip().octets(); + impl Into for &UdpEp { + fn into(self) -> SocketAddr { + match self.0.family as _ { + riot_sys::AF_INET6 => $nsn_crate::SocketAddrV6::new( + // unsafe: Access to C union whose type was just checked + unsafe { self.0.addr.ipv6.into() }, + self.0.port, + 0, + self.0.netif.into(), + ) + .into(), + + riot_sys::AF_INET => $nsn_crate::SocketAddrV4::new( + // unsafe: Access to C union whose type was just checked + unsafe { self.0.addr.ipv4.into() }, + self.0.port, + ) + .into(), + + _ => panic!("Endpoint not expressible in no_std_net"), } } - - UdpEp(ep) } - } - impl Into for SocketAddr { - fn into(self) -> UdpEp { - (&self).into() - } - } - - impl Into for &UdpEp { - fn into(self) -> SocketAddr { - match self.0.family as _ { - riot_sys::AF_INET6 => embedded_nal_async::SocketAddrV6::new( - // unsafe: Access to C union whose type was just checked - unsafe { self.0.addr.ipv6.into() }, - self.0.port, - 0, - self.0.netif.into(), - ) - .into(), - - riot_sys::AF_INET => embedded_nal_async::SocketAddrV4::new( - // unsafe: Access to C union whose type was just checked - unsafe { self.0.addr.ipv4.into() }, - self.0.port, - ) - .into(), - - _ => panic!("Endpoint not expressible in embedded_nal_async"), + impl Into for UdpEp { + fn into(self) -> SocketAddr { + (&self).into() } } - } + }; +} - impl Into for UdpEp { - fn into(self) -> SocketAddr { - (&self).into() - } - } +#[cfg(feature = "with_embedded_nal")] +mod implementation_no_std_net_0_5 { + implementation_no_std_net! {no_std_net_0_5} +} + +#[cfg(feature = "with_embedded_nal_async")] +mod implementation_no_std_net_0_6 { + implementation_no_std_net! {no_std_net_0_6} } diff --git a/src/socket_embedded_nal_async_udp.rs b/src/socket_embedded_nal_async_udp.rs index cf62ceaa..48530f0f 100644 --- a/src/socket_embedded_nal_async_udp.rs +++ b/src/socket_embedded_nal_async_udp.rs @@ -346,9 +346,6 @@ macro_rules! implementation_module { } } -mod implementation { - implementation_module! {embedded_nal_async} -} mod implementation_0_7 { implementation_module! {embedded_nal_async_0_7} } diff --git a/src/spi/mod.rs b/src/spi/mod.rs index f8ed4410..b3740b84 100644 --- a/src/spi/mod.rs +++ b/src/spi/mod.rs @@ -1,10 +1,10 @@ -use crate::Never; +use core::convert::Infallible; use embedded_hal_0_2::blocking; use riot_sys::{ spi_acquire, spi_clk_t, spi_cs_t, spi_mode_t, spi_release, spi_t, spi_transfer_bytes, }; -pub struct SPIDevice(#[deprecated(note = "Use constructor instead")] pub spi_t); +pub struct SPIDevice(spi_t); pub struct AcquiredSPI<'a> { device: &'a mut SPIDevice, @@ -65,7 +65,7 @@ impl SPIDevice { // } impl<'a> blocking::spi::Transfer for AcquiredSPI<'a> { - type Error = Never; + type Error = Infallible; fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { unsafe { diff --git a/src/thread.rs b/src/thread.rs index aa9e964a..93e950bb 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -23,8 +23,7 @@ pub use riot_c::*; mod tokenparts; #[cfg(doc)] pub use tokenparts::TokenParts; -#[allow(deprecated)] -pub use tokenparts::{EndToken, InIsr, InThread, StartToken, TerminationToken, ValueInThread}; +pub use tokenparts::{EndToken, InIsr, InThread, StartToken, ValueInThread}; mod stack_stats; pub use stack_stats::{StackStats, StackStatsError}; diff --git a/src/thread/tokenparts.rs b/src/thread/tokenparts.rs index ccd4afc8..057e2787 100644 --- a/src/thread/tokenparts.rs +++ b/src/thread/tokenparts.rs @@ -18,9 +18,6 @@ pub struct EndToken { _not_send: PhantomData<*const ()>, } -#[deprecated(note = "Renamed to EndToken")] -pub type TerminationToken = EndToken; - /// A [StartToken] that has possibly already lost some of its properties. /// /// Note that while this item shows up in the documentation, the type is actually hidden and only @@ -152,7 +149,7 @@ impl TokenParts { // would be sent to again. pub fn with_message_queue< const N: usize, - F: FnOnce(TokenParts) -> crate::Never, + F: FnOnce(TokenParts) -> crate::never::Never, >( self, f: F, @@ -186,11 +183,6 @@ impl TokenParts { _not_send: PhantomData, } } - - #[deprecated(note = "Renamed to can_end")] - pub fn termination(self) -> EndToken { - self.can_end() - } } /// Zero-size statement that the current code is not running in an interrupt @@ -217,7 +209,6 @@ impl InThread { /// Note that this is actually running code; to avoid that, call [`TokenParts::in_thread()`], /// which is a purely type-level procedure. pub fn new() -> Result { - #[allow(deprecated)] // It's deprecatedly pub match crate::interrupt::irq_is_in() { true => Err(unsafe { InIsr::new_unchecked() }), false => Ok(unsafe { InThread::new_unchecked() }), diff --git a/src/ztimer/mod.rs b/src/ztimer/mod.rs index b6916f6d..ba4cb083 100644 --- a/src/ztimer/mod.rs +++ b/src/ztimer/mod.rs @@ -20,6 +20,8 @@ use pin_project::{pin_project, pinned_drop}; use riot_sys::ztimer_clock_t; +use crate::thread::{InThread, ValueInThread}; + // Useful for working with durations const NANOS_PER_SEC: u32 = 1_000_000_000; @@ -36,7 +38,7 @@ pub struct Clock(*mut ztimer_clock_t); #[derive(Copy, Clone, Debug)] pub struct Ticks(pub u32); -impl Clock { +impl ValueInThread> { /// Pause the current thread for the duration of ticks in the timer's time scale. /// /// Wraps [ztimer_sleep](https://doc.riot-os.org/group__sys__ztimer.html#gade98636e198f2d571c8acd861d29d360) @@ -52,6 +54,10 @@ impl Clock { /// *very* short delays.". /// /// Wraps [ztimer_spin](https://doc.riot-os.org/group__sys__ztimer.html#ga9de3d9e3290746b856bb23eb2dccaa7c) + /// + /// Note that this would not technically require the self to be a [ValueInThread] (as spinning + /// is doable in an ISR), but it's so discouraged that the Rust wrapper takes the position that + /// it's best done using a [ValueInThread]. #[doc(alias = "ztimer_spin")] pub fn spin_ticks(&self, duration: u32) { unsafe { riot_sys::ztimer_spin(crate::inline_cast_mut(self.0), duration) }; @@ -76,19 +82,6 @@ impl Clock { self.sleep_ticks(ticks.try_into().expect("Was just checked manually above")); } - /// Similar to [`sleep_ticks()`], but this does not block but creates a future to be - /// `.await`ed. - /// - /// Note that time starts running only when this is polled, for otherwise there's no pinned - /// Self around. - pub async fn sleep_async(&self, duration: Ticks) { - AsyncSleep::NeverPolled(NascentAsyncSleep { - clock: *self, - ticks: duration, - }) - .await - } - /// Set the given callback to be executed in an interrupt some ticks in the future. /// /// Then, start the in_thread function from in the thread this is called from (as a regular @@ -110,6 +103,13 @@ impl Clock { /// (Might make sense to do this without an extra function variant: if the callback ignores /// the timer argument and always returns None, that's all in the caller type and probebly /// inlined right away). + /// + /// While (unless with sleep) nothing would break if this were called from an interrupt + /// context, it would not work either: As RIOT uses flat interrupt priorities, any code + /// executed in the `in_thread` handler would still be run in the original interrupt, and while + /// the configured ZTimer would fire its interrupt during that time, the interrupt would not be + /// serviced, and the timer would be removed already by the time the original interrupt + /// completes and ZTimer is serviced (finding no actually pending callback). pub fn set_during R, R>( &self, callback: I, @@ -180,13 +180,41 @@ impl Clock { result } } + +impl Clock { + /// Similar to [`sleep_ticks()`], but this does not block but creates a future to be + /// `.await`ed. + /// + /// Note that time starts running only when this is polled, for otherwise there's no pinned + /// Self around. + pub async fn sleep_async(&self, duration: Ticks) { + AsyncSleep::NeverPolled(NascentAsyncSleep { + clock: *self, + ticks: duration, + }) + .await + } +} impl Clock<1> { /// Get the global second ZTimer clock, ZTIMER_SEC. /// - /// This function is only available if the ztimer_sec module is built. + /// This function verifies (at a small runtime cost) that the caller is in a thread context. + /// This can be avoided by calling `in_thread.promote(Clock::sec_unbound())` on an existing + /// [riot_wrappers::thread::InThread] token. #[cfg(riot_module_ztimer_sec)] #[doc(alias = "ZTIMER_SEC")] - pub fn sec() -> Self { + pub fn sec() -> ValueInThread { + InThread::new() + .expect("Thread-bound ZTimer clock created in ISR") + .promote(Self::sec_unbound()) + } + + /// Get the global second ZTimer clock, ZTIMER_SEC. + /// + /// The clock is *not* packed in a [ValueInThread], which makes the blocking sleep methods and + /// delay implementations unavailable, but works even in interrupts contexts. + #[cfg(riot_module_ztimer_sec)] + pub fn sec_unbound() -> Self { Clock(unsafe { riot_sys::ZTIMER_SEC }) } } @@ -194,10 +222,23 @@ impl Clock<1> { impl Clock<1000> { /// Get the global milliseconds ZTimer clock, ZTIMER_MSEC. /// - /// This function is only available if the ztimer_msec module is built. + /// This function verifies (at a small runtime cost) that the caller is in a thread context. + /// This can be avoided by calling `in_thread.promote(Clock::msec_unbound())` on an existing + /// [riot_wrappers::thread::InThread] token. #[cfg(riot_module_ztimer_msec)] #[doc(alias = "ZTIMER_MSEC")] - pub fn msec() -> Self { + pub fn msec() -> ValueInThread { + InThread::new() + .expect("Thread-bound ZTimer clock created in ISR") + .promote(Self::msec_unbound()) + } + + /// Get the global milliseconds ZTimer clock, ZTIMER_MSEC. + /// + /// The clock is *not* packed in a [ValueInThread], which makes the blocking sleep methods and + /// delay implementations unavailable, but works even in interrupts contexts. + #[cfg(riot_module_ztimer_msec)] + pub fn msec_unbound() -> Self { Clock(unsafe { riot_sys::ZTIMER_MSEC }) } } @@ -205,23 +246,24 @@ impl Clock<1000> { impl Clock<1000000> { /// Get the global microseconds ZTimer clock, ZTIMER_USEC. /// - /// This function is only available if the ztimer_usec module is built. + /// This function verifies (at a small runtime cost) that the caller is in a thread context. + /// This can be avoided by calling `in_thread.promote(Clock::usec_unbound())` on an existing + /// [riot_wrappers::thread::InThread] token. #[cfg(riot_module_ztimer_usec)] #[doc(alias = "ZTIMER_USEC")] - pub fn usec() -> Self { - Clock(unsafe { riot_sys::ZTIMER_USEC }) - } -} - -impl embedded_hal_0_2::blocking::delay::DelayMs for Clock<1000> { - fn delay_ms(&mut self, ms: u32) { - self.sleep_ticks(ms.into()); + pub fn usec() -> ValueInThread { + InThread::new() + .expect("Thread-bound ZTimer clock created in ISR") + .promote(Self::usec_unbound()) } -} -impl embedded_hal_0_2::blocking::delay::DelayUs for Clock<1000000> { - fn delay_us(&mut self, us: u32) { - self.sleep_ticks(us); + /// Get the global microseconds ZTimer clock, ZTIMER_USEC. + /// + /// The clock is *not* packed in a [ValueInThread], which makes the blocking sleep methods and + /// delay implementations unavailable, but works even in interrupts contexts. + #[cfg(riot_module_ztimer_usec)] + pub fn usec_unbound() -> Self { + Clock(unsafe { riot_sys::ZTIMER_USEC }) } } @@ -247,19 +289,21 @@ pub struct Delay; impl embedded_hal_async::delay::DelayNs for Delay { async fn delay_ns(&mut self, ns: u32) { // See struct level documentation - Clock::usec().sleep_async(Ticks(ns.div_ceil(1000))).await + Clock::usec_unbound() + .sleep_async(Ticks(ns.div_ceil(1000))) + .await } async fn delay_us(&mut self, us: u32) { - Clock::usec().sleep_async(Ticks(us)).await + Clock::usec_unbound().sleep_async(Ticks(us)).await } async fn delay_ms(&mut self, us: u32) { - Clock::msec().sleep_async(Ticks(us)).await + Clock::msec_unbound().sleep_async(Ticks(us)).await } } -impl embedded_hal::delay::DelayNs for Clock { +impl embedded_hal::delay::DelayNs for ValueInThread> { // FIXME: Provide delay_us and delay_ms, at least for the clocks where those fit, to avoid the // loops where the provided function wakes up every 4.3s