diff --git a/plugins/ccgx/README.md b/plugins/ccgx/README.md index 6079161a3..a3b192c41 100644 --- a/plugins/ccgx/README.md +++ b/plugins/ccgx/README.md @@ -108,8 +108,18 @@ This plugin uses the following plugin-specific quirks: DMC devices need a specified trigger code to request the device to update the firmware and the trigger code depends on the devices. + 0x0: Do not update + 0x1: Update immediately + 0x2: Update after port disconnected + Since: 1.8.0 +### CcgxDmcCompositeVersion + +Set the parent composite version, as a 32 bit integer. + +Since: 1.8.11 + ## External Interface Access This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/ccgx/fu-ccgx-dmc-common.c b/plugins/ccgx/fu-ccgx-dmc-common.c index 6a963e051..3966c2fd1 100644 --- a/plugins/ccgx/fu-ccgx-dmc-common.c +++ b/plugins/ccgx/fu-ccgx-dmc-common.c @@ -19,3 +19,115 @@ fu_ccgx_dmc_update_model_type_to_string(DmcUpdateModel val) return "Pending Reset"; return NULL; } + +const gchar * +fu_ccgx_dmc_devx_device_type_to_string(DmcDevxDeviceType device_type) +{ + if (device_type == DMC_DEVX_DEVICE_TYPE_INVALID) + return "invalid"; + if (device_type == DMC_DEVX_DEVICE_TYPE_CCG3) + return "ccg3"; + if (device_type == DMC_DEVX_DEVICE_TYPE_DMC) + return "dmc"; + if (device_type == DMC_DEVX_DEVICE_TYPE_CCG4) + return "ccg4"; + if (device_type == DMC_DEVX_DEVICE_TYPE_CCG5) + return "ccg5"; + if (device_type == DMC_DEVX_DEVICE_TYPE_HX3) + return "hx3"; + if (device_type == DMC_DEVX_DEVICE_TYPE_HX3_PD) + return "hx3-pd"; + if (device_type == DMC_DEVX_DEVICE_TYPE_DMC_PD) + return "dmc-pd"; + if (device_type == DMC_DEVX_DEVICE_TYPE_SPI) + return "spi"; + return NULL; +} + +const gchar * +fu_ccgx_dmc_img_mode_to_string(DmcImgMode img_mode) +{ + if (img_mode == DMC_IMG_MODE_SINGLE_IMG) + return "single"; + if (img_mode == DMC_IMG_MODE_DUAL_IMG_SYM) + return "dual-sym"; + if (img_mode == DMC_IMG_MODE_DUAL_IMG_ASYM) + return "dual-asym"; + if (img_mode == DMC_IMG_MODE_SINGLE_IMG_WITH_RAM_IMG) + return "single-with-ram-img"; + return NULL; +} + +const gchar * +fu_ccgx_dmc_device_status_to_string(DmcDeviceStatus device_status) +{ + if (device_status == DMC_DEVICE_STATUS_IDLE) + return "idle"; + if (device_status == DMC_DEVICE_STATUS_UPDATE_IN_PROGRESS) + return "update-in-progress"; + if (device_status == DMC_DEVICE_STATUS_UPDATE_PARTIAL) + return "update-partial"; + if (device_status == DMC_DEVICE_STATUS_UPDATE_COMPLETE_FULL) + return "update-complete-full"; + if (device_status == DMC_DEVICE_STATUS_UPDATE_COMPLETE_PARTIAL) + return "update-complete-partial"; + if (device_status == DMC_DEVICE_STATUS_UPDATE_PHASE_1_COMPLETE) + return "update-phase1-complete"; + if (device_status == DMC_DEVICE_STATUS_FW_DOWNLOADED_UPDATE_PEND) + return "fw-downloaded-update-pend"; + if (device_status == DMC_DEVICE_STATUS_FW_DOWNLOADED_PARTIAL_UPDATE_PEND) + return "fw-downloaded-partial-update-pend"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_IN_PROGRESS) + return "phase2-update-in-progress"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_PARTIAL) + return "phase2-update-partial"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_FACTORY_BACKUP) + return "phase2-update-factory-backup"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_COMPLETE_PARTIAL) + return "phase2-update-complete-partial"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_COMPLETE_FULL) + return "phase2-update-complete-full"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_INVALID_FWCT) + return "phase2-update-fail-invalid-fwct"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_INVALID_DOCK_IDENTITY) + return "phase2-update-fail-invalid-dock-identifier"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_INVALID_COMPOSITE_VER) + return "phase2-update-fail-invalid-composite-ver"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_AUTHENTICATION_FAILED) + return "phase2-update-fail-authentication-failed"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_INVALID_ALGORITHM) + return "phase2-update-fail-invalid-algorithm"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_SPI_READ_FAILED) + return "phase2-update-fail-spi-read-failed"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_NO_VALID_KEY) + return "phase2-update-fail-no-valid-key"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_NO_VALID_SPI_PACKAGE) + return "phase2-update-fail-no-valid-spi-package"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_RAM_INIT_FAILED) + return "phase2-update-fail-ram-init-failed"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_FACTORY_BACKUP_FAILED) + return "phase2-update-fail-factory-backup-failed"; + if (device_status == DMC_DEVICE_STATUS_PHASE2_UPDATE_FAIL_NO_VALID_FACTORY_PACKAGE) + return "phase2-update-fail-no-valid-factory-package"; + if (device_status == DMC_DEVICE_STATUS_UPDATE_FAIL) + return "update-fail"; + return NULL; +} + +const gchar * +fu_ccgx_dmc_img_status_to_string(DmcImgStatus img_status) +{ + if (img_status == DMC_IMG_STATUS_UNKNOWN) + return "unknown"; + if (img_status == DMC_IMG_STATUS_VALID) + return "valid"; + if (img_status == DMC_IMG_STATUS_INVALID) + return "invalid"; + if (img_status == DMC_IMG_STATUS_RECOVERY) + return "recovery"; + if (img_status == DMC_IMG_STATUS_RECOVERED_FROM_SECONDARY) + return "recovered-from-secondary"; + if (img_status == DMC_IMG_STATUS_NOT_SUPPORTED) + return "not-supported"; + return NULL; +} diff --git a/plugins/ccgx/fu-ccgx-dmc-common.h b/plugins/ccgx/fu-ccgx-dmc-common.h index be4c572de..86aba6298 100644 --- a/plugins/ccgx/fu-ccgx-dmc-common.h +++ b/plugins/ccgx/fu-ccgx-dmc-common.h @@ -80,6 +80,7 @@ typedef enum { * image. Secondary acts as recovery */ DMC_IMG_MODE_DUAL_IMG_ASYM, DMC_IMG_MODE_SINGLE_IMG_WITH_RAM_IMG, + DMC_IMG_MODE_LAST } DmcImgMode; /* this data type enumerates the dock status */ @@ -121,6 +122,18 @@ typedef enum { DMC_DEVICE_STATUS_UPDATE_FAIL = 0xFF } DmcDeviceStatus; +typedef enum { + DMC_DEVX_DEVICE_TYPE_INVALID = 0x00, + DMC_DEVX_DEVICE_TYPE_CCG3 = 0x01, + DMC_DEVX_DEVICE_TYPE_DMC = 0x02, + DMC_DEVX_DEVICE_TYPE_CCG4 = 0x03, + DMC_DEVX_DEVICE_TYPE_CCG5 = 0x04, + DMC_DEVX_DEVICE_TYPE_HX3 = 0x05, + DMC_DEVX_DEVICE_TYPE_HX3_PD = 0x0A, + DMC_DEVX_DEVICE_TYPE_DMC_PD = 0x0B, + DMC_DEVX_DEVICE_TYPE_SPI = 0xFF +} DmcDevxDeviceType; + /* this data type enumerates the request codes for vendor interface */ typedef enum { DMC_RQT_CODE_UPGRADE_START = 0xD0, @@ -282,3 +295,11 @@ typedef struct __attribute__((packed)) { const gchar * fu_ccgx_dmc_update_model_type_to_string(DmcUpdateModel val); +const gchar * +fu_ccgx_dmc_device_status_to_string(DmcDeviceStatus device_status); +const gchar * +fu_ccgx_dmc_devx_device_type_to_string(DmcDevxDeviceType device_type); +const gchar * +fu_ccgx_dmc_img_status_to_string(DmcImgStatus img_status); +const gchar * +fu_ccgx_dmc_img_mode_to_string(DmcImgMode img_mode); diff --git a/plugins/ccgx/fu-ccgx-dmc-device.c b/plugins/ccgx/fu-ccgx-dmc-device.c index 6de1600af..79f475bdc 100644 --- a/plugins/ccgx/fu-ccgx-dmc-device.c +++ b/plugins/ccgx/fu-ccgx-dmc-device.c @@ -12,6 +12,7 @@ #include "fu-ccgx-common.h" #include "fu-ccgx-dmc-common.h" #include "fu-ccgx-dmc-device.h" +#include "fu-ccgx-dmc-devx-device.h" #include "fu-ccgx-dmc-firmware.h" #define DMC_FW_WRITE_STATUS_RETRY_COUNT 3 @@ -21,6 +22,7 @@ struct _FuCcgxDmcDevice { FuUsbDevice parent_instance; FWImageType fw_image_type; DmcDockIdentity dock_id; + DmcDockStatus dock_status; guint8 ep_intr_in; guint8 ep_bulk_out; DmcUpdateModel update_model; @@ -37,10 +39,8 @@ struct _FuCcgxDmcDevice { G_DEFINE_TYPE(FuCcgxDmcDevice, fu_ccgx_dmc_device, FU_TYPE_USB_DEVICE) static gboolean -fu_ccgx_dmc_device_get_dock_id(FuCcgxDmcDevice *self, DmcDockIdentity *dock_id, GError **error) +fu_ccgx_dmc_device_ensure_dock_id(FuCcgxDmcDevice *self, GError **error) { - g_return_val_if_fail(dock_id != NULL, FALSE); - if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, G_USB_DEVICE_REQUEST_TYPE_VENDOR, @@ -48,8 +48,8 @@ fu_ccgx_dmc_device_get_dock_id(FuCcgxDmcDevice *self, DmcDockIdentity *dock_id, DMC_RQT_CODE_DOCK_IDENTITY, /* request */ 0, /* value */ 0, /* index */ - (guint8 *)dock_id, /* data */ - sizeof(DmcDockIdentity), /* length */ + (guint8 *)&self->dock_id, /* data */ + sizeof(self->dock_id), /* length */ NULL, /* actual length */ DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, NULL, @@ -61,41 +61,37 @@ fu_ccgx_dmc_device_get_dock_id(FuCcgxDmcDevice *self, DmcDockIdentity *dock_id, } static gboolean -fu_ccgx_dmc_device_get_dock_status(FuCcgxDmcDevice *self, - DmcDockStatus *dock_status, - GError **error) +fu_ccgx_dmc_device_ensure_status(FuCcgxDmcDevice *self, GError **error) { - g_return_val_if_fail(dock_status != NULL, FALSE); - /* read minimum status length */ if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, G_USB_DEVICE_REQUEST_TYPE_VENDOR, G_USB_DEVICE_RECIPIENT_DEVICE, - DMC_RQT_CODE_DOCK_STATUS, /* request */ - 0, /* value */ - 0, /* index */ - (guint8 *)dock_status, /* data */ - DMC_GET_STATUS_MIN_LEN, /* length */ - NULL, /* actual length */ + DMC_RQT_CODE_DOCK_STATUS, /* request */ + 0, /* value */ + 0, /* index */ + (guint8 *)&self->dock_status, /* data */ + DMC_GET_STATUS_MIN_LEN, /* length */ + NULL, /* actual length */ DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, NULL, error)) { g_prefix_error(error, "get_dock_status min size error: "); return FALSE; } - if (dock_status->status_length <= sizeof(DmcDockStatus)) { + if (self->dock_status.status_length <= sizeof(self->dock_status)) { /* read full status length */ if (!g_usb_device_control_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, G_USB_DEVICE_REQUEST_TYPE_VENDOR, G_USB_DEVICE_RECIPIENT_DEVICE, - DMC_RQT_CODE_DOCK_STATUS, /* request */ - 0, /* value */ - 0, /* index */ - (guint8 *)dock_status, /* data */ - sizeof(DmcDockStatus), /* length */ - NULL, /* actual length */ + DMC_RQT_CODE_DOCK_STATUS, /* request */ + 0, /* value */ + 0, /* index */ + (guint8 *)&self->dock_status, /* data */ + sizeof(self->dock_status), /* length */ + NULL, /* actual length */ DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, NULL, error)) { @@ -103,6 +99,21 @@ fu_ccgx_dmc_device_get_dock_status(FuCcgxDmcDevice *self, return FALSE; } } + if (g_getenv("FWUPD_CCGX_VERBOSE") != NULL) { + fu_dump_raw(G_LOG_DOMAIN, + "DmcDockStatus", + (const guint8 *)&self->dock_status, + sizeof(self->dock_status)); + } + + /* add devx children */ + for (guint i = 0; i < self->dock_status.device_count; i++) { + g_autoptr(FuCcgxDmcDevxDevice) devx = + fu_ccgx_dmc_devx_device_new(FU_DEVICE(self), &self->dock_status.devx_status[i]); + fu_device_add_child(FU_DEVICE(self), FU_DEVICE(devx)); + } + + /* success */ return TRUE; } @@ -319,6 +330,13 @@ fu_ccgx_dmc_device_to_string(FuDevice *device, guint idt, GString *str) fu_string_append_kx(str, idt, "EpBulkOut", self->ep_bulk_out); fu_string_append_kx(str, idt, "EpIntrIn", self->ep_intr_in); fu_string_append_kx(str, idt, "TriggerCode", self->trigger_code); + fu_string_append(str, + idt, + "DeviceStatus", + fu_ccgx_dmc_device_status_to_string(self->dock_status.device_status)); + fu_string_append_kx(str, idt, "DeviceCount", self->dock_status.device_count); + fu_string_append_kx(str, idt, "StatusLength", self->dock_status.status_length); + fu_string_append_kx(str, idt, "CompositeVersion", self->dock_status.composite_version); } static gboolean @@ -651,41 +669,44 @@ fu_ccgx_dmc_device_attach(FuDevice *device, FuProgress *progress, GError **error return TRUE; } +static void +fu_ccgx_dmc_device_ensure_factory_version(FuCcgxDmcDevice *self) +{ + for (guint i = 0; i < DMC_DOCK_MAX_DEV_COUNT; i++) { + DmcDevxStatus *status = &self->dock_status.devx_status[i]; + guint64 fwver_img1 = fu_memread_uint64(status->fw_version + 0x08, G_LITTLE_ENDIAN); + guint64 fwver_img2 = fu_memread_uint64(status->fw_version + 0x10, G_LITTLE_ENDIAN); + if (status->device_type == 0x2 && fwver_img1 == fwver_img2 && fwver_img1 != 0) { + g_debug("overriding version as device is in factory mode"); + fu_device_set_version_from_uint32(FU_DEVICE(self), 0x1); + return; + } + } +} + static gboolean fu_ccgx_dmc_device_setup(FuDevice *device, GError **error) { FuCcgxDmcDevice *self = FU_CCGX_DMC_DEVICE(device); - DmcDockStatus dock_status = {0}; - DmcDockIdentity dock_id = {0}; /* FuUsbDevice->setup */ if (!FU_DEVICE_CLASS(fu_ccgx_dmc_device_parent_class)->setup(device, error)) return FALSE; /* get dock identity */ - if (!fu_ccgx_dmc_device_get_dock_id(self, &dock_id, error)) + if (!fu_ccgx_dmc_device_ensure_dock_id(self, error)) + return FALSE; + if (!fu_ccgx_dmc_device_ensure_status(self, error)) return FALSE; - /* store dock identity */ - if (!fu_memcpy_safe((guint8 *)&self->dock_id, - sizeof(DmcDockIdentity), - 0x0, /* dst */ - (guint8 *)&dock_id, - sizeof(DmcDockIdentity), - 0, /* src */ - sizeof(DmcDockIdentity), - error)) - return FALSE; - - /* get dock status */ - if (!fu_ccgx_dmc_device_get_dock_status(self, &dock_status, error)) - return FALSE; - - /* set composite version */ - fu_device_set_version_from_uint32(FU_DEVICE(self), dock_status.composite_version); + /* use composite version, but also try to detect "factory mode" where the SPI has been + * imaged but has not been updated manually to the initial version */ + fu_device_set_version_from_uint32(device, self->dock_status.composite_version); + if (fu_device_get_version_raw(device) == 0) + fu_ccgx_dmc_device_ensure_factory_version(self); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); - if (dock_id.custom_meta_data_flag > 0) + if (self->dock_id.custom_meta_data_flag > 0) fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); else fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); diff --git a/plugins/ccgx/fu-ccgx-dmc-devx-device.c b/plugins/ccgx/fu-ccgx-dmc-devx-device.c new file mode 100644 index 000000000..5e400332a --- /dev/null +++ b/plugins/ccgx/fu-ccgx-dmc-devx-device.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2023 Richard Hughes + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-ccgx-dmc-devx-device.h" + +#define DMC_FW_WRITE_STATUS_RETRY_COUNT 3 +#define DMC_FW_WRITE_STATUS_RETRY_DELAY_MS 30 + +struct _FuCcgxDmcDevxDevice { + FuDevice parent_instance; + DmcDevxStatus status; +}; + +G_DEFINE_TYPE(FuCcgxDmcDevxDevice, fu_ccgx_dmc_devx_device, FU_TYPE_DEVICE) + +static gchar * +fu_ccgx_dmc_devx_status_version_dmc_bfw(DmcDevxStatus *status, gsize offset) +{ + return g_strdup_printf("%u.%u.%u.%u", + status->fw_version[offset + 3] >> 4, + status->fw_version[offset + 3] & 0xFu, + status->fw_version[offset + 2], + fu_memread_uint16(status->fw_version + offset, G_LITTLE_ENDIAN)); +} + +static gchar * +fu_ccgx_dmc_devx_status_version_dmc_app(DmcDevxStatus *status, gsize offset) +{ + return g_strdup_printf("%u.%u.%u", + status->fw_version[offset + 4 + 3] >> 4, + status->fw_version[offset + 4 + 3] & 0xFu, + status->fw_version[offset + 4 + 2]); +} + +static gchar * +fu_ccgx_dmc_devx_status_version_hx3(DmcDevxStatus *status, gsize offset) +{ + return g_strdup_printf("%u.%u.%u", + status->fw_version[offset + 4 + 3], + status->fw_version[offset + 4 + 2], + status->fw_version[offset + 4 + 1]); +} + +static void +fu_ccgx_dmc_devx_device_hexver_to_string(FuCcgxDmcDevxDevice *self, + const gchar *kind, + gsize offset, + guint idt, + GString *str) +{ + DmcDevxStatus *status = &self->status; + g_autofree gchar *key = g_strdup_printf("FwVersion[%s]", kind); + g_autofree gchar *val = + fu_version_from_uint64(fu_memread_uint64(status->fw_version + offset, G_LITTLE_ENDIAN), + FWUPD_VERSION_FORMAT_HEX); + fu_string_append(str, idt, key, val); +} + +static void +fu_ccgx_dmc_devx_device_hx3ver_to_string(FuCcgxDmcDevxDevice *self, + const gchar *kind, + gsize offset, + guint idt, + GString *str) +{ + DmcDevxStatus *status = &self->status; + g_autofree gchar *key = g_strdup_printf("FwVersion[%s]", kind); + g_autofree gchar *val = fu_ccgx_dmc_devx_status_version_hx3(status, offset); + fu_string_append(str, idt, key, val); +} + +static void +fu_ccgx_dmc_devx_device_dmcver_to_string(FuCcgxDmcDevxDevice *self, + const gchar *kind, + gsize offset, + guint idt, + GString *str) +{ + DmcDevxStatus *status = &self->status; + g_autofree gchar *key = g_strdup_printf("FwVersion[%s]", kind); + g_autofree gchar *bfw_val = fu_ccgx_dmc_devx_status_version_dmc_bfw(status, offset); + g_autofree gchar *app_val = fu_ccgx_dmc_devx_status_version_dmc_app(status, offset); + g_autofree gchar *tmp = g_strdup_printf("base:%s\tapp:%s", bfw_val, app_val); + fu_string_append(str, idt, key, tmp); +} + +static DmcDevxDeviceType +fu_ccgx_dmc_devx_device_version_type(FuCcgxDmcDevxDevice *self) +{ + DmcDevxStatus *status = &self->status; + if (status->device_type == DMC_DEVX_DEVICE_TYPE_DMC || + status->device_type == DMC_DEVX_DEVICE_TYPE_CCG3 || + status->device_type == DMC_DEVX_DEVICE_TYPE_CCG4 || + status->device_type == DMC_DEVX_DEVICE_TYPE_CCG5 || status->device_type == 0x0B) + return DMC_DEVX_DEVICE_TYPE_DMC; + if (status->device_type == DMC_DEVX_DEVICE_TYPE_HX3) + return DMC_DEVX_DEVICE_TYPE_HX3; + return DMC_DEVX_DEVICE_TYPE_INVALID; +} + +static void +fu_ccgx_dmc_devx_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuCcgxDmcDevxDevice *self = FU_CCGX_DMC_DEVX_DEVICE(device); + DmcDevxStatus *status = &self->status; + DmcDevxDeviceType device_version_type = fu_ccgx_dmc_devx_device_version_type(self); + const gchar *device_type = fu_ccgx_dmc_devx_device_type_to_string(status->device_type); + + if (device_type != NULL) { + g_autofree gchar *tmp = + g_strdup_printf("0x%x [%s]", status->device_type, device_type); + fu_string_append(str, idt, "DeviceType", tmp); + } else { + fu_string_append_kx(str, idt, "DeviceType", status->device_type); + } + if (status->image_mode < DMC_IMG_MODE_LAST) { + g_autofree gchar *tmp = + g_strdup_printf("0x%x [%s]", + status->image_mode, + fu_ccgx_dmc_img_mode_to_string(status->image_mode)); + fu_string_append(str, idt, "ImageMode", tmp); + } else { + fu_string_append_kx(str, idt, "ImageMode", status->image_mode); + } + + fu_string_append_kx(str, idt, "CurrentImage", status->current_image); + fu_string_append(str, + idt, + "ImgStatus1", + fu_ccgx_dmc_img_status_to_string(status->img_status & 0x0F)); + fu_string_append(str, + idt, + "ImgStatus2", + fu_ccgx_dmc_img_status_to_string((status->img_status >> 4) & 0x0F)); + + /* versions */ + if (device_version_type == DMC_DEVX_DEVICE_TYPE_DMC) { + fu_ccgx_dmc_devx_device_dmcver_to_string(self, "boot", 0x00, idt, str); + fu_ccgx_dmc_devx_device_dmcver_to_string(self, "img1", 0x08, idt, str); + if (status->image_mode != DMC_IMG_MODE_SINGLE_IMG) + fu_ccgx_dmc_devx_device_dmcver_to_string(self, "img2", 0x10, idt, str); + } else if (device_version_type == DMC_DEVX_DEVICE_TYPE_HX3) { + fu_ccgx_dmc_devx_device_hx3ver_to_string(self, "boot", 0x00, idt, str); + fu_ccgx_dmc_devx_device_hx3ver_to_string(self, "img1", 0x08, idt, str); + if (status->image_mode != DMC_IMG_MODE_SINGLE_IMG) + fu_ccgx_dmc_devx_device_hx3ver_to_string(self, "img2", 0x10, idt, str); + } else { + fu_ccgx_dmc_devx_device_hexver_to_string(self, "boot", 0x00, idt, str); + fu_ccgx_dmc_devx_device_hexver_to_string(self, "img1", 0x08, idt, str); + if (status->image_mode != DMC_IMG_MODE_SINGLE_IMG) + fu_ccgx_dmc_devx_device_hexver_to_string(self, "img2", 0x10, idt, str); + } +} + +static gboolean +fu_ccgx_dmc_devx_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + if (g_strcmp0(key, "CcgxDmcCompositeVersion") == 0) { + guint64 tmp = 0; + FuDevice *proxy = fu_device_get_proxy(device); + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, error)) + return FALSE; + if (fu_device_get_version_raw(proxy) != tmp) { + g_debug("overriding composite version from %u to %u from %s", + (guint)fu_device_get_version_raw(proxy), + (guint)tmp, + fu_device_get_id(device)); + fu_device_set_version_from_uint32(proxy, tmp); + } + return TRUE; + } + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no supported"); + return FALSE; +} + +static const gchar * +fu_ccgx_dmc_devx_device_type_to_name(DmcDevxDeviceType device_type) +{ + if (device_type == DMC_DEVX_DEVICE_TYPE_CCG3) + return "CCG3"; + if (device_type == DMC_DEVX_DEVICE_TYPE_DMC) + return "DMC"; + if (device_type == DMC_DEVX_DEVICE_TYPE_CCG4) + return "CCG4"; + if (device_type == DMC_DEVX_DEVICE_TYPE_CCG5) + return "CCG5"; + if (device_type == DMC_DEVX_DEVICE_TYPE_HX3) + return "HX3"; + if (device_type == DMC_DEVX_DEVICE_TYPE_HX3_PD) + return "HX3 PD"; + if (device_type == DMC_DEVX_DEVICE_TYPE_DMC_PD) + return "DMC PD"; + if (device_type == DMC_DEVX_DEVICE_TYPE_SPI) + return "SPI"; + return "Unknown"; +} + +static gboolean +fu_ccgx_dmc_devx_device_probe(FuDevice *device, GError **error) +{ + FuCcgxDmcDevxDevice *self = FU_CCGX_DMC_DEVX_DEVICE(device); + FuDevice *proxy = fu_device_get_proxy(device); + DmcDevxStatus *status = &self->status; + DmcDevxDeviceType device_version_type = fu_ccgx_dmc_devx_device_version_type(self); + gsize offset = 0; + g_autofree gchar *logical_id = g_strdup_printf("0x%02x", status->component_id); + g_autofree gchar *version = NULL; + + fu_device_set_name(device, fu_ccgx_dmc_devx_device_type_to_name(status->device_type)); + fu_device_set_logical_id(device, logical_id); + + /* for the version number */ + if (status->current_image == 0x01) + offset = 4; + else if (status->current_image == 0x02) + offset = 8; + + /* version, if possible */ + if (device_version_type == DMC_DEVX_DEVICE_TYPE_DMC) { + version = fu_ccgx_dmc_devx_status_version_dmc_bfw(status, offset); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + } else if (device_version_type == DMC_DEVX_DEVICE_TYPE_HX3) { + version = fu_ccgx_dmc_devx_status_version_hx3(status, offset); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, version); + } + if (version != NULL) { + fu_device_set_version(device, version); + fu_device_add_instance_strsafe(device, "VER", version); + } + + /* add GUIDs */ + fu_device_add_instance_strup(device, + "TYPE", + fu_ccgx_dmc_devx_device_type_to_string(status->device_type)); + fu_device_add_instance_u8(device, "CID", status->component_id); + fu_device_add_instance_u16(device, "VID", fu_usb_device_get_vid(FU_USB_DEVICE(proxy))); + fu_device_add_instance_u16(device, "PID", fu_usb_device_get_pid(FU_USB_DEVICE(proxy))); + fu_device_build_instance_id(device, NULL, "USB", "VID", "PID", "CID", NULL); + fu_device_build_instance_id_quirk(device, NULL, "USB", "VID", "PID", "CID", "TYPE", NULL); + fu_device_build_instance_id_quirk(device, NULL, "USB", "VID", "PID", "CID", "VER", NULL); + + /* success */ + return TRUE; +} + +static void +fu_ccgx_dmc_devx_device_init(FuCcgxDmcDevxDevice *self) +{ +} + +static void +fu_ccgx_dmc_devx_device_class_init(FuCcgxDmcDevxDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->probe = fu_ccgx_dmc_devx_device_probe; + klass_device->to_string = fu_ccgx_dmc_devx_device_to_string; + klass_device->set_quirk_kv = fu_ccgx_dmc_devx_device_set_quirk_kv; +} + +FuCcgxDmcDevxDevice * +fu_ccgx_dmc_devx_device_new(FuDevice *proxy, DmcDevxStatus *status) +{ + FuCcgxDmcDevxDevice *self = g_object_new(FU_TYPE_CCGX_DMC_DEVX_DEVICE, + "context", + fu_device_get_context(proxy), + "proxy", + proxy, + NULL); + self->status = *status; + return self; +} diff --git a/plugins/ccgx/fu-ccgx-dmc-devx-device.h b/plugins/ccgx/fu-ccgx-dmc-devx-device.h new file mode 100644 index 000000000..c832adbed --- /dev/null +++ b/plugins/ccgx/fu-ccgx-dmc-devx-device.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 Richard Hughes + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-ccgx-dmc-common.h" + +#define FU_TYPE_CCGX_DMC_DEVX_DEVICE (fu_ccgx_dmc_devx_device_get_type()) +G_DECLARE_FINAL_TYPE(FuCcgxDmcDevxDevice, + fu_ccgx_dmc_devx_device, + FU, + CCGX_DMC_DEVX_DEVICE, + FuDevice) + +FuCcgxDmcDevxDevice * +fu_ccgx_dmc_devx_device_new(FuDevice *proxy, DmcDevxStatus *status); diff --git a/plugins/ccgx/meson.build b/plugins/ccgx/meson.build index 949591fac..35eb92abf 100644 --- a/plugins/ccgx/meson.build +++ b/plugins/ccgx/meson.build @@ -14,6 +14,7 @@ plugin_builtin_ccgx = static_library('fu_plugin_ccgx', 'fu-ccgx-hpi-common.c', 'fu-ccgx-hpi-device.c', 'fu-ccgx-dmc-device.c', + 'fu-ccgx-dmc-devx-device.c', 'fu-ccgx-dmc-firmware.c', # fuzzing 'fu-ccgx-dmc-common.c', # fuzzing ],