mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #16833 from chrysn-pull-requests/rust-lib
Add some Rust library building infrastructure
This commit is contained in:
commit
d9879c96ca
@ -19,6 +19,7 @@ config BOARD_MICROBIT_V2
|
||||
select HAS_VDD_LC_FILTER_REG1
|
||||
|
||||
select HAVE_SAUL_GPIO
|
||||
select HAVE_LSM303AGR
|
||||
|
||||
source "$(RIOTBOARD)/common/microbit/Kconfig"
|
||||
source "$(RIOTBOARD)/common/nrf52/Kconfig"
|
||||
|
@ -1,2 +1,6 @@
|
||||
include $(RIOTBOARD)/common/microbit/Makefile.dep
|
||||
include $(RIOTBOARD)/common/nrf52/Makefile.dep
|
||||
|
||||
ifneq (,$(filter saul_default,$(USEMODULE)))
|
||||
USEMODULE += lsm303agr
|
||||
endif
|
||||
|
1
boards/microbit-v2/include/lsm303agr-config.rs
Normal file
1
boards/microbit-v2/include/lsm303agr-config.rs
Normal file
@ -0,0 +1 @@
|
||||
const I2C_DEVICES: &[u8] = &[0];
|
@ -79,6 +79,28 @@ The wrappers are [documented together with riot-sys and some of the examples].
|
||||
[I2CDevice]: https://rustdoc.etonomy.org/riot_wrappers/i2c/struct.I2CDevice.html
|
||||
[corresponding embedded-hal I2C traits]: https://rustdoc.etonomy.org/embedded_hal/blocking/i2c/index.html
|
||||
|
||||
Library components in Rust
|
||||
--------------------------
|
||||
|
||||
It is possible to use Rust in different modules than the application itself.
|
||||
|
||||
Such modules are usually pseudomodules (although they may be mixed with C in regular modules as well).
|
||||
They always depend on the `rust_riotmodules` module / crate,
|
||||
which collects all enabled modules into a single crate by means of optional features.
|
||||
|
||||
If the application is not written in Rust,
|
||||
that then depends on `rust_riotmodules_standalone`,
|
||||
which adds a panic handler and serves as a root crate.
|
||||
|
||||
If the application is written in Rust,
|
||||
`rust_riotmodules` needs to be added as a dependency of the application.
|
||||
(This helps deduplicate between application and library code,
|
||||
and also avoids symbol name clashes).
|
||||
This is done by adding a dependency on the local `rust_riotmodules` crate (which is a no-op when no such modules are enabled),
|
||||
and placing an `extern crate rust_riotmodules;` statement in the code.
|
||||
(The latter is needed even after most `extern crate` was abolished in 2018,
|
||||
because crates depended on but not used otherwise are usually not linked in).
|
||||
|
||||
Toolchain {#toolchain}
|
||||
---------
|
||||
|
||||
|
@ -106,6 +106,7 @@ rsource "lpd8808/Kconfig"
|
||||
rsource "lpsxxx/Kconfig"
|
||||
rsource "lsm6dsl/Kconfig"
|
||||
rsource "lsm303dlhc/Kconfig"
|
||||
rsource "lsm303agr/Kconfig"
|
||||
rsource "ltc4150/Kconfig"
|
||||
rsource "mag3110/Kconfig"
|
||||
rsource "mhz19/Kconfig"
|
||||
|
17
drivers/lsm303agr/Cargo.toml
Normal file
17
drivers/lsm303agr/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "riot-module-lsm303agr"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
authors = ["Christian Amsüss <chrysn@fsfe.org>"]
|
||||
license = "LGPL-2.1-only"
|
||||
|
||||
# Shipped with RIOT-OS; this has no external API that would make
|
||||
# sense to consume in any context than from within RIOT
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
lsm303agr = "^0.2"
|
||||
riot-wrappers = "^0.7.17"
|
||||
# Whatever lsm uses
|
||||
nb = "*"
|
20
drivers/lsm303agr/Kconfig
Normal file
20
drivers/lsm303agr/Kconfig
Normal file
@ -0,0 +1,20 @@
|
||||
# Copyright (c) 2022 HAW Hamburg
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
#
|
||||
|
||||
config MODULE_LSM303AGR
|
||||
bool
|
||||
prompt "LSM303AGR 3D accelerometer/magnetometer" if !(MODULE_SAUL_DEFAULT && HAVE_LSM303AGR)
|
||||
default y if (MODULE_SAUL_DEFAULT && HAVE_LSM303AGR)
|
||||
depends on HAS_PERIPH_I2C
|
||||
depends on TEST_KCONFIG
|
||||
select MODULE_RUST_RIOTMODULES
|
||||
select MODULE_PERIPH_I2C
|
||||
|
||||
config HAVE_LSM303AGR
|
||||
bool
|
||||
help
|
||||
Indicates that a lsm303agr sensor is present.
|
3
drivers/lsm303agr/Makefile.dep
Normal file
3
drivers/lsm303agr/Makefile.dep
Normal file
@ -0,0 +1,3 @@
|
||||
USEMODULE += rust_riotmodules
|
||||
|
||||
FEATURES_REQUIRED += periph_i2c
|
1
drivers/lsm303agr/Makefile.include
Normal file
1
drivers/lsm303agr/Makefile.include
Normal file
@ -0,0 +1 @@
|
||||
PSEUDOMODULES += lsm303agr
|
47
drivers/lsm303agr/doc.txt
Normal file
47
drivers/lsm303agr/doc.txt
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
|
||||
@defgroup drivers_lsm303agr LSM303AGR 3D accelerometer/magnetometer
|
||||
@ingroup drivers_sensors
|
||||
@ingroup drivers_saul
|
||||
@brief Device driver for the LSM303AGR 3D accelerometer/magnetometer
|
||||
|
||||
This driver is written in Rust,
|
||||
and based on the externally maintained [lsm303agr] crate.
|
||||
|
||||
This means that:
|
||||
|
||||
- it is only available on platforms supported by Rust,
|
||||
- it needs Rust installed on the build machine as described in @ref using-rust, and
|
||||
- it downloads additional Rust code from crates.io (in versions pinned by RIOT) when first used.
|
||||
|
||||
[lsm303agr]: https://crates.io/crates/lsm303agr
|
||||
|
||||
## Usage
|
||||
|
||||
When configured on a board, the devices are initialized at a fixed data acquisition rate and the chip's default range of +-2g.
|
||||
Data values are obtained on demand
|
||||
whenever queried through @ref drivers_saul.
|
||||
|
||||
For each device, two SAUL entries are registered labelled "LSM303AGR accelerometer" and "LSM303AGR magnetometer",
|
||||
which produces 3-axis values in units of g and Tesla, respectively.
|
||||
Accelerometer values are always scaled to milli-g (they come that way and don't exceed the i16 range of SAUL phydats);
|
||||
magnetometer readings are dynamically downscaled from their original i32 Nanotesla readings to fit in a phydat.
|
||||
|
||||
The driver is configured for a board by placing an `lsm303agr-config.rs` file
|
||||
in the board's include directory, which lists the I2C device(s) on which an accelerometer should be found:
|
||||
|
||||
```
|
||||
const I2C_DEVICES: &[u8] = &[0];
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
- Advanced features of the sensor
|
||||
(adjusting acquisition rate or resolution, free-fall detection, interrupts etc.)
|
||||
are not exposed.
|
||||
|
||||
- The driver accepts some memory overhead (roughly, two mutexes) to avoid unsafe code and enhance readability.
|
||||
The unsafe code would be sound based on the assertion that the initialization code is only called once
|
||||
(and in particular is not reentrant).
|
||||
|
||||
*/
|
123
drivers/lsm303agr/src/lib.rs
Normal file
123
drivers/lsm303agr/src/lib.rs
Normal file
@ -0,0 +1,123 @@
|
||||
#![no_std]
|
||||
|
||||
use lsm303agr::{interface, mode, Lsm303agr, AccelOutputDataRate::Hz50};
|
||||
|
||||
use riot_wrappers::{saul, println, i2c, cstr::cstr, mutex::Mutex};
|
||||
use saul::{Phydat, registration};
|
||||
|
||||
// FIXME: Is this the way we want to go? It's mimicking the C way, but we could just as well take
|
||||
// the board config from some YAML.
|
||||
include!(concat!(env!("BOARDDIR"), "/include/lsm303agr-config.rs"));
|
||||
|
||||
const NDEVICES: usize = I2C_DEVICES.len();
|
||||
|
||||
static DRIVER: registration::Driver<SaulLSM> = registration::Driver::new();
|
||||
static DRIVER_MAG: registration::Driver<SaulLSM, MagAspect> = registration::Driver::new();
|
||||
|
||||
// These two being in mutexes is somewhat unnecessary (the mutexes are locked at startup and then
|
||||
// never unlocked). The alternative is to unsafely access them (asserting that auto_init_lsm303agr
|
||||
// / init will only ever be called once), or hiding that assertion at some preprocessor level (like
|
||||
// cortex-m-rt's main does).
|
||||
//
|
||||
// Doing it at runtime comes at the cost of two global mutexes in memory, and some more startup
|
||||
// calls.
|
||||
//
|
||||
// Using an Option (with .insert) rather than MaybeUninit (with .write) is another step that
|
||||
// sacrifices minimal resources (could be none at all, didn't check) for readability.
|
||||
|
||||
// This can't go into ROM because it has a .next pointer that is altered at runtime when some other
|
||||
// device is registered. (In an alternative implementation where all SAUL registries are managed by
|
||||
// XFA, this would be possible; finding the point in time when they are ready to be used would be
|
||||
// tricky, though.).
|
||||
static REG: Mutex<[Option<registration::Registration<SaulLSM>>; NDEVICES]> = Mutex::new([None; NDEVICES]);
|
||||
static REG_MAG: Mutex<[Option<registration::Registration<SaulLSM, MagAspect>>; NDEVICES]> = Mutex::new([None; NDEVICES]);
|
||||
// This can't go into ROM because it contains an inner Mutex (and possibly state data from the
|
||||
// Lsm303agr instance, didn't bother to check)
|
||||
static LSM: Mutex<[Option<SaulLSM>; NDEVICES]> = Mutex::new([None; NDEVICES]);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn auto_init_lsm303agr() {
|
||||
if let Err(e) = init() {
|
||||
println!("LSM303AGR init error: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the configured LSM303AGR device, returning an error string for debug if anything
|
||||
/// goes wrong
|
||||
fn init() -> Result<(), &'static str> {
|
||||
let lsm = LSM
|
||||
.try_leak()
|
||||
.expect("LSM303AGR init is only called once");
|
||||
|
||||
let reg = REG
|
||||
.try_leak()
|
||||
.expect("LSM303AGR init is only called once");
|
||||
let reg_mag = REG_MAG
|
||||
.try_leak()
|
||||
.expect("LSM303AGR init is only called once");
|
||||
|
||||
for (&i2cdev, (lsm, (reg, reg_mag))) in I2C_DEVICES.iter().zip(lsm.iter_mut().zip(reg.iter_mut().zip(reg_mag.iter_mut()))) {
|
||||
let mut device = Lsm303agr::new_with_i2c(i2c::I2CDevice::new(i2cdev));
|
||||
|
||||
device.init()
|
||||
.map_err(|_| "Device initialization failed")?;
|
||||
device.set_accel_odr(Hz50)
|
||||
.map_err(|_| "Device configuration failed")?;
|
||||
|
||||
let lsm = lsm.insert(SaulLSM { device: Mutex::new(device) });
|
||||
|
||||
let reg = reg.insert(registration::Registration::new(&DRIVER, lsm, Some(cstr!("LSM303AGR accelerometer"))));
|
||||
let reg_mag = reg_mag.insert(registration::Registration::new(&DRIVER_MAG, lsm, Some(cstr!("LSM303AGR magnetometer"))));
|
||||
|
||||
reg.register_static();
|
||||
reg_mag.register_static();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct SaulLSM {
|
||||
device: Mutex<Lsm303agr<interface::I2cInterface<i2c::I2CDevice>, mode::MagOneShot>>,
|
||||
}
|
||||
|
||||
impl registration::Drivable for &SaulLSM {
|
||||
const CLASS: saul::Class = saul::Class::Sensor(Some(saul::SensorClass::Accel));
|
||||
|
||||
const HAS_READ: bool = true;
|
||||
|
||||
fn read(self) -> Result<Phydat, registration::Error> {
|
||||
// SAUL doesn't guarantee exclusive access; different threads may read simultaneously.
|
||||
let mut device = self.device.try_lock()
|
||||
.ok_or(registration::Error)?;
|
||||
|
||||
let data = device.accel_data()
|
||||
.map_err(|_| registration::Error)?;
|
||||
// Data is in the +-2g range by default, which doesn't overflow even the i16 SAUL uses
|
||||
Ok(Phydat::new(&[data.x as _, data.y as _, data.z as _], Some(saul::Unit::G), -3))
|
||||
}
|
||||
}
|
||||
|
||||
struct MagAspect(&'static SaulLSM);
|
||||
|
||||
impl From<&'static SaulLSM> for MagAspect {
|
||||
fn from(input: &'static SaulLSM) -> Self {
|
||||
Self(input)
|
||||
}
|
||||
}
|
||||
|
||||
impl registration::Drivable for MagAspect {
|
||||
const CLASS: saul::Class = saul::Class::Sensor(Some(saul::SensorClass::Mag));
|
||||
|
||||
const HAS_READ: bool = true;
|
||||
|
||||
fn read(self) -> Result<Phydat, registration::Error> {
|
||||
// SAUL doesn't guarantee exclusive access; different threads may read simultaneously.
|
||||
let mut device = self.0.device.try_lock()
|
||||
.ok_or(registration::Error)?;
|
||||
|
||||
let data = nb::block!(device.mag_data())
|
||||
.map_err(|_| registration::Error)?;
|
||||
// Original data is in nanotesla
|
||||
return Ok(Phydat::fit(&[data.x, data.y, data.z], Some(saul::Unit::T), -9))
|
||||
}
|
||||
}
|
@ -187,6 +187,10 @@ void saul_init_devs(void)
|
||||
extern void auto_init_lpsxxx(void);
|
||||
auto_init_lpsxxx();
|
||||
}
|
||||
if (IS_USED(MODULE_LSM303AGR)) {
|
||||
extern void auto_init_lsm303agr(void);
|
||||
auto_init_lsm303agr();
|
||||
}
|
||||
if (IS_USED(MODULE_LSM303DLHC)) {
|
||||
extern void auto_init_lsm303dlhc(void);
|
||||
auto_init_lsm303dlhc();
|
||||
|
@ -14,3 +14,9 @@ riot-wrappers = { version = "^0.7.18", features = [ "set_panic_handler", "panic_
|
||||
coap-message-demos = { git = "https://gitlab.com/chrysn/coap-message-demos/", default-features = false }
|
||||
coap-handler-implementations = "0.3"
|
||||
riot-coap-handler-demos = { git = "https://gitlab.com/etonomy/riot-module-examples/", features = [ "vfs" ] }
|
||||
|
||||
# While currently this exmple does not use any RIOT modules implemented in
|
||||
# Rust, that may change; it is best practice for any RIOT application that has
|
||||
# its own top-level Rust crate to include rust_riotmodules from inside
|
||||
# RIOTBASE.
|
||||
rust_riotmodules = { path = "../../sys/rust_riotmodules/" }
|
||||
|
@ -10,6 +10,8 @@ use riot_wrappers::{gcoap, thread, ztimer, gnrc};
|
||||
|
||||
use coap_handler_implementations::{ReportingHandlerBuilder, HandlerBuilder};
|
||||
|
||||
extern crate rust_riotmodules;
|
||||
|
||||
riot_main!(main);
|
||||
|
||||
fn main() {
|
||||
|
@ -11,3 +11,9 @@ crate-type = ["staticlib"]
|
||||
[dependencies]
|
||||
# `default-features = false` can be removed with 0.8, and enables building on stable during the 0.7 series
|
||||
riot-wrappers = { version = "0.7", features = [ "set_panic_handler" ], default-features = false }
|
||||
|
||||
# While currently this exmple does not use any RIOT modules implemented in
|
||||
# Rust, that may change; it is best practice for any RIOT application that has
|
||||
# its own top-level Rust crate to include rust_riotmodules from inside
|
||||
# RIOTBASE.
|
||||
rust_riotmodules = { path = "../../sys/rust_riotmodules/" }
|
||||
|
@ -8,6 +8,8 @@
|
||||
use riot_wrappers::riot_main;
|
||||
use riot_wrappers::println;
|
||||
|
||||
extern crate rust_riotmodules;
|
||||
|
||||
riot_main!(main);
|
||||
|
||||
fn main() {
|
||||
|
@ -80,5 +80,3 @@ LINKFLAGS += $(CFLAGS_CPU) $(CFLAGS_LINK) $(CFLAGS_DBG) $(CFLAGS_OPT) -nostartfi
|
||||
|
||||
# Platform triple as used by Rust
|
||||
RUST_TARGET = riscv32imac-unknown-none-elf
|
||||
# Workaround for https://github.com/rust-lang/rust-bindgen/issues/1555
|
||||
CARGO_EXTRACFLAGS += --target=riscv32
|
||||
|
@ -1,13 +1,3 @@
|
||||
# Rust's own version of the target triple / quadruple.
|
||||
#
|
||||
# This does not have a sane default, and needs to be set in the architecture
|
||||
# files.
|
||||
# RUST_TARGET = ...
|
||||
|
||||
# Flags that need to be added to the RIOT_CFLAGS passed to cargo in order to
|
||||
# make bindgen happy
|
||||
CARGO_EXTRACFLAGS ?=
|
||||
|
||||
# Setting anything other than "debug" or "release" will necessitate additional
|
||||
# -Z unstable-options as of 2021-03 nightlies.
|
||||
CARGO_PROFILE ?= release
|
||||
|
@ -1,6 +1,22 @@
|
||||
CARGO_COMPILE_COMMANDS = $(BINDIR)/cargo-compile-commands.json
|
||||
CARGO_COMPILE_COMMANDS_FLAGS = --clang
|
||||
|
||||
# When an application crate is built, it has to use rust_riotmodules_standalone itself to
|
||||
# pull in any Rust modules that might be enabled through RIOT's module system.
|
||||
# (If the application fails to add the rust_riotmodules_standalone dependency, that will
|
||||
# go unnoticed and work fine if none of the catchall-dispatched modules are
|
||||
# active -- but if one is enabled, this also serves to ensure the application
|
||||
# is not silently built without them, as the feature will not be available to
|
||||
# Cargo).
|
||||
#
|
||||
# This list should eventually be autogenerated.
|
||||
ifneq (,$(filter lsm303agr,$(USEMODULE)))
|
||||
CARGO_OPTIONS += --features rust_riotmodules/riot-module-lsm303agr
|
||||
endif
|
||||
ifneq (,$(filter shell_democommands,$(USEMODULE)))
|
||||
CARGO_OPTIONS += --features rust_riotmodules/riot-module-shell-democommands
|
||||
endif
|
||||
|
||||
# This is duplicating the compile-commands rule because unlike in the use case
|
||||
# when a $(RIOTBASE)/compile_commands.json is built, we *want* this to be
|
||||
# per-board and per-application. (The large mechanisms are shared anyway).
|
||||
@ -64,3 +80,6 @@ $(APPLICATION_RUST_MODULE).module: $(CARGO_LIB) FORCE
|
||||
# (should they not exist), and also from re-building everything every time
|
||||
# because the .cargo inside is as ephemeral as the build container.
|
||||
$(shell mkdir -p ~/.cargo/git ~/.cargo/registry)
|
||||
|
||||
FORCE:
|
||||
.phony: FORCE
|
||||
|
@ -132,3 +132,8 @@ export AFL_FLAGS # Additional command-line flags passed to afl durin
|
||||
# LOG_LEVEL # Logging level as integer (NONE: 0, ERROR: 1, WARNING: 2, INFO: 3, DEBUG: 4, default: 3)
|
||||
# KCONFIG_ADD_CONFIG # List of .config files to be merged used by Boards and CPUs. See kconfig.mk
|
||||
# VERBOSE_ASSERT # Set to 1 to print the file and line of a failed assert when assertions blow
|
||||
|
||||
export RUST_TARGET # Rust's own version of the target triple / quadruple.
|
||||
#
|
||||
# It is set by the architecture (and thus eventually the CPU), and exported to
|
||||
# be available when building Rust modules.
|
||||
|
@ -79,6 +79,7 @@ rsource "progress_bar/Kconfig"
|
||||
rsource "ps/Kconfig"
|
||||
rsource "random/Kconfig"
|
||||
rsource "rtc_utils/Kconfig"
|
||||
rsource "rust_riotmodules/Kconfig"
|
||||
rsource "saul_reg/Kconfig"
|
||||
rsource "schedstatistics/Kconfig"
|
||||
rsource "sema/Kconfig"
|
||||
|
@ -338,6 +338,11 @@ ifneq (,$(filter shell_commands,$(USEMODULE)))
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(filter shell_democommands,$(USEMODULE)))
|
||||
USEMODULE += rust_riotmodules
|
||||
USEMODULE += shell
|
||||
endif
|
||||
|
||||
ifneq (,$(filter md5sum sha1sum sha256sum,$(USEMODULE)))
|
||||
USEMODULE += vfs_util
|
||||
USEMODULE += hashes
|
||||
@ -956,4 +961,8 @@ ifneq (,$(filter fido2_ctap,$(USEMODULE)))
|
||||
USEMODULE += fido2
|
||||
endif
|
||||
|
||||
ifneq (,$(filter rust_riotmodules,$(USEMODULE)))
|
||||
include $(RIOTBASE)/sys/rust_riotmodules/Makefile.dep
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/sys/test_utils/Makefile.dep
|
||||
|
@ -159,3 +159,9 @@ endif
|
||||
ifneq (,$(filter shell_lock,$(USEMODULE)))
|
||||
include $(RIOTBASE)/sys/shell_lock/Makefile.include
|
||||
endif
|
||||
|
||||
PSEUDOMODULES += shell_democommands
|
||||
|
||||
ifneq (,$(filter rust_riotmodules,$(USEMODULE)))
|
||||
include $(RIOTBASE)/sys/rust_riotmodules/Makefile.include
|
||||
endif
|
||||
|
14
sys/rust_riotmodules/Cargo.toml
Normal file
14
sys/rust_riotmodules/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "rust_riotmodules"
|
||||
version = "0.1.0"
|
||||
authors = ["Christian Amsüss <chrysn@fsfe.org>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
|
||||
[dependencies]
|
||||
# The list contains all modules available in RIOT, and should eventually be
|
||||
# autogenerated (or at least automatically checked for consistency).
|
||||
|
||||
riot-module-lsm303agr = { path = "../../drivers/lsm303agr", optional = true }
|
||||
riot-module-shell-democommands = { path = "../../sys/shell/democommands", optional = true }
|
23
sys/rust_riotmodules/Kconfig
Normal file
23
sys/rust_riotmodules/Kconfig
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright (c) 2022 HAW Hamburg
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
#
|
||||
|
||||
menuconfig MODULE_RUST_RIOTMODULES
|
||||
bool "RUST RIOT modules"
|
||||
depends on TEST_KCONFIG
|
||||
depends on HAS_RUST_TARGET
|
||||
help
|
||||
This module is used when some module asks to have its code built
|
||||
through rust_riotmodules.
|
||||
|
||||
config MODULE_RUST_RIOTMODULES_STANDALONE
|
||||
bool "RUST RIOT modules are standalone"
|
||||
default y
|
||||
depends on MODULE_RUST_RIOTMODULES
|
||||
help
|
||||
This module is used when rust_riotmodules is selected, and the main
|
||||
application does not already contain Rust code through which the
|
||||
rust_riotmodules are built.
|
10
sys/rust_riotmodules/Makefile.dep
Normal file
10
sys/rust_riotmodules/Makefile.dep
Normal file
@ -0,0 +1,10 @@
|
||||
# No check for the presence of any of any dependencies (were there none, this
|
||||
# wouldn't have been pulled in), but only pull in building this in if the
|
||||
# application does not already provide a crate -- in which case the
|
||||
# CARGO_OPTIONS added depending on the pseudomodules are enabled in the regular
|
||||
# cargo-targets.
|
||||
ifeq (,${APPLICATION_RUST_MODULE})
|
||||
USEMODULE += rust_riotmodules_standalone
|
||||
endif
|
||||
|
||||
FEATURES_REQUIRED += rust_target
|
6
sys/rust_riotmodules/Makefile.include
Normal file
6
sys/rust_riotmodules/Makefile.include
Normal file
@ -0,0 +1,6 @@
|
||||
## This module is used when some module asking to have its code built
|
||||
# through rust_riotmodules. Whether that happens through actually enabling the
|
||||
# rust_riotmodules_standalone module or by the application's crate was decided
|
||||
# in ./Makefile.dep depending on whether a Rust application module is present
|
||||
# or not.
|
||||
PSEUDOMODULES += rust_riotmodules
|
14
sys/rust_riotmodules/src/lib.rs
Normal file
14
sys/rust_riotmodules/src/lib.rs
Normal file
@ -0,0 +1,14 @@
|
||||
#![no_std]
|
||||
|
||||
// Crates in here are pub used in case they make anything accessible to Rust applications directly;
|
||||
// they can then access the crates as `rust_riotmodules::lsm303agr` or similar.
|
||||
//
|
||||
// (Also, if they were not pub used, they'd need to be extern crate'd).
|
||||
|
||||
// This list should be as auto-generated / -maintained as the one in Cargo.toml
|
||||
|
||||
#[cfg(feature = "riot-module-lsm303agr")]
|
||||
pub use riot_module_lsm303agr as lsm303agr;
|
||||
|
||||
#[cfg(feature = "riot-module-shell-democommands")]
|
||||
pub use riot_module_shell_democommands as democommands;
|
BIN
sys/rust_riotmodules_standalone/Cargo.lock
generated
Normal file
BIN
sys/rust_riotmodules_standalone/Cargo.lock
generated
Normal file
Binary file not shown.
21
sys/rust_riotmodules_standalone/Cargo.toml
Normal file
21
sys/rust_riotmodules_standalone/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "rust_riotmodules_standalone"
|
||||
version = "0.1.0"
|
||||
authors = ["Christian Amsüss <chrysn@fsfe.org>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = [ "staticlib" ]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = "s"
|
||||
debug = true
|
||||
panic = "abort"
|
||||
codegen-units = 1
|
||||
|
||||
[dependencies]
|
||||
riot-wrappers = { version = "0.7", default-features = false, features = [ "set_panic_handler" ] }
|
||||
|
||||
rust_riotmodules = { path = "../rust_riotmodules" }
|
12
sys/rust_riotmodules_standalone/Makefile
Normal file
12
sys/rust_riotmodules_standalone/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
|
||||
include $(RIOTMAKE)/cargo-settings.inc.mk
|
||||
|
||||
APPLICATION_RUST_MODULE = rust_riotmodules_standalone
|
||||
|
||||
# No need to set the Cargo features enabled through the general pseudomodules:
|
||||
# They're added to the crate in cargo-targets.inc.mk no matter whether it's
|
||||
# building the rust_riotmodules_standalone top level crate or an application
|
||||
# crate.
|
||||
|
||||
include $(RIOTMAKE)/cargo-targets.inc.mk
|
9
sys/rust_riotmodules_standalone/src/lib.rs
Normal file
9
sys/rust_riotmodules_standalone/src/lib.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#![no_std]
|
||||
|
||||
// As we're pulling all crates in only for their side effects of having symbols (they are required
|
||||
// on the Rust side like riot_wrappers' panic_handler, and rust_riotmodules does that on its own
|
||||
// too) all these crates have to be extern-crate'd to be pulled in because they are not used on the
|
||||
// language level.
|
||||
|
||||
extern crate riot_wrappers;
|
||||
extern crate rust_riotmodules;
|
14
sys/shell/democommands/Cargo.toml
Normal file
14
sys/shell/democommands/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "riot-module-shell-democommands"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
authors = ["Christian Amsüss <chrysn@fsfe.org>"]
|
||||
license = "LGPL-2.1-only"
|
||||
|
||||
# Shipped with RIOT-OS; this has no external API that would make
|
||||
# sense to consume in any context than from within RIOT
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
riot-wrappers = "^0.7.17"
|
17
sys/shell/democommands/src/lib.rs
Normal file
17
sys/shell/democommands/src/lib.rs
Normal file
@ -0,0 +1,17 @@
|
||||
#![no_std]
|
||||
|
||||
use riot_wrappers::println;
|
||||
use core::fmt::Write;
|
||||
|
||||
riot_wrappers::static_command!(static_hello_world, "hello_world", "Print a greeting", hello_world);
|
||||
|
||||
pub fn hello_world<'a>(_w: &mut impl Write, args: impl IntoIterator<Item=&'a str>) {
|
||||
let mut args = args.into_iter();
|
||||
let commandname = args.next().expect("How was this started without an argv[0]?");
|
||||
|
||||
match args.next() {
|
||||
Some("--help") => println!("Usage: {commandname}"),
|
||||
None => println!("Hello RIOT!"),
|
||||
_ => println!("Invalid argument."),
|
||||
};
|
||||
}
|
12
tests/rust_libs/Makefile
Normal file
12
tests/rust_libs/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += shell
|
||||
USEMODULE += shell_democommands
|
||||
|
||||
FEATURES_REQUIRED += rust_target
|
||||
|
||||
# Currently unknown, something related to the LED_PORT definition that doesn't
|
||||
# pass C2Rust's transpilation
|
||||
BOARD_BLACKLIST := ek-lm4f120xl
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
10
tests/rust_libs/Makefile.ci
Normal file
10
tests/rust_libs/Makefile.ci
Normal file
@ -0,0 +1,10 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
nucleo-l011k4 \
|
||||
nucleo-l031k6 \
|
||||
samd10-xmini \
|
||||
stk3200 \
|
||||
stm32f030f4-demo \
|
||||
stm32g0316-disco \
|
||||
#
|
1
tests/rust_libs/main.c
Symbolic link
1
tests/rust_libs/main.c
Symbolic link
@ -0,0 +1 @@
|
||||
../shell/main.c
|
59
tests/rust_libs/tests/01-run.py
Executable file
59
tests/rust_libs/tests/01-run.py
Executable file
@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2017 Alexandre Abadie <alexandre.abadie@inria.fr>
|
||||
# 2022 Christian Amsüss <chrysn@fsfe.org>
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
import sys
|
||||
from testrunner import run
|
||||
|
||||
|
||||
EXPECTED_HELP = (
|
||||
'Command Description',
|
||||
'---------------------------------------',
|
||||
'bufsize Get the shell\'s buffer size',
|
||||
'start_test starts a test',
|
||||
'end_test ends a test',
|
||||
'echo prints the input command',
|
||||
'empty print nothing on command',
|
||||
'hello_world Print a greeting',
|
||||
'xfa_test1 xfa test command 1',
|
||||
'xfa_test2 xfa test command 2',
|
||||
)
|
||||
|
||||
PROMPT = '> '
|
||||
|
||||
CMDS = (
|
||||
('start_test', '[TEST_START]'),
|
||||
|
||||
# test default commands
|
||||
('help', EXPECTED_HELP),
|
||||
|
||||
('end_test', '[TEST_END]'),
|
||||
)
|
||||
|
||||
CMDS_REGEX = {'ps.rs'}
|
||||
|
||||
|
||||
def check_cmd(child, cmd, expected):
|
||||
regex = cmd in CMDS_REGEX
|
||||
child.expect(PROMPT)
|
||||
child.sendline(cmd)
|
||||
for line in expected:
|
||||
if regex:
|
||||
child.expect(line)
|
||||
else:
|
||||
child.expect_exact(line)
|
||||
|
||||
|
||||
def testfunc(child):
|
||||
# loop other defined commands and expected output
|
||||
for cmd, expected in CMDS:
|
||||
check_cmd(child, cmd, expected)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(run(testfunc))
|
@ -17,3 +17,8 @@ panic = "abort"
|
||||
[dependencies]
|
||||
# `default-features = false` can be removed with 0.8, and enables building on stable during the 0.7 series
|
||||
riot-wrappers = { version = "0.7", features = [ "set_panic_handler" ], default-features = false }
|
||||
|
||||
# While currently this test does not use any RIOT modules implemented in Rust,
|
||||
# that may change; it is best practice for any RIOT application that has its
|
||||
# own top-level Rust crate to include rust_riotmodules from inside RIOTBASE.
|
||||
rust_riotmodules = { path = "../../sys/rust_riotmodules/" }
|
||||
|
@ -8,6 +8,8 @@
|
||||
use riot_wrappers::riot_main;
|
||||
use riot_wrappers::println;
|
||||
|
||||
extern crate rust_riotmodules;
|
||||
|
||||
riot_main!(main);
|
||||
|
||||
fn main() {
|
||||
|
Loading…
Reference in New Issue
Block a user