Skip to content

Commit

Permalink
Chonky Firmware Update Update :D
Browse files Browse the repository at this point in the history
  • Loading branch information
FrostyCoolSlug committed Nov 24, 2024
1 parent 71a61dd commit 5e4a0f2
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 80 deletions.
7 changes: 1 addition & 6 deletions daemon/src/firmware/firmware_file.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
use anyhow::{bail, Result};
use byteorder::{LittleEndian, ReadBytesExt};
use goxlr_ipc::FirmwareInfo;
use goxlr_types::{DeviceType, VersionNumber};
use std::io;
use std::io::Cursor;
use std::path::PathBuf;

pub struct FirmwareInfo {
pub path: PathBuf,
pub device_type: DeviceType,
pub version: VersionNumber,
}

pub fn check_firmware(path: &PathBuf) -> Result<FirmwareInfo> {
load_firmware_file(path)
}
Expand Down
41 changes: 25 additions & 16 deletions daemon/src/firmware/firmware_update.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::firmware::firmware_file::{check_firmware, FirmwareInfo};
use crate::firmware::firmware_file::check_firmware;
use crate::FIRMWARE_BASE;
use anyhow::{bail, Result};
use futures_util::StreamExt;
use goxlr_ipc::UpdateState;
use goxlr_ipc::{FirmwareInfo, UpdateState};
use goxlr_types::{DeviceType, VersionNumber};
use log::{error, info};
use reqwest::Client;
Expand All @@ -22,21 +22,25 @@ type OneShot<T> = oneshot::Sender<T>;
const FAIL_BACK_FULL_FIRMWARE: &str = "GoXLR_Firmware.bin";
const FAIL_BACK_MINI_FIRMWARE: &str = "GoXLR_MINI_Firmware.bin";

#[derive(Clone)]
pub struct FirmwareUpdateSettings {
pub sender: Sender,
pub device: FirmwareUpdateDevice,
pub file: Option<PathBuf>,
pub force: bool,
}

#[derive(Clone)]
pub struct FirmwareUpdateDevice {
pub serial: String,
pub device_type: DeviceType,
pub current_firmware: VersionNumber,
}
pub async fn do_firmware_update(settings: FirmwareUpdateSettings) {

pub async fn start_firmware_update(settings: FirmwareUpdateSettings) {
info!("Beginning Firmware Update...");
let sender = settings.sender.clone();
let device = settings.device;
let device = settings.device.clone();

if let Err(e) = set_update_state(&device.serial, sender.clone(), UpdateState::Starting).await {
error!("Something's gone horribly wrong: {}", e);
Expand Down Expand Up @@ -69,19 +73,24 @@ pub async fn do_firmware_update(settings: FirmwareUpdateSettings) {
return;
}

if file_info.version <= device.current_firmware && settings.file.is_none() {
// We're apparently downloaded this file, and it would cause a downgrade / reinstall
// which is unexpected. Bail out instead of proceeding.
let which = if file_info.version < device.current_firmware {
"older"
} else {
"the same"
};

let error = format!("Downloaded file is {} as current firmware.", which);
send_error(&device.serial, sender, error).await;
return;
// So we go either one of two ways here, if there's a problem, we set the update as 'Paused' with
// the file_info, and the UI can then send a 'Continue' if the user is happy with the info, otherwise
// we just go with the update.
if file_info.version <= device.current_firmware {
set_update_state(
&device.serial,
sender.clone(),
UpdateState::Pause(file_info),
)
.await;
} else {
do_firmware_update(settings, file_info).await;
}
}

pub async fn do_firmware_update(settings: FirmwareUpdateSettings, file_info: FirmwareInfo) {
let sender = settings.sender.clone();
let device = settings.device;

// Ok, when we get here we should be good to go, grab the firmware bytes from disk..
let firmware = match fs::read(file_info.path) {
Expand Down
120 changes: 68 additions & 52 deletions daemon/src/primary_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::device::Device;
use crate::events::EventTriggers;
use crate::files::extract_defaults;
use crate::firmware::firmware_update::{
do_firmware_update, FirmwareMessages, FirmwareRequest, FirmwareUpdateDevice,
FirmwareUpdateSettings,
do_firmware_update, start_firmware_update, FirmwareMessages, FirmwareRequest,
FirmwareUpdateDevice, FirmwareUpdateSettings,
};
use crate::platform::{get_ui_app_path, has_autostart, set_autostart};
use crate::{
Expand Down Expand Up @@ -42,7 +42,8 @@ pub enum DeviceCommand {
RunDaemonCommand(DaemonCommand, oneshot::Sender<Result<()>>),
RunDeviceCommand(String, GoXLRCommand, oneshot::Sender<Result<()>>),
GetDeviceMicLevel(String, oneshot::Sender<Result<f64>>),
RunFirmwareUpdate(String, Option<PathBuf>, oneshot::Sender<Result<()>>),
RunFirmwareUpdate(String, Option<PathBuf>, bool, oneshot::Sender<Result<()>>),
ContinueFirmwareUpdate(String, oneshot::Sender<Result<()>>),
ClearFirmwareState(String, oneshot::Sender<Result<()>>),
}

Expand All @@ -53,6 +54,12 @@ pub enum DeviceStateChange {
Wake(oneshot::Sender<()>),
}

#[derive(Clone)]
struct FirmwareUpdateState {
settings: FirmwareUpdateSettings,
status: FirmwareStatus,
}

pub type DeviceSender = Sender<DeviceCommand>;
pub type DeviceReceiver = Receiver<DeviceCommand>;

Expand Down Expand Up @@ -107,7 +114,7 @@ pub async fn spawn_usb_handler(

// Create the Primary Device List, and 'Ignore' list..
let mut devices: HashMap<String, Device> = HashMap::new();
let mut devices_firmware: HashMap<String, Option<FirmwareStatus>> = HashMap::new();
let mut devices_firmware: HashMap<String, FirmwareUpdateState> = HashMap::new();
let mut ignore_list = HashMap::new();

let mut files = get_files(&mut file_manager, &settings).await;
Expand All @@ -129,57 +136,46 @@ pub async fn spawn_usb_handler(
let mut change_found = false;
tokio::select! {
Some(version) = firmware_receiver.recv() => {
// Uncomment this for testing purposes!
// use enum_map::enum_map;
// let version = enum_map! {
// DeviceType::Mini => {
// Some(VersionNumber::from(String::from("0.0.0.0")))
// },
// DeviceType::Full => {
// Some(VersionNumber::from(String::from("0.0.0.0")))
// },
// DeviceType::Unknown => {
// Some(VersionNumber::from(String::from("0.0.0.0")))
// }
// };

firmware_version = Some(version);
change_found = true;
},
Some(received) = firmware_update_receiver.recv() => {
match received {
FirmwareRequest::SetUpdateState(serial,state) => {
if let Some(Some(status)) = devices_firmware.get_mut(&serial) {
status.state = state;
status.progress = 0;
FirmwareRequest::SetUpdateState(serial,status) => {
if let Some(state) = devices_firmware.get_mut(&serial) {
state.status.state = status;
state.status.progress = 0;
change_found = true;
} else {
// We don't have a state for this device, set one.
let state = FirmwareStatus {
state,
let status = FirmwareStatus {
state: status,
progress: 0,
error: None
};

// Only create this if the serial is present..
if devices.contains_key(&serial) {
devices_firmware.insert(serial, Some(state));
// Get the current status..
let state = devices_firmware.get_mut(&serial).unwrap();
state.status = status;

change_found = true;
}
}
}
FirmwareRequest::SetStateProgress(serial, progress) => {
if let Some(Some(status)) = devices_firmware.get_mut(&serial) {
status.progress = progress;
if let Some(state) = devices_firmware.get_mut(&serial) {
state.status.progress = progress;
change_found = true;
} else {
error!("Update State does not exist! Ignoring.");
}
}
FirmwareRequest::SetError(serial, error) => {
debug!("Setting Error: {}", error);
if let Some(Some(status)) = devices_firmware.get_mut(&serial) {
status.error = Some(error);
if let Some(state) = devices_firmware.get_mut(&serial) {
state.status.error = Some(error);
change_found = true;
} else {
error!("Update State does not exist! Ignoring..");
Expand Down Expand Up @@ -245,7 +241,6 @@ pub async fn spawn_usb_handler(
let serial = String::from(device.serial());

devices.insert(serial.clone(), device);
devices_firmware.insert(serial, None);
change_found = true;
}
Err(e) => {
Expand Down Expand Up @@ -283,9 +278,16 @@ pub async fn spawn_usb_handler(
Some(serial) = disconnect_receiver.recv() => {
info!("[{}] Device Disconnected", serial);
devices.remove(&serial);
if devices_firmware.contains_key(&serial) && devices_firmware.get(&serial).unwrap().is_none() {
// Only remove if we have no status (prevents the reboot from clearing the state)
devices_firmware.remove(&serial);

// If this device was actively doing a firmware update that's not complete, we should scream
// INCREDIBLY loudly (in the logs).. We will keep this device around though, the error will
// be handled by the firmware updater, and presented accordingly.
if devices_firmware.contains_key(&serial) {
let state = devices_firmware.get(&serial).unwrap();
match state.status.state {
UpdateState::Failed | UpdateState::Pause(_) | UpdateState::Complete => todo!(),
_ => warn!("DEVICE REMOVED BEFORE UPDATE COMPLETE")
}
}
change_found = true;
},
Expand Down Expand Up @@ -476,7 +478,7 @@ pub async fn spawn_usb_handler(
}
},

DeviceCommand::RunFirmwareUpdate(serial, file, sender) => {
DeviceCommand::RunFirmwareUpdate(serial, file, force, sender) => {
if let Some(device) = devices.get_mut(&serial) {
let device_type = device.get_hardware_type();
let current_firmware = device.get_firmware_version();
Expand All @@ -490,32 +492,44 @@ pub async fn spawn_usb_handler(
current_firmware,
},
file,
force,
};
tokio::spawn(do_firmware_update(update_settings));
tokio::spawn(start_firmware_update(update_settings));
let _ = sender.send(Ok(()));
} else {
let _ = sender.send(Err(anyhow!("Device {} is not connected", serial)));
}
},
DeviceCommand::ContinueFirmwareUpdate(serial, sender) => {
if let Some(state) = devices_firmware.get(&serial) {
match &state.status.state {
UpdateState::Pause(file_info) => {
tokio::spawn(do_firmware_update(state.settings.clone(), file_info.to_owned()));
let _ = sender.send(Ok(()));
}
_ => {
let _ = sender.send(Err(anyhow!("Update not in Paused State!")));
}
}
} else {
let _ = sender.send(Err(anyhow!("Devite not performing firmware update.")));
}
},

DeviceCommand::ClearFirmwareState(serial, sender) => {
if let Some(device) = devices_firmware.get_mut(&serial) {
if let Some(status) = device {
if status.state != UpdateState::Complete && status.state != UpdateState::Failed {
let _ = sender.send(Err(anyhow!("Cannot Clear, update in progress")));
} else {
device.take();

if !devices.contains_key(&serial) {
// If the device is no longer attached, remove it.
devices.remove(&serial);
}
if let Some(device) = devices_firmware.get(&serial) {
match device.status.state {
UpdateState::Complete | UpdateState::Failed | UpdateState::Pause(_) => {
// We're at a point where this update can be stopped, nuke it from the firmware list..
devices_firmware.remove(&serial);
},
_ => {
// Can't stop it just yet.. :D
let _ = sender.send(Err(anyhow!("Cannot Clear, update in progress..")));
}
} else {
let _ = sender.send(Err(anyhow!("Device not performing firmware update")));
}
};
} else {
let _ = sender.send(Err(anyhow!("Device not Present")));
let _ = sender.send(Err(anyhow!("Device not performing firmware update.")));
}
}
}
Expand Down Expand Up @@ -571,7 +585,7 @@ async fn get_daemon_status(
http_settings: &HttpSettings,
driver_details: &DriverDetails,
firmware_versions: &Option<EnumMap<DeviceType, Option<VersionNumber>>>,
firmware_state: &HashMap<String, Option<FirmwareStatus>>,
firmware_state: &HashMap<String, FirmwareUpdateState>,
files: Files,
app_check: &Option<String>,
) -> DaemonStatus {
Expand Down Expand Up @@ -609,7 +623,9 @@ async fn get_daemon_status(
};

for (serial, state) in firmware_state {
status.firmware.insert(serial.to_owned(), state.clone());
status
.firmware
.insert(serial.to_owned(), state.status.clone());
}

for (serial, device) in devices {
Expand Down
4 changes: 2 additions & 2 deletions daemon/src/servers/server_packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ pub async fn handle_packet(
Ok(DaemonResponse::Ok)
}

DaemonRequest::RunFirmwareUpdate(serial, path) => {
DaemonRequest::RunFirmwareUpdate(serial, path, force) => {
let (tx, rx) = oneshot::channel();
let path = path.map(PathBuf::from);
usb_tx
.send(DeviceCommand::RunFirmwareUpdate(serial, path, tx))
.send(DeviceCommand::RunFirmwareUpdate(serial, path, force, tx))
.await
.map_err(anyhow::Error::msg)?;
rx.await
Expand Down
2 changes: 1 addition & 1 deletion ipc/src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::path::PathBuf;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct DaemonStatus {
pub config: DaemonConfig,
pub firmware: HashMap<String, Option<FirmwareStatus>>,
pub firmware: HashMap<String, FirmwareStatus>,
pub mixers: HashMap<String, MixerStatus>,
pub paths: Paths,
pub files: Files,
Expand Down
14 changes: 11 additions & 3 deletions ipc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ mod device;
pub use device::*;
use goxlr_types::{
AnimationMode, Button, ButtonColourGroups, ButtonColourOffStyle, ChannelName,
CompressorAttackTime, CompressorRatio, CompressorReleaseTime, DisplayMode,
CompressorAttackTime, CompressorRatio, CompressorReleaseTime, DeviceType, DisplayMode,
DisplayModeComponents, EchoStyle, EffectBankPresets, EncoderColourTargets, EqFrequencies,
FaderDisplayStyle, FaderName, GateTimes, GenderStyle, HardTuneSource, HardTuneStyle,
InputDevice, MegaphoneStyle, MicrophoneType, MiniEqFrequencies, Mix, MuteFunction, MuteState,
OutputDevice, PitchStyle, ReverbStyle, RobotRange, RobotStyle, SampleBank, SampleButtons,
SamplePlayOrder, SamplePlaybackMode, SamplerColourTargets, SimpleColourTargets,
SamplePlayOrder, SamplePlaybackMode, SamplerColourTargets, SimpleColourTargets, VersionNumber,
WaterfallDirection,
};

Expand All @@ -25,7 +25,7 @@ pub enum DaemonRequest {
Daemon(DaemonCommand),
GetMicLevel(String),
Command(String, GoXLRCommand),
RunFirmwareUpdate(String, Option<String>),
RunFirmwareUpdate(String, Option<String>, bool),
ClearFirmwareState(String),
}

Expand Down Expand Up @@ -74,6 +74,7 @@ pub enum UpdateState {
Starting,
Manifest,
Download,
Pause(FirmwareInfo),
ClearNVR,
UploadFirmware,
ValidateUpload,
Expand All @@ -82,6 +83,13 @@ pub enum UpdateState {
Complete,
}

#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct FirmwareInfo {
pub path: PathBuf,
pub device_type: DeviceType,
pub version: VersionNumber,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, Eq, PartialEq)]
pub enum LogLevel {
Off,
Expand Down

0 comments on commit 5e4a0f2

Please sign in to comment.