Add a new plugin to handle HP M2xfd monitors

Added support for genesys logic devices.
This commit is contained in:
Ricardo Cañuelo 2021-09-28 10:30:39 +02:00 committed by Richard Hughes
parent cc19be590a
commit 9466e778f5
20 changed files with 4329 additions and 0 deletions

View File

@ -352,6 +352,8 @@ def _build(bld: Builder) -> None:
Fuzzer("ebitdo"),
Fuzzer("elanfp"),
Fuzzer("elantp"),
Fuzzer("genesys-scaler", srcdir="genesys", pattern="genesys-scaler-firmware"),
Fuzzer("genesys-usbhub", srcdir="genesys", pattern="genesys-usbhub-firmware"),
Fuzzer("pixart", srcdir="pixart-rf", pattern="pxi-firmware"),
Fuzzer("redfish-smbios", srcdir="redfish", pattern="redfish-smbios"),
Fuzzer("synaptics-prometheus", pattern="synaprom-firmware"),

View File

@ -435,6 +435,7 @@ done
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_ep963x.so
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_fastboot.so
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_fresco_pd.so
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_genesys.so
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_gpio.so
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_hailuck.so
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_iommu.so

134
plugins/genesys/README.md Normal file
View File

@ -0,0 +1,134 @@
# Genesys Logic
## Introduction
This plugin allows updating the Genesys Logic USB Hub devices.
* GL3523
* GL3590
Additionally, this plugin allows updating the MStar Semiconductor Scaler connected via an I²C bus.
* TSUM G
## Firmware Format
The daemon will decompress the cabinet archives and extract the firmware blob in an unspecified binary file format.
This plugin supports the following protocol IDs:
* com.genesys.usbhub
* com.mstarsemi.scaler
## GUID Generation
These devices use the standard USB DeviceInstanceId values for the USB Hub, e.g.
* HP Mxfd FHD Monitor: `USB\VID_03F0&PID_0610`
These devices also use custom GUID values for the Scaler, e.g.
* HP M24fd USB-C Monitor: `GENESYS_SCALER\MSTAR_TSUM_G&PUBKEY_B335BDCE-7073-5D0E-9BD3-9B69C1A6899F`
* HP M27fd USB-C Monitor: `GENESYS_SCALER\MSTAR_TSUM_G&PUBKEY_847A3650-8648-586B-83C8-8B53714F37E3`
The Public Key is product-specific and is required to identify the product.
## Quirk Use
This plugin uses the following plugin-specific quirks:
### has-mstar-scaler
USB Hub has a MStar Semiconductor Scaler attached via I²C.
Since 1.7.6.
### GenesysUsbhubSwitchRequest
USB Hub Switch Request value.
* HP Mxfd FHD Monitors: `0xA1`
Since 1.7.6.
### GenesysUsbhubReadRequest
USB Hub Read Request value.
* HP Mxfd FHD Monitors: `0xA2`
Since 1.7.6.
### GenesysUsbhubWriteRequest
USB Hub Write Request value.
* HP Mxfd FHD Monitors: `0xA3`
Since 1.7.6.
### use-i2c-ch0
Scalar uses I²C channel 0.
Since 1.7.6.
### pause-r2-cpu
Scalar pause R2 CPU.
Since 1.7.6.
### GenesysScalerDeviceTransferSize
Scaler Block size to use for transfers.
* MStar Semiconductor TSUM G: `0x40`
Since 1.7.6.
### GenesysScalerGpioOutputRegister
Scaler GPIO Output Register value.
* MStar Semiconductor TSUM G: `0x0426`
Since 1.7.6.
### GenesysScalerGpioEnableRegister
Scaler GPIO Enable Register value.
* MStar Semiconductor TSUM G: `0x0428`
Since 1.7.6.
### GenesysScalerGpioValue
Scaler GPIO value.
* MStar Semiconductor TSUM G: `0x01`
Since 1.7.6.
## Runtime Requirement
The USB Hub devices and its attached Scaler require libgusb version [0.3.8][1] or later to be detected.
## Update Behavior
The devices are independently updated at runtime using USB control transfers.
The firmware is deployed when the device is in normal runtime mode, and the device will reset when the new firmware has been written.
The HP Mxfd FHD Monitors must be connected to host via the USB-C cable to apply an update. The devices remain functional during the update; the Scaler update is ~10 minute long.
## Vendor ID Security
The vendor ID is set from the USB vendor, for example set to `USB:0x03F0` for HP.
## External Interface Access
This plugin requires read/write access to `/dev/bus/usb`.
[1]: https://github.com/hughsie/libgusb/commit/4e118c154dde70e196c4381bd97790a9413c3552

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2021 Gaël PORTAY <gael.portay@collabora.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
typedef struct {
guint8 reg;
guint8 expected_val;
} FuGenesysWaitFlashRegisterHelper;
typedef struct __attribute__((packed)) {
guint8 tool_string_version; /* 0xff = not supported */
/* byte arrays are ASCII encoded and not NUL terminated */
guint8 mask_project_code[4];
guint8 mask_project_hardware[1]; /* 0=a, 1=b... */
guint8 mask_project_firmware[2]; /* 01,02,03... */
guint8 mask_project_ic_type[6]; /* 352310=GL3523-10 (ASCII string) */
guint8 running_project_code[4];
guint8 running_project_hardware[1];
guint8 running_project_firmware[2];
guint8 running_project_ic_type[6];
guint8 firmware_version[4]; /* MMmm=MM.mm (ASCII string) */
} FuGenesysStaticToolString;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2021 Gaël PORTAY <gael.portay@collabora.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <fwupdplugin.h>
#define FU_TYPE_GENESYS_SCALER_DEVICE (fu_genesys_scaler_device_get_type())
G_DECLARE_FINAL_TYPE(FuGenesysScalerDevice,
fu_genesys_scaler_device,
FU,
GENESYS_SCALER_DEVICE,
FuDevice)
FuGenesysScalerDevice *
fu_genesys_scaler_device_new(FuContext *ctx);

