2025-06-01 16:04:00 -05:00
|
|
|
|
//! navka-server – bridge HID reports between client (gRPC) and /dev/hidg0
|
|
|
|
|
|
#![forbid(unsafe_code)]
|
2025-06-01 13:31:22 -05:00
|
|
|
|
|
2025-06-01 16:04:00 -05:00
|
|
|
|
use anyhow::Result;
|
|
|
|
|
|
use navka_common::navka::{
|
|
|
|
|
|
hid_report::*, relay_server::Relay, HidReport, RelayServer,
|
|
|
|
|
|
};
|
|
|
|
|
|
use std::{path::Path, sync::Arc};
|
|
|
|
|
|
use tokio::{
|
|
|
|
|
|
fs::OpenOptions,
|
|
|
|
|
|
io::AsyncWriteExt,
|
|
|
|
|
|
signal,
|
|
|
|
|
|
sync::{mpsc, Mutex},
|
|
|
|
|
|
};
|
|
|
|
|
|
use tonic::{transport::Server, Request, Response, Status};
|
2025-06-01 13:31:22 -05:00
|
|
|
|
|
2025-06-01 16:04:00 -05:00
|
|
|
|
/// Implementation of the gRPC service generated by tonic-build.
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
|
pub struct RelaySvc {
|
|
|
|
|
|
/// Shared handle to /dev/hidg0 (protected by a mutex because several
|
|
|
|
|
|
/// gRPC streams may write concurrently).
|
|
|
|
|
|
hidg: Arc<Mutex<tokio::fs::File>>,
|
|
|
|
|
|
}
|
2025-06-01 13:31:22 -05:00
|
|
|
|
|
|
|
|
|
|
#[tonic::async_trait]
|
|
|
|
|
|
impl Relay for RelaySvc {
|
2025-06-01 16:04:00 -05:00
|
|
|
|
type StreamStream = tokio_stream::wrappers::ReceiverStream<Result<HidReport, Status>>;
|
2025-06-01 13:31:22 -05:00
|
|
|
|
|
|
|
|
|
|
async fn stream(
|
|
|
|
|
|
&self,
|
|
|
|
|
|
request: Request<tonic::Streaming<HidReport>>,
|
|
|
|
|
|
) -> Result<Response<Self::StreamStream>, Status> {
|
|
|
|
|
|
let mut inbound = request.into_inner();
|
|
|
|
|
|
|
2025-06-01 16:04:00 -05:00
|
|
|
|
// Channel we’ll use if one day we need to send something back.
|
|
|
|
|
|
let (tx, rx) = mpsc::channel(32);
|
|
|
|
|
|
|
|
|
|
|
|
let hidg = self.hidg.clone();
|
2025-06-01 13:31:22 -05:00
|
|
|
|
tokio::spawn(async move {
|
2025-06-01 16:04:00 -05:00
|
|
|
|
while let Some(report) = inbound.message().await.transpose()? {
|
|
|
|
|
|
// Write raw 8-byte packet to the gadget device
|
|
|
|
|
|
hidg.lock().await.write_all(&report.data).await?;
|
2025-06-01 13:31:22 -05:00
|
|
|
|
}
|
2025-06-01 16:04:00 -05:00
|
|
|
|
Ok::<(), anyhow::Error>(())
|
2025-06-01 13:31:22 -05:00
|
|
|
|
});
|
|
|
|
|
|
|
2025-06-01 16:04:00 -05:00
|
|
|
|
Ok(Response::new(tokio_stream::wrappers::ReceiverStream::new(rx)))
|
2025-06-01 13:31:22 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[tokio::main]
|
2025-06-01 16:04:00 -05:00
|
|
|
|
async fn main() -> Result<()> {
|
|
|
|
|
|
// Open /dev/hidg0 once and share it.
|
|
|
|
|
|
let hid_dev = if Path::new("/dev/hidg0").exists() {
|
|
|
|
|
|
OpenOptions::new().write(true).open("/dev/hidg0").await?
|
|
|
|
|
|
} else {
|
|
|
|
|
|
anyhow::bail!("/dev/hidg0 not found – is navka-gadget running?");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let svc = RelayServer::new(RelaySvc {
|
|
|
|
|
|
hidg: Arc::new(Mutex::new(hid_dev)),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
println!("🛰️ navka-server listening on 0.0.0.0:50051");
|
2025-06-01 13:31:22 -05:00
|
|
|
|
Server::builder()
|
2025-06-01 16:04:00 -05:00
|
|
|
|
.add_service(svc)
|
|
|
|
|
|
.serve_with_shutdown("0.0.0.0:50051".parse()?, async {
|
|
|
|
|
|
signal::ctrl_c().await.ok();
|
|
|
|
|
|
})
|
2025-06-01 13:31:22 -05:00
|
|
|
|
.await?;
|
2025-06-01 16:04:00 -05:00
|
|
|
|
|
2025-06-01 13:31:22 -05:00
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|