mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-09-12 10:34:24 +08:00
- 在补丁内核上启用 HID 写入唤醒及 USB 远程唤醒描述符 - 支持全局配置 Flash 和 CD-ROM INQUIRY 字符串 - 兼容普通内核的通用 INQUIRY 属性 - 调整 OTG 功能布局并更新大容量 DVD 镜像提示
208 lines
5.8 KiB
Rust
208 lines
5.8 KiB
Rust
use std::path::{Path, PathBuf};
|
|
use tracing::debug;
|
|
|
|
use super::configfs::{
|
|
create_dir, create_symlink, remove_dir, remove_file, write_bytes, write_file,
|
|
write_file_if_exists,
|
|
};
|
|
use super::function::GadgetFunction;
|
|
use super::report_desc::{
|
|
CONSUMER_CONTROL, KEYBOARD, KEYBOARD_WITH_LED, MOUSE_ABSOLUTE, MOUSE_RELATIVE,
|
|
};
|
|
use crate::error::Result;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum HidFunctionType {
|
|
Keyboard,
|
|
MouseRelative,
|
|
MouseAbsolute,
|
|
ConsumerControl,
|
|
}
|
|
|
|
impl HidFunctionType {
|
|
pub fn protocol(&self) -> u8 {
|
|
match self {
|
|
HidFunctionType::Keyboard => 1,
|
|
HidFunctionType::MouseRelative => 2,
|
|
HidFunctionType::MouseAbsolute => 2,
|
|
HidFunctionType::ConsumerControl => 0,
|
|
}
|
|
}
|
|
|
|
pub fn subclass(&self) -> u8 {
|
|
match self {
|
|
HidFunctionType::Keyboard => 1,
|
|
HidFunctionType::MouseRelative => 1,
|
|
HidFunctionType::MouseAbsolute => 0,
|
|
HidFunctionType::ConsumerControl => 0,
|
|
}
|
|
}
|
|
|
|
pub fn report_length(&self, _keyboard_leds: bool) -> u8 {
|
|
match self {
|
|
HidFunctionType::Keyboard => 8,
|
|
HidFunctionType::MouseRelative => 4,
|
|
HidFunctionType::MouseAbsolute => 6,
|
|
HidFunctionType::ConsumerControl => 2,
|
|
}
|
|
}
|
|
|
|
pub fn report_desc(&self, keyboard_leds: bool) -> &'static [u8] {
|
|
match self {
|
|
HidFunctionType::Keyboard => {
|
|
if keyboard_leds {
|
|
KEYBOARD_WITH_LED
|
|
} else {
|
|
KEYBOARD
|
|
}
|
|
}
|
|
HidFunctionType::MouseRelative => MOUSE_RELATIVE,
|
|
HidFunctionType::MouseAbsolute => MOUSE_ABSOLUTE,
|
|
HidFunctionType::ConsumerControl => CONSUMER_CONTROL,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct HidFunction {
|
|
instance: u8,
|
|
func_type: HidFunctionType,
|
|
name: String,
|
|
keyboard_leds: bool,
|
|
}
|
|
|
|
impl HidFunction {
|
|
pub fn keyboard(instance: u8, keyboard_leds: bool) -> Self {
|
|
Self {
|
|
instance,
|
|
func_type: HidFunctionType::Keyboard,
|
|
name: format!("hid.usb{}", instance),
|
|
keyboard_leds,
|
|
}
|
|
}
|
|
|
|
pub fn mouse_relative(instance: u8) -> Self {
|
|
Self {
|
|
instance,
|
|
func_type: HidFunctionType::MouseRelative,
|
|
name: format!("hid.usb{}", instance),
|
|
keyboard_leds: false,
|
|
}
|
|
}
|
|
|
|
pub fn mouse_absolute(instance: u8) -> Self {
|
|
Self {
|
|
instance,
|
|
func_type: HidFunctionType::MouseAbsolute,
|
|
name: format!("hid.usb{}", instance),
|
|
keyboard_leds: false,
|
|
}
|
|
}
|
|
|
|
pub fn consumer_control(instance: u8) -> Self {
|
|
Self {
|
|
instance,
|
|
func_type: HidFunctionType::ConsumerControl,
|
|
name: format!("hid.usb{}", instance),
|
|
keyboard_leds: false,
|
|
}
|
|
}
|
|
|
|
fn function_path(&self, gadget_path: &Path) -> PathBuf {
|
|
gadget_path.join("functions").join(self.name())
|
|
}
|
|
|
|
pub fn device_path(&self) -> PathBuf {
|
|
PathBuf::from(format!("/dev/hidg{}", self.instance))
|
|
}
|
|
}
|
|
|
|
impl GadgetFunction for HidFunction {
|
|
fn name(&self) -> &str {
|
|
&self.name
|
|
}
|
|
|
|
fn create(&self, gadget_path: &Path) -> Result<()> {
|
|
let func_path = self.function_path(gadget_path);
|
|
create_dir(&func_path)?;
|
|
|
|
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(self.keyboard_leds).to_string(),
|
|
)?;
|
|
|
|
write_bytes(
|
|
&func_path.join("report_desc"),
|
|
self.func_type.report_desc(self.keyboard_leds),
|
|
)?;
|
|
|
|
// Supported by the PiKVM HID kernel patch. Older kernels simply do
|
|
// not expose this ConfigFS attribute.
|
|
let _ = write_file_if_exists(&func_path.join("wakeup_on_write"), "1")?;
|
|
|
|
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.report_length(false), 8);
|
|
assert_eq!(HidFunctionType::Keyboard.report_length(true), 8);
|
|
assert_eq!(HidFunctionType::MouseRelative.report_length(false), 4);
|
|
assert_eq!(HidFunctionType::MouseAbsolute.report_length(false), 6);
|
|
}
|
|
|
|
#[test]
|
|
fn test_hid_function_names() {
|
|
let kb = HidFunction::keyboard(0, false);
|
|
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");
|
|
}
|
|
}
|