examples/leds_rust: Add Rust App for blinking the LED

- This PR adds `examples/leds_rust` to call NuttX POSIX APIs like `open()` and `ioctl()`, so that it blinks an LED
- The `leds_rust` app is also used for testing the GPIO and LED Drivers for Ox64 BL808 SBC and QEMU RISC-V Emulator in Google Summer of Code
- `leds_rust` be executed locally on Linux / macOS / Windows, by commenting out the first 2 lines of code
- The code is based on `examples/leds` in C, and `examples/hello_rust` in Rust

Co-Authored-By: Lup Yuen Lee <9960133+lupyuen@users.noreply.github.com>
This commit is contained in:
Rushabh Gala 2024-08-01 00:27:36 +05:30 committed by Xiang Xiao
parent b4999fa916
commit 437c903c47
5 changed files with 336 additions and 0 deletions

View File

@ -0,0 +1,30 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#
config EXAMPLES_LEDS_RUST
tristate "\"LEDs Rust\" example"
default n
depends on USERLED
---help---
Enable the \"LEDs Rust\" example
if EXAMPLES_LEDS_RUST
config EXAMPLES_LEDS_RUST_PROGNAME
string "Program name"
default "leds_rust"
---help---
This is the name of the program that will be used when the
program is installed.
config EXAMPLES_LEDS_RUST_PRIORITY
int "LEDs Rust task priority"
default 100
config EXAMPLES_LEDS_RUST_STACKSIZE
int "LEDs Rust stack size"
default DEFAULT_TASK_STACKSIZE
endif

View File

@ -0,0 +1,23 @@
############################################################################
# apps/examples/leds_rust/Make.defs
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
ifneq ($(CONFIG_EXAMPLES_LEDS_RUST),)
CONFIGURED_APPS += $(APPDIR)/examples/leds_rust
endif

View File

@ -0,0 +1,36 @@
############################################################################
# apps/examples/leds_rust/Makefile
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
include $(APPDIR)/Make.defs
# Hello, Rust! built-in application info
PROGNAME = $(CONFIG_EXAMPLES_LEDS_RUST_PROGNAME)
PRIORITY = $(CONFIG_EXAMPLES_LEDS_RUST_PRIORITY)
STACKSIZE = $(CONFIG_EXAMPLES_LEDS_RUST_STACKSIZE)
MODULE = $(CONFIG_EXAMPLES_LEDS_RUST)
# Hello, Rust! Example
MAINSRC = leds_rust_main.rs
RUSTFLAGS += -C panic=abort -O
include $(APPDIR)/Application.mk

View File

