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}