mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-26 00:47:32 +00:00

It's actually quite hard to build a front-end for fwupd at the moment as you're never sure when the progress bar is going to zip back to 0% and start all over again. Some plugins go 0..100% for write, others go 0..100% for erase, then again for write, then *again* for verify. By creating a helper object we can easily split up the progress of the specific task, e.g. write_firmware(). We can encode at the plugin level "the erase takes 50% of the time, the write takes 40% and the read takes 10%". This means we can have a progressbar which goes up just once at a consistent speed.
143 lines
4.0 KiB
C
143 lines
4.0 KiB
C
/*
|
|
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "fu-optionrom-device.h"
|
|
|
|
struct _FuOptionromDevice {
|
|
FuUdevDevice parent_instance;
|
|
};
|
|
|
|
G_DEFINE_TYPE(FuOptionromDevice, fu_optionrom_device, FU_TYPE_UDEV_DEVICE)
|
|
|
|
static gboolean
|
|
fu_optionrom_device_probe(FuDevice *device, GError **error)
|
|
{
|
|
g_autofree gchar *fn = NULL;
|
|
|
|
/* FuUdevDevice->probe */
|
|
if (!FU_DEVICE_CLASS(fu_optionrom_device_parent_class)->probe(device, error))
|
|
return FALSE;
|
|
|
|
/* does the device even have ROM? */
|
|
fn = g_build_filename(fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device)), "rom", NULL);
|
|
if (!g_file_test(fn, G_FILE_TEST_EXISTS)) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Unable to read firmware from device");
|
|
return FALSE;
|
|
}
|
|
|
|
/* set the physical ID */
|
|
return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "pci", error);
|
|
}
|
|
|
|
static GBytes *
|
|
fu_optionrom_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error)
|
|
{
|
|
FuUdevDevice *udev_device = FU_UDEV_DEVICE(device);
|
|
guint number_reads = 0;
|
|
g_autofree gchar *fn = NULL;
|
|
g_autofree gchar *rom_fn = NULL;
|
|
g_autoptr(GByteArray) buf = g_byte_array_new();
|
|
g_autoptr(GError) error_local = NULL;
|
|
g_autoptr(GFile) file = NULL;
|
|
g_autoptr(GInputStream) stream = NULL;
|
|
|
|
/* open the file */
|
|
rom_fn = g_build_filename(fu_udev_device_get_sysfs_path(udev_device), "rom", NULL);
|
|
if (rom_fn == NULL) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"Unable to read firmware from device");
|
|
return NULL;
|
|
}
|
|
|
|
/* open file */
|
|
file = g_file_new_for_path(rom_fn);
|
|
stream = G_INPUT_STREAM(g_file_read(file, NULL, &error_local));
|
|
if (stream == NULL) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_AUTH_FAILED,
|
|
error_local->message);
|
|
return NULL;
|
|
}
|
|
|
|
/* we have to enable the read for devices */
|
|
fn = g_file_get_path(file);
|
|
if (g_str_has_prefix(fn, "/sys")) {
|
|
g_autoptr(GFileOutputStream) output_stream = NULL;
|
|
output_stream = g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error);
|
|
if (output_stream == NULL)
|
|
return NULL;
|
|
if (g_output_stream_write(G_OUTPUT_STREAM(output_stream), "1", 1, NULL, error) < 0)
|
|
return NULL;
|
|
}
|
|
|
|
/* ensure we got enough data to fill the buffer */
|
|
while (TRUE) {
|
|
gssize sz;
|
|
guint8 tmp[32 * 1024] = {0x0};
|
|
sz = g_input_stream_read(stream, tmp, sizeof(tmp), NULL, error);
|
|
if (sz == 0)
|
|
break;
|
|
g_debug("ROM returned 0x%04x bytes", (guint)sz);
|
|
if (sz < 0)
|
|
return NULL;
|
|
g_byte_array_append(buf, tmp, sz);
|
|
|
|
/* check the firmware isn't serving us small chunks */
|
|
if (number_reads++ > 1024) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"firmware not fulfilling requests");
|
|
return NULL;
|
|
}
|
|
}
|
|
if (buf->len < 512) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"firmware too small: %u bytes",
|
|
buf->len);
|
|
return NULL;
|
|
}
|
|
return g_byte_array_free_to_bytes(g_steal_pointer(&buf));
|
|
}
|
|
|
|
static void
|
|
fu_optionrom_device_init(FuOptionromDevice *self)
|
|
{
|
|
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL);
|
|
fu_device_add_icon(FU_DEVICE(self), "audio-card");
|
|
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE);
|
|
fu_device_set_logical_id(FU_DEVICE(self), "rom");
|
|
fu_udev_device_set_flags(FU_UDEV_DEVICE(self),
|
|
FU_UDEV_DEVICE_FLAG_OPEN_READ |
|
|
FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT);
|
|
}
|
|
|
|
static void
|
|
fu_optionrom_device_finalize(GObject *object)
|
|
{
|
|
G_OBJECT_CLASS(fu_optionrom_device_parent_class)->finalize(object);
|
|
}
|
|
|
|
static void
|
|
fu_optionrom_device_class_init(FuOptionromDeviceClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
|
object_class->finalize = fu_optionrom_device_finalize;
|
|
klass_device->dump_firmware = fu_optionrom_device_dump_firmware;
|
|
klass_device->probe = fu_optionrom_device_probe;
|
|
}
|