1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//! Rust Embedded HAL for Apache NuttX RTOS

#![no_std]  //  Use the Rust Core Library instead of the Rust Standard Library, which is not compatible with embedded systems

//  Import NuttX Macros
#[macro_use]
mod macros;

//  Import NuttX HAL
mod hal;
pub use hal::*;

//  Import Libraries
use core::{            //  Rust Core Library
    fmt,               //  String Formatting    
    str::FromStr,      //  For converting `str` to `String`
};

/// Print a message to the serial console.
/// TODO: Auto-generate this wrapper with `bindgen` from the C declaration
pub fn puts(s: &str) -> i32 {  //  `&str` is a reference to a string slice, similar to `const char *` in C

    extern "C" {  //  Import C Function
        /// Print a message to the serial console (from C stdio library)
        fn puts(s: *const u8) -> i32;
    }

    //  Convert `str` to `String`, which similar to `char [64]` in C
    //  TODO: Increase the buffer size if we're sure we won't overflow the stack
    let mut s_with_null = String::from_str(s)  //  `mut` because we will modify it
        .expect("puts conversion failed");     //  If it exceeds 64 chars, halt with an error
    
    //  Terminate the string with null, since we will be passing to C
    s_with_null.push('\0')
        .expect("puts overflow");  //  If we exceed 64 chars, halt with an error

    //  Convert the null-terminated string to a pointer
    let p = s_with_null.as_str().as_ptr();

    //  Call the C function
    unsafe {  //  Flag this code as unsafe because we're calling a C function
        puts(p)
    }

    //  No semicolon `;` here, so the value returned by the C function will be passed to our caller
}

/// Print a formatted message to the serial console. Called by println! macro.
pub fn puts_format(args: fmt::Arguments<'_>) {
    //  Allocate a 64-byte buffer.
    //  TODO: Increase the buffer size if we're sure we won't overflow the stack
    let mut buf = String::new();

    //  Format the message into the buffer
    fmt::write(&mut buf, args)
        .expect("puts_format overflow");

    //  Print the buffer
    puts(&buf);
}

/// Limit Strings to 64 chars, similar to `char[64]` in C
pub type String = heapless::String::<64>;

extern "C" {  //  Import POSIX Functions. TODO: Import with bindgen from https://github.com/lupyuen/incubator-nuttx/blob/rusti2c/include/unistd.h
    /// Open a file path
    pub fn open(path: *const u8, oflag: i32, ...) -> i32;
    /// Read from a file descriptor
    pub fn read(fd: i32, buf: *mut u8, count: size_t)    -> ssize_t;
    /// Write to a file descriptor
    pub fn write(fd: i32, buf: *const u8, count: size_t) -> ssize_t;
    /// Close a file descriptor
    pub fn close(fd: i32) -> i32;
    /// Execute device commands
    pub fn ioctl(fd: i32, request: i32, ...) -> i32;  //  On NuttX: request is i32, not u64 like Linux
    /// Sleep for a number of seconds
    pub fn sleep(secs: u32)  -> u32;
    /// Sleep for a number of microseconds
    pub fn usleep(usec: u32) -> u32;
    /// Exit the program
    pub fn exit(status: u32) -> !;
}

//  GPIO ioctl Commands. TODO: Import with bindgen from https://github.com/lupyuen/incubator-nuttx/blob/rust/include/nuttx/ioexpander/gpio.h
/// Set the value of an output GPIO
pub const GPIOC_WRITE:      i32 = _GPIOBASE | 1;  //  _GPIOC(1)
/// Read the value of an input or output GPIO
pub const GPIOC_READ:       i32 = _GPIOBASE | 2;  //  _GPIOC(2)
/// Return the GPIO pin type.
pub const GPIOC_PINTYPE:    i32 = _GPIOBASE | 3;  //  _GPIOC(3)
/// Register to receive a signal whenever there an interrupt
/// is received on an input gpio pin.  This feature, of course,
/// depends upon interrupt GPIO support from the platform.
pub const GPIOC_REGISTER:   i32 = _GPIOBASE | 4;  //  _GPIOC(4)
/// Stop receiving signals for pin interrupts.
pub const GPIOC_UNREGISTER: i32 = _GPIOBASE | 5;  //  _GPIOC(5)
/// Set the GPIO pin type.
pub const GPIOC_SETPINTYPE: i32 = _GPIOBASE | 6;  //  _GPIOC(6)

