qsi-dock: Create a new plugin to update docks from QSI

This commit is contained in:
Kevin Chen 2022-10-26 13:55:02 +08:00 committed by Richard Hughes
parent 14ef2d17c6
commit a447fca101
11 changed files with 878 additions and 0 deletions

View File

@ -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')

View 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`.

View 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);
}

View 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);

View 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;

View 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;
}

View 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);

View 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;
}

View 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)

View 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

View File

@ -0,0 +1,3 @@
[USB\VID_047D&PID_808D]
Plugin = qsi_dock
GType = FuQsiDockMcuDevice