//! HID (Human Interface Device) control module //! //! This module provides keyboard and mouse control for remote KVM: //! - USB OTG gadget mode (native Linux USB gadget) //! - CH9329 serial HID controller //! //! Architecture: //! ```text //! Web Client --> WebSocket/DataChannel --> HID Events --> Backend --> Target PC //! | //! [OTG | CH9329] //! ``` pub mod backend; pub mod ch9329; pub mod consumer; pub mod datachannel; pub mod keyboard; pub mod otg; pub mod types; pub mod websocket; pub use backend::{HidBackend, HidBackendStatus, HidBackendType}; pub use keyboard::CanonicalKey; pub use otg::LedState; pub use types::{ ConsumerEvent, KeyEventType, KeyboardEvent, KeyboardModifiers, MouseButton, MouseEvent, MouseEventType, }; /// HID backend information #[derive(Debug, Clone)] pub struct HidInfo { /// Backend name pub name: String, /// Whether backend is initialized pub initialized: bool, /// Whether absolute mouse positioning is supported pub supports_absolute_mouse: bool, /// Screen resolution for absolute mouse pub screen_resolution: Option<(u32, u32)>, } /// Unified HID runtime state used by snapshots and events. #[derive(Debug, Clone, PartialEq, Eq)] pub struct HidRuntimeState { /// Whether a backend is configured and expected to exist. pub available: bool, /// Stable backend key: "otg", "ch9329", "none". pub backend: String, /// Whether the backend is currently initialized and operational. pub initialized: bool, /// Whether the backend is currently online. pub online: bool, /// Whether absolute mouse positioning is supported. pub supports_absolute_mouse: bool, /// Screen resolution for absolute mouse mode. pub screen_resolution: Option<(u32, u32)>, /// Device path associated with the backend, if any. pub device: Option, /// Current user-facing error, if any. pub error: Option, /// Current programmatic error code, if any. pub error_code: Option, } impl HidRuntimeState { fn from_backend_type(backend_type: &HidBackendType) -> Self { Self { available: !matches!(backend_type, HidBackendType::None), backend: backend_type.name_str().to_string(), initialized: false, online: false, supports_absolute_mouse: false, screen_resolution: None, device: device_for_backend_type(backend_type), error: None, error_code: None, } } fn from_backend(backend_type: &HidBackendType, backend: &dyn HidBackend) -> Self { let status = backend.status(); Self { available: !matches!(backend_type, HidBackendType::None), backend: backend_type.name_str().to_string(), initialized: status.initialized, online: status.online, supports_absolute_mouse: backend.supports_absolute_mouse(), screen_resolution: backend.screen_resolution(), device: device_for_backend_type(backend_type), error: status.error, error_code: status.error_code, } } fn with_error( backend_type: &HidBackendType, current: &Self, reason: impl Into, error_code: impl Into, ) -> Self { let mut next = current.clone(); next.available = !matches!(backend_type, HidBackendType::None); next.backend = backend_type.name_str().to_string(); next.initialized = false; next.online = false; next.device = device_for_backend_type(backend_type); next.error = Some(reason.into()); next.error_code = Some(error_code.into()); next } } use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use tokio::sync::RwLock; use tracing::{info, warn}; use crate::error::{AppError, Result}; use crate::events::EventBus; use crate::otg::OtgService; use std::time::Duration; use tokio::sync::mpsc; use tokio::sync::Mutex; use tokio::task::JoinHandle; const HID_EVENT_QUEUE_CAPACITY: usize = 64; const HID_EVENT_SEND_TIMEOUT_MS: u64 = 30; #[derive(Debug)] enum HidEvent { Keyboard(KeyboardEvent), Mouse(MouseEvent), Consumer(ConsumerEvent), Reset, } /// HID controller managing keyboard and mouse input pub struct HidController { /// OTG Service reference (only used when backend is OTG) otg_service: Option>, /// Active backend backend: Arc>>>, /// Backend type (mutable for reload) backend_type: Arc>, /// Event bus for broadcasting state changes (optional) events: Arc>>>, /// Unified HID runtime state. runtime_state: Arc>, /// HID event queue sender (non-blocking) hid_tx: mpsc::Sender, /// HID event queue receiver (moved into worker on first start) hid_rx: Mutex>>, /// Coalesced mouse move (latest) pending_move: Arc>>, /// Pending move flag (fast path) pending_move_flag: Arc, /// Worker task handle hid_worker: Mutex>>, /// Backend initialization fast flag backend_available: Arc, } impl HidController { /// Create a new HID controller with specified backend /// /// For OTG backend, otg_service should be provided to support hot-reload pub fn new(backend_type: HidBackendType, otg_service: Option>) -> Self { let (hid_tx, hid_rx) = mpsc::channel(HID_EVENT_QUEUE_CAPACITY); Self { otg_service, backend: Arc::new(RwLock::new(None)), backend_type: Arc::new(RwLock::new(backend_type.clone())), events: Arc::new(tokio::sync::RwLock::new(None)), runtime_state: Arc::new(RwLock::new(HidRuntimeState::from_backend_type( &backend_type, ))), hid_tx, hid_rx: Mutex::new(Some(hid_rx)), pending_move: Arc::new(parking_lot::Mutex::new(None)), pending_move_flag: Arc::new(AtomicBool::new(false)), hid_worker: Mutex::new(None), backend_available: Arc::new(AtomicBool::new(false)), } } /// Set event bus for broadcasting state changes pub async fn set_event_bus(&self, events: Arc) { *self.events.write().await = Some(events); } /// Initialize the HID backend pub async fn init(&self) -> Result<()> { let backend_type = self.backend_type.read().await.clone(); let backend: Arc = match backend_type { HidBackendType::Otg => { // Request HID functions from OtgService let otg_service = self .otg_service .as_ref() .ok_or_else(|| AppError::Internal("OtgService not available".into()))?; info!("Requesting HID functions from OtgService"); let handles = otg_service.enable_hid().await?; // Create OtgBackend from handles (no longer manages gadget itself) info!("Creating OTG HID backend from device paths"); Arc::new(otg::OtgBackend::from_handles(handles)?) } HidBackendType::Ch9329 { ref port, baud_rate, } => { info!( "Initializing CH9329 HID backend on {} @ {} baud", port, baud_rate ); Arc::new(ch9329::Ch9329Backend::with_baud_rate(port, baud_rate)?) } HidBackendType::None => { warn!("HID backend disabled"); return Ok(()); } }; if let Err(e) = backend.init().await { self.backend_available.store(false, Ordering::Release); let error_state = { let backend_type = self.backend_type.read().await.clone(); let current = self.runtime_state.read().await.clone(); HidRuntimeState::with_error( &backend_type, ¤t, format!("Failed to initialize HID backend: {}", e), "init_failed", ) }; self.apply_runtime_state(error_state).await; return Err(e); } *self.backend.write().await = Some(backend); self.sync_runtime_state_from_backend().await; // Start HID event worker (once) self.start_event_worker().await; info!("HID backend initialized: {:?}", backend_type); Ok(()) } /// Shutdown the HID backend and release resources pub async fn shutdown(&self) -> Result<()> { info!("Shutting down HID controller"); // Close the backend if let Some(backend) = self.backend.write().await.take() { if let Err(e) = backend.shutdown().await { warn!("Error shutting down HID backend: {}", e); } } self.backend_available.store(false, Ordering::Release); let backend_type = self.backend_type.read().await.clone(); let mut shutdown_state = HidRuntimeState::from_backend_type(&backend_type); if matches!(backend_type, HidBackendType::None) { shutdown_state.available = false; } else { shutdown_state.error = Some("HID backend stopped".to_string()); shutdown_state.error_code = Some("shutdown".to_string()); } self.apply_runtime_state(shutdown_state).await; // If OTG backend, notify OtgService to disable HID if matches!(backend_type, HidBackendType::Otg) { if let Some(ref otg_service) = self.otg_service { info!("Disabling HID functions in OtgService"); otg_service.disable_hid().await?; } } info!("HID controller shutdown complete"); Ok(()) } /// Send keyboard event pub async fn send_keyboard(&self, event: KeyboardEvent) -> Result<()> { if !self.backend_available.load(Ordering::Acquire) { return Err(AppError::BadRequest( "HID backend not available".to_string(), )); } self.enqueue_event(HidEvent::Keyboard(event)).await } /// Send mouse event pub async fn send_mouse(&self, event: MouseEvent) -> Result<()> { if !self.backend_available.load(Ordering::Acquire) { return Err(AppError::BadRequest( "HID backend not available".to_string(), )); } if matches!( event.event_type, MouseEventType::Move | MouseEventType::MoveAbs ) { // Best-effort: drop/merge move events if queue is full self.enqueue_mouse_move(event) } else { self.enqueue_event(HidEvent::Mouse(event)).await } } /// Send consumer control event (multimedia keys) pub async fn send_consumer(&self, event: ConsumerEvent) -> Result<()> { if !self.backend_available.load(Ordering::Acquire) { return Err(AppError::BadRequest( "HID backend not available".to_string(), )); } self.enqueue_event(HidEvent::Consumer(event)).await } /// Reset all keys (release all pressed keys) pub async fn reset(&self) -> Result<()> { if !self.backend_available.load(Ordering::Acquire) { return Ok(()); } // Reset is important but best-effort; enqueue to avoid blocking self.enqueue_event(HidEvent::Reset).await } /// Check if backend is available pub async fn is_available(&self) -> bool { self.backend_available.load(Ordering::Acquire) } /// Get backend type pub async fn backend_type(&self) -> HidBackendType { self.backend_type.read().await.clone() } /// Get backend info pub async fn info(&self) -> Option { let state = self.runtime_state.read().await.clone(); if !state.available { return None; } Some(HidInfo { name: state.backend, initialized: state.initialized, supports_absolute_mouse: state.supports_absolute_mouse, screen_resolution: state.screen_resolution, }) } /// Get current HID runtime state snapshot. pub async fn snapshot(&self) -> HidRuntimeState { self.runtime_state.read().await.clone() } /// Reload the HID backend with new type pub async fn reload(&self, new_backend_type: HidBackendType) -> Result<()> { info!("Reloading HID backend: {:?}", new_backend_type); self.backend_available.store(false, Ordering::Release); // Shutdown existing backend first if let Some(backend) = self.backend.write().await.take() { if let Err(e) = backend.shutdown().await { warn!("Error shutting down old HID backend: {}", e); } } // Create and initialize new backend let new_backend: Option> = match new_backend_type { HidBackendType::Otg => { info!("Initializing OTG HID backend"); // Get OtgService reference let otg_service = match self.otg_service.as_ref() { Some(svc) => svc, None => { warn!("OTG backend requires OtgService, but it's not available"); return Err(AppError::Config( "OTG backend not available (OtgService missing)".to_string(), )); } }; // Request HID functions from OtgService match otg_service.enable_hid().await { Ok(handles) => { // Create OtgBackend from handles match otg::OtgBackend::from_handles(handles) { Ok(backend) => { let backend = Arc::new(backend); match backend.init().await { Ok(_) => { info!("OTG backend initialized successfully"); Some(backend) } Err(e) => { warn!("Failed to initialize OTG backend: {}", e); // Cleanup: disable HID in OtgService if let Err(e2) = otg_service.disable_hid().await { warn!( "Failed to cleanup HID after init failure: {}", e2 ); } None } } } Err(e) => { warn!("Failed to create OTG backend: {}", e); // Cleanup: disable HID in OtgService if let Err(e2) = otg_service.disable_hid().await { warn!("Failed to cleanup HID after creation failure: {}", e2); } None } } } Err(e) => { warn!("Failed to enable HID in OtgService: {}", e); None } } } HidBackendType::Ch9329 { ref port, baud_rate, } => { info!( "Initializing CH9329 HID backend on {} @ {} baud", port, baud_rate ); match ch9329::Ch9329Backend::with_baud_rate(port, baud_rate) { Ok(b) => { let backend = Arc::new(b); match backend.init().await { Ok(_) => Some(backend), Err(e) => { warn!("Failed to initialize CH9329 backend: {}", e); None } } } Err(e) => { warn!("Failed to create CH9329 backend: {}", e); None } } } HidBackendType::None => { warn!("HID backend disabled"); None } }; *self.backend.write().await = new_backend; if matches!(new_backend_type, HidBackendType::None) { *self.backend_type.write().await = HidBackendType::None; self.apply_runtime_state(HidRuntimeState::from_backend_type(&HidBackendType::None)) .await; return Ok(()); } if self.backend.read().await.is_some() { info!("HID backend reloaded successfully: {:?}", new_backend_type); self.start_event_worker().await; // Update backend_type on success *self.backend_type.write().await = new_backend_type.clone(); self.sync_runtime_state_from_backend().await; Ok(()) } else { warn!("HID backend reload resulted in no active backend"); self.backend_available.store(false, Ordering::Release); // Update backend_type even on failure (to reflect the attempted change) *self.backend_type.write().await = new_backend_type.clone(); let current = self.runtime_state.read().await.clone(); let error_state = HidRuntimeState::with_error( &new_backend_type, ¤t, "Failed to initialize HID backend", "init_failed", ); self.apply_runtime_state(error_state).await; Err(AppError::Internal( "Failed to reload HID backend".to_string(), )) } } async fn apply_runtime_state(&self, next: HidRuntimeState) { apply_runtime_state(&self.runtime_state, &self.events, next).await; } async fn sync_runtime_state_from_backend(&self) { let backend_opt = self.backend.read().await.clone(); let backend_type = self.backend_type.read().await.clone(); let next = match backend_opt.as_ref() { Some(backend) => HidRuntimeState::from_backend(&backend_type, backend.as_ref()), None => HidRuntimeState::from_backend_type(&backend_type), }; self.backend_available .store(next.initialized, Ordering::Release); self.apply_runtime_state(next).await; } async fn start_event_worker(&self) { let mut worker_guard = self.hid_worker.lock().await; if worker_guard.is_some() { return; } let mut rx_guard = self.hid_rx.lock().await; let rx = match rx_guard.take() { Some(rx) => rx, None => return, }; let backend = self.backend.clone(); let backend_type = self.backend_type.clone(); let runtime_state = self.runtime_state.clone(); let events = self.events.clone(); let backend_available = self.backend_available.clone(); let pending_move = self.pending_move.clone(); let pending_move_flag = self.pending_move_flag.clone(); let handle = tokio::spawn(async move { let mut rx = rx; loop { let event = match rx.recv().await { Some(ev) => ev, None => break, }; process_hid_event( event, &backend, &backend_type, &runtime_state, &events, backend_available.as_ref(), ) .await; // After each event, flush latest move if pending if pending_move_flag.swap(false, Ordering::AcqRel) { let move_event = { pending_move.lock().take() }; if let Some(move_event) = move_event { process_hid_event( HidEvent::Mouse(move_event), &backend, &backend_type, &runtime_state, &events, backend_available.as_ref(), ) .await; } } } }); *worker_guard = Some(handle); } fn enqueue_mouse_move(&self, event: MouseEvent) -> Result<()> { match self.hid_tx.try_send(HidEvent::Mouse(event.clone())) { Ok(_) => Ok(()), Err(mpsc::error::TrySendError::Full(_)) => { *self.pending_move.lock() = Some(event); self.pending_move_flag.store(true, Ordering::Release); Ok(()) } Err(mpsc::error::TrySendError::Closed(_)) => { Err(AppError::BadRequest("HID event queue closed".to_string())) } } } async fn enqueue_event(&self, event: HidEvent) -> Result<()> { match self.hid_tx.try_send(event) { Ok(_) => Ok(()), Err(mpsc::error::TrySendError::Full(ev)) => { // For non-move events, wait briefly to avoid dropping critical input let tx = self.hid_tx.clone(); let send_result = tokio::time::timeout( Duration::from_millis(HID_EVENT_SEND_TIMEOUT_MS), tx.send(ev), ) .await; if send_result.is_ok() { Ok(()) } else { warn!("HID event queue full, dropping event"); Ok(()) } } Err(mpsc::error::TrySendError::Closed(_)) => { Err(AppError::BadRequest("HID event queue closed".to_string())) } } } } async fn process_hid_event( event: HidEvent, backend: &Arc>>>, backend_type: &Arc>, runtime_state: &Arc>, events: &Arc>>>, backend_available: &AtomicBool, ) { let backend_opt = backend.read().await.clone(); let backend = match backend_opt { Some(b) => b, None => return, }; let backend_for_send = backend.clone(); let result = tokio::task::spawn_blocking(move || { futures::executor::block_on(async move { match event { HidEvent::Keyboard(ev) => backend_for_send.send_keyboard(ev).await, HidEvent::Mouse(ev) => backend_for_send.send_mouse(ev).await, HidEvent::Consumer(ev) => backend_for_send.send_consumer(ev).await, HidEvent::Reset => backend_for_send.reset().await, } }) }) .await; let result = match result { Ok(r) => r, Err(_) => return, }; match result { Ok(_) => {} Err(e) => { warn!("HID event processing failed: {}", e); } } let backend_kind = backend_type.read().await.clone(); let next = HidRuntimeState::from_backend(&backend_kind, backend.as_ref()); backend_available.store(next.initialized, Ordering::Release); apply_runtime_state(runtime_state, events, next).await; } impl Default for HidController { fn default() -> Self { Self::new(HidBackendType::None, None) } } fn device_for_backend_type(backend_type: &HidBackendType) -> Option { match backend_type { HidBackendType::Ch9329 { port, .. } => Some(port.clone()), _ => None, } } async fn apply_runtime_state( runtime_state: &Arc>, events: &Arc>>>, next: HidRuntimeState, ) { let changed = { let mut guard = runtime_state.write().await; if *guard == next { false } else { *guard = next.clone(); true } }; if !changed { return; } if let Some(events) = events.read().await.as_ref() { events.mark_device_info_dirty(); } }