mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-14 09:14:40 +00:00
wistron-dock: Add a new plugin to update several dock models
This commit is contained in:
parent
3cbfbe1e09
commit
47f537e005
28
data/device-tests/wistron-dock-40b7.json
Normal file
28
data/device-tests/wistron-dock-40b7.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "Wistron Dock 40B7",
|
||||||
|
"interactive": true,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"url": "https://fwupd.org/downloads/f00838f10d6faf58ff57cbfcfca390ed63bd804e4c654f19ff89423b286dc5d2-lda_viking_r_composite.cab",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"version": "1.0.1.4",
|
||||||
|
"guids": [
|
||||||
|
"78d3f11b-6ad9-5739-a650-2f0d2955a710"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://fwupd.org/downloads/43bcfb3278ad2baf918af6abc972e0de9a0de66a25b012177b00d96664819010-lda_viking_r_composite.cab",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"version": "1.0.1.6",
|
||||||
|
"guids": [
|
||||||
|
"78d3f11b-6ad9-5739-a650-2f0d2955a710"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -108,3 +108,4 @@ subdir('vbe')
|
|||||||
subdir('vli')
|
subdir('vli')
|
||||||
subdir('wacom-raw')
|
subdir('wacom-raw')
|
||||||
subdir('wacom-usb')
|
subdir('wacom-usb')
|
||||||
|
subdir('wistron-dock')
|
||||||
|
45
plugins/wistron-dock/README.md
Normal file
45
plugins/wistron-dock/README.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Wistron Dock
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Wistron use a generic flashing protocol for dock devices supplied to various OEMs. The protocol is
|
||||||
|
compatible with designs that use Nuvoton, Infineon, and GD MCUs.
|
||||||
|
|
||||||
|
## Firmware Format
|
||||||
|
|
||||||
|
The daemon will decompress the cabinet archive and extract a firmware blob in a zipped file format.
|
||||||
|
The archive must contain exactly one file with each of these extensions:
|
||||||
|
|
||||||
|
* `.wdfl.sig`
|
||||||
|
* `.wdfl`
|
||||||
|
* `.bin`
|
||||||
|
|
||||||
|
This plugin supports the following protocol ID:
|
||||||
|
|
||||||
|
* com.wistron.dock
|
||||||
|
|
||||||
|
## GUID Generation
|
||||||
|
|
||||||
|
These devices use the standard USB DeviceInstanceId values, e.g.
|
||||||
|
|
||||||
|
* `USB\VID_0FB8&PID_0010&REV_0001`
|
||||||
|
* `USB\VID_0FB8&PID_0010`
|
||||||
|
* `USB\VID_0FB8`
|
||||||
|
|
||||||
|
Devices also have additional instance IDs which corresponds MCU type, e.g.
|
||||||
|
|
||||||
|
* `USB\VID_0FB8&PID_0010&MCUID_M4521`
|
||||||
|
|
||||||
|
## Update Behavior
|
||||||
|
|
||||||
|
The device enters DFU mode, then writes the fixed-size WDFL signature and WDFL data, then writes
|
||||||
|
blocks of variable sized data. Finally it clears DFU mode and the user can re-plug the USB-C cable
|
||||||
|
to trigger the update.
|
||||||
|
|
||||||
|
## Vendor ID Security
|
||||||
|
|
||||||
|
The vendor ID is set from the USB vendor, in this example set to `USB:0x0FB8`
|
||||||
|
|
||||||
|
## External Interface Access
|
||||||
|
|
||||||
|
This plugin requires read/write access to `/dev/bus/usb`.
|
53
plugins/wistron-dock/fu-wistron-dock-common.c
Normal file
53
plugins/wistron-dock/fu-wistron-dock-common.c
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "fu-wistron-dock-common.h"
|
||||||
|
|
||||||
|
const gchar *
|
||||||
|
fu_wistron_dock_update_phase_to_string(guint8 update_phase)
|
||||||
|
{
|
||||||
|
if (update_phase == FU_WISTRON_DOCK_UPDATE_PHASE_DOWNLOAD)
|
||||||
|
return "download";
|
||||||
|
if (update_phase == FU_WISTRON_DOCK_UPDATE_PHASE_DEPLOY)
|
||||||
|
return "deploy";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gchar *
|
||||||
|
fu_wistron_dock_component_idx_to_string(guint8 component_idx)
|
||||||
|
{
|
||||||
|
if (component_idx == FU_WISTRON_DOCK_COMPONENT_IDX_MCU)
|
||||||
|
return "mcu";
|
||||||
|
if (component_idx == FU_WISTRON_DOCK_COMPONENT_IDX_PD)
|
||||||
|
return "pd";
|
||||||
|
if (component_idx == FU_WISTRON_DOCK_COMPONENT_IDX_AUDIO)
|
||||||
|
return "audio";
|
||||||
|
if (component_idx == FU_WISTRON_DOCK_COMPONENT_IDX_USB)
|
||||||
|
return "usb";
|
||||||
|
if (component_idx == FU_WISTRON_DOCK_COMPONENT_IDX_MST)
|
||||||
|
return "mst";
|
||||||
|
if (component_idx == FU_WISTRON_DOCK_COMPONENT_IDX_SPI)
|
||||||
|
return "spi";
|
||||||
|
if (component_idx == FU_WISTRON_DOCK_COMPONENT_IDX_DOCK)
|
||||||
|
return "dock";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gchar *
|
||||||
|
fu_wistron_dock_status_code_to_string(guint8 status_code)
|
||||||
|
{
|
||||||
|
if (status_code == FU_WISTRON_DOCK_STATUS_CODE_ENTER)
|
||||||
|
return "enter";
|
||||||
|
if (status_code == FU_WISTRON_DOCK_STATUS_CODE_PREPARE)
|
||||||
|
return "prepare";
|
||||||
|
if (status_code == FU_WISTRON_DOCK_STATUS_CODE_UPDATING)
|
||||||
|
return "updating";
|
||||||
|
if (status_code == FU_WISTRON_DOCK_STATUS_CODE_COMPLETE)
|
||||||
|
return "complete";
|
||||||
|
return NULL;
|
||||||
|
}
|
64
plugins/wistron-dock/fu-wistron-dock-common.h
Normal file
64
plugins/wistron-dock/fu-wistron-dock-common.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#define FU_WISTRON_DOCK_CMD_ICP_ENTER 0x81
|
||||||
|
#define FU_WISTRON_DOCK_CMD_ICP_EXIT 0x82
|
||||||
|
#define FU_WISTRON_DOCK_CMD_ICP_ADDRESS 0x84
|
||||||
|
#define FU_WISTRON_DOCK_CMD_ICP_READBLOCK 0x85
|
||||||
|
#define FU_WISTRON_DOCK_CMD_ICP_WRITEBLOCK 0x86
|
||||||
|
#define FU_WISTRON_DOCK_CMD_ICP_MCUID 0x87
|
||||||
|
#define FU_WISTRON_DOCK_CMD_ICP_BBINFO 0x88 /* bb code information */
|
||||||
|
#define FU_WISTRON_DOCK_CMD_ICP_USERINFO 0x89 /* user code information */
|
||||||
|
#define FU_WISTRON_DOCK_CMD_ICP_DONE 0x5A
|
||||||
|
#define FU_WISTRON_DOCK_CMD_ICP_ERROR 0xFF
|
||||||
|
#define FU_WISTRON_DOCK_CMD_ICP_EXIT_WDRESET 0x01 /* exit ICP with watch dog reset */
|
||||||
|
|
||||||
|
#define FU_WISTRON_DOCK_CMD_DFU_ENTER 0x91
|
||||||
|
#define FU_WISTRON_DOCK_CMD_DFU_EXIT 0x92
|
||||||
|
#define FU_WISTRON_DOCK_CMD_DFU_ADDRESS 0x93
|
||||||
|
#define FU_WISTRON_DOCK_CMD_DFU_READIMG_BLOCK 0x94
|
||||||
|
#define FU_WISTRON_DOCK_CMD_DFU_WRITEIMG_BLOCK 0x95
|
||||||
|
#define FU_WISTRON_DOCK_CMD_DFU_VERIFY 0x96
|
||||||
|
#define FU_WISTRON_DOCK_CMD_DFU_COMPOSITE_VER 0x97
|
||||||
|
#define FU_WISTRON_DOCK_CMD_DFU_WRITE_WDFL_SIG 0x98
|
||||||
|
#define FU_WISTRON_DOCK_CMD_DFU_WRITE_WDFL_DATA 0x99
|
||||||
|
#define FU_WISTRON_DOCK_CMD_DFU_VERIFY_WDFL 0x9A
|
||||||
|
#define FU_WISTRON_DOCK_CMD_DFU_SERINAL_NUMBER 0x9B
|
||||||
|
#define FU_WISTRON_DOCK_CMD_DFU_DONE 0x5A
|
||||||
|
#define FU_WISTRON_DOCK_CMD_DFU_ERROR 0xFF
|
||||||
|
|
||||||
|
#define FU_WISTRON_DOCK_COMPONENT_IDX_MCU 0x0
|
||||||
|
#define FU_WISTRON_DOCK_COMPONENT_IDX_PD 0x1
|
||||||
|
#define FU_WISTRON_DOCK_COMPONENT_IDX_AUDIO 0x2
|
||||||
|
#define FU_WISTRON_DOCK_COMPONENT_IDX_USB 0x3
|
||||||
|
#define FU_WISTRON_DOCK_COMPONENT_IDX_MST 0x4
|
||||||
|
#define FU_WISTRON_DOCK_COMPONENT_IDX_SPI 0xA
|
||||||
|
#define FU_WISTRON_DOCK_COMPONENT_IDX_DOCK 0xF
|
||||||
|
|
||||||
|
#define FU_WISTRON_DOCK_UPDATE_PHASE_DOWNLOAD 0x1
|
||||||
|
#define FU_WISTRON_DOCK_UPDATE_PHASE_DEPLOY 0x2
|
||||||
|
|
||||||
|
#define FU_WISTRON_DOCK_STATUS_CODE_ENTER 0x01
|
||||||
|
#define FU_WISTRON_DOCK_STATUS_CODE_PREPARE 0x02
|
||||||
|
#define FU_WISTRON_DOCK_STATUS_CODE_UPDATING 0x03
|
||||||
|
#define FU_WISTRON_DOCK_STATUS_CODE_COMPLETE 0x04 /* unplug cable to trigger update */
|
||||||
|
|
||||||
|
#define FU_WISTRON_DOCK_WDIT_SIZE 512 /* bytes */
|
||||||
|
#define FU_WISTRON_DOCK_WDIT_TAG_ID 0x4954 /* 'IT' */
|
||||||
|
|
||||||
|
#define FU_WISTRON_DOCK_WDFL_SIG_SIZE 256 /* bytes */
|
||||||
|
#define FU_WISTRON_DOCK_WDFL_DATA_SIZE 1328 /* bytes */
|
||||||
|
|
||||||
|
const gchar *
|
||||||
|
fu_wistron_dock_component_idx_to_string(guint8 component_idx);
|
||||||
|
const gchar *
|
||||||
|
fu_wistron_dock_update_phase_to_string(guint8 update_phase);
|
||||||
|
const gchar *
|
||||||
|
fu_wistron_dock_status_code_to_string(guint8 status_code);
|
965
plugins/wistron-dock/fu-wistron-dock-device.c
Normal file
965
plugins/wistron-dock/fu-wistron-dock-device.c
Normal file
@ -0,0 +1,965 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||||
|
* Copyright (C) 2022 Wistron <Felix_F_Chen@wistron.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "fu-wistron-dock-common.h"
|
||||||
|
#include "fu-wistron-dock-device.h"
|
||||||
|
|
||||||
|
struct _FuWistronDockDevice {
|
||||||
|
FuHidDevice parent_instance;
|
||||||
|
guint8 component_idx;
|
||||||
|
guint8 update_phase;
|
||||||
|
guint8 status_code;
|
||||||
|
guint8 imgmode;
|
||||||
|
gchar *icp_bbinfo;
|
||||||
|
gchar *icp_userinfo;
|
||||||
|
guint device_insert_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(FuWistronDockDevice, fu_wistron_dock_device, FU_TYPE_HID_DEVICE)
|
||||||
|
|
||||||
|
#define FU_WISTRON_DOCK_TRANSFER_BLOCK_SIZE 512 /* bytes */
|
||||||
|
#define FU_WISTRON_DOCK_TRANSFER_TIMEOUT 5000 /* ms */
|
||||||
|
#define FU_WISTRON_DOCK_TRANSFER_RETRY_COUNT 5
|
||||||
|
#define FU_WISTRON_DOCK_TRANSFER_RETRY_DELAY 100 /* ms */
|
||||||
|
|
||||||
|
#define FU_WISTRON_DOCK_ID_USB_CONTROL 0x06 /* 7 bytes */
|
||||||
|
#define FU_WISTRON_DOCK_ID_USB_BLOCK 0x07 /* 512 bytes */
|
||||||
|
#define FU_WISTRON_DOCK_ID_IMG_CONTROL 0x16 /* 7 bytes */
|
||||||
|
#define FU_WISTRON_DOCK_ID_DOCK_IMG_DATA 0x17 /* 512 bytes */
|
||||||
|
#define FU_WISTRON_DOCK_ID_DOCK_WDIT 0x20 /* 512 bytes */
|
||||||
|
#define FU_WISTRON_DOCK_ID_DOCK_WDFL_SIG 0x21 /* 256 bytes */
|
||||||
|
#define FU_WISTRON_DOCK_ID_DOCK_WDFL_DATA 0x22 /* 1440 bytes */
|
||||||
|
#define FU_WISTRON_DOCK_ID_DOCK_SN 0x23 /* 32 bytes */
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_wistron_dock_device_to_string(FuDevice *device, guint idt, GString *str)
|
||||||
|
{
|
||||||
|
FuWistronDockDevice *self = FU_WISTRON_DOCK_DEVICE(device);
|
||||||
|
|
||||||
|
/* FuHidDevice->to_string */
|
||||||
|
FU_DEVICE_CLASS(fu_wistron_dock_device_parent_class)->to_string(device, idt, str);
|
||||||
|
|
||||||
|
fu_string_append(str,
|
||||||
|
idt,
|
||||||
|
"ComponentIdx",
|
||||||
|
fu_wistron_dock_component_idx_to_string(self->component_idx));
|
||||||
|
fu_string_append(str,
|
||||||
|
idt,
|
||||||
|
"UpdatePhase",
|
||||||
|
fu_wistron_dock_update_phase_to_string(self->update_phase));
|
||||||
|
fu_string_append(str,
|
||||||
|
idt,
|
||||||
|
"StatusCode",
|
||||||
|
fu_wistron_dock_status_code_to_string(self->status_code));
|
||||||
|
fu_string_append_kx(str, idt, "ImgMode", self->imgmode);
|
||||||
|
if (self->icp_bbinfo != NULL)
|
||||||
|
fu_string_append(str, idt, "IcpBbInfo", self->icp_bbinfo);
|
||||||
|
if (self->icp_userinfo != NULL)
|
||||||
|
fu_string_append(str, idt, "IcpUserInfo", self->icp_userinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
guint8 *cmd;
|
||||||
|
gsize cmdsz;
|
||||||
|
guint8 *buf;
|
||||||
|
gsize bufsz;
|
||||||
|
gboolean check_result;
|
||||||
|
} FuWistronDockHelper;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_control_cb(FuDevice *device, gpointer user_data, GError **error)
|
||||||
|
{
|
||||||
|
FuWistronDockHelper *helper = (FuWistronDockHelper *)user_data;
|
||||||
|
if (!fu_hid_device_set_report(FU_HID_DEVICE(device),
|
||||||
|
helper->cmd[0], /* value */
|
||||||
|
helper->cmd,
|
||||||
|
helper->cmdsz,
|
||||||
|
FU_WISTRON_DOCK_TRANSFER_TIMEOUT,
|
||||||
|
FU_HID_DEVICE_FLAG_IS_FEATURE,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (helper->check_result) {
|
||||||
|
if (!fu_hid_device_get_report(FU_HID_DEVICE(device),
|
||||||
|
helper->buf[0], /* value */
|
||||||
|
helper->buf,
|
||||||
|
helper->bufsz,
|
||||||
|
FU_WISTRON_DOCK_TRANSFER_TIMEOUT,
|
||||||
|
FU_HID_DEVICE_FLAG_IS_FEATURE,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (helper->buf[7] != FU_WISTRON_DOCK_CMD_ICP_DONE) {
|
||||||
|
g_set_error(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"not icp-done, got 0x%02x",
|
||||||
|
helper->cmd[7]);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_control_write(FuWistronDockDevice *self,
|
||||||
|
guint8 *cmd,
|
||||||
|
gsize cmdsz,
|
||||||
|
gboolean check_result,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuWistronDockHelper helper = {.cmd = cmd,
|
||||||
|
.cmdsz = cmdsz,
|
||||||
|
.buf = cmd,
|
||||||
|
.bufsz = cmdsz,
|
||||||
|
.check_result = check_result};
|
||||||
|
return fu_device_retry_full(FU_DEVICE(self),
|
||||||
|
fu_wistron_dock_device_control_cb,
|
||||||
|
FU_WISTRON_DOCK_TRANSFER_RETRY_COUNT,
|
||||||
|
FU_WISTRON_DOCK_TRANSFER_RETRY_DELAY,
|
||||||
|
&helper,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_control_read(FuWistronDockDevice *self,
|
||||||
|
guint8 *cmd,
|
||||||
|
gsize cmdsz,
|
||||||
|
guint8 *buf,
|
||||||
|
gsize bufsz,
|
||||||
|
gboolean check_result,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuWistronDockHelper helper = {.cmd = cmd,
|
||||||
|
.cmdsz = cmdsz,
|
||||||
|
.buf = buf,
|
||||||
|
.bufsz = bufsz,
|
||||||
|
.check_result = check_result};
|
||||||
|
return fu_device_retry_full(FU_DEVICE(self),
|
||||||
|
fu_wistron_dock_device_control_cb,
|
||||||
|
FU_WISTRON_DOCK_TRANSFER_RETRY_COUNT,
|
||||||
|
FU_WISTRON_DOCK_TRANSFER_RETRY_DELAY,
|
||||||
|
&helper,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_data_write_cb(FuDevice *device, gpointer user_data, GError **error)
|
||||||
|
{
|
||||||
|
FuWistronDockHelper *helper = (FuWistronDockHelper *)user_data;
|
||||||
|
if (!fu_hid_device_set_report(FU_HID_DEVICE(device),
|
||||||
|
helper->cmd[0], /* value */
|
||||||
|
helper->cmd,
|
||||||
|
helper->cmdsz,
|
||||||
|
FU_WISTRON_DOCK_TRANSFER_TIMEOUT,
|
||||||
|
FU_HID_DEVICE_FLAG_IS_FEATURE,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_hid_device_set_report(FU_HID_DEVICE(device),
|
||||||
|
helper->buf[0], /* value */
|
||||||
|
helper->buf,
|
||||||
|
helper->bufsz,
|
||||||
|
FU_WISTRON_DOCK_TRANSFER_TIMEOUT,
|
||||||
|
FU_HID_DEVICE_FLAG_IS_FEATURE,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_hid_device_get_report(FU_HID_DEVICE(device),
|
||||||
|
helper->cmd[0], /* value */
|
||||||
|
helper->cmd,
|
||||||
|
helper->cmdsz,
|
||||||
|
FU_WISTRON_DOCK_TRANSFER_TIMEOUT,
|
||||||
|
FU_HID_DEVICE_FLAG_IS_FEATURE,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (helper->cmd[7] != FU_WISTRON_DOCK_CMD_ICP_DONE) {
|
||||||
|
g_set_error(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"not icp-done, got 0x%02x",
|
||||||
|
helper->cmd[7]);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_data_write(FuWistronDockDevice *self,
|
||||||
|
guint8 *cmd,
|
||||||
|
gsize cmdsz,
|
||||||
|
guint8 *buf,
|
||||||
|
gsize bufsz,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuWistronDockHelper helper = {.cmd = cmd, .cmdsz = cmdsz, .buf = buf, .bufsz = bufsz};
|
||||||
|
return fu_device_retry_full(FU_DEVICE(self),
|
||||||
|
fu_wistron_dock_device_data_write_cb,
|
||||||
|
FU_WISTRON_DOCK_TRANSFER_RETRY_COUNT,
|
||||||
|
FU_WISTRON_DOCK_TRANSFER_RETRY_DELAY,
|
||||||
|
&helper,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_write_wdfl_sig(FuWistronDockDevice *self,
|
||||||
|
const guint8 *buf,
|
||||||
|
gsize bufsz,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
guint8 cmd[8] = {FU_WISTRON_DOCK_ID_IMG_CONTROL, FU_WISTRON_DOCK_CMD_DFU_WRITE_WDFL_SIG};
|
||||||
|
guint8 towrite[FU_WISTRON_DOCK_WDFL_SIG_SIZE + 1] = {FU_WISTRON_DOCK_ID_DOCK_WDFL_SIG};
|
||||||
|
|
||||||
|
if (!fu_memcpy_safe(towrite,
|
||||||
|
sizeof(towrite),
|
||||||
|
0x1, /* dst */
|
||||||
|
buf,
|
||||||
|
bufsz,
|
||||||
|
0x0, /* src */
|
||||||
|
bufsz,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
return fu_wistron_dock_device_data_write(self,
|
||||||
|
cmd,
|
||||||
|
sizeof(cmd),
|
||||||
|
towrite,
|
||||||
|
sizeof(towrite),
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_write_wdfl_data(FuWistronDockDevice *self,
|
||||||
|
const guint8 *buf,
|
||||||
|
gsize bufsz,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
guint8 cmd[8] = {FU_WISTRON_DOCK_ID_IMG_CONTROL, FU_WISTRON_DOCK_CMD_DFU_WRITE_WDFL_DATA};
|
||||||
|
guint8 towrite[FU_WISTRON_DOCK_WDFL_DATA_SIZE + 1] = {FU_WISTRON_DOCK_ID_DOCK_WDFL_DATA};
|
||||||
|
if (!fu_memcpy_safe(towrite,
|
||||||
|
sizeof(towrite),
|
||||||
|
0x1, /* dst */
|
||||||
|
buf,
|
||||||
|
bufsz,
|
||||||
|
0x0, /* src */
|
||||||
|
bufsz,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
return fu_wistron_dock_device_data_write(self,
|
||||||
|
cmd,
|
||||||
|
sizeof(cmd),
|
||||||
|
towrite,
|
||||||
|
sizeof(towrite),
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_set_img_address(FuWistronDockDevice *self, guint32 addr, GError **error)
|
||||||
|
{
|
||||||
|
guint8 cmd[8] = {FU_WISTRON_DOCK_ID_IMG_CONTROL, FU_WISTRON_DOCK_CMD_DFU_ADDRESS};
|
||||||
|
fu_memwrite_uint32(cmd + 2, addr, G_BIG_ENDIAN);
|
||||||
|
return fu_wistron_dock_device_control_write(self, cmd, sizeof(cmd), TRUE, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_write_img_data(FuWistronDockDevice *self,
|
||||||
|
const guint8 *buf,
|
||||||
|
gsize bufsz,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
guint8 cmd[8] = {FU_WISTRON_DOCK_ID_IMG_CONTROL, FU_WISTRON_DOCK_CMD_DFU_WRITEIMG_BLOCK};
|
||||||
|
guint8 towrite[FU_WISTRON_DOCK_TRANSFER_BLOCK_SIZE + 1] = {
|
||||||
|
FU_WISTRON_DOCK_ID_DOCK_IMG_DATA};
|
||||||
|
if (!fu_memcpy_safe(towrite,
|
||||||
|
sizeof(towrite),
|
||||||
|
0x1, /* dst */
|
||||||
|
buf,
|
||||||
|
bufsz,
|
||||||
|
0x0, /* src */
|
||||||
|
bufsz,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
return fu_wistron_dock_device_data_write(self,
|
||||||
|
cmd,
|
||||||
|
sizeof(cmd),
|
||||||
|
towrite,
|
||||||
|
sizeof(towrite),
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_write_blocks(FuWistronDockDevice *self,
|
||||||
|
GPtrArray *chunks,
|
||||||
|
FuProgress *progress,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
/* progress */
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* set address */
|
||||||
|
if (!fu_wistron_dock_device_set_img_address(self,
|
||||||
|
fu_chunk_get_address(chk),
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error,
|
||||||
|
"failed to set img address 0x%x",
|
||||||
|
fu_chunk_get_address(chk));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write */
|
||||||
|
if (!fu_wistron_dock_device_write_img_data(self,
|
||||||
|
fu_chunk_get_data(chk),
|
||||||
|
fu_chunk_get_data_sz(chk),
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error,
|
||||||
|
"failed to write img data 0x%x",
|
||||||
|
fu_chunk_get_address(chk));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update progress */
|
||||||
|
fu_progress_step_done(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FuFirmware *
|
||||||
|
fu_wistron_dock_device_prepare_firmware(FuDevice *device,
|
||||||
|
GBytes *fw,
|
||||||
|
FwupdInstallFlags flags,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(FuFirmware) firmware = fu_archive_firmware_new();
|
||||||
|
g_autoptr(FuFirmware) fw_cbin = NULL;
|
||||||
|
g_autoptr(FuFirmware) fw_new = fu_firmware_new();
|
||||||
|
g_autoptr(FuFirmware) fw_wdfl = NULL;
|
||||||
|
g_autoptr(FuFirmware) fw_wsig = NULL;
|
||||||
|
|
||||||
|
/* unzip and get images */
|
||||||
|
if (!fu_firmware_parse(firmware, fw, flags, error))
|
||||||
|
return NULL;
|
||||||
|
fw_wsig = fu_archive_firmware_get_image_fnmatch(FU_ARCHIVE_FIRMWARE(firmware),
|
||||||
|
"*.wdfl.sig",
|
||||||
|
error);
|
||||||
|
if (fw_wsig == NULL)
|
||||||
|
return NULL;
|
||||||
|
fw_wdfl =
|
||||||
|
fu_archive_firmware_get_image_fnmatch(FU_ARCHIVE_FIRMWARE(firmware), "*.wdfl", error);
|
||||||
|
if (fw_wdfl == NULL)
|
||||||
|
return NULL;
|
||||||
|
fw_cbin =
|
||||||
|
fu_archive_firmware_get_image_fnmatch(FU_ARCHIVE_FIRMWARE(firmware), "*.bin", error);
|
||||||
|
if (fw_cbin == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* sanity check sizes */
|
||||||
|
if (fu_firmware_get_size(fw_wsig) < FU_WISTRON_DOCK_WDFL_SIG_SIZE) {
|
||||||
|
g_set_error(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"WDFL signature size invalid, got 0x%x, expected >= 0x%x",
|
||||||
|
(guint)fu_firmware_get_size(fw_wsig),
|
||||||
|
(guint)FU_WISTRON_DOCK_WDFL_SIG_SIZE);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (fu_firmware_get_size(fw_wdfl) != FU_WISTRON_DOCK_WDFL_DATA_SIZE) {
|
||||||
|
g_set_error(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"WDFL size invalid, got 0x%x, expected 0x%x",
|
||||||
|
(guint)fu_firmware_get_size(fw_wdfl),
|
||||||
|
(guint)FU_WISTRON_DOCK_WDFL_DATA_SIZE);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
fu_firmware_set_id(fw_wsig, FU_FIRMWARE_ID_SIGNATURE);
|
||||||
|
fu_firmware_add_image(fw_new, fw_wsig);
|
||||||
|
fu_firmware_set_id(fw_wdfl, FU_FIRMWARE_ID_HEADER);
|
||||||
|
fu_firmware_add_image(fw_new, fw_wdfl);
|
||||||
|
fu_firmware_set_id(fw_cbin, FU_FIRMWARE_ID_PAYLOAD);
|
||||||
|
fu_firmware_add_image(fw_new, fw_cbin);
|
||||||
|
return g_steal_pointer(&fw_new);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_write_firmware(FuDevice *device,
|
||||||
|
FuFirmware *firmware,
|
||||||
|
FuProgress *progress,
|
||||||
|
FwupdInstallFlags flags,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuWistronDockDevice *self = FU_WISTRON_DOCK_DEVICE(device);
|
||||||
|
g_autoptr(GBytes) fw_cbin = NULL;
|
||||||
|
g_autoptr(GBytes) fw_wdfl = NULL;
|
||||||
|
g_autoptr(GBytes) fw_wsig = NULL;
|
||||||
|
g_autoptr(GPtrArray) chunks = NULL;
|
||||||
|
|
||||||
|
/* progress */
|
||||||
|
fu_progress_set_id(progress, G_STRLOC);
|
||||||
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "write-wdfl-signature");
|
||||||
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "write-wdfl-data");
|
||||||
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, "write-payload");
|
||||||
|
|
||||||
|
/* write WDFL signature */
|
||||||
|
fw_wsig = fu_firmware_get_image_by_id_bytes(firmware, FU_FIRMWARE_ID_SIGNATURE, error);
|
||||||
|
if (fw_wsig == NULL)
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_wistron_dock_device_write_wdfl_sig(self,
|
||||||
|
g_bytes_get_data(fw_wsig, NULL),
|
||||||
|
g_bytes_get_size(fw_wsig),
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to write WDFL signature: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
fu_progress_step_done(progress);
|
||||||
|
|
||||||
|
/* write WDFL data */
|
||||||
|
fw_wdfl = fu_firmware_get_image_by_id_bytes(firmware, FU_FIRMWARE_ID_HEADER, error);
|
||||||
|
if (fw_wdfl == NULL)
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_wistron_dock_device_write_wdfl_data(self,
|
||||||
|
g_bytes_get_data(fw_wdfl, NULL),
|
||||||
|
g_bytes_get_size(fw_wdfl),
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to write WDFL data: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
fu_progress_step_done(progress);
|
||||||
|
|
||||||
|
/* write each block */
|
||||||
|
fw_cbin = fu_firmware_get_image_by_id_bytes(firmware, FU_FIRMWARE_ID_PAYLOAD, error);
|
||||||
|
if (fw_cbin == NULL)
|
||||||
|
return FALSE;
|
||||||
|
chunks = fu_chunk_array_new_from_bytes(fw_cbin,
|
||||||
|
0x0,
|
||||||
|
0x0, /* page_sz */
|
||||||
|
FU_WISTRON_DOCK_TRANSFER_BLOCK_SIZE);
|
||||||
|
if (!fu_wistron_dock_device_write_blocks(self,
|
||||||
|
chunks,
|
||||||
|
fu_progress_get_child(progress),
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to write payload: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
fu_progress_step_done(progress);
|
||||||
|
|
||||||
|
/* success! */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_ensure_mcuid(FuWistronDockDevice *self, GError **error)
|
||||||
|
{
|
||||||
|
guint8 cmd[8] = {FU_WISTRON_DOCK_ID_USB_CONTROL, FU_WISTRON_DOCK_CMD_ICP_MCUID};
|
||||||
|
guint8 buf[8] = {FU_WISTRON_DOCK_ID_USB_CONTROL};
|
||||||
|
g_autofree gchar *tmp = NULL;
|
||||||
|
|
||||||
|
if (!fu_wistron_dock_device_control_read(self,
|
||||||
|
cmd,
|
||||||
|
sizeof(cmd),
|
||||||
|
buf,
|
||||||
|
sizeof(buf),
|
||||||
|
TRUE,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
tmp = fu_strsafe((const gchar *)buf + 2, 5);
|
||||||
|
fu_device_add_instance_str(FU_DEVICE(self), "MCUID", tmp);
|
||||||
|
return fu_device_build_instance_id(FU_DEVICE(self),
|
||||||
|
error,
|
||||||
|
"USB",
|
||||||
|
"VID",
|
||||||
|
"PID",
|
||||||
|
"MCUID",
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_ensure_bbinfo(FuWistronDockDevice *self, GError **error)
|
||||||
|
{
|
||||||
|
guint8 cmd[8] = {FU_WISTRON_DOCK_ID_USB_CONTROL, FU_WISTRON_DOCK_CMD_ICP_BBINFO};
|
||||||
|
guint8 buf[8] = {FU_WISTRON_DOCK_ID_USB_CONTROL};
|
||||||
|
|
||||||
|
if (!fu_wistron_dock_device_control_read(self,
|
||||||
|
cmd,
|
||||||
|
sizeof(cmd),
|
||||||
|
buf,
|
||||||
|
sizeof(buf),
|
||||||
|
TRUE,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
g_free(self->icp_bbinfo);
|
||||||
|
self->icp_bbinfo = g_strdup_printf("%u.%u.%u", buf[2], buf[3], buf[4]);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_ensure_userinfo(FuWistronDockDevice *self, GError **error)
|
||||||
|
{
|
||||||
|
guint8 cmd[8] = {FU_WISTRON_DOCK_ID_USB_CONTROL, FU_WISTRON_DOCK_CMD_ICP_USERINFO};
|
||||||
|
guint8 buf[8] = {FU_WISTRON_DOCK_ID_USB_CONTROL};
|
||||||
|
|
||||||
|
if (!fu_wistron_dock_device_control_read(self,
|
||||||
|
cmd,
|
||||||
|
sizeof(cmd),
|
||||||
|
buf,
|
||||||
|
sizeof(buf),
|
||||||
|
TRUE,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
g_free(self->icp_userinfo);
|
||||||
|
self->icp_userinfo = g_strdup_printf("%u.%u.%u", buf[2], buf[3], buf[4]);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
guint8 hid_id;
|
||||||
|
gchar tag_id[2];
|
||||||
|
guint16 vid;
|
||||||
|
guint16 pid;
|
||||||
|
guint8 imgmode;
|
||||||
|
guint8 update_state;
|
||||||
|
guint8 status_code;
|
||||||
|
guint32 composite_version;
|
||||||
|
guint8 device_cnt;
|
||||||
|
guint8 reserved;
|
||||||
|
} FuWistronDockWdit;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
guint8 comp_id;
|
||||||
|
guint8 mode;
|
||||||
|
guint8 status;
|
||||||
|
guint8 _reserved;
|
||||||
|
guint32 version_build;
|
||||||
|
guint32 version1;
|
||||||
|
guint32 version2;
|
||||||
|
gchar name[32];
|
||||||
|
} FuWistronDockWditImg;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_parse_wdit_img(FuWistronDockDevice *self,
|
||||||
|
const guint8 *buf,
|
||||||
|
gsize bufsz,
|
||||||
|
guint8 device_cnt,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
gsize offset = sizeof(FuWistronDockWdit) + 1;
|
||||||
|
for (guint j = 0; j < device_cnt; j++) {
|
||||||
|
guint32 version_raw = 0;
|
||||||
|
guint8 comp_id = 0;
|
||||||
|
guint8 mode = 0;
|
||||||
|
guint8 status = 0;
|
||||||
|
gchar name_tmp[32] = {0x0};
|
||||||
|
g_autofree gchar *name = NULL;
|
||||||
|
g_autofree gchar *version0 = NULL;
|
||||||
|
g_autofree gchar *version1 = NULL;
|
||||||
|
g_autofree gchar *version2 = NULL;
|
||||||
|
|
||||||
|
/* id */
|
||||||
|
if (!fu_memread_uint8_safe(buf,
|
||||||
|
bufsz,
|
||||||
|
offset + G_STRUCT_OFFSET(FuWistronDockWditImg, comp_id),
|
||||||
|
&comp_id,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* mode: 0=single, 1=dual-s, 2=dual-a */
|
||||||
|
if (!fu_memread_uint8_safe(buf,
|
||||||
|
bufsz,
|
||||||
|
offset + G_STRUCT_OFFSET(FuWistronDockWditImg, mode),
|
||||||
|
&mode,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* status: 0=unknown, 1=valid, 2=invalid */
|
||||||
|
if (!fu_memread_uint8_safe(buf,
|
||||||
|
bufsz,
|
||||||
|
offset + G_STRUCT_OFFSET(FuWistronDockWditImg, status),
|
||||||
|
&status,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* versions */
|
||||||
|
if (!fu_memread_uint32_safe(
|
||||||
|
buf,
|
||||||
|
bufsz,
|
||||||
|
offset + G_STRUCT_OFFSET(FuWistronDockWditImg, version_build),
|
||||||
|
&version_raw,
|
||||||
|
G_BIG_ENDIAN,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (version_raw != 0)
|
||||||
|
version0 = fu_version_from_uint32(version_raw, FWUPD_VERSION_FORMAT_QUAD);
|
||||||
|
if (!fu_memread_uint32_safe(buf,
|
||||||
|
bufsz,
|
||||||
|
offset +
|
||||||
|
G_STRUCT_OFFSET(FuWistronDockWditImg, version1),
|
||||||
|
&version_raw,
|
||||||
|
G_BIG_ENDIAN,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (version_raw != 0)
|
||||||
|
version1 = fu_version_from_uint32(version_raw, FWUPD_VERSION_FORMAT_QUAD);
|
||||||
|
if (!fu_memread_uint32_safe(buf,
|
||||||
|
bufsz,
|
||||||
|
offset +
|
||||||
|
G_STRUCT_OFFSET(FuWistronDockWditImg, version2),
|
||||||
|
&version_raw,
|
||||||
|
G_BIG_ENDIAN,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (version_raw != 0)
|
||||||
|
version2 = fu_version_from_uint32(version_raw, FWUPD_VERSION_FORMAT_QUAD);
|
||||||
|
|
||||||
|
/* name */
|
||||||
|
if (!fu_memcpy_safe((guint8 *)&name_tmp,
|
||||||
|
sizeof(name_tmp),
|
||||||
|
0x0,
|
||||||
|
buf,
|
||||||
|
bufsz,
|
||||||
|
offset + G_STRUCT_OFFSET(FuWistronDockWditImg, name),
|
||||||
|
sizeof(name_tmp),
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
name = fu_strsafe(name_tmp, sizeof(name_tmp));
|
||||||
|
g_debug("%s: bld:%s, img1:%s, img2:%s", name, version0, version1, version2);
|
||||||
|
g_debug(" - comp-id:%u, mode:%u, status:%u/%u",
|
||||||
|
comp_id,
|
||||||
|
mode,
|
||||||
|
(guint)status & 0x0F,
|
||||||
|
(guint)(status & 0xF0) >> 4);
|
||||||
|
|
||||||
|
offset += sizeof(FuWistronDockWditImg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_ensure_wdit(FuWistronDockDevice *self, GError **error)
|
||||||
|
{
|
||||||
|
guint16 tag_id = 0x0;
|
||||||
|
guint16 usb_pid = 0x0;
|
||||||
|
guint16 usb_vid = 0x0;
|
||||||
|
guint32 version_raw = 0;
|
||||||
|
guint8 update_state = 0x0;
|
||||||
|
guint8 buf[FU_WISTRON_DOCK_WDIT_SIZE + 1] = {FU_WISTRON_DOCK_ID_DOCK_WDIT};
|
||||||
|
|
||||||
|
/* get WDIT */
|
||||||
|
if (!fu_hid_device_get_report(FU_HID_DEVICE(self),
|
||||||
|
buf[0], /* value */
|
||||||
|
buf,
|
||||||
|
sizeof(buf),
|
||||||
|
FU_WISTRON_DOCK_TRANSFER_TIMEOUT,
|
||||||
|
FU_HID_DEVICE_FLAG_IS_FEATURE |
|
||||||
|
FU_HID_DEVICE_FLAG_RETRY_FAILURE |
|
||||||
|
FU_HID_DEVICE_FLAG_ALLOW_TRUNC,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_memread_uint16_safe(buf,
|
||||||
|
sizeof(buf),
|
||||||
|
G_STRUCT_OFFSET(FuWistronDockWdit, tag_id),
|
||||||
|
&tag_id,
|
||||||
|
G_BIG_ENDIAN,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (tag_id != FU_WISTRON_DOCK_WDIT_TAG_ID) {
|
||||||
|
g_set_error(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"WDIT tag invalid, expected 0x%x, got 0x%x",
|
||||||
|
(guint)FU_WISTRON_DOCK_WDIT_TAG_ID,
|
||||||
|
tag_id);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify VID & PID */
|
||||||
|
if (!fu_memread_uint16_safe(buf,
|
||||||
|
sizeof(buf),
|
||||||
|
G_STRUCT_OFFSET(FuWistronDockWdit, vid),
|
||||||
|
&usb_vid,
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_memread_uint16_safe(buf,
|
||||||
|
sizeof(buf),
|
||||||
|
G_STRUCT_OFFSET(FuWistronDockWdit, pid),
|
||||||
|
&usb_pid,
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (usb_vid != fu_usb_device_get_vid(FU_USB_DEVICE(self)) ||
|
||||||
|
usb_pid != fu_usb_device_get_pid(FU_USB_DEVICE(self))) {
|
||||||
|
g_set_error(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"USB VID:PID invalid, expected %04X:%04X, got %04X:%04X",
|
||||||
|
(guint)fu_usb_device_get_vid(FU_USB_DEVICE(self)),
|
||||||
|
(guint)fu_usb_device_get_pid(FU_USB_DEVICE(self)),
|
||||||
|
usb_vid,
|
||||||
|
usb_pid);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* image mode */
|
||||||
|
if (!fu_memread_uint8_safe(buf,
|
||||||
|
sizeof(buf),
|
||||||
|
G_STRUCT_OFFSET(FuWistronDockWdit, imgmode),
|
||||||
|
&self->imgmode,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (self->imgmode == 0)
|
||||||
|
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
|
||||||
|
else if (self->imgmode == 1)
|
||||||
|
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD);
|
||||||
|
|
||||||
|
/* update state */
|
||||||
|
if (!fu_memread_uint8_safe(buf,
|
||||||
|
sizeof(buf),
|
||||||
|
G_STRUCT_OFFSET(FuWistronDockWdit, update_state),
|
||||||
|
&update_state,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
self->update_phase = (update_state & 0xF0) >> 4;
|
||||||
|
if (self->update_phase == FU_WISTRON_DOCK_UPDATE_PHASE_DOWNLOAD)
|
||||||
|
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
|
||||||
|
else
|
||||||
|
fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
|
||||||
|
if (fu_wistron_dock_update_phase_to_string(self->update_phase) == NULL)
|
||||||
|
g_warning("unknown update_phase 0x%02x", self->update_phase);
|
||||||
|
self->component_idx = update_state & 0xF;
|
||||||
|
if (fu_wistron_dock_component_idx_to_string(self->component_idx) == NULL)
|
||||||
|
g_warning("unknown component_idx 0x%02x", self->component_idx);
|
||||||
|
|
||||||
|
/* status code */
|
||||||
|
if (!fu_memread_uint8_safe(buf,
|
||||||
|
sizeof(buf),
|
||||||
|
G_STRUCT_OFFSET(FuWistronDockWdit, status_code),
|
||||||
|
&self->status_code,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (fu_wistron_dock_status_code_to_string(self->status_code) == NULL)
|
||||||
|
g_warning("unknown status_code 0x%02x", self->status_code);
|
||||||
|
|
||||||
|
/* composite version */
|
||||||
|
if (!fu_memread_uint32_safe(buf,
|
||||||
|
sizeof(buf),
|
||||||
|
G_STRUCT_OFFSET(FuWistronDockWdit, composite_version),
|
||||||
|
&version_raw,
|
||||||
|
G_BIG_ENDIAN,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
fu_device_set_version_raw(FU_DEVICE(self), version_raw);
|
||||||
|
if (version_raw != 0x0) {
|
||||||
|
g_autofree gchar *version =
|
||||||
|
fu_version_from_uint32(version_raw,
|
||||||
|
fu_device_get_version_format(FU_DEVICE(self)));
|
||||||
|
fu_device_set_version(FU_DEVICE(self), version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for debugging only */
|
||||||
|
if (g_getenv("FWUPD_WISTRON_DOCK_VERBOSE") != NULL) {
|
||||||
|
guint8 device_cnt = 0x0;
|
||||||
|
if (!fu_memread_uint8_safe(buf,
|
||||||
|
sizeof(buf),
|
||||||
|
G_STRUCT_OFFSET(FuWistronDockWdit, device_cnt),
|
||||||
|
&device_cnt,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_wistron_dock_device_parse_wdit_img(self,
|
||||||
|
buf,
|
||||||
|
sizeof(buf),
|
||||||
|
MIN(device_cnt, 32),
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to parse imgs: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* adding the MCU while flashing the device, ignore until it comes back in runtime mode */
|
||||||
|
if (self->update_phase == FU_WISTRON_DOCK_UPDATE_PHASE_DEPLOY &&
|
||||||
|
self->status_code == FU_WISTRON_DOCK_STATUS_CODE_UPDATING) {
|
||||||
|
g_set_error(error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_NOT_SUPPORTED,
|
||||||
|
"ignoring device in MCU mode");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_setup(FuDevice *device, GError **error)
|
||||||
|
{
|
||||||
|
FuWistronDockDevice *self = FU_WISTRON_DOCK_DEVICE(device);
|
||||||
|
|
||||||
|
/* FuUsbDevice->setup */
|
||||||
|
if (!FU_DEVICE_CLASS(fu_wistron_dock_device_parent_class)->setup(device, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!fu_wistron_dock_device_ensure_mcuid(self, error)) {
|
||||||
|
g_prefix_error(error, "failed to get MCUID: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!fu_wistron_dock_device_ensure_bbinfo(self, error)) {
|
||||||
|
g_prefix_error(error, "failed to get BBINFO: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!fu_wistron_dock_device_ensure_userinfo(self, error)) {
|
||||||
|
g_prefix_error(error, "failed to get USERINFO: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!fu_wistron_dock_device_ensure_wdit(self, error)) {
|
||||||
|
g_prefix_error(error, "failed to get WDIT: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_detach(FuDevice *device, FuProgress *progress, GError **error)
|
||||||
|
{
|
||||||
|
FuWistronDockDevice *self = FU_WISTRON_DOCK_DEVICE(device);
|
||||||
|
guint8 cmd[8] = {FU_WISTRON_DOCK_ID_IMG_CONTROL, FU_WISTRON_DOCK_CMD_DFU_ENTER};
|
||||||
|
|
||||||
|
/* sanity check */
|
||||||
|
if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) {
|
||||||
|
g_debug("already in bootloader mode, skipping");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
if (!fu_wistron_dock_device_control_write(self, cmd, sizeof(cmd), FALSE, error))
|
||||||
|
return FALSE;
|
||||||
|
return fu_wistron_dock_device_ensure_wdit(self, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_insert_cb(gpointer user_data)
|
||||||
|
{
|
||||||
|
FuWistronDockDevice *self = FU_WISTRON_DOCK_DEVICE(user_data);
|
||||||
|
g_autoptr(FwupdRequest) request = fwupd_request_new();
|
||||||
|
|
||||||
|
/* interactive request to start the SPI write */
|
||||||
|
fwupd_request_set_kind(request, FWUPD_REQUEST_KIND_IMMEDIATE);
|
||||||
|
fwupd_request_set_id(request, FWUPD_REQUEST_ID_INSERT_USB_CABLE);
|
||||||
|
fwupd_request_add_flag(request, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE);
|
||||||
|
fwupd_request_set_message(
|
||||||
|
request,
|
||||||
|
"The update will continue when the device USB cable has been re-inserted.");
|
||||||
|
fu_device_emit_request(FU_DEVICE(self), request);
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
self->device_insert_id = 0;
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_cleanup(FuDevice *device,
|
||||||
|
FuProgress *progress,
|
||||||
|
FwupdInstallFlags flags,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuWistronDockDevice *self = FU_WISTRON_DOCK_DEVICE(device);
|
||||||
|
|
||||||
|
/* ensure the timeout has been cleared, even on error */
|
||||||
|
if (self->device_insert_id != 0) {
|
||||||
|
g_source_remove(self->device_insert_id);
|
||||||
|
self->device_insert_id = 0;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_wistron_dock_device_attach(FuDevice *device, FuProgress *progress, GError **error)
|
||||||
|
{
|
||||||
|
FuWistronDockDevice *self = FU_WISTRON_DOCK_DEVICE(device);
|
||||||
|
guint8 cmd[8] = {FU_WISTRON_DOCK_ID_IMG_CONTROL, FU_WISTRON_DOCK_CMD_DFU_EXIT};
|
||||||
|
g_autoptr(FwupdRequest) request = fwupd_request_new();
|
||||||
|
|
||||||
|
/* sanity check */
|
||||||
|
if (!fu_wistron_dock_device_ensure_wdit(self, error))
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) {
|
||||||
|
g_debug("already in runtime mode, skipping");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
if (!fu_wistron_dock_device_control_write(self, cmd, sizeof(cmd), FALSE, error))
|
||||||
|
return FALSE;
|
||||||
|
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
|
||||||
|
|
||||||
|
/* the user has to remove the USB cable, wait 15 seconds, then re-insert it */
|
||||||
|
fwupd_request_set_kind(request, FWUPD_REQUEST_KIND_IMMEDIATE);
|
||||||
|
fwupd_request_set_id(request, FWUPD_REQUEST_ID_REMOVE_USB_CABLE);
|
||||||
|
fwupd_request_add_flag(request, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE);
|
||||||
|
fwupd_request_set_message(
|
||||||
|
request,
|
||||||
|
"The update will continue when the device USB cable has been unplugged.");
|
||||||
|
fu_device_emit_request(device, request);
|
||||||
|
fu_progress_step_done(progress);
|
||||||
|
|
||||||
|
/* set a timeout, which will trigger as we're waiting for the device --
|
||||||
|
* no sync sleep is possible as the device will re-enumerate one more time */
|
||||||
|
fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_BUSY);
|
||||||
|
self->device_insert_id = g_timeout_add_seconds(20, fu_wistron_dock_device_insert_cb, self);
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_wistron_dock_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, 20, "write");
|
||||||
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 20, "attach");
|
||||||
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 60, "reload");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_wistron_dock_device_init(FuWistronDockDevice *self)
|
||||||
|
{
|
||||||
|
fu_device_add_protocol(FU_DEVICE(self), "com.wistron.dock");
|
||||||
|
fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD);
|
||||||
|
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE);
|
||||||
|
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE);
|
||||||
|
fu_device_set_remove_delay(FU_DEVICE(self), 5 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_wistron_dock_device_finalize(GObject *object)
|
||||||
|
{
|
||||||
|
FuWistronDockDevice *self = FU_WISTRON_DOCK_DEVICE(object);
|
||||||
|
if (self->device_insert_id != 0)
|
||||||
|
g_source_remove(self->device_insert_id);
|
||||||
|
g_free(self->icp_bbinfo);
|
||||||
|
g_free(self->icp_userinfo);
|
||||||
|
G_OBJECT_CLASS(fu_wistron_dock_device_parent_class)->finalize(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_wistron_dock_device_class_init(FuWistronDockDeviceClass *klass)
|
||||||
|
{
|
||||||
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
||||||
|
object_class->finalize = fu_wistron_dock_device_finalize;
|
||||||
|
klass_device->to_string = fu_wistron_dock_device_to_string;
|
||||||
|
klass_device->prepare_firmware = fu_wistron_dock_device_prepare_firmware;
|
||||||
|
klass_device->write_firmware = fu_wistron_dock_device_write_firmware;
|
||||||
|
klass_device->attach = fu_wistron_dock_device_attach;
|
||||||
|
klass_device->detach = fu_wistron_dock_device_detach;
|
||||||
|
klass_device->setup = fu_wistron_dock_device_setup;
|
||||||
|
klass_device->cleanup = fu_wistron_dock_device_cleanup;
|
||||||
|
klass_device->set_progress = fu_wistron_dock_device_set_progress;
|
||||||
|
}
|
16
plugins/wistron-dock/fu-wistron-dock-device.h
Normal file
16
plugins/wistron-dock/fu-wistron-dock-device.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fwupdplugin.h>
|
||||||
|
|
||||||
|
#define FU_TYPE_WISTRON_DOCK_DEVICE (fu_wistron_dock_device_get_type())
|
||||||
|
G_DECLARE_FINAL_TYPE(FuWistronDockDevice,
|
||||||
|
fu_wistron_dock_device,
|
||||||
|
FU,
|
||||||
|
WISTRON_DOCK_DEVICE,
|
||||||
|
FuHidDevice)
|
35
plugins/wistron-dock/fu-wistron-dock-plugin.c
Normal file
35
plugins/wistron-dock/fu-wistron-dock-plugin.c
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "fu-wistron-dock-device.h"
|
||||||
|
#include "fu-wistron-dock-plugin.h"
|
||||||
|
|
||||||
|
struct _FuWistronDockPlugin {
|
||||||
|
FuPlugin parent_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(FuWistronDockPlugin, fu_wistron_dock_plugin, FU_TYPE_PLUGIN)
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_wistron_dock_plugin_init(FuWistronDockPlugin *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_wistron_dock_plugin_constructed(GObject *obj)
|
||||||
|
{
|
||||||
|
FuPlugin *plugin = FU_PLUGIN(obj);
|
||||||
|
fu_plugin_add_device_gtype(plugin, FU_TYPE_WISTRON_DOCK_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_wistron_dock_plugin_class_init(FuWistronDockPluginClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
||||||
|
object_class->constructed = fu_wistron_dock_plugin_constructed;
|
||||||
|
}
|
11
plugins/wistron-dock/fu-wistron-dock-plugin.h
Normal file
11
plugins/wistron-dock/fu-wistron-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(FuWistronDockPlugin, fu_wistron_dock_plugin, FU, WISTRON_DOCK_PLUGIN, FuPlugin)
|
16
plugins/wistron-dock/meson.build
Normal file
16
plugins/wistron-dock/meson.build
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
if gusb.found()
|
||||||
|
cargs = ['-DG_LOG_DOMAIN="FuPluginWistronDock"']
|
||||||
|
|
||||||
|
plugin_quirks += files('wistron-dock.quirk')
|
||||||
|
plugin_builtins += static_library('fu_plugin_wistron_dock',
|
||||||
|
sources: [
|
||||||
|
'fu-wistron-dock-common.c',
|
||||||
|
'fu-wistron-dock-device.c',
|
||||||
|
'fu-wistron-dock-plugin.c',
|
||||||
|
],
|
||||||
|
include_directories: plugin_incdirs,
|
||||||
|
link_with: plugin_libs,
|
||||||
|
c_args: cargs,
|
||||||
|
dependencies: plugin_deps,
|
||||||
|
)
|
||||||
|
endif
|
7
plugins/wistron-dock/wistron-dock.quirk
Normal file
7
plugins/wistron-dock/wistron-dock.quirk
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Wistron Travel Dock
|
||||||
|
[USB\VID_0FB8&PID_0010]
|
||||||
|
Plugin = wistron_dock
|
||||||
|
|
||||||
|
# Wistron USB-C Dock
|
||||||
|
[USB\VID_17EF&PID_30EF]
|
||||||
|
Plugin = wistron_dock
|
Loading…
Reference in New Issue
Block a user