//! navka-server — receive HidReport and write to /dev/hidg0 #![forbid(unsafe_code)] use std::{pin::Pin, sync::Arc}; use tokio::{fs::OpenOptions, io::AsyncWriteExt}; use tokio_stream::{wrappers::ReceiverStream, Stream, StreamExt}; use tonic::{transport::Server, Request, Response, Status}; use tracing::{info, error}; use navka_common::navka::{ relay_server::{Relay, RelayServer}, HidReport, }; struct Handler { /// shared async handle to /dev/hidg0 hid: Arc>, } #[tonic::async_trait] impl Relay for Handler { type StreamStream = Pin> + Send + 'static>>; async fn stream( &self, request: Request>, ) -> Result, Status> { let mut in_stream = request.into_inner(); let hid = self.hid.clone(); let (tx, rx) = tokio::sync::mpsc::channel(32); tokio::spawn(async move { while let Some(msg) = in_stream.next().await.transpose()? { let data = msg.data.get(..8).ok_or_else(|| Status::invalid_argument("short"))?; { let mut f = hid.lock().await; if let Err(e) = f.write_all(data).await { error!("USB write failed: {e}"); return Err(Status::internal(e.to_string())); } f.flush().await.ok(); } info!(bytes=?data, len=data.len(), "HID report received"); let _ = tx.send(Ok(msg)).await; } Ok::<_, Status>(()) }); Ok(Response::new(Box::pin(ReceiverStream::new(rx)))) } } #[tokio::main] async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); let file = OpenOptions::new() .write(true) .read(true) .custom_flags(libc::O_NONBLOCK) .open("/dev/hidg0") .await?; let hid = Arc::new(tokio::sync::Mutex::new(file)); let handler = Handler { hid }; println!("🌐 navka-server listening on 0.0.0.0:50051"); Server::builder() .add_service(RelayServer::new(handler)) .serve(([0, 0, 0, 0], 50051).into()) .await?; Ok(()) }