mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-06-14 19:51:58 +08:00
refactor(otg): 简化运行时与设置逻辑
This commit is contained in:
@@ -12,6 +12,26 @@ use crate::video::codec_constraints::{
|
||||
enforce_constraints_with_stream_manager, StreamCodecConstraints,
|
||||
};
|
||||
|
||||
fn hid_backend_type(config: &HidConfig) -> crate::hid::HidBackendType {
|
||||
match config.backend {
|
||||
HidBackend::Otg => crate::hid::HidBackendType::Otg,
|
||||
HidBackend::Ch9329 => crate::hid::HidBackendType::Ch9329 {
|
||||
port: config.ch9329_port.clone(),
|
||||
baud_rate: config.ch9329_baudrate,
|
||||
},
|
||||
HidBackend::None => crate::hid::HidBackendType::None,
|
||||
}
|
||||
}
|
||||
|
||||
async fn reconcile_otg_from_store(state: &Arc<AppState>) -> Result<()> {
|
||||
let config = state.config.get();
|
||||
state
|
||||
.otg_service
|
||||
.apply_config(&config.hid, &config.msd)
|
||||
.await
|
||||
.map_err(|e| AppError::Config(format!("OTG reconcile failed: {}", e)))
|
||||
}
|
||||
|
||||
/// 应用 Video 配置变更
|
||||
pub async fn apply_video_config(
|
||||
state: &Arc<AppState>,
|
||||
@@ -125,56 +145,26 @@ pub async fn apply_hid_config(
|
||||
old_config: &HidConfig,
|
||||
new_config: &HidConfig,
|
||||
) -> Result<()> {
|
||||
// 检查 OTG 描述符是否变更
|
||||
let current_msd_enabled = state.config.get().msd.enabled;
|
||||
new_config.validate_otg_endpoint_budget(current_msd_enabled)?;
|
||||
|
||||
let descriptor_changed = old_config.otg_descriptor != new_config.otg_descriptor;
|
||||
let old_hid_functions = old_config.effective_otg_functions();
|
||||
let mut new_hid_functions = new_config.effective_otg_functions();
|
||||
|
||||
// Low-endpoint UDCs (e.g., musb) cannot handle consumer control endpoints reliably
|
||||
if new_config.backend == HidBackend::Otg {
|
||||
if let Some(udc) = crate::otg::configfs::resolve_udc_name(new_config.otg_udc.as_deref()) {
|
||||
if crate::otg::configfs::is_low_endpoint_udc(&udc) && new_hid_functions.consumer {
|
||||
tracing::warn!(
|
||||
"UDC {} has low endpoint resources, disabling consumer control",
|
||||
udc
|
||||
);
|
||||
new_hid_functions.consumer = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let old_hid_functions = old_config.constrained_otg_functions();
|
||||
let new_hid_functions = new_config.constrained_otg_functions();
|
||||
let hid_functions_changed = old_hid_functions != new_hid_functions;
|
||||
let keyboard_leds_changed =
|
||||
old_config.effective_otg_keyboard_leds() != new_config.effective_otg_keyboard_leds();
|
||||
let endpoint_budget_changed =
|
||||
old_config.resolved_otg_endpoint_limit() != new_config.resolved_otg_endpoint_limit();
|
||||
|
||||
if new_config.backend == HidBackend::Otg && new_hid_functions.is_empty() {
|
||||
return Err(AppError::BadRequest(
|
||||
"OTG HID functions cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// 如果描述符变更且当前使用 OTG 后端,需要重建 Gadget
|
||||
if descriptor_changed && new_config.backend == HidBackend::Otg {
|
||||
tracing::info!("OTG descriptor changed, updating gadget...");
|
||||
if let Err(e) = state
|
||||
.otg_service
|
||||
.update_descriptor(&new_config.otg_descriptor)
|
||||
.await
|
||||
{
|
||||
tracing::error!("Failed to update OTG descriptor: {}", e);
|
||||
return Err(AppError::Config(format!(
|
||||
"OTG descriptor update failed: {}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
tracing::info!("OTG descriptor updated successfully");
|
||||
}
|
||||
|
||||
// 检查是否需要重载 HID 后端
|
||||
if old_config.backend == new_config.backend
|
||||
&& old_config.ch9329_port == new_config.ch9329_port
|
||||
&& old_config.ch9329_baudrate == new_config.ch9329_baudrate
|
||||
&& old_config.otg_udc == new_config.otg_udc
|
||||
&& !descriptor_changed
|
||||
&& !hid_functions_changed
|
||||
&& !keyboard_leds_changed
|
||||
&& !endpoint_budget_changed
|
||||
{
|
||||
tracing::info!("HID config unchanged, skipping reload");
|
||||
return Ok(());
|
||||
@@ -182,30 +172,27 @@ pub async fn apply_hid_config(
|
||||
|
||||
tracing::info!("Applying HID config changes...");
|
||||
|
||||
if new_config.backend == HidBackend::Otg
|
||||
&& (hid_functions_changed || old_config.backend != HidBackend::Otg)
|
||||
{
|
||||
let new_hid_backend = hid_backend_type(new_config);
|
||||
let transitioning_away_from_otg =
|
||||
old_config.backend == HidBackend::Otg && new_config.backend != HidBackend::Otg;
|
||||
|
||||
if transitioning_away_from_otg {
|
||||
state
|
||||
.otg_service
|
||||
.update_hid_functions(new_hid_functions.clone())
|
||||
.hid
|
||||
.reload(new_hid_backend.clone())
|
||||
.await
|
||||
.map_err(|e| AppError::Config(format!("OTG HID function update failed: {}", e)))?;
|
||||
.map_err(|e| AppError::Config(format!("HID reload failed: {}", e)))?;
|
||||
}
|
||||
|
||||
let new_hid_backend = match new_config.backend {
|
||||
HidBackend::Otg => crate::hid::HidBackendType::Otg,
|
||||
HidBackend::Ch9329 => crate::hid::HidBackendType::Ch9329 {
|
||||
port: new_config.ch9329_port.clone(),
|
||||
baud_rate: new_config.ch9329_baudrate,
|
||||
},
|
||||
HidBackend::None => crate::hid::HidBackendType::None,
|
||||
};
|
||||
reconcile_otg_from_store(state).await?;
|
||||
|
||||
state
|
||||
.hid
|
||||
.reload(new_hid_backend)
|
||||
.await
|
||||
.map_err(|e| AppError::Config(format!("HID reload failed: {}", e)))?;
|
||||
if !transitioning_away_from_otg {
|
||||
state
|
||||
.hid
|
||||
.reload(new_hid_backend)
|
||||
.await
|
||||
.map_err(|e| AppError::Config(format!("HID reload failed: {}", e)))?;
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
"HID backend reloaded successfully: {:?}",
|
||||
@@ -221,6 +208,12 @@ pub async fn apply_msd_config(
|
||||
old_config: &MsdConfig,
|
||||
new_config: &MsdConfig,
|
||||
) -> Result<()> {
|
||||
state
|
||||
.config
|
||||
.get()
|
||||
.hid
|
||||
.validate_otg_endpoint_budget(new_config.enabled)?;
|
||||
|
||||
tracing::info!("MSD config sent, checking if reload needed...");
|
||||
tracing::debug!("Old MSD config: {:?}", old_config);
|
||||
tracing::debug!("New MSD config: {:?}", new_config);
|
||||
@@ -260,6 +253,8 @@ pub async fn apply_msd_config(
|
||||
if new_msd_enabled {
|
||||
tracing::info!("(Re)initializing MSD...");
|
||||
|
||||
reconcile_otg_from_store(state).await?;
|
||||
|
||||
// Shutdown existing controller if present
|
||||
let mut msd_guard = state.msd.write().await;
|
||||
if let Some(msd) = msd_guard.as_mut() {
|
||||
@@ -295,6 +290,17 @@ pub async fn apply_msd_config(
|
||||
}
|
||||
*msd_guard = None;
|
||||
tracing::info!("MSD shutdown complete");
|
||||
|
||||
reconcile_otg_from_store(state).await?;
|
||||
}
|
||||
|
||||
let current_config = state.config.get();
|
||||
if current_config.hid.backend == HidBackend::Otg && old_msd_enabled != new_msd_enabled {
|
||||
state
|
||||
.hid
|
||||
.reload(crate::hid::HidBackendType::Otg)
|
||||
.await
|
||||
.map_err(|e| AppError::Config(format!("OTG HID reload failed: {}", e)))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -307,7 +307,9 @@ pub struct HidConfigUpdate {
|
||||
pub otg_udc: Option<String>,
|
||||
pub otg_descriptor: Option<OtgDescriptorConfigUpdate>,
|
||||
pub otg_profile: Option<OtgHidProfile>,
|
||||
pub otg_endpoint_budget: Option<OtgEndpointBudget>,
|
||||
pub otg_functions: Option<OtgHidFunctionsUpdate>,
|
||||
pub otg_keyboard_leds: Option<bool>,
|
||||
pub mouse_absolute: Option<bool>,
|
||||
}
|
||||
|
||||
@@ -346,9 +348,15 @@ impl HidConfigUpdate {
|
||||
if let Some(profile) = self.otg_profile.clone() {
|
||||
config.otg_profile = profile;
|
||||
}
|
||||
if let Some(budget) = self.otg_endpoint_budget {
|
||||
config.otg_endpoint_budget = budget;
|
||||
}
|
||||
if let Some(ref functions) = self.otg_functions {
|
||||
functions.apply_to(&mut config.otg_functions);
|
||||
}
|
||||
if let Some(enabled) = self.otg_keyboard_leds {
|
||||
config.otg_keyboard_leds = enabled;
|
||||
}
|
||||
if let Some(absolute) = self.mouse_absolute {
|
||||
config.mouse_absolute = absolute;
|
||||
}
|
||||
|
||||
@@ -598,38 +598,14 @@ pub struct SetupRequest {
|
||||
pub hid_ch9329_baudrate: Option<u32>,
|
||||
pub hid_otg_udc: Option<String>,
|
||||
pub hid_otg_profile: Option<String>,
|
||||
pub hid_otg_endpoint_budget: Option<crate::config::OtgEndpointBudget>,
|
||||
pub hid_otg_keyboard_leds: Option<bool>,
|
||||
pub msd_enabled: Option<bool>,
|
||||
// Extension settings
|
||||
pub ttyd_enabled: Option<bool>,
|
||||
pub rustdesk_enabled: Option<bool>,
|
||||
}
|
||||
|
||||
fn normalize_otg_profile_for_low_endpoint(config: &mut AppConfig) {
|
||||
if !matches!(config.hid.backend, crate::config::HidBackend::Otg) {
|
||||
return;
|
||||
}
|
||||
let udc = crate::otg::configfs::resolve_udc_name(config.hid.otg_udc.as_deref());
|
||||
let Some(udc) = udc else {
|
||||
return;
|
||||
};
|
||||
if !crate::otg::configfs::is_low_endpoint_udc(&udc) {
|
||||
return;
|
||||
}
|
||||
match config.hid.otg_profile {
|
||||
crate::config::OtgHidProfile::Full => {
|
||||
config.hid.otg_profile = crate::config::OtgHidProfile::FullNoConsumer;
|
||||
}
|
||||
crate::config::OtgHidProfile::FullNoMsd => {
|
||||
config.hid.otg_profile = crate::config::OtgHidProfile::FullNoConsumerNoMsd;
|
||||
}
|
||||
crate::config::OtgHidProfile::Custom => {
|
||||
if config.hid.otg_functions.consumer {
|
||||
config.hid.otg_functions.consumer = false;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn setup_init(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(req): Json<SetupRequest>,
|
||||
@@ -703,32 +679,19 @@ pub async fn setup_init(
|
||||
config.hid.otg_udc = Some(udc);
|
||||
}
|
||||
if let Some(profile) = req.hid_otg_profile.clone() {
|
||||
config.hid.otg_profile = match profile.as_str() {
|
||||
"full" => crate::config::OtgHidProfile::Full,
|
||||
"full_no_msd" => crate::config::OtgHidProfile::FullNoMsd,
|
||||
"full_no_consumer" => crate::config::OtgHidProfile::FullNoConsumer,
|
||||
"full_no_consumer_no_msd" => crate::config::OtgHidProfile::FullNoConsumerNoMsd,
|
||||
"legacy_keyboard" => crate::config::OtgHidProfile::LegacyKeyboard,
|
||||
"legacy_mouse_relative" => crate::config::OtgHidProfile::LegacyMouseRelative,
|
||||
"custom" => crate::config::OtgHidProfile::Custom,
|
||||
_ => config.hid.otg_profile.clone(),
|
||||
};
|
||||
if matches!(config.hid.backend, crate::config::HidBackend::Otg) {
|
||||
match config.hid.otg_profile {
|
||||
crate::config::OtgHidProfile::Full
|
||||
| crate::config::OtgHidProfile::FullNoConsumer => {
|
||||
config.msd.enabled = true;
|
||||
}
|
||||
crate::config::OtgHidProfile::FullNoMsd
|
||||
| crate::config::OtgHidProfile::FullNoConsumerNoMsd
|
||||
| crate::config::OtgHidProfile::LegacyKeyboard
|
||||
| crate::config::OtgHidProfile::LegacyMouseRelative => {
|
||||
config.msd.enabled = false;
|
||||
}
|
||||
crate::config::OtgHidProfile::Custom => {}
|
||||
}
|
||||
if let Some(parsed) = crate::config::OtgHidProfile::from_legacy_str(&profile) {
|
||||
config.hid.otg_profile = parsed;
|
||||
}
|
||||
}
|
||||
if let Some(budget) = req.hid_otg_endpoint_budget {
|
||||
config.hid.otg_endpoint_budget = budget;
|
||||
}
|
||||
if let Some(enabled) = req.hid_otg_keyboard_leds {
|
||||
config.hid.otg_keyboard_leds = enabled;
|
||||
}
|
||||
if let Some(enabled) = req.msd_enabled {
|
||||
config.msd.enabled = enabled;
|
||||
}
|
||||
|
||||
// Extension settings
|
||||
if let Some(enabled) = req.ttyd_enabled {
|
||||
@@ -737,29 +700,18 @@ pub async fn setup_init(
|
||||
if let Some(enabled) = req.rustdesk_enabled {
|
||||
config.rustdesk.enabled = enabled;
|
||||
}
|
||||
|
||||
normalize_otg_profile_for_low_endpoint(config);
|
||||
})
|
||||
.await?;
|
||||
|
||||
// Get updated config for HID reload
|
||||
let new_config = state.config.get();
|
||||
|
||||
if matches!(new_config.hid.backend, crate::config::HidBackend::Otg) {
|
||||
let mut hid_functions = new_config.hid.effective_otg_functions();
|
||||
if let Some(udc) = crate::otg::configfs::resolve_udc_name(new_config.hid.otg_udc.as_deref())
|
||||
{
|
||||
if crate::otg::configfs::is_low_endpoint_udc(&udc) && hid_functions.consumer {
|
||||
tracing::warn!(
|
||||
"UDC {} has low endpoint resources, disabling consumer control",
|
||||
udc
|
||||
);
|
||||
hid_functions.consumer = false;
|
||||
}
|
||||
}
|
||||
if let Err(e) = state.otg_service.update_hid_functions(hid_functions).await {
|
||||
tracing::warn!("Failed to apply HID functions during setup: {}", e);
|
||||
}
|
||||
if let Err(e) = state
|
||||
.otg_service
|
||||
.apply_config(&new_config.hid, &new_config.msd)
|
||||
.await
|
||||
{
|
||||
tracing::warn!("Failed to apply OTG config during setup: {}", e);
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
@@ -881,8 +833,10 @@ pub async fn update_config(
|
||||
let new_config: AppConfig = serde_json::from_value(merged)
|
||||
.map_err(|e| AppError::BadRequest(format!("Invalid config format: {}", e)))?;
|
||||
|
||||
let mut new_config = new_config;
|
||||
normalize_otg_profile_for_low_endpoint(&mut new_config);
|
||||
let new_config = new_config;
|
||||
new_config
|
||||
.hid
|
||||
.validate_otg_endpoint_budget(new_config.msd.enabled)?;
|
||||
|
||||
// Apply the validated config
|
||||
state.config.set(new_config.clone()).await?;
|
||||
@@ -910,232 +864,76 @@ pub async fn update_config(
|
||||
// Get new config for device reloading
|
||||
let new_config = state.config.get();
|
||||
|
||||
// Video config processing - always reload if section was sent
|
||||
if has_video {
|
||||
tracing::info!("Video config sent, applying settings...");
|
||||
|
||||
let device = new_config
|
||||
.video
|
||||
.device
|
||||
.clone()
|
||||
.ok_or_else(|| AppError::BadRequest("video_device is required".to_string()))?;
|
||||
|
||||
// Map to PixelFormat/Resolution
|
||||
let format = new_config
|
||||
.video
|
||||
.format
|
||||
.as_ref()
|
||||
.and_then(|f| {
|
||||
serde_json::from_value::<crate::video::format::PixelFormat>(
|
||||
serde_json::Value::String(f.clone()),
|
||||
)
|
||||
.ok()
|
||||
})
|
||||
.unwrap_or(crate::video::format::PixelFormat::Mjpeg);
|
||||
let resolution =
|
||||
crate::video::format::Resolution::new(new_config.video.width, new_config.video.height);
|
||||
|
||||
if let Err(e) = state
|
||||
.stream_manager
|
||||
.apply_video_config(&device, format, resolution, new_config.video.fps)
|
||||
.await
|
||||
if let Err(e) =
|
||||
config::apply::apply_video_config(&state, &old_config.video, &new_config.video).await
|
||||
{
|
||||
tracing::error!("Failed to apply video config: {}", e);
|
||||
// Rollback config on failure
|
||||
state.config.set((*old_config).clone()).await?;
|
||||
return Ok(Json(LoginResponse {
|
||||
success: false,
|
||||
message: Some(format!("Video configuration invalid: {}", e)),
|
||||
}));
|
||||
}
|
||||
tracing::info!("Video config applied successfully");
|
||||
}
|
||||
|
||||
// Stream config processing (encoder backend, bitrate, etc.)
|
||||
if has_stream {
|
||||
tracing::info!("Stream config sent, applying encoder settings...");
|
||||
|
||||
// Update WebRTC streamer encoder backend
|
||||
let encoder_backend = new_config.stream.encoder.to_backend();
|
||||
tracing::info!(
|
||||
"Updating encoder backend to: {:?} (from config: {:?})",
|
||||
encoder_backend,
|
||||
new_config.stream.encoder
|
||||
);
|
||||
|
||||
state
|
||||
.stream_manager
|
||||
.webrtc_streamer()
|
||||
.update_encoder_backend(encoder_backend)
|
||||
.await;
|
||||
|
||||
// Update bitrate if changed
|
||||
state
|
||||
.stream_manager
|
||||
.webrtc_streamer()
|
||||
.set_bitrate_preset(new_config.stream.bitrate_preset)
|
||||
.await
|
||||
.ok(); // Ignore error if no active stream
|
||||
|
||||
tracing::info!(
|
||||
"Stream config applied: encoder={:?}, bitrate={}",
|
||||
new_config.stream.encoder,
|
||||
new_config.stream.bitrate_preset
|
||||
);
|
||||
if let Err(e) =
|
||||
config::apply::apply_stream_config(&state, &old_config.stream, &new_config.stream).await
|
||||
{
|
||||
tracing::error!("Failed to apply stream config: {}", e);
|
||||
state.config.set((*old_config).clone()).await?;
|
||||
return Ok(Json(LoginResponse {
|
||||
success: false,
|
||||
message: Some(format!("Stream configuration invalid: {}", e)),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// HID config processing - always reload if section was sent
|
||||
if has_hid {
|
||||
tracing::info!("HID config sent, reloading HID backend...");
|
||||
|
||||
// Determine new backend type
|
||||
let new_hid_backend = match new_config.hid.backend {
|
||||
crate::config::HidBackend::Otg => crate::hid::HidBackendType::Otg,
|
||||
crate::config::HidBackend::Ch9329 => crate::hid::HidBackendType::Ch9329 {
|
||||
port: new_config.hid.ch9329_port.clone(),
|
||||
baud_rate: new_config.hid.ch9329_baudrate,
|
||||
},
|
||||
crate::config::HidBackend::None => crate::hid::HidBackendType::None,
|
||||
};
|
||||
|
||||
// Reload HID backend - return success=false on error
|
||||
if let Err(e) = state.hid.reload(new_hid_backend).await {
|
||||
if let Err(e) =
|
||||
config::apply::apply_hid_config(&state, &old_config.hid, &new_config.hid).await
|
||||
{
|
||||
tracing::error!("HID reload failed: {}", e);
|
||||
// Rollback config on failure
|
||||
state.config.set((*old_config).clone()).await?;
|
||||
return Ok(Json(LoginResponse {
|
||||
success: false,
|
||||
message: Some(format!("HID configuration invalid: {}", e)),
|
||||
}));
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
"HID backend reloaded successfully: {:?}",
|
||||
new_config.hid.backend
|
||||
);
|
||||
}
|
||||
|
||||
// Audio config processing - always reload if section was sent
|
||||
if has_audio {
|
||||
tracing::info!("Audio config sent, applying settings...");
|
||||
|
||||
// Create audio controller config from new config
|
||||
let audio_config = crate::audio::AudioControllerConfig {
|
||||
enabled: new_config.audio.enabled,
|
||||
device: new_config.audio.device.clone(),
|
||||
quality: crate::audio::AudioQuality::from_str(&new_config.audio.quality),
|
||||
};
|
||||
|
||||
// Update audio controller
|
||||
if let Err(e) = state.audio.update_config(audio_config).await {
|
||||
tracing::error!("Audio config update failed: {}", e);
|
||||
// Don't rollback config for audio errors - it's not critical
|
||||
// Just log the error
|
||||
} else {
|
||||
tracing::info!(
|
||||
"Audio config applied: enabled={}, device={}",
|
||||
new_config.audio.enabled,
|
||||
new_config.audio.device
|
||||
);
|
||||
}
|
||||
|
||||
// Also update WebRTC audio enabled state
|
||||
if let Err(e) = state
|
||||
.stream_manager
|
||||
.set_webrtc_audio_enabled(new_config.audio.enabled)
|
||||
.await
|
||||
if let Err(e) =
|
||||
config::apply::apply_audio_config(&state, &old_config.audio, &new_config.audio).await
|
||||
{
|
||||
tracing::warn!("Failed to update WebRTC audio state: {}", e);
|
||||
} else {
|
||||
tracing::info!("WebRTC audio enabled: {}", new_config.audio.enabled);
|
||||
}
|
||||
|
||||
// Reconnect audio sources for existing WebRTC sessions
|
||||
// This is needed because the audio controller was restarted with new config
|
||||
if new_config.audio.enabled {
|
||||
state.stream_manager.reconnect_webrtc_audio_sources().await;
|
||||
tracing::warn!("Audio config update failed: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// MSD config processing - reload if enabled state or directory changed
|
||||
if has_msd {
|
||||
tracing::info!("MSD config sent, checking if reload needed...");
|
||||
tracing::debug!("Old MSD config: {:?}", old_config.msd);
|
||||
tracing::debug!("New MSD config: {:?}", new_config.msd);
|
||||
|
||||
let old_msd_enabled = old_config.msd.enabled;
|
||||
let new_msd_enabled = new_config.msd.enabled;
|
||||
let msd_dir_changed = old_config.msd.msd_dir != new_config.msd.msd_dir;
|
||||
|
||||
tracing::info!(
|
||||
"MSD enabled: old={}, new={}",
|
||||
old_msd_enabled,
|
||||
new_msd_enabled
|
||||
);
|
||||
if msd_dir_changed {
|
||||
tracing::info!("MSD directory changed: {}", new_config.msd.msd_dir);
|
||||
if let Err(e) =
|
||||
config::apply::apply_msd_config(&state, &old_config.msd, &new_config.msd).await
|
||||
{
|
||||
tracing::error!("MSD initialization failed: {}", e);
|
||||
state.config.set((*old_config).clone()).await?;
|
||||
return Ok(Json(LoginResponse {
|
||||
success: false,
|
||||
message: Some(format!("MSD initialization failed: {}", e)),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure MSD directories exist (msd/images, msd/ventoy)
|
||||
let msd_dir = new_config.msd.msd_dir_path();
|
||||
if let Err(e) = std::fs::create_dir_all(msd_dir.join("images")) {
|
||||
tracing::warn!("Failed to create MSD images directory: {}", e);
|
||||
}
|
||||
if let Err(e) = std::fs::create_dir_all(msd_dir.join("ventoy")) {
|
||||
tracing::warn!("Failed to create MSD ventoy directory: {}", e);
|
||||
}
|
||||
|
||||
let needs_reload = old_msd_enabled != new_msd_enabled || msd_dir_changed;
|
||||
if !needs_reload {
|
||||
tracing::info!(
|
||||
"MSD enabled state unchanged ({}) and directory unchanged, no reload needed",
|
||||
new_msd_enabled
|
||||
);
|
||||
} else if new_msd_enabled {
|
||||
tracing::info!("(Re)initializing MSD...");
|
||||
|
||||
// Shutdown existing controller if present
|
||||
let mut msd_guard = state.msd.write().await;
|
||||
if let Some(msd) = msd_guard.as_mut() {
|
||||
if let Err(e) = msd.shutdown().await {
|
||||
tracing::warn!("MSD shutdown failed: {}", e);
|
||||
}
|
||||
}
|
||||
*msd_guard = None;
|
||||
drop(msd_guard);
|
||||
|
||||
let msd = crate::msd::MsdController::new(
|
||||
state.otg_service.clone(),
|
||||
new_config.msd.msd_dir_path(),
|
||||
);
|
||||
if let Err(e) = msd.init().await {
|
||||
tracing::error!("MSD initialization failed: {}", e);
|
||||
// Rollback config on failure
|
||||
state.config.set((*old_config).clone()).await?;
|
||||
return Ok(Json(LoginResponse {
|
||||
success: false,
|
||||
message: Some(format!("MSD initialization failed: {}", e)),
|
||||
}));
|
||||
}
|
||||
|
||||
// Set event bus
|
||||
let events = state.events.clone();
|
||||
msd.set_event_bus(events).await;
|
||||
|
||||
// Store the initialized controller
|
||||
*state.msd.write().await = Some(msd);
|
||||
tracing::info!("MSD initialized successfully");
|
||||
} else {
|
||||
tracing::info!("MSD disabled in config, shutting down...");
|
||||
|
||||
let mut msd_guard = state.msd.write().await;
|
||||
if let Some(msd) = msd_guard.as_mut() {
|
||||
if let Err(e) = msd.shutdown().await {
|
||||
tracing::warn!("MSD shutdown failed: {}", e);
|
||||
}
|
||||
}
|
||||
*msd_guard = None;
|
||||
tracing::info!("MSD shutdown complete");
|
||||
if has_atx {
|
||||
if let Err(e) =
|
||||
config::apply::apply_atx_config(&state, &old_config.atx, &new_config.atx).await
|
||||
{
|
||||
tracing::error!("ATX configuration invalid: {}", e);
|
||||
state.config.set((*old_config).clone()).await?;
|
||||
return Ok(Json(LoginResponse {
|
||||
success: false,
|
||||
message: Some(format!("ATX configuration invalid: {}", e)),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2247,6 +2045,8 @@ pub struct HidStatus {
|
||||
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>,
|
||||
@@ -3018,6 +2818,8 @@ pub async fn hid_status(State(state): State<Arc<AppState>>) -> Json<HidStatus> {
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user