class: center, middle # Renode & Tock Drivers API .title[.center[![SecureEmbeddedProgramming](../images/sep.png)]] .left[ Alexandru Radovici, Cosmin Daniel Radu, ilustrations by [Mieuneli](http://miau.laura.ro) ] --- # Overview 1. Tock Applications 2. Simple Driver 3. System calls --- # Tock Applications TODO --- # Hello Driver Kernel 1. Write the `Hello` and its implementation 2. Implement the `Driver` trait for `Hello` - all methiods have default implementation 3. Add the driver to the board structure in `main.rs` User space - Implement the system calls --- ## Kernel (hello.rs) Write the driver and implement the `Driver` trait (1 and 2). Write a file `hello.rs` in the same folder as `main.rs`. ```rust use kernel::{Driver, AppId, ReturnCode}; /// Each driver is identified by a unique number /// /// numbers higher than 0xa0000000 are unused by standard drivers pub const DRIVER_NUM: usize = 0xa0000000; /// The Hello structure pub struct Hello {} /// The hello implementation impl Hello { pub fn new() -> Hello { Hello {} } } /// The driver system calls implementation impl Driver for Hello { /// subscribe and allow will use the default implementation /// command syscall fn command(&self, command_num: usize, _data1: usize, _data2: usize, _app_id: AppId) -> ReturnCode { match command_num { // command_num 0 is used to verify if the driver exists 0 => ReturnCode::SUCCESS, // the command number is not defined _ => ReturnCode::ENOSUPPORT, } } } ``` --- ## Kernel (main.rs) Register the driver for in the board's structure (3) In `main.rs` ```rust /// use the driver module mod hello; // ... /// A structure representing this platform that holds references to all /// capsules for this platform. struct STM32F4Discovery { console: &'static capsules::console::Console<'static>, ipc: kernel::ipc::IPC, led: &'static capsules::led::LED<'static, stm32f407zg::gpio::Pin<'static>>, button: &'static capsules::button::Button<'static, stm32f407zg::gpio::Pin<'static>>, alarm: &'static capsules::alarm::AlarmDriver< 'static, VirtualMuxAlarm<'static, stm32f407zg::tim2::Tim2<'static>>, >, gpio: &'static capsules::gpio::GPIO<'static, stm32f407zg::gpio::Pin<'static>>, // hello driver hello: &'static hello::Hello } // ... /// Mapping of integer syscalls to objects that implement syscalls. impl Platform for STM32F4Discovery { fn with_driver
(&self, driver_num: usize, f: F) -> R where F: FnOnce(Option<&dyn kernel::Driver>) -> R, { match driver_num { capsules::console::DRIVER_NUM => f(Some(self.console)), capsules::led::DRIVER_NUM => f(Some(self.led)), capsules::button::DRIVER_NUM => f(Some(self.button)), capsules::alarm::DRIVER_NUM => f(Some(self.alarm)), kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)), capsules::gpio::DRIVER_NUM => f(Some(self.gpio)), // hello driver hello::DRIVER_NUM => f(Some(self.hello)), _ => f(None), } } } // ... /// Reset Handler. /// /// This symbol is loaded into vector table by the STM32F446RE chip crate. /// When the chip first powers on or later does a hard reset, after the core /// initializes all the hardware, the address of this function is loaded and /// execution begins here. #[no_mangle] pub unsafe fn reset_handler() { // ... /// Create a new instance of type Hello using Hello::new () let hello = static_init! (hello::Hello, hello::Hello::new()); /// Create a new instance of the board's structure let stm32f407zg = STM32F4Discovery { console: console, ipc: kernel::ipc::IPC::new(board_kernel, &memory_allocation_capability), led: led, button: button, alarm: alarm, gpio: gpio, // the hello driver hello: hello, }; // ... } ``` --- ## User space In your application's folder (`libtock-c/examples/...` - clone of of the examples) hello.h ```c #pargma once #define HELLO_DRIVER_NUMBER 0xa0000000 bool hello_is_available (void); ``` hello.c ```c #include "hello.h" #include
bool hello_is_available (void) { if (command (HELLO_DRIVER_NUMBER, 0, 0, 0) == TOCK_SUCCESS) { return true; } else { return false; } } ``` --- ## Workpoint 2 .top_image[![Work In Progress](../images/work_in_progress.png)] Add `command_num` 1 to the driver and print a text on the shell when it is called. ```rust /// The driver system calls implementation impl Driver for Hello { /// subscribe and allow will use the default implementation /// command syscall fn command(&self, command_num: usize, _data1: usize, _data2: usize, _app_id: AppId) -> ReturnCode { match command_num { // command_num 0 is used to verify if the driver exists 0 => ReturnCode::SUCCESS, 1 => // ... // the command number is not defined _ => ReturnCode::ENOSUPPORT, } } } ``` Add a function for it in the header and source file in userspace. Write and upload an app that userspac the driver. --- # Mutability All Tock traits use `&self` to be able to share a driver with several other drivers. Interior mutability: - Cell
(`core::cell::Cell`) - OptionalCell
(`kernel::common:cells::OptionalCell`) - TakeCell
(`kernel::common::cells::TakeCell`) .right[ .card[.small_vertical[![Cell](../images/sep_cell.png)]] .card[.small_vertical[![OptionalCell](../images/sep_optionalcell.png)]] .card[.small_vertical[![TakeCell](../images/sep_takecell.png)]] ] --- ## Cell Store a mutable value inside a immutable structure ```rust use kernel::{Driver, AppId, ReturnCode}; use core::cell::Cell; /// Each driver is identified by a unique number /// /// numbers higher than 0xa0000000 are unused by standard drivers pub const DRIVER_NUM: usize = 0xa0000000; /// The Hello structure pub struct Hello { nr: Cell
} /// The hello implementation impl Hello { pub fn new() -> Hello { Hello { nr: Cell::new (0) } } } /// The driver system calls implementation impl Driver for Hello { /// subscribe and allow will use the default implementation /// command syscall fn command(&self, command_num: usize, _data1: usize, _data2: usize, _app_id: AppId) -> ReturnCode { match command_num { // command_num 0 is used to verify if the driver exists 0 => ReturnCode::SUCCESS, 2 => { // modify number self.nr.set (self.nr.get () + 1); ReturnCode::SuccessWithValue { value: self.nr.get() } } // the command number is not defined _ => ReturnCode::ENOSUPPORT, } } } ``` --- ## Workpoint 3 .top_image[![Work In Progress](../images/work_in_progress.png)] Add `command_num` 2 that will increment a counter and return it to the app. Hint: use `ReturnCode::SuccessWithValue { value: ... }` Each time the app calls the system call, the driver will will return an incremented number. ```c #include "hello.c" #include
#include
int nr; int main(void) { while (true) { nr = hello_counter (); printf ("Counter %d\n", n); delay_ms (1000); } } ``` Hint: store the current number in the `Hello` structure.