mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-04 03:36:57 +00:00
qsi-dock: Create a new plugin to update docks from QSI
This commit is contained in:
parent
14ef2d17c6
commit
a447fca101
@ -78,6 +78,7 @@ subdir('pci-mei')
|
||||
subdir('pci-psp')
|
||||
subdir('pixart-rf')
|
||||
subdir('powerd')
|
||||
subdir('qsi-dock')
|
||||
subdir('realtek-mst')
|
||||
subdir('redfish')
|
||||
subdir('rts54hid')
|
||||
|
32
plugins/qsi-dock/README.md
Normal file
32
plugins/qsi-dock/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
# QSI Dock
|
||||
|
||||
## Introduction
|
||||
|
||||
This plugin uses the MCU to write all the dock firmware components. The MCU version
|
||||
is provided by the DMC bcdDevice.
|
||||
|
||||
This plugin supports the following protocol ID:
|
||||
|
||||
* com.qsi.dock
|
||||
|
||||
## GUID Generation
|
||||
|
||||
These devices use the standard USB DeviceInstanceId values, e.g.
|
||||
|
||||
* `USB\VID_047D&PID_808D&REV_0001`
|
||||
* `USB\VID_047D&PID_808D`
|
||||
* `USB\VID_047D`
|
||||
|
||||
## Update Behavior
|
||||
|
||||
The device usually presents in runtime mode, but on detach re-enumerates with
|
||||
the same USB PID in an unlocked mode. On attach the device again re-enumerates
|
||||
back to the runtime locked mode.
|
||||
|
||||
## Vendor ID Security
|
||||
|
||||
The vendor ID is set from the USB vendor.
|
||||
|
||||
## External Interface Access
|
||||
|
||||
This plugin requires read/write access to `/dev/bus/usb`.
|
88
plugins/qsi-dock/fu-qsi-dock-child-device.c
Normal file
88
plugins/qsi-dock/fu-qsi-dock-child-device.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "fu-qsi-dock-child-device.h"
|
||||
#include "fu-qsi-dock-mcu-device.h"
|
||||
|
||||
struct _FuQsiDockChildDevice {
|
||||
FuDevice parent_instance;
|
||||
guint8 chip_idx;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FuQsiDockChildDevice, fu_qsi_dock_child_device, FU_TYPE_DEVICE)
|
||||
|
||||
void
|
||||
fu_qsi_dock_child_device_set_chip_idx(FuQsiDockChildDevice *self, guint8 chip_idx)
|
||||
{
|
||||
self->chip_idx = chip_idx;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qsi_dock_child_device_to_string(FuDevice *device, guint idt, GString *str)
|
||||
{
|
||||
FuQsiDockChildDevice *self = FU_QSI_DOCK_CHILD_DEVICE(device);
|
||||
fu_string_append_kx(str, idt, "ChipIdx", self->chip_idx);
|
||||
}
|
||||
|
||||
/* use the parents parser */
|
||||
static FuFirmware *
|
||||
fu_qsi_dock_mcu_device_prepare_firmware(FuDevice *device,
|
||||
GBytes *fw,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
FuDevice *parent = fu_device_get_parent(device);
|
||||
if (parent == NULL) {
|
||||
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no parent");
|
||||
return NULL;
|
||||
}
|
||||
return fu_device_prepare_firmware(parent, fw, flags, error);
|
||||
}
|
||||
|
||||
/* only update this specific child component */
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_write_firmware(FuDevice *device,
|
||||
FuFirmware *firmware,
|
||||
FuProgress *progress,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
FuQsiDockChildDevice *self = FU_QSI_DOCK_CHILD_DEVICE(device);
|
||||
FuDevice *parent = fu_device_get_parent(device);
|
||||
if (parent == NULL) {
|
||||
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "no parent");
|
||||
return FALSE;
|
||||
}
|
||||
return fu_qsi_dock_mcu_device_write_firmware_with_idx(FU_QSI_DOCK_MCU_DEVICE(parent),
|
||||
firmware,
|
||||
self->chip_idx,
|
||||
progress,
|
||||
flags,
|
||||
error);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qsi_dock_child_device_init(FuQsiDockChildDevice *self)
|
||||
{
|
||||
fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qsi_dock_child_device_class_init(FuQsiDockChildDeviceClass *klass)
|
||||
{
|
||||
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
||||
klass_device->to_string = fu_qsi_dock_child_device_to_string;
|
||||
klass_device->prepare_firmware = fu_qsi_dock_mcu_device_prepare_firmware;
|
||||
klass_device->write_firmware = fu_qsi_dock_mcu_device_write_firmware;
|
||||
}
|
||||
|
||||
FuDevice *
|
||||
fu_qsi_dock_child_new(FuContext *ctx)
|
||||
{
|
||||
return g_object_new(FU_TYPE_QSI_DOCK_CHILD_DEVICE, "context", ctx, NULL);
|
||||
}
|
21
plugins/qsi-dock/fu-qsi-dock-child-device.h
Normal file
21
plugins/qsi-dock/fu-qsi-dock-child-device.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fwupdplugin.h>
|
||||
|
||||
#define FU_TYPE_QSI_DOCK_CHILD_DEVICE (fu_qsi_dock_child_device_get_type())
|
||||
G_DECLARE_FINAL_TYPE(FuQsiDockChildDevice,
|
||||
fu_qsi_dock_child_device,
|
||||
FU,
|
||||
QSI_DOCK_CHILD_DEVICE,
|
||||
FuDevice)
|
||||
|
||||
FuDevice *
|
||||
fu_qsi_dock_child_new(FuContext *ctx);
|
||||
void
|
||||
fu_qsi_dock_child_device_set_chip_idx(FuQsiDockChildDevice *self, guint8 chip_idx);
|
67
plugins/qsi-dock/fu-qsi-dock-common.h
Normal file
67
plugins/qsi-dock/fu-qsi-dock-common.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
* Copyright (C) 2022 Kevin Chen <hsinfu.chen@qsitw.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#define FU_QSI_DOCK_REPORT_ID 5
|
||||
|
||||
#define FU_QSI_DOCK_FIRMWARE_IDX_NONE 0x00
|
||||
#define FU_QSI_DOCK_FIRMWARE_IDX_DMC_PD 0x01
|
||||
#define FU_QSI_DOCK_FIRMWARE_IDX_DP 0x02
|
||||
#define FU_QSI_DOCK_FIRMWARE_IDX_TBT4 0x04
|
||||
#define FU_QSI_DOCK_FIRMWARE_IDX_USB3 0x08
|
||||
#define FU_QSI_DOCK_FIRMWARE_IDX_USB2 0x10
|
||||
#define FU_QSI_DOCK_FIRMWARE_IDX_AUDIO 0x20
|
||||
#define FU_QSI_DOCK_FIRMWARE_IDX_I225 0x40
|
||||
#define FU_QSI_DOCK_FIRMWARE_IDX_MCU 0x80
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
guint8 DMC[5];
|
||||
guint8 PD[5];
|
||||
guint8 DP5x[5];
|
||||
guint8 DP6x[5];
|
||||
guint8 TBT4[5];
|
||||
guint8 USB3[5];
|
||||
guint8 USB2[5];
|
||||
guint8 AUDIO[5];
|
||||
guint8 I255[5];
|
||||
guint8 MCU[2];
|
||||
guint8 bcdVersion[2];
|
||||
} FuQsiDockIspVersionInMcu;
|
||||
|
||||
typedef enum {
|
||||
FU_QSI_DOCK_CMD1_BOOT = 0x11,
|
||||
FU_QSI_DOCK_CMD1_SYSTEM = 0x31,
|
||||
FU_QSI_DOCK_CMD1_MCU = 0x51,
|
||||
FU_QSI_DOCK_CMD1_SPI = 0x61,
|
||||
FU_QSI_DOCK_CMD1_I2C_VMM = 0x71,
|
||||
FU_QSI_DOCK_CMD1_I2C_CCG = 0x81,
|
||||
|
||||
FU_QSI_DOCK_CMD1_MASS_MCU = 0xC0,
|
||||
FU_QSI_DOCK_CMD1_MASS_SPI,
|
||||
FU_QSI_DOCK_CMD1_MASS_I2C_VMM,
|
||||
FU_QSI_DOCK_CMD1_MASS_I2C_CY,
|
||||
} FuQsiDockCmd;
|
||||
|
||||
typedef enum {
|
||||
FU_QSI_DOCK_CMD2_CMD_DEVICE_STATUS,
|
||||
FU_QSI_DOCK_CMD2_CMD_SET_BOOT_MODE,
|
||||
FU_QSI_DOCK_CMD2_CMD_SET_AP_MODE,
|
||||
FU_QSI_DOCK_CMD2_CMD_ERASE_AP_PAGE,
|
||||
FU_QSI_DOCK_CMD2_CMD_CHECKSUM,
|
||||
FU_QSI_DOCK_CMD2_CMD_DEVICE_VERSION,
|
||||
FU_QSI_DOCK_CMD2_CMD_DEVICE_PCB_VERSION,
|
||||
FU_QSI_DOCK_CMD2_CMD_DEVICE_SN,
|
||||
} FuQsiDockCmd2_0x51;
|
||||
|
||||
typedef enum {
|
||||
FU_QSI_DOCK_CMD2_SPI_EXTERNAL_FLASH_INI,
|
||||
FU_QSI_DOCK_CMD2_SPI_EXTERNAL_FLASH_ERASE,
|
||||
FU_QSI_DOCK_CMD2_SPI_EXTERNAL_FLASH_CHECKSUM,
|
||||
} FuQsiDockCmd2_0x61;
|
580
plugins/qsi-dock/fu-qsi-dock-mcu-device.c
Normal file
580
plugins/qsi-dock/fu-qsi-dock-mcu-device.c
Normal file
@ -0,0 +1,580 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
* Copyright (C) 2022 Kevin Chen <hsinfu.chen@qsitw.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "fu-qsi-dock-child-device.h"
|
||||
#include "fu-qsi-dock-common.h"
|
||||
#include "fu-qsi-dock-mcu-device.h"
|
||||
|
||||
struct _FuQsiDockMcuDevice {
|
||||
FuHidDevice parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FuQsiDockMcuDevice, fu_qsi_dock_mcu_device, FU_TYPE_HID_DEVICE)
|
||||
|
||||
#define FU_QSI_DOCK_MCU_DEVICE_TIMEOUT 90000 /* ms */
|
||||
|
||||
#define FU_QSI_DOCK_TX_ISP_LENGTH 61
|
||||
#define FU_QSI_DOCK_TX_ISP_LENGTH_MCU 60
|
||||
#define FU_QSI_DOCK_EXTERN_FLASH_PAGE_SIZE 256
|
||||
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_tx(FuQsiDockMcuDevice *self,
|
||||
guint8 CmdPrimary,
|
||||
guint8 CmdSecond,
|
||||
const guint8 *inbuf,
|
||||
gsize inbufsz,
|
||||
GError **error)
|
||||
{
|
||||
guint8 buf[64] = {FU_QSI_DOCK_REPORT_ID, CmdPrimary, CmdSecond};
|
||||
|
||||
return fu_hid_device_set_report(FU_HID_DEVICE(self),
|
||||
FU_QSI_DOCK_REPORT_ID,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
FU_QSI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_rx(FuQsiDockMcuDevice *self, guint8 *outbuf, gsize outbufsz, GError **error)
|
||||
{
|
||||
guint8 buf[64];
|
||||
|
||||
if (!fu_hid_device_get_report(FU_HID_DEVICE(self),
|
||||
FU_QSI_DOCK_REPORT_ID,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
FU_QSI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error)) {
|
||||
return FALSE;
|
||||
}
|
||||
if (outbuf != NULL) {
|
||||
if (!fu_memcpy_safe(outbuf,
|
||||
outbufsz,
|
||||
0x0, /* dst */
|
||||
buf,
|
||||
sizeof(buf),
|
||||
0x5, /* src */
|
||||
outbufsz,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_txrx(FuQsiDockMcuDevice *self,
|
||||
guint8 cmd1,
|
||||
guint8 cmd2,
|
||||
const guint8 *inbuf,
|
||||
gsize inbufsz,
|
||||
guint8 *outbuf,
|
||||
gsize outbufsz,
|
||||
GError **error)
|
||||
{
|
||||
if (!fu_qsi_dock_mcu_device_tx(self, cmd1, cmd2, inbuf, inbufsz, error))
|
||||
return FALSE;
|
||||
return fu_qsi_dock_mcu_device_rx(self, outbuf, outbufsz, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_get_status(FuQsiDockMcuDevice *self, GError **error)
|
||||
{
|
||||
guint8 response = 0;
|
||||
guint8 cmd1 = FU_QSI_DOCK_CMD1_MCU;
|
||||
guint8 cmd2 = FU_QSI_DOCK_CMD2_CMD_DEVICE_STATUS;
|
||||
|
||||
if (!fu_qsi_dock_mcu_device_txrx(self,
|
||||
cmd1,
|
||||
cmd2,
|
||||
&cmd1,
|
||||
sizeof(cmd1),
|
||||
&response,
|
||||
sizeof(response),
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_enumerate_children(FuQsiDockMcuDevice *self, GError **error)
|
||||
{
|
||||
guint8 outbuf[49] = {0};
|
||||
struct {
|
||||
const gchar *name;
|
||||
guint8 chip_idx;
|
||||
gsize offset;
|
||||
} components[] = {
|
||||
{"MCU", FU_QSI_DOCK_FIRMWARE_IDX_MCU, G_STRUCT_OFFSET(FuQsiDockIspVersionInMcu, MCU)},
|
||||
{"bcdVersion",
|
||||
FU_QSI_DOCK_FIRMWARE_IDX_NONE,
|
||||
G_STRUCT_OFFSET(FuQsiDockIspVersionInMcu, bcdVersion)},
|
||||
{NULL, 0, 0}};
|
||||
|
||||
for (guint i = 0; components[i].name != NULL; i++) {
|
||||
const guint8 *val = outbuf + components[i].offset;
|
||||
g_autofree gchar *version = NULL;
|
||||
g_autoptr(FuDevice) child = NULL;
|
||||
|
||||
child = fu_qsi_dock_child_new(fu_device_get_context(FU_DEVICE(self)));
|
||||
if (g_strcmp0(components[i].name, "bcdVersion") == 0) {
|
||||
if ((val[0] == 0x00 && val[1] == 0x00) ||
|
||||
(val[0] == 0xFF && val[1] == 0xFF)) {
|
||||
g_debug("ignoring %s", components[i].name);
|
||||
continue;
|
||||
}
|
||||
|
||||
version = g_strdup_printf("%x.%x.%02x", val[0] & 0xFu, val[0] >> 4, val[1]);
|
||||
g_debug("ignoring %s --> %s", components[i].name, version);
|
||||
|
||||
continue;
|
||||
} else if (g_strcmp0(components[i].name, "MCU") == 0) {
|
||||
if ((val[0] == 0x00 && val[1] == 0x00) ||
|
||||
(val[0] == 0xFF && val[1] == 0xFF)) {
|
||||
g_debug("ignoring %s", components[i].name);
|
||||
continue;
|
||||
}
|
||||
|
||||
version = g_strdup_printf("%X.%X", val[0], val[1]);
|
||||
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_PLAIN);
|
||||
|
||||
fu_device_set_version(child, version);
|
||||
fu_device_set_name(child, "Dock Management Controller");
|
||||
} else {
|
||||
g_warning("unhandled %s", components[i].name);
|
||||
}
|
||||
|
||||
/* add virtual device */
|
||||
fu_device_add_instance_u16(child,
|
||||
"VID",
|
||||
fu_usb_device_get_vid(FU_USB_DEVICE(self)));
|
||||
fu_device_add_instance_u16(child,
|
||||
"PID",
|
||||
fu_usb_device_get_pid(FU_USB_DEVICE(self)));
|
||||
fu_device_add_instance_str(child, "CID", components[i].name);
|
||||
if (!fu_device_build_instance_id(child, error, "USB", "VID", "PID", "CID", NULL))
|
||||
return FALSE;
|
||||
if (fu_device_get_name(child) == NULL)
|
||||
fu_device_set_name(child, components[i].name);
|
||||
fu_device_set_logical_id(child, components[i].name);
|
||||
fu_qsi_dock_child_device_set_chip_idx(FU_QSI_DOCK_CHILD_DEVICE(child),
|
||||
components[i].chip_idx);
|
||||
fu_device_add_child(FU_DEVICE(self), child);
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_setup(FuDevice *device, GError **error)
|
||||
{
|
||||
FuQsiDockMcuDevice *self = FU_QSI_DOCK_MCU_DEVICE(device);
|
||||
|
||||
/* FuUsbDevice->setup */
|
||||
if (!FU_DEVICE_CLASS(fu_qsi_dock_mcu_device_parent_class)->setup(device, error))
|
||||
return FALSE;
|
||||
|
||||
/* get status and component versions */
|
||||
if (!fu_qsi_dock_mcu_device_get_status(self, error))
|
||||
return FALSE;
|
||||
if (!fu_qsi_dock_mcu_device_enumerate_children(self, error))
|
||||
return FALSE;
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_checksum(FuQsiDockMcuDevice *self,
|
||||
guint32 checksum,
|
||||
guint32 length,
|
||||
GError **error)
|
||||
{
|
||||
guint8 buf[64] = {FU_QSI_DOCK_REPORT_ID,
|
||||
FU_QSI_DOCK_CMD1_SPI,
|
||||
FU_QSI_DOCK_CMD2_SPI_EXTERNAL_FLASH_CHECKSUM,
|
||||
0};
|
||||
guint8 fw_length[4];
|
||||
guint8 checksum_val[2];
|
||||
|
||||
fu_memwrite_uint32(fw_length, length, G_LITTLE_ENDIAN);
|
||||
fu_memwrite_uint16(checksum_val, checksum, G_LITTLE_ENDIAN);
|
||||
|
||||
/* fw length */
|
||||
if (!fu_memcpy_safe(buf,
|
||||
sizeof(buf),
|
||||
0x03, /* dst */
|
||||
fw_length,
|
||||
sizeof(fw_length),
|
||||
0x0,
|
||||
sizeof(fw_length),
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* checksum */
|
||||
if (!fu_memcpy_safe(buf,
|
||||
sizeof(buf),
|
||||
0x07, /* dst */
|
||||
checksum_val,
|
||||
sizeof(checksum_val),
|
||||
0x0,
|
||||
sizeof(checksum_val),
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* SetReport+GetReport */
|
||||
if (!fu_hid_device_set_report(FU_HID_DEVICE(self),
|
||||
FU_QSI_DOCK_REPORT_ID,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
FU_QSI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error))
|
||||
return FALSE;
|
||||
memset(buf, 0x0, sizeof(buf));
|
||||
if (!fu_hid_device_get_report(FU_HID_DEVICE(self),
|
||||
FU_QSI_DOCK_REPORT_ID,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
FU_QSI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* MCU Checksum Compare Result 0:Pass 1:Fail */
|
||||
if (buf[2] != 0) {
|
||||
g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "checksum did not match");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_write_chunk(FuQsiDockMcuDevice *self,
|
||||
FuChunk *chk_page,
|
||||
guint32 *checksum_tmp,
|
||||
FuProgress *progress,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GPtrArray) chunks = NULL;
|
||||
|
||||
chunks = fu_chunk_array_new(fu_chunk_get_data(chk_page),
|
||||
fu_chunk_get_data_sz(chk_page),
|
||||
0x0,
|
||||
0x0,
|
||||
FU_QSI_DOCK_TX_ISP_LENGTH_MCU);
|
||||
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 checksum_buf[FU_QSI_DOCK_TX_ISP_LENGTH_MCU] = {0x0};
|
||||
guint8 buf[64] = {FU_QSI_DOCK_REPORT_ID,
|
||||
FU_QSI_DOCK_CMD1_MASS_SPI,
|
||||
fu_chunk_get_data_sz(chk)};
|
||||
|
||||
/* SetReport */
|
||||
if (!fu_memcpy_safe(buf,
|
||||
sizeof(buf),
|
||||
0x04, /* dst */
|
||||
fu_chunk_get_data(chk),
|
||||
fu_chunk_get_data_sz(chk),
|
||||
0x0, /* src */
|
||||
fu_chunk_get_data_sz(chk),
|
||||
error))
|
||||
return FALSE;
|
||||
if (!fu_hid_device_set_report(FU_HID_DEVICE(self),
|
||||
FU_QSI_DOCK_REPORT_ID,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
FU_QSI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* sum checksum value */
|
||||
if (!fu_memcpy_safe(checksum_buf,
|
||||
sizeof(checksum_buf),
|
||||
0x0, /* dst */
|
||||
fu_chunk_get_data(chk),
|
||||
fu_chunk_get_data_sz(chk),
|
||||
0x0, /* src */
|
||||
fu_chunk_get_data_sz(chk),
|
||||
error))
|
||||
return FALSE;
|
||||
*checksum_tmp += fu_sum32(checksum_buf, sizeof(checksum_buf));
|
||||
|
||||
/* GetReport */
|
||||
memset(buf, 0x0, sizeof(buf));
|
||||
if (!fu_hid_device_get_report(FU_HID_DEVICE(self),
|
||||
FU_QSI_DOCK_REPORT_ID,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
FU_QSI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* MCU ACK 0:Pass 1:Fail */
|
||||
if (buf[2] != 0) {
|
||||
g_set_error(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INVALID_FILE,
|
||||
"ACK error for chunk %u",
|
||||
i);
|
||||
return FALSE;
|
||||
}
|
||||
fu_progress_step_done(progress);
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_write_chunks(FuQsiDockMcuDevice *self,
|
||||
GPtrArray *chunks,
|
||||
guint32 *checksum,
|
||||
FuProgress *progress,
|
||||
GError **error)
|
||||
{
|
||||
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);
|
||||
if (!fu_qsi_dock_mcu_device_write_chunk(self,
|
||||
chk,
|
||||
checksum,
|
||||
fu_progress_get_child(progress),
|
||||
error)) {
|
||||
g_prefix_error(error, "failed to write chunk 0x%x", i);
|
||||
return FALSE;
|
||||
}
|
||||
fu_progress_step_done(progress);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_wait_for_spi_initial_ready_cb(FuDevice *device,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
FuQsiDockMcuDevice *self = FU_QSI_DOCK_MCU_DEVICE(device);
|
||||
guint8 buf[64] = {FU_QSI_DOCK_REPORT_ID,
|
||||
FU_QSI_DOCK_CMD1_SPI,
|
||||
FU_QSI_DOCK_CMD2_SPI_EXTERNAL_FLASH_INI};
|
||||
|
||||
/* SetReport+GetReport */
|
||||
if (!fu_hid_device_set_report(FU_HID_DEVICE(self),
|
||||
FU_QSI_DOCK_REPORT_ID,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
FU_QSI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error))
|
||||
return FALSE;
|
||||
memset(buf, 0x0, sizeof(buf));
|
||||
if (!fu_hid_device_get_report(FU_HID_DEVICE(self),
|
||||
FU_QSI_DOCK_REPORT_ID,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
FU_QSI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_wait_for_spi_erase_ready_cb(FuQsiDockMcuDevice *self,
|
||||
guint32 length,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
guint8 buf[64] = {FU_QSI_DOCK_REPORT_ID,
|
||||
FU_QSI_DOCK_CMD1_SPI,
|
||||
FU_QSI_DOCK_CMD2_SPI_EXTERNAL_FLASH_ERASE};
|
||||
guint32 offset = 0;
|
||||
guint8 fw_length[4];
|
||||
guint8 flash_offset[4];
|
||||
|
||||
fu_memwrite_uint32(fw_length, length, G_LITTLE_ENDIAN);
|
||||
fu_memwrite_uint32(flash_offset, offset, G_LITTLE_ENDIAN);
|
||||
|
||||
/* write erase flash size */
|
||||
if (!fu_memcpy_safe(buf,
|
||||
sizeof(buf),
|
||||
0x03, /* dst */
|
||||
fw_length,
|
||||
sizeof(fw_length),
|
||||
0x0,
|
||||
sizeof(fw_length),
|
||||
error))
|
||||
return FALSE;
|
||||
if (!fu_memcpy_safe(buf,
|
||||
sizeof(buf),
|
||||
0x07, /* dst */
|
||||
flash_offset,
|
||||
sizeof(flash_offset),
|
||||
0x0,
|
||||
sizeof(flash_offset),
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* SetReport+GetReport */
|
||||
if (!fu_hid_device_set_report(FU_HID_DEVICE(self),
|
||||
FU_QSI_DOCK_REPORT_ID,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
FU_QSI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error))
|
||||
return FALSE;
|
||||
memset(buf, 0x0, sizeof(buf));
|
||||
if (!fu_hid_device_get_report(FU_HID_DEVICE(self),
|
||||
FU_QSI_DOCK_REPORT_ID,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
FU_QSI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
fu_qsi_dock_mcu_device_write_firmware_with_idx(FuQsiDockMcuDevice *self,
|
||||
FuFirmware *firmware,
|
||||
guint8 chip_idx,
|
||||
FuProgress *progress,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
guint32 checksum_val = 0;
|
||||
g_autoptr(GBytes) fw_align = NULL;
|
||||
g_autoptr(GBytes) fw = NULL;
|
||||
g_autoptr(GPtrArray) chunks = 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_WRITE, 90, NULL);
|
||||
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 10, NULL);
|
||||
|
||||
/* align data */
|
||||
fw = fu_firmware_get_bytes(firmware, error);
|
||||
if (fw == NULL)
|
||||
return FALSE;
|
||||
fw_align = fu_bytes_align(fw, FU_QSI_DOCK_EXTERN_FLASH_PAGE_SIZE, 0x0);
|
||||
|
||||
/* initial external flash */
|
||||
if (!fu_device_retry(FU_DEVICE(self),
|
||||
fu_qsi_dock_mcu_device_wait_for_spi_initial_ready_cb,
|
||||
30,
|
||||
NULL,
|
||||
error)) {
|
||||
g_prefix_error(error, "failed to wait for initial: ");
|
||||
return FALSE;
|
||||
}
|
||||
if (!fu_qsi_dock_mcu_device_wait_for_spi_erase_ready_cb(self,
|
||||
g_bytes_get_size(fw),
|
||||
fu_progress_get_child(progress),
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* write external flash */
|
||||
chunks = fu_chunk_array_new_from_bytes(fw_align, 0, 0, FU_QSI_DOCK_EXTERN_FLASH_PAGE_SIZE);
|
||||
if (!fu_qsi_dock_mcu_device_write_chunks(self,
|
||||
chunks,
|
||||
&checksum_val,
|
||||
fu_progress_get_child(progress),
|
||||
error))
|
||||
return FALSE;
|
||||
fu_progress_step_done(progress);
|
||||
|
||||
/* verify flash data */
|
||||
if (!fu_qsi_dock_mcu_device_checksum(self, checksum_val, g_bytes_get_size(fw), error))
|
||||
return FALSE;
|
||||
fu_progress_step_done(progress);
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_write_firmware(FuDevice *device,
|
||||
FuFirmware *firmware,
|
||||
FuProgress *progress,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
return fu_qsi_dock_mcu_device_write_firmware_with_idx(FU_QSI_DOCK_MCU_DEVICE(device),
|
||||
firmware,
|
||||
0xFF,
|
||||
progress,
|
||||
flags,
|
||||
error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_qsi_dock_mcu_device_attach(FuDevice *device, FuProgress *progress, GError **error)
|
||||
{
|
||||
FuQsiDockMcuDevice *self = FU_QSI_DOCK_MCU_DEVICE(device);
|
||||
|
||||
if (!fu_qsi_dock_mcu_device_get_status(self, error))
|
||||
return FALSE;
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qsi_dock_mcu_device_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, 0, "detach");
|
||||
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write");
|
||||
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach");
|
||||
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload");
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qsi_dock_mcu_device_init(FuQsiDockMcuDevice *self)
|
||||
{
|
||||
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD);
|
||||
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE);
|
||||
fu_hid_device_add_flag(FU_HID_DEVICE(self), FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER);
|
||||
fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_INHIBIT_CHILDREN);
|
||||
fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_NO_SERIAL_NUMBER);
|
||||
fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER);
|
||||
fu_device_add_protocol(FU_DEVICE(self), "com.qsi.dock");
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qsi_dock_mcu_device_class_init(FuQsiDockMcuDeviceClass *klass)
|
||||
{
|
||||
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
||||
|
||||
klass_device->setup = fu_qsi_dock_mcu_device_setup;
|
||||
klass_device->attach = fu_qsi_dock_mcu_device_attach;
|
||||
klass_device->set_progress = fu_qsi_dock_mcu_device_set_progress;
|
||||
klass_device->write_firmware = fu_qsi_dock_mcu_device_write_firmware;
|
||||
}
|
23
plugins/qsi-dock/fu-qsi-dock-mcu-device.h
Normal file
23
plugins/qsi-dock/fu-qsi-dock-mcu-device.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fwupdplugin.h>
|
||||
|
||||
#define FU_TYPE_QSI_DOCK_MCU_DEVICE (fu_qsi_dock_mcu_device_get_type())
|
||||
G_DECLARE_FINAL_TYPE(FuQsiDockMcuDevice,
|
||||
fu_qsi_dock_mcu_device,
|
||||
FU,
|
||||
QSI_DOCK_MCU_DEVICE,
|
||||
FuHidDevice)
|
||||
gboolean
|
||||
fu_qsi_dock_mcu_device_write_firmware_with_idx(FuQsiDockMcuDevice *self,
|
||||
FuFirmware *firmware,
|
||||
guint8 chip_idx,
|
||||
FuProgress *progress,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error);
|
36
plugins/qsi-dock/fu-qsi-dock-plugin.c
Normal file
36
plugins/qsi-dock/fu-qsi-dock-plugin.c
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
* Copyright (C) 2022 Kevin Chen <hsinfu.chen@qsitw.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "fu-qsi-dock-mcu-device.h"
|
||||
#include "fu-qsi-dock-plugin.h"
|
||||
|
||||
struct _FuQsiDockPlugin {
|
||||
FuPlugin parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FuQsiDockPlugin, fu_qsi_dock_plugin, FU_TYPE_PLUGIN)
|
||||
|
||||
static void
|
||||
fu_qsi_dock_plugin_init(FuQsiDockPlugin *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qsi_dock_plugin_constructed(GObject *obj)
|
||||
{
|
||||
FuPlugin *plugin = FU_PLUGIN(obj);
|
||||
fu_plugin_add_device_gtype(plugin, FU_TYPE_QSI_DOCK_MCU_DEVICE);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qsi_dock_plugin_class_init(FuQsiDockPluginClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
||||
object_class->constructed = fu_qsi_dock_plugin_constructed;
|
||||
}
|
11
plugins/qsi-dock/fu-qsi-dock-plugin.h
Normal file
11
plugins/qsi-dock/fu-qsi-dock-plugin.h
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fwupdplugin.h>
|
||||
|
||||
G_DECLARE_FINAL_TYPE(FuQsiDockPlugin, fu_qsi_dock_plugin, FU, QSI_DOCK_PLUGIN, FuPlugin)
|
16
plugins/qsi-dock/meson.build
Normal file
16
plugins/qsi-dock/meson.build
Normal file
@ -0,0 +1,16 @@
|
||||
if gusb.found()
|
||||
cargs = ['-DG_LOG_DOMAIN="FuPluginQsiDock"']
|
||||
|
||||
plugin_quirks += files('qsi-dock.quirk')
|
||||
plugin_builtins += static_library('fu_plugin_qsi_dock',
|
||||
sources: [
|
||||
'fu-qsi-dock-child-device.c',
|
||||
'fu-qsi-dock-mcu-device.c',
|
||||
'fu-qsi-dock-plugin.c',
|
||||
],
|
||||
include_directories: plugin_incdirs,
|
||||
link_with: plugin_libs,
|
||||
c_args: cargs,
|
||||
dependencies: plugin_deps,
|
||||
)
|
||||
endif
|
3
plugins/qsi-dock/qsi-dock.quirk
Normal file
3
plugins/qsi-dock/qsi-dock.quirk
Normal file
@ -0,0 +1,3 @@
|
||||
[USB\VID_047D&PID_808D]
|
||||
Plugin = qsi_dock
|
||||
GType = FuQsiDockMcuDevice
|
Loading…
Reference in New Issue
Block a user