View File

@ -0,0 +1,287 @@
/*
* Copyright (C) 2021 Gaël PORTAY <gael.portay@collabora.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupdplugin.h>
#include "fu-genesys-scaler-firmware.h"
struct _FuGenesysScalerFirmware {
FuFirmwareClass parent_instance;
FuGenesysMtkFooter footer;
guint protect_sector_addr[2];
gsize protect_sector_size[2];
guint public_key_addr;
gsize public_key_size;
guint addr;
};
G_DEFINE_TYPE(FuGenesysScalerFirmware, fu_genesys_scaler_firmware, FU_TYPE_FIRMWARE)
void
fu_genesys_scaler_firmware_decrypt(guint8 *buf, gsize bufsz)
{
const gchar *key = "mstar";
const gsize keylen = strlen(key);
for (guint i = 0; i < bufsz; i++)
buf[i] ^= key[i % keylen];
}
static gboolean
fu_genesys_scaler_firmware_parse(FuFirmware *firmware,
GBytes *fw,
guint64 addr_start,
guint64 addr_end,
FwupdInstallFlags flags,
GError **error)
{
FuGenesysScalerFirmware *self = FU_GENESYS_SCALER_FIRMWARE(firmware);
gsize bufsz = 0;
const guint8 *buf = g_bytes_get_data(fw, &bufsz);
buf = g_bytes_get_data(fw, &bufsz);
if (!fu_memcpy_safe((guint8 *)&self->footer,
sizeof(self->footer),
0, /* dst */
buf,
bufsz,
bufsz - sizeof(self->footer), /* src */
sizeof(self->footer),
error))
return FALSE;
fu_genesys_scaler_firmware_decrypt((guint8 *)&self->footer, sizeof(self->footer));
if (memcmp(self->footer.data.header.default_head,
MTK_RSA_HEADER,
sizeof(self->footer.data.header.default_head)) != 0) {
g_autofree gchar *str = NULL;
str = fu_common_strsafe((const gchar *)self->footer.data.header.default_head,
sizeof(self->footer.data.header.default_head));
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"invalid footer, expected %s, and got %s",
MTK_RSA_HEADER,
str);
return FALSE;
}
if (self->footer.data.header.configuration_setting.bits.second_image) {
if (!fu_common_read_uint32_safe(
self->footer.data.header.second_image_program_addr,
sizeof(self->footer.data.header.second_image_program_addr),
0,
&self->addr,
G_LITTLE_ENDIAN,
error))
return FALSE;
}
if (self->footer.data.header.configuration_setting.bits.decrypt_mode) {
if (!fu_common_read_uint32_safe(
self->footer.data.header.scaler_public_key_addr,
sizeof(self->footer.data.header.scaler_public_key_addr),
0,
&self->public_key_addr,
G_LITTLE_ENDIAN,
error))
return FALSE;
self->public_key_size = 0x1000;
}
if (self->footer.data.header.configuration_setting.bits.special_protect_sector) {
if (self->footer.data.header.protect_sector[0].area.size) {
self->protect_sector_addr[0] =
(self->footer.data.header.protect_sector[0].area.addr_high << 16) |
(self->footer.data.header.protect_sector[0].area.addr_low[1] << 8) |
(self->footer.data.header.protect_sector[0].area.addr_low[0]);
self->protect_sector_addr[0] *= 0x1000;
self->protect_sector_size[0] =
self->footer.data.header.protect_sector[0].area.size * 0x1000;
}
if (self->footer.data.header.protect_sector[1].area.size) {
self->protect_sector_addr[1] =
(self->footer.data.header.protect_sector[1].area.addr_high << 16) |
(self->footer.data.header.protect_sector[1].area.addr_low[1] << 8) |
(self->footer.data.header.protect_sector[1].area.addr_low[0]);
self->protect_sector_addr[1] *= 0x1000;
self->protect_sector_size[1] =
self->footer.data.header.protect_sector[1].area.size * 0x1000;
}
}
/* success */
return TRUE;
}
static void
fu_genesys_scaler_firmware_export(FuFirmware *firmware,
FuFirmwareExportFlags flags,
XbBuilderNode *bn)
{
FuGenesysScalerFirmware *self = FU_GENESYS_SCALER_FIRMWARE(firmware);
if (self->footer.data.header.model_name[0] != '\0') {
fu_xmlb_builder_insert_kv(bn,
"model_name",
(const gchar *)self->footer.data.header.model_name);
}
if (self->footer.data.header.scaler_group[0] != '\0') {
fu_xmlb_builder_insert_kv(bn,
"scaler_group",
(const gchar *)self->footer.data.header.scaler_group);
}
if (self->footer.data.header.panel_type[0] != '\0') {
fu_xmlb_builder_insert_kv(bn,
"panel_type",
(const gchar *)self->footer.data.header.panel_type);
}
if (self->footer.data.header.scaler_packet_date[0] != '\0') {
fu_xmlb_builder_insert_kv(
bn,
"scaler_packet_date",
(const gchar *)self->footer.data.header.scaler_packet_date);
}
if (self->footer.data.header.scaler_packet_version[0] != '\0') {
fu_xmlb_builder_insert_kv(
bn,
"scaler_packet_version",
(const gchar *)self->footer.data.header.scaler_packet_version);
}
fu_xmlb_builder_insert_kx(bn,
"configuration_setting",
self->footer.data.header.configuration_setting.r8);
if (self->footer.data.header.configuration_setting.bits.second_image)
fu_xmlb_builder_insert_kx(bn, "second_image_program_addr", self->addr);
if (self->footer.data.header.configuration_setting.bits.decrypt_mode) {
gchar N[0x200 + 1] = {'\0'};
gchar E[0x006 + 1] = {'\0'};
fu_xmlb_builder_insert_kx(bn, "public_key_addr", self->public_key_addr);
fu_xmlb_builder_insert_kx(bn, "public_key_size", self->public_key_size);
memcpy(N, self->footer.data.public_key.N + 4, sizeof(N) - 1);
fu_xmlb_builder_insert_kv(bn, "N", N);
memcpy(E, self->footer.data.public_key.E + 4, sizeof(E) - 1);
fu_xmlb_builder_insert_kv(bn, "E", E);
}
if (self->footer.data.header.configuration_setting.bits.special_protect_sector) {
if (self->protect_sector_size[0]) {
fu_xmlb_builder_insert_kx(bn,
"protect_sector_addr0",
self->protect_sector_addr[0]);
fu_xmlb_builder_insert_kx(bn,
"protect_sector_size0",
self->protect_sector_size[0]);
}
if (self->protect_sector_size[1]) {
fu_xmlb_builder_insert_kx(bn,
"protect_sector_addr1",
self->protect_sector_addr[1]);
fu_xmlb_builder_insert_kx(bn,
"protect_sector_size1",
self->protect_sector_size[1]);
}
}
if (self->footer.data.header.configuration_setting.bits.boot_code_size_in_header) {
fu_xmlb_builder_insert_kx(bn,
"boot_code_size",
self->footer.data.header.boot_code_size);
}
fu_xmlb_builder_insert_kx(bn, "addr", self->addr);
}
static gboolean
fu_genesys_scaler_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
{
FuGenesysScalerFirmware *self = FU_GENESYS_SCALER_FIRMWARE(firmware);
const gchar *tmp;
/* optional properties */
tmp = xb_node_query_text(n, "model_name", NULL);
if (tmp != NULL) {
if (!fu_memcpy_safe((guint8 *)&self->footer.data.header.model_name,
sizeof(self->footer.data.header.model_name),
0x0, /* dst */
(const guint8 *)tmp,
strlen(tmp),
0x0, /* src */
strlen(tmp),
error))
return FALSE;
}
/* success */
return TRUE;
}
static GBytes *
fu_genesys_scaler_firmware_write(FuFirmware *firmware, GError **error)
{
FuGenesysScalerFirmware *self = FU_GENESYS_SCALER_FIRMWARE(firmware);
FuGenesysMtkFooter footer = {0x0};
g_autoptr(GByteArray) buf = g_byte_array_new();
g_autoptr(GBytes) blob = NULL;
/* payload */
blob = fu_firmware_get_bytes(firmware, error);
if (blob == NULL)
return NULL;
fu_byte_array_append_bytes(buf, blob);
/* "encrypted" footer */
if (!fu_memcpy_safe((guint8 *)&footer,
sizeof(footer),
0, /* dst */
(guint8 *)&self->footer,
sizeof(self->footer),
0, /* src */
sizeof(footer),
error))
return NULL;
if (!fu_memcpy_safe((guint8 *)&footer.data.header.default_head,
sizeof(footer.data.header.default_head),
0, /* dst */
(guint8 *)&MTK_RSA_HEADER,
strlen(MTK_RSA_HEADER),
0, /* src */
strlen(MTK_RSA_HEADER),
error))
return NULL;
fu_genesys_scaler_firmware_decrypt((guint8 *)&footer, sizeof(footer));
g_byte_array_append(buf, (const guint8 *)&footer, sizeof(footer));
/* success */
return g_byte_array_free_to_bytes(g_steal_pointer(&buf));
}
static void
fu_genesys_scaler_firmware_init(FuGenesysScalerFirmware *self)
{
}
static void
fu_genesys_scaler_firmware_class_init(FuGenesysScalerFirmwareClass *klass)
{
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass);
klass_firmware->parse = fu_genesys_scaler_firmware_parse;
klass_firmware->export = fu_genesys_scaler_firmware_export;
klass_firmware->build = fu_genesys_scaler_firmware_build;
klass_firmware->write = fu_genesys_scaler_firmware_write;
}
FuFirmware *
fu_genesys_scaler_firmware_new(void)
{
return FU_FIRMWARE(g_object_new(FU_TYPE_GENESYS_SCALER_FIRMWARE, NULL));
}

