nuttx_embedded_hal/
hal.rs

1//! Rust Embedded HAL for Apache NuttX RTOS
2
3use core::{
4    str::FromStr,
5};
6use embedded_hal::{
7    blocking::{
8        delay::{DelayMs, DelayUs},
9        i2c,
10        spi,
11    },
12    digital::v2,
13};
14use crate::{
15    close, ioctl, read, usleep, write,
16    i2c_msg_s, i2c_transfer_s, size_t, ssize_t,
17    GPIOC_WRITE, I2CIOC_TRANSFER, I2C_M_READ, O_RDWR,
18    String,
19};
20
21/// NuttX Implementation of I2C Read
22impl i2c::Read for I2c {
23    /// Error Type
24    type Error = i32;
25
26    /// TODO: Read `buf` from I2C Port
27    fn read(&mut self, _addr: u8, _buf: &mut [u8]) -> Result<(), Self::Error> {
28        //  Not implemented, because BL602 needs the I2C Sub Address for read to work
29        panic!();
30    }
31}
32
33/// NuttX Implementation of I2C Write
34impl i2c::Write for I2c {
35    /// Error Type
36    type Error = i32;
37
38    /// Write `buf` to I2C Port.
39    /// We assume this is a Write I2C Register operation, with Register ID at `buf[0]`.
40    /// TODO: Handle other kinds of I2C operations
41    fn write(&mut self, addr: u8, buf: &[u8]) -> Result<(), Self::Error> {
42        //  Copy to local buffer because we need a mutable reference
43        let mut buf2 = [0 ; 64];
44        assert!(buf.len() <= buf2.len());
45        buf2[..buf.len()].copy_from_slice(buf);
46
47        //  Buffer for received I2C data
48        let mut rbuf = [0 ; 1];
49
50        //  Compose I2C Transfer
51        let msg = [
52            //  First I2C Message: Send Register ID and I2C Data as I2C Sub Address
53            i2c_msg_s {
54                frequency: self.frequency,  //  I2C Frequency
55                addr:      addr as u16,     //  I2C Address
56                buffer:    buf2.as_mut_ptr(),     //  Buffer to be sent
57                length:    buf.len() as ssize_t,  //  Number of bytes to send
58
59                //  For BL602: Register ID must be passed as I2C Sub Address
60                #[cfg(target_arch = "riscv32")]  //  If architecture is RISC-V 32-bit...
61                flags:     crate::I2C_M_NOSTOP,  //  I2C Flags: Send I2C Sub Address
62                
63                //  Otherwise pass Register ID as I2C Data
64                #[cfg(not(target_arch = "riscv32"))]  //  If architecture is not RISC-V 32-bit...
65                flags:     0,  //  I2C Flags: None
66
67                //  TODO: Check for BL602 specifically (by target_abi?), not just RISC-V 32-bit
68            },
69            //  Second I2C Message: Read I2C Data, because this forces BL602 to send the first message correctly
70            i2c_msg_s {
71                frequency: self.frequency,  //  I2C Frequency
72                addr:      addr as u16,     //  I2C Address
73                buffer:    rbuf.as_mut_ptr(),      //  Buffer to be received
74                length:    rbuf.len() as ssize_t,  //  Number of bytes to receive
75                flags:     I2C_M_READ,  //  I2C Flags: Read I2C Data
76            },
77        ];
78        
79        //  Compose ioctl Argument to write I2C Registers
80        let xfer = i2c_transfer_s {
81            msgv: msg.as_ptr(),         //  Array of I2C messages for the transfer
82            msgc: msg.len() as size_t,  //  Number of messages in the array
83        };
84
85        //  Execute I2C Transfer to write I2C Registers
86        let ret = unsafe { 
87            ioctl(
88                self.fd,          //  I2C Port
89                I2CIOC_TRANSFER,  //  I2C Transfer
90                &xfer             //  I2C Messages for the transfer
91            )
92        };
93        assert!(ret >= 0);   
94        Ok(())
95    }
96}
97
98/// NuttX Implementation of I2C WriteRead
99impl i2c::WriteRead for I2c {
100    /// Error Type
101    type Error = i32;
102
103    /// Write `wbuf` to I2C Port and read `rbuf` from I2C Port.
104    /// We assume this is a Read I2C Register operation, with Register ID at `wbuf[0]`.
105    /// TODO: Handle other kinds of I2C operations
106    fn write_read(&mut self, addr: u8, wbuf: &[u8], rbuf: &mut [u8]) -> Result<(), Self::Error> {
107        //  We assume this is a Read I2C Register operation, with Register ID at wbuf[0]
108        assert_eq!(wbuf.len(), 1);
109        let reg_id = wbuf[0];
110
111        //  Read I2C Registers, starting at Register ID
112        let mut start = [reg_id ; 1];
113
114        //  Compose I2C Transfer
115        let msg = [
116            //  First I2C Message: Send Register ID
117            i2c_msg_s {
118                frequency: self.frequency,  //  I2C Frequency
119                addr:      addr as u16,     //  I2C Address
120                buffer:    start.as_mut_ptr(),      //  Buffer to be sent
121                length:    start.len() as ssize_t,  //  Number of bytes to send
122
123                //  For BL602: Register ID must be passed as I2C Sub Address
124                #[cfg(target_arch = "riscv32")]  //  If architecture is RISC-V 32-bit...
125                flags:     crate::I2C_M_NOSTOP,  //  I2C Flags: Send I2C Sub Address
126                
127                //  Otherwise pass Register ID as I2C Data
128                #[cfg(not(target_arch = "riscv32"))]  //  If architecture is not RISC-V 32-bit...
129                flags:     0,  //  I2C Flags: None
130
131                //  TODO: Check for BL602 specifically (by target_abi?), not just RISC-V 32-bit
132            },
133            //  Second I2C Message: Receive Register Values
134            i2c_msg_s {
135                frequency: self.frequency,  //  I2C Frequency
136                addr:      addr as u16,     //  I2C Address
137                buffer:    rbuf.as_mut_ptr(),      //  Buffer to be received
138                length:    rbuf.len() as ssize_t,  //  Number of bytes to receive
139                flags:     I2C_M_READ,  //  I2C Flags: Read I2C Data
140            },
141        ];
142
143        //  Compose ioctl Argument
144        let xfer = i2c_transfer_s {
145            msgv: msg.as_ptr(),         //  Array of I2C messages for the transfer
146            msgc: msg.len() as size_t,  //  Number of messages in the array
147        };
148
149        //  Execute I2C Transfer
150        let ret = unsafe { 
151            ioctl(
152                self.fd,
153                I2CIOC_TRANSFER,
154                &xfer
155            )
156        };
157        assert!(ret >= 0);   
158        Ok(())
159    }
160}
161
162/// NuttX Implementation of SPI Transfer
163impl spi::Transfer<u8> for Spi {
164    /// Error Type
165    type Error = i32;
166
167    /// Transfer SPI data
168    fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
169        //  Transmit data
170        let bytes_written = unsafe { 
171            write(self.fd, words.as_ptr(), words.len() as u32) 
172        };
173        assert_eq!(bytes_written, words.len() as i32);
174
175        //  Read response
176        let bytes_read = unsafe { 
177            read(self.fd, words.as_mut_ptr(), words.len() as u32) 
178        };
179        assert_eq!(bytes_read, words.len() as i32);
180
181        //  Return response
182        Ok(words)
183    }
184}
185
186/// NuttX Implementation of SPI Write
187impl spi::Write<u8> for Spi{
188    /// Error Type
189    type Error = i32;
190
191    /// Write SPI data
192    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
193        //  Transmit data
194        let bytes_written = unsafe { 
195            write(self.fd, words.as_ptr(), words.len() as u32) 
196        };
197        assert_eq!(bytes_written, words.len() as i32);
198        Ok(())
199    }
200}
201
202/// NuttX Implementation of GPIO Output
203impl v2::OutputPin for OutputPin {
204    /// Error Type
205    type Error = i32;
206
207    /// Set the GPIO Output to High
208    fn set_high(&mut self) -> Result<(), Self::Error> {
209        let ret = unsafe { 
210            ioctl(self.fd, GPIOC_WRITE, 1) 
211        };
212        assert!(ret >= 0);
213        Ok(())
214    }
215
216    /// Set the GPIO Output to low
217    fn set_low(&mut self) -> Result<(), Self::Error> {
218        let ret = unsafe { 
219            ioctl(self.fd, GPIOC_WRITE, 0) 
220        };
221        assert!(ret >= 0);
222        Ok(())
223    }
224}
225
226/// NuttX Implementation of GPIO Input
227impl v2::InputPin for InputPin {
228    /// Error Type
229    type Error = i32;
230
231    /// Return true if GPIO Input is high
232    fn is_high(&self) -> Result<bool, Self::Error> {
233        let mut invalue: i32 = 0;
234        let addr: *mut i32 = &mut invalue;
235        let ret = unsafe {
236            ioctl(self.fd, crate::GPIOC_READ, addr)
237        };
238        assert!(ret >= 0);
239        match invalue {
240            0 => Ok(false),
241            _ => Ok(true),
242        }
243    }
244
245    /// Return true if GPIO Input is low
246    fn is_low(&self) -> Result<bool, Self::Error> {
247        Ok(!self.is_high()?)
248    }
249}
250
251/// NuttX Implementation of GPIO Interrupt
252impl v2::InputPin for InterruptPin {
253    /// Error Type
254    type Error = i32;
255
256    /// Return true if GPIO Input is high
257    fn is_high(&self) -> Result<bool, Self::Error> {
258        let mut invalue: i32 = 0;
259        let addr: *mut i32 = &mut invalue;
260        let ret = unsafe {
261            ioctl(self.fd, crate::GPIOC_READ, addr)
262        };
263        assert!(ret >= 0);
264        match invalue {
265            0 => Ok(false),
266            _ => Ok(true),
267        }
268    }
269
270    /// Return true if GPIO Input is low
271    fn is_low(&self) -> Result<bool, Self::Error> {
272        Ok(!self.is_high()?)
273    }
274}
275
276/// NuttX Implementation of GPIO Unused
277impl v2::OutputPin for UnusedPin {
278    /// Error Type
279    type Error = i32;
280
281    /// Set the pin to high
282    fn set_high(&mut self) -> Result<(), Self::Error> {
283        Ok(())
284    }
285
286    /// Set the pin to low
287    fn set_low(&mut self) -> Result<(), Self::Error> {
288        Ok(())
289    }
290}
291
292/// NuttX Implementation of Delay in Microseconds
293impl DelayUs<u8> for Delay {
294    /// Sleep for us microseconds
295    fn delay_us(&mut self, us: u8) {
296        unsafe { usleep(us as u32); }
297    }
298}
299
300/// NuttX Implementation of Delay in Microseconds
301impl DelayUs<u16> for Delay {
302    /// Sleep for us microseconds
303    fn delay_us(&mut self, us: u16) {
304        unsafe { usleep(us as u32); }
305    }
306}
307
308/// NuttX Implementation of Delay in Microseconds
309impl DelayUs<u32> for Delay {
310    /// Sleep for us microseconds
311    fn delay_us(&mut self, us: u32) {
312        unsafe { usleep(us); }
313    }
314}
315
316/// NuttX Implementation of Delay in Milliseconds
317impl DelayMs<u8> for Delay {
318    /// Sleep for ms milliseconds
319    fn delay_ms(&mut self, ms: u8) {
320        unsafe { usleep(ms as u32 * 1000); }
321    }
322}
323
324/// NuttX Implementation of Delay in Milliseconds
325impl DelayMs<u16> for Delay {
326    /// Sleep for ms milliseconds
327    fn delay_ms(&mut self, ms: u16) {
328        unsafe { usleep(ms as u32 * 1000); }
329    }
330}
331
332/// NuttX Implementation of Delay in Milliseconds
333impl DelayMs<u32> for Delay {
334    /// Sleep for ms milliseconds
335    fn delay_ms(&mut self, ms: u32) {
336        unsafe { usleep(ms * 1000); }
337    }
338}
339
340/// NuttX Implementation of I2C Bus
341impl I2c {
342    /// Create an I2C Bus from a Device Path (e.g. "/dev/i2c0")
343    pub fn new(path: &str, frequency: u32) -> Result<Self, i32> {
344        //  Open the NuttX Device Path (e.g. "/dev/i2c0") for read-write
345        let fd = open(path, O_RDWR);
346        if fd < 0 { return Err(fd) }
347
348        //  Return the I2C Bus
349        Ok(Self { fd, frequency })
350    }
351}
352
353/// NuttX Implementation of SPI Bus
354impl Spi {
355    /// Create an SPI Bus from a Device Path (e.g. "/dev/spitest0")
356    pub fn new(path: &str) -> Result<Self, i32> {
357        //  Open the NuttX Device Path (e.g. "/dev/spitest0") for read-write
358        let fd = open(path, O_RDWR);
359        if fd < 0 { return Err(fd) }
360
361        //  Return the SPI Bus
362        Ok(Self { fd })
363    }
364}
365
366/// NuttX Implementation of GPIO Input
367impl InputPin {
368    /// Create a GPIO Input Pin from a Device Path (e.g. "/dev/gpio0")
369    pub fn new(path: &str) -> Result<Self, i32> {
370        //  Open the NuttX Device Path (e.g. "/dev/gpio0") for read-write
371        let fd = open(path, O_RDWR);
372        if fd < 0 { return Err(fd) }
373
374        //  Return the pin
375        Ok(Self { fd })
376    }
377}
378
379/// NuttX Implementation of GPIO Output
380impl OutputPin {
381    /// Create a GPIO Output Pin from a Device Path (e.g. "/dev/gpio1")
382    pub fn new(path: &str) -> Result<Self, i32> {
383        //  Open the NuttX Device Path (e.g. "/dev/gpio1") for read-write
384        let fd = open(path, O_RDWR);
385        if fd < 0 { return Err(fd) }
386
387        //  Return the pin
388        Ok(Self { fd })
389    }
390}
391
392/// NuttX Implementation of GPIO Interrupt
393impl InterruptPin {
394    /// Create a GPIO Interrupt Pin from a Device Path (e.g. "/dev/gpio2")
395    pub fn new(path: &str) -> Result<Self, i32> {
396        //  Open the NuttX Device Path (e.g. "/dev/gpio2") for read-write
397        let fd = open(path, O_RDWR);
398        if fd < 0 { return Err(fd) }
399
400        //  Return the pin
401        Ok(Self { fd })
402    }
403}
404
405/// NuttX Implementation of GPIO Unused
406impl UnusedPin {
407    /// Create a GPIO Unused Pin
408    pub fn new() -> Result<Self, i32> {
409        //  Return the pin
410        Ok(Self {})
411    }
412}
413
414/// NuttX Implementation of I2C Bus
415impl Drop for I2c {
416    /// Close the I2C Bus
417    fn drop(&mut self) {
418        unsafe { close(self.fd) };
419    }
420}
421
422/// NuttX Implementation of SPI Bus
423impl Drop for Spi {
424    /// Close the SPI Bus
425    fn drop(&mut self) {
426        unsafe { close(self.fd) };
427    }
428}
429
430/// NuttX Implementation of GPIO Input
431impl Drop for InputPin {
432    /// Close the GPIO Input
433    fn drop(&mut self) {
434        unsafe { close(self.fd) };
435    }
436}
437
438/// NuttX Implementation of GPIO Output
439impl Drop for OutputPin {
440    /// Close the GPIO Output
441    fn drop(&mut self) {
442        unsafe { close(self.fd) };
443    }
444}
445
446/// NuttX Implementation of GPIO Interrupt
447impl Drop for InterruptPin {
448    /// Close the GPIO Interrupt
449    fn drop(&mut self) {
450        unsafe { close(self.fd) };
451    }
452}
453
454/// NuttX I2C Bus
455pub struct I2c {
456    /// NuttX File Descriptor
457    fd: i32,
458    /// I2C Frequency in Hz
459    frequency: u32,
460}
461
462/// NuttX SPI Bus
463pub struct Spi {
464    /// NuttX File Descriptor
465    fd: i32,
466}
467
468/// NuttX GPIO Input
469pub struct InputPin {
470    /// NuttX File Descriptor
471    fd: i32,
472}
473
474/// NuttX GPIO Output
475pub struct OutputPin {
476    /// NuttX File Descriptor
477    fd: i32,
478}
479
480/// NuttX GPIO Interrupt
481pub struct InterruptPin {
482    /// NuttX File Descriptor
483    fd: i32,
484}
485
486/// NuttX GPIO Unused
487pub struct UnusedPin {
488}
489
490/// NuttX Delay
491pub struct Delay;
492
493/// Open a file and return the file descriptor.
494/// TODO: Auto-generate this wrapper with `bindgen` from the C declaration
495fn open(path: &str, oflag: i32) -> i32 {  //  `&str` is a reference to a string slice, similar to `const char *` in C
496
497    use crate::open;
498
499    //  Convert `str` to `String`, which similar to `char [64]` in C
500    let mut s_with_null = String::from_str(path)  //  `mut` because we will modify it
501        .expect("open conversion failed");        //  If it exceeds 64 chars, halt with an error
502    
503    //  Terminate the string with null, since we will be passing to C
504    s_with_null.push('\0')
505        .expect("open overflow");  //  If we exceed 64 chars, halt with an error
506
507    //  Convert the null-terminated string to a pointer
508    let p = s_with_null.as_str().as_ptr();
509
510    //  Call the C function
511    unsafe {  //  Flag this code as unsafe because we're calling a C function
512        open(p, oflag)
513    }
514
515    //  No semicolon `;` here, so the value returned by the C function will be passed to our caller
516}