mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-09 09:55:39 +00:00
cfu: Add an initial outline CFU plugin
There's no actual hardware to test this against yet, but this is how I would lay out a plugin if there was. We still need to work out a generic encapsulation for the offer and payload (for each component and bank) so this can work with LVFS and fwupd.
This commit is contained in:
parent
d1dff82803
commit
8e24fa77cf
@ -415,6 +415,7 @@ done
|
|||||||
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_ata.so
|
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_ata.so
|
||||||
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_bcm57xx.so
|
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_bcm57xx.so
|
||||||
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_ccgx.so
|
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_ccgx.so
|
||||||
|
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_cfu.so
|
||||||
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_colorhug.so
|
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_colorhug.so
|
||||||
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_cros_ec.so
|
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_cros_ec.so
|
||||||
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_cpu.so
|
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_cpu.so
|
||||||
|
38
plugins/cfu/README.md
Normal file
38
plugins/cfu/README.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Component Firmware update
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
CFU is a protocol from Microsoft to make it easy to install firmware on HID devices.
|
||||||
|
|
||||||
|
This protocol is unique in that it requires has a pre-download phase before sending the firmware to
|
||||||
|
the microcontroller. This is so the device can check if the firmware is required and compatible.
|
||||||
|
CFU also requires devices to be able to transfer the entire new transfer mode in runtime mode.
|
||||||
|
|
||||||
|
See <https://docs.microsoft.com/en-us/windows-hardware/drivers/cfu/cfu-specification> for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
This plugin supports the following protocol ID:
|
||||||
|
|
||||||
|
* com.microsoft.cfu
|
||||||
|
|
||||||
|
## GUID Generation
|
||||||
|
|
||||||
|
These devices use standard USB DeviceInstanceId values, as well as two extra for the component ID
|
||||||
|
and the bank, e.g.
|
||||||
|
|
||||||
|
* `HIDRAW\VEN_17EF&DEV_7226&CID_01&BANK_1`
|
||||||
|
* `HIDRAW\VEN_17EF&DEV_7226&CID_01`
|
||||||
|
* `HIDRAW\VEN_17EF&DEV_7226`
|
||||||
|
|
||||||
|
## Update Behavior
|
||||||
|
|
||||||
|
The device has to support runtime updates and does not have a detach-into-bootloader mode -- but
|
||||||
|
after the install has completed the device still has to reboot into the new firmware.
|
||||||
|
|
||||||
|
## Vendor ID Security
|
||||||
|
|
||||||
|
The vendor ID is set from the USB vendor, in this instance set to `HIDRAW:0x17EF`
|
||||||
|
|
||||||
|
## External Interface Access
|
||||||
|
|
||||||
|
This plugin requires read/write access to `/dev/bus/usb`.
|
2
plugins/cfu/cfu.quirk
Normal file
2
plugins/cfu/cfu.quirk
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[USB\VID_273F&PID_100A]
|
||||||
|
Plugin = cfu
|
293
plugins/cfu/fu-cfu-device.c
Normal file
293
plugins/cfu/fu-cfu-device.c
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <fwupdplugin.h>
|
||||||
|
|
||||||
|
#include "fu-cfu-device.h"
|
||||||
|
#include "fu-cfu-module.h"
|
||||||
|
|
||||||
|
struct _FuCfuDevice {
|
||||||
|
FuHidDevice parent_instance;
|
||||||
|
guint8 protocol_version;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(FuCfuDevice, fu_cfu_device, FU_TYPE_HID_DEVICE)
|
||||||
|
|
||||||
|
#define FU_CFU_DEVICE_TIMEOUT 5000 /* ms */
|
||||||
|
#define FU_CFU_FEATURE_SIZE 60 /* bytes */
|
||||||
|
|
||||||
|
#define FU_CFU_CMD_GET_FIRMWARE_VERSION 0x00
|
||||||
|
#define FU_CFU_CMD_SEND_OFFER 0x00 // TODO
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_cfu_device_to_string(FuDevice *device, guint idt, GString *str)
|
||||||
|
{
|
||||||
|
FuCfuDevice *self = FU_CFU_DEVICE(device);
|
||||||
|
|
||||||
|
/* FuUdevDevice->to_string */
|
||||||
|
FU_DEVICE_CLASS(fu_cfu_device_parent_class)->to_string(device, idt, str);
|
||||||
|
|
||||||
|
fu_common_string_append_kx(str, idt, "ProtocolVersion", self->protocol_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_cfu_device_write_offer(FuCfuDevice *self,
|
||||||
|
FuFirmware *firmware,
|
||||||
|
FuProgress *progress,
|
||||||
|
FwupdInstallFlags flags,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
const guint8 *buf;
|
||||||
|
gsize bufsz = 0;
|
||||||
|
guint8 buf2[FU_CFU_FEATURE_SIZE] = {0};
|
||||||
|
g_autofree guint8 *buf_tmp = NULL;
|
||||||
|
g_autoptr(GBytes) blob = NULL;
|
||||||
|
|
||||||
|
/* generate a offer blob */
|
||||||
|
if (flags & FWUPD_INSTALL_FLAG_FORCE)
|
||||||
|
fu_cfu_offer_set_force_ignore_version(FU_CFU_OFFER(firmware), TRUE);
|
||||||
|
blob = fu_firmware_write(firmware, error);
|
||||||
|
if (blob == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* send it to the hardware */
|
||||||
|
buf = g_bytes_get_data(blob, &bufsz);
|
||||||
|
buf_tmp = fu_memdup_safe(buf, bufsz, error);
|
||||||
|
if (buf_tmp == NULL)
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_hid_device_set_report(FU_HID_DEVICE(self),
|
||||||
|
FU_CFU_CMD_SEND_OFFER,
|
||||||
|
buf_tmp,
|
||||||
|
bufsz,
|
||||||
|
FU_CFU_DEVICE_TIMEOUT,
|
||||||
|
FU_HID_DEVICE_FLAG_IS_FEATURE,
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to send offer: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!fu_hid_device_get_report(FU_HID_DEVICE(self),
|
||||||
|
FU_CFU_CMD_SEND_OFFER,
|
||||||
|
buf2,
|
||||||
|
sizeof(buf2),
|
||||||
|
FU_CFU_DEVICE_TIMEOUT,
|
||||||
|
FU_HID_DEVICE_FLAG_IS_FEATURE,
|
||||||
|
error)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
g_debug("status:%s reject:%s",
|
||||||
|
fu_cfu_device_offer_to_string(buf2[13]),
|
||||||
|
fu_cfu_device_reject_to_string(buf2[9]));
|
||||||
|
if (buf2[13] != FU_CFU_DEVICE_OFFER_ACCEPT) {
|
||||||
|
g_set_error(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"not supported: %s",
|
||||||
|
fu_cfu_device_offer_to_string(buf2[13]));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_cfu_device_write_payload(FuCfuDevice *self,
|
||||||
|
FuFirmware *firmware,
|
||||||
|
FuProgress *progress,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(GPtrArray) chunks = NULL;
|
||||||
|
|
||||||
|
/* write each chunk */
|
||||||
|
chunks = fu_firmware_get_chunks(firmware, error);
|
||||||
|
if (chunks == NULL)
|
||||||
|
return FALSE;
|
||||||
|
fu_progress_set_id(progress, G_STRLOC);
|
||||||
|
fu_progress_set_steps(progress, chunks->len);
|
||||||
|
for (guint i = 0; i < chunks->len; i++) {
|
||||||
|
FuChunk *chk = g_ptr_array_index(chunks, i);
|
||||||
|
guint8 databuf[60] = {0};
|
||||||
|
guint8 buf2[60] = {0};
|
||||||
|
|
||||||
|
/* flags */
|
||||||
|
if (i == 0)
|
||||||
|
databuf[0] = FU_CFU_DEVICE_FLAG_FIRST_BLOCK;
|
||||||
|
else if (i == chunks->len - 1)
|
||||||
|
databuf[0] = FU_CFU_DEVICE_FLAG_LAST_BLOCK;
|
||||||
|
|
||||||
|
/* length */
|
||||||
|
databuf[1] = fu_chunk_get_data_sz(chk);
|
||||||
|
|
||||||
|
/* sequence number */
|
||||||
|
if (!fu_common_write_uint16_safe(databuf,
|
||||||
|
sizeof(databuf),
|
||||||
|
0x2,
|
||||||
|
i + 1,
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* address */
|
||||||
|
if (!fu_common_write_uint32_safe(databuf,
|
||||||
|
sizeof(databuf),
|
||||||
|
0x4,
|
||||||
|
fu_chunk_get_address(chk),
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* data */
|
||||||
|
if (!fu_memcpy_safe(databuf,
|
||||||
|
sizeof(databuf),
|
||||||
|
0x8, /* dst */
|
||||||
|
fu_chunk_get_data(chk),
|
||||||
|
fu_chunk_get_data_sz(chk),
|
||||||
|
0x0, /* src */
|
||||||
|
fu_chunk_get_data_sz(chk),
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "memory copy for payload fail: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
// send
|
||||||
|
// revc
|
||||||
|
if (buf2[5] != FU_CFU_DEVICE_STATUS_SUCCESS) {
|
||||||
|
g_set_error(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"failed to send chunk %u: %s",
|
||||||
|
i + 1,
|
||||||
|
fu_cfu_device_status_to_string(buf2[5]));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
fu_progress_step_done(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_cfu_device_write_firmware(FuDevice *device,
|
||||||
|
FuFirmware *firmware,
|
||||||
|
FuProgress *progress,
|
||||||
|
FwupdInstallFlags flags,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuCfuDevice *self = FU_CFU_DEVICE(device);
|
||||||
|
g_autoptr(FuFirmware) fw_offer = NULL;
|
||||||
|
g_autoptr(FuFirmware) fw_payload = NULL;
|
||||||
|
|
||||||
|
/* progress */
|
||||||
|
fu_progress_set_id(progress, G_STRLOC);
|
||||||
|
fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED);
|
||||||
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2); /* offer */
|
||||||
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98); /* payload */
|
||||||
|
|
||||||
|
/* send offer */
|
||||||
|
fw_offer = fu_firmware_get_image_by_id(firmware, FU_FIRMWARE_ID_HEADER, error);
|
||||||
|
if (fw_offer == NULL)
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_cfu_device_write_offer(self,
|
||||||
|
fw_offer,
|
||||||
|
fu_progress_get_child(progress),
|
||||||
|
flags,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
fu_progress_step_done(progress);
|
||||||
|
|
||||||
|
/* send payload */
|
||||||
|
fw_payload = fu_firmware_get_image_by_id(firmware, FU_FIRMWARE_ID_PAYLOAD, error);
|
||||||
|
if (fw_payload == NULL)
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_cfu_device_write_payload(self, fw_payload, fu_progress_get_child(progress), error))
|
||||||
|
return FALSE;
|
||||||
|
fu_progress_step_done(progress);
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_cfu_device_setup(FuDevice *device, GError **error)
|
||||||
|
{
|
||||||
|
FuCfuDevice *self = FU_CFU_DEVICE(device);
|
||||||
|
guint8 buf[FU_CFU_FEATURE_SIZE] = {0};
|
||||||
|
guint8 component_cnt = 0;
|
||||||
|
guint8 tmp = 0;
|
||||||
|
gsize offset = 0;
|
||||||
|
g_autoptr(GHashTable) modules_by_cid = NULL;
|
||||||
|
|
||||||
|
/* FuHidDevice->setup */
|
||||||
|
if (!FU_DEVICE_CLASS(fu_cfu_device_parent_class)->setup(device, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* get version */
|
||||||
|
if (!fu_hid_device_get_report(FU_HID_DEVICE(device),
|
||||||
|
FU_CFU_CMD_GET_FIRMWARE_VERSION,
|
||||||
|
buf,
|
||||||
|
sizeof(buf),
|
||||||
|
FU_CFU_DEVICE_TIMEOUT,
|
||||||
|
FU_HID_DEVICE_FLAG_IS_FEATURE,
|
||||||
|
error)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!fu_common_read_uint8_safe(buf, sizeof(buf), 0x0, &component_cnt, error))
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_common_read_uint8_safe(buf, sizeof(buf), 0x3, &tmp, error))
|
||||||
|
return FALSE;
|
||||||
|
self->protocol_version = tmp & 0b1111;
|
||||||
|
|
||||||
|
/* keep track of all modules so we can work out which are dual bank */
|
||||||
|
modules_by_cid = g_hash_table_new(g_int_hash, g_int_equal);
|
||||||
|
|
||||||
|
/* read each component module version */
|
||||||
|
offset += 4;
|
||||||
|
for (guint i = 0; i < component_cnt; i++) {
|
||||||
|
g_autoptr(FuCfuModule) module = fu_cfu_module_new(device);
|
||||||
|
FuCfuModule *module_tmp;
|
||||||
|
|
||||||
|
if (!fu_cfu_module_setup(module, buf, sizeof(buf), offset, error))
|
||||||
|
return FALSE;
|
||||||
|
fu_device_add_child(device, FU_DEVICE(module));
|
||||||
|
|
||||||
|
/* same module already exists, so mark both as being dual bank */
|
||||||
|
module_tmp =
|
||||||
|
g_hash_table_lookup(modules_by_cid,
|
||||||
|
GINT_TO_POINTER(fu_cfu_module_get_component_id(module)));
|
||||||
|
if (module_tmp != NULL) {
|
||||||
|
fu_device_add_flag(FU_DEVICE(module), FWUPD_DEVICE_FLAG_DUAL_IMAGE);
|
||||||
|
fu_device_add_flag(FU_DEVICE(module_tmp), FWUPD_DEVICE_FLAG_DUAL_IMAGE);
|
||||||
|
} else {
|
||||||
|
g_hash_table_insert(modules_by_cid,
|
||||||
|
GINT_TO_POINTER(fu_cfu_module_get_component_id(module)),
|
||||||
|
module);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* done */
|
||||||
|
offset += 0x8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_cfu_device_init(FuCfuDevice *self)
|
||||||
|
{
|
||||||
|
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_cfu_device_class_init(FuCfuDeviceClass *klass)
|
||||||
|
{
|
||||||
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
||||||
|
klass_device->setup = fu_cfu_device_setup;
|
||||||
|
klass_device->to_string = fu_cfu_device_to_string;
|
||||||
|
klass_device->write_firmware = fu_cfu_device_write_firmware;
|
||||||
|
}
|
12
plugins/cfu/fu-cfu-device.h
Normal file
12
plugins/cfu/fu-cfu-device.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fwupdplugin.h>
|
||||||
|
|
||||||
|
#define FU_TYPE_CFU_DEVICE (fu_cfu_device_get_type())
|
||||||
|
G_DECLARE_FINAL_TYPE(FuCfuDevice, fu_cfu_device, FU, CFU_DEVICE, FuHidDevice)
|
200
plugins/cfu/fu-cfu-module.c
Normal file
200
plugins/cfu/fu-cfu-module.c
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*#
|
||||||
|
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <fwupdplugin.h>
|
||||||
|
|
||||||
|
#include "fu-cfu-module.h"
|
||||||
|
|
||||||
|
struct _FuCfuModule {
|
||||||
|
FuDevice parent_instance;
|
||||||
|
guint8 component_id;
|
||||||
|
guint8 bank;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(FuCfuModule, fu_cfu_module, FU_TYPE_DEVICE)
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_cfu_module_to_string(FuDevice *device, guint idt, GString *str)
|
||||||
|
{
|
||||||
|
FuCfuModule *self = FU_CFU_MODULE(device);
|
||||||
|
fu_common_string_append_kx(str, idt, "ComponentId", self->component_id);
|
||||||
|
fu_common_string_append_kx(str, idt, "Bank", self->bank);
|
||||||
|
}
|
||||||
|
|
||||||
|
guint8
|
||||||
|
fu_cfu_module_get_component_id(FuCfuModule *self)
|
||||||
|
{
|
||||||
|
return self->component_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
guint8
|
||||||
|
fu_cfu_module_get_bank(FuCfuModule *self)
|
||||||
|
{
|
||||||
|
return self->bank;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
fu_cfu_module_setup(FuCfuModule *self, const guint8 *buf, gsize bufsz, gsize offset, GError **error)
|
||||||
|
{
|
||||||
|
FuDevice *parent = fu_device_get_proxy(FU_DEVICE(self));
|
||||||
|
guint32 version_raw = 0;
|
||||||
|
guint8 tmp = 0;
|
||||||
|
g_autofree gchar *instance_id0 = NULL;
|
||||||
|
g_autofree gchar *instance_id1 = NULL;
|
||||||
|
g_autofree gchar *instance_id2 = NULL;
|
||||||
|
g_autofree gchar *logical_id = NULL;
|
||||||
|
g_autofree gchar *version = NULL;
|
||||||
|
|
||||||
|
/* component ID */
|
||||||
|
if (!fu_common_read_uint8_safe(buf, bufsz, offset + 0x5, &self->component_id, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* these GUIDs may cause the name or version-format to be overwritten */
|
||||||
|
instance_id0 = g_strdup_printf("HIDRAW\\VEN_%04X&DEV_%04X",
|
||||||
|
fu_udev_device_get_vendor(FU_UDEV_DEVICE(parent)),
|
||||||
|
fu_udev_device_get_model(FU_UDEV_DEVICE(parent)));
|
||||||
|
fu_device_add_instance_id(FU_DEVICE(self), instance_id0);
|
||||||
|
instance_id1 = g_strdup_printf("HIDRAW\\VEN_%04X&DEV_%04X&CID_%02X",
|
||||||
|
fu_udev_device_get_vendor(FU_UDEV_DEVICE(parent)),
|
||||||
|
fu_udev_device_get_model(FU_UDEV_DEVICE(parent)),
|
||||||
|
self->component_id);
|
||||||
|
fu_device_add_instance_id(FU_DEVICE(self), instance_id1);
|
||||||
|
|
||||||
|
/* bank */
|
||||||
|
if (!fu_common_read_uint8_safe(buf, bufsz, offset + 0x4, &tmp, error))
|
||||||
|
return FALSE;
|
||||||
|
self->bank = tmp & 0b11;
|
||||||
|
instance_id2 = g_strdup_printf("HIDRAW\\VEN_%04X&DEV_%04X&CID_%02X&BANK_%01X",
|
||||||
|
fu_udev_device_get_vendor(FU_UDEV_DEVICE(parent)),
|
||||||
|
fu_udev_device_get_model(FU_UDEV_DEVICE(parent)),
|
||||||
|
self->component_id,
|
||||||
|
self->bank);
|
||||||
|
fu_device_add_instance_id(FU_DEVICE(self), instance_id2);
|
||||||
|
|
||||||
|
/* set name, if not already set using a quirk */
|
||||||
|
if (fu_device_get_name(FU_DEVICE(self)) == NULL) {
|
||||||
|
g_autofree gchar *name = NULL;
|
||||||
|
name = g_strdup_printf("%s (0x%02X:0x%02x)",
|
||||||
|
fu_device_get_name(parent),
|
||||||
|
self->component_id,
|
||||||
|
self->bank);
|
||||||
|
fu_device_set_name(FU_DEVICE(self), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* version */
|
||||||
|
if (!fu_common_read_uint32_safe(buf, bufsz, offset, &version_raw, G_LITTLE_ENDIAN, error))
|
||||||
|
return FALSE;
|
||||||
|
fu_device_set_version_raw(FU_DEVICE(self), version_raw);
|
||||||
|
version = fu_common_version_from_uint32(version_raw,
|
||||||
|
fu_device_get_version_format(FU_DEVICE(self)));
|
||||||
|
|
||||||
|
/* logical ID */
|
||||||
|
logical_id = g_strdup_printf("CID:0x%02x,BANK:0x%02x", self->component_id, self->bank);
|
||||||
|
fu_device_set_logical_id(FU_DEVICE(self), logical_id);
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FuFirmware *
|
||||||
|
fu_cfu_module_prepare_firmware(FuDevice *device,
|
||||||
|
GBytes *fw,
|
||||||
|
FwupdInstallFlags flags,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(FuFirmware) firmware = fu_firmware_new();
|
||||||
|
g_autoptr(FuFirmware) offer = fu_cfu_offer_new();
|
||||||
|
g_autoptr(FuFirmware) payload = fu_cfu_payload_new();
|
||||||
|
g_autoptr(GBytes) fw_offset = NULL;
|
||||||
|
|
||||||
|
/* offer */
|
||||||
|
if (!fu_firmware_parse(offer, fw, flags, error))
|
||||||
|
return NULL;
|
||||||
|
fu_firmware_set_id(offer, FU_FIRMWARE_ID_HEADER);
|
||||||
|
fu_firmware_add_image(firmware, offer);
|
||||||
|
|
||||||
|
/* payload */
|
||||||
|
fw_offset = fu_common_bytes_new_offset(fw, 0x10, g_bytes_get_size(fw) - 0x10, error);
|
||||||
|
if (fw_offset == NULL)
|
||||||
|
return NULL;
|
||||||
|
if (!fu_firmware_parse(payload, fw_offset, flags, error))
|
||||||
|
return NULL;
|
||||||
|
fu_firmware_set_id(payload, FU_FIRMWARE_ID_PAYLOAD);
|
||||||
|
fu_firmware_add_image(firmware, payload);
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return g_steal_pointer(&firmware);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_cfu_module_write_firmware(FuDevice *device,
|
||||||
|
FuFirmware *firmware,
|
||||||
|
FuProgress *progress,
|
||||||
|
FwupdInstallFlags flags,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuDevice *proxy;
|
||||||
|
g_autoptr(GBytes) fw = NULL;
|
||||||
|
|
||||||
|
/* get the whole image */
|
||||||
|
fw = fu_firmware_get_bytes(firmware, error);
|
||||||
|
if (fw == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* process by the parent */
|
||||||
|
proxy = fu_device_get_proxy(device);
|
||||||
|
if (proxy == NULL) {
|
||||||
|
g_set_error(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"no proxy device assigned");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return fu_device_write_firmware(proxy, fw, progress, flags, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_cfu_module_set_progress(FuDevice *self, FuProgress *progress)
|
||||||
|
{
|
||||||
|
fu_progress_set_id(progress, G_STRLOC);
|
||||||
|
fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED);
|
||||||
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2); /* detach */
|
||||||
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 96); /* write */
|
||||||
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2); /* attach */
|
||||||
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2); /* reload */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_cfu_module_init(FuCfuModule *self)
|
||||||
|
{
|
||||||
|
fu_device_add_protocol(FU_DEVICE(self), "com.microsoft.cfu");
|
||||||
|
fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_cfu_module_class_init(FuCfuModuleClass *klass)
|
||||||
|
{
|
||||||
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
||||||
|
klass_device->to_string = fu_cfu_module_to_string;
|
||||||
|
klass_device->prepare_firmware = fu_cfu_module_prepare_firmware;
|
||||||
|
klass_device->write_firmware = fu_cfu_module_write_firmware;
|
||||||
|
klass_device->set_progress = fu_cfu_module_set_progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
FuCfuModule *
|
||||||
|
fu_cfu_module_new(FuDevice *parent)
|
||||||
|
{
|
||||||
|
FuCfuModule *self;
|
||||||
|
self = g_object_new(FU_TYPE_CFU_MODULE,
|
||||||
|
"ctx",
|
||||||
|
fu_device_get_context(parent),
|
||||||
|
"proxy",
|
||||||
|
parent,
|
||||||
|
NULL);
|
||||||
|
return self;
|
||||||
|
}
|
25
plugins/cfu/fu-cfu-module.h
Normal file
25
plugins/cfu/fu-cfu-module.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fwupdplugin.h>
|
||||||
|
|
||||||
|
#define FU_TYPE_CFU_MODULE (fu_cfu_module_get_type())
|
||||||
|
G_DECLARE_FINAL_TYPE(FuCfuModule, fu_cfu_module, FU, CFU_MODULE, FuDevice)
|
||||||
|
|
||||||
|
guint8
|
||||||
|
fu_cfu_module_get_component_id(FuCfuModule *self);
|
||||||
|
guint8
|
||||||
|
fu_cfu_module_get_bank(FuCfuModule *self);
|
||||||
|
gboolean
|
||||||
|
fu_cfu_module_setup(FuCfuModule *self,
|
||||||
|
const guint8 *buf,
|
||||||
|
gsize bufsz,
|
||||||
|
gsize offset,
|
||||||
|
GError **error);
|
||||||
|
FuCfuModule *
|
||||||
|
fu_cfu_module_new(FuDevice *parent);
|
18
plugins/cfu/fu-plugin-cfu.c
Normal file
18
plugins/cfu/fu-plugin-cfu.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <fwupdplugin.h>
|
||||||
|
|
||||||
|
#include "fu-cfu-device.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
fu_plugin_init(FuPlugin *plugin)
|
||||||
|
{
|
||||||
|
fu_plugin_set_build_hash(plugin, FU_BUILD_HASH);
|
||||||
|
fu_plugin_add_device_gtype(plugin, FU_TYPE_CFU_DEVICE);
|
||||||
|
}
|
31
plugins/cfu/meson.build
Normal file
31
plugins/cfu/meson.build
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
cargs = ['-DG_LOG_DOMAIN="FuPluginCfu"']
|
||||||
|
|
||||||
|
install_data([
|
||||||
|
'cfu.quirk',
|
||||||
|
],
|
||||||
|
install_dir: join_paths(datadir, 'fwupd', 'quirks.d')
|
||||||
|
)
|
||||||
|
|
||||||
|
shared_module('fu_plugin_cfu',
|
||||||
|
fu_hash,
|
||||||
|
sources : [
|
||||||
|
'fu-cfu-device.c',
|
||||||
|
'fu-cfu-module.c',
|
||||||
|
'fu-plugin-cfu.c',
|
||||||
|
],
|
||||||
|
include_directories : [
|
||||||
|
root_incdir,
|
||||||
|
fwupd_incdir,
|
||||||
|
fwupdplugin_incdir,
|
||||||
|
],
|
||||||
|
install : true,
|
||||||
|
install_dir: plugin_dir,
|
||||||
|
link_with : [
|
||||||
|
fwupd,
|
||||||
|
fwupdplugin,
|
||||||
|
],
|
||||||
|
c_args : cargs,
|
||||||
|
dependencies : [
|
||||||
|
plugin_deps,
|
||||||
|
],
|
||||||
|
)
|
@ -8,6 +8,7 @@ subdir('ata')
|
|||||||
subdir('bcm57xx')
|
subdir('bcm57xx')
|
||||||
subdir('bios')
|
subdir('bios')
|
||||||
subdir('ccgx')
|
subdir('ccgx')
|
||||||
|
subdir('cfu')
|
||||||
subdir('colorhug')
|
subdir('colorhug')
|
||||||
subdir('cpu')
|
subdir('cpu')
|
||||||
subdir('cros-ec')
|
subdir('cros-ec')
|
||||||
|
Loading…
Reference in New Issue
Block a user