Merge #131 131: Better subscribe API r=Woyten a=Woyten This PR improves the usability of the `subscribe` API. The current API has the following problems: - It is impossible to implement `SubscribableCallback` for different `FnMut` traits. This is the reason why the odd `WithCallback` helper objects were introduced. - `SubscribableCallback` is crate private by accident. As a result, it is hard to reason what types actually implement `SubscribableCallback` Besides that, the following APIs have been made more consistent: - LEDs - Buttons - GPIO - Temperature Co-authored-by: Woyten <woyten.tielesch@online.de>
diff --git a/.travis.yml b/.travis.yml index d3547db..651ad97 100644 --- a/.travis.yml +++ b/.travis.yml
@@ -18,7 +18,7 @@ install: - rustup target add thumbv7em-none-eabi - rustup target add riscv32imc-unknown-none-elf - - rustup component add rustfmt-preview + - rustup component add rustfmt - rustup component add clippy script:
diff --git a/CHANGELOG.md b/CHANGELOG.md index d15206f..0ab5997 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -2,23 +2,41 @@ ## 0.2.0 (WIP) +### Comprehensive Changes + - Many functions are asynchronous - - To make create an `async` main function you can use the attribute `#[libtock::main]` + - To create an `async` main function you can use the attribute `#[libtock::main]` - To retrieve the value of an asynchronous `value`, use `value.await` - This is only possible within an `async fn`, so either - Make the caller `fn` of `.await` an `async fn` - Not recommended: Use `core::executor::block_on(value)` to retrieve the `value` +- Most API functions, including `main()`, return a `Result<T, TockError>` +- All drivers can exclusively be retrieved by `retrieve_drivers` which returns a `Drivers` singleton. Drivers can be shared between different tasks only if it is safe to do so. + +### Changed APIs + +- The basic APIs have been made consistent. They are initialized via driver factories and no longer require a `WithCallback` object, s.t. the callback subscription is more intuitive. The affected APIs are: + - LEDs + - Buttons + - GPIO + - Temperature + - ADC (partially) +- The timer API now supports concurrent sleep operations + +### Syscalls + +- `syscalls::subscribe` is actually usable - `syscalls::yieldk_for` is no longer available - Yielding manually is discouraged as it conflicts with Rust's safety guarantees. If you need to wait for a condition, use `futures::wait_until` and `.await`. - `syscalls::yieldk` has become `unsafe` for the same reason -- Commands are no longer `unsafe` +- `syscalls::command` is no longer `unsafe` - The low-level syscalls have been moved to `syscalls::raw` - `syscalls::subscribe_ptr` becomes `syscalls::raw::subscribe` - `syscalls::allow_ptr` becomes `syscalls::raw::allow` + +### Miscellaneous + - Targets without support for atomics can be built -- Most API functions, including `main()`, return a `Result<T, TockError>` -- The library now supports parallel timers -- all drivers can exclusively be retrieved by `retrieve_drivers` which returns a `Drivers`-singleton. Drivers can be shared between different tasks only if it is safe to do so. ## a8bb4fa9be504517d5533511fd8e607ea61f1750 (0.1.0)
diff --git a/examples/adc.rs b/examples/adc.rs index ef100a3..711176e 100644 --- a/examples/adc.rs +++ b/examples/adc.rs
@@ -3,28 +3,24 @@ use core::fmt::Write; use libtock::result::TockResult; use libtock::timer::Duration; -use libtock::Drivers; #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - console_driver, - timer_context, - adc_driver, - .. - } = libtock::retrieve_drivers()?; + let mut drivers = libtock::retrieve_drivers()?; - let mut driver = timer_context.create_timer_driver(); - let timer_driver = driver.activate()?; - let mut console = console_driver.create_console(); - let mut with_callback = adc_driver.with_callback(|channel: usize, value: usize| { + let adc_driver = drivers.adc.init_driver()?; + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate()?; + let mut console = drivers.console.create_console(); + + let mut callback = |channel, value| { writeln!(console, "channel: {}, value: {}", channel, value).unwrap(); - }); + }; - let adc = with_callback.init()?; + let _subscription = adc_driver.subscribe(&mut callback)?; loop { - adc.sample(0)?; + adc_driver.sample(0)?; timer_driver.sleep(Duration::from_ms(2000)).await?; } }
diff --git a/examples/adc_buffer.rs b/examples/adc_buffer.rs index b948b93..86f94b2 100644 --- a/examples/adc_buffer.rs +++ b/examples/adc_buffer.rs
@@ -4,31 +4,29 @@ use libtock::adc::AdcBuffer; use libtock::result::TockResult; use libtock::syscalls; -use libtock::Drivers; #[libtock::main] /// Reads a 128 byte sample into a buffer and prints the first value to the console. async fn main() -> TockResult<()> { - let Drivers { - console_driver, - adc_driver, - .. - } = libtock::retrieve_drivers()?; - let mut console = console_driver.create_console(); + let mut drivers = libtock::retrieve_drivers()?; + + let adc_driver = drivers.adc.init_driver()?; + let mut console = drivers.console.create_console(); + let mut adc_buffer = AdcBuffer::default(); let mut temp_buffer = [0; libtock::adc::BUFFER_SIZE]; - let adc_buffer = libtock::adc::Adc::init_buffer(&mut adc_buffer)?; + let adc_buffer = adc_driver.init_buffer(&mut adc_buffer)?; - let mut with_callback = adc_driver.with_callback(|_, _| { + let mut callback = |_, _| { adc_buffer.read_bytes(&mut temp_buffer[..]); writeln!(console, "First sample in buffer: {}", temp_buffer[0]).unwrap(); - }); + }; - let adc = with_callback.init()?; + let _subscription = adc_driver.subscribe(&mut callback)?; loop { - adc.sample_continuous_buffered(0, 128)?; + adc_driver.sample_continuous_buffered(0, 128)?; unsafe { syscalls::raw::yieldk() }; } }
diff --git a/examples/alloc_error.rs b/examples/alloc_error.rs new file mode 100644 index 0000000..ec42b5a --- /dev/null +++ b/examples/alloc_error.rs
@@ -0,0 +1,16 @@ +// Triggers the out-of-memory handler. Should make all LEDs cycle. + +#![no_std] + +extern crate alloc; + +use alloc::vec::Vec; +use libtock::result::TockResult; + +#[libtock::main] +fn main() -> TockResult<()> { + let mut vec = Vec::new(); + loop { + vec.push(0); + } +}
diff --git a/examples/ble_scanning.rs b/examples/ble_scanning.rs index a00ed46..bbbe76a 100644 --- a/examples/ble_scanning.rs +++ b/examples/ble_scanning.rs
@@ -6,7 +6,6 @@ use libtock::simple_ble; use libtock::simple_ble::BleCallback; use libtock::simple_ble::BleScanningDriver; -use libtock::Drivers; use serde::Deserialize; #[derive(Deserialize)] @@ -17,17 +16,13 @@ #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - led_driver_factory, - mut ble_scanning_driver, - .. - } = libtock::retrieve_drivers()?; + let mut drivers = libtock::retrieve_drivers()?; - let led_driver = led_driver_factory.create_driver()?; + let leds_driver = drivers.leds.init_driver()?; let mut shared_buffer = BleScanningDriver::create_scan_buffer(); let mut my_buffer = BleScanningDriver::create_scan_buffer(); - let shared_memory = ble_scanning_driver.share_memory(&mut shared_buffer)?; + let shared_memory = drivers.ble_scanning.share_memory(&mut shared_buffer)?; let mut callback = BleCallback::new(|_: usize, _: usize| { shared_memory.read_bytes(&mut my_buffer[..]); @@ -35,13 +30,14 @@ .and_then(|service_data| ble_parser::extract_for_service([91, 79], service_data)) .and_then(|payload| corepack::from_bytes::<LedCommand>(&payload).ok()) .and_then(|msg| { - led_driver + leds_driver .get(msg.nr as usize) - .map(|led| led.set_state(msg.st)) + .map(|led| led.set(msg.st)) + .into() }); }); - let _subscription = ble_scanning_driver.start(&mut callback)?; + let _subscription = drivers.ble_scanning.start(&mut callback)?; future::pending().await }
diff --git a/examples/blink.rs b/examples/blink.rs index aa9b64c..fd3b1e5 100644 --- a/examples/blink.rs +++ b/examples/blink.rs
@@ -2,26 +2,21 @@ use libtock::result::TockResult; use libtock::timer::Duration; -use libtock::Drivers; #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - led_driver_factory, - timer_context, - .. - } = libtock::retrieve_drivers()?; + let mut drivers = libtock::retrieve_drivers()?; - let mut driver = timer_context.create_timer_driver(); - let timer_driver = driver.activate()?; - let led_driver = led_driver_factory.create_driver()?; + let leds_driver = drivers.leds.init_driver()?; + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate()?; // Blink the LEDs in a binary count pattern and scale // to the number of LEDs on the board. let mut count: usize = 0; loop { - for led in led_driver.all() { - let i = led.number(); + for led in leds_driver.leds() { + let i = led.led_num(); if count & (1 << i) == (1 << i) { led.on()?; } else {
diff --git a/examples/blink_random.rs b/examples/blink_random.rs index 8734693..93a263d 100644 --- a/examples/blink_random.rs +++ b/examples/blink_random.rs
@@ -1,45 +1,37 @@ #![no_std] -use libtock::led::LedDriver; +use libtock::leds::LedsDriver; use libtock::result::TockResult; use libtock::timer::Duration; -use libtock::Drivers; #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - timer_context, - mut rng_driver, - led_driver_factory, - .. - } = libtock::retrieve_drivers()?; + let mut drivers = libtock::retrieve_drivers()?; - let led_driver = led_driver_factory.create_driver()?; + let leds_driver = drivers.leds.init_driver()?; + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate()?; - let mut driver = timer_context.create_timer_driver(); - let timer_driver = driver.activate()?; - - let num_leds = led_driver.count()?; // blink_nibble assumes 4 leds. - assert_eq!(num_leds, 4); + assert_eq!(leds_driver.num_leds(), 4); let mut buf = [0; 64]; loop { - rng_driver.fill_buffer(&mut buf).await?; + drivers.rng.fill_buffer(&mut buf).await?; for &x in buf.iter() { - blink_nibble(x, &led_driver)?; + blink_nibble(&leds_driver, x)?; timer_driver.sleep(Duration::from_ms(100)).await?; - blink_nibble(x >> 4, &led_driver)?; + blink_nibble(&leds_driver, x >> 4)?; timer_driver.sleep(Duration::from_ms(100)).await?; } } } // Takes the 4 least-significant bits of x, and turn the 4 leds on/off accordingly. -fn blink_nibble(x: u8, led_driver: &LedDriver) -> TockResult<()> { +fn blink_nibble(leds_driver: &LedsDriver, x: u8) -> TockResult<()> { for i in 0..4 { - let led = led_driver.get(i).unwrap(); + let led = leds_driver.get(i)?; if (x >> i) & 1 != 0 { led.on()?; } else {
diff --git a/examples/button_leds.rs b/examples/button_leds.rs index ce6565f..4a48f2f 100644 --- a/examples/button_leds.rs +++ b/examples/button_leds.rs
@@ -3,29 +3,23 @@ use futures::future; use libtock::buttons::ButtonState; use libtock::result::TockResult; -use libtock::Drivers; #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - led_driver_factory, - button_driver, - .. - } = libtock::retrieve_drivers()?; + let mut drivers = libtock::retrieve_drivers()?; - let led_driver = led_driver_factory.create_driver()?; + let buttons_driver = drivers.buttons.init_driver()?; + let leds_driver = drivers.leds.init_driver()?; - let mut with_callback = button_driver.with_callback(|button_num: usize, state| { - match state { - ButtonState::Pressed => led_driver.get(button_num).unwrap().toggle().ok().unwrap(), - ButtonState::Released => (), - }; - }); + let mut callback = |button_num, state| { + if let (ButtonState::Pressed, Ok(led)) = (state, leds_driver.get(button_num)) { + led.toggle().ok().unwrap(); + } + }; - let mut buttons = with_callback.init()?; - - for mut button in &mut buttons { - button.enable()?; + let _subscription = buttons_driver.subscribe(&mut callback)?; + for button in buttons_driver.buttons() { + button.enable_interrupt()?; } future::pending().await
diff --git a/examples/button_read.rs b/examples/button_read.rs index 2ce209f..c3f9aa6 100644 --- a/examples/button_read.rs +++ b/examples/button_read.rs
@@ -1,34 +1,27 @@ #![no_std] use core::fmt::Write; -use libtock::buttons::ButtonState; use libtock::result::TockResult; use libtock::timer::Duration; -use libtock::Drivers; #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - console_driver, - timer_context, - button_driver, - .. - } = libtock::retrieve_drivers()?; - let mut console = console_driver.create_console(); - let mut with_callback = button_driver.with_callback(|_, _| {}); - let mut buttons = with_callback.init()?; - let mut button = buttons.iter_mut().next().unwrap(); - let button = button.enable()?; + let mut drivers = libtock::retrieve_drivers()?; - let mut driver = timer_context.create_timer_driver(); - let timer_driver = driver.activate()?; + let buttons_driver = drivers.buttons.init_driver()?; + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate()?; + let mut console = drivers.console.create_console(); loop { - match button.read()? { - ButtonState::Pressed => writeln!(console, "pressed"), - ButtonState::Released => writeln!(console, "released"), - }?; - + for button in buttons_driver.buttons() { + writeln!( + console, + "button {}: {:?}", + button.button_num(), + button.read()? + )?; + } timer_driver.sleep(Duration::from_ms(500)).await?; } }
diff --git a/examples/button_subscribe.rs b/examples/button_subscribe.rs index 268e6f0..d06a0e9 100644 --- a/examples/button_subscribe.rs +++ b/examples/button_subscribe.rs
@@ -1,39 +1,41 @@ #![no_std] +use core::cell::Cell; use core::fmt::Write; -use futures::future; use libtock::buttons::ButtonState; use libtock::result::TockResult; -use libtock::Drivers; +use libtock::timer::Duration; -// FIXME: Hangs up when buttons are pressed rapidly. Yielding in callback leads to stack overflow. #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - console_driver, - button_driver, - .. - } = libtock::retrieve_drivers()?; - let mut console = console_driver.create_console(); - let mut with_callback = button_driver.with_callback(|button_num: usize, state| { - writeln!( - console, - "Button: {} - State: {}", - button_num, - match state { - ButtonState::Pressed => "pressed", - ButtonState::Released => "released", - } - ) - .ok() - .unwrap(); - }); + let mut drivers = libtock::retrieve_drivers()?; - let mut buttons = with_callback.init()?; + let buttons_driver = drivers.buttons.init_driver()?; + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate()?; + let mut console = drivers.console.create_console(); - for mut button in &mut buttons { - button.enable()?; + let pressed_count = Cell::new(0usize); + let released_count = Cell::new(0usize); + + let mut callback = |_button_num, state| match state { + ButtonState::Pressed => pressed_count.set(pressed_count.get() + 1), + ButtonState::Released => released_count.set(released_count.get() + 1), + }; + + let _subscription = buttons_driver.subscribe(&mut callback)?; + + for button in buttons_driver.buttons() { + button.enable_interrupt()?; } - future::pending().await + loop { + writeln!( + console, + "pressed: {}, released: {}", + pressed_count.get(), + released_count.get() + )?; + timer_driver.sleep(Duration::from_ms(500)).await?; + } }
diff --git a/examples/gpio.rs b/examples/gpio.rs index ee42d69..9241e74 100644 --- a/examples/gpio.rs +++ b/examples/gpio.rs
@@ -2,25 +2,22 @@ use libtock::result::TockResult; use libtock::timer::Duration; -use libtock::Drivers; // Example works on P0.03 #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - timer_context, - gpio_driver, - .. - } = libtock::retrieve_drivers()?; - let pin = gpio_driver.pin(0)?; - let pin = pin.open_for_write()?; - let mut driver = timer_context.create_timer_driver(); - let timer_driver = driver.activate()?; + let mut drivers = libtock::retrieve_drivers()?; + let mut gpio_driver = drivers.gpio.init_driver()?; + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate()?; + + let mut gpio = gpio_driver.gpios().next().unwrap(); + let gpio_out = gpio.enable_output()?; loop { - pin.set_high()?; + gpio_out.set_high()?; timer_driver.sleep(Duration::from_ms(500)).await?; - pin.set_low()?; + gpio_out.set_low()?; timer_driver.sleep(Duration::from_ms(500)).await?; } }
diff --git a/examples/gpio_read.rs b/examples/gpio_read.rs index 5e3d403..a29662e 100644 --- a/examples/gpio_read.rs +++ b/examples/gpio_read.rs
@@ -1,32 +1,24 @@ #![no_std] use core::fmt::Write; -use libtock::gpio::InputMode; +use libtock::gpio::ResistorMode; use libtock::result::TockResult; use libtock::timer::Duration; -use libtock::Drivers; // example works on p0.03 #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - console_driver, - timer_context, - gpio_driver, - .. - } = libtock::retrieve_drivers()?; - let mut console = console_driver.create_console(); - let pin = gpio_driver.pin(0)?; - let pin = pin.open_for_read(None, InputMode::PullDown)?; - let mut driver = timer_context.create_timer_driver(); - let timer_driver = driver.activate()?; + let mut drivers = libtock::retrieve_drivers()?; + let mut gpio_driver = drivers.gpio.init_driver()?; + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate()?; + let mut console = drivers.console.create_console(); + + let mut gpio = gpio_driver.gpios().next().unwrap(); + let gpio_in = gpio.enable_input(ResistorMode::PullDown)?; loop { - if pin.read() { - writeln!(console, "true")?; - } else { - writeln!(console, "false")?; - } + writeln!(console, "{:?}", gpio_in.read()?)?; timer_driver.sleep(Duration::from_ms(500)).await?; } }
diff --git a/examples/hardware_test.rs b/examples/hardware_test.rs index db2dd34..2f7028c 100644 --- a/examples/hardware_test.rs +++ b/examples/hardware_test.rs
@@ -8,13 +8,13 @@ use core::fmt::Write; use futures::future; use libtock::console::Console; -use libtock::gpio::GpioPinRead; -use libtock::gpio::GpioPinWrite; -use libtock::gpio::InputMode; +use libtock::gpio::GpioRead; +use libtock::gpio::GpioState; +use libtock::gpio::GpioWrite; +use libtock::gpio::ResistorMode; use libtock::result::TockResult; use libtock::timer::DriverContext; use libtock::timer::Duration; -use libtock::Drivers; static mut STATIC: usize = 0; @@ -36,18 +36,16 @@ #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - console_driver, - gpio_driver, - mut timer_context, - .. - } = libtock::retrieve_drivers()?; - let mut gpio_iter = gpio_driver.all_pins()?; - let mut console = console_driver.create_console(); - let pin_in = gpio_iter.next().unwrap(); - let pin_out = gpio_iter.next().unwrap(); - let pin_in = pin_in.open_for_read(None, InputMode::PullDown)?; - let mut pin_out = pin_out.open_for_write()?; + let mut drivers = libtock::retrieve_drivers()?; + + let mut gpio_driver = drivers.gpio.init_driver()?; + let mut console = drivers.console.create_console(); + + let mut gpios = gpio_driver.gpios(); + let mut pin_in = gpios.next().unwrap(); + let pin_in = pin_in.enable_input(ResistorMode::PullDown)?; + let mut pin_out = gpios.next().unwrap(); + let mut pin_out = pin_out.enable_output()?; writeln!(console, "[test-results]")?; @@ -59,7 +57,7 @@ test_trait_objects(&mut console, &pin_in, &mut pin_out)?; - test_callbacks_and_wait_forever(&mut console, &mut timer_context).await + test_callbacks_and_wait_forever(&mut console, &mut drivers.timer).await } fn test_heap(console: &mut Console) { @@ -78,27 +76,26 @@ /// trait_obj_value_string = string fn test_trait_objects( console: &mut Console, - pin_in: &GpioPinRead, - pin_out: &mut GpioPinWrite, + pin_in: &GpioRead, + pin_out: &mut GpioWrite, ) -> TockResult<()> { pin_out.set_high()?; let string = String::from("string"); - let x = if pin_in.read() { - &1usize as &dyn MyTrait - } else { - &string as &dyn MyTrait + let x = match pin_in.read()? { + GpioState::Low => &string as &dyn MyTrait, + GpioState::High => &1usize as &dyn MyTrait, }; - let y = if !pin_in.read() { - &1usize as &dyn MyTrait - } else { - &string as &dyn MyTrait + let y = match pin_in.read()? { + GpioState::Low => &1usize as &dyn MyTrait, + GpioState::High => &string as &dyn MyTrait, }; x.do_something_with_a_console(console); y.do_something_with_a_console(console); + Ok(()) } @@ -109,10 +106,15 @@ } /// needs P0.03 and P0.04 to be connected -fn test_gpio(console: &mut Console, pin_in: &GpioPinRead, pin_out: &mut GpioPinWrite) { +fn test_gpio(console: &mut Console, pin_in: &GpioRead, pin_out: &mut GpioWrite) { pin_out.set_high().ok().unwrap(); - writeln!(console, "gpio_works = {}", pin_in.read()).unwrap(); + writeln!( + console, + "gpio_works = {}", + pin_in.read().ok() == Some(GpioState::High) + ) + .unwrap(); } async fn test_callbacks_and_wait_forever(
diff --git a/examples/panic.rs b/examples/panic.rs index 6b476e3..5abc01a 100644 --- a/examples/panic.rs +++ b/examples/panic.rs
@@ -1,9 +1,10 @@ +// Triggers the panic handler. Should make all LEDs flash. + #![no_std] use libtock::result::TockResult; #[libtock::main] async fn main() -> TockResult<()> { - let _ = libtock::LibTock {}; panic!("Bye world!"); }
diff --git a/examples/sensors.rs b/examples/sensors.rs index 4e72c8c..7ea87c7 100644 --- a/examples/sensors.rs +++ b/examples/sensors.rs
@@ -2,33 +2,37 @@ use core::fmt::Write; use libtock::result::TockResult; -use libtock::sensors::*; +use libtock::sensors::Sensor; use libtock::timer::Duration; -use libtock::Drivers; #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - console_driver, - timer_context, - mut temperature_sensor, - mut humidity_sensor, - mut ambient_light_sensor, - mut ninedof_driver, - .. - } = libtock::retrieve_drivers()?; - let mut console = console_driver.create_console(); - let mut driver = timer_context.create_timer_driver(); - let timer_driver = driver.activate()?; + let mut drivers = libtock::retrieve_drivers()?; + + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate()?; + let mut console = drivers.console.create_console(); loop { - writeln!(console, "Humidity: {}\n", humidity_sensor.read()?)?; - writeln!(console, "Temperature: {}\n", temperature_sensor.read()?)?; - writeln!(console, "Light: {}\n", ambient_light_sensor.read()?)?; + writeln!( + console, + "Humidity: {}\n", + drivers.humidity_sensor.read()? + )?; + writeln!( + console, + "Temperature: {}\n", + drivers.temperature_sensor.read()? + )?; + writeln!( + console, + "Light: {}\n", + drivers.ambient_light_sensor.read()? + )?; writeln!( console, "Accel: {}\n", - ninedof_driver.read_acceleration()? + drivers.ninedof.read_acceleration()? )?; timer_driver.sleep(Duration::from_ms(500)).await?; }
diff --git a/examples/seven_segment.rs b/examples/seven_segment.rs index 4249157..88b0700 100644 --- a/examples/seven_segment.rs +++ b/examples/seven_segment.rs
@@ -3,7 +3,6 @@ use libtock::electronics::ShiftRegister; use libtock::result::TockResult; use libtock::timer::Duration; -use libtock::Drivers; fn number_to_bits(n: u8) -> [bool; 8] { match n { @@ -24,18 +23,19 @@ // Example works on a shift register on P0.03, P0.04, P0.28 #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - timer_context, - gpio_driver, - .. - } = libtock::retrieve_drivers()?; - let mut shift_register = ShiftRegister::new( - gpio_driver.pin(0)?.open_for_write()?, - gpio_driver.pin(1)?.open_for_write()?, - gpio_driver.pin(2)?.open_for_write()?, - ); + let mut drivers = libtock::retrieve_drivers()?; - let mut driver = timer_context.create_timer_driver(); + let mut gpio_driver = drivers.gpio.init_driver()?; + let mut gpios = gpio_driver.gpios(); + let mut gpio0 = gpios.next().unwrap(); + let gpio0 = gpio0.enable_output()?; + let mut gpio1 = gpios.next().unwrap(); + let gpio1 = gpio1.enable_output()?; + let mut gpio2 = gpios.next().unwrap(); + let gpio2 = gpio2.enable_output()?; + let mut shift_register = ShiftRegister::new(&gpio0, &gpio1, &gpio2); + + let mut driver = drivers.timer.create_timer_driver(); let timer_driver = driver.activate()?; let mut i = 0;
diff --git a/examples/simple_ble.rs b/examples/simple_ble.rs index e312ef5..23543ed 100644 --- a/examples/simple_ble.rs +++ b/examples/simple_ble.rs
@@ -5,7 +5,6 @@ use libtock::result::TockResult; use libtock::simple_ble::BleAdvertisingDriver; use libtock::timer::Duration; -use libtock::Drivers; use serde::Serialize; #[derive(Serialize)] @@ -16,16 +15,13 @@ #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - led_driver_factory, - timer_context, - mut ble_advertising_driver, - .. - } = libtock::retrieve_drivers()?; + let mut drivers = libtock::retrieve_drivers()?; - let led_driver = led_driver_factory.create_driver()?; + let leds_driver = drivers.leds.init_driver()?; + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate()?; - let led = led_driver.get(0).unwrap(); + let led = leds_driver.leds().next().unwrap(); let uuid: [u8; 2] = [0x00, 0x18]; @@ -33,6 +29,7 @@ let mut buffer = BleAdvertisingDriver::create_advertising_buffer(); let mut gap_payload = BlePayload::default(); + gap_payload .add_flag(ble_composer::flags::LE_GENERAL_DISCOVERABLE) .unwrap(); @@ -44,12 +41,12 @@ gap_payload .add(ble_composer::gap_types::COMPLETE_LOCAL_NAME, b"Tock!") .unwrap(); + gap_payload.add_service_payload([91, 79], &payload).unwrap(); - let _handle = ble_advertising_driver.initialize(100, &gap_payload, &mut buffer); - - let mut driver = timer_context.create_timer_driver(); - let timer_driver = driver.activate()?; + let _handle = drivers + .ble_advertising + .initialize(100, &gap_payload, &mut buffer); loop { led.on()?;
diff --git a/examples/temperature.rs b/examples/temperature.rs index 9198f1b..7428187 100644 --- a/examples/temperature.rs +++ b/examples/temperature.rs
@@ -2,16 +2,20 @@ use core::fmt::Write; use libtock::result::TockResult; -use libtock::Drivers; +use libtock::timer::Duration; #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - console_driver, - mut temperature_driver, - .. - } = libtock::retrieve_drivers()?; - let mut console = console_driver.create_console(); - let temperature = temperature_driver.measure_temperature().await?; - writeln!(console, "Temperature: {}", temperature).map_err(Into::into) + let mut drivers = libtock::retrieve_drivers()?; + + let mut temperature_driver = drivers.temperature.init_driver()?; + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate()?; + let mut console = drivers.console.create_console(); + + loop { + let temperature = temperature_driver.measure_temperature().await?; + writeln!(console, "Temperature: {}", temperature)?; + timer_driver.sleep(Duration::from_ms(1000)).await?; + } }
diff --git a/examples/timer.rs b/examples/timer.rs index 9ed0112..493f0b9 100644 --- a/examples/timer.rs +++ b/examples/timer.rs
@@ -8,25 +8,21 @@ use libtock::result::TockResult; use libtock::timer::DriverContext; use libtock::timer::Duration; -use libtock::Drivers; const DELAY_MS: usize = 500; #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - mut timer_context, - console_driver, - .. - } = libtock::retrieve_drivers()?; - let mut console = console_driver.create_console(); + let mut drivers = libtock::retrieve_drivers()?; + + let mut console = drivers.console.create_console(); let mut previous_ticks = None; for i in 0.. { - print_now(&mut console, &mut timer_context, &mut previous_ticks, i)?; - let mut driver = timer_context.create_timer_driver(); - let timer_driver = driver.activate()?; + print_now(&mut console, &mut drivers.timer, &mut previous_ticks, i)?; + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate()?; timer_driver.sleep(Duration::from_ms(DELAY_MS)).await?; }
diff --git a/examples/timer_parallel.rs b/examples/timer_parallel.rs index 389727a..66ddac2 100644 --- a/examples/timer_parallel.rs +++ b/examples/timer_parallel.rs
@@ -1,16 +1,15 @@ #![no_std] use futures::future; -use libtock::led::Led; +use libtock::leds::Led; use libtock::result::TockResult; use libtock::timer::Duration; use libtock::timer::ParallelSleepDriver; -use libtock::Drivers; -async fn blink<'a>( - timer_driver: &'a ParallelSleepDriver<'a>, +async fn blink( + timer_driver: &ParallelSleepDriver<'_>, duration: Duration<usize>, - led: &'a mut Led<'a>, + led: Led<'_>, ) -> TockResult<()> { loop { led.toggle()?; @@ -21,24 +20,17 @@ #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - led_driver_factory, - timer_context, - .. - } = libtock::retrieve_drivers()?; - let led_driver = led_driver_factory.create_driver()?; + let mut drivers = libtock::retrieve_drivers()?; - let mut led_iter = led_driver.all(); - let mut led_1 = led_iter.next().unwrap(); - let mut led_2 = led_iter.next().unwrap(); - let mut led_3 = led_iter.next().unwrap(); + let leds_driver = drivers.leds.init_driver()?; + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate()?; - let mut driver = timer_context.create_timer_driver(); - let timer_driver = driver.activate()?; + let mut leds = leds_driver.leds(); - let fut_1 = blink(&timer_driver, Duration::from_ms(500), &mut led_1); - let fut_2 = blink(&timer_driver, Duration::from_ms(333), &mut led_2); - let fut_3 = blink(&timer_driver, Duration::from_ms(250), &mut led_3); + let fut_1 = blink(&timer_driver, Duration::from_ms(500), leds.next().unwrap()); + let fut_2 = blink(&timer_driver, Duration::from_ms(333), leds.next().unwrap()); + let fut_3 = blink(&timer_driver, Duration::from_ms(250), leds.next().unwrap()); future::try_join3(fut_1, fut_2, fut_3).await?; Ok(())
diff --git a/examples/timer_subscribe.rs b/examples/timer_subscribe.rs index f7e158c..5c69be1 100644 --- a/examples/timer_subscribe.rs +++ b/examples/timer_subscribe.rs
@@ -4,17 +4,14 @@ use futures::future; use libtock::result::TockResult; use libtock::timer::Duration; -use libtock::Drivers; #[libtock::main] async fn main() -> TockResult<()> { - let Drivers { - console_driver, - mut timer_context, - .. - } = libtock::retrieve_drivers()?; - let mut console = console_driver.create_console(); - let mut with_callback = timer_context.with_callback(|_, _| { + let mut drivers = libtock::retrieve_drivers()?; + + let mut console = drivers.console.create_console(); + + let mut with_callback = drivers.timer.with_callback(|_, _| { writeln!( console, "This line is printed 2 seconds after the start of the program.",
diff --git a/src/adc.rs b/src/adc.rs index 7a18cce..7d351df 100644 --- a/src/adc.rs +++ b/src/adc.rs
@@ -1,9 +1,11 @@ -use crate::callback::{CallbackSubscription, SubscribableCallback}; +use crate::callback::CallbackSubscription; +use crate::callback::Consumer; use crate::result::TockResult; use crate::shared_memory::SharedMemory; use crate::syscalls; +use core::marker::PhantomData; -pub const DRIVER_NUM: usize = 0x0005; +pub const DRIVER_NUMBER: usize = 0x0005; pub const BUFFER_SIZE: usize = 128; mod command_nr { @@ -25,11 +27,16 @@ } #[non_exhaustive] -pub struct AdcDriver; +pub struct AdcDriverFactory; -impl AdcDriver { - pub fn with_callback<CB>(self, callback: CB) -> WithCallback<CB> { - WithCallback { callback } +impl AdcDriverFactory { + pub fn init_driver(&mut self) -> TockResult<Adc> { + let adc = Adc { + // num_channels + num_channels: syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?, + lifetime: PhantomData, + }; + Ok(adc) } } @@ -47,65 +54,61 @@ } pub struct Adc<'a> { - count: usize, - #[allow(dead_code)] // Used in drop - subscription: CallbackSubscription<'a>, + num_channels: usize, + lifetime: PhantomData<&'a ()>, } -pub struct WithCallback<CB> { - callback: CB, -} +struct AdcEventConsumer; -impl<CB: FnMut(usize, usize)> SubscribableCallback for WithCallback<CB> { - fn call_rust(&mut self, _: usize, channel: usize, value: usize) { - (self.callback)(channel, value); - } -} - -impl<'a, CB> WithCallback<CB> -where - Self: SubscribableCallback, -{ - pub fn init(&mut self) -> TockResult<Adc> { - let adc = Adc { - count: syscalls::command(DRIVER_NUM, command_nr::COUNT, 0, 0)?, - subscription: syscalls::subscribe(DRIVER_NUM, subscribe_nr::SUBSCRIBE_CALLBACK, self)?, - }; - Ok(adc) +impl<CB: FnMut(usize, usize)> Consumer<CB> for AdcEventConsumer { + fn consume(data: &mut CB, _: usize, channel: usize, value: usize) { + data(channel, value); } } impl<'a> Adc<'a> { - pub fn init_buffer(buffer: &'a mut AdcBuffer) -> TockResult<SharedMemory> { - syscalls::allow(DRIVER_NUM, allow_nr::BUFFER, &mut buffer.buffer).map_err(Into::into) + pub fn init_buffer(&self, buffer: &'a mut AdcBuffer) -> TockResult<SharedMemory> { + syscalls::allow(DRIVER_NUMBER, allow_nr::BUFFER, &mut buffer.buffer).map_err(Into::into) } - pub fn init_alt_buffer(alt_buffer: &'a mut AdcBuffer) -> TockResult<SharedMemory> { - syscalls::allow(DRIVER_NUM, allow_nr::BUFFER_ALT, &mut alt_buffer.buffer) + pub fn init_alt_buffer(&self, alt_buffer: &'a mut AdcBuffer) -> TockResult<SharedMemory> { + syscalls::allow(DRIVER_NUMBER, allow_nr::BUFFER_ALT, &mut alt_buffer.buffer) .map_err(Into::into) } /// Return the number of available channels pub fn count(&self) -> usize { - self.count + self.num_channels + } + + pub fn subscribe<CB: FnMut(usize, usize)>( + &self, + callback: &'a mut CB, + ) -> TockResult<CallbackSubscription> { + syscalls::subscribe::<AdcEventConsumer, _>( + DRIVER_NUMBER, + subscribe_nr::SUBSCRIBE_CALLBACK, + callback, + ) + .map_err(Into::into) } /// Start a single sample of channel pub fn sample(&self, channel: usize) -> TockResult<()> { - syscalls::command(DRIVER_NUM, command_nr::START, channel, 0)?; + syscalls::command(DRIVER_NUMBER, command_nr::START, channel, 0)?; Ok(()) } /// Start continuous sampling of channel pub fn sample_continuous(&self, channel: usize) -> TockResult<()> { - syscalls::command(DRIVER_NUM, command_nr::START_REPEAT, channel, 0)?; + syscalls::command(DRIVER_NUMBER, command_nr::START_REPEAT, channel, 0)?; Ok(()) } /// Start continuous sampling to first buffer pub fn sample_continuous_buffered(&self, channel: usize, frequency: usize) -> TockResult<()> { syscalls::command( - DRIVER_NUM, + DRIVER_NUMBER, command_nr::START_REPEAT_BUFFER, channel, frequency, @@ -120,7 +123,7 @@ frequency: usize, ) -> TockResult<()> { syscalls::command( - DRIVER_NUM, + DRIVER_NUMBER, command_nr::START_REPEAT_BUFFER_ALT, channel, frequency, @@ -130,7 +133,7 @@ /// Stop any started sampling operation pub fn stop(&self) -> TockResult<()> { - syscalls::command(DRIVER_NUM, command_nr::STOP, 0, 0)?; + syscalls::command(DRIVER_NUMBER, command_nr::STOP, 0, 0)?; Ok(()) } }
diff --git a/src/buttons.rs b/src/buttons.rs index 7004cd8..aac5cb2 100644 --- a/src/buttons.rs +++ b/src/buttons.rs
@@ -1,5 +1,7 @@ use crate::callback::CallbackSubscription; -use crate::callback::SubscribableCallback; +use crate::callback::Consumer; +use crate::result::OtherError; +use crate::result::OutOfRangeError; use crate::result::TockResult; use crate::syscalls; use core::marker::PhantomData; @@ -18,53 +20,93 @@ } #[non_exhaustive] -pub struct ButtonDriver; +pub struct ButtonsDriverFactory; -impl ButtonDriver { - pub fn with_callback<CB>(self, callback: CB) -> WithCallback<CB> { - WithCallback { callback } - } -} - -pub struct WithCallback<CB> { - callback: CB, -} - -impl<CB: FnMut(usize, ButtonState)> SubscribableCallback for WithCallback<CB> { - fn call_rust(&mut self, button_num: usize, state: usize, _: usize) { - (self.callback)(button_num, state.into()); - } -} - -impl<CB> WithCallback<CB> -where - Self: SubscribableCallback, -{ - pub fn init(&mut self) -> TockResult<Buttons> { - let buttons = Buttons { - count: syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?, - subscription: syscalls::subscribe( - DRIVER_NUMBER, - subscribe_nr::SUBSCRIBE_CALLBACK, - self, - )?, +impl ButtonsDriverFactory { + pub fn init_driver(&mut self) -> TockResult<ButtonsDriver> { + let buttons_driver = ButtonsDriver { + num_buttons: syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?, + lifetime: PhantomData, }; - Ok(buttons) + Ok(buttons_driver) + } +} + +pub struct ButtonsDriver<'a> { + num_buttons: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> ButtonsDriver<'a> { + pub fn num_buttons(&self) -> usize { + self.num_buttons + } + + /// Returns the button at 0-based index `button_num` + pub fn get(&self, button_num: usize) -> Result<Button, OutOfRangeError> { + if button_num < self.num_buttons { + Ok(Button { + button_num, + lifetime: PhantomData, + }) + } else { + Err(OutOfRangeError) + } + } + + pub fn buttons(&self) -> Buttons { + Buttons { + num_buttons: self.num_buttons, + curr_button: 0, + lifetime: PhantomData, + } + } + + pub fn subscribe<CB: Fn(usize, ButtonState)>( + &self, + callback: &'a mut CB, + ) -> TockResult<CallbackSubscription> { + syscalls::subscribe::<ButtonsEventConsumer, _>( + DRIVER_NUMBER, + subscribe_nr::SUBSCRIBE_CALLBACK, + callback, + ) + .map_err(Into::into) + } +} + +struct ButtonsEventConsumer; + +impl<CB: Fn(usize, ButtonState)> Consumer<CB> for ButtonsEventConsumer { + fn consume(callback: &mut CB, button_num: usize, button_state: usize, _: usize) { + let button_state = match button_state { + 0 => ButtonState::Released, + 1 => ButtonState::Pressed, + _ => return, + }; + callback(button_num, button_state); } } pub struct Buttons<'a> { - count: usize, - #[allow(dead_code)] // Used in drop - subscription: CallbackSubscription<'a>, + num_buttons: usize, + curr_button: usize, + lifetime: PhantomData<&'a ()>, } -impl<'a> Buttons<'a> { - pub fn iter_mut(&mut self) -> ButtonIter { - ButtonIter { - curr_button: 0, - button_count: self.count, - _lifetime: Default::default(), +impl<'a> Iterator for Buttons<'a> { + type Item = Button<'a>; + + fn next(&mut self) -> Option<Self::Item> { + if self.curr_button < self.num_buttons { + let item = Button { + button_num: self.curr_button, + lifetime: PhantomData, + }; + self.curr_button += 1; + Some(item) + } else { + None } } } @@ -75,65 +117,45 @@ Released, } -impl From<usize> for ButtonState { - fn from(state: usize) -> ButtonState { - match state { - 0 => ButtonState::Released, - 1 => ButtonState::Pressed, - _ => unreachable!(), +impl From<ButtonState> for bool { + fn from(button_state: ButtonState) -> Self { + match button_state { + ButtonState::Released => false, + ButtonState::Pressed => true, } } } -impl<'a, 'b> IntoIterator for &'b mut Buttons<'a> { - type Item = ButtonHandle<'b>; - type IntoIter = ButtonIter<'b>; - - fn into_iter(self) -> Self::IntoIter { - self.iter_mut() - } -} - -pub struct ButtonIter<'a> { - curr_button: usize, - button_count: usize, - _lifetime: PhantomData<&'a ()>, -} - -impl<'a> Iterator for ButtonIter<'a> { - type Item = ButtonHandle<'a>; - - fn next(&mut self) -> Option<Self::Item> { - if self.curr_button < self.button_count { - let item = ButtonHandle { - button_num: self.curr_button, - _lifetime: Default::default(), - }; - self.curr_button += 1; - Some(item) - } else { - None - } - } -} - -pub struct ButtonHandle<'a> { +pub struct Button<'a> { button_num: usize, - _lifetime: PhantomData<&'a ()>, + lifetime: PhantomData<&'a ()>, } -impl<'a> ButtonHandle<'a> { - pub fn enable(&mut self) -> TockResult<Button> { +impl<'a> Button<'a> { + pub fn button_num(&self) -> usize { + self.button_num + } + + pub fn read(&self) -> TockResult<ButtonState> { + let button_state = syscalls::command(DRIVER_NUMBER, command_nr::READ, self.button_num, 0)?; + match button_state { + 0 => Ok(ButtonState::Released), + 1 => Ok(ButtonState::Pressed), + _ => Err(OtherError::ButtonsDriverInvalidState.into()), + } + } + + pub fn enable_interrupt(&self) -> TockResult<()> { syscalls::command( DRIVER_NUMBER, command_nr::ENABLE_INTERRUPT, self.button_num, 0, )?; - Ok(Button { handle: self }) + Ok(()) } - pub fn disable(&mut self) -> TockResult<()> { + pub fn disable_interrupt(&self) -> TockResult<()> { syscalls::command( DRIVER_NUMBER, command_nr::DISABLE_INTERRUPT, @@ -143,15 +165,3 @@ Ok(()) } } - -pub struct Button<'a> { - handle: &'a ButtonHandle<'a>, -} - -impl<'a> Button<'a> { - pub fn read(&self) -> TockResult<ButtonState> { - syscalls::command(DRIVER_NUMBER, command_nr::READ, self.handle.button_num, 0) - .map(ButtonState::from) - .map_err(Into::into) - } -}
diff --git a/src/callback.rs b/src/callback.rs index 8149b32..053806f 100644 --- a/src/callback.rs +++ b/src/callback.rs
@@ -2,13 +2,39 @@ use core::marker::PhantomData; use core::ptr; -pub trait SubscribableCallback { - fn call_rust(&mut self, arg1: usize, arg2: usize, arg3: usize); +pub trait Consumer<T> { + fn consume(data: &mut T, arg1: usize, arg2: usize, arg3: usize); } -impl<F: FnMut(usize, usize, usize)> SubscribableCallback for F { - fn call_rust(&mut self, arg1: usize, arg2: usize, arg3: usize) { - self(arg1, arg2, arg3) +pub struct Identity3Consumer; + +impl<CB: FnMut(usize, usize, usize)> Consumer<CB> for Identity3Consumer { + fn consume(data: &mut CB, arg1: usize, arg2: usize, arg3: usize) { + data(arg1, arg2, arg3); + } +} + +pub struct Identity2Consumer; + +impl<CB: FnMut(usize, usize)> Consumer<CB> for Identity2Consumer { + fn consume(data: &mut CB, arg1: usize, arg2: usize, _: usize) { + data(arg1, arg2); + } +} + +pub struct Identity1Consumer; + +impl<CB: FnMut(usize)> Consumer<CB> for Identity1Consumer { + fn consume(data: &mut CB, arg1: usize, _: usize, _: usize) { + data(arg1); + } +} + +pub struct Identity0Consumer; + +impl<CB: FnMut()> Consumer<CB> for Identity0Consumer { + fn consume(data: &mut CB, _: usize, _: usize, _: usize) { + data(); } } @@ -16,15 +42,15 @@ pub struct CallbackSubscription<'a> { driver_number: usize, subscribe_number: usize, - _lifetime: PhantomData<&'a ()>, + lifetime: PhantomData<&'a ()>, } impl<'a> CallbackSubscription<'a> { - pub fn new(driver_number: usize, subscribe_number: usize) -> CallbackSubscription<'a> { + pub(crate) fn new(driver_number: usize, subscribe_number: usize) -> CallbackSubscription<'a> { CallbackSubscription { driver_number, subscribe_number, - _lifetime: Default::default(), + lifetime: PhantomData, } } }
diff --git a/src/console.rs b/src/console.rs index b4f2c51..ee6f50a 100644 --- a/src/console.rs +++ b/src/console.rs
@@ -1,3 +1,4 @@ +use crate::callback::Identity0Consumer; use crate::futures; use crate::result::TockResult; use crate::syscalls; @@ -56,8 +57,8 @@ )?; let is_written = Cell::new(false); - let mut is_written_alarm = |_, _, _| is_written.set(true); - let subscription = syscalls::subscribe( + let mut is_written_alarm = || is_written.set(true); + let subscription = syscalls::subscribe::<Identity0Consumer, _>( DRIVER_NUMBER, subscribe_nr::SET_ALARM, &mut is_written_alarm,
diff --git a/src/debug/mod.rs b/src/debug/mod.rs index 1e55bb3..4596aaa 100644 --- a/src/debug/mod.rs +++ b/src/debug/mod.rs
@@ -1,23 +1,23 @@ //! Heapless debugging functions for Tock troubleshooting mod low_level_debug; -use crate::drivers::Drivers; -pub use low_level_debug::*; -use crate::retrieve_drivers_unsafe; +use crate::drivers; + +pub use low_level_debug::*; pub fn println() { let buffer = [b'\n']; - let Drivers { console_driver, .. } = unsafe { retrieve_drivers_unsafe() }; - let mut console = console_driver.create_console(); + let drivers = unsafe { drivers::retrieve_drivers_unsafe() }; + let mut console = drivers.console.create_console(); let _ = console.write(&buffer); } pub fn print_as_hex(value: usize) { let mut buffer = [b'\n'; 11]; write_as_hex(&mut buffer, value); - let Drivers { console_driver, .. } = unsafe { retrieve_drivers_unsafe() }; - let mut console = console_driver.create_console(); + let drivers = unsafe { drivers::retrieve_drivers_unsafe() }; + let mut console = drivers.console.create_console(); let _ = console.write(buffer); } @@ -29,8 +29,8 @@ let mut buffer = [b'\n'; 15]; buffer[0..4].clone_from_slice(b"SP: "); write_as_hex(&mut buffer[4..15], stack_pointer); - let Drivers { console_driver, .. } = unsafe { retrieve_drivers_unsafe() }; - let mut console = console_driver.create_console(); + let drivers = unsafe { drivers::retrieve_drivers_unsafe() }; + let mut console = drivers.console.create_console(); let _ = console.write(buffer); } @@ -54,8 +54,8 @@ } } buffer[27] = b'\n'; - let Drivers { console_driver, .. } = retrieve_drivers_unsafe(); - let mut console = console_driver.create_console(); + let drivers = drivers::retrieve_drivers_unsafe(); + let mut console = drivers.console.create_console(); let _ = console.write(&buffer); }
diff --git a/src/drivers.rs b/src/drivers.rs index 814f61b..a8e1e98 100644 --- a/src/drivers.rs +++ b/src/drivers.rs
@@ -1,8 +1,8 @@ -use crate::adc::AdcDriver; -use crate::buttons::ButtonDriver; +use crate::adc::AdcDriverFactory; +use crate::buttons::ButtonsDriverFactory; use crate::console::ConsoleDriver; -use crate::gpio::GpioDriver; -use crate::led::LedDriverFactory; +use crate::gpio::GpioDriverFactory; +use crate::leds::LedsDriverFactory; use crate::result::OtherError; use crate::result::TockError; use crate::result::TockResult; @@ -13,27 +13,27 @@ use crate::sensors::TemperatureSensor; use crate::simple_ble::BleAdvertisingDriver; use crate::simple_ble::BleScanningDriver; -use crate::temperature::TemperatureDriver; +use crate::temperature::TemperatureDriverFactory; use crate::timer::DriverContext; use core::cell::Cell; /// Struct containing all drivers constructible through [retrieve_drivers()] #[non_exhaustive] pub struct Drivers { - pub console_driver: ConsoleDriver, - pub led_driver_factory: LedDriverFactory, - pub timer_context: DriverContext, - pub gpio_driver: GpioDriver, - pub temperature_driver: TemperatureDriver, - pub button_driver: ButtonDriver, - pub adc_driver: AdcDriver, - pub rng_driver: RngDriver, - pub ble_advertising_driver: BleAdvertisingDriver, - pub ble_scanning_driver: BleScanningDriver, + pub console: ConsoleDriver, + pub leds: LedsDriverFactory, + pub timer: DriverContext, + pub gpio: GpioDriverFactory, + pub temperature: TemperatureDriverFactory, + pub buttons: ButtonsDriverFactory, + pub adc: AdcDriverFactory, + pub rng: RngDriver, + pub ble_advertising: BleAdvertisingDriver, + pub ble_scanning: BleScanningDriver, pub ambient_light_sensor: AmbientLightSensor, pub temperature_sensor: TemperatureSensor, pub humidity_sensor: HumiditySensor, - pub ninedof_driver: NinedofDriver, + pub ninedof: NinedofDriver, } /// Retrieve [Drivers] struct. Returns struct only once. @@ -56,31 +56,30 @@ #[allow(clippy::declare_interior_mutable_const)] const DRIVERS: Drivers = Drivers { - adc_driver: AdcDriver, - ble_advertising_driver: BleAdvertisingDriver, - ble_scanning_driver: BleScanningDriver, - button_driver: ButtonDriver, - console_driver: ConsoleDriver, - led_driver_factory: LedDriverFactory, - timer_context: DriverContext { + adc: AdcDriverFactory, + ble_advertising: BleAdvertisingDriver, + ble_scanning: BleScanningDriver, + buttons: ButtonsDriverFactory, + console: ConsoleDriver, + leds: LedsDriverFactory, + timer: DriverContext { active_timer: Cell::new(None), }, - gpio_driver: GpioDriver, - temperature_driver: TemperatureDriver, - rng_driver: RngDriver, + gpio: GpioDriverFactory, + temperature: TemperatureDriverFactory, + rng: RngDriver, ambient_light_sensor: AmbientLightSensor, temperature_sensor: TemperatureSensor, humidity_sensor: HumiditySensor, - ninedof_driver: NinedofDriver, + ninedof: NinedofDriver, }; static mut DRIVERS_SINGLETON: Option<Drivers> = Some(DRIVERS); #[cfg(test)] mod test { - use super::DRIVERS; - use super::DRIVERS_SINGLETON; - use crate::retrieve_drivers; + use super::*; + #[test] pub fn can_be_retrieved_once() { reset_drivers_singleton();
diff --git a/src/electronics/shift_register.rs b/src/electronics/shift_register.rs index 10eac0d..0e7c63e 100644 --- a/src/electronics/shift_register.rs +++ b/src/electronics/shift_register.rs
@@ -1,17 +1,17 @@ -use crate::gpio::GpioPinWrite; +use crate::gpio::GpioWrite; use crate::result::TockResult; pub struct ShiftRegister<'a> { - data_pin: GpioPinWrite<'a>, - clock_pin: GpioPinWrite<'a>, - latch_pin: GpioPinWrite<'a>, + data_pin: &'a GpioWrite<'a>, + clock_pin: &'a GpioWrite<'a>, + latch_pin: &'a GpioWrite<'a>, } impl<'a> ShiftRegister<'a> { pub fn new( - data_pin: GpioPinWrite<'a>, - clock_pin: GpioPinWrite<'a>, - latch_pin: GpioPinWrite<'a>, + data_pin: &'a GpioWrite<'a>, + clock_pin: &'a GpioWrite<'a>, + latch_pin: &'a GpioWrite<'a>, ) -> ShiftRegister<'a> { ShiftRegister { data_pin,
diff --git a/src/entry_point/mod.rs b/src/entry_point/mod.rs index 6d299dd..cc5f402 100644 --- a/src/entry_point/mod.rs +++ b/src/entry_point/mod.rs
@@ -85,7 +85,7 @@ /// into the rustc-generated main(). This cannot use mutable global variables or /// global references to globals until it is done setting up the data segment. #[no_mangle] -pub unsafe extern "C" fn rust_start(app_start: usize, stacktop: usize, app_heap_break: usize) -> ! { +unsafe extern "C" fn rust_start(app_start: usize, stacktop: usize, app_heap_break: usize) -> ! { extern "C" { // This function is created internally by `rustc`. See // `src/lang_items.rs` for more details. @@ -146,12 +146,9 @@ use core::ptr::NonNull; use linked_list_allocator::Heap; -#[global_allocator] -static ALLOCATOR: TockAllocator = TockAllocator; - static mut HEAP: Heap = Heap::empty(); -struct TockAllocator; +pub struct TockAllocator; unsafe impl GlobalAlloc for TockAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
diff --git a/src/gpio.rs b/src/gpio.rs index 6f87e44..e69d5ee 100644 --- a/src/gpio.rs +++ b/src/gpio.rs
@@ -1,12 +1,14 @@ +use crate::callback::CallbackSubscription; +use crate::callback::Consumer; use crate::result::OtherError; -use crate::result::TockError; use crate::result::TockResult; use crate::syscalls; use core::marker::PhantomData; const DRIVER_NUMBER: usize = 0x00004; + mod command_nr { - pub const NUMBER_PINS: usize = 0; + pub const COUNT: usize = 0; pub const ENABLE_OUTPUT: usize = 1; pub const SET_HIGH: usize = 2; pub const SET_LOW: usize = 3; @@ -23,50 +25,101 @@ } #[non_exhaustive] -pub struct GpioDriver; +pub struct GpioDriverFactory; -impl GpioDriver { - pub fn all_pins<'a>(&'a self) -> TockResult<GpioIter<'a>> { - let number = self.number_of_pins()?; - Ok(GpioIter { - curr_gpio: 0, - gpio_count: number, - phantom: PhantomData, - }) +impl GpioDriverFactory { + pub fn init_driver(&mut self) -> TockResult<GpioDriver> { + let driver = GpioDriver { + num_gpios: syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?, + lifetime: PhantomData, + }; + Ok(driver) + } +} + +pub struct GpioDriver<'a> { + num_gpios: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> GpioDriver<'a> { + pub fn num_gpios(&self) -> usize { + self.num_gpios } - pub fn pin<'a>(&'a self, pin: usize) -> TockResult<GpioPinUnitialized<'a>> { - let number = self.number_of_pins()?; - if pin < number { - Ok(GpioPinUnitialized { - number: pin, - phantom: PhantomData, - }) - } else { - Err(TockError::Other(OtherError::NotEnoughGpioPins)) + pub fn gpios(&mut self) -> Gpios { + Gpios { + num_gpios: self.num_gpios(), + curr_gpio: 0, + lifetime: PhantomData, } } - fn number_of_pins(&self) -> TockResult<usize> { - syscalls::command(DRIVER_NUMBER, command_nr::NUMBER_PINS, 0, 0).map_err(Into::into) + pub fn subscribe<CB: Fn(usize, GpioState)>( + &self, + callback: &'a mut CB, + ) -> TockResult<CallbackSubscription> { + syscalls::subscribe::<GpioEventConsumer, _>( + DRIVER_NUMBER, + subscribe_nr::SUBSCRIBE_CALLBACK, + callback, + ) + .map_err(Into::into) } } -#[derive(Copy, Clone)] -pub struct GpioIter<'a> { - curr_gpio: usize, - gpio_count: usize, - phantom: PhantomData<&'a mut ()>, +struct GpioEventConsumer; + +impl<CB: Fn(usize, GpioState)> Consumer<CB> for GpioEventConsumer { + fn consume(callback: &mut CB, gpio_num: usize, gpio_state: usize, _: usize) { + let gpio_state = match gpio_state { + 0 => GpioState::Low, + 1 => GpioState::High, + _ => return, + }; + callback(gpio_num, gpio_state); + } } -impl<'a> Iterator for GpioIter<'a> { - type Item = GpioPinUnitialized<'a>; +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum GpioState { + Low, + High, +} + +impl From<GpioState> for bool { + fn from(gpio_state: GpioState) -> Self { + match gpio_state { + GpioState::Low => false, + GpioState::High => true, + } + } +} + +impl From<bool> for GpioState { + fn from(from_value: bool) -> Self { + if from_value { + GpioState::Low + } else { + GpioState::High + } + } +} + +pub struct Gpios<'a> { + num_gpios: usize, + curr_gpio: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> Iterator for Gpios<'a> { + type Item = Gpio<'a>; fn next(&mut self) -> Option<Self::Item> { - if self.curr_gpio < self.gpio_count { - let item = GpioPinUnitialized { - number: self.curr_gpio, - phantom: PhantomData, + if self.curr_gpio < self.num_gpios { + let item = Gpio { + gpio_num: self.curr_gpio, + lifetime: PhantomData, }; self.curr_gpio += 1; Some(item) @@ -76,142 +129,131 @@ } } -pub enum InputMode { - PullUp, - PullDown, - PullNone, +pub struct Gpio<'a> { + gpio_num: usize, + lifetime: PhantomData<&'a ()>, } -pub enum IrqMode { - EitherEdge, - RisingEdge, - FallingEdge, -} - -impl InputMode { - fn to_num(&self) -> usize { - match self { - InputMode::PullNone => 0, - InputMode::PullUp => 1, - InputMode::PullDown => 2, - } - } -} - -impl IrqMode { - fn to_num(&self) -> usize { - match self { - IrqMode::EitherEdge => 0, - IrqMode::RisingEdge => 1, - IrqMode::FallingEdge => 2, - } - } -} - -pub struct GpioPinUnitialized<'a> { - number: usize, - phantom: PhantomData<&'a mut ()>, -} - -pub struct GpioPinWrite<'a> { - number: usize, - phantom: PhantomData<&'a mut ()>, -} - -pub struct GpioPinRead<'a> { - number: usize, - phantom: PhantomData<&'a mut ()>, -} - -impl<'a> GpioPinUnitialized<'a> { - pub fn open_for_write(self) -> TockResult<GpioPinWrite<'a>> { - syscalls::command(DRIVER_NUMBER, command_nr::ENABLE_OUTPUT, self.number, 0)?; - Ok(GpioPinWrite { - number: self.number, - phantom: PhantomData, - }) +impl<'a> Gpio<'a> { + pub fn enable_output(&mut self) -> TockResult<GpioWrite> { + syscalls::command(DRIVER_NUMBER, command_nr::ENABLE_OUTPUT, self.gpio_num, 0)?; + let gpio_write = GpioWrite { + gpio_num: self.gpio_num, + lifetime: PhantomData, + }; + Ok(gpio_write) } - pub fn open_for_read( - self, - callback: Option<(extern "C" fn(usize, usize, usize, usize), IrqMode)>, - input_mode: InputMode, - ) -> TockResult<GpioPinRead<'a>> { - let (callback, irq_mode) = callback.unwrap_or((noop_callback, IrqMode::EitherEdge)); - self.enable_input(input_mode) - .and_then(|pin| pin.subscribe_callback(callback)) - .and_then(move |pin| pin.enable_callback(irq_mode)) - } - - fn subscribe_callback( - self, - callback: extern "C" fn(usize, usize, usize, usize), - ) -> TockResult<GpioPinUnitialized<'a>> { - syscalls::subscribe_fn( - DRIVER_NUMBER, - subscribe_nr::SUBSCRIBE_CALLBACK, - callback, - self.number, - )?; - Ok(self) - } - - fn enable_input(self, mode: InputMode) -> TockResult<GpioPinUnitialized<'a>> { + pub fn enable_input(&mut self, resistor_mode: ResistorMode) -> TockResult<GpioRead> { syscalls::command( DRIVER_NUMBER, command_nr::ENABLE_INPUT, - self.number, - mode.to_num(), + self.gpio_num, + resistor_mode as usize, )?; - Ok(self) + let gpio_read = GpioRead { + gpio_num: self.gpio_num, + lifetime: PhantomData, + }; + Ok(gpio_read) + } +} + +pub struct GpioWrite<'a> { + gpio_num: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> GpioWrite<'a> { + pub fn gpio_num(&self) -> usize { + self.gpio_num } - fn enable_callback(self, irq_mode: IrqMode) -> TockResult<GpioPinRead<'a>> { + pub fn set(&self, state: impl Into<GpioState>) -> TockResult<()> { + match state.into() { + GpioState::Low => self.set_low(), + GpioState::High => self.set_high(), + } + } + + pub fn set_low(&self) -> TockResult<()> { + syscalls::command(DRIVER_NUMBER, command_nr::SET_LOW, self.gpio_num, 0)?; + Ok(()) + } + + pub fn set_high(&self) -> TockResult<()> { + syscalls::command(DRIVER_NUMBER, command_nr::SET_HIGH, self.gpio_num, 0)?; + Ok(()) + } + + pub fn toggle(&self) -> TockResult<()> { + syscalls::command(DRIVER_NUMBER, command_nr::TOGGLE, self.gpio_num, 0)?; + Ok(()) + } +} + +impl<'a> Drop for GpioWrite<'a> { + fn drop(&mut self) { + let _ = syscalls::command(DRIVER_NUMBER, command_nr::DISABLE, self.gpio_num, 0); + } +} + +pub struct GpioRead<'a> { + gpio_num: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> GpioRead<'a> { + pub fn gpio_num(&self) -> usize { + self.gpio_num + } + + pub fn read(&self) -> TockResult<GpioState> { + let button_state = syscalls::command(DRIVER_NUMBER, command_nr::READ, self.gpio_num, 0)?; + match button_state { + 0 => Ok(GpioState::Low), + 1 => Ok(GpioState::High), + _ => Err(OtherError::GpioDriverInvalidState.into()), + } + } + + pub fn enable_interrupt(&self, trigger_type: TriggerType) -> TockResult<()> { syscalls::command( DRIVER_NUMBER, command_nr::ENABLE_INTERRUPT, - self.number, - irq_mode.to_num(), + self.gpio_num, + trigger_type as usize, )?; - Ok(GpioPinRead { - number: self.number, - phantom: PhantomData, - }) + Ok(()) } -} -impl<'a> GpioPinWrite<'a> { - pub fn set_low(&self) -> TockResult<()> { - syscalls::command(DRIVER_NUMBER, command_nr::SET_LOW, self.number, 0)?; - Ok(()) - } - pub fn set_high(&self) -> TockResult<()> { - syscalls::command(DRIVER_NUMBER, command_nr::SET_HIGH, self.number, 0)?; - Ok(()) - } - pub fn toggle(&self) -> TockResult<()> { - syscalls::command(DRIVER_NUMBER, command_nr::TOGGLE, self.number, 0)?; + pub fn disable_interrupt(&self, trigger_type: TriggerType) -> TockResult<()> { + syscalls::command( + DRIVER_NUMBER, + command_nr::DISABLE_INTERRUPT, + self.gpio_num, + trigger_type as usize, + )?; Ok(()) } } -impl<'a> GpioPinRead<'a> { - pub fn read(&'a self) -> bool { - syscalls::command(DRIVER_NUMBER, command_nr::READ, self.number, 0).ok() == Some(1) - } -} - -impl<'a> Drop for GpioPinWrite<'a> { +impl<'a> Drop for GpioRead<'a> { fn drop(&mut self) { - let _ = syscalls::command(DRIVER_NUMBER, command_nr::DISABLE, self.number, 0); + let _ = syscalls::command(DRIVER_NUMBER, command_nr::DISABLE, self.gpio_num, 0); } } -impl<'a> Drop for GpioPinRead<'a> { - fn drop(&mut self) { - let _ = syscalls::command(DRIVER_NUMBER, command_nr::DISABLE_INTERRUPT, self.number, 0); - let _ = syscalls::command(DRIVER_NUMBER, command_nr::DISABLE, self.number, 0); - } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ResistorMode { + PullNone = 0, + PullUp = 1, + PullDown = 2, } -extern "C" fn noop_callback(_: usize, _: usize, _: usize, _: usize) {} +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum TriggerType { + EitherEdge = 0, + RisingEdge = 1, + FallingEdge = 2, +}
diff --git a/src/lang_items.rs b/src/lang_items.rs index 6ff7529..69ddf85 100644 --- a/src/lang_items.rs +++ b/src/lang_items.rs
@@ -18,10 +18,12 @@ //! `rustc_main`. That's covered by the `_start` function in the root of this //! crate. -use crate::led::LedDriver; +use crate::drivers; +use crate::entry_point::TockAllocator; +use crate::leds::LedsDriver; +use crate::result::TockResult; use crate::timer::Duration; use crate::timer::ParallelSleepDriver; -use crate::Drivers; use core::alloc::Layout; use core::executor; use core::panic::PanicInfo; @@ -48,63 +50,63 @@ // Flash all LEDs (if available). executor::block_on(async { - let Drivers { - led_driver_factory, - timer_context, - .. - } = crate::retrieve_drivers_unsafe(); - let mut driver = timer_context.create_timer_driver(); - let timer_driver = driver.activate().ok(); - let led_driver = led_driver_factory.create_driver().ok(); - if let (Some(ref led_driver), Some(ref timer_driver)) = (led_driver, timer_driver) { - blink_all_leds(timer_driver, led_driver).await; + let mut drivers = drivers::retrieve_drivers_unsafe(); + + let leds_driver = drivers.leds.init_driver(); + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate(); + + if let (Ok(leds_driver), Ok(timer_driver)) = (leds_driver, timer_driver) { + let _ = blink_all_leds(&leds_driver, &timer_driver).await; } loop {} - }); - // Never type is not supported for T in Future - unreachable!() + }) } -async fn blink_all_leds(timer_driver: &ParallelSleepDriver<'_>, led_driver: &LedDriver) { +async fn blink_all_leds( + leds_driver: &LedsDriver<'_>, + timer_driver: &ParallelSleepDriver<'_>, +) -> TockResult<()> { loop { - for led in led_driver.all() { - let _ = led.on(); + for led in leds_driver.leds() { + led.on()?; } - let _ = timer_driver.sleep(Duration::from_ms(100)).await; - for led in led_driver.all() { - let _ = led.off(); + timer_driver.sleep(Duration::from_ms(100)).await?; + for led in leds_driver.leds() { + led.off()?; } - let _ = timer_driver.sleep(Duration::from_ms(100)).await; + timer_driver.sleep(Duration::from_ms(100)).await?; } } +#[global_allocator] +static ALLOCATOR: TockAllocator = TockAllocator; + #[alloc_error_handler] unsafe fn alloc_error_handler(_: Layout) -> ! { executor::block_on(async { - let Drivers { - led_driver_factory, - timer_context, - .. - } = crate::retrieve_drivers_unsafe(); - let mut driver = timer_context.create_timer_driver(); - let timer_driver = driver.activate().ok(); - let led_driver = led_driver_factory.create_driver().ok(); + let mut drivers = drivers::retrieve_drivers_unsafe(); - if let (Some(led_driver), Some(timer_driver)) = (led_driver, timer_driver) { - cycle_all_leds(&timer_driver, &led_driver).await; + let leds_driver = drivers.leds.init_driver(); + let mut timer_driver = drivers.timer.create_timer_driver(); + let timer_driver = timer_driver.activate(); + + if let (Ok(leds_driver), Ok(timer_driver)) = (leds_driver, timer_driver) { + let _ = cycle_all_leds(&leds_driver, &timer_driver).await; } loop {} - }); - // Never type is not supported for T in Future - unreachable!() + }) } -async fn cycle_all_leds(timer_driver: &ParallelSleepDriver<'_>, led_driver: &LedDriver) { +async fn cycle_all_leds( + leds_driver: &LedsDriver<'_>, + timer_driver: &ParallelSleepDriver<'_>, +) -> TockResult<()> { loop { - for led in led_driver.all() { - let _ = led.on(); - let _ = timer_driver.sleep(Duration::from_ms(100)).await; - let _ = led.off(); + for led in leds_driver.leds() { + led.on()?; + timer_driver.sleep(Duration::from_ms(100)).await?; + led.off()?; } } }
diff --git a/src/led.rs b/src/led.rs deleted file mode 100644 index 6e69944..0000000 --- a/src/led.rs +++ /dev/null
@@ -1,111 +0,0 @@ -use crate::result::TockResult; -use crate::syscalls::command; -use core::marker::PhantomData; - -const DRIVER_NUMBER: usize = 0x00002; - -mod command_nr { - pub const COUNT: usize = 0; - pub const ON: usize = 1; - pub const OFF: usize = 2; - pub const TOGGLE: usize = 3; -} - -#[non_exhaustive] -pub struct LedDriverFactory; - -impl LedDriverFactory { - pub fn create_driver(self) -> TockResult<LedDriver> { - command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?; - Ok(LedDriver) - } -} - -#[non_exhaustive] -pub struct LedDriver; - -pub struct Led<'a> { - led_num: usize, - phantom: PhantomData<&'a mut ()>, -} - -impl LedDriver { - pub fn get(&self, led_num: usize) -> Option<Led> { - if led_num < self.count().ok().unwrap() { - Some(Led { - led_num, - phantom: PhantomData, - }) - } else { - None - } - } - - pub fn count(&self) -> TockResult<usize> { - command(DRIVER_NUMBER, command_nr::COUNT, 0, 0).map_err(Into::into) - } - - pub fn all(&self) -> LedIter { - LedIter { - curr_led: 0, - led_count: self.count().unwrap_or(0), - phantom: PhantomData, - } - } -} - -/// Returns an iterator over all available LEDs. If the LED driver is not -/// present, the iterator will be empty. - -impl<'a> Led<'a> { - pub fn set_state(&self, state: bool) -> TockResult<()> { - if state { - self.on() - } else { - self.off() - } - } - - pub fn on(&self) -> TockResult<()> { - command(DRIVER_NUMBER, command_nr::ON, self.led_num, 0)?; - Ok(()) - } - - pub fn off(&self) -> TockResult<()> { - command(DRIVER_NUMBER, command_nr::OFF, self.led_num, 0)?; - Ok(()) - } - - pub fn toggle(&self) -> TockResult<()> { - command(DRIVER_NUMBER, command_nr::TOGGLE, self.led_num, 0)?; - Ok(()) - } - - pub fn number(&self) -> usize { - self.led_num - } -} - -#[derive(Copy, Clone)] -pub struct LedIter<'a> { - curr_led: usize, - led_count: usize, - phantom: PhantomData<&'a mut ()>, -} - -impl<'a> Iterator for LedIter<'a> { - type Item = Led<'a>; - - fn next(&mut self) -> Option<Self::Item> { - if self.curr_led < self.led_count { - let item = Led { - led_num: self.curr_led, - phantom: PhantomData, - }; - self.curr_led += 1; - Some(item) - } else { - None - } - } -}
diff --git a/src/leds.rs b/src/leds.rs new file mode 100644 index 0000000..e0f420c --- /dev/null +++ b/src/leds.rs
@@ -0,0 +1,129 @@ +use crate::result::OutOfRangeError; +use crate::result::TockResult; +use crate::syscalls::command; +use core::marker::PhantomData; + +const DRIVER_NUMBER: usize = 0x00002; + +mod command_nr { + pub const COUNT: usize = 0; + pub const ON: usize = 1; + pub const OFF: usize = 2; + pub const TOGGLE: usize = 3; +} + +#[non_exhaustive] +pub struct LedsDriverFactory; + +impl LedsDriverFactory { + pub fn init_driver(&mut self) -> TockResult<LedsDriver> { + let driver = LedsDriver { + num_leds: command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?, + lifetime: PhantomData, + }; + Ok(driver) + } +} + +pub struct LedsDriver<'a> { + num_leds: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> LedsDriver<'a> { + pub fn num_leds(&self) -> usize { + self.num_leds + } + + pub fn leds(&self) -> Leds { + Leds { + num_leds: self.num_leds, + curr_led: 0, + lifetime: PhantomData, + } + } + + /// Returns the led at 0-based index `led_num` + pub fn get(&self, led_num: usize) -> Result<Led, OutOfRangeError> { + if led_num < self.num_leds { + Ok(Led { + led_num, + lifetime: PhantomData, + }) + } else { + Err(OutOfRangeError) + } + } +} + +pub struct Leds<'a> { + num_leds: usize, + curr_led: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> Iterator for Leds<'a> { + type Item = Led<'a>; + + fn next(&mut self) -> Option<Self::Item> { + if self.curr_led < self.num_leds { + let item = Led { + led_num: self.curr_led, + lifetime: PhantomData, + }; + self.curr_led += 1; + Some(item) + } else { + None + } + } +} + +pub struct Led<'a> { + led_num: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> Led<'a> { + pub fn led_num(&self) -> usize { + self.led_num + } + + pub fn set(&self, state: impl Into<LedState>) -> TockResult<()> { + match state.into() { + LedState::On => self.on(), + LedState::Off => self.off(), + } + } + + pub fn on(&self) -> TockResult<()> { + command(DRIVER_NUMBER, command_nr::ON, self.led_num, 0)?; + Ok(()) + } + + pub fn off(&self) -> TockResult<()> { + command(DRIVER_NUMBER, command_nr::OFF, self.led_num, 0)?; + Ok(()) + } + + pub fn toggle(&self) -> TockResult<()> { + command(DRIVER_NUMBER, command_nr::TOGGLE, self.led_num, 0)?; + Ok(()) + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum LedState { + On, + Off, +} + +impl From<bool> for LedState { + fn from(from_value: bool) -> Self { + if from_value { + LedState::On + } else { + LedState::Off + } + } +}
diff --git a/src/lib.rs b/src/lib.rs index 1c1ecf0..e7d9d63 100644 --- a/src/lib.rs +++ b/src/lib.rs
@@ -1,41 +1,32 @@ #![feature(asm, alloc_error_handler, lang_items, naked_functions)] #![cfg_attr(any(target_arch = "arm", target_arch = "riscv32"), no_std)] -mod callback; +mod entry_point; +#[cfg(any(target_arch = "arm", target_arch = "riscv32"))] +mod lang_items; pub mod adc; pub mod ble_composer; pub mod ble_parser; pub mod buttons; +pub mod callback; pub mod console; pub mod debug; +pub mod drivers; pub mod electronics; pub mod futures; pub mod gpio; -pub mod led; +pub mod leds; pub mod memop; pub mod result; pub mod rng; pub mod sensors; pub mod shared_memory; pub mod simple_ble; +pub mod syscalls; pub mod temperature; pub mod timer; pub mod unwind_symbols; -#[cfg(any(target_arch = "arm", target_arch = "riscv32"))] -pub mod entry_point; - -#[cfg(any(target_arch = "arm", target_arch = "riscv32"))] -mod lang_items; - -pub mod syscalls; - +pub use drivers::retrieve_drivers; pub use libtock_codegen::main; - -pub(crate) mod drivers; -pub use drivers::*; - -/// Dummy structure to force importing the panic_handler and other no_std elements when nothing else -/// is imported. -pub struct LibTock;
diff --git a/src/result.rs b/src/result.rs index f3929ca..9dc4f16 100644 --- a/src/result.rs +++ b/src/result.rs
@@ -67,10 +67,12 @@ #[derive(Copy, Clone)] pub enum OtherError { + ButtonsDriverInvalidState, + GpioDriverInvalidState, TimerDriverDurationOutOfRange, TimerDriverErroneousClockFrequency, DriverAlreadyTaken, - NotEnoughGpioPins, + OutOfRangeError, } impl From<OtherError> for TockError { @@ -79,6 +81,14 @@ } } +pub struct OutOfRangeError; + +impl From<OutOfRangeError> for TockError { + fn from(_other: OutOfRangeError) -> Self { + TockError::Other(OtherError::OutOfRangeError) + } +} + pub const SUCCESS: isize = 0; pub const FAIL: isize = -1; pub const EBUSY: isize = -2;
diff --git a/src/rng.rs b/src/rng.rs index e04500f..039fa77 100644 --- a/src/rng.rs +++ b/src/rng.rs
@@ -1,3 +1,4 @@ +use crate::callback::Identity0Consumer; use crate::futures; use crate::result::TockResult; use crate::syscalls; @@ -26,8 +27,8 @@ let buf_len = buf.len(); let shared_memory = syscalls::allow(DRIVER_NUMBER, allow_nr::SHARE_BUFFER, buf)?; let is_filled = Cell::new(false); - let mut is_filled_alarm = |_, _, _| is_filled.set(true); - let subscription = syscalls::subscribe( + let mut is_filled_alarm = || is_filled.set(true); + let subscription = syscalls::subscribe::<Identity0Consumer, _>( DRIVER_NUMBER, subscribe_nr::BUFFER_FILLED, &mut is_filled_alarm,
diff --git a/src/simple_ble.rs b/src/simple_ble.rs index f700385..2876bce 100644 --- a/src/simple_ble.rs +++ b/src/simple_ble.rs
@@ -1,6 +1,6 @@ use crate::ble_composer::BlePayload; use crate::callback::CallbackSubscription; -use crate::callback::SubscribableCallback; +use crate::callback::Consumer; use crate::result::TockResult; use crate::shared_memory::SharedMemory; use crate::syscalls; @@ -79,9 +79,9 @@ } } -impl<CB: FnMut(usize, usize)> SubscribableCallback for BleCallback<CB> { - fn call_rust(&mut self, arg1: usize, arg2: usize, _: usize) { - (self.callback)(arg1, arg2); +impl<CB: FnMut(usize, usize)> Consumer<BleCallback<CB>> for BleCallback<CB> { + fn consume(data: &mut BleCallback<CB>, arg1: usize, arg2: usize, _: usize) { + (data.callback)(arg1, arg2); } } @@ -100,15 +100,15 @@ syscalls::allow(DRIVER_NUMBER, allow_nr::ALLOW_SCAN_BUFFER, scan_buffer).map_err(Into::into) } - pub fn start<'a, CB>( + pub fn start<'a, CB: FnMut(usize, usize)>( &'a mut self, callback: &'a mut BleCallback<CB>, - ) -> TockResult<CallbackSubscription> - where - BleCallback<CB>: SubscribableCallback, - { - let subscription = - syscalls::subscribe(DRIVER_NUMBER, subscribe_nr::BLE_PASSIVE_SCAN_SUB, callback)?; + ) -> TockResult<CallbackSubscription> { + let subscription = syscalls::subscribe::<BleCallback<CB>, _>( + DRIVER_NUMBER, + subscribe_nr::BLE_PASSIVE_SCAN_SUB, + callback, + )?; syscalls::command(DRIVER_NUMBER, command_nr::PASSIVE_SCAN, 1, 0)?; Ok(subscription) }
diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index 4cbef0f..c0289e5 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs
@@ -7,7 +7,7 @@ mod platform; use crate::callback::CallbackSubscription; -use crate::callback::SubscribableCallback; +use crate::callback::Consumer; use crate::result::AllowError; use crate::result::CommandError; use crate::result::SubscribeError; @@ -33,26 +33,26 @@ } } -pub fn subscribe<CB: SubscribableCallback>( +pub fn subscribe<C: Consumer<T>, T>( driver_number: usize, subscribe_number: usize, - callback: &mut CB, + payload: &mut T, ) -> Result<CallbackSubscription, SubscribeError> { - extern "C" fn c_callback<CB: SubscribableCallback>( + extern "C" fn c_callback<T, C: Consumer<T>>( arg1: usize, arg2: usize, arg3: usize, data: usize, ) { - let callback = unsafe { &mut *(data as *mut CB) }; - callback.call_rust(arg1, arg2, arg3); + let payload = unsafe { &mut *(data as *mut T) }; + C::consume(payload, arg1, arg2, arg3); } subscribe_fn( driver_number, subscribe_number, - c_callback::<CB>, - callback as *mut CB as usize, + c_callback::<T, C>, + payload as *mut _ as usize, ) .map(|_| CallbackSubscription::new(driver_number, subscribe_number)) }
diff --git a/src/temperature.rs b/src/temperature.rs index 4420791..1f1dcb1 100644 --- a/src/temperature.rs +++ b/src/temperature.rs
@@ -1,14 +1,18 @@ +use crate::callback::Identity1Consumer; use crate::futures; use crate::result::TockError; +use crate::result::TockResult; use crate::syscalls; use core::cell::Cell; use core::fmt; use core::fmt::Display; +use core::marker::PhantomData; use core::mem; const DRIVER_NUMBER: usize = 0x60000; mod command_nr { + pub const IS_DRIVER_AVAILABLE: usize = 0; pub const START_MEASUREMENT: usize = 1; } @@ -17,25 +21,40 @@ } #[non_exhaustive] -pub struct TemperatureDriver; +pub struct TemperatureDriverFactory; -impl TemperatureDriver { +impl TemperatureDriverFactory { + pub fn init_driver(&mut self) -> TockResult<TemperatureDriver> { + syscalls::command(DRIVER_NUMBER, command_nr::IS_DRIVER_AVAILABLE, 0, 0)?; + let driver = TemperatureDriver { + lifetime: PhantomData, + }; + Ok(driver) + } +} + +pub struct TemperatureDriver<'a> { + lifetime: PhantomData<&'a ()>, +} + +impl<'a> TemperatureDriver<'a> { pub async fn measure_temperature(&mut self) -> Result<Temperature, TockError> { - let temperature = Cell::<Option<isize>>::new(None); - let mut callback = |arg1, _, _| temperature.set(Some(arg1 as isize)); - let subscription = syscalls::subscribe( + let temperature = Cell::new(None); + let mut callback = |centi_celsius| temperature.set(Some(centi_celsius as isize)); + let subscription = syscalls::subscribe::<Identity1Consumer, _>( DRIVER_NUMBER, subscribe_nr::SUBSCRIBE_CALLBACK, &mut callback, )?; syscalls::command(DRIVER_NUMBER, command_nr::START_MEASUREMENT, 0, 0)?; - let temperatur = Temperature { + let result = Temperature { centi_celsius: futures::wait_for_value(|| temperature.get()).await, }; mem::drop(subscription); - Ok(temperatur) + Ok(result) } } + #[derive(Copy, Clone)] pub struct Temperature { centi_celsius: isize,
diff --git a/src/timer.rs b/src/timer.rs index 80adc1a..946c6e5 100644 --- a/src/timer.rs +++ b/src/timer.rs
@@ -1,7 +1,7 @@ //! Async timer driver. Can be used for (non-busy) sleeping. use crate::callback::CallbackSubscription; -use crate::callback::SubscribableCallback; +use crate::callback::Consumer; use crate::futures; use crate::result::OtherError; use crate::result::TockError; @@ -33,22 +33,21 @@ phantom: PhantomData<&'a mut ()>, } -impl<CB: FnMut(ClockValue, Alarm)> SubscribableCallback for WithCallback<'_, CB> { - fn call_rust(&mut self, clock_value: usize, alarm_id: usize, _: usize) { - (self.callback)( +struct TimerEventConsumer; + +impl<CB: FnMut(ClockValue, Alarm)> Consumer<WithCallback<'_, CB>> for TimerEventConsumer { + fn consume(data: &mut WithCallback<CB>, clock_value: usize, alarm_id: usize, _: usize) { + (data.callback)( ClockValue { num_ticks: clock_value as isize, - clock_frequency: self.clock_frequency, + clock_frequency: data.clock_frequency, }, Alarm { alarm_id }, ); } } -impl<'a, CB> WithCallback<'a, CB> -where - Self: SubscribableCallback, -{ +impl<'a, CB: FnMut(ClockValue, Alarm)> WithCallback<'a, CB> { pub fn init(&'a mut self) -> TockResult<Timer<'a>> { let num_notifications = syscalls::command(DRIVER_NUMBER, command_nr::IS_DRIVER_AVAILABLE, 0, 0)?; @@ -64,8 +63,11 @@ hz: clock_frequency, }; - let subscription = - syscalls::subscribe(DRIVER_NUMBER, subscribe_nr::SUBSCRIBE_CALLBACK, self)?; + let subscription = syscalls::subscribe::<TimerEventConsumer, _>( + DRIVER_NUMBER, + subscribe_nr::SUBSCRIBE_CALLBACK, + self, + )?; Ok(Timer { num_notifications, @@ -286,12 +288,10 @@ /// Context for the time driver. /// You can create a context as follows: /// ```no_run -/// # use libtock::timer::DriverContext; /// # use libtock::result::TockResult; -/// # use libtock::Drivers; -/// # #[libtock::main] -/// # async fn main() -> TockResult<()> { -/// let Drivers { timer_context, .. } = libtock::retrieve_drivers()?; +/// # async fn doc() -> TockResult<()> { +/// let mut drivers = libtock::retrieve_drivers()?; +/// let mut timer_context = drivers.timer; /// # Ok(()) /// # } /// ``` @@ -302,10 +302,10 @@ impl DriverContext { /// Create a driver timer from a context. - pub fn create_timer_driver(&self) -> TimerDriver<'_> { + pub fn create_timer_driver(&mut self) -> TimerDriver { TimerDriver { callback: Callback, - context: &self, + context: self, } } @@ -320,14 +320,12 @@ /// Timer driver instance. You can create a TimerDriver from a DriverContext as follows: /// ```no_run -/// # use libtock::timer::DriverContext; /// # use libtock::result::TockResult; -/// # use libtock::Drivers; -/// # #[libtock::main] -/// # async fn main() -> TockResult<()> { -/// # let Drivers { timer_context,.. } = libtock::retrieve_drivers()?; -/// # let mut driver = timer_context.create_timer_driver(); -/// let timer_driver = driver.activate()?; +/// # async fn doc() -> TockResult<()> { +/// # let mut drivers = libtock::retrieve_drivers()?; +/// # let mut timer_context = drivers.timer; +/// let mut timer_driver = timer_context.create_timer_driver(); +/// let timer_driver = timer_driver.activate()?; /// # Ok(()) /// # } /// ``` @@ -338,23 +336,22 @@ struct Callback; -impl SubscribableCallback for Callback { - fn call_rust(&mut self, _: usize, _: usize, _: usize) {} +struct ParallelTimerConsumer; + +impl<'a> Consumer<Callback> for ParallelTimerConsumer { + fn consume(_: &mut Callback, _: usize, _: usize, _: usize) {} } /// Activated time driver. Updates current time in the context and manages /// active alarms. /// Example usage (sleep for 1 second): /// ```no_run -/// # use libtock::timer::DriverContext; /// # use libtock::result::TockResult; /// # use libtock::timer::Duration; -/// # use libtock::Drivers; -/// # #[libtock::main] -/// # async fn main() -> TockResult<()> { -/// # let Drivers { timer_context,.. } = libtock::retrieve_drivers()?; -/// # let mut driver = timer_context.create_timer_driver(); -/// let timer_driver = driver.activate()?; +/// # async fn doc() -> TockResult<()> { +/// # let mut drivers = libtock::retrieve_drivers()?; +/// # let mut timer_driver = drivers.timer.create_timer_driver(); +/// let timer_driver = timer_driver.activate()?; /// timer_driver.sleep(Duration::from_ms(1000)).await?; /// # Ok(()) /// # } @@ -368,7 +365,7 @@ /// Activate the timer driver, will return a ParallelSleepDriver which /// can used to sleep. pub fn activate(&'a mut self) -> TockResult<ParallelSleepDriver<'a>> { - let subscription = syscalls::subscribe( + let subscription = syscalls::subscribe::<ParallelTimerConsumer, _>( DRIVER_NUMBER, subscribe_nr::SUBSCRIBE_CALLBACK, &mut self.callback,
diff --git a/src/unwind_symbols.rs b/src/unwind_symbols.rs index 98debbe..4215626 100644 --- a/src/unwind_symbols.rs +++ b/src/unwind_symbols.rs
@@ -1,3 +1,5 @@ +#![doc(hidden)] + // The stack unwinding ABI in ARM is specified as part of EABI. Although // libunwind has not been ported to Tock OS (and likely will not be), LLVM still // assumes some of the symbols are present. This causes linking errors @@ -6,6 +8,7 @@ // functions; for example, the Linux Kernel does so in arch/arm/kernel/unwind.c. // We do so here as well. The addition of these symbols to libtock-rs was // discussed at https://groups.google.com/forum/#!topic/tock-dev/eov8fJmskLk. + #[cfg(target_arch = "arm")] #[no_mangle] pub extern "C" fn __aeabi_unwind_cpp_pr0() {}