mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-28 08:31:52 +08:00
- 后端:切换事务+transition_id,/stream/mode 返回 switching/transition_id 与实际 codec - 事件:新增 mode_switching/mode_ready,config/webrtc_ready/mode_changed 关联事务 - 编码/格式:扩展 NV21/NV16/NV24/RGB/BGR 输入与转换链路,RKMPP direct input 优化 - 前端:useVideoSession 统一切换,失败回退真实切回 MJPEG,菜单格式同步修复 - 清理:useVideoStream 降级为 MJPEG-only
250 lines
7.3 KiB
Rust
250 lines
7.3 KiB
Rust
//! HID Function implementation for USB Gadget
|
|
|
|
use std::path::{Path, PathBuf};
|
|
use tracing::debug;
|
|
|
|
use super::configfs::{
|
|
create_dir, create_symlink, remove_dir, remove_file, write_bytes, write_file,
|
|
};
|
|
use super::function::{FunctionMeta, GadgetFunction};
|
|
use super::report_desc::{CONSUMER_CONTROL, KEYBOARD, MOUSE_ABSOLUTE, MOUSE_RELATIVE};
|
|
use crate::error::Result;
|
|
|
|
/// HID function type
|
|
#[derive(Debug, Clone)]
|
|
pub enum HidFunctionType {
|
|
/// Keyboard (no LED feedback)
|
|
/// Uses 1 endpoint: IN
|
|
Keyboard,
|
|
/// Relative mouse (traditional mouse movement)
|
|
/// Uses 1 endpoint: IN
|
|
MouseRelative,
|
|
/// Absolute mouse (touchscreen-like positioning)
|
|
/// Uses 1 endpoint: IN
|
|
MouseAbsolute,
|
|
/// Consumer control (multimedia keys)
|
|
/// Uses 1 endpoint: IN
|
|
ConsumerControl,
|
|
}
|
|
|
|
impl HidFunctionType {
|
|
/// Get endpoints required for this function type
|
|
pub fn endpoints(&self) -> u8 {
|
|
match self {
|
|
HidFunctionType::Keyboard => 1,
|
|
HidFunctionType::MouseRelative => 1,
|
|
HidFunctionType::MouseAbsolute => 1,
|
|
HidFunctionType::ConsumerControl => 1,
|
|
}
|
|
}
|
|
|
|
/// Get HID protocol
|
|
pub fn protocol(&self) -> u8 {
|
|
match self {
|
|
HidFunctionType::Keyboard => 1, // Keyboard
|
|
HidFunctionType::MouseRelative => 2, // Mouse
|
|
HidFunctionType::MouseAbsolute => 2, // Mouse
|
|
HidFunctionType::ConsumerControl => 0, // None
|
|
}
|
|
}
|
|
|
|
/// Get HID subclass
|
|
pub fn subclass(&self) -> u8 {
|
|
match self {
|
|
HidFunctionType::Keyboard => 1, // Boot interface
|
|
HidFunctionType::MouseRelative => 1, // Boot interface
|
|
HidFunctionType::MouseAbsolute => 0, // No boot interface
|
|
HidFunctionType::ConsumerControl => 0, // No boot interface
|
|
}
|
|
}
|
|
|
|
/// Get report length in bytes
|
|
pub fn report_length(&self) -> u8 {
|
|
match self {
|
|
HidFunctionType::Keyboard => 8,
|
|
HidFunctionType::MouseRelative => 4,
|
|
HidFunctionType::MouseAbsolute => 6,
|
|
HidFunctionType::ConsumerControl => 2,
|
|
}
|
|
}
|
|
|
|
/// Get report descriptor
|
|
pub fn report_desc(&self) -> &'static [u8] {
|
|
match self {
|
|
HidFunctionType::Keyboard => KEYBOARD,
|
|
HidFunctionType::MouseRelative => MOUSE_RELATIVE,
|
|
HidFunctionType::MouseAbsolute => MOUSE_ABSOLUTE,
|
|
HidFunctionType::ConsumerControl => CONSUMER_CONTROL,
|
|
}
|
|
}
|
|
|
|
/// Get description
|
|
pub fn description(&self) -> &'static str {
|
|
match self {
|
|
HidFunctionType::Keyboard => "Keyboard",
|
|
HidFunctionType::MouseRelative => "Relative Mouse",
|
|
HidFunctionType::MouseAbsolute => "Absolute Mouse",
|
|
HidFunctionType::ConsumerControl => "Consumer Control",
|
|
}
|
|
}
|
|
}
|
|
|
|
/// HID Function for USB Gadget
|
|
#[derive(Debug, Clone)]
|
|
pub struct HidFunction {
|
|
/// Instance number (usb0, usb1, ...)
|
|
instance: u8,
|
|
/// Function type
|
|
func_type: HidFunctionType,
|
|
/// Cached function name (avoids repeated allocation)
|
|
name: String,
|
|
}
|
|
|
|
impl HidFunction {
|
|
/// Create a keyboard function
|
|
pub fn keyboard(instance: u8) -> Self {
|
|
Self {
|
|
instance,
|
|
func_type: HidFunctionType::Keyboard,
|
|
name: format!("hid.usb{}", instance),
|
|
}
|
|
}
|
|
|
|
/// Create a relative mouse function
|
|
pub fn mouse_relative(instance: u8) -> Self {
|
|
Self {
|
|
instance,
|
|
func_type: HidFunctionType::MouseRelative,
|
|
name: format!("hid.usb{}", instance),
|
|
}
|
|
}
|
|
|
|
/// Create an absolute mouse function
|
|
pub fn mouse_absolute(instance: u8) -> Self {
|
|
Self {
|
|
instance,
|
|
func_type: HidFunctionType::MouseAbsolute,
|
|
name: format!("hid.usb{}", instance),
|
|
}
|
|
}
|
|
|
|
/// Create a consumer control function
|
|
pub fn consumer_control(instance: u8) -> Self {
|
|
Self {
|
|
instance,
|
|
func_type: HidFunctionType::ConsumerControl,
|
|
name: format!("hid.usb{}", instance),
|
|
}
|
|
}
|
|
|
|
/// Get function path in gadget
|
|
fn function_path(&self, gadget_path: &Path) -> PathBuf {
|
|
gadget_path.join("functions").join(self.name())
|
|
}
|
|
|
|
/// Get expected device path (e.g., /dev/hidg0)
|
|
pub fn device_path(&self) -> PathBuf {
|
|
PathBuf::from(format!("/dev/hidg{}", self.instance))
|
|
}
|
|
}
|
|
|
|
impl GadgetFunction for HidFunction {
|
|
fn name(&self) -> &str {
|
|
&self.name
|
|
}
|
|
|
|
fn endpoints_required(&self) -> u8 {
|
|
self.func_type.endpoints()
|
|
}
|
|
|
|
fn meta(&self) -> FunctionMeta {
|
|
FunctionMeta {
|
|
name: self.name().to_string(),
|
|
description: self.func_type.description().to_string(),
|
|
endpoints: self.endpoints_required(),
|
|
enabled: true,
|
|
}
|
|
}
|
|
|
|
fn create(&self, gadget_path: &Path) -> Result<()> {
|
|
let func_path = self.function_path(gadget_path);
|
|
create_dir(&func_path)?;
|
|
|
|
// Set HID parameters
|
|
write_file(
|
|
&func_path.join("protocol"),
|
|
&self.func_type.protocol().to_string(),
|
|
)?;
|
|
write_file(
|
|
&func_path.join("subclass"),
|
|
&self.func_type.subclass().to_string(),
|
|
)?;
|
|
write_file(
|
|
&func_path.join("report_length"),
|
|
&self.func_type.report_length().to_string(),
|
|
)?;
|
|
|
|
// Write report descriptor
|
|
write_bytes(&func_path.join("report_desc"), self.func_type.report_desc())?;
|
|
|
|
debug!(
|
|
"Created HID function: {} at {}",
|
|
self.name(),
|
|
func_path.display()
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
fn link(&self, config_path: &Path, gadget_path: &Path) -> Result<()> {
|
|
let func_path = self.function_path(gadget_path);
|
|
let link_path = config_path.join(self.name());
|
|
|
|
if !link_path.exists() {
|
|
create_symlink(&func_path, &link_path)?;
|
|
debug!("Linked HID function {} to config", self.name());
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn unlink(&self, config_path: &Path) -> Result<()> {
|
|
let link_path = config_path.join(self.name());
|
|
remove_file(&link_path)?;
|
|
debug!("Unlinked HID function {}", self.name());
|
|
Ok(())
|
|
}
|
|
|
|
fn cleanup(&self, gadget_path: &Path) -> Result<()> {
|
|
let func_path = self.function_path(gadget_path);
|
|
remove_dir(&func_path)?;
|
|
debug!("Cleaned up HID function {}", self.name());
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_hid_function_types() {
|
|
assert_eq!(HidFunctionType::Keyboard.endpoints(), 1);
|
|
assert_eq!(HidFunctionType::MouseRelative.endpoints(), 1);
|
|
assert_eq!(HidFunctionType::MouseAbsolute.endpoints(), 1);
|
|
|
|
assert_eq!(HidFunctionType::Keyboard.report_length(), 8);
|
|
assert_eq!(HidFunctionType::MouseRelative.report_length(), 4);
|
|
assert_eq!(HidFunctionType::MouseAbsolute.report_length(), 6);
|
|
}
|
|
|
|
#[test]
|
|
fn test_hid_function_names() {
|
|
let kb = HidFunction::keyboard(0);
|
|
assert_eq!(kb.name(), "hid.usb0");
|
|
assert_eq!(kb.device_path(), PathBuf::from("/dev/hidg0"));
|
|
|
|
let mouse = HidFunction::mouse_relative(1);
|
|
assert_eq!(mouse.name(), "hid.usb1");
|
|
}
|
|
}
|