From 437c903c47d37d36aa1bd8ca75a078d304fadc99 Mon Sep 17 00:00:00 2001 From: Rushabh Gala <88271018+rushabhvg@users.noreply.github.com> Date: Thu, 1 Aug 2024 00:27:36 +0530 Subject: [PATCH] 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> --- examples/leds_rust/Kconfig | 30 +++++++ examples/leds_rust/Make.defs | 23 +++++ examples/leds_rust/Makefile | 36 ++++++++ examples/leds_rust/leds_rust_main.rs | 128 +++++++++++++++++++++++++++ examples/leds_rust/nuttx.rs | 119 +++++++++++++++++++++++++ 5 files changed, 336 insertions(+) create mode 100644 examples/leds_rust/Kconfig create mode 100644 examples/leds_rust/Make.defs create mode 100644 examples/leds_rust/Makefile create mode 100644 examples/leds_rust/leds_rust_main.rs create mode 100644 examples/leds_rust/nuttx.rs diff --git a/examples/leds_rust/Kconfig b/examples/leds_rust/Kconfig new file mode 100644 index 000000000..914693c88 --- /dev/null +++ b/examples/leds_rust/Kconfig @@ -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 diff --git a/examples/leds_rust/Make.defs b/examples/leds_rust/Make.defs new file mode 100644 index 000000000..0749823da --- /dev/null +++ b/examples/leds_rust/Make.defs @@ -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 diff --git a/examples/leds_rust/Makefile b/examples/leds_rust/Makefile new file mode 100644 index 000000000..ab3d86e2b --- /dev/null +++ b/examples/leds_rust/Makefile @@ -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 diff --git a/examples/leds_rust/leds_rust_main.rs b/examples/leds_rust/leds_rust_main.rs new file mode 100644 index 000000000..1bd1bca42 --- /dev/null +++ b/examples/leds_rust/leds_rust_main.rs @@ -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 { + /* "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()); +} diff --git a/examples/leds_rust/nuttx.rs b/examples/leds_rust/nuttx.rs new file mode 100644 index 000000000..4b075767b --- /dev/null +++ b/examples/leds_rust/nuttx.rs @@ -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 { + 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 { + 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()); + } + } +}