@ -0,0 +1,128 @@
/****************************************************************************
* apps/examples/leds_rust/leds_rust_main.rs
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Attributes
****************************************************************************/
/* Comment out these lines for testing with Rust Standard Library */
#![no_main]
#![no_std]
/****************************************************************************
* Uses
****************************************************************************/
#[cfg(target_os = "none")]
use core::{
panic::PanicInfo,
result::Result::{self, Err, Ok},
};
/****************************************************************************
* Modules
****************************************************************************/
mod nuttx;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Panic Handler (needed for [no_std] compilation)
****************************************************************************/
#[cfg(target_os = "none")] /* For NuttX */
#[panic_handler]
fn panic(_panic: &PanicInfo<'_>) -> ! {
loop {}
}
/****************************************************************************
* rust_main
****************************************************************************/
fn rust_main(_argc: i32, _argv: *const *const u8) -> Result<i32, i32> {
/* "Hello, Rust!!" using puts() from libc */
nuttx::safe_puts("Hello, Rust!!");
/* Blink LED 1 using ioctl() from NuttX */
nuttx::safe_puts("Opening /dev/userleds");
let fd = nuttx::safe_open("/dev/userleds", nuttx::O_WRONLY)?;
nuttx::safe_puts("Set LED 1 to 1");
nuttx::safe_ioctl(fd, nuttx::ULEDIOC_SETALL, 1)?;
nuttx::safe_puts("Sleeping...");
unsafe {
nuttx::usleep(500_000);
}
nuttx::safe_puts("Set LED 1 to 0");
nuttx::safe_ioctl(fd, nuttx::ULEDIOC_SETALL, 0)?;
unsafe {
nuttx::close(fd);
}
/* Exit with status 0 */
Ok(0)
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* leds_rust_main
****************************************************************************/
#[no_mangle]
pub extern "C" fn leds_rust_main(_argc: i32, _argv: *const *const u8) -> i32 {
/* Call the program logic in Rust Main */
let res = rust_main(0, core::ptr::null());
/* If Rust Main returns an error, print it */
if let Err(e) = res {
unsafe {
nuttx::printf(
b"ERROR: rust_main() failed with error %d\n\0" as *const u8,
e,
);
}
e
} else {
0
}
}
/****************************************************************************
* main
****************************************************************************/
#[cfg(not(target_os = "none"))] /* For Testing Locally */
fn main() {
leds_rust_main(0, core::ptr::null());
}

119
examples/leds_rust/nuttx.rs Normal file
View File

@ -0,0 +1,119 @@
/****************************************************************************
* apps/examples/leds_rust/nuttx.rs
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/* NuttX Definitions for Rust Apps */
/****************************************************************************
* Uses
****************************************************************************/
use core::result::Result::{self, Err, Ok};
/****************************************************************************
* Externs
****************************************************************************/
extern "C" {
pub fn printf(format: *const u8, ...) -> i32;
pub fn open(path: *const u8, oflag: i32, ...) -> i32;
pub fn close(fd: i32) -> i32;
pub fn ioctl(fd: i32, request: i32, ...) -> i32;
pub fn usleep(usec: u32) -> u32;
pub fn puts(s: *const u8) -> i32;
}
/****************************************************************************
* Public Constants
****************************************************************************/
pub const ENAMETOOLONG: i32 = 36;
pub const O_WRONLY: i32 = 1 << 1;
pub const PATH_MAX: usize = 256;
pub const PUTS_MAX: usize = 256;
pub const ULEDIOC_SETALL: i32 = 0x1d03;
/****************************************************************************
* Private Functions
****************************************************************************/
/* Copy the Rust Str to the Byte Buffer and terminate with null */
fn copy_to_buffer(s: &str, buffer: &mut [u8]) -> Result<(), ()> {
let byte_str = s.as_bytes();
let len = byte_str.len();
if len >= buffer.len() {
Err(())
} else {
buffer[..len].copy_from_slice(&byte_str[..len]);
buffer[len] = 0;
Ok(())
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/* Safe Version of open() */
pub fn safe_open(path: &str, oflag: i32) -> Result<i32, i32> {
let mut buffer = [0u8; PATH_MAX];
let res = copy_to_buffer(path, &mut buffer);
if res.is_err() {
unsafe {
puts(b"ERROR: safe_open() path size exceeds PATH_MAX\0" as *const u8);
}
return Err(-ENAMETOOLONG);
}
let fd = unsafe { open(buffer.as_ptr(), oflag) };
if fd < 0 {
Err(fd)
} else {
Ok(fd)
}
}
/* Safe Version of ioctl() */
pub fn safe_ioctl(fd: i32, request: i32, arg: i32) -> Result<i32, i32> {
let ret = unsafe { ioctl(fd, request, arg) };
if ret < 0 {
Err(ret)
} else {
Ok(ret)
}
}
/* Safe Version of puts() */
pub fn safe_puts(s: &str) {
let mut buffer = [0u8; PUTS_MAX];
let res = copy_to_buffer(s, &mut buffer);
if res.is_err() {
unsafe {
puts(b"ERROR: safe_puts() string size exceeds PUTS_MAX\0" as *const u8);
}
} else {
unsafe {
puts(buffer.as_ptr());
}
}
}