Woking
This commit is contained in:
32
Cargo.lock
generated
32
Cargo.lock
generated
@@ -2,24 +2,6 @@
|
||||
# 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"
|
||||
@@ -38,23 +20,11 @@ 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",
|
||||
"libc",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
@@ -12,4 +12,4 @@ codegen-units = 1
|
||||
[dependencies]
|
||||
serde = { version = "1", features = ["derive"]}
|
||||
serde_json = "1"
|
||||
nix = { version = "0.30", features = ["term", "poll"]}
|
||||
libc = "0.2"
|
||||
|
||||
223
src/main.rs
223
src/main.rs
@@ -1,12 +1,8 @@
|
||||
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};
|
||||
use std::io;
|
||||
use std::os::fd::{AsRawFd, RawFd};
|
||||
use std::result;
|
||||
|
||||
// Requests -----------------------------------------------------------
|
||||
#[derive(Serialize, Debug)]
|
||||
@@ -76,7 +72,6 @@ type Result<T> = result::Result<T, Error>;
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
Io(io::Error),
|
||||
Nix(Errno),
|
||||
Json(serde_json::Error),
|
||||
Framing,
|
||||
Protocol(&'static str),
|
||||
@@ -86,109 +81,193 @@ impl From<io::Error> for Error {
|
||||
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)?;
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::Io(e) => write!(f, "io error: {e}"),
|
||||
Error::Json(e) => write!(f, "json error: {e}"),
|
||||
Error::Framing => f.write_str("invalid message framing"),
|
||||
Error::Protocol(msg) => write!(f, "protocol error: {msg}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
const DELIM: u8 = 0;
|
||||
|
||||
fn set_raw(fd: RawFd) -> Result<()> {
|
||||
unsafe {
|
||||
let mut term = std::mem::zeroed::<libc::termios>();
|
||||
if libc::tcgetattr(fd, &mut term) != 0 {
|
||||
return Err(Error::Io(io::Error::last_os_error()));
|
||||
}
|
||||
libc::cfmakeraw(&mut term);
|
||||
term.c_cc[libc::VMIN] = 1;
|
||||
term.c_cc[libc::VTIME] = 0;
|
||||
if libc::tcsetattr(fd, libc::TCSANOW, &term) != 0 {
|
||||
return Err(Error::Io(io::Error::last_os_error()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(fd: BorrowedFd<'_>) -> Result<()> {
|
||||
let mut p = [PollFd::new(fd, PollFlags::POLLIN)];
|
||||
let mut tmp = [0u8; 1024];
|
||||
fn flush(fd: RawFd) -> Result<()> {
|
||||
println!("Flushing pending input…");
|
||||
let mut buf = [0u8; 1024];
|
||||
let mut total = 0usize;
|
||||
loop {
|
||||
match poll(&mut p, 0u8)? {
|
||||
0 => return Ok(()),
|
||||
_ => {
|
||||
let _ = read(fd, &mut tmp)?;
|
||||
let ready = loop {
|
||||
let mut pfd = libc::pollfd {
|
||||
fd,
|
||||
events: libc::POLLIN,
|
||||
revents: 0,
|
||||
};
|
||||
let res = unsafe { libc::poll(&mut pfd, 1, 0) };
|
||||
if res < 0 {
|
||||
let err = io::Error::last_os_error();
|
||||
if err.kind() == io::ErrorKind::Interrupted {
|
||||
continue;
|
||||
}
|
||||
return Err(Error::Io(err));
|
||||
}
|
||||
break res;
|
||||
};
|
||||
|
||||
if ready == 0 {
|
||||
if total == 0 {
|
||||
println!("Flush complete; nothing pending.");
|
||||
} else {
|
||||
println!("Flush complete; drained {total} bytes.");
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
loop {
|
||||
let read = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) };
|
||||
if read < 0 {
|
||||
let err = io::Error::last_os_error();
|
||||
match err.kind() {
|
||||
io::ErrorKind::Interrupted => continue,
|
||||
io::ErrorKind::WouldBlock => break,
|
||||
_ => return Err(Error::Io(err)),
|
||||
}
|
||||
} else if read == 0 {
|
||||
break;
|
||||
} else {
|
||||
total += read as usize;
|
||||
if read as usize != buf.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_fd(fd: BorrowedFd<'_>, mut buf: &[u8]) -> Result<()> {
|
||||
fn write_all_fd(fd: RawFd, 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..];
|
||||
let written = unsafe { libc::write(fd, buf.as_ptr().cast(), buf.len()) };
|
||||
if written < 0 {
|
||||
let err = io::Error::last_os_error();
|
||||
match err.kind() {
|
||||
io::ErrorKind::Interrupted => continue,
|
||||
io::ErrorKind::WouldBlock => continue,
|
||||
_ => return Err(Error::Io(err)),
|
||||
}
|
||||
Err(Errno::EINTR) | Err(Errno::EAGAIN) => continue,
|
||||
Err(e) => return Err(e.into()),
|
||||
} else if written == 0 {
|
||||
return Err(Error::Framing);
|
||||
} else {
|
||||
buf = &buf[written as usize..];
|
||||
}
|
||||
}
|
||||
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<()> {
|
||||
fn write_message(fd: RawFd, msg: &impl Serialize) -> Result<()> {
|
||||
let payload = serde_json::to_vec(msg)?;
|
||||
write_len(fd, payload.len())?;
|
||||
println!("Sending message: {}", String::from_utf8_lossy(&payload));
|
||||
write_all_fd(fd, &[DELIM])?;
|
||||
write_all_fd(fd, &payload)?;
|
||||
write_all_fd(fd, &[DELIM])?;
|
||||
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 wait_for_readable(fd: RawFd) -> Result<()> {
|
||||
loop {
|
||||
let mut pfd = libc::pollfd {
|
||||
fd,
|
||||
events: libc::POLLIN,
|
||||
revents: 0,
|
||||
};
|
||||
let res = unsafe { libc::poll(&mut pfd, 1, -1) };
|
||||
if res < 0 {
|
||||
let err = io::Error::last_os_error();
|
||||
if err.kind() == io::ErrorKind::Interrupted {
|
||||
continue;
|
||||
}
|
||||
return Err(Error::Io(err));
|
||||
}
|
||||
if res > 0 {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_message(fd: RawFd) -> Result<RpcResponse> {
|
||||
println!("Waiting for response…");
|
||||
wait_for_readable(fd)?;
|
||||
let mut buf = Vec::new();
|
||||
let mut byte = [0u8; 1];
|
||||
loop {
|
||||
let read = unsafe { libc::read(fd, byte.as_mut_ptr().cast(), 1) };
|
||||
if read < 0 {
|
||||
let err = io::Error::last_os_error();
|
||||
match err.kind() {
|
||||
io::ErrorKind::Interrupted => continue,
|
||||
io::ErrorKind::WouldBlock => {
|
||||
wait_for_readable(fd)?;
|
||||
continue;
|
||||
}
|
||||
_ => return Err(Error::Io(err)),
|
||||
}
|
||||
} else if read == 0 {
|
||||
return Err(Error::Framing);
|
||||
} else {
|
||||
let value = byte[0];
|
||||
if value == DELIM {
|
||||
if buf.is_empty() {
|
||||
continue;
|
||||
}
|
||||
println!("Received message: {}", String::from_utf8_lossy(&buf));
|
||||
return Ok(serde_json::from_slice(&buf)?);
|
||||
} else {
|
||||
buf.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
println!("Opening /dev/hvc0…");
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/hvc0")?;
|
||||
let fd = file.as_fd();
|
||||
let fd = file.as_raw_fd();
|
||||
|
||||
println!("Configuring raw console mode…");
|
||||
set_raw(fd)?;
|
||||
println!("Console is now in raw mode.");
|
||||
flush(fd)?;
|
||||
|
||||
println!("Requesting device list…");
|
||||
write_message(fd, &RpcRequest::List)?;
|
||||
match read_message(fd)? {
|
||||
RpcResponse::List { data } => {
|
||||
|
||||
Reference in New Issue
Block a user