First commit
This commit is contained in:
12
.cargo/config.toml
Normal file
12
.cargo/config.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[build]
|
||||
target = "riscv64gc-unknown-linux-musl"
|
||||
rustflags = [
|
||||
"-Zlocation-detail=none",
|
||||
"-Zfmt-debug=none",
|
||||
"-Zunstable-options",
|
||||
"-Cpanic=immediate-abort",
|
||||
]
|
||||
|
||||
[unstable]
|
||||
build-std = ["core", "alloc", "std", "panic_abort"]
|
||||
build-std-features = ["optimize_for_size"]
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
144
Cargo.lock
generated
Normal file
144
Cargo.lock
generated
Normal file
@@ -0,0 +1,144 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oc2r-rust"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06"
|
||||
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "oc2r-rust"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
|
||||
codegen-units = 1
|
||||
[dependencies]
|
||||
serde = { version = "1", features = ["derive"]}
|
||||
serde_json = "1"
|
||||
nix = { version = "0.30", features = ["term", "poll"]}
|
||||
6
Cross.toml
Normal file
6
Cross.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[target.riscv64gc-unknown-linux-musl]
|
||||
image = "ghcr.io/cross-rs/riscv64gc-unknown-linux-musl:edge"
|
||||
|
||||
[build]
|
||||
build-std = true
|
||||
default-target = "riscv64gc-unknown-linux-musl"
|
||||
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "nightly"
|
||||
202
src/main.rs
Normal file
202
src/main.rs
Normal file
@@ -0,0 +1,202 @@
|
||||
use nix::errno::Errno;
|
||||
use nix::poll::{PollFd, PollFlags, PollTimeout, poll};
|
||||
use nix::sys::termios;
|
||||
use nix::sys::termios::SpecialCharacterIndices::{VMIN, VTIME};
|
||||
use nix::unistd::{read, write};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs::OpenOptions;
|
||||
use std::os::fd::{AsFd, BorrowedFd};
|
||||
use std::{io, result};
|
||||
|
||||
// Requests -----------------------------------------------------------
|
||||
#[derive(Serialize, Debug)]
|
||||
#[serde(tag = "type", rename_all = "lowercase")]
|
||||
pub enum RpcRequest<'a> {
|
||||
List,
|
||||
Methods {
|
||||
data: &'a str,
|
||||
},
|
||||
Invoke {
|
||||
#[serde(rename = "data")]
|
||||
payload: InvokePayload<'a>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct InvokePayload<'a> {
|
||||
#[serde(rename = "deviceId")]
|
||||
pub device_id: &'a str,
|
||||
pub name: &'a str,
|
||||
pub parameters: &'a [serde_json::Value],
|
||||
}
|
||||
|
||||
// Responses ----------------------------------------------------------
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(tag = "type", rename_all = "lowercase")]
|
||||
pub enum RpcResponse {
|
||||
List { data: Vec<DeviceEntry> },
|
||||
Methods { data: Vec<MethodEntry> },
|
||||
Result { data: serde_json::Value },
|
||||
Error { data: String },
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct DeviceEntry {
|
||||
#[serde(rename = "deviceId")]
|
||||
pub device_id: String,
|
||||
#[serde(default)]
|
||||
pub type_names: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct MethodEntry {
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
#[serde(default)]
|
||||
pub parameters: Vec<MethodParameter>,
|
||||
#[serde(default)]
|
||||
pub return_type: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct MethodParameter {
|
||||
#[serde(default)]
|
||||
pub name: Option<String>,
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
#[serde(default)]
|
||||
pub r#type: Option<String>,
|
||||
}
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
Io(io::Error),
|
||||
Nix(Errno),
|
||||
Json(serde_json::Error),
|
||||
Framing,
|
||||
Protocol(&'static str),
|
||||
}
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error::Io(e)
|
||||
}
|
||||
}
|
||||
impl From<Errno> for Error {
|
||||
fn from(e: Errno) -> Self {
|
||||
Error::Nix(e)
|
||||
}
|
||||
}
|
||||
impl From<serde_json::Error> for Error {
|
||||
fn from(e: serde_json::Error) -> Self {
|
||||
Error::Json(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_raw(fd: BorrowedFd<'_>) -> Result<()> {
|
||||
let mut term = termios::tcgetattr(fd)?;
|
||||
term.local_flags.remove(
|
||||
termios::LocalFlags::ICANON | termios::LocalFlags::ECHO | termios::LocalFlags::ISIG,
|
||||
);
|
||||
term.control_chars[VMIN as usize] = 1;
|
||||
term.control_chars[VTIME as usize] = 0;
|
||||
termios::tcsetattr(fd, termios::SetArg::TCSANOW, &term)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(fd: BorrowedFd<'_>) -> Result<()> {
|
||||
let mut p = [PollFd::new(fd, PollFlags::POLLIN)];
|
||||
let mut tmp = [0u8; 1024];
|
||||
loop {
|
||||
match poll(&mut p, 0u8)? {
|
||||
0 => return Ok(()),
|
||||
_ => {
|
||||
let _ = read(fd, &mut tmp)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_fd(fd: BorrowedFd<'_>, mut buf: &[u8]) -> Result<()> {
|
||||
while !buf.is_empty() {
|
||||
match write(fd, buf) {
|
||||
Ok(0) => return Err(Error::Framing),
|
||||
Ok(n) => buf = &buf[n..],
|
||||
Err(Errno::EINTR) => continue,
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact_fd(fd: BorrowedFd<'_>, mut buf: &mut [u8]) -> Result<()> {
|
||||
while !buf.is_empty() {
|
||||
match read(fd, buf) {
|
||||
Ok(0) => return Err(Error::Framing),
|
||||
Ok(n) => {
|
||||
let tmp = buf;
|
||||
buf = &mut tmp[n..];
|
||||
}
|
||||
Err(Errno::EINTR) | Err(Errno::EAGAIN) => continue,
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_len(fd: BorrowedFd<'_>, len: usize) -> Result<()> {
|
||||
let l = u32::try_from(len).map_err(|_| Error::Framing)?;
|
||||
write_all_fd(fd, &l.to_le_bytes())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_len(fd: BorrowedFd<'_>) -> Result<usize> {
|
||||
let mut b = [0u8; 4];
|
||||
read_exact_fd(fd, &mut b)?;
|
||||
Ok(u32::from_le_bytes(b) as usize)
|
||||
}
|
||||
|
||||
fn write_message(fd: BorrowedFd<'_>, msg: &impl Serialize) -> Result<()> {
|
||||
let payload = serde_json::to_vec(msg)?;
|
||||
write_len(fd, payload.len())?;
|
||||
write_all_fd(fd, &payload)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_message(fd: BorrowedFd<'_>) -> Result<RpcResponse> {
|
||||
let mut p = [PollFd::new(fd, PollFlags::POLLIN)];
|
||||
poll(&mut p, Option::<u8>::None)?; // infinite
|
||||
let len = read_len(fd)?;
|
||||
let mut buf = vec![0u8; len];
|
||||
read_exact_fd(fd, &mut buf)?;
|
||||
Ok(serde_json::from_slice(&buf)?)
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/hvc0")?;
|
||||
let fd = file.as_fd();
|
||||
|
||||
set_raw(fd)?;
|
||||
flush(fd)?;
|
||||
|
||||
write_message(fd, &RpcRequest::List)?;
|
||||
match read_message(fd)? {
|
||||
RpcResponse::List { data } => {
|
||||
println!("devices: {data:#?}");
|
||||
Ok(())
|
||||
}
|
||||
RpcResponse::Error { data: _ } => Err(Error::Protocol("rpc error")),
|
||||
RpcResponse::Methods { .. } => Err(Error::Protocol("unexpected methods response")),
|
||||
RpcResponse::Result { .. } => Err(Error::Protocol("unexpected result response")),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user