View File

@ -0,0 +1,149 @@
/*
* Copyright (C) 2021 Gaël PORTAY <gael.portay@collabora.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <fwupdplugin.h>
#define FU_TYPE_GENESYS_SCALER_FIRMWARE (fu_genesys_scaler_firmware_get_type())
G_DECLARE_FINAL_TYPE(FuGenesysScalerFirmware,
fu_genesys_scaler_firmware,
FU,
GENESYS_SCALER_FIRMWARE,
FuFirmware)
#define MTK_RSA_HEADER "MTK_RSA_HEADER"
typedef struct __attribute__((packed)) {
guint8 default_head[14];
guint8 reserved_0e_0f[2];
guint8 model_name[16];
guint8 reserved_20;
guint8 size[2];
guint8 reserved_23_27[5];
guint8 scaler_group[10];
guint8 reserved_32_53[34];
guint8 panel_type[10];
guint8 scaler_packet_date[8];
guint8 reserved_66_67[2];
guint8 scaler_packet_version[4];
guint8 reserved_6c_7f[20];
union {
guint8 r8;
struct {
/*
* Decrypt Mode:
*
* 0: Scaler decrypt
* 1: ISP Tool decrypt
*/
guint8 decrypt_mode : 1;
/*
* Second Image:
*
* 0: 1st image or dual image; programming address at 0x000000
* 1: 2nd image; programming address set by .second_image_program_addr
*/
guint8 second_image : 1;
/*
* Dual image turn:
*
* 0: fix second programing address set by .second_image_program_addr
* 1: support Dual image turn rule
* - TSUM: Not supported
* - MST9U: ISP Tool need update to DUT least version image address
* - HAWK: Not supported
*/
guint8 dual_image_turn : 1;
/*
* Special Protect Sector:
*
* 0: No Special Protect sector
* 1: Support Special Protect sector
*/
guint8 special_protect_sector : 1;
/*
* HAWK bypass mode
*
* 0: No support HAWK bypass mode
* 1: Support HAWK bypass mode
*/
guint8 hawk_bypass_mode : 1;
/*
* Boot Code Size in header
*
* 0: Follow original search bin address rule
* 1: Get Boot code size from header set by .boot_code_size
*/
guint8 boot_code_size_in_header : 1;
/* Reserved */
guint8 reserved_6_7 : 2;
} __attribute__((packed)) bits;
} configuration_setting;
guint8 reserved_81_85[5];
/* If configuration_setting.bits.second_image set */
guint8 second_image_program_addr[4];
/*
* If configuration_setting.bits.decrypt is set
*
* TSUM/HAWK: ISP Tool need protect flash public address cant erase and write
* MST9U: Not supported
*/
guint8 scaler_public_key_addr[4];
/*
* If configuration_setting.bits.special_protect_sector is set
*
* ISP Tool can't erase "Special Protect Sector" area.
*
* [19:00]: Protect continuous sector start.
* [23:20]: Protect sector continuous number.
*
* Examples: If need to protect FA000 ~FFFFF, Special Protect sector = 0x6000FA;
* If need to protect FA000 only, Special Protect sector = 0x1000FA;
* If no need to protect, Special Protect sector = 0x000000;
*/
union {
guint32 r32;
struct {
guint8 addr_low[2];
guint8 addr_high : 4;
guint8 size : 4;
} __attribute__((packed)) area;
} protect_sector[2];
/*
* If configuration.bits .second_image and .dual_image_turn are set
* and .boot_code_size.
*/
guint32 boot_code_size;
} FuGenesysMtkRsaHeader;
typedef union __attribute__((packed)) {
guint8 raw[0x312];
struct {
struct {
guint8 N[0x206];
guint8 E[0x00c];
} __attribute__((packed)) public_key;
FuGenesysMtkRsaHeader header;
} data;
} FuGenesysMtkFooter;
void
fu_genesys_scaler_firmware_decrypt(guint8 *buf, gsize bufsz);
FuFirmware *
fu_genesys_scaler_firmware_new(void);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
/*
* Copyright (C) 2021 Ricardo Cañuelo <ricardo.canuelo@collabora.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <fwupdplugin.h>
#define FU_TYPE_GENESYS_USBHUB_DEVICE (fu_genesys_usbhub_device_get_type())
G_DECLARE_FINAL_TYPE(FuGenesysUsbhubDevice,
fu_genesys_usbhub_device,
FU,
GENESYS_USBHUB_DEVICE,
FuUsbDevice)

View File

@ -0,0 +1,391 @@
/*
* Copyright (C) 2021 Gaël PORTAY <gael.portay@collabora.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupdplugin.h>
#include "fu-genesys-common.h"
#include "fu-genesys-usbhub-firmware.h"
struct _FuGenesysUsbhubFirmware {
FuFirmwareClass parent_instance;
FuGenesysStaticToolString static_ts;
};
G_DEFINE_TYPE(FuGenesysUsbhubFirmware, fu_genesys_usbhub_firmware, FU_TYPE_FIRMWARE)
static gboolean
fu_genesys_usbhub_firmware_verify(const guint8 *buf, gsize bufsz, guint16 code_size, GError **error)
{
guint16 fw_checksum, checksum;
/* check code-size */
if (code_size < sizeof(checksum)) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"code-size is too small: %u bytes",
code_size);
return FALSE;
}
/* get checksum */
if (!fu_common_read_uint16_safe(buf,
bufsz,
code_size - sizeof(checksum),
&fw_checksum,
G_BIG_ENDIAN,
error))
return FALSE;
/* calculate checksum */
checksum = fu_common_sum16(buf, code_size - sizeof(checksum));
if (checksum != fw_checksum) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"checksum mismatch, got 0x%04x, expected 0x%04x",
checksum,
fw_checksum);
return FALSE;
}
return TRUE;
}
static gboolean
fu_genesys_usbhub_firmware_parse(FuFirmware *firmware,
GBytes *fw,
guint64 addr_start,
guint64 addr_end,
FwupdInstallFlags flags,
GError **error)
{
FuGenesysUsbhubFirmware *self = FU_GENESYS_USBHUB_FIRMWARE(firmware);
gsize bufsz = 0;
const guint8 *buf = g_bytes_get_data(fw, &bufsz);
guint8 sign[4];
guint16 code_size = 0x6000;
guint16 version_raw = 0;
gboolean is3590 = FALSE;
g_autofree gchar *version = NULL;
/* check signature */
if (!fu_memcpy_safe(sign,
sizeof(sign),
0, /* dst */
buf,
bufsz,
0xfc,
sizeof(sign),
error))
return FALSE;
if (memcmp(sign, "XROM", sizeof(sign)) != 0 && memcmp(sign, "SRON", sizeof(sign)) != 0) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"signature not supported");
return FALSE;
}
/* get static tool string */
if (!fu_memcpy_safe((guint8 *)&self->static_ts,
sizeof(self->static_ts),
0, /* dst */
buf,
bufsz,
0x221, /* src */
sizeof(self->static_ts),
error))
return FALSE;
/* not a GL3523, is GL3590? */
if (memcmp(self->static_ts.mask_project_ic_type, "3523", 4) != 0) {
if (!fu_memcpy_safe((guint8 *)&self->static_ts,
sizeof(self->static_ts),
0, /* dst */
buf,
bufsz,
0x241, /* src */
sizeof(self->static_ts),
error))
return FALSE;
/* not a GL3590 either */
if (memcmp(self->static_ts.mask_project_ic_type, "3590", 4) != 0) {
g_autofree gchar *tmp =
fu_common_strsafe((const gchar *)&self->static_ts.mask_project_ic_type,
sizeof(self->static_ts.mask_project_ic_type));
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"IC type %s not supported",
tmp);
return FALSE;
}
is3590 = TRUE;
}
/* unsupported static tool string */
if (self->static_ts.tool_string_version == 0xff) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Static Tool String not supported");
return FALSE;
}
/* deduce code size */
if (!is3590) {
guint8 ic_type_revision;
code_size = 0x6000;
ic_type_revision = 10 * (self->static_ts.mask_project_ic_type[4] - '0') +
(self->static_ts.mask_project_ic_type[5] - '0');
if (ic_type_revision == 50)
code_size = 0x8000;
} else {
code_size = 0x6000;
}
/* calculate checksum */
if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0)
if (!fu_genesys_usbhub_firmware_verify(buf, bufsz, code_size, error))
return FALSE;
/* get firmware version */
if (!fu_common_read_uint16_safe(buf, bufsz, 0x10E, &version_raw, G_LITTLE_ENDIAN, error))
return FALSE;
fu_firmware_set_version_raw(firmware, version_raw);
version =
g_strdup_printf("%02x.%02x", (version_raw & 0xFF00U) >> 8, (version_raw & 0x00FFU));
fu_firmware_set_version(firmware, version);
/* success */
return TRUE;
}
static GBytes *
fu_genesys_usbhub_firmware_write(FuFirmware *firmware, GError **error)
{
FuGenesysUsbhubFirmware *self = FU_GENESYS_USBHUB_FIRMWARE(firmware);
g_autoptr(GByteArray) buf = g_byte_array_new();
guint16 code_size = 0x6000;
guint16 checksum;
const guint8 sign[] = {'X', 'R', 'O', 'M'};
/* fixed size */
fu_byte_array_set_size(buf, code_size);
/* signature */
if (!fu_memcpy_safe(buf->data,
buf->len,
0xfc, /* dst */
sign,
sizeof(sign),
0x0, /* src */
sizeof(sign),
error))
return NULL;
/* static tool string */
if (!fu_memcpy_safe(buf->data,
buf->len,
0x221, /* dst */
(const guint8 *)&self->static_ts,
sizeof(self->static_ts),
0x0, /* src */
sizeof(self->static_ts),
error))
return NULL;
/* checksum */
checksum = fu_common_sum16(buf->data, code_size - sizeof(checksum));
if (!fu_common_write_uint16_safe(buf->data,
buf->len,
code_size - sizeof(guint16),
checksum,
G_BIG_ENDIAN,
error))
return NULL;
/* version */
if (!fu_common_write_uint16_safe(buf->data,
buf->len,
0x10E,
0x1234, // TODO: parse from firmware version string
G_BIG_ENDIAN,
error))
return NULL;
/* success */
return g_byte_array_free_to_bytes(g_steal_pointer(&buf));
}
static gchar *
fu_genesys_usbhub_firmware_get_project_ic_type_string(const gchar *tmp)
{
return g_strdup_printf("GL%c%c%c%c-%c%c", tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5]);
}
static void
fu_genesys_usbhub_firmware_export(FuFirmware *firmware,
FuFirmwareExportFlags flags,
XbBuilderNode *bn)
{
FuGenesysUsbhubFirmware *self = FU_GENESYS_USBHUB_FIRMWARE(firmware);
g_autofree gchar *tool_string_version = NULL;
g_autofree gchar *mask_project_code = NULL;
g_autofree gchar *mask_project_hardware = NULL;
g_autofree gchar *mask_project_firmware = NULL;
g_autofree gchar *mask_project_ic_type = NULL;
g_autofree gchar *running_project_code = NULL;
g_autofree gchar *running_project_hardware = NULL;
g_autofree gchar *running_project_firmware = NULL;
g_autofree gchar *running_project_ic_type = NULL;
tool_string_version = fu_common_strsafe((const gchar *)&self->static_ts.tool_string_version,
sizeof(self->static_ts.tool_string_version));
fu_xmlb_builder_insert_kv(bn, "tool_string_version", tool_string_version);
mask_project_code = fu_common_strsafe((const gchar *)&self->static_ts.mask_project_code,
sizeof(self->static_ts.mask_project_code));
fu_xmlb_builder_insert_kv(bn, "mask_project_code", mask_project_code);
mask_project_hardware =
fu_common_strsafe((const gchar *)&self->static_ts.mask_project_hardware,
sizeof(self->static_ts.mask_project_hardware));
if (mask_project_hardware != NULL)
mask_project_hardware[0] += 0x10; /* '1' -> 'A'... */
fu_xmlb_builder_insert_kv(bn, "mask_project_hardware", mask_project_hardware);
mask_project_firmware =
fu_common_strsafe((const gchar *)&self->static_ts.mask_project_firmware,
sizeof(self->static_ts.mask_project_firmware));
fu_xmlb_builder_insert_kv(bn, "mask_project_firmware", mask_project_firmware);
mask_project_ic_type = fu_genesys_usbhub_firmware_get_project_ic_type_string(
(const gchar *)self->static_ts.mask_project_ic_type);
fu_xmlb_builder_insert_kv(bn, "mask_project_ic_type", mask_project_ic_type);
running_project_code =
fu_common_strsafe((const gchar *)&self->static_ts.running_project_code,
sizeof(self->static_ts.running_project_code));
fu_xmlb_builder_insert_kv(bn, "running_project_code", running_project_code);
running_project_hardware =
fu_common_strsafe((const gchar *)&self->static_ts.running_project_hardware,
sizeof(self->static_ts.running_project_hardware));
if (running_project_hardware != NULL)
running_project_hardware[0] += 0x10; /* '1' -> 'A'... */
fu_xmlb_builder_insert_kv(bn, "running_project_hardware", running_project_hardware);
running_project_firmware =
fu_common_strsafe((const gchar *)&self->static_ts.running_project_firmware,
sizeof(self->static_ts.running_project_firmware));
fu_xmlb_builder_insert_kv(bn, "running_project_firmware", running_project_firmware);
running_project_ic_type = fu_genesys_usbhub_firmware_get_project_ic_type_string(
(const gchar *)self->static_ts.running_project_ic_type);
fu_xmlb_builder_insert_kv(bn, "running_project_ic_type", running_project_ic_type);
}
static gboolean
fu_genesys_usbhub_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
{
FuGenesysUsbhubFirmware *self = FU_GENESYS_USBHUB_FIRMWARE(firmware);
const gchar *tmp;
guint64 tmp64;
/* optional properties */
tmp64 = xb_node_query_text_as_uint(n, "tool_string_version", NULL);
if (tmp64 != G_MAXUINT64) {
if (tmp64 > G_MAXUINT8) {
g_set_error_literal(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"invalid tool_string_version");
return FALSE;
}
self->static_ts.tool_string_version = tmp64;
}
/* mask_project_code */
tmp = xb_node_query_text(n, "mask_project_code", NULL);
if (tmp != NULL) {
gsize len = strlen(tmp);
if (len != 4) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"invalid mask_project_code %s, got 0x%x length",
tmp,
(guint)len);
return FALSE;
}
if (!fu_memcpy_safe((guint8 *)&self->static_ts.mask_project_code,
sizeof(self->static_ts.mask_project_code),
0x0, /* dst */
(const guint8 *)tmp,
len,
0x0, /* src */
len,
error))
return FALSE;
}
/* mask_project_ic_type */
tmp = xb_node_query_text(n, "mask_project_ic_type", NULL);
if (tmp != NULL) {
gsize len = strlen(tmp);
if (len != 6) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"invalid mask_project_ic_type %s, got 0x%x length",
tmp,
(guint)len);
return FALSE;
}
if (!fu_memcpy_safe((guint8 *)&self->static_ts.mask_project_ic_type,
sizeof(self->static_ts.mask_project_ic_type),
0x0, /* dst */
(const guint8 *)tmp,
len,
0x0, /* src */
len,
error))
return FALSE;
}
/* success */
return TRUE;
}
static void
fu_genesys_usbhub_firmware_init(FuGenesysUsbhubFirmware *self)
{
fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM);
}
static void
fu_genesys_usbhub_firmware_class_init(FuGenesysUsbhubFirmwareClass *klass)
{
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass);
klass_firmware->parse = fu_genesys_usbhub_firmware_parse;
klass_firmware->export = fu_genesys_usbhub_firmware_export;
klass_firmware->build = fu_genesys_usbhub_firmware_build;
klass_firmware->write = fu_genesys_usbhub_firmware_write;
}
FuFirmware *
fu_genesys_usbhub_firmware_new(void)
{
return FU_FIRMWARE(g_object_new(FU_TYPE_GENESYS_USBHUB_FIRMWARE, NULL));
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2021 Gaël PORTAY <gael.portay@collabora.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <fwupdplugin.h>
#define FU_TYPE_GENESYS_USBHUB_FIRMWARE (fu_genesys_usbhub_firmware_get_type())
G_DECLARE_FINAL_TYPE(FuGenesysUsbhubFirmware,
fu_genesys_usbhub_firmware,
FU,
GENESYS_USBHUB_FIRMWARE,
FuFirmware)
FuFirmware *
fu_genesys_usbhub_firmware_new(void);

View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2021 Ricardo Cañuelo <ricardo.canuelo@collabora.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupdplugin.h>
#include "fu-genesys-scaler-firmware.h"
#include "fu-genesys-usbhub-device.h"
#include "fu-genesys-usbhub-firmware.h"
static void
fu_plugin_genesys_init(FuPlugin *plugin)
{
FuContext *ctx = fu_plugin_get_context(plugin);
fu_context_add_quirk_key(ctx, "GenesysScalerGpioOutputRegister");
fu_context_add_quirk_key(ctx, "GenesysScalerGpioEnableRegister");
fu_context_add_quirk_key(ctx, "GenesysScalerGpioValue");
fu_plugin_add_device_gtype(plugin, FU_TYPE_GENESYS_USBHUB_DEVICE);
fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_GENESYS_USBHUB_FIRMWARE);
fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_GENESYS_SCALER_FIRMWARE);
}
void
fu_plugin_init_vfuncs(FuPluginVfuncs *vfuncs)
{
vfuncs->build_hash = FU_BUILD_HASH;
vfuncs->init = fu_plugin_genesys_init;
}

View File

@ -0,0 +1,25 @@
# M2xfd
# usbhub
[USB\VID_03F0&PID_0610]
Plugin = genesys
Name = HP USB-C Controller
Flags = has-mstar-scaler
GenesysUsbhubSwitchRequest = 0xA1
GenesysUsbhubReadRequest = 0xA2
GenesysUsbhubWriteRequest = 0xA3
# scaler
[GENESYS_SCALER\MSTAR_TSUM_G&PUBKEY_B335BDCE-7073-5D0E-9BD3-9B69C1A6899F]
Name = HP M24fd USB-C Monitor
Flags = use-i2c-ch0
GenesysScalerGpioOutputRegister = 0x0426
GenesysScalerGpioEnableRegister = 0x0428
GenesysScalerGpioValue = 0x01
[GENESYS_SCALER\MSTAR_TSUM_G&PUBKEY_847A3650-8648-586B-83C8-8B53714F37E3]
Name = HP M27fd USB-C Monitor
Flags = use-i2c-ch0
GenesysScalerGpioOutputRegister = 0x0426
GenesysScalerGpioEnableRegister = 0x0428
GenesysScalerGpioValue = 0x01

View File

@ -0,0 +1,35 @@
if get_option('gusb')
cargs = ['-DG_LOG_DOMAIN="FuPluginGenesys"']
install_data([
'genesys.quirk',
],
install_dir: join_paths(datadir, 'fwupd', 'quirks.d')
)
shared_module('fu_plugin_genesys',
fu_hash,
sources : [
'fu-genesys-scaler-firmware.c', # fuzzing
'fu-genesys-usbhub-firmware.c', # fuzzing
'fu-genesys-scaler-device.c',
'fu-genesys-usbhub-device.c',
'fu-plugin-genesys.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,
],
)
endif

View File

@ -0,0 +1 @@
hello worldmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstar '?> >2+)7,713rm2tarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarmstarm

View File

@ -0,0 +1,4 @@
<firmware gtype="FuGenesysScalerFirmware">
<data>aGVsbG8gd29ybGQ=</data> <!-- base64 -->
<model_name>A</model_name>
</firmware>

Binary file not shown.

View File

@ -0,0 +1,5 @@
<firmware gtype="FuGenesysUsbhubFirmware">
<data>aGVsbG8gd29ybGQ=</data> <!-- base64 -->
<tool_string_version>1</tool_string_version>
<mask_project_ic_type>352310</mask_project_ic_type>
</firmware>

View File

@ -24,6 +24,7 @@ subdir('ep963x')
subdir('fastboot')
subdir('flashrom')
subdir('fresco-pd')
subdir('genesys')
subdir('goodix-moc')
subdir('gpio')
subdir('hailuck')