mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-06-14 03:32:00 +08:00
@@ -37,7 +37,14 @@ pub struct AppConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AppConfig {
|
impl AppConfig {
|
||||||
|
pub fn enforce_invariants(&mut self) {
|
||||||
|
if self.hid.backend != HidBackend::Otg {
|
||||||
|
self.msd.enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn apply_platform_defaults(&mut self) {
|
pub fn apply_platform_defaults(&mut self) {
|
||||||
crate::platform::defaults::apply(self);
|
crate::platform::defaults::apply(self);
|
||||||
|
self.enforce_invariants();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ impl ConfigStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn load(&self) -> Result<()> {
|
pub async fn load(&self) -> Result<()> {
|
||||||
let config = Self::load_config(&self.pool).await?;
|
let mut config = Self::load_config(&self.pool).await?;
|
||||||
|
config.enforce_invariants();
|
||||||
self.cache.store(Arc::new(config));
|
self.cache.store(Arc::new(config));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -73,6 +74,8 @@ impl ConfigStore {
|
|||||||
|
|
||||||
pub async fn set(&self, config: AppConfig) -> Result<()> {
|
pub async fn set(&self, config: AppConfig) -> Result<()> {
|
||||||
let _guard = self.write_lock.lock().await;
|
let _guard = self.write_lock.lock().await;
|
||||||
|
let mut config = config;
|
||||||
|
config.enforce_invariants();
|
||||||
Self::save_config_to_db(&self.pool, &config).await?;
|
Self::save_config_to_db(&self.pool, &config).await?;
|
||||||
self.cache.store(Arc::new(config));
|
self.cache.store(Arc::new(config));
|
||||||
|
|
||||||
@@ -91,6 +94,7 @@ impl ConfigStore {
|
|||||||
let current = self.cache.load();
|
let current = self.cache.load();
|
||||||
let mut config = (**current).clone();
|
let mut config = (**current).clone();
|
||||||
f(&mut config);
|
f(&mut config);
|
||||||
|
config.enforce_invariants();
|
||||||
|
|
||||||
Self::save_config_to_db(&self.pool, &config).await?;
|
Self::save_config_to_db(&self.pool, &config).await?;
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use std::sync::{mpsc, Arc};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
use tracing::{info, trace};
|
use tracing::{info, trace, warn};
|
||||||
|
|
||||||
use super::backend::{HidBackend, HidBackendRuntimeSnapshot};
|
use super::backend::{HidBackend, HidBackendRuntimeSnapshot};
|
||||||
use super::ch9329_proto::{
|
use super::ch9329_proto::{
|
||||||
@@ -36,6 +36,8 @@ const RECONNECT_DELAY_MS: u64 = 2000;
|
|||||||
|
|
||||||
const INIT_WAIT_MS: u64 = 3000;
|
const INIT_WAIT_MS: u64 = 3000;
|
||||||
|
|
||||||
|
const RECONNECT_COMMAND_POLL_MS: u64 = 100;
|
||||||
|
|
||||||
struct Ch9329RuntimeState {
|
struct Ch9329RuntimeState {
|
||||||
initialized: AtomicBool,
|
initialized: AtomicBool,
|
||||||
online: AtomicBool,
|
online: AtomicBool,
|
||||||
@@ -117,15 +119,15 @@ pub struct Ch9329Backend {
|
|||||||
baud_rate: u32,
|
baud_rate: u32,
|
||||||
worker_tx: Mutex<Option<mpsc::Sender<WorkerCommand>>>,
|
worker_tx: Mutex<Option<mpsc::Sender<WorkerCommand>>>,
|
||||||
worker_handle: Mutex<Option<thread::JoinHandle<()>>>,
|
worker_handle: Mutex<Option<thread::JoinHandle<()>>>,
|
||||||
keyboard_state: Mutex<KeyboardReport>,
|
keyboard_state: Arc<Mutex<KeyboardReport>>,
|
||||||
mouse_buttons: AtomicU8,
|
mouse_buttons: Arc<AtomicU8>,
|
||||||
screen_resolution: RwLock<(u32, u32)>,
|
screen_resolution: RwLock<(u32, u32)>,
|
||||||
chip_info: Arc<RwLock<Option<ChipInfo>>>,
|
chip_info: Arc<RwLock<Option<ChipInfo>>>,
|
||||||
led_status: Arc<RwLock<LedStatus>>,
|
led_status: Arc<RwLock<LedStatus>>,
|
||||||
address: u8,
|
address: u8,
|
||||||
last_abs_x: AtomicU16,
|
last_abs_x: Arc<AtomicU16>,
|
||||||
last_abs_y: AtomicU16,
|
last_abs_y: Arc<AtomicU16>,
|
||||||
relative_mouse_active: AtomicBool,
|
relative_mouse_active: Arc<AtomicBool>,
|
||||||
runtime: Arc<Ch9329RuntimeState>,
|
runtime: Arc<Ch9329RuntimeState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,15 +142,15 @@ impl Ch9329Backend {
|
|||||||
baud_rate,
|
baud_rate,
|
||||||
worker_tx: Mutex::new(None),
|
worker_tx: Mutex::new(None),
|
||||||
worker_handle: Mutex::new(None),
|
worker_handle: Mutex::new(None),
|
||||||
keyboard_state: Mutex::new(KeyboardReport::default()),
|
keyboard_state: Arc::new(Mutex::new(KeyboardReport::default())),
|
||||||
mouse_buttons: AtomicU8::new(0),
|
mouse_buttons: Arc::new(AtomicU8::new(0)),
|
||||||
screen_resolution: RwLock::new((1920, 1080)),
|
screen_resolution: RwLock::new((1920, 1080)),
|
||||||
chip_info: Arc::new(RwLock::new(None)),
|
chip_info: Arc::new(RwLock::new(None)),
|
||||||
led_status: Arc::new(RwLock::new(LedStatus::default())),
|
led_status: Arc::new(RwLock::new(LedStatus::default())),
|
||||||
address: DEFAULT_ADDR,
|
address: DEFAULT_ADDR,
|
||||||
last_abs_x: AtomicU16::new(0),
|
last_abs_x: Arc::new(AtomicU16::new(0)),
|
||||||
last_abs_y: AtomicU16::new(0),
|
last_abs_y: Arc::new(AtomicU16::new(0)),
|
||||||
relative_mouse_active: AtomicBool::new(false),
|
relative_mouse_active: Arc::new(AtomicBool::new(false)),
|
||||||
runtime: Arc::new(Ch9329RuntimeState::new()),
|
runtime: Arc::new(Ch9329RuntimeState::new()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -168,9 +170,27 @@ impl Ch9329Backend {
|
|||||||
std::path::Path::new(&self.port_path).exists()
|
std::path::Path::new(&self.port_path).exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serial_error_to_hid_error(e: serialport::Error, operation: &str) -> AppError {
|
fn serial_error_to_hid_error(
|
||||||
|
port_path: &str,
|
||||||
|
e: serialport::Error,
|
||||||
|
operation: &str,
|
||||||
|
) -> AppError {
|
||||||
|
let port_present = {
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
crate::utils::list_serial_ports()
|
||||||
|
.iter()
|
||||||
|
.any(|port| port.eq_ignore_ascii_case(port_path))
|
||||||
|
}
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
{
|
||||||
|
std::path::Path::new(port_path).exists()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let error_code = match e.kind() {
|
let error_code = match e.kind() {
|
||||||
serialport::ErrorKind::NoDevice => "port_not_found",
|
serialport::ErrorKind::NoDevice if !port_present => "port_not_found",
|
||||||
|
serialport::ErrorKind::NoDevice => "device_unavailable",
|
||||||
serialport::ErrorKind::InvalidInput => "invalid_config",
|
serialport::ErrorKind::InvalidInput => "invalid_config",
|
||||||
serialport::ErrorKind::Io(_) => "io_error",
|
serialport::ErrorKind::Io(_) => "io_error",
|
||||||
_ => "serial_error",
|
_ => "serial_error",
|
||||||
@@ -204,7 +224,7 @@ impl Ch9329Backend {
|
|||||||
serialport::new(port_path, baud_rate)
|
serialport::new(port_path, baud_rate)
|
||||||
.timeout(Duration::from_millis(RESPONSE_TIMEOUT_MS))
|
.timeout(Duration::from_millis(RESPONSE_TIMEOUT_MS))
|
||||||
.open()
|
.open()
|
||||||
.map_err(|e| Self::serial_error_to_hid_error(e, "Failed to open serial port"))
|
.map_err(|e| Self::serial_error_to_hid_error(port_path, e, "Failed to open serial port"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_packet(
|
fn write_packet(
|
||||||
@@ -302,6 +322,28 @@ impl Ch9329Backend {
|
|||||||
.ok_or_else(|| Self::backend_error("Failed to parse chip info", "invalid_response"))
|
.ok_or_else(|| Self::backend_error("Failed to parse chip info", "invalid_response"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn open_ready_port(
|
||||||
|
port_path: &str,
|
||||||
|
baud_rate: u32,
|
||||||
|
address: u8,
|
||||||
|
) -> Result<(Box<dyn serialport::SerialPort>, ChipInfo)> {
|
||||||
|
Self::open_port(port_path, baud_rate).and_then(|mut port| {
|
||||||
|
let info = Self::query_chip_info_on_port(port.as_mut(), address)?;
|
||||||
|
Ok((port, info))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn record_runtime_error(runtime: &Arc<Ch9329RuntimeState>, err: &AppError) {
|
||||||
|
if let AppError::HidError {
|
||||||
|
reason, error_code, ..
|
||||||
|
} = err
|
||||||
|
{
|
||||||
|
runtime.set_error(reason.clone(), error_code.clone());
|
||||||
|
} else {
|
||||||
|
runtime.set_error(err.to_string(), "error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn update_chip_info_cache(
|
fn update_chip_info_cache(
|
||||||
chip_info: &Arc<RwLock<Option<ChipInfo>>>,
|
chip_info: &Arc<RwLock<Option<ChipInfo>>>,
|
||||||
led_status: &Arc<RwLock<LedStatus>>,
|
led_status: &Arc<RwLock<LedStatus>>,
|
||||||
@@ -342,6 +384,49 @@ impl Ch9329Backend {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn wait_reconnect_delay(rx: &mpsc::Receiver<WorkerCommand>) -> bool {
|
||||||
|
let deadline = Instant::now() + Duration::from_millis(RECONNECT_DELAY_MS);
|
||||||
|
loop {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now >= deadline {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let remaining = deadline.saturating_duration_since(now);
|
||||||
|
let timeout = remaining.min(Duration::from_millis(RECONNECT_COMMAND_POLL_MS));
|
||||||
|
match rx.recv_timeout(timeout) {
|
||||||
|
Ok(WorkerCommand::Shutdown) | Err(mpsc::RecvTimeoutError::Disconnected) => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Ok(_) | Err(mpsc::RecvTimeoutError::Timeout) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn release_state_on_port(port: &mut dyn serialport::SerialPort, address: u8) -> Result<()> {
|
||||||
|
let reset_sequence = [(cmd::SEND_KB_GENERAL_DATA, vec![0; 8])];
|
||||||
|
|
||||||
|
for (cmd, data) in reset_sequence {
|
||||||
|
Self::xfer_packet(port, address, cmd, &data)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_local_state(
|
||||||
|
keyboard_state: &Arc<Mutex<KeyboardReport>>,
|
||||||
|
mouse_buttons: &Arc<AtomicU8>,
|
||||||
|
last_abs_x: &Arc<AtomicU16>,
|
||||||
|
last_abs_y: &Arc<AtomicU16>,
|
||||||
|
relative_mouse_active: &Arc<AtomicBool>,
|
||||||
|
) {
|
||||||
|
keyboard_state.lock().clear();
|
||||||
|
mouse_buttons.store(0, Ordering::Relaxed);
|
||||||
|
last_abs_x.store(0, Ordering::Relaxed);
|
||||||
|
last_abs_y.store(0, Ordering::Relaxed);
|
||||||
|
relative_mouse_active.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
fn worker_reconnect_loop(
|
fn worker_reconnect_loop(
|
||||||
rx: &mpsc::Receiver<WorkerCommand>,
|
rx: &mpsc::Receiver<WorkerCommand>,
|
||||||
port_path: &str,
|
port_path: &str,
|
||||||
@@ -351,18 +436,9 @@ impl Ch9329Backend {
|
|||||||
led_status: &Arc<RwLock<LedStatus>>,
|
led_status: &Arc<RwLock<LedStatus>>,
|
||||||
runtime: &Arc<Ch9329RuntimeState>,
|
runtime: &Arc<Ch9329RuntimeState>,
|
||||||
) -> Option<Box<dyn serialport::SerialPort>> {
|
) -> Option<Box<dyn serialport::SerialPort>> {
|
||||||
|
runtime.set_offline();
|
||||||
loop {
|
loop {
|
||||||
match rx.recv_timeout(Duration::from_millis(RECONNECT_DELAY_MS)) {
|
match Self::open_ready_port(port_path, baud_rate, address) {
|
||||||
Ok(WorkerCommand::Shutdown) => return None,
|
|
||||||
Ok(_) => continue,
|
|
||||||
Err(mpsc::RecvTimeoutError::Disconnected) => return None,
|
|
||||||
Err(mpsc::RecvTimeoutError::Timeout) => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
match Self::open_port(port_path, baud_rate).and_then(|mut port| {
|
|
||||||
let info = Self::query_chip_info_on_port(port.as_mut(), address)?;
|
|
||||||
Ok((port, info))
|
|
||||||
}) {
|
|
||||||
Ok((port, info)) => {
|
Ok((port, info)) => {
|
||||||
info!(
|
info!(
|
||||||
"CH9329 reconnected: {}, USB: {}",
|
"CH9329 reconnected: {}, USB: {}",
|
||||||
@@ -380,11 +456,9 @@ impl Ch9329Backend {
|
|||||||
return Some(port);
|
return Some(port);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let AppError::HidError {
|
Self::record_runtime_error(runtime, &err);
|
||||||
reason, error_code, ..
|
if !Self::wait_reconnect_delay(rx) {
|
||||||
} = err
|
return None;
|
||||||
{
|
|
||||||
runtime.set_error(reason, error_code);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -437,36 +511,43 @@ impl Ch9329Backend {
|
|||||||
chip_info: Arc<RwLock<Option<ChipInfo>>>,
|
chip_info: Arc<RwLock<Option<ChipInfo>>>,
|
||||||
led_status: Arc<RwLock<LedStatus>>,
|
led_status: Arc<RwLock<LedStatus>>,
|
||||||
runtime: Arc<Ch9329RuntimeState>,
|
runtime: Arc<Ch9329RuntimeState>,
|
||||||
|
keyboard_state: Arc<Mutex<KeyboardReport>>,
|
||||||
|
mouse_buttons: Arc<AtomicU8>,
|
||||||
|
last_abs_x: Arc<AtomicU16>,
|
||||||
|
last_abs_y: Arc<AtomicU16>,
|
||||||
|
relative_mouse_active: Arc<AtomicBool>,
|
||||||
init_tx: mpsc::Sender<Result<ChipInfo>>,
|
init_tx: mpsc::Sender<Result<ChipInfo>>,
|
||||||
) {
|
) {
|
||||||
runtime.set_initialized(true);
|
runtime.set_initialized(true);
|
||||||
|
|
||||||
let mut port = match Self::open_port(&port_path, baud_rate).and_then(|mut port| {
|
let mut init_tx = Some(init_tx);
|
||||||
let info = Self::query_chip_info_on_port(port.as_mut(), address)?;
|
let mut port = loop {
|
||||||
Ok((port, info))
|
match Self::open_ready_port(&port_path, baud_rate, address) {
|
||||||
}) {
|
Ok((port, info)) => {
|
||||||
Ok((port, info)) => {
|
info!(
|
||||||
info!(
|
"CH9329 serial port opened: {} @ {} baud",
|
||||||
"CH9329 serial port opened: {} @ {} baud",
|
port_path, baud_rate
|
||||||
port_path, baud_rate
|
);
|
||||||
);
|
if Self::update_chip_info_cache(&chip_info, &led_status, info.clone()) {
|
||||||
if Self::update_chip_info_cache(&chip_info, &led_status, info.clone()) {
|
runtime.notify();
|
||||||
runtime.notify();
|
}
|
||||||
|
runtime.set_online();
|
||||||
|
if let Some(init_tx) = init_tx.take() {
|
||||||
|
let _ = init_tx.send(Ok(info));
|
||||||
|
}
|
||||||
|
break port;
|
||||||
}
|
}
|
||||||
runtime.set_online();
|
Err(err) => {
|
||||||
let _ = init_tx.send(Ok(info));
|
Self::record_runtime_error(&runtime, &err);
|
||||||
port
|
if let Some(init_tx) = init_tx.take() {
|
||||||
}
|
let _ = init_tx.send(Err(err));
|
||||||
Err(err) => {
|
}
|
||||||
if let AppError::HidError {
|
if !Self::wait_reconnect_delay(&rx) {
|
||||||
reason, error_code, ..
|
runtime.set_offline();
|
||||||
} = &err
|
runtime.set_initialized(false);
|
||||||
{
|
return;
|
||||||
runtime.set_error(reason.clone(), error_code.clone());
|
}
|
||||||
}
|
}
|
||||||
let _ = init_tx.send(Err(err));
|
|
||||||
runtime.set_initialized(false);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -482,6 +563,7 @@ impl Ch9329Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Self::try_best_effort_reset(port.as_mut(), address);
|
Self::try_best_effort_reset(port.as_mut(), address);
|
||||||
|
drop(port);
|
||||||
|
|
||||||
let Some(new_port) = Self::worker_reconnect_loop(
|
let Some(new_port) = Self::worker_reconnect_loop(
|
||||||
&rx,
|
&rx,
|
||||||
@@ -500,28 +582,17 @@ impl Ch9329Backend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(WorkerCommand::ResetState) => {
|
Ok(WorkerCommand::ResetState) => {
|
||||||
let reset_sequence = [
|
Self::clear_local_state(
|
||||||
(cmd::SEND_KB_GENERAL_DATA, vec![0; 8]),
|
&keyboard_state,
|
||||||
(cmd::SEND_MS_ABS_DATA, vec![0x02, 0, 0, 0, 0, 0, 0]),
|
&mouse_buttons,
|
||||||
(cmd::SEND_KB_MEDIA_DATA, vec![0x02, 0x00, 0x00, 0x00]),
|
&last_abs_x,
|
||||||
];
|
&last_abs_y,
|
||||||
|
&relative_mouse_active,
|
||||||
let mut reset_failed = false;
|
);
|
||||||
for (cmd, data) in reset_sequence {
|
if let Err(err) = Self::release_state_on_port(port.as_mut(), address) {
|
||||||
if let Err(err) = Self::xfer_packet(port.as_mut(), address, cmd, &data) {
|
Self::record_runtime_error(&runtime, &err);
|
||||||
if let AppError::HidError {
|
Self::try_best_effort_reset(port.as_mut(), address);
|
||||||
reason, error_code, ..
|
drop(port);
|
||||||
} = err
|
|
||||||
{
|
|
||||||
runtime.set_error(reason, error_code);
|
|
||||||
}
|
|
||||||
reset_failed = true;
|
|
||||||
Self::try_best_effort_reset(port.as_mut(), address);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if reset_failed {
|
|
||||||
let Some(new_port) = Self::worker_reconnect_loop(
|
let Some(new_port) = Self::worker_reconnect_loop(
|
||||||
&rx,
|
&rx,
|
||||||
&port_path,
|
&port_path,
|
||||||
@@ -556,6 +627,7 @@ impl Ch9329Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Self::try_best_effort_reset(port.as_mut(), address);
|
Self::try_best_effort_reset(port.as_mut(), address);
|
||||||
|
drop(port);
|
||||||
|
|
||||||
let Some(new_port) = Self::worker_reconnect_loop(
|
let Some(new_port) = Self::worker_reconnect_loop(
|
||||||
&rx,
|
&rx,
|
||||||
@@ -596,12 +668,29 @@ impl HidBackend for Ch9329Backend {
|
|||||||
let chip_info = self.chip_info.clone();
|
let chip_info = self.chip_info.clone();
|
||||||
let led_status = self.led_status.clone();
|
let led_status = self.led_status.clone();
|
||||||
let runtime = self.runtime.clone();
|
let runtime = self.runtime.clone();
|
||||||
|
let keyboard_state = self.keyboard_state.clone();
|
||||||
|
let mouse_buttons = self.mouse_buttons.clone();
|
||||||
|
let last_abs_x = self.last_abs_x.clone();
|
||||||
|
let last_abs_y = self.last_abs_y.clone();
|
||||||
|
let relative_mouse_active = self.relative_mouse_active.clone();
|
||||||
|
|
||||||
let handle = thread::Builder::new()
|
let handle = thread::Builder::new()
|
||||||
.name("ch9329-worker".to_string())
|
.name("ch9329-worker".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
Self::worker_loop(
|
Self::worker_loop(
|
||||||
port_path, baud_rate, address, rx, chip_info, led_status, runtime, init_tx,
|
port_path,
|
||||||
|
baud_rate,
|
||||||
|
address,
|
||||||
|
rx,
|
||||||
|
chip_info,
|
||||||
|
led_status,
|
||||||
|
runtime,
|
||||||
|
keyboard_state,
|
||||||
|
mouse_buttons,
|
||||||
|
last_abs_x,
|
||||||
|
last_abs_y,
|
||||||
|
relative_mouse_active,
|
||||||
|
init_tx,
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.map_err(|e| AppError::Internal(format!("Failed to spawn CH9329 worker: {}", e)))?;
|
.map_err(|e| AppError::Internal(format!("Failed to spawn CH9329 worker: {}", e)))?;
|
||||||
@@ -626,7 +715,6 @@ impl HidBackend for Ch9329Backend {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Ok(Err(err)) => {
|
Ok(Err(err)) => {
|
||||||
let _ = handle.join();
|
|
||||||
self.record_error(
|
self.record_error(
|
||||||
format!(
|
format!(
|
||||||
"CH9329 not responding on {} @ {} baud: {}",
|
"CH9329 not responding on {} @ {} baud: {}",
|
||||||
@@ -634,10 +722,13 @@ impl HidBackend for Ch9329Backend {
|
|||||||
),
|
),
|
||||||
"init_failed",
|
"init_failed",
|
||||||
);
|
);
|
||||||
Err(AppError::Internal(format!(
|
warn!(
|
||||||
"CH9329 not responding on {} @ {} baud: {}",
|
"CH9329 not responding on {} @ {} baud, retrying in background: {}",
|
||||||
self.port_path, self.baud_rate, err
|
self.port_path, self.baud_rate, err
|
||||||
)))
|
);
|
||||||
|
*self.worker_tx.lock() = Some(tx);
|
||||||
|
*self.worker_handle.lock() = Some(handle);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let _ = tx.send(WorkerCommand::Shutdown);
|
let _ = tx.send(WorkerCommand::Shutdown);
|
||||||
|
|||||||
@@ -167,7 +167,8 @@ pub async fn apply_hid_config(
|
|||||||
new_config: &HidConfig,
|
new_config: &HidConfig,
|
||||||
options: ConfigApplyOptions,
|
options: ConfigApplyOptions,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let current_msd_enabled = state.config.get().msd.enabled;
|
let current_config = state.config.get();
|
||||||
|
let current_msd_enabled = current_config.msd.enabled && new_config.backend == HidBackend::Otg;
|
||||||
new_config.validate_otg_endpoint_budget(current_msd_enabled)?;
|
new_config.validate_otg_endpoint_budget(current_msd_enabled)?;
|
||||||
|
|
||||||
let descriptor_changed = old_config.otg_descriptor != new_config.otg_descriptor;
|
let descriptor_changed = old_config.otg_descriptor != new_config.otg_descriptor;
|
||||||
@@ -235,18 +236,19 @@ pub async fn apply_msd_config(
|
|||||||
new_config: &MsdConfig,
|
new_config: &MsdConfig,
|
||||||
options: ConfigApplyOptions,
|
options: ConfigApplyOptions,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
state
|
let current_config = state.config.get();
|
||||||
.config
|
let hid_backend_is_otg = current_config.hid.backend == HidBackend::Otg;
|
||||||
.get()
|
let effective_new_msd_enabled = new_config.enabled && hid_backend_is_otg;
|
||||||
|
current_config
|
||||||
.hid
|
.hid
|
||||||
.validate_otg_endpoint_budget(new_config.enabled)?;
|
.validate_otg_endpoint_budget(effective_new_msd_enabled)?;
|
||||||
|
|
||||||
tracing::info!("MSD config sent, checking if reload needed...");
|
tracing::info!("MSD config sent, checking if reload needed...");
|
||||||
tracing::debug!("Old MSD config: {:?}", old_config);
|
tracing::debug!("Old MSD config: {:?}", old_config);
|
||||||
tracing::debug!("New MSD config: {:?}", new_config);
|
tracing::debug!("New MSD config: {:?}", new_config);
|
||||||
|
|
||||||
let old_msd_enabled = old_config.enabled;
|
let old_msd_enabled = old_config.enabled;
|
||||||
let new_msd_enabled = new_config.enabled;
|
let new_msd_enabled = effective_new_msd_enabled;
|
||||||
let msd_dir_changed = old_config.msd_dir != new_config.msd_dir;
|
let msd_dir_changed = old_config.msd_dir != new_config.msd_dir;
|
||||||
|
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ pub async fn update_hid_config(
|
|||||||
.config
|
.config
|
||||||
.update(|config| {
|
.update(|config| {
|
||||||
req.apply_to(&mut config.hid);
|
req.apply_to(&mut config.hid);
|
||||||
|
config.enforce_invariants();
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ pub async fn update_msd_config(
|
|||||||
.config
|
.config
|
||||||
.update(|config| {
|
.update(|config| {
|
||||||
req.apply_to(&mut config.msd);
|
req.apply_to(&mut config.msd);
|
||||||
|
config.enforce_invariants();
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ pub async fn setup_init(
|
|||||||
if let Some(enabled) = req.msd_enabled {
|
if let Some(enabled) = req.msd_enabled {
|
||||||
config.msd.enabled = enabled;
|
config.msd.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
config.enforce_invariants();
|
||||||
|
|
||||||
// Extension settings
|
// Extension settings
|
||||||
if let Some(enabled) = req.ttyd_enabled {
|
if let Some(enabled) = req.ttyd_enabled {
|
||||||
|
|||||||
@@ -319,6 +319,7 @@ function hidErrorHint(errorCode?: string | null, backend?: string | null, reason
|
|||||||
case 'io_error':
|
case 'io_error':
|
||||||
case 'write_failed':
|
case 'write_failed':
|
||||||
case 'read_failed':
|
case 'read_failed':
|
||||||
|
case 'device_unavailable':
|
||||||
if (backend === 'otg') return t('hid.errorHints.otgIoError')
|
if (backend === 'otg') return t('hid.errorHints.otgIoError')
|
||||||
if (backend === 'ch9329') return t('hid.errorHints.ch9329IoError')
|
if (backend === 'ch9329') return t('hid.errorHints.ch9329IoError')
|
||||||
return t('hid.errorHints.ioError')
|
return t('hid.errorHints.ioError')
|
||||||
|
|||||||
@@ -1286,10 +1286,10 @@ async function saveConfig() {
|
|||||||
hidUpdate.otg_functions = { ...config.value.hid_otg_functions }
|
hidUpdate.otg_functions = { ...config.value.hid_otg_functions }
|
||||||
hidUpdate.otg_keyboard_leds = config.value.hid_otg_keyboard_leds
|
hidUpdate.otg_keyboard_leds = config.value.hid_otg_keyboard_leds
|
||||||
}
|
}
|
||||||
await configStore.updateMsd({
|
|
||||||
enabled: config.value.msd_enabled,
|
|
||||||
})
|
|
||||||
await configStore.updateHid(hidUpdate)
|
await configStore.updateHid(hidUpdate)
|
||||||
|
await configStore.updateMsd({
|
||||||
|
enabled: config.value.hid_backend === 'otg' && config.value.msd_enabled,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeSection.value === 'msd') {
|
if (activeSection.value === 'msd') {
|
||||||
|
|||||||
Reference in New Issue
Block a user