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 glib-2.0)
|
||||||
LDFLAGS += $(shell $(PKG_CONFIG) --libs gtk4)
|
LDFLAGS += $(shell $(PKG_CONFIG) --libs gtk4)
|
||||||
LDFLAGS += $(shell $(PKG_CONFIG) --libs alsa)
|
LDFLAGS += $(shell $(PKG_CONFIG) --libs alsa)
|
||||||
LDFLAGS += -lm
|
LDFLAGS += -lm -lcrypto
|
||||||
|
|
||||||
COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) -c
|
COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) -c
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,16 @@
|
|||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Title of the window */
|
||||||
|
.window-title {
|
||||||
|
font-size: large;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Links */
|
||||||
|
.linktext {
|
||||||
|
color: #89CFF0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Label above controls-content */
|
/* Label above controls-content */
|
||||||
.controls-label {
|
.controls-label {
|
||||||
font-size: smaller;
|
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 "alsa-sim.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
|
#include "scarlett2-firmware.h"
|
||||||
#include "window-hardware.h"
|
#include "window-hardware.h"
|
||||||
#include "window-iface.h"
|
#include "window-iface.h"
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ static void startup(GtkApplication *app, gpointer user_data) {
|
|||||||
|
|
||||||
load_css();
|
load_css();
|
||||||
|
|
||||||
|
scarlett2_enum_firmware();
|
||||||
alsa_init();
|
alsa_init();
|
||||||
|
|
||||||
create_no_card_window();
|
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-no-mixer.h"
|
||||||
#include "iface-none.h"
|
#include "iface-none.h"
|
||||||
#include "iface-unknown.h"
|
#include "iface-unknown.h"
|
||||||
|
#include "iface-update.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
#include "window-iface.h"
|
#include "window-iface.h"
|
||||||
@@ -27,8 +28,25 @@ void create_card_window(struct alsa_card *card) {
|
|||||||
int has_startup = true;
|
int has_startup = true;
|
||||||
int has_mixer = 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+
|
// 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);
|
card->window_main_contents = create_iface_mixer_main(card);
|
||||||
|
|
||||||
// Gen 3 Solo or 2i2
|
// Gen 3 Solo or 2i2
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include "device-reset-config.h"
|
#include "device-reset-config.h"
|
||||||
|
#include "device-update-firmware.h"
|
||||||
#include "gtkhelper.h"
|
#include "gtkhelper.h"
|
||||||
#include "scarlett2.h"
|
#include "scarlett2.h"
|
||||||
|
#include "scarlett2-firmware.h"
|
||||||
#include "scarlett2-ioctls.h"
|
#include "scarlett2-ioctls.h"
|
||||||
#include "widget-boolean.h"
|
#include "widget-boolean.h"
|
||||||
#include "window-startup.h"
|
#include "window-startup.h"
|
||||||
@@ -227,6 +229,39 @@ static void add_reset_actions(
|
|||||||
"factory default settings. The firmware will be left unchanged.",
|
"factory default settings. The firmware will be left unchanged.",
|
||||||
G_CALLBACK(create_reset_config_window)
|
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) {
|
static void add_no_startup_controls_msg(GtkWidget *grid) {
|
||||||
|
|||||||
Reference in New Issue
Block a user