mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-25 22:40:50 +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.
178 lines
5.7 KiB
C
178 lines
5.7 KiB
C
/*
|
|
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "fu-logitech-hidpp-common.h"
|
|
#include "fu-logitech-hidpp-hidpp.h"
|
|
#include "fu-logitech-hidpp-runtime-unifying.h"
|
|
|
|
struct _FuLogitechHidPpRuntimeUnifying {
|
|
FuLogitechHidPpRuntime parent_instance;
|
|
};
|
|
|
|
G_DEFINE_TYPE(FuLogitechHidPpRuntimeUnifying,
|
|
fu_logitech_hidpp_runtime_unifying,
|
|
FU_TYPE_HIDPP_RUNTIME)
|
|
#define GET_PRIVATE(o) (fu_logitech_hidpp_runtime_unifying_get_instance_private(o))
|
|
|
|
static gboolean
|
|
fu_logitech_hidpp_runtime_unifying_detach(FuDevice *device, FuProgress *progress, GError **error)
|
|
{
|
|
FuLogitechHidPpRuntime *self = FU_HIDPP_RUNTIME(device);
|
|
g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new();
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
msg->report_id = HIDPP_REPORT_ID_SHORT;
|
|
msg->device_id = HIDPP_DEVICE_IDX_RECEIVER;
|
|
msg->sub_id = HIDPP_SUBID_SET_REGISTER;
|
|
msg->function_id = HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE;
|
|
msg->data[0] = 'I';
|
|
msg->data[1] = 'C';
|
|
msg->data[2] = 'P';
|
|
msg->hidpp_version = 1;
|
|
msg->flags = FU_UNIFYING_HIDPP_MSG_FLAG_LONGER_TIMEOUT;
|
|
if (!fu_logitech_hidpp_send(fu_logitech_hidpp_runtime_get_io_channel(self),
|
|
msg,
|
|
FU_UNIFYING_DEVICE_TIMEOUT_MS,
|
|
&error_local)) {
|
|
if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_WRITE)) {
|
|
g_debug("failed to detach to bootloader: %s", error_local->message);
|
|
} else {
|
|
g_prefix_error(&error_local, "failed to detach to bootloader: ");
|
|
g_propagate_error(error, g_steal_pointer(&error_local));
|
|
return FALSE;
|
|
}
|
|
}
|
|
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_logitech_hidpp_runtime_unifying_setup_internal(FuDevice *device, GError **error)
|
|
{
|
|
FuLogitechHidPpRuntime *self = FU_HIDPP_RUNTIME(device);
|
|
guint8 config[10];
|
|
g_autofree gchar *version_fw = NULL;
|
|
|
|
/* read all 10 bytes of the version register */
|
|
memset(config, 0x00, sizeof(config));
|
|
for (guint i = 0x01; i < 0x05; i++) {
|
|
g_autoptr(FuLogitechHidPpHidppMsg) msg = fu_logitech_hidpp_msg_new();
|
|
|
|
/* workaround a bug in the 12.01 firmware, which fails with
|
|
* INVALID_VALUE when reading MCU1_HW_VERSION */
|
|
if (i == 0x03)
|
|
continue;
|
|
|
|
msg->report_id = HIDPP_REPORT_ID_SHORT;
|
|
msg->device_id = HIDPP_DEVICE_IDX_RECEIVER;
|
|
msg->sub_id = HIDPP_SUBID_GET_REGISTER;
|
|
msg->function_id = HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION;
|
|
msg->data[0] = i;
|
|
msg->hidpp_version = 1;
|
|
if (!fu_logitech_hidpp_transfer(fu_logitech_hidpp_runtime_get_io_channel(self),
|
|
msg,
|
|
error)) {
|
|
g_prefix_error(error, "failed to read device config: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_memcpy_safe(config,
|
|
sizeof(config),
|
|
i * 2, /* dst */
|
|
msg->data,
|
|
sizeof(msg->data),
|
|
0x1, /* src */
|
|
2,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
|
|
/* get firmware version */
|
|
version_fw = fu_logitech_hidpp_format_version("RQR",
|
|
config[2],
|
|
config[3],
|
|
(guint16)config[4] << 8 | config[5]);
|
|
fu_device_set_version(device, version_fw);
|
|
|
|
/* get bootloader version */
|
|
if (fu_logitech_hidpp_runtime_get_version_bl_major(self) > 0) {
|
|
g_autofree gchar *version_bl = NULL;
|
|
version_bl = fu_logitech_hidpp_format_version(
|
|
"BOT",
|
|
fu_logitech_hidpp_runtime_get_version_bl_major(self),
|
|
config[8],
|
|
config[9]);
|
|
fu_device_set_version_bootloader(FU_DEVICE(device), version_bl);
|
|
|
|
/* is the dongle expecting signed firmware */
|
|
if ((fu_logitech_hidpp_runtime_get_version_bl_major(self) == 0x01 &&
|
|
config[8] >= 0x04) ||
|
|
(fu_logitech_hidpp_runtime_get_version_bl_major(self) == 0x03 &&
|
|
config[8] >= 0x02)) {
|
|
fu_logitech_hidpp_runtime_set_signed_firmware(self, TRUE);
|
|
fu_device_add_protocol(device, "com.logitech.unifyingsigned");
|
|
}
|
|
}
|
|
if (!fu_logitech_hidpp_runtime_get_signed_firmware(self))
|
|
fu_device_add_protocol(device, "com.logitech.unifying");
|
|
|
|
/* enable HID++ notifications */
|
|
if (!fu_logitech_hidpp_runtime_enable_notifications(self, error)) {
|
|
g_prefix_error(error, "failed to enable notifications: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_logitech_hidpp_runtime_unifying_setup(FuDevice *device, GError **error)
|
|
{
|
|
g_autoptr(GError) error_local = NULL;
|
|
for (guint i = 0; i < 5; i++) {
|
|
g_clear_error(&error_local);
|
|
/* HID++1.0 devices have to sleep to allow Solaar to talk to
|
|
* the device first -- we can't use the SwID as this is a
|
|
* HID++2.0 feature */
|
|
g_usleep(200 * 1000);
|
|
if (fu_logitech_hidpp_runtime_unifying_setup_internal(device, &error_local))
|
|
return TRUE;
|
|
if (!g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_INVALID_DATA)) {
|
|
g_propagate_error(error, g_steal_pointer(&error_local));
|
|
return FALSE;
|
|
}
|
|
}
|
|
g_propagate_error(error, g_steal_pointer(&error_local));
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
fu_logitech_hidpp_runtime_unifying_set_progress(FuDevice *self, FuProgress *progress)
|
|
{
|
|
fu_progress_set_id(progress, G_STRLOC);
|
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0); /* detach */
|
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 70); /* write */
|
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 4); /* attach */
|
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 27); /* reload */
|
|
}
|
|
|
|
static void
|
|
fu_logitech_hidpp_runtime_unifying_class_init(FuLogitechHidPpRuntimeUnifyingClass *klass)
|
|
{
|
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
|
|
|
klass_device->detach = fu_logitech_hidpp_runtime_unifying_detach;
|
|
klass_device->setup = fu_logitech_hidpp_runtime_unifying_setup;
|
|
klass_device->set_progress = fu_logitech_hidpp_runtime_unifying_set_progress;
|
|
}
|
|
|
|
static void
|
|
fu_logitech_hidpp_runtime_unifying_init(FuLogitechHidPpRuntimeUnifying *self)
|
|
{
|
|
}
|