//  GPIO Constants. TODO: Import with bindgen from https://github.com/lupyuen/incubator-nuttx/blob/rust/include/fcntl.h
/// GPIO driver commands
pub const _GPIOBASE: i32 = 0x2300;
//  #define _GPIOC(nr)       _IOC(_GPIOBASE,nr)
//  #define _IOC(type,nr)    ((type)|(nr))

//  I2C ioctl Commands.  TODO: Import with bindgen from https://github.com/lupyuen/incubator-nuttx/blob/rusti2c/include/nuttx/i2c/i2c_master.h#L105-L129
/// Perform an I2C transfer
pub const I2CIOC_TRANSFER: i32 = _I2CBASE | 0x0001;  //  _I2CIOC(0x0001)
/// Perform an I2C bus reset in an attempt to break loose stuck I2C devices.
pub const I2CIOC_RESET:    i32 = _I2CBASE | 0x0002;  //  _I2CIOC(0x0002)
//  #define _I2CIOC(nr)       _IOC(_I2CBASE,nr)
//  #define _IOC(type,nr)     ((type)|(nr))

//  I2C Constants. TODO: Import with bindgen from https://github.com/lupyuen/incubator-nuttx/blob/rusti2c/include/nuttx/i2c/i2c_master.h#L93-L96
/// Read data, from slave to master
pub const I2C_M_READ:    u16 = 0x0001;
/// Ten bit address
pub const I2C_M_TEN:     u16 = 0x0002;
/// Message should not end with a STOP
pub const I2C_M_NOSTOP:  u16 = 0x0040;
/// Message should not begin with a START
pub const I2C_M_NOSTART: u16 = 0x0080;
/// I2C driver commands
pub const _I2CBASE:      i32 = 0x2100; 

/// I2C Message Struct: I2C transaction segment beginning with a START. A number of these can
/// be transferred together to form an arbitrary sequence of write/read
/// transfer to an I2C device.
/// TODO: Import with bindgen from https://github.com/lupyuen/incubator-nuttx/blob/rusti2c/include/nuttx/i2c/i2c_master.h#L208-L215
#[repr(C)]
pub struct i2c_msg_s {
    /// I2C Frequency
    pub frequency: u32,
    /// I2C Address
    pub addr: u16,
    /// I2C Flags (I2C_M_*)
    pub flags: u16,
    /// Buffer to be transferred
    pub buffer: *mut u8,
    /// Length of the buffer in bytes
    pub length: ssize_t,
}

/// I2C Transfer Struct: This structure is used to communicate with the I2C character driver in
/// order to perform IOCTL transfers.
/// TODO: Import with bindgen from https://github.com/lupyuen/incubator-nuttx/blob/rusti2c/include/nuttx/i2c/i2c_master.h#L231-L235
#[repr(C)]
pub struct i2c_transfer_s {
    /// Array of I2C messages for the transfer
    pub msgv: *const i2c_msg_s,
    /// Number of messages in the array
    pub msgc: size_t,
}

//  Input / Output Constants. TODO: Import with bindgen from https://github.com/lupyuen/incubator-nuttx/blob/rust/include/fcntl.h
/// Open for read access (only)
pub const O_RDONLY: i32 = 1 << 0;       
/// Read access is permitted (non-standard)
pub const O_RDOK:   i32 = O_RDONLY;      
/// Open for write access (only)
pub const O_WRONLY: i32 = 1 << 1;        
/// Write access is permitted (non-standard)
pub const O_WROK:   i32 = O_WRONLY;      
/// Open for both read & write access
pub const O_RDWR:   i32 = O_RDOK|O_WROK; 

/// size_t for NuttX 32-bit. TODO: Support other architectures
#[allow(non_camel_case_types)]
pub type size_t = u32;

/// ssize_t for NuttX 32-bit. TODO: Support other architectures
#[allow(non_camel_case_types)]
pub type ssize_t = i32;