mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-03 16:44:26 +00:00

Conceptually we were trying to stuff subtly different actions into one vfunc: * Read firmware from the device to update the verification checksums * Read a firmware blob from the device for debugging For the first action we might want to mask out the sections of the flash with serial numbers (so the verification hashes match the ones published on the LVFS) and for the second we want just a raw ROM file from the hardware with no pre-processing that we can compare against an external SPI dumper. Split out ->dump_firmware to get the raw blob, and allow plugins to also implement ->read_firmware() if they have to mask out specific offsets or remove specific images from the FuFirmware container. In the common case when masking is not required, fall back to using a 'binary' FuFirmware automatically to make most plugins simpler.
232 lines
7.6 KiB
C
232 lines
7.6 KiB
C
/*
|
|
* Copyright (C) 2017-2019 VIA Corporation
|
|
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "fu-vli-pd-common.h"
|
|
#include "fu-vli-pd-firmware.h"
|
|
|
|
#include "fu-vli-usbhub-common.h"
|
|
#include "fu-vli-usbhub-device.h"
|
|
#include "fu-vli-usbhub-pd-device.h"
|
|
|
|
struct _FuVliUsbhubPdDevice
|
|
{
|
|
FuDevice parent_instance;
|
|
FuVliPdHdr hdr;
|
|
FuVliDeviceKind device_kind;
|
|
};
|
|
|
|
G_DEFINE_TYPE (FuVliUsbhubPdDevice, fu_vli_usbhub_pd_device, FU_TYPE_DEVICE)
|
|
|
|
static void
|
|
fu_vli_usbhub_pd_device_to_string (FuDevice *device, guint idt, GString *str)
|
|
{
|
|
FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE (device);
|
|
fu_common_string_append_kv (str, idt, "DeviceKind",
|
|
fu_vli_common_device_kind_to_string (self->device_kind));
|
|
fu_common_string_append_kx (str, idt, "FwOffset",
|
|
fu_vli_common_device_kind_get_offset (self->device_kind));
|
|
fu_common_string_append_kx (str, idt, "FwSize",
|
|
fu_vli_common_device_kind_get_size (self->device_kind));
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_pd_device_probe (FuDevice *device, GError **error)
|
|
{
|
|
FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE (device);
|
|
|
|
guint32 fwver;
|
|
g_autofree gchar *fwver_str = NULL;
|
|
g_autofree gchar *instance_id0 = NULL;
|
|
g_autofree gchar *instance_id1 = NULL;
|
|
g_autofree gchar *instance_id2 = NULL;
|
|
g_autofree gchar *instance_id3 = NULL;
|
|
|
|
/* get version */
|
|
fwver = GUINT32_FROM_BE (self->hdr.fwver);
|
|
self->device_kind = fu_vli_pd_common_guess_device_kind (fwver);
|
|
if (self->device_kind == FU_VLI_DEVICE_KIND_UNKNOWN) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"PD version invalid [0x%x]", fwver);
|
|
return FALSE;
|
|
}
|
|
fu_device_set_name (device, fu_vli_common_device_kind_to_string (self->device_kind));
|
|
|
|
/* use header to populate device info */
|
|
fu_device_set_version_raw (device, fwver);
|
|
fwver_str = fu_common_version_from_uint32 (fwver, FWUPD_VERSION_FORMAT_QUAD);
|
|
fu_device_set_version (device, fwver_str);
|
|
instance_id0 = g_strdup_printf ("USB\\VID_%04X&PID_%04X&APP_%02X",
|
|
GUINT16_FROM_LE (self->hdr.vid),
|
|
GUINT16_FROM_LE (self->hdr.pid),
|
|
fwver & 0xff);
|
|
fu_device_add_instance_id (device, instance_id0);
|
|
instance_id1 = g_strdup_printf ("USB\\VID_%04X&PID_%04X&DEV_%s",
|
|
GUINT16_FROM_LE (self->hdr.vid),
|
|
GUINT16_FROM_LE (self->hdr.pid),
|
|
fu_vli_common_device_kind_to_string (self->device_kind));
|
|
fu_device_add_instance_id (device, instance_id1);
|
|
|
|
/* add standard GUIDs in order of priority */
|
|
instance_id2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X",
|
|
GUINT16_FROM_LE (self->hdr.vid),
|
|
GUINT16_FROM_LE (self->hdr.pid));
|
|
fu_device_add_instance_id (device, instance_id2);
|
|
instance_id3 = g_strdup_printf ("USB\\VID_%04X",
|
|
GUINT16_FROM_LE (self->hdr.vid));
|
|
fu_device_add_instance_id_full (device, instance_id3,
|
|
FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS);
|
|
|
|
/* these have a backup section */
|
|
if (fu_vli_common_device_kind_get_offset (self->device_kind) == VLI_USBHUB_FLASHMAP_ADDR_PD)
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_SELF_RECOVERY);
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static FuFirmware *
|
|
fu_vli_usbhub_pd_device_prepare_firmware (FuDevice *device,
|
|
GBytes *fw,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE (device);
|
|
FuVliDeviceKind device_kind;
|
|
g_autoptr(FuFirmware) firmware = fu_vli_pd_firmware_new ();
|
|
|
|
/* check is compatible with firmware */
|
|
if (!fu_firmware_parse (firmware, fw, flags, error))
|
|
return NULL;
|
|
device_kind = fu_vli_pd_firmware_get_kind (FU_VLI_PD_FIRMWARE (firmware));
|
|
if (self->device_kind != device_kind) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"firmware incompatible, got %s, expected %s",
|
|
fu_vli_common_device_kind_to_string (device_kind),
|
|
fu_vli_common_device_kind_to_string (self->device_kind));
|
|
return NULL;
|
|
}
|
|
|
|
/* we could check this against flags */
|
|
g_debug ("parsed version: %s", fu_firmware_get_version (firmware));
|
|
return g_steal_pointer (&firmware);
|
|
}
|
|
|
|
static GBytes *
|
|
fu_vli_usbhub_pd_device_dump_firmware (FuDevice *device, GError **error)
|
|
{
|
|
FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE (fu_device_get_parent (device));
|
|
FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE (device);
|
|
g_autoptr(FuDeviceLocker) locker = NULL;
|
|
|
|
/* open device */
|
|
locker = fu_device_locker_new (parent, error);
|
|
if (locker == NULL)
|
|
return NULL;
|
|
|
|
/* read */
|
|
fu_device_set_status (FU_DEVICE (device), FWUPD_STATUS_DEVICE_READ);
|
|
return fu_vli_device_spi_read (FU_VLI_DEVICE (parent),
|
|
fu_vli_common_device_kind_get_offset (self->device_kind),
|
|
fu_device_get_firmware_size_max (device),
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_pd_device_write_firmware (FuDevice *device,
|
|
FuFirmware *firmware,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE (device);
|
|
FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE (fu_device_get_parent (device));
|
|
gsize bufsz = 0;
|
|
const guint8 *buf;
|
|
g_autoptr(FuDeviceLocker) locker = NULL;
|
|
g_autoptr(GBytes) fw = NULL;
|
|
|
|
/* simple image */
|
|
fw = fu_firmware_get_image_default_bytes (firmware, error);
|
|
if (fw == NULL)
|
|
return FALSE;
|
|
|
|
/* open device */
|
|
locker = fu_device_locker_new (parent, error);
|
|
if (locker == NULL)
|
|
return FALSE;
|
|
|
|
/* erase */
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE);
|
|
buf = g_bytes_get_data (fw, &bufsz);
|
|
if (!fu_vli_device_spi_erase (FU_VLI_DEVICE (parent),
|
|
fu_vli_common_device_kind_get_offset (self->device_kind),
|
|
bufsz, error))
|
|
return FALSE;
|
|
|
|
/* write */
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);
|
|
if (!fu_vli_device_spi_write (FU_VLI_DEVICE (parent),
|
|
fu_vli_common_device_kind_get_offset (self->device_kind),
|
|
buf, bufsz, error))
|
|
return FALSE;
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
/* reboot the parent FuVliUsbhubDevice if we update the FuVliUsbhubPdDevice */
|
|
static gboolean
|
|
fu_vli_usbhub_pd_device_attach (FuDevice *device, GError **error)
|
|
{
|
|
FuDevice *parent = fu_device_get_parent (device);
|
|
g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (parent, error);
|
|
if (locker == NULL)
|
|
return FALSE;
|
|
return fu_device_attach (parent, error);
|
|
}
|
|
|
|
static void
|
|
fu_vli_usbhub_pd_device_init (FuVliUsbhubPdDevice *self)
|
|
{
|
|
fu_device_add_icon (FU_DEVICE (self), "audio-card");
|
|
fu_device_set_protocol (FU_DEVICE (self), "com.vli.usbhub");
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE);
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE);
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NO_GUID_MATCHING);
|
|
fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_QUAD);
|
|
fu_device_set_install_duration (FU_DEVICE (self), 15); /* seconds */
|
|
fu_device_set_logical_id (FU_DEVICE (self), "PD");
|
|
fu_device_set_summary (FU_DEVICE (self), "USB-C Power Delivery Device");
|
|
}
|
|
|
|
static void
|
|
fu_vli_usbhub_pd_device_class_init (FuVliUsbhubPdDeviceClass *klass)
|
|
{
|
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
|
|
klass_device->to_string = fu_vli_usbhub_pd_device_to_string;
|
|
klass_device->probe = fu_vli_usbhub_pd_device_probe;
|
|
klass_device->attach = fu_vli_usbhub_pd_device_attach;
|
|
klass_device->dump_firmware = fu_vli_usbhub_pd_device_dump_firmware;
|
|
klass_device->write_firmware = fu_vli_usbhub_pd_device_write_firmware;
|
|
klass_device->prepare_firmware = fu_vli_usbhub_pd_device_prepare_firmware;
|
|
}
|
|
|
|
FuDevice *
|
|
fu_vli_usbhub_pd_device_new (FuVliPdHdr *hdr)
|
|
{
|
|
FuVliUsbhubPdDevice *self = g_object_new (FU_TYPE_VLI_USBHUB_PD_DEVICE, NULL);
|
|
memcpy (&self->hdr, hdr, sizeof(self->hdr));
|
|
return FU_DEVICE (self);
|
|
}
|