mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-14 04:33:08 +00:00
usi-dock: Add support for various TBT4 docks for Lenovo and HP
With much help from Victor Cheng <victor_cheng@usiglobal.com>
This commit is contained in:
parent
f6833f96c3
commit
56f6f99633
@ -484,6 +484,7 @@ done
|
||||
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_uefi_pk.so
|
||||
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_uefi_recovery.so
|
||||
%endif
|
||||
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_usi_dock.so
|
||||
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_logind.so
|
||||
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_logitech_bulkcontroller.so
|
||||
%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_logitech_hidpp.so
|
||||
|
@ -71,6 +71,7 @@ subdir('uefi-dbx')
|
||||
subdir('uefi-pk')
|
||||
subdir('uefi-recovery')
|
||||
subdir('upower')
|
||||
subdir('usi-dock')
|
||||
subdir('vli')
|
||||
subdir('wacom-raw')
|
||||
subdir('wacom-usb')
|
||||
|
37
plugins/usi-dock/README.md
Normal file
37
plugins/usi-dock/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# USI 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.usi.dock
|
||||
|
||||
## GUID Generation
|
||||
|
||||
These devices use the standard USB DeviceInstanceId values, e.g.
|
||||
|
||||
* `USB\VID_17EF&PID_7226&REV_0001`
|
||||
* `USB\VID_17EF&PID_7226`
|
||||
* `USB\VID_17EF`
|
||||
|
||||
Additionally, some extra "component ID" instance IDs are added.
|
||||
|
||||
* `USB\VID_17EF&PID_7226&CID_TBT4`
|
||||
* `USB\VID_17EF&PID_7226&CID_USB3`
|
||||
|
||||
## 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`.
|
68
plugins/usi-dock/data/lsusb.txt
Normal file
68
plugins/usi-dock/data/lsusb.txt
Normal file
@ -0,0 +1,68 @@
|
||||
Bus 001 Device 024: ID 17ef:30b4 Lenovo ThinkPad Thunderbolt 4 Dock MCU Controller
|
||||
Device Descriptor:
|
||||
bLength 18
|
||||
bDescriptorType 1
|
||||
bcdUSB 2.00
|
||||
bDeviceClass 0
|
||||
bDeviceSubClass 0
|
||||
bDeviceProtocol 0
|
||||
bMaxPacketSize0 64
|
||||
idVendor 0x17ef Lenovo
|
||||
idProduct 0x30b4
|
||||
bcdDevice 1.00
|
||||
iManufacturer 1 Lenovo
|
||||
iProduct 2 ThinkPad Thunderbolt 4 Dock MCU Controller
|
||||
iSerial 3
|
||||
bNumConfigurations 1
|
||||
Configuration Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 2
|
||||
wTotalLength 0x0029
|
||||
bNumInterfaces 1
|
||||
bConfigurationValue 1
|
||||
iConfiguration 0
|
||||
bmAttributes 0x80
|
||||
(Bus Powered)
|
||||
MaxPower 100mA
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 0
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 2
|
||||
bInterfaceClass 3 Human Interface Device
|
||||
bInterfaceSubClass 0
|
||||
bInterfaceProtocol 0
|
||||
iInterface 0
|
||||
HID Device Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 33
|
||||
bcdHID 1.11
|
||||
bCountryCode 0 Not supported
|
||||
bNumDescriptors 1
|
||||
bDescriptorType 34 Report
|
||||
wDescriptorLength 39
|
||||
Report Descriptors:
|
||||
** UNAVAILABLE **
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x81 EP 1 IN
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0040 1x 64 bytes
|
||||
bInterval 1
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x02 EP 2 OUT
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0040 1x 64 bytes
|
||||
bInterval 1
|
||||
Device Status: 0x0000
|
||||
(Bus Powered)
|
43
plugins/usi-dock/fu-plugin-usi-dock.c
Normal file
43
plugins/usi-dock/fu-plugin-usi-dock.c
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
* Copyright (C) 2021 Victor Cheng <victor_cheng@usiglobal.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <fwupdplugin.h>
|
||||
|
||||
#include "fu-usi-dock-dmc-device.h"
|
||||
#include "fu-usi-dock-mcu-device.h"
|
||||
|
||||
#define USI_DOCK_TBT_INSTANCE_ID "THUNDERBOLT\\VEN_0108&DEV_2031"
|
||||
|
||||
static void
|
||||
fu_plugin_usi_dock_dmc_registered(FuPlugin *plugin, FuDevice *device)
|
||||
{
|
||||
/* usb device from thunderbolt plugin */
|
||||
if (g_strcmp0(fu_device_get_plugin(device), "thunderbolt") == 0 &&
|
||||
fu_device_has_guid(device, USI_DOCK_TBT_INSTANCE_ID)) {
|
||||
g_autofree gchar *msg = NULL;
|
||||
msg = g_strdup_printf("firmware update inhibited by [%s] plugin",
|
||||
fu_plugin_get_name(plugin));
|
||||
fu_device_inhibit(device, "usb-blocked", msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fu_usi_dock_init(FuPlugin *plugin)
|
||||
{
|
||||
fu_plugin_add_device_gtype(plugin, FU_TYPE_USI_DOCK_MCU_DEVICE);
|
||||
fu_plugin_add_device_gtype(plugin, FU_TYPE_USI_DOCK_DMC_DEVICE);
|
||||
}
|
||||
|
||||
void
|
||||
fu_plugin_init_vfuncs(FuPluginVfuncs *vfuncs)
|
||||
{
|
||||
vfuncs->build_hash = FU_BUILD_HASH;
|
||||
vfuncs->init = fu_usi_dock_init;
|
||||
vfuncs->device_registered = fu_plugin_usi_dock_dmc_registered;
|
||||
}
|
88
plugins/usi-dock/fu-usi-dock-child-device.c
Normal file
88
plugins/usi-dock/fu-usi-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-usi-dock-child-device.h"
|
||||
#include "fu-usi-dock-mcu-device.h"
|
||||
|
||||
struct _FuUsiDockChildDevice {
|
||||
FuDevice parent_instance;
|
||||
guint8 chip_idx;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FuUsiDockChildDevice, fu_usi_dock_child_device, FU_TYPE_DEVICE)
|
||||
|
||||
void
|
||||
fu_usi_dock_child_device_set_chip_idx(FuUsiDockChildDevice *self, guint8 chip_idx)
|
||||
{
|
||||
self->chip_idx = chip_idx;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_usi_dock_child_device_to_string(FuDevice *device, guint idt, GString *str)
|
||||
{
|
||||
FuUsiDockChildDevice *self = FU_USI_DOCK_CHILD_DEVICE(device);
|
||||
fu_common_string_append_kx(str, idt, "ChipIdx", self->chip_idx);
|
||||
}
|
||||
|
||||
/* use the parents parser */
|
||||
static FuFirmware *
|
||||
fu_usi_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_usi_dock_mcu_device_write_firmware(FuDevice *device,
|
||||
FuFirmware *firmware,
|
||||
FuProgress *progress,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
FuUsiDockChildDevice *self = FU_USI_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_usi_dock_mcu_device_write_firmware_with_idx(FU_USI_DOCK_MCU_DEVICE(parent),
|
||||
firmware,
|
||||
self->chip_idx,
|
||||
progress,
|
||||
flags,
|
||||
error);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_usi_dock_child_device_init(FuUsiDockChildDevice *self)
|
||||
{
|
||||
fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_usi_dock_child_device_class_init(FuUsiDockChildDeviceClass *klass)
|
||||
{
|
||||
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
||||
klass_device->to_string = fu_usi_dock_child_device_to_string;
|
||||
klass_device->prepare_firmware = fu_usi_dock_mcu_device_prepare_firmware;
|
||||
klass_device->write_firmware = fu_usi_dock_mcu_device_write_firmware;
|
||||
}
|
||||
|
||||
FuDevice *
|
||||
fu_usi_dock_child_new(FuContext *ctx)
|
||||
{
|
||||
return g_object_new(FU_TYPE_USI_DOCK_CHILD_DEVICE, "context", ctx, NULL);
|
||||
}
|
21
plugins/usi-dock/fu-usi-dock-child-device.h
Normal file
21
plugins/usi-dock/fu-usi-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_USI_DOCK_CHILD_DEVICE (fu_usi_dock_child_device_get_type())
|
||||
G_DECLARE_FINAL_TYPE(FuUsiDockChildDevice,
|
||||
fu_usi_dock_child_device,
|
||||
FU,
|
||||
USI_DOCK_CHILD_DEVICE,
|
||||
FuDevice)
|
||||
|
||||
FuDevice *
|
||||
fu_usi_dock_child_new(FuContext *ctx);
|
||||
void
|
||||
fu_usi_dock_child_device_set_chip_idx(FuUsiDockChildDevice *self, guint8 chip_idx);
|
40
plugins/usi-dock/fu-usi-dock-common.c
Normal file
40
plugins/usi-dock/fu-usi-dock-common.c
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
* Copyright (C) 2021 Victor Cheng <victor_cheng@usiglobal.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "fu-usi-dock-common.h"
|
||||
|
||||
const gchar *
|
||||
fu_usi_dock_spi_state_to_string(guint8 val)
|
||||
{
|
||||
if (val == SPI_STATE_NONE)
|
||||
return "none";
|
||||
if (val == SPI_STATE_SWITCH_SUCCESS)
|
||||
return "switch-success";
|
||||
if (val == SPI_STATE_SWITCH_FAIL)
|
||||
return "switch-fail";
|
||||
if (val == SPI_STATE_CMD_SUCCESS)
|
||||
return "cmd-success";
|
||||
if (val == SPI_STATE_CMD_FAIL)
|
||||
return "cmd-fail";
|
||||
if (val == SPI_STATE_RW_SUCCESS)
|
||||
return "rw-success";
|
||||
if (val == SPI_STATE_RW_FAIL)
|
||||
return "rw-fail";
|
||||
if (val == SPI_STATE_READY)
|
||||
return "ready";
|
||||
if (val == SPI_STATE_BUSY)
|
||||
return "busy";
|
||||
if (val == SPI_STATE_TIMEOUT)
|
||||
return "timeout";
|
||||
if (val == SPI_STATE_FLASH_FOUND)
|
||||
return "flash-found";
|
||||
if (val == SPI_STATE_FLASH_NOT_FOUND)
|
||||
return "flash-not-found";
|
||||
return NULL;
|
||||
}
|
107
plugins/usi-dock/fu-usi-dock-common.h
Normal file
107
plugins/usi-dock/fu-usi-dock-common.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
* Copyright (C) 2021 Victor Cheng <victor_cheng@usiglobal.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#define USB_HID_REPORT_ID1 1u
|
||||
#define USB_HID_REPORT_ID2 2u
|
||||
|
||||
#define USBUID_ISP_DEVICE_CMD_MCU_NONE 0x0
|
||||
#define USBUID_ISP_DEVICE_CMD_MCU_STATUS 0x1
|
||||
#define USBUID_ISP_DEVICE_CMD_MCU_JUMP2BOOT 0x2
|
||||
#define USBUID_ISP_DEVICE_CMD_READ_MCU_VERSIONPAGE 0x3
|
||||
#define USBUID_ISP_DEVICE_CMD_SET_I225_PWR 0x4
|
||||
#define USBUID_ISP_DEVICE_CMD_DOCK_RESET 0x5
|
||||
#define USBUID_ISP_DEVICE_CMD_VERSION_WRITEBACK 0x6
|
||||
|
||||
#define USBUID_ISP_DEVICE_CMD_FWBUFER_INITIAL 0x01
|
||||
#define USBUID_ISP_DEVICE_CMD_FWBUFER_ERASE_FLASH 0x02
|
||||
#define USBUID_ISP_DEVICE_CMD_FWBUFER_PROGRAM 0x03
|
||||
#define USBUID_ISP_DEVICE_CMD_FWBUFER_WRITE_RESPONSE 0x04
|
||||
#define USBUID_ISP_DEVICE_CMD_FWBUFER_READ_STATUS 0x05
|
||||
#define USBUID_ISP_DEVICE_CMD_FWBUFER_CHECKSUM 0x06
|
||||
#define USBUID_ISP_DEVICE_CMD_FWBUFER_END 0x07
|
||||
#define USBUID_ISP_DEVICE_CMD_FWBUFER_TRANSFER_FINISH 0x08
|
||||
#define USBUID_ISP_DEVICE_CMD_FWBUFER_ERROR_END 0x09
|
||||
|
||||
#define USBUID_ISP_INTERNAL_FW_CMD_INITAL 0x0A
|
||||
#define USBUID_ISP_INTERNAL_FW_CMD_UPDATE_FW 0x0B
|
||||
#define USBUID_ISP_INTERNAL_FW_CMD_TARGET_CHECKSUM 0x0C
|
||||
#define USBUID_ISP_INTERNAL_FW_CMD_ISP_END 0x0D
|
||||
#define USBUID_ISP_CMD_ALL 0xFF
|
||||
|
||||
#define TAG_TAG2_ISP_BOOT 0 /* before Common CMD for bootload, with TAG0, TAG1, CMD */
|
||||
#define TAG_TAG2_ISP 0x5a /* before Common, with TAG0, TAG1, CMD */
|
||||
#define TAG_TAG2_CMD_MCU 0x6a /* USB->MCU(Common-cmd mode), with TAG0, TAG1, CMD */
|
||||
#define TAG_TAG2_CMD_SPI 0x7a /* USB->MCU->SPI(Common-cmd mode), with TAG0, TAG1, CMD */
|
||||
#define TAG_TAG2_CMD_I2C 0x8a /* USB->MCU->I2C(Mass data transmission) */
|
||||
#define TAG_TAG2_MASS_DATA_MCU 0x6b /* MASS data transfer for MCU 0xA0 */
|
||||
#define TAG_TAG2_MASS_DATA_SPI 0x7b /* MASS data transfer for External flash 0xA1 */
|
||||
#define TAG_TAG2_MASS_DATA_I2C 0x8b /* MASS data transfer for TBT flash */
|
||||
|
||||
#define DP_VERSION_FROM_MCU 0x01 /* if in use */
|
||||
#define NIC_VERSION_FROM_MCU 0x2 /* if in use */
|
||||
|
||||
#define External_Valid_Value 0x37
|
||||
#define TX_ISP_LENGTH 61
|
||||
|
||||
#define W25Q16DV_PAGE_SIZE 256
|
||||
|
||||
#define FIRMWARE_IDX_NONE 0x00
|
||||
#define FIRMWARE_IDX_DMC_PD 0x01
|
||||
#define FIRMWARE_IDX_DP 0x02
|
||||
#define FIRMWARE_IDX_TBT4 0x04
|
||||
#define FIRMWARE_IDX_USB3 0x08
|
||||
#define FIRMWARE_IDX_USB2 0x10
|
||||
#define FIRMWARE_IDX_AUDIO 0x20
|
||||
#define FIRMWARE_IDX_I225 0x40
|
||||
#define FIRMWARE_IDX_MCU 0x80
|
||||
|
||||
typedef enum {
|
||||
SPI_STATE_NONE,
|
||||
SPI_STATE_SWITCH_SUCCESS,
|
||||
SPI_STATE_SWITCH_FAIL,
|
||||
SPI_STATE_CMD_SUCCESS,
|
||||
SPI_STATE_CMD_FAIL,
|
||||
SPI_STATE_RW_SUCCESS,
|
||||
SPI_STATE_RW_FAIL,
|
||||
SPI_STATE_READY,
|
||||
SPI_STATE_BUSY,
|
||||
SPI_STATE_TIMEOUT,
|
||||
SPI_STATE_FLASH_FOUND,
|
||||
SPI_STATE_FLASH_NOT_FOUND,
|
||||
} SPI_BUS_STATE;
|
||||
|
||||
typedef struct {
|
||||
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];
|
||||
} IspVersionInMcu_t;
|
||||
|
||||
typedef struct {
|
||||
guint8 id;
|
||||
guint8 length;
|
||||
guint8 mcutag1;
|
||||
guint8 mcutag2;
|
||||
guint8 inbuf[59];
|
||||
guint8 mcutag3;
|
||||
} UsiDockSetReportBuf;
|
||||
|
||||
const gchar *
|
||||
fu_usi_dock_idx_to_string(guint8 val);
|
||||
const gchar *
|
||||
fu_usi_dock_spi_state_to_string(guint8 val);
|
55
plugins/usi-dock/fu-usi-dock-dmc-device.c
Normal file
55
plugins/usi-dock/fu-usi-dock-dmc-device.c
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "fu-usi-dock-dmc-device.h"
|
||||
|
||||
struct _FuUsiDockDmcDevice {
|
||||
FuUsbDevice parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FuUsiDockDmcDevice, fu_usi_dock_dmc_device, FU_TYPE_USB_DEVICE)
|
||||
|
||||
static void
|
||||
fu_usi_dock_dmc_device_parent_notify_cb(FuDevice *device, GParamSpec *pspec, gpointer user_data)
|
||||
{
|
||||
FuDevice *parent = fu_device_get_parent(device);
|
||||
if (parent != NULL) {
|
||||
g_autofree gchar *instance_id = NULL;
|
||||
|
||||
/* slightly odd: the MCU device uses the DMC version number */
|
||||
g_debug("absorbing DMC version into MCU");
|
||||
fu_device_set_version_format(parent, fu_device_get_version_format(device));
|
||||
fu_device_set_version(parent, fu_device_get_version(device));
|
||||
fu_device_set_serial(parent, fu_device_get_serial(device));
|
||||
|
||||
/* allow matching firmware */
|
||||
instance_id = g_strdup_printf("USB\\VID_%04X&PID_%04X&CID_%s",
|
||||
fu_usb_device_get_vid(FU_USB_DEVICE(parent)),
|
||||
fu_usb_device_get_pid(FU_USB_DEVICE(parent)),
|
||||
fu_device_get_name(device));
|
||||
fu_device_add_instance_id(parent, instance_id);
|
||||
|
||||
/* don't allow firmware updates on this */
|
||||
fu_device_set_name(device, "Dock Management Controller Information");
|
||||
fu_device_inhibit(device, "dummy", "Use the MCU to update the DMC device");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fu_usi_dock_dmc_device_init(FuUsiDockDmcDevice *self)
|
||||
{
|
||||
g_signal_connect(self,
|
||||
"notify::parent",
|
||||
G_CALLBACK(fu_usi_dock_dmc_device_parent_notify_cb),
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_usi_dock_dmc_device_class_init(FuUsiDockDmcDeviceClass *klass)
|
||||
{
|
||||
}
|
16
plugins/usi-dock/fu-usi-dock-dmc-device.h
Normal file
16
plugins/usi-dock/fu-usi-dock-dmc-device.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fwupdplugin.h>
|
||||
|
||||
#define FU_TYPE_USI_DOCK_DMC_DEVICE (fu_usi_dock_dmc_device_get_type())
|
||||
G_DECLARE_FINAL_TYPE(FuUsiDockDmcDevice,
|
||||
fu_usi_dock_dmc_device,
|
||||
FU,
|
||||
USI_DOCK_DMC_DEVICE,
|
||||
FuUsbDevice)
|
738
plugins/usi-dock/fu-usi-dock-mcu-device.c
Normal file
738
plugins/usi-dock/fu-usi-dock-mcu-device.c
Normal file
@ -0,0 +1,738 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
* Copyright (C) 2021 Victor Cheng <victor_cheng@usiglobal.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "fu-usi-dock-child-device.h"
|
||||
#include "fu-usi-dock-common.h"
|
||||
#include "fu-usi-dock-dmc-device.h"
|
||||
#include "fu-usi-dock-mcu-device.h"
|
||||
|
||||
struct _FuUsiDockMcuDevice {
|
||||
FuHidDevice parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FuUsiDockMcuDevice, fu_usi_dock_mcu_device, FU_TYPE_HID_DEVICE)
|
||||
|
||||
#define FU_USI_DOCK_MCU_DEVICE_TIMEOUT 5000 /* ms */
|
||||
|
||||
#define FU_USI_DOCK_DEVICE_FLAG_VERFMT_HP (1 << 0)
|
||||
|
||||
static gboolean
|
||||
fu_usi_dock_mcu_device_tx(FuUsiDockMcuDevice *self,
|
||||
guint8 tag2,
|
||||
const guint8 *inbuf,
|
||||
gsize inbufsz,
|
||||
GError **error)
|
||||
{
|
||||
UsiDockSetReportBuf tx_buffer = {
|
||||
.id = USB_HID_REPORT_ID2,
|
||||
.length = 0x3 + inbufsz,
|
||||
.mcutag1 = 0xFE,
|
||||
.mcutag2 = 0xFF,
|
||||
.mcutag3 = tag2,
|
||||
};
|
||||
|
||||
if (inbuf != NULL) {
|
||||
if (!fu_memcpy_safe(tx_buffer.inbuf,
|
||||
sizeof(tx_buffer.inbuf),
|
||||
0x0, /* dst */
|
||||
inbuf,
|
||||
inbufsz,
|
||||
0x0, /* src */
|
||||
inbufsz,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* special cases */
|
||||
if (tx_buffer.inbuf[0] == USBUID_ISP_INTERNAL_FW_CMD_UPDATE_FW) {
|
||||
tx_buffer.inbuf[1] = 0xFF;
|
||||
}
|
||||
|
||||
return fu_hid_device_set_report(FU_HID_DEVICE(self),
|
||||
USB_HID_REPORT_ID2,
|
||||
(guint8 *)&tx_buffer,
|
||||
sizeof(tx_buffer),
|
||||
FU_USI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_usi_dock_mcu_device_rx(FuUsiDockMcuDevice *self,
|
||||
guint8 cmd,
|
||||
guint8 *outbuf,
|
||||
gsize outbufsz,
|
||||
GError **error)
|
||||
{
|
||||
guint8 buf[64] = {0};
|
||||
|
||||
if (!fu_hid_device_get_report(FU_HID_DEVICE(self),
|
||||
USB_HID_REPORT_ID2,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
FU_USI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error)) {
|
||||
return FALSE;
|
||||
}
|
||||
if (buf[0] != USB_HID_REPORT_ID2) {
|
||||
g_set_error(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"invalid ID, expected 0x%02x, got 0x%02x",
|
||||
USB_HID_REPORT_ID2,
|
||||
buf[0]);
|
||||
return FALSE;
|
||||
}
|
||||
if (buf[2] != 0xFE || buf[3] != 0xFF) {
|
||||
g_set_error(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"invalid tags, expected 0x%02x:0x%02x, got 0x%02x:0x%02x",
|
||||
0xFEu,
|
||||
0xFFu,
|
||||
buf[2],
|
||||
buf[3]);
|
||||
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_usi_dock_mcu_device_txrx(FuUsiDockMcuDevice *self,
|
||||
guint8 tag2,
|
||||
const guint8 *inbuf,
|
||||
gsize inbufsz,
|
||||
guint8 *outbuf,
|
||||
gsize outbufsz,
|
||||
GError **error)
|
||||
{
|
||||
if (!fu_usi_dock_mcu_device_tx(self, tag2, inbuf, inbufsz, error))
|
||||
return FALSE;
|
||||
return fu_usi_dock_mcu_device_rx(self, USBUID_ISP_CMD_ALL, outbuf, outbufsz, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_usi_dock_mcu_device_get_status(FuUsiDockMcuDevice *self, GError **error)
|
||||
{
|
||||
guint8 cmd = USBUID_ISP_DEVICE_CMD_MCU_STATUS;
|
||||
guint8 response = 0;
|
||||
|
||||
if (!fu_usi_dock_mcu_device_txrx(self,
|
||||
TAG_TAG2_CMD_MCU,
|
||||
&cmd,
|
||||
sizeof(cmd),
|
||||
&response,
|
||||
sizeof(response),
|
||||
error))
|
||||
return FALSE;
|
||||
if (response == 0x1) {
|
||||
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_BUSY, "device is busy");
|
||||
return FALSE;
|
||||
}
|
||||
if (response == 0xFF) {
|
||||
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "device timed out");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_usi_dock_mcu_device_enumerate_children(FuUsiDockMcuDevice *self, GError **error)
|
||||
{
|
||||
guint8 inbuf[] = {USBUID_ISP_DEVICE_CMD_READ_MCU_VERSIONPAGE,
|
||||
DP_VERSION_FROM_MCU | NIC_VERSION_FROM_MCU};
|
||||
guint8 outbuf[49] = {0x0};
|
||||
struct {
|
||||
const gchar *name;
|
||||
guint8 chip_idx;
|
||||
gsize offset;
|
||||
} components[] = {
|
||||
{"DMC", FIRMWARE_IDX_DMC_PD, G_STRUCT_OFFSET(IspVersionInMcu_t, DMC)},
|
||||
{"PD", FIRMWARE_IDX_DP, G_STRUCT_OFFSET(IspVersionInMcu_t, PD)},
|
||||
{"DP5x", FIRMWARE_IDX_NONE, G_STRUCT_OFFSET(IspVersionInMcu_t, DP5x)},
|
||||
{"DP6x", FIRMWARE_IDX_NONE, G_STRUCT_OFFSET(IspVersionInMcu_t, DP6x)},
|
||||
{"TBT4", FIRMWARE_IDX_TBT4, G_STRUCT_OFFSET(IspVersionInMcu_t, TBT4)},
|
||||
{"USB3", FIRMWARE_IDX_USB3, G_STRUCT_OFFSET(IspVersionInMcu_t, USB3)},
|
||||
{"USB2", FIRMWARE_IDX_USB2, G_STRUCT_OFFSET(IspVersionInMcu_t, USB2)},
|
||||
{"AUDIO", FIRMWARE_IDX_AUDIO, G_STRUCT_OFFSET(IspVersionInMcu_t, AUDIO)},
|
||||
{"I255", FIRMWARE_IDX_I225, G_STRUCT_OFFSET(IspVersionInMcu_t, I255)},
|
||||
{"MCU", FIRMWARE_IDX_MCU, G_STRUCT_OFFSET(IspVersionInMcu_t, MCU)},
|
||||
{"bcdVersion", FIRMWARE_IDX_NONE, G_STRUCT_OFFSET(IspVersionInMcu_t, bcdVersion)},
|
||||
{NULL, 0, 0}};
|
||||
|
||||
/* assume DP and NIC in-use */
|
||||
if (!fu_usi_dock_mcu_device_txrx(self,
|
||||
TAG_TAG2_CMD_MCU,
|
||||
inbuf,
|
||||
sizeof(inbuf),
|
||||
outbuf,
|
||||
sizeof(outbuf),
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
for (guint i = 0; components[i].name != NULL; i++) {
|
||||
const guint8 *val = outbuf + components[i].offset;
|
||||
g_autofree gchar *version = NULL;
|
||||
g_autofree gchar *instance_id = NULL;
|
||||
g_autoptr(FuDevice) child = NULL;
|
||||
|
||||
child = fu_usi_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;
|
||||
}
|
||||
if (fu_device_has_private_flag(FU_DEVICE(self),
|
||||
FU_USI_DOCK_DEVICE_FLAG_VERFMT_HP)) {
|
||||
version = g_strdup_printf("%x.%x.%x.%x",
|
||||
val[0] >> 4,
|
||||
val[0] & 0xFu,
|
||||
val[1] >> 4,
|
||||
val[1] & 0xFu);
|
||||
fu_device_set_version_format(FU_DEVICE(self),
|
||||
FWUPD_VERSION_FORMAT_QUAD);
|
||||
fu_device_set_version(FU_DEVICE(self), version);
|
||||
} else {
|
||||
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, "DMC") == 0) {
|
||||
if ((val[2] == 0x00 && val[3] == 0x00 && val[4] == 0x00) ||
|
||||
(val[2] == 0xFF && val[3] == 0xFF && val[4] == 0xFF)) {
|
||||
g_debug("ignoring %s", components[i].name);
|
||||
continue;
|
||||
}
|
||||
version = g_strdup_printf("%d.%d.%d", val[2], val[3], val[4]);
|
||||
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET);
|
||||
fu_device_set_version(child, version);
|
||||
fu_device_set_name(child, "Dock Management Controller");
|
||||
} else if (g_strcmp0(components[i].name, "PD") == 0) {
|
||||
if ((val[1] == 0x00 && val[2] == 0x00 && val[3] == 0x00 &&
|
||||
val[4] == 0x00) ||
|
||||
(val[1] == 0xFF && val[2] == 0xFF && val[3] == 0xFF &&
|
||||
val[4] == 0xFF)) {
|
||||
g_debug("ignoring %s", components[i].name);
|
||||
continue;
|
||||
}
|
||||
if (fu_device_has_private_flag(FU_DEVICE(self),
|
||||
FU_USI_DOCK_DEVICE_FLAG_VERFMT_HP)) {
|
||||
version =
|
||||
g_strdup_printf("%d.%d.%d.%d", val[3], val[4], val[1], val[2]);
|
||||
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_QUAD);
|
||||
} else {
|
||||
version = g_strdup_printf("%d.%d.%d", val[2], val[3], val[4]);
|
||||
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET);
|
||||
}
|
||||
fu_device_set_version(child, version);
|
||||
fu_device_set_name(child, "Power Delivery");
|
||||
} else if (g_strcmp0(components[i].name, "TBT4") == 0) {
|
||||
if ((val[1] == 0x00 && val[2] == 0x00 && val[3] == 0x00) ||
|
||||
(val[1] == 0xFF && val[2] == 0xFF && val[3] == 0xFF)) {
|
||||
g_debug("ignoring %s", components[i].name);
|
||||
continue;
|
||||
}
|
||||
version = g_strdup_printf("%02x.%02x.%02x", val[1], val[2], val[3]);
|
||||
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET);
|
||||
fu_device_set_version(child, version);
|
||||
fu_device_add_icon(child, "thunderbolt");
|
||||
fu_device_set_name(child, "Thunderbolt 4 Controller");
|
||||
} else if (g_strcmp0(components[i].name, "DP5x") == 0) {
|
||||
if ((val[2] == 0x00 && val[3] == 0x00 && val[4] == 0x00) ||
|
||||
(val[2] == 0xFF && val[3] == 0xFF && val[4] == 0xFF)) {
|
||||
g_debug("ignoring %s", components[i].name);
|
||||
continue;
|
||||
}
|
||||
version = g_strdup_printf("%d.%02d.%03d", val[2], val[3], val[4]);
|
||||
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET);
|
||||
fu_device_set_version(child, version);
|
||||
fu_device_add_icon(child, "video-display");
|
||||
fu_device_set_name(child, "Display Port 5");
|
||||
} else if (g_strcmp0(components[i].name, "DP6x") == 0) {
|
||||
if ((val[2] == 0x00 && val[3] == 0x00 && val[4] == 0x00) ||
|
||||
(val[2] == 0xFF && val[3] == 0xFF && val[4] == 0xFF)) {
|
||||
g_debug("ignoring %s", components[i].name);
|
||||
continue;
|
||||
}
|
||||
if (fu_device_has_private_flag(FU_DEVICE(self),
|
||||
FU_USI_DOCK_DEVICE_FLAG_VERFMT_HP)) {
|
||||
version =
|
||||
g_strdup_printf("%x.%x.%x.%x", val[3], val[4], val[2], val[1]);
|
||||
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_QUAD);
|
||||
fu_device_set_name(child, "USB/PD HUB");
|
||||
} else {
|
||||
version = g_strdup_printf("%d.%02d.%03d", val[2], val[3], val[4]);
|
||||
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET);
|
||||
fu_device_set_name(child, "Display Port 6");
|
||||
}
|
||||
fu_device_set_version(child, version);
|
||||
fu_device_add_icon(child, "video-display");
|
||||
} else if (g_strcmp0(components[i].name, "USB3") == 0) {
|
||||
if ((val[3] == 0x00 && val[4] == 0x00) ||
|
||||
(val[3] == 0xFF && val[4] == 0xFF)) {
|
||||
g_debug("ignoring %s", components[i].name);
|
||||
continue;
|
||||
}
|
||||
version = g_strdup_printf("%02X%02X", val[3], val[4]);
|
||||
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_NUMBER);
|
||||
fu_device_set_version(child, version);
|
||||
fu_device_set_name(child, "USB 3 Hub");
|
||||
} else if (g_strcmp0(components[i].name, "USB2") == 0) {
|
||||
if ((val[0] == 0x00 && val[1] == 0x00 && val[2] == 0x00 && val[3] == 0x00 &&
|
||||
val[4] == 0x00) ||
|
||||
(val[0] == 0xFF && val[1] == 0xFF && val[2] == 0xFF && val[3] == 0xFF &&
|
||||
val[4] == 0xFF)) {
|
||||
g_debug("ignoring %s", components[i].name);
|
||||
continue;
|
||||
}
|
||||
version =
|
||||
g_strdup_printf("%c%c%c%c%c", val[0], val[1], val[2], val[3], val[4]);
|
||||
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_PLAIN);
|
||||
fu_device_set_version(child, version);
|
||||
fu_device_set_name(child, "USB 2 Hub");
|
||||
} else if (g_strcmp0(components[i].name, "AUDIO") == 0) {
|
||||
if ((val[2] == 0x00 && val[3] == 0x00 && val[4] == 0x00) ||
|
||||
(val[2] == 0xFF && val[3] == 0xFF && val[4] == 0xFF)) {
|
||||
g_debug("ignoring %s", components[i].name);
|
||||
continue;
|
||||
}
|
||||
version = g_strdup_printf("%02X-%02X-%02X", val[2], val[3], val[4]);
|
||||
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_PLAIN);
|
||||
fu_device_set_version(child, version);
|
||||
fu_device_set_name(child, "Audio Controller");
|
||||
} else if (g_strcmp0(components[i].name, "I255") == 0) {
|
||||
if ((val[2] == 0x00 && val[3] == 0x00 && val[4] == 0x00) ||
|
||||
(val[2] == 0xFF && val[3] == 0xFF && val[4] == 0xFF)) {
|
||||
g_debug("ignoring %s", components[i].name);
|
||||
continue;
|
||||
}
|
||||
version = g_strdup_printf("%x.%x.%x", val[2] >> 4, val[3], val[4]);
|
||||
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET);
|
||||
fu_device_set_version(child, version);
|
||||
fu_device_add_icon(child, "network-wired");
|
||||
fu_device_set_name(child, "Ethernet Adapter");
|
||||
} 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;
|
||||
}
|
||||
if (fu_device_has_private_flag(FU_DEVICE(self),
|
||||
FU_USI_DOCK_DEVICE_FLAG_VERFMT_HP)) {
|
||||
version = g_strdup_printf("%x.%x.%x.%x",
|
||||
val[0] >> 4,
|
||||
val[0] & 0xFu,
|
||||
val[1] >> 4,
|
||||
val[1] & 0xFu);
|
||||
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_QUAD);
|
||||
} else {
|
||||
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 */
|
||||
instance_id = g_strdup_printf("USB\\VID_%04X&PID_%04X&CID_%s",
|
||||
fu_usb_device_get_vid(FU_USB_DEVICE(self)),
|
||||
fu_usb_device_get_pid(FU_USB_DEVICE(self)),
|
||||
components[i].name);
|
||||
fu_device_add_instance_id(child, instance_id);
|
||||
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_usi_dock_child_device_set_chip_idx(FU_USI_DOCK_CHILD_DEVICE(child),
|
||||
components[i].chip_idx);
|
||||
fu_device_add_child(FU_DEVICE(self), child);
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_usi_dock_mcu_device_setup(FuDevice *device, GError **error)
|
||||
{
|
||||
FuUsiDockMcuDevice *self = FU_USI_DOCK_MCU_DEVICE(device);
|
||||
|
||||
/* FuUsbDevice->setup */
|
||||
if (!FU_DEVICE_CLASS(fu_usi_dock_mcu_device_parent_class)->setup(device, error))
|
||||
return FALSE;
|
||||
|
||||
/* get status and component versions */
|
||||
if (!fu_usi_dock_mcu_device_get_status(self, error))
|
||||
return FALSE;
|
||||
if (!fu_usi_dock_mcu_device_enumerate_children(self, error))
|
||||
return FALSE;
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_usi_dock_mcu_device_write_chunk(FuUsiDockMcuDevice *self, FuChunk *chk, GError **error)
|
||||
{
|
||||
guint8 buf[64] = {0x0};
|
||||
guint32 length = 0;
|
||||
guint32 pagesize = fu_chunk_get_data_sz(chk);
|
||||
|
||||
while (pagesize != 0) {
|
||||
memset(buf, 0x0, sizeof(buf));
|
||||
buf[63] = TAG_TAG2_MASS_DATA_SPI;
|
||||
buf[0] = USB_HID_REPORT_ID2;
|
||||
|
||||
/* set length and buffer */
|
||||
if (pagesize >= TX_ISP_LENGTH) {
|
||||
length = TX_ISP_LENGTH;
|
||||
pagesize -= TX_ISP_LENGTH;
|
||||
} else {
|
||||
length = pagesize;
|
||||
pagesize = 0;
|
||||
}
|
||||
buf[1] = length;
|
||||
|
||||
/* SetReport */
|
||||
if (!fu_memcpy_safe(buf,
|
||||
sizeof(buf),
|
||||
0x2, /* dst */
|
||||
fu_chunk_get_data(chk),
|
||||
fu_chunk_get_data_sz(chk),
|
||||
fu_chunk_get_data_sz(chk) - pagesize - length, /* src */
|
||||
length,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
if (!fu_hid_device_set_report(FU_HID_DEVICE(self),
|
||||
USB_HID_REPORT_ID2,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
FU_USI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* GetReport */
|
||||
memset(buf, 0x0, sizeof(buf));
|
||||
if (!fu_hid_device_get_report(FU_HID_DEVICE(self),
|
||||
USB_HID_REPORT_ID2,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
FU_USI_DOCK_MCU_DEVICE_TIMEOUT,
|
||||
FU_HID_DEVICE_FLAG_NONE,
|
||||
error)) {
|
||||
return FALSE;
|
||||
}
|
||||
if (buf[0] != USB_HID_REPORT_ID2) {
|
||||
g_set_error(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"invalid ID, expected 0x%02x, got 0x%02x",
|
||||
USB_HID_REPORT_ID2,
|
||||
buf[0]);
|
||||
return FALSE;
|
||||
}
|
||||
if (buf[63] != TAG_TAG2_CMD_SPI) {
|
||||
g_set_error(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"invalid tag2, expected 0x%02x, got 0x%02x",
|
||||
(guint)TAG_TAG2_CMD_SPI,
|
||||
buf[58]);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_usi_dock_mcu_device_write_chunks(FuUsiDockMcuDevice *self,
|
||||
GPtrArray *chunks,
|
||||
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_usi_dock_mcu_device_write_chunk(self, chk, error)) {
|
||||
g_prefix_error(error, "failed to write chunk 0x%x", i);
|
||||
return FALSE;
|
||||
}
|
||||
fu_progress_step_done(progress);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_usi_dock_mcu_device_wait_for_spi_ready_cb(FuDevice *device, gpointer user_data, GError **error)
|
||||
{
|
||||
FuUsiDockMcuDevice *self = FU_USI_DOCK_MCU_DEVICE(device);
|
||||
guint8 buf[] = {USBUID_ISP_DEVICE_CMD_FWBUFER_READ_STATUS};
|
||||
guint8 val = 0;
|
||||
|
||||
if (!fu_usi_dock_mcu_device_txrx(self,
|
||||
TAG_TAG2_CMD_SPI,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
&val,
|
||||
sizeof(val),
|
||||
error))
|
||||
return FALSE;
|
||||
if (val != SPI_STATE_READY) {
|
||||
g_set_error(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_BUSY,
|
||||
"SPI state is %s [0x%02x]",
|
||||
fu_usi_dock_spi_state_to_string(val),
|
||||
val);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_usi_dock_mcu_device_wait_for_checksum_cb(FuDevice *device, gpointer user_data, GError **error)
|
||||
{
|
||||
FuUsiDockMcuDevice *self = FU_USI_DOCK_MCU_DEVICE(device);
|
||||
|
||||
if (!fu_usi_dock_mcu_device_rx(self,
|
||||
USBUID_ISP_CMD_ALL,
|
||||
(guint8 *)user_data,
|
||||
sizeof(guint8),
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
fu_usi_dock_mcu_device_write_firmware_with_idx(FuUsiDockMcuDevice *self,
|
||||
FuFirmware *firmware,
|
||||
guint8 chip_idx,
|
||||
FuProgress *progress,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
guint8 cmd;
|
||||
guint8 val = 0x0;
|
||||
g_autoptr(GBytes) fw = NULL;
|
||||
g_autoptr(GPtrArray) chunks = NULL;
|
||||
guint8 checksum = 0xFF;
|
||||
|
||||
/* progress */
|
||||
fu_progress_set_id(progress, G_STRLOC);
|
||||
fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED);
|
||||
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0);
|
||||
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 6);
|
||||
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 40);
|
||||
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 13);
|
||||
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 42);
|
||||
|
||||
/* initial external flash */
|
||||
cmd = USBUID_ISP_DEVICE_CMD_FWBUFER_INITIAL;
|
||||
if (!fu_usi_dock_mcu_device_txrx(self,
|
||||
TAG_TAG2_CMD_SPI,
|
||||
&cmd,
|
||||
sizeof(cmd),
|
||||
&val,
|
||||
sizeof(val),
|
||||
error))
|
||||
return FALSE;
|
||||
if (val != SPI_STATE_READY) {
|
||||
g_set_error(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"invalid state for CMD_FWBUFER_INITIAL, got 0x%02x",
|
||||
val);
|
||||
return FALSE;
|
||||
}
|
||||
fu_progress_step_done(progress);
|
||||
|
||||
/* erase external flash */
|
||||
cmd = USBUID_ISP_DEVICE_CMD_FWBUFER_ERASE_FLASH;
|
||||
if (!fu_usi_dock_mcu_device_txrx(self,
|
||||
TAG_TAG2_CMD_SPI,
|
||||
&cmd,
|
||||
sizeof(cmd),
|
||||
NULL,
|
||||
0x0,
|
||||
error))
|
||||
return FALSE;
|
||||
if (!fu_device_retry(FU_DEVICE(self),
|
||||
fu_usi_dock_mcu_device_wait_for_spi_ready_cb,
|
||||
30,
|
||||
NULL,
|
||||
error)) {
|
||||
g_prefix_error(error, "failed to wait for erase: ");
|
||||
return FALSE;
|
||||
}
|
||||
fu_progress_step_done(progress);
|
||||
|
||||
/* write external flash */
|
||||
cmd = USBUID_ISP_DEVICE_CMD_FWBUFER_PROGRAM;
|
||||
if (!fu_usi_dock_mcu_device_txrx(self,
|
||||
TAG_TAG2_CMD_SPI,
|
||||
&cmd,
|
||||
sizeof(cmd),
|
||||
NULL,
|
||||
0x0,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
fw = fu_firmware_get_bytes(firmware, error);
|
||||
if (fw == NULL)
|
||||
return FALSE;
|
||||
chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, W25Q16DV_PAGE_SIZE);
|
||||
if (!fu_usi_dock_mcu_device_write_chunks(self,
|
||||
chunks,
|
||||
fu_progress_get_child(progress),
|
||||
error))
|
||||
return FALSE;
|
||||
fu_progress_step_done(progress);
|
||||
|
||||
/* file transfer – finished */
|
||||
cmd = USBUID_ISP_DEVICE_CMD_FWBUFER_TRANSFER_FINISH;
|
||||
if (!fu_usi_dock_mcu_device_txrx(self,
|
||||
TAG_TAG2_CMD_SPI,
|
||||
&cmd,
|
||||
sizeof(cmd),
|
||||
NULL,
|
||||
0x0,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* MCU checksum */
|
||||
if (!fu_device_retry(FU_DEVICE(self),
|
||||
fu_usi_dock_mcu_device_wait_for_checksum_cb,
|
||||
300,
|
||||
&checksum,
|
||||
error)) {
|
||||
g_prefix_error(error, "failed to wait for checksum: ");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (checksum != 0x0) {
|
||||
g_set_error(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"invalid checksum result for CMD_FWBUFER_CHECKSUM, got 0x%02x",
|
||||
checksum);
|
||||
return FALSE;
|
||||
}
|
||||
fu_progress_step_done(progress);
|
||||
|
||||
/* internal flash */
|
||||
cmd = USBUID_ISP_INTERNAL_FW_CMD_UPDATE_FW;
|
||||
if (!fu_usi_dock_mcu_device_txrx(self,
|
||||
TAG_TAG2_CMD_MCU,
|
||||
&cmd,
|
||||
sizeof(cmd),
|
||||
NULL,
|
||||
0x0,
|
||||
error))
|
||||
return FALSE;
|
||||
fu_progress_step_done(progress);
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_usi_dock_mcu_device_write_firmware(FuDevice *device,
|
||||
FuFirmware *firmware,
|
||||
FuProgress *progress,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
return fu_usi_dock_mcu_device_write_firmware_with_idx(FU_USI_DOCK_MCU_DEVICE(device),
|
||||
firmware,
|
||||
0xFF, /* all */
|
||||
progress,
|
||||
flags,
|
||||
error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_usi_dock_mcu_device_attach(FuDevice *device, FuProgress *progress, GError **error)
|
||||
{
|
||||
fu_device_set_remove_delay(device, 500000);
|
||||
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_usi_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_ERASE, 2); /* erase */
|
||||
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90); /* write */
|
||||
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 6); /* attach */
|
||||
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2); /* reload */
|
||||
}
|
||||
|
||||
static void
|
||||
fu_usi_dock_mcu_device_init(FuUsiDockMcuDevice *self)
|
||||
{
|
||||
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE);
|
||||
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC);
|
||||
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE);
|
||||
|
||||
fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_NO_SERIAL_NUMBER);
|
||||
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_REPLUG_MATCH_GUID);
|
||||
|
||||
fu_device_register_private_flag(FU_DEVICE(self),
|
||||
FU_USI_DOCK_DEVICE_FLAG_VERFMT_HP,
|
||||
"verfmt-hp");
|
||||
fu_hid_device_add_flag(FU_HID_DEVICE(self), FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER);
|
||||
fu_device_add_protocol(FU_DEVICE(self), "com.usi.dock");
|
||||
fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER);
|
||||
fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE);
|
||||
fu_device_retry_set_delay(FU_DEVICE(self), 1000);
|
||||
fu_device_add_icon(FU_DEVICE(self), "dock");
|
||||
}
|
||||
|
||||
static void
|
||||
fu_usi_dock_mcu_device_class_init(FuUsiDockMcuDeviceClass *klass)
|
||||
{
|
||||
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
||||
|
||||
klass_device->write_firmware = fu_usi_dock_mcu_device_write_firmware;
|
||||
klass_device->attach = fu_usi_dock_mcu_device_attach;
|
||||
klass_device->setup = fu_usi_dock_mcu_device_setup;
|
||||
klass_device->set_progress = fu_usi_dock_mcu_device_set_progress;
|
||||
}
|
23
plugins/usi-dock/fu-usi-dock-mcu-device.h
Normal file
23
plugins/usi-dock/fu-usi-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_USI_DOCK_MCU_DEVICE (fu_usi_dock_mcu_device_get_type())
|
||||
G_DECLARE_FINAL_TYPE(FuUsiDockMcuDevice,
|
||||
fu_usi_dock_mcu_device,
|
||||
FU,
|
||||
USI_DOCK_MCU_DEVICE,
|
||||
FuHidDevice)
|
||||
gboolean
|
||||
fu_usi_dock_mcu_device_write_firmware_with_idx(FuUsiDockMcuDevice *self,
|
||||
FuFirmware *firmware,
|
||||
guint8 chip_idx,
|
||||
FuProgress *progress,
|
||||
FwupdInstallFlags flags,
|
||||
GError **error);
|
33
plugins/usi-dock/meson.build
Normal file
33
plugins/usi-dock/meson.build
Normal file
@ -0,0 +1,33 @@
|
||||
if get_option('gusb')
|
||||
cargs = ['-DG_LOG_DOMAIN="FuPluginUsiDock"']
|
||||
|
||||
install_data(['usi-dock.quirk'],
|
||||
install_dir: join_paths(datadir, 'fwupd', 'quirks.d')
|
||||
)
|
||||
|
||||
shared_module('fu_plugin_usi_dock',
|
||||
fu_hash,
|
||||
sources : [
|
||||
'fu-usi-dock-common.c',
|
||||
'fu-usi-dock-child-device.c',
|
||||
'fu-usi-dock-dmc-device.c',
|
||||
'fu-usi-dock-mcu-device.c',
|
||||
'fu-plugin-usi-dock.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
|
15
plugins/usi-dock/usi-dock.quirk
Normal file
15
plugins/usi-dock/usi-dock.quirk
Normal file
@ -0,0 +1,15 @@
|
||||
[USB\VID_17EF&PID_30B4]
|
||||
Plugin = usi_dock
|
||||
GType = FuUsiDockMcuDevice
|
||||
Name = ThinkPad Thunderbolt 4 Dock
|
||||
|
||||
[USB\VID_17EF&PID_30B5]
|
||||
Plugin = usi_dock
|
||||
GType = FuUsiDockDmcDevice
|
||||
ParentGuid = USB\VID_17EF&PID_30B4
|
||||
|
||||
[USB\VID_03F0&PID_0505]
|
||||
Plugin = usi_dock
|
||||
GType = FuUsiDockMcuDevice
|
||||
Name = USB-C G2 Multiport Hub MCU Controller
|
||||
Flags = verfmt-hp
|
Loading…
Reference in New Issue
Block a user