Skip to content

Commit d74134e

Browse files
Nathan Jonestylerwhall
authored andcommitted
Implement serial Read/Write traits
1 parent c1fd526 commit d74134e

File tree

10 files changed

+243
-0
lines changed

10 files changed

+243
-0
lines changed

rust/zephyr-sys/wrapper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <misc/mempool.h>
88
#include <device.h>
99
#include <uart.h>
10+
#include <drivers/console/uart_pipe.h>
1011

1112
// Create a constant we can use from Rust in all cases
1213
#ifdef CONFIG_USERSPACE

rust/zephyr/src/uart.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
use super::NegErr;
22
use crate::device::Device;
33

4+
use std::cmp;
5+
use std::io;
6+
use std::io::{Read, Write, Error, ErrorKind};
7+
48
pub trait UartSyscalls {
59
fn uart_poll_out(device: &Device, out_char: char);
610

@@ -13,6 +17,91 @@ pub trait UartSyscalls {
1317
fn uart_configure(device: &Device, config: &UartConfig) -> Result<(), u32>;
1418
}
1519

20+
type UartCallback = unsafe extern "C" fn(*mut u8, *mut usize) -> *mut u8;
21+
22+
fn uart_pipe_register(buf: &mut [u8], cb: UartCallback) {
23+
unsafe {
24+
zephyr_sys::raw::uart_pipe_register(buf.as_mut_ptr(), buf.len(), Some(cb))
25+
};
26+
}
27+
28+
pub struct UartDevice;
29+
30+
const SERIAL_BUF_SIZE: usize = 4096;
31+
static mut SERIAL_BUF: [u8; SERIAL_BUF_SIZE] = [0; SERIAL_BUF_SIZE];
32+
static mut SERIAL_WRITE_INDEX: usize = 0;
33+
static mut SERIAL_READ_INDEX: usize = 0;
34+
35+
/// Returns the amount of space that can be written into the serial buffer including wrapping.
36+
unsafe fn serial_buf_space() -> usize {
37+
(SERIAL_READ_INDEX - SERIAL_WRITE_INDEX - 1) & (SERIAL_BUF_SIZE - 1)
38+
}
39+
40+
/// Returns the amount of data available for reading in the serial buffer without wrapping.
41+
unsafe fn serial_buf_count_to_end() -> usize {
42+
let end = SERIAL_BUF_SIZE - SERIAL_READ_INDEX;
43+
let n = (SERIAL_WRITE_INDEX + end) & (SERIAL_BUF_SIZE - 1);
44+
if n < end { n } else { end }
45+
}
46+
47+
#[no_mangle]
48+
unsafe extern "C" fn rust_uart_cb(buf: *mut u8, off: *mut usize) -> *mut u8 {
49+
let array = std::slice::from_raw_parts(buf, *off);
50+
let space = serial_buf_space();
51+
let idx = SERIAL_WRITE_INDEX;
52+
if space < *off {
53+
// XXX: do we need to handle this?
54+
panic!("serial overflow")
55+
}
56+
if idx + *off < SERIAL_BUF_SIZE {
57+
// easy case, copy in one chunk
58+
SERIAL_BUF[idx..idx+*off].copy_from_slice(&array);
59+
} else {
60+
// wrap, copy in two chunks
61+
let len = SERIAL_BUF_SIZE - idx;
62+
SERIAL_BUF[idx..SERIAL_BUF_SIZE].copy_from_slice(&array[..len]);
63+
SERIAL_BUF[0..*off-len].copy_from_slice(&array[len..]);
64+
}
65+
SERIAL_WRITE_INDEX = (SERIAL_WRITE_INDEX + *off) & (SERIAL_BUF_SIZE - 1);
66+
*off = 0;
67+
return buf;
68+
}
69+
70+
impl Read for UartDevice {
71+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
72+
unsafe {
73+
let avail = serial_buf_count_to_end();
74+
let idx = SERIAL_READ_INDEX;
75+
let amt = cmp::min(buf.len(), avail);
76+
if amt == 0 {
77+
return Err(Error::new(ErrorKind::WouldBlock, "would block"));
78+
}
79+
buf[..amt].copy_from_slice(&SERIAL_BUF[idx..idx+amt]);
80+
SERIAL_READ_INDEX = (SERIAL_READ_INDEX + amt) & (SERIAL_BUF_SIZE - 1);
81+
return Ok(amt);
82+
}
83+
}
84+
}
85+
86+
impl Write for UartDevice {
87+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
88+
unsafe {
89+
zephyr_sys::raw::uart_pipe_send(
90+
buf.as_ptr(),
91+
buf.len() as i32
92+
)
93+
};
94+
95+
// uart_pipe_send returns an int but it is always 0.
96+
Ok(buf.len())
97+
}
98+
99+
fn flush(&mut self) -> io::Result<()> {
100+
Ok(())
101+
}
102+
}
103+
104+
16105
macro_rules! trait_impl {
17106
($context:ident, $context_struct:path) => {
18107
impl UartSyscalls for $context_struct {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.13.1)
4+
get_filename_component(ZEPHYR_RUST ${CMAKE_CURRENT_SOURCE_DIR}/../.. ABSOLUTE)
5+
list(APPEND ZEPHYR_MODULES ${ZEPHYR_RUST})
6+
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
7+
project(hello_world)
8+
target_sources(app PRIVATE ./src/main.c)

samples/rust-app-serial/Cargo.lock

Lines changed: 61 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

samples/rust-app-serial/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "app"
3+
version = "0.1.0"
4+
authors = ["Tyler Hall <tylerwhall@gmail.com>"]
5+
edition = "2018"
6+
7+
[dependencies]
8+
cstr = "0.1.7"

samples/rust-app-serial/README.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Build
2+
=====
3+
4+
.. code-block:: console
5+
6+
mkdir -p build-x86 && cd build-x86
7+
cmake -GNinja -DBOARD=qemu_x86 ..
8+
ninja
9+
10+
Run
11+
===
12+
13+
.. code-block:: console
14+
sh ../run.sh
15+
16+
A `run.sh` script has been provided which adds the following line to the qemu
17+
command:
18+
19+
.. code-block:: console
20+
-serial tcp:localhost:4444,server,nowait
21+
22+
You can then connect to localhost:4444 using nc to simulate the other end of
23+
the serial connection.
24+
25+
Of course, you can change the -serial argument to any other option that qemu
26+
supports.

samples/rust-app-serial/prj.conf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CONFIG_MAIN_STACK_SIZE=2048
2+
CONFIG_NATIVE_UART_0_ON_OWN_PTY=y
3+
CONFIG_RUST_ALLOC_POOL=y
4+
CONFIG_RUST=y
5+
CONFIG_SERIAL=y
6+
CONFIG_UART_NATIVE_POSIX=y
7+
CONFIG_UART_PIPE_ON_DEV_NAME="UART_1"
8+
CONFIG_UART_PIPE=y
9+
CONFIG_USERSPACE=y

samples/rust-app-serial/run.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
$ZEPHYR_SDK_INSTALL_DIR/sysroots/x86_64-pokysdk-linux/usr/bin/qemu-system-i386 \
2+
-m 12 -cpu qemu32,+nx,+pae -device isa-debug-exit,iobase=0xf4,iosize=0x04 \
3+
-no-reboot -nographic -no-acpi -net none -pidfile qemu.pid -serial mon:stdio \
4+
-serial tcp:localhost:4444,server,nowait \
5+
-kernel ./zephyr/zephyr.elf

samples/rust-app-serial/src/lib.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
extern crate zephyr;
2+
3+
use std::io::{Read, Write};
4+
use std::time::Duration;
5+
6+
#[no_mangle]
7+
pub extern "C" fn rust_main() {
8+
let mut serial = zephyr::uart::UartDevice {};
9+
10+
loop {
11+
let x = "hello\n";
12+
let mut buf = [0u8; 32];
13+
let res = serial.write(x.as_bytes());
14+
println!("write: result {:?}", res);
15+
match serial.read(&mut buf) {
16+
Ok(n) => {
17+
let mystr = std::str::from_utf8(&buf).unwrap();
18+
println!("read: n={} mystr={}", n, mystr);
19+
},
20+
Err(rc) => println!("read: err rc={}", rc),
21+
}
22+
std::thread::sleep(Duration::from_millis(1000));
23+
}
24+
}

samples/rust-app-serial/src/main.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <zephyr.h>
2+
#include <drivers/console/uart_pipe.h>
3+
4+
extern void rust_main(void);
5+
extern u8_t *rust_uart_cb(u8_t *buf, size_t *off);
6+
7+
void main(void)
8+
{
9+
u8_t buf[256];
10+
uart_pipe_register(buf, sizeof(buf), rust_uart_cb);
11+
rust_main();
12+
}

0 commit comments

Comments
 (0)