mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-10-28 08:20:49 +08:00
CH9329 负责键鼠,OTG 提供虚拟媒体、USB 网络和音频;分别配置设备、报告运行状态,并在 OTG 配置变更和回滚时保留未变化的 CH9329 实例。 补齐双设备校验、串口冲突检查、初始化引导和功能入口,修复组合模式下鼠标兼容选项缺失。 验证:独立功能版本的 591 项 Rust 库测试、前端生产构建、3 项引导逻辑测试及 2 组浏览器回归通过。未进行目标 KVM 硬件验证。
170 lines
5.2 KiB
Rust
170 lines
5.2 KiB
Rust
use super::*;
|
|
use crate::error::AppError;
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct Ch9329DescriptorQuery {
|
|
pub port: Option<String>,
|
|
pub baud_rate: Option<u32>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub struct HidStatus {
|
|
pub available: bool,
|
|
pub backend: String,
|
|
pub configured_backend: String,
|
|
#[cfg(unix)]
|
|
pub otg: crate::otg::service::OtgRuntimeStatus,
|
|
pub initialized: bool,
|
|
pub online: bool,
|
|
pub supports_absolute_mouse: bool,
|
|
pub keyboard_leds_enabled: bool,
|
|
pub led_state: crate::hid::LedState,
|
|
pub screen_resolution: Option<(u32, u32)>,
|
|
pub device: Option<String>,
|
|
pub error: Option<String>,
|
|
pub error_code: Option<String>,
|
|
}
|
|
|
|
/// OTG self-check status for troubleshooting USB gadget issues
|
|
#[cfg(unix)]
|
|
pub async fn hid_otg_self_check(
|
|
State(state): State<Arc<AppState>>,
|
|
) -> Json<crate::otg::self_check::OtgSelfCheckResponse> {
|
|
let config = state.config.get();
|
|
Json(crate::otg::self_check::run(config.as_ref()))
|
|
}
|
|
|
|
/// Get HID status
|
|
pub async fn hid_status(State(state): State<Arc<AppState>>) -> Json<HidStatus> {
|
|
let hid = state.hid.snapshot().await;
|
|
let config = state.config.get();
|
|
Json(HidStatus {
|
|
available: hid.available,
|
|
backend: hid.backend,
|
|
configured_backend: config.hid.backend.as_str().to_string(),
|
|
#[cfg(unix)]
|
|
otg: state
|
|
.otg_service
|
|
.runtime_status(
|
|
config.hid.backend.uses_otg_hid()
|
|
|| config.msd.enabled
|
|
|| config.otg_network.enabled
|
|
|| config.uac.enabled,
|
|
)
|
|
.await,
|
|
initialized: hid.initialized,
|
|
online: hid.online,
|
|
supports_absolute_mouse: hid.supports_absolute_mouse,
|
|
keyboard_leds_enabled: hid.keyboard_leds_enabled,
|
|
led_state: hid.led_state,
|
|
screen_resolution: hid.screen_resolution,
|
|
device: hid.device,
|
|
error: hid.error,
|
|
error_code: hid.error_code,
|
|
})
|
|
}
|
|
|
|
/// Reset HID state
|
|
pub async fn hid_reset(State(state): State<Arc<AppState>>) -> Result<Json<LoginResponse>> {
|
|
state.hid.reset().await?;
|
|
|
|
Ok(Json(LoginResponse {
|
|
success: true,
|
|
message: Some("HID state reset".to_string()),
|
|
}))
|
|
}
|
|
|
|
/// Read the CH9329 USB descriptor, falling back to the saved config when SET is not low.
|
|
pub async fn hid_ch9329_descriptor(
|
|
State(state): State<Arc<AppState>>,
|
|
Query(query): Query<Ch9329DescriptorQuery>,
|
|
) -> Result<Json<crate::config::Ch9329DescriptorState>> {
|
|
let config = state.config.get();
|
|
let hid = &config.hid;
|
|
let port = query.port.as_deref().filter(|port| !port.trim().is_empty());
|
|
let baud_rate = query.baud_rate;
|
|
|
|
let descriptor_result = match (port, baud_rate) {
|
|
(Some(port), Some(baud_rate))
|
|
if port != hid.ch9329_port || baud_rate != hid.ch9329_baudrate =>
|
|
{
|
|
crate::hid::ch9329::Ch9329Backend::read_device_descriptor(port, baud_rate)
|
|
}
|
|
_ => state.hid.read_ch9329_descriptor().await,
|
|
};
|
|
|
|
let descriptor = match descriptor_result {
|
|
Ok(descriptor) => descriptor,
|
|
Err(err) if is_ch9329_config_mode_unavailable(&err) => cached_ch9329_descriptor(hid),
|
|
Err(err) => return Err(err),
|
|
};
|
|
Ok(Json(descriptor))
|
|
}
|
|
|
|
fn is_ch9329_config_mode_unavailable(err: &AppError) -> bool {
|
|
matches!(
|
|
err,
|
|
AppError::HidError {
|
|
backend,
|
|
error_code,
|
|
..
|
|
} if backend == "ch9329" && error_code == "invalid_response"
|
|
)
|
|
}
|
|
|
|
fn cached_ch9329_descriptor(
|
|
hid: &crate::config::HidConfig,
|
|
) -> crate::config::Ch9329DescriptorState {
|
|
let descriptor = hid.ch9329_descriptor.clone();
|
|
crate::config::Ch9329DescriptorState {
|
|
manufacturer_enabled: !descriptor.manufacturer.is_empty(),
|
|
product_enabled: !descriptor.product.is_empty(),
|
|
serial_enabled: descriptor
|
|
.serial_number
|
|
.as_ref()
|
|
.is_some_and(|value| !value.is_empty()),
|
|
config_mode_available: false,
|
|
descriptor,
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct BluetoothAction {
|
|
pub action: String,
|
|
pub seconds: Option<u32>,
|
|
}
|
|
pub async fn hid_bluetooth_status(
|
|
State(state): State<Arc<AppState>>,
|
|
) -> Result<Json<serde_json::Value>> {
|
|
Ok(Json(state.hid.bluetooth_status().await?))
|
|
}
|
|
pub async fn hid_bluetooth_action(
|
|
State(state): State<Arc<AppState>>,
|
|
Json(req): Json<BluetoothAction>,
|
|
) -> Result<Json<serde_json::Value>> {
|
|
let _guard = crate::runtime::try_apply_lock(&state.config_apply_locks.otg, "bluetooth")?;
|
|
state
|
|
.hid
|
|
.bluetooth_action(&req.action, req.seconds.unwrap_or(120))
|
|
.await?;
|
|
Ok(Json(serde_json::json!({"success": true})))
|
|
}
|
|
|
|
pub async fn hid_bluetooth_adapters() -> Result<Json<serde_json::Value>> {
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
Ok(Json(
|
|
serde_json::to_value(
|
|
one_kvm_bluetooth_hid::adapters()
|
|
.await
|
|
.map_err(AppError::ServiceUnavailable)?,
|
|
)
|
|
.map_err(|e| AppError::Internal(e.to_string()))?,
|
|
))
|
|
}
|
|
#[cfg(not(target_os = "linux"))]
|
|
{
|
|
Err(AppError::BadRequest("Bluetooth HID requires Linux".into()))
|
|
}
|
|
}
|