Add support for firmware update
This commit is contained in:
@@ -26,7 +26,7 @@ CFLAGS += $(shell $(PKG_CONFIG) --cflags alsa)
|
||||
LDFLAGS += $(shell $(PKG_CONFIG) --libs glib-2.0)
|
||||
LDFLAGS += $(shell $(PKG_CONFIG) --libs gtk4)
|
||||
LDFLAGS += $(shell $(PKG_CONFIG) --libs alsa)
|
||||
LDFLAGS += -lm
|
||||
LDFLAGS += -lm -lcrypto
|
||||
|
||||
COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) -c
|
||||
|
||||
|
||||
@@ -14,6 +14,16 @@
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
/* Title of the window */
|
||||
.window-title {
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
.linktext {
|
||||
color: #89CFF0;
|
||||
}
|
||||
|
||||
/* Label above controls-content */
|
||||
.controls-label {
|
||||
font-size: smaller;
|
||||
|
||||
140
src/device-update-firmware.c
Normal file
140
src/device-update-firmware.c
Normal file
@@ -0,0 +1,140 @@
|
||||
// SPDX-FileCopyrightText: 2024 Geoffrey D. Bennett <g@b4.vu>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include "device-reset-config.h"
|
||||
#include "scarlett2.h"
|
||||
#include "scarlett2-firmware.h"
|
||||
#include "scarlett2-ioctls.h"
|
||||
#include "window-modal.h"
|
||||
|
||||
static gpointer update_progress(
|
||||
struct modal_data *modal_data,
|
||||
char *text,
|
||||
int progress
|
||||
) {
|
||||
struct progress_data *progress_data = g_new0(struct progress_data, 1);
|
||||
progress_data->modal_data = modal_data;
|
||||
progress_data->text = text;
|
||||
progress_data->progress = progress;
|
||||
|
||||
g_main_context_invoke(NULL, modal_update_progress, progress_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define fail(msg) { \
|
||||
if (hwdep) \
|
||||
scarlett2_close(hwdep); \
|
||||
if (firmware) \
|
||||
scarlett2_free_firmware_file(firmware); \
|
||||
return update_progress(modal_data, msg, -1); \
|
||||
}
|
||||
|
||||
#define failsndmsg(msg) g_strdup_printf(msg, snd_strerror(err))
|
||||
|
||||
gpointer update_firmware_thread(gpointer user_data) {
|
||||
struct modal_data *modal_data = user_data;
|
||||
struct alsa_card *card = modal_data->card;
|
||||
|
||||
int err = 0;
|
||||
snd_hwdep_t *hwdep = NULL;
|
||||
|
||||
// read the firmware file
|
||||
update_progress(modal_data, g_strdup("Checking firmware..."), 0);
|
||||
struct scarlett2_firmware_file *firmware =
|
||||
scarlett2_get_best_firmware(card->pid);
|
||||
|
||||
// if no firmware, fail
|
||||
if (!firmware)
|
||||
fail(failsndmsg("No update firmware found for device: %s"));
|
||||
|
||||
if (firmware->header.usb_pid != card->pid)
|
||||
fail(g_strdup("Firmware file does not match device"));
|
||||
|
||||
update_progress(modal_data, g_strdup("Resetting configuration..."), 0);
|
||||
|
||||
err = scarlett2_open_card(card->device, &hwdep);
|
||||
if (err < 0)
|
||||
fail(failsndmsg("Unable to open hwdep interface: %s"));
|
||||
|
||||
err = scarlett2_erase_config(hwdep);
|
||||
if (err < 0)
|
||||
fail(failsndmsg("Unable to reset configuration: %s"));
|
||||
|
||||
while (1) {
|
||||
g_usleep(50000);
|
||||
|
||||
err = scarlett2_get_erase_progress(hwdep);
|
||||
if (err < 0)
|
||||
fail(failsndmsg("Unable to get erase progress: %s"));
|
||||
if (err == 255)
|
||||
break;
|
||||
|
||||
update_progress(modal_data, NULL, err);
|
||||
}
|
||||
|
||||
update_progress(modal_data, g_strdup("Erasing flash..."), 0);
|
||||
|
||||
err = scarlett2_erase_firmware(hwdep);
|
||||
if (err < 0)
|
||||
fail(failsndmsg("Unable to erase upgrade firmware: %s"));
|
||||
|
||||
while (1) {
|
||||
g_usleep(50000);
|
||||
|
||||
err = scarlett2_get_erase_progress(hwdep);
|
||||
if (err < 0)
|
||||
fail(failsndmsg("Unable to get erase progress: %s"));
|
||||
if (err == 255)
|
||||
break;
|
||||
|
||||
update_progress(modal_data, NULL, err);
|
||||
}
|
||||
|
||||
update_progress(modal_data, g_strdup("Writing firmware..."), 0);
|
||||
|
||||
size_t offset = 0;
|
||||
size_t len = firmware->header.firmware_length;
|
||||
unsigned char *buf = firmware->firmware_data;
|
||||
|
||||
while (offset < len) {
|
||||
err = snd_hwdep_write(hwdep, buf + offset, len - offset);
|
||||
if (err < 0)
|
||||
fail(failsndmsg("Unable to write firmware: %s"));
|
||||
|
||||
offset += err;
|
||||
|
||||
update_progress(modal_data, NULL, (offset * 100) / len);
|
||||
}
|
||||
|
||||
g_main_context_invoke(NULL, modal_start_reboot_progress, modal_data);
|
||||
scarlett2_reboot(hwdep);
|
||||
scarlett2_close(hwdep);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void join_thread(gpointer thread) {
|
||||
g_thread_join(thread);
|
||||
}
|
||||
|
||||
static void update_firmware_yes_callback(struct modal_data *modal_data) {
|
||||
GThread *thread = g_thread_new(
|
||||
"update_firmware_thread", update_firmware_thread, modal_data
|
||||
);
|
||||
g_object_set_data_full(
|
||||
G_OBJECT(modal_data->button_box), "thread", thread, join_thread
|
||||
);
|
||||
}
|
||||
|
||||
void create_update_firmware_window(GtkWidget *w, struct alsa_card *card) {
|
||||
create_modal_window(
|
||||
w, card,
|
||||
"Confirm Update Firmware",
|
||||
"Updating Firmware",
|
||||
"The firmware update process will take about 15 seconds.\n"
|
||||
"Please do not disconnect the device while updating.\n"
|
||||
"Ready to proceed?",
|
||||
update_firmware_yes_callback
|
||||
);
|
||||
}
|
||||
9
src/device-update-firmware.h
Normal file
9
src/device-update-firmware.h
Normal file
@@ -0,0 +1,9 @@
|
||||
// SPDX-FileCopyrightText: 2024 Geoffrey D. Bennett <g@b4.vu>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include "alsa.h"
|
||||
|
||||
void create_update_firmware_window(GtkWidget *w, struct alsa_card *card);
|
||||
36
src/hardware.c
Normal file
36
src/hardware.c
Normal file
@@ -0,0 +1,36 @@
|
||||
// SPDX-FileCopyrightText: 2023-2024 Geoffrey D. Bennett <g@b4.vu>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "hardware.h"
|
||||
|
||||
struct scarlett2_device scarlett2_supported[] = {
|
||||
{ 0x8203, "Scarlett 2nd Gen 6i6" },
|
||||
{ 0x8204, "Scarlett 2nd Gen 18i8" },
|
||||
{ 0x8201, "Scarlett 2nd Gen 18i20" },
|
||||
{ 0x8211, "Scarlett 3rd Gen Solo" },
|
||||
{ 0x8210, "Scarlett 3rd Gen 2i2" },
|
||||
{ 0x8212, "Scarlett 3rd Gen 4i4" },
|
||||
{ 0x8213, "Scarlett 3rd Gen 8i6" },
|
||||
{ 0x8214, "Scarlett 3rd Gen 18i8" },
|
||||
{ 0x8215, "Scarlett 3rd Gen 18i20" },
|
||||
{ 0x8218, "Scarlett 4th Gen Solo" },
|
||||
{ 0x8219, "Scarlett 4th Gen 2i2" },
|
||||
{ 0x821a, "Scarlett 4th Gen 4i4" },
|
||||
{ 0x8206, "Clarett USB 2Pre" },
|
||||
{ 0x8207, "Clarett USB 4Pre" },
|
||||
{ 0x8208, "Clarett USB 8Pre" },
|
||||
{ 0x820a, "Clarett+ 2Pre" },
|
||||
{ 0x820b, "Clarett+ 4Pre" },
|
||||
{ 0x820c, "Clarett+ 8Pre" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
struct scarlett2_device *get_device_for_pid(int pid) {
|
||||
for (int i = 0; scarlett2_supported[i].name; i++)
|
||||
if (scarlett2_supported[i].pid == pid)
|
||||
return &scarlett2_supported[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
10
src/hardware.h
Normal file
10
src/hardware.h
Normal file
@@ -0,0 +1,10 @@
|
||||
// SPDX-FileCopyrightText: 2023-2024 Geoffrey D. Bennett <g@b4.vu>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// Supported devices
|
||||
struct scarlett2_device {
|
||||
int pid;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct scarlett2_device *get_device_for_pid(int pid);
|
||||
62
src/iface-update.c
Normal file
62
src/iface-update.c
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-FileCopyrightText: 2024 Geoffrey D. Bennett <g@b4.vu>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "alsa.h"
|
||||
#include "device-update-firmware.h"
|
||||
#include "gtkhelper.h"
|
||||
#include "scarlett2-firmware.h"
|
||||
|
||||
GtkWidget *create_iface_update_main(struct alsa_card *card) {
|
||||
GtkWidget *top = gtk_frame_new(NULL);
|
||||
gtk_widget_add_css_class(top, "window-frame");
|
||||
|
||||
GtkWidget *content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 30);
|
||||
gtk_widget_add_css_class(content, "window-content");
|
||||
gtk_widget_add_css_class(content, "top-level-content");
|
||||
gtk_widget_add_css_class(content, "big-padding");
|
||||
gtk_frame_set_child(GTK_FRAME(top), content);
|
||||
|
||||
// explanation
|
||||
GtkWidget *w;
|
||||
|
||||
w = gtk_label_new("Firmware Update Required");
|
||||
gtk_widget_add_css_class(w, "window-title");
|
||||
gtk_box_append(GTK_BOX(content), w);
|
||||
|
||||
uint32_t best_firmware_version =
|
||||
scarlett2_get_best_firmware_version(card->pid);
|
||||
|
||||
if (!best_firmware_version) {
|
||||
w = gtk_label_new(NULL);
|
||||
gtk_label_set_markup(
|
||||
GTK_LABEL(w),
|
||||
"A firmware update is required for this device in order to\n"
|
||||
"access all of its features. Please obtain the firmware from\n"
|
||||
"<a class=\"linktext\" "
|
||||
"href=\"https://github.com/geoffreybennett/scarlett2-firmware\">"
|
||||
"https://github.com/geoffreybennett/scarlett2-firmware</a>,\n"
|
||||
"and restart this application."
|
||||
);
|
||||
|
||||
gtk_box_append(GTK_BOX(content), w);
|
||||
return top;
|
||||
}
|
||||
|
||||
w = gtk_label_new(
|
||||
"A firmware update is required for this device in order to\n"
|
||||
"access all of its features. This process will take about 15\n"
|
||||
"seconds. Please do not disconnect the device during the\n"
|
||||
"update."
|
||||
);
|
||||
gtk_box_append(GTK_BOX(content), w);
|
||||
|
||||
w = gtk_button_new_with_label("Update");
|
||||
g_signal_connect(
|
||||
GTK_BUTTON(w), "clicked", G_CALLBACK(create_update_firmware_window), card
|
||||
);
|
||||
gtk_box_append(GTK_BOX(content), w);
|
||||
|
||||
return top;
|
||||
}
|
||||
8
src/iface-update.h
Normal file
8
src/iface-update.h
Normal file
@@ -0,0 +1,8 @@
|
||||
// SPDX-FileCopyrightText: 2024 Geoffrey D. Bennett <g@b4.vu>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "alsa.h"
|
||||
|
||||
GtkWidget *create_iface_update_main(struct alsa_card *card);
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "alsa-sim.h"
|
||||
#include "main.h"
|
||||
#include "menu.h"
|
||||
#include "scarlett2-firmware.h"
|
||||
#include "window-hardware.h"
|
||||
#include "window-iface.h"
|
||||
|
||||
@@ -34,6 +35,7 @@ static void startup(GtkApplication *app, gpointer user_data) {
|
||||
|
||||
load_css();
|
||||
|
||||
scarlett2_enum_firmware();
|
||||
alsa_init();
|
||||
|
||||
create_no_card_window();
|
||||
|
||||
289
src/scarlett2-firmware.c
Normal file
289
src/scarlett2-firmware.c
Normal file
@@ -0,0 +1,289 @@
|
||||
// SPDX-FileCopyrightText: 2023-2024 Geoffrey D. Bennett <g@b4.vu>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "scarlett2-firmware.h"
|
||||
|
||||
// List of found firmware files
|
||||
struct found_firmware {
|
||||
char *fn;
|
||||
struct scarlett2_firmware_header *firmware;
|
||||
};
|
||||
|
||||
GHashTable *best_firmware = NULL;
|
||||
|
||||
static int verify_sha256(
|
||||
const unsigned char *data,
|
||||
size_t length,
|
||||
const unsigned char *expected_hash
|
||||
) {
|
||||
unsigned char computed_hash[SHA256_DIGEST_LENGTH];
|
||||
SHA256(data, length, computed_hash);
|
||||
return memcmp(computed_hash, expected_hash, SHA256_DIGEST_LENGTH) == 0;
|
||||
}
|
||||
|
||||
static struct scarlett2_firmware_file *read_header(FILE *file) {
|
||||
struct scarlett2_firmware_file *firmware = calloc(
|
||||
1, sizeof(struct scarlett2_firmware_file)
|
||||
);
|
||||
if (!firmware) {
|
||||
perror("Failed to allocate memory for firmware structure");
|
||||
goto error;
|
||||
}
|
||||
|
||||
size_t read_count = fread(
|
||||
&firmware->header, sizeof(struct scarlett2_firmware_header), 1, file
|
||||
);
|
||||
|
||||
if (read_count != 1) {
|
||||
if (feof(file))
|
||||
fprintf(stderr, "Unexpected end of file\n");
|
||||
else
|
||||
perror("Failed to read header");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strncmp(firmware->header.magic, MAGIC_STRING, 8) != 0) {
|
||||
fprintf(stderr, "Invalid magic number\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
firmware->header.usb_vid = ntohs(firmware->header.usb_vid);
|
||||
firmware->header.usb_pid = ntohs(firmware->header.usb_pid);
|
||||
firmware->header.firmware_version = ntohl(firmware->header.firmware_version);
|
||||
firmware->header.firmware_length = ntohl(firmware->header.firmware_length);
|
||||
|
||||
return firmware;
|
||||
|
||||
error:
|
||||
free(firmware);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct scarlett2_firmware_header *scarlett2_read_firmware_header(
|
||||
const char *fn
|
||||
) {
|
||||
FILE *file = fopen(fn, "rb");
|
||||
if (!file) {
|
||||
perror("fopen");
|
||||
fprintf(stderr, "Unable to open %s\n", fn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct scarlett2_firmware_file *firmware = read_header(file);
|
||||
if (!firmware) {
|
||||
fprintf(stderr, "Error reading firmware header from %s\n", fn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return realloc(firmware, sizeof(struct scarlett2_firmware_header));
|
||||
}
|
||||
|
||||
struct scarlett2_firmware_file *scarlett2_read_firmware_file(const char *fn) {
|
||||
FILE *file = fopen(fn, "rb");
|
||||
if (!file) {
|
||||
perror("fopen");
|
||||
fprintf(stderr, "Unable to open %s\n", fn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct scarlett2_firmware_file *firmware = read_header(file);
|
||||
if (!firmware) {
|
||||
fprintf(stderr, "Error reading firmware header from %s\n", fn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
firmware->firmware_data = malloc(firmware->header.firmware_length);
|
||||
if (!firmware->firmware_data) {
|
||||
perror("Failed to allocate memory for firmware data");
|
||||
goto error;
|
||||
}
|
||||
|
||||
size_t read_count = fread(
|
||||
firmware->firmware_data, 1, firmware->header.firmware_length, file
|
||||
);
|
||||
|
||||
if (read_count != firmware->header.firmware_length) {
|
||||
if (feof(file))
|
||||
fprintf(stderr, "Unexpected end of file\n");
|
||||
else
|
||||
perror("Failed to read firmware data");
|
||||
fprintf(stderr, "Error reading firmware data from %s\n", fn);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!verify_sha256(
|
||||
firmware->firmware_data,
|
||||
firmware->header.firmware_length,
|
||||
firmware->header.sha256
|
||||
)) {
|
||||
fprintf(stderr, "Corrupt firmware (failed checksum) in %s\n", fn);
|
||||
goto error;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return firmware;
|
||||
|
||||
error:
|
||||
scarlett2_free_firmware_file(firmware);
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void scarlett2_free_firmware_header(struct scarlett2_firmware_header *firmware) {
|
||||
if (firmware)
|
||||
free(firmware);
|
||||
}
|
||||
|
||||
void scarlett2_free_firmware_file(struct scarlett2_firmware_file *firmware) {
|
||||
if (firmware) {
|
||||
free(firmware->firmware_data);
|
||||
free(firmware);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_found_firmware(gpointer data) {
|
||||
struct found_firmware *found = data;
|
||||
|
||||
free(found->fn);
|
||||
scarlett2_free_firmware_header(found->firmware);
|
||||
free(found);
|
||||
}
|
||||
|
||||
static void init_best_firmware(void) {
|
||||
if (best_firmware)
|
||||
return;
|
||||
|
||||
best_firmware = g_hash_table_new_full(
|
||||
g_direct_hash, g_direct_equal, NULL, free_found_firmware
|
||||
);
|
||||
}
|
||||
|
||||
// Add a firmware file to the list of found firmware
|
||||
// files, if it's better than the one already found
|
||||
// for the same device.
|
||||
static void add_found_firmware(
|
||||
char *fn,
|
||||
struct scarlett2_firmware_header *firmware
|
||||
) {
|
||||
gpointer key = GINT_TO_POINTER(firmware->usb_pid);
|
||||
struct found_firmware *found = g_hash_table_lookup(best_firmware, key);
|
||||
|
||||
// already have a firmware file for this device?
|
||||
if (found) {
|
||||
|
||||
// lower version number, ignore
|
||||
if (firmware->firmware_version <= found->firmware->firmware_version) {
|
||||
free(fn);
|
||||
scarlett2_free_firmware_header(firmware);
|
||||
return;
|
||||
}
|
||||
|
||||
// higher version number, replace
|
||||
g_hash_table_remove(best_firmware, key);
|
||||
}
|
||||
|
||||
found = malloc(sizeof(struct found_firmware));
|
||||
if (!found) {
|
||||
perror("Failed to allocate memory for firmware structure");
|
||||
return;
|
||||
}
|
||||
|
||||
found->fn = fn;
|
||||
found->firmware = firmware;
|
||||
|
||||
g_hash_table_insert(best_firmware, key, found);
|
||||
}
|
||||
|
||||
// look for firmware files in the given directory
|
||||
static void enum_firmware_dir(const char *dir_name) {
|
||||
DIR *dir = opendir(dir_name);
|
||||
|
||||
if (!dir) {
|
||||
if (errno == ENOENT) {
|
||||
fprintf(stderr, "Firmware directory %s does not exist\n", dir_name);
|
||||
return;
|
||||
}
|
||||
fprintf(
|
||||
stderr, "Error opening directory %s: %s\n", dir_name, strerror(errno)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
struct dirent *entry;
|
||||
|
||||
while ((entry = readdir(dir))) {
|
||||
char *full_fn;
|
||||
|
||||
// check if the file is a .bin file
|
||||
if (strlen(entry->d_name) < 4 ||
|
||||
strcmp(entry->d_name + strlen(entry->d_name) - 4, ".bin") != 0)
|
||||
continue;
|
||||
|
||||
// check if the file is a regular file
|
||||
if (entry->d_type == DT_UNKNOWN) {
|
||||
struct stat st;
|
||||
full_fn = g_build_filename(dir_name, entry->d_name, NULL);
|
||||
if (stat(full_fn, &st) < 0) {
|
||||
perror("stat");
|
||||
g_free(full_fn);
|
||||
continue;
|
||||
}
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
g_free(full_fn);
|
||||
continue;
|
||||
}
|
||||
} else if (entry->d_type != DT_REG) {
|
||||
continue;
|
||||
} else {
|
||||
full_fn = g_build_filename(dir_name, entry->d_name, NULL);
|
||||
}
|
||||
|
||||
struct scarlett2_firmware_header *firmware =
|
||||
scarlett2_read_firmware_header(full_fn);
|
||||
|
||||
if (!firmware) {
|
||||
fprintf(stderr, "Error reading firmware file %s\n", full_fn);
|
||||
g_free(full_fn);
|
||||
continue;
|
||||
}
|
||||
|
||||
add_found_firmware(full_fn, firmware);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void scarlett2_enum_firmware(void) {
|
||||
init_best_firmware();
|
||||
enum_firmware_dir(SCARLETT2_FIRMWARE_DIR);
|
||||
}
|
||||
|
||||
uint32_t scarlett2_get_best_firmware_version(uint32_t pid) {
|
||||
struct found_firmware *found = g_hash_table_lookup(
|
||||
best_firmware, GINT_TO_POINTER(pid)
|
||||
);
|
||||
if (!found)
|
||||
return 0;
|
||||
|
||||
return found->firmware->firmware_version;
|
||||
}
|
||||
|
||||
struct scarlett2_firmware_file *scarlett2_get_best_firmware(uint32_t pid) {
|
||||
struct found_firmware *found = g_hash_table_lookup(
|
||||
best_firmware, GINT_TO_POINTER(pid)
|
||||
);
|
||||
if (!found)
|
||||
return NULL;
|
||||
|
||||
return scarlett2_read_firmware_file(found->fn);
|
||||
}
|
||||
46
src/scarlett2-firmware.h
Normal file
46
src/scarlett2-firmware.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: 2023-2024 Geoffrey D. Bennett <g@b4.vu>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// System-wide firmware directory
|
||||
#define SCARLETT2_FIRMWARE_DIR "/usr/lib/firmware/scarlett2"
|
||||
|
||||
#define MAGIC_STRING "SCARLETT"
|
||||
|
||||
struct scarlett2_firmware_header {
|
||||
char magic[8]; // "SCARLETT"
|
||||
uint16_t usb_vid; // Big-endian
|
||||
uint16_t usb_pid; // Big-endian
|
||||
uint32_t firmware_version; // Big-endian
|
||||
uint32_t firmware_length; // Big-endian
|
||||
uint8_t sha256[32];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct scarlett2_firmware_file {
|
||||
struct scarlett2_firmware_header header;
|
||||
uint8_t *firmware_data;
|
||||
};
|
||||
|
||||
struct scarlett2_firmware_header *scarlett2_read_firmware_header(
|
||||
const char *fn
|
||||
);
|
||||
|
||||
void scarlett2_free_firmware_header(
|
||||
struct scarlett2_firmware_header *firmware
|
||||
);
|
||||
|
||||
struct scarlett2_firmware_file *scarlett2_read_firmware_file(
|
||||
const char *fn
|
||||
);
|
||||
|
||||
void scarlett2_free_firmware_file(
|
||||
struct scarlett2_firmware_file *firmware
|
||||
);
|
||||
|
||||
void scarlett2_enum_firmware(void);
|
||||
|
||||
uint32_t scarlett2_get_best_firmware_version(uint32_t pid);
|
||||
struct scarlett2_firmware_file *scarlett2_get_best_firmware(uint32_t pid);
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "iface-no-mixer.h"
|
||||
#include "iface-none.h"
|
||||
#include "iface-unknown.h"
|
||||
#include "iface-update.h"
|
||||
#include "main.h"
|
||||
#include "menu.h"
|
||||
#include "window-iface.h"
|
||||
@@ -27,8 +28,25 @@ void create_card_window(struct alsa_card *card) {
|
||||
int has_startup = true;
|
||||
int has_mixer = true;
|
||||
|
||||
struct alsa_elem *firmware_elem =
|
||||
get_elem_by_name(card->elems, "Firmware Version");
|
||||
struct alsa_elem *min_firmware_elem =
|
||||
get_elem_by_name(card->elems, "Minimum Firmware Version");
|
||||
int firmware_version = 0;
|
||||
int min_firmware_version = 0;
|
||||
if (firmware_elem && min_firmware_elem) {
|
||||
firmware_version = alsa_get_elem_value(firmware_elem);
|
||||
min_firmware_version = alsa_get_elem_value(min_firmware_elem);
|
||||
}
|
||||
|
||||
// Firmware update required
|
||||
if (firmware_version < min_firmware_version) {
|
||||
card->window_main_contents = create_iface_update_main(card);
|
||||
has_startup = false;
|
||||
has_mixer = false;
|
||||
|
||||
// Gen 2 or Gen 3 4i4+
|
||||
if (get_elem_by_prefix(card->elems, "Mixer")) {
|
||||
} else if (get_elem_by_prefix(card->elems, "Mixer")) {
|
||||
card->window_main_contents = create_iface_mixer_main(card);
|
||||
|
||||
// Gen 3 Solo or 2i2
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "device-reset-config.h"
|
||||
#include "device-update-firmware.h"
|
||||
#include "gtkhelper.h"
|
||||
#include "scarlett2.h"
|
||||
#include "scarlett2-firmware.h"
|
||||
#include "scarlett2-ioctls.h"
|
||||
#include "widget-boolean.h"
|
||||
#include "window-startup.h"
|
||||
@@ -227,6 +229,39 @@ static void add_reset_actions(
|
||||
"factory default settings. The firmware will be left unchanged.",
|
||||
G_CALLBACK(create_reset_config_window)
|
||||
);
|
||||
|
||||
// Update Firmware
|
||||
struct alsa_elem *firmware_elem =
|
||||
get_elem_by_name(card->elems, "Firmware Version");
|
||||
|
||||
if (!firmware_elem)
|
||||
return;
|
||||
|
||||
int firmware_version = alsa_get_elem_value(firmware_elem);
|
||||
uint32_t best_firmware_version =
|
||||
scarlett2_get_best_firmware_version(card->pid);
|
||||
|
||||
if (firmware_version >= best_firmware_version)
|
||||
return;
|
||||
|
||||
char *s = g_strdup_printf(
|
||||
"Updating the firmware will reset the interface to its "
|
||||
"factory default settings and update the firmware from version "
|
||||
"%d to %d.",
|
||||
firmware_version,
|
||||
best_firmware_version
|
||||
);
|
||||
add_reset_action(
|
||||
card,
|
||||
grid,
|
||||
grid_y,
|
||||
"Update Firmware",
|
||||
"Update",
|
||||
s,
|
||||
G_CALLBACK(create_update_firmware_window)
|
||||
);
|
||||
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
static void add_no_startup_controls_msg(GtkWidget *grid) {
|
||||
|
||||
Reference in New Issue
Block a user