mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-22 17:55:42 +00:00

The benefit of using the proxy device is that we can 'use' the proxy device for device access, but 'report' the progress on the passed FuDevice instance. This means the front-end reports the device status correctly when updating composite devices that us proxies. The comment always said we should move it to the daemon if another plugin started doing this, and that is now.
1066 lines
34 KiB
C
1066 lines
34 KiB
C
/*
|
|
* Copyright (C) 2017 VIA Corporation
|
|
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <fwupdplugin.h>
|
|
#include <string.h>
|
|
|
|
#include "fu-vli-usbhub-common.h"
|
|
#include "fu-vli-usbhub-device.h"
|
|
#include "fu-vli-usbhub-firmware.h"
|
|
#include "fu-vli-usbhub-msp430-device.h"
|
|
#include "fu-vli-usbhub-rtd21xx-device.h"
|
|
#include "fu-vli-usbhub-pd-device.h"
|
|
|
|
struct _FuVliUsbhubDevice
|
|
{
|
|
FuVliDevice parent_instance;
|
|
gboolean disable_powersave;
|
|
guint8 update_protocol;
|
|
FuVliUsbhubHeader hd1_hdr; /* factory */
|
|
FuVliUsbhubHeader hd2_hdr; /* update */
|
|
};
|
|
|
|
G_DEFINE_TYPE (FuVliUsbhubDevice, fu_vli_usbhub_device, FU_TYPE_VLI_DEVICE)
|
|
|
|
/**
|
|
* FU_VLI_USBHUB_DEVICE_FLAG_ATTACH_WITH_GPIOB:
|
|
*
|
|
* Use GPIO-B reset to reset the device.
|
|
*/
|
|
#define FU_VLI_USBHUB_DEVICE_FLAG_ATTACH_WITH_GPIOB (1 << 0)
|
|
/**
|
|
* FU_VLI_USBHUB_DEVICE_FLAG_USB2:
|
|
*
|
|
* Device is USB-2 speed.
|
|
*/
|
|
#define FU_VLI_USBHUB_DEVICE_FLAG_USB2 (1 << 1)
|
|
/**
|
|
* FU_VLI_USBHUB_DEVICE_FLAG_USB3:
|
|
*
|
|
* Device is USB-3 speed.
|
|
*/
|
|
#define FU_VLI_USBHUB_DEVICE_FLAG_USB3 (1 << 2)
|
|
/**
|
|
* FU_VLI_USBHUB_DEVICE_FLAG_UNLOCK_LEGACY813:
|
|
*
|
|
* Device type VL813 needs unlocking with a custom VDR request.
|
|
*/
|
|
#define FU_VLI_USBHUB_DEVICE_FLAG_UNLOCK_LEGACY813 (1 << 3)
|
|
/**
|
|
* FU_VLI_USBHUB_DEVICE_FLAG_HAS_SHARED_SPI_PD:
|
|
*
|
|
* Device shares the SPI device with the PD device.
|
|
*/
|
|
#define FU_VLI_USBHUB_DEVICE_FLAG_HAS_SHARED_SPI_PD (1 << 4)
|
|
/**
|
|
* FU_VLI_USBHUB_DEVICE_FLAG_HAS_MSP430:
|
|
*
|
|
* Device has a MSP430 attached via I²C.
|
|
*/
|
|
#define FU_VLI_USBHUB_DEVICE_FLAG_HAS_MSP430 (1 << 5)
|
|
/**
|
|
* FU_VLI_USBHUB_DEVICE_FLAG_HAS_RTD21XX:
|
|
*
|
|
* Device has a RTD21XX attached via I²C.
|
|
*/
|
|
#define FU_VLI_USBHUB_DEVICE_FLAG_HAS_RTD21XX (1 << 6)
|
|
|
|
static void
|
|
fu_vli_usbhub_device_to_string (FuDevice *device, guint idt, GString *str)
|
|
{
|
|
FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE (device);
|
|
|
|
/* parent */
|
|
FU_DEVICE_CLASS (fu_vli_usbhub_device_parent_class)->to_string (device, idt, str);
|
|
|
|
fu_common_string_append_kb (str, idt, "DisablePowersave", self->disable_powersave);
|
|
fu_common_string_append_kx (str, idt, "UpdateProtocol", self->update_protocol);
|
|
if (self->update_protocol >= 0x2) {
|
|
fu_common_string_append_kv (str, idt, "H1Hdr@0x0", NULL);
|
|
fu_vli_usbhub_header_to_string (&self->hd1_hdr, idt + 1, str);
|
|
if (self->hd2_hdr.dev_id != 0xffff) {
|
|
fu_common_string_append_kv (str, idt, "H2Hdr@0x1000", NULL);
|
|
fu_vli_usbhub_header_to_string (&self->hd2_hdr, idt + 1, str);
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_vdr_unlock_813 (FuVliUsbhubDevice *self, GError **error)
|
|
{
|
|
GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self));
|
|
if (!g_usb_device_control_transfer (usb_device,
|
|
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
0x85, 0x8786, 0x8988,
|
|
NULL, 0x0, NULL,
|
|
FU_VLI_DEVICE_TIMEOUT,
|
|
NULL, error)) {
|
|
g_prefix_error (error, "failed to UnLock_VL813: ");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_read_reg (FuVliUsbhubDevice *self, guint16 addr, guint8 *buf, GError **error)
|
|
{
|
|
if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
|
|
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
addr >> 8, addr & 0xff, 0x0,
|
|
buf, 0x1, NULL,
|
|
FU_VLI_DEVICE_TIMEOUT,
|
|
NULL, error)) {
|
|
g_prefix_error (error, "failed to read register 0x%x: ", addr);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_write_reg (FuVliUsbhubDevice *self, guint16 addr, guint8 value, GError **error)
|
|
{
|
|
if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
|
|
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
addr >> 8, addr & 0xff, (guint16) value,
|
|
NULL, 0x0, NULL,
|
|
FU_VLI_DEVICE_TIMEOUT,
|
|
NULL, error)) {
|
|
g_prefix_error (error, "failed to write register 0x%x: ", addr);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_spi_read_status (FuVliDevice *self, guint8 *status, GError **error)
|
|
{
|
|
guint8 spi_cmd = 0x0;
|
|
if (!fu_vli_device_get_spi_cmd (self, FU_VLI_DEVICE_SPI_REQ_READ_STATUS,
|
|
&spi_cmd, error))
|
|
return FALSE;
|
|
return g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
|
|
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
0xc1, spi_cmd, 0x0000,
|
|
status, 0x1, NULL,
|
|
FU_VLI_DEVICE_TIMEOUT,
|
|
NULL, error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_spi_read_data (FuVliDevice *self, guint32 addr, guint8 *buf, gsize bufsz, GError **error)
|
|
{
|
|
guint8 spi_cmd = 0x0;
|
|
guint16 value;
|
|
guint16 index;
|
|
if (!fu_vli_device_get_spi_cmd (self, FU_VLI_DEVICE_SPI_REQ_READ_DATA,
|
|
&spi_cmd, error))
|
|
return FALSE;
|
|
value = ((addr >> 8) & 0xff00) | spi_cmd;
|
|
index = ((addr << 8) & 0xff00) | ((addr >> 8) & 0x00ff);
|
|
return g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
|
|
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
0xc4, value, index,
|
|
buf, bufsz, NULL,
|
|
FU_VLI_DEVICE_TIMEOUT,
|
|
NULL, error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_spi_write_status (FuVliDevice *self, guint8 status, GError **error)
|
|
{
|
|
guint8 spi_cmd = 0x0;
|
|
if (!fu_vli_device_get_spi_cmd (self, FU_VLI_DEVICE_SPI_REQ_WRITE_STATUS,
|
|
&spi_cmd, error))
|
|
return FALSE;
|
|
if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
|
|
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
0xd1, spi_cmd, 0x0000,
|
|
&status, 0x1, NULL,
|
|
FU_VLI_DEVICE_TIMEOUT,
|
|
NULL, error)) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* Fix_For_GD_&_EN_SPI_Flash */
|
|
g_usleep (100 * 1000);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_spi_write_enable (FuVliDevice *self, GError **error)
|
|
{
|
|
guint8 spi_cmd = 0x0;
|
|
if (!fu_vli_device_get_spi_cmd (self, FU_VLI_DEVICE_SPI_REQ_WRITE_EN,
|
|
&spi_cmd, error))
|
|
return FALSE;
|
|
if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
|
|
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
0xd1, spi_cmd, 0x0000,
|
|
NULL, 0x0, NULL,
|
|
FU_VLI_DEVICE_TIMEOUT,
|
|
NULL, error)) {
|
|
g_prefix_error (error, "failed to write enable SPI: ");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_spi_chip_erase (FuVliDevice *self, GError **error)
|
|
{
|
|
guint8 spi_cmd = 0x0;
|
|
if (!fu_vli_device_get_spi_cmd (self, FU_VLI_DEVICE_SPI_REQ_CHIP_ERASE,
|
|
&spi_cmd, error))
|
|
return FALSE;
|
|
if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
|
|
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
0xd1, spi_cmd, 0x0000,
|
|
NULL, 0x0, NULL,
|
|
FU_VLI_DEVICE_TIMEOUT,
|
|
NULL, error)) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_spi_sector_erase (FuVliDevice *self, guint32 addr, GError **error)
|
|
{
|
|
guint8 spi_cmd = 0x0;
|
|
guint16 value;
|
|
guint16 index;
|
|
if (!fu_vli_device_get_spi_cmd (self, FU_VLI_DEVICE_SPI_REQ_SECTOR_ERASE,
|
|
&spi_cmd, error))
|
|
return FALSE;
|
|
value = ((addr >> 8) & 0xff00) | spi_cmd;
|
|
index = ((addr << 8) & 0xff00) | ((addr >> 8) & 0x00ff);
|
|
return g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
|
|
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
0xd4, value, index,
|
|
NULL, 0x0, NULL,
|
|
FU_VLI_DEVICE_TIMEOUT,
|
|
NULL, error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_spi_write_data (FuVliDevice *self,
|
|
guint32 addr,
|
|
const guint8 *buf,
|
|
gsize bufsz,
|
|
GError **error)
|
|
{
|
|
guint8 spi_cmd = 0x0;
|
|
guint16 value;
|
|
guint16 index;
|
|
if (!fu_vli_device_get_spi_cmd (self, FU_VLI_DEVICE_SPI_REQ_PAGE_PROG,
|
|
&spi_cmd, error))
|
|
return FALSE;
|
|
value = ((addr >> 8) & 0xff00) | spi_cmd;
|
|
index = ((addr << 8) & 0xff00) | ((addr >> 8) & 0x00ff);
|
|
if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
|
|
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
0xd4, value, index,
|
|
(guint8 *) buf, bufsz, NULL,
|
|
FU_VLI_DEVICE_TIMEOUT,
|
|
NULL, error)) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#define VL817_ADDR_GPIO_OUTPUT_ENABLE 0xF6A0 /* 0=input, 1=output */
|
|
#define VL817_ADDR_GPIO_SET_OUTPUT_DATA 0xF6A1 /* 0=low, 1=high */
|
|
#define VL817_ADDR_GPIO_GET_INPUT_DATA 0xF6A2 /* 0=low, 1=high */
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_attach (FuDevice *device, GError **error)
|
|
{
|
|
FuDevice *proxy = fu_device_get_proxy_with_fallback (device);
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
/* update UI */
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART);
|
|
|
|
/* some hardware has to toggle a GPIO to reset the entire PCB */
|
|
if (fu_vli_device_get_kind (FU_VLI_DEVICE (proxy)) == FU_VLI_DEVICE_KIND_VL817 &&
|
|
fu_device_has_private_flag (device, FU_VLI_USBHUB_DEVICE_FLAG_ATTACH_WITH_GPIOB)) {
|
|
guint8 tmp = 0x0;
|
|
|
|
/* set GPIOB output enable */
|
|
g_debug ("using GPIO reset for %s", fu_device_get_id (device));
|
|
if (!fu_vli_usbhub_device_read_reg (FU_VLI_USBHUB_DEVICE (proxy),
|
|
VL817_ADDR_GPIO_OUTPUT_ENABLE,
|
|
&tmp, error))
|
|
return FALSE;
|
|
if (!fu_vli_usbhub_device_write_reg (FU_VLI_USBHUB_DEVICE (proxy),
|
|
VL817_ADDR_GPIO_OUTPUT_ENABLE,
|
|
tmp | (1 << 1), error))
|
|
return FALSE;
|
|
|
|
/* toggle GPIOB to trigger reset */
|
|
if (!fu_vli_usbhub_device_read_reg (FU_VLI_USBHUB_DEVICE (proxy),
|
|
VL817_ADDR_GPIO_SET_OUTPUT_DATA,
|
|
&tmp, error))
|
|
return FALSE;
|
|
if (!fu_vli_usbhub_device_write_reg (FU_VLI_USBHUB_DEVICE (proxy),
|
|
VL817_ADDR_GPIO_SET_OUTPUT_DATA,
|
|
tmp ^ (1 << 1), error))
|
|
return FALSE;
|
|
} else {
|
|
/* replug, and ignore the device going away */
|
|
if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (proxy)),
|
|
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
0xf6, 0x0040, 0x0002,
|
|
NULL, 0x0, NULL,
|
|
FU_VLI_DEVICE_TIMEOUT,
|
|
NULL, &error_local)) {
|
|
if (g_error_matches (error_local,
|
|
G_USB_DEVICE_ERROR,
|
|
G_USB_DEVICE_ERROR_NO_DEVICE) ||
|
|
g_error_matches (error_local,
|
|
G_USB_DEVICE_ERROR,
|
|
G_USB_DEVICE_ERROR_FAILED)) {
|
|
g_debug ("ignoring %s", error_local->message);
|
|
} else {
|
|
g_propagate_prefixed_error (error,
|
|
g_steal_pointer (&error_local),
|
|
"failed to restart device: ");
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* success */
|
|
fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
|
|
return TRUE;
|
|
}
|
|
|
|
/* disable hub sleep states -- not really required by 815~ hubs */
|
|
static gboolean
|
|
fu_vli_usbhub_device_disable_u1u2 (FuVliUsbhubDevice *self, GError **error)
|
|
{
|
|
guint8 buf = 0x0;
|
|
|
|
/* clear Reg[0xF8A2] bit_3 & bit_7 -- also
|
|
* clear Total Switch / Flag To Disable FW Auto-Reload Function */
|
|
if (!fu_vli_usbhub_device_read_reg (self, 0xf8a2, &buf, error))
|
|
return FALSE;
|
|
buf &= 0x77;
|
|
if (!fu_vli_usbhub_device_write_reg (self, 0xf8a2, buf, error))
|
|
return FALSE;
|
|
|
|
/* clear Reg[0xF832] bit_0 & bit_1 */
|
|
if (!fu_vli_usbhub_device_read_reg (self, 0xf832, &buf, error))
|
|
return FALSE;
|
|
buf &= 0xfc;
|
|
if (!fu_vli_usbhub_device_write_reg (self, 0xf832, buf, error))
|
|
return FALSE;
|
|
|
|
/* clear Reg[0xF920] bit_1 & bit_2 */
|
|
if (!fu_vli_usbhub_device_read_reg (self, 0xf920, &buf, error))
|
|
return FALSE;
|
|
buf &= 0xf9;
|
|
if (!fu_vli_usbhub_device_write_reg (self, 0xf920, buf, error))
|
|
return FALSE;
|
|
|
|
/* set Reg[0xF836] bit_3 */
|
|
if (!fu_vli_usbhub_device_read_reg (self, 0xf836, &buf, error))
|
|
return FALSE;
|
|
buf |= 0x08;
|
|
if (!fu_vli_usbhub_device_write_reg (self, 0xf836, buf, error))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_guess_kind (FuVliUsbhubDevice *self, GError **error)
|
|
{
|
|
GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self));
|
|
guint8 b811P812 = 0x0;
|
|
guint8 b820Q7Q8 = 0x0;
|
|
guint8 b820822 = 0x0;
|
|
guint8 chipid1 = 0x0;
|
|
guint8 chipid2 = 0x0;
|
|
guint8 chipid12 = 0x0;
|
|
guint8 chipid22 = 0x0;
|
|
guint8 chipver = 0x0;
|
|
guint8 chipver2 = 0x0;
|
|
gint tPid = g_usb_device_get_pid (usb_device) & 0x0fff;
|
|
|
|
if (!fu_vli_usbhub_device_read_reg (self, 0xf88c, &chipver, error)) {
|
|
g_prefix_error (error, "Read_ChipVer failed: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_vli_usbhub_device_read_reg (self, 0xf63f, &chipver2, error)) {
|
|
g_prefix_error (error, "Read_ChipVer2 failed: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_vli_usbhub_device_read_reg (self, 0xf800, &b811P812, error)) {
|
|
g_prefix_error (error, "Read_811P812 failed: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_vli_usbhub_device_read_reg (self, 0xf88e, &chipid1, error)) {
|
|
g_prefix_error (error, "Read_ChipID1 failed: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_vli_usbhub_device_read_reg (self, 0xf88f, &chipid2, error)) {
|
|
g_prefix_error (error, "Read_ChipID2 failed: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_vli_usbhub_device_read_reg (self, 0xf64e, &chipid12, error)) {
|
|
g_prefix_error (error, "Read_ChipID12 failed: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_vli_usbhub_device_read_reg (self, 0xf64f, &chipid22, error)) {
|
|
g_prefix_error (error, "Read_ChipID22 failed: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_vli_usbhub_device_read_reg (self, 0xf651, &b820Q7Q8, error)) {
|
|
g_prefix_error (error, "Read_820Q7Q8 failed: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_vli_usbhub_device_read_reg (self, 0xf88c, &b820822, error)) {
|
|
g_prefix_error (error, "Read_820822 failed: ");
|
|
return FALSE;
|
|
}
|
|
if (g_getenv ("FWUPD_VLI_USBHUB_VERBOSE") != NULL) {
|
|
g_debug ("chipver = 0x%02x", chipver);
|
|
g_debug ("chipver2 = 0x%02x", chipver2);
|
|
g_debug ("b811P812 = 0x%02x", b811P812);
|
|
g_debug ("chipid1 = 0x%02x", chipid1);
|
|
g_debug ("chipid2 = 0x%02x", chipid2);
|
|
g_debug ("chipid12 = 0x%02x", chipid12);
|
|
g_debug ("chipid22 = 0x%02x", chipid22);
|
|
g_debug ("b820Q7Q8 = 0x%02x", b820Q7Q8);
|
|
g_debug ("b820822 = 0x%02x", b820822);
|
|
}
|
|
|
|
if (chipid2 == 0x35 && chipid1 == 0x07) {
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL210);
|
|
} else if (chipid2 == 0x35 && chipid1 == 0x18) {
|
|
if (b820822 == 0xF0) {
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL822);
|
|
} else {
|
|
if (b820Q7Q8 & (1 << 2))
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL820Q8);
|
|
else
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL820Q7);
|
|
}
|
|
} else if (chipid2 == 0x35 && chipid1 == 0x31) {
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL815);
|
|
} else if (chipid2 == 0x35 && chipid1 == 0x38) {
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL817);
|
|
} else if (chipid2 == 0x35 && chipid1 == 0x45) {
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL211);
|
|
} else if (chipid22 == 0x35 && chipid12 == 0x53) {
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL120);
|
|
} else if (chipid2 == 0x35 && chipid1 == 0x57) {
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL819);
|
|
} else if (tPid == 0x810) {
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL810);
|
|
} else if (tPid == 0x811) {
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL811);
|
|
} else if ((b811P812 & ((1 << 5) | (1 << 4))) == 0) {
|
|
if (chipver == 0x10)
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL811PB0);
|
|
else
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL811PB3);
|
|
} else if ((b811P812 & ((1 << 5) | (1 << 4))) == (1 << 4)) {
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL812Q4S);
|
|
} else if ((b811P812 & ((1 << 5) | (1 << 4))) == ((1 << 5) | (1 << 4))) {
|
|
if (chipver == 0x10)
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL812B0);
|
|
else
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL812B3);
|
|
} else {
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_NOT_SUPPORTED,
|
|
"hardware is not supported");
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_probe (FuDevice *device, GError **error)
|
|
{
|
|
guint16 usbver = fu_usb_device_get_spec (FU_USB_DEVICE (device));
|
|
|
|
/* FuUsbDevice->probe */
|
|
if (!FU_DEVICE_CLASS (fu_vli_usbhub_device_parent_class)->probe (device, error))
|
|
return FALSE;
|
|
|
|
/* quirks now applied... */
|
|
if (usbver > 0x0300 ||
|
|
fu_device_has_private_flag (device, FU_VLI_USBHUB_DEVICE_FLAG_USB3)) {
|
|
fu_device_set_summary (device, "USB 3.x hub");
|
|
/* prefer to show the USB 3 device and only fall back to the
|
|
* USB 2 version as a recovery */
|
|
fu_device_set_priority (device, 1);
|
|
} else if (usbver > 0x0200 ||
|
|
fu_device_has_private_flag (device, FU_VLI_USBHUB_DEVICE_FLAG_USB2)) {
|
|
fu_device_set_summary (device, "USB 2.x hub");
|
|
} else {
|
|
fu_device_set_summary (device, "USB hub");
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_pd_setup (FuVliUsbhubDevice *self, GError **error)
|
|
{
|
|
g_autoptr(FuDevice) dev = NULL;
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
/* add child */
|
|
dev = fu_vli_usbhub_pd_device_new (self);
|
|
if (!fu_device_probe (dev, error))
|
|
return FALSE;
|
|
if (!fu_device_setup (dev, &error_local)) {
|
|
if (g_error_matches (error_local,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_FOUND)) {
|
|
g_debug ("%s", error_local->message);
|
|
} else {
|
|
g_warning ("cannot create PD device: %s",
|
|
error_local->message);
|
|
}
|
|
return TRUE;
|
|
}
|
|
fu_device_add_child (FU_DEVICE (self), dev);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_msp430_setup (FuVliUsbhubDevice *self, GError **error)
|
|
{
|
|
g_autoptr(FuDevice) dev = NULL;
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
/* add child */
|
|
dev = fu_vli_usbhub_msp430_device_new (self);
|
|
if (!fu_device_probe (dev, error))
|
|
return FALSE;
|
|
if (!fu_device_setup (dev, &error_local)) {
|
|
if (g_error_matches (error_local,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_FOUND)) {
|
|
g_debug ("%s", error_local->message);
|
|
} else {
|
|
g_warning ("cannot create MSP430 I²C device: %s",
|
|
error_local->message);
|
|
}
|
|
return TRUE;
|
|
}
|
|
fu_device_add_child (FU_DEVICE (self), dev);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_rtd21xx_setup (FuVliUsbhubDevice *self, GError **error)
|
|
{
|
|
g_autoptr(FuDevice) dev = NULL;
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
/* add child */
|
|
dev = fu_vli_usbhub_rtd21xx_device_new (self);
|
|
if (!fu_device_probe (dev, error))
|
|
return FALSE;
|
|
if (!fu_device_setup (dev, &error_local)) {
|
|
if (g_error_matches (error_local,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_FOUND)) {
|
|
g_debug ("%s", error_local->message);
|
|
} else {
|
|
g_warning ("cannot create RTD21XX I²C device: %s",
|
|
error_local->message);
|
|
}
|
|
return TRUE;
|
|
}
|
|
fu_device_add_child (FU_DEVICE (self), dev);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_ready (FuDevice *device, GError **error)
|
|
{
|
|
FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE (device);
|
|
g_autoptr(GError) error_tmp = NULL;
|
|
|
|
/* try to read a block of data which will fail for 813-type devices */
|
|
if (fu_device_has_private_flag (device, FU_VLI_USBHUB_DEVICE_FLAG_UNLOCK_LEGACY813) &&
|
|
!fu_vli_device_spi_read_block (FU_VLI_DEVICE (self), 0x0, (guint8 *) &self->hd1_hdr,
|
|
sizeof(self->hd1_hdr), &error_tmp)) {
|
|
g_warning ("failed to read, trying to unlock 813: %s", error_tmp->message);
|
|
if (!fu_vli_usbhub_device_vdr_unlock_813 (self, error))
|
|
return FALSE;
|
|
if (!fu_vli_device_spi_read_block (FU_VLI_DEVICE (self), 0x0, (guint8 *) &self->hd1_hdr,
|
|
sizeof(self->hd1_hdr), error)) {
|
|
g_prefix_error (error, "813 unlock fail: ");
|
|
return FALSE;
|
|
}
|
|
g_debug ("813 unlock OK");
|
|
/* VL813 & VL210 have same PID (0x0813), and only VL813 can reply */
|
|
fu_vli_device_set_kind (FU_VLI_DEVICE (self), FU_VLI_DEVICE_KIND_VL813);
|
|
} else {
|
|
if (!fu_vli_usbhub_device_guess_kind (self, error))
|
|
return FALSE;
|
|
}
|
|
|
|
/* read HD1 (factory) header */
|
|
if (!fu_vli_device_spi_read_block (FU_VLI_DEVICE (self), VLI_USBHUB_FLASHMAP_ADDR_HD1,
|
|
(guint8 *) &self->hd1_hdr,
|
|
sizeof(self->hd1_hdr), error)) {
|
|
g_prefix_error (error, "failed to read HD1 header: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* detect update protocol from the device ID */
|
|
switch (GUINT16_FROM_BE(self->hd1_hdr.dev_id) >> 8) {
|
|
/* VL810~VL813 */
|
|
case 0x0d:
|
|
self->update_protocol = 0x1;
|
|
self->disable_powersave = TRUE;
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE);
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE);
|
|
fu_device_set_install_duration (FU_DEVICE (self), 10); /* seconds */
|
|
break;
|
|
/* VL817~ */
|
|
case 0x05:
|
|
self->update_protocol = 0x2;
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_DUAL_IMAGE);
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_SELF_RECOVERY);
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE);
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE);
|
|
fu_device_set_install_duration (FU_DEVICE (self), 15); /* seconds */
|
|
break;
|
|
default:
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_NOT_SUPPORTED,
|
|
"hardware is not supported, dev_id=0x%x",
|
|
(guint) GUINT16_FROM_BE(self->hd1_hdr.dev_id));
|
|
return FALSE;
|
|
}
|
|
|
|
/* read HD2 (update) header */
|
|
if (self->update_protocol >= 0x2) {
|
|
if (!fu_vli_device_spi_read_block (FU_VLI_DEVICE (self), VLI_USBHUB_FLASHMAP_ADDR_HD2,
|
|
(guint8 *) &self->hd2_hdr,
|
|
sizeof(self->hd2_hdr), error)) {
|
|
g_prefix_error (error, "failed to read HD2 header: ");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* detect the PD child */
|
|
if (fu_device_has_private_flag (device, FU_VLI_USBHUB_DEVICE_FLAG_HAS_SHARED_SPI_PD)) {
|
|
if (!fu_vli_usbhub_device_pd_setup (self, error))
|
|
return FALSE;
|
|
}
|
|
|
|
/* detect the I²C child */
|
|
if (fu_usb_device_get_spec (FU_USB_DEVICE (self)) >= 0x0300 &&
|
|
fu_device_has_private_flag (device, FU_VLI_USBHUB_DEVICE_FLAG_HAS_MSP430)) {
|
|
if (!fu_vli_usbhub_device_msp430_setup (self, error))
|
|
return FALSE;
|
|
}
|
|
if (fu_device_has_private_flag (device, FU_VLI_USBHUB_DEVICE_FLAG_HAS_RTD21XX)) {
|
|
if (!fu_vli_usbhub_device_rtd21xx_setup (self, error))
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static FuFirmware *
|
|
fu_vli_usbhub_device_prepare_firmware (FuDevice *device,
|
|
GBytes *fw,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE (device);
|
|
FuVliDeviceKind device_kind;
|
|
guint16 device_id;
|
|
g_autoptr(FuFirmware) firmware = fu_vli_usbhub_firmware_new ();
|
|
|
|
/* check is compatible with firmware */
|
|
if (!fu_firmware_parse (firmware, fw, flags, error))
|
|
return NULL;
|
|
device_kind = fu_vli_usbhub_firmware_get_device_kind (FU_VLI_USBHUB_FIRMWARE (firmware));
|
|
if (fu_vli_device_get_kind (FU_VLI_DEVICE (self)) != device_kind) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"firmware incompatible, got %s, expected %s",
|
|
fu_vli_common_device_kind_to_string (device_kind),
|
|
fu_vli_common_device_kind_to_string (fu_vli_device_get_kind (FU_VLI_DEVICE (self))));
|
|
return NULL;
|
|
}
|
|
device_id = fu_vli_usbhub_firmware_get_device_id (FU_VLI_USBHUB_FIRMWARE (firmware));
|
|
if (GUINT16_FROM_BE(self->hd1_hdr.dev_id) != device_id) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"firmware incompatible, got 0x%04x, expected 0x%04x",
|
|
device_id,
|
|
(guint) GUINT16_FROM_BE(self->hd1_hdr.dev_id));
|
|
return NULL;
|
|
}
|
|
|
|
/* we could check this against flags */
|
|
g_debug ("parsed version: %s", fu_firmware_get_version (firmware));
|
|
return g_steal_pointer (&firmware);
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_update_v1 (FuVliUsbhubDevice *self,
|
|
FuFirmware *firmware,
|
|
GError **error)
|
|
{
|
|
gsize bufsz = 0;
|
|
const guint8 *buf;
|
|
g_autoptr(GBytes) fw = NULL;
|
|
|
|
/* simple image */
|
|
fw = fu_firmware_get_bytes (firmware, error);
|
|
if (fw == NULL)
|
|
return FALSE;
|
|
|
|
/* erase */
|
|
fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_ERASE);
|
|
if (!fu_vli_device_spi_erase_all (FU_VLI_DEVICE (self), error)) {
|
|
g_prefix_error (error, "failed to erase chip: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* write in chunks */
|
|
fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_WRITE);
|
|
buf = g_bytes_get_data (fw, &bufsz);
|
|
if (!fu_vli_device_spi_write (FU_VLI_DEVICE (self), 0x0, buf, bufsz, error))
|
|
return FALSE;
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
/* if no header1 or ROM code update, write data directly */
|
|
static gboolean
|
|
fu_vli_usbhub_device_update_v2_recovery (FuVliUsbhubDevice *self, GBytes *fw, GError **error)
|
|
{
|
|
gsize bufsz = 0;
|
|
const guint8 *buf = g_bytes_get_data (fw, &bufsz);
|
|
|
|
/* erase */
|
|
fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_ERASE);
|
|
for (guint32 addr = 0; addr < bufsz; addr += 0x1000) {
|
|
if (!fu_vli_device_spi_erase_sector (FU_VLI_DEVICE (self), addr, error)) {
|
|
g_prefix_error (error, "failed to erase sector @0x%x: ", addr);
|
|
return FALSE;
|
|
}
|
|
fu_device_set_progress_full (FU_DEVICE (self), (gsize) addr, bufsz);
|
|
}
|
|
|
|
/* write in chunks */
|
|
fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_WRITE);
|
|
if (!fu_vli_device_spi_write (FU_VLI_DEVICE (self), VLI_USBHUB_FLASHMAP_ADDR_HD1,
|
|
buf, bufsz, error))
|
|
return FALSE;
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_hd1_is_valid (FuVliUsbhubHeader *hdr)
|
|
{
|
|
if (hdr->prev_ptr != VLI_USBHUB_FLASHMAP_IDX_INVALID)
|
|
return FALSE;
|
|
if (hdr->checksum != fu_vli_usbhub_header_crc8 (hdr))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_hd1_recover (FuVliUsbhubDevice *self, FuVliUsbhubHeader *hdr, GError **error)
|
|
{
|
|
/* point to HD2, i.e. updated firmware */
|
|
if (hdr->next_ptr != VLI_USBHUB_FLASHMAP_IDX_HD2) {
|
|
hdr->next_ptr = VLI_USBHUB_FLASHMAP_IDX_HD2;
|
|
hdr->checksum = fu_vli_usbhub_header_crc8 (hdr);
|
|
}
|
|
|
|
/* write new header block */
|
|
if (!fu_vli_device_spi_erase_sector (FU_VLI_DEVICE (self), VLI_USBHUB_FLASHMAP_ADDR_HD1, error)) {
|
|
g_prefix_error (error,
|
|
"failed to erase header1 sector at 0x%x: ",
|
|
(guint) VLI_USBHUB_FLASHMAP_ADDR_HD1);
|
|
return FALSE;
|
|
}
|
|
if (!fu_vli_device_spi_write_block (FU_VLI_DEVICE (self), VLI_USBHUB_FLASHMAP_ADDR_HD1,
|
|
(const guint8 *) hdr,
|
|
sizeof(FuVliUsbhubHeader),
|
|
error)) {
|
|
g_prefix_error (error,
|
|
"failed to write header1 block at 0x%x: ",
|
|
(guint) VLI_USBHUB_FLASHMAP_ADDR_HD1);
|
|
return FALSE;
|
|
}
|
|
|
|
/* update the cached copy */
|
|
memcpy (&self->hd1_hdr, hdr, sizeof(self->hd1_hdr));
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_update_v2 (FuVliUsbhubDevice *self, FuFirmware *firmware, GError **error)
|
|
{
|
|
gsize buf_fwsz = 0;
|
|
guint32 hd1_fw_sz;
|
|
guint32 hd2_fw_sz;
|
|
guint32 hd2_fw_addr;
|
|
guint32 hd2_fw_offset;
|
|
const guint8 *buf_fw;
|
|
FuVliUsbhubHeader hdr = { 0x0 };
|
|
g_autoptr(GBytes) fw = NULL;
|
|
|
|
/* simple image */
|
|
fw = fu_firmware_get_bytes (firmware, error);
|
|
if (fw == NULL)
|
|
return FALSE;
|
|
|
|
/* root header is valid */
|
|
if (fu_vli_usbhub_device_hd1_is_valid (&self->hd1_hdr)) {
|
|
|
|
/* no update has ever been done */
|
|
if (self->hd1_hdr.next_ptr != VLI_USBHUB_FLASHMAP_IDX_HD2) {
|
|
|
|
/* backup HD1 before recovering */
|
|
if (!fu_vli_device_spi_erase_sector (FU_VLI_DEVICE (self), VLI_USBHUB_FLASHMAP_ADDR_HD2, error)) {
|
|
g_prefix_error (error, "failed to erase sector at header 1: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_vli_device_spi_write_block (FU_VLI_DEVICE (self), VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP,
|
|
(const guint8 *) &self->hd1_hdr, sizeof(hdr),
|
|
error)) {
|
|
g_prefix_error (error, "failed to write block at header 1: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_vli_usbhub_device_hd1_recover (self, &self->hd1_hdr, error)) {
|
|
g_prefix_error (error, "failed to write header: ");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* copy the header from the backup zone */
|
|
} else {
|
|
g_debug ("HD1 was invalid, reading backup");
|
|
if (!fu_vli_device_spi_read_block (FU_VLI_DEVICE (self), VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP,
|
|
(guint8 *) &self->hd1_hdr, sizeof(hdr),
|
|
error)) {
|
|
g_prefix_error (error,
|
|
"failed to read root header from 0x%x: ",
|
|
(guint) VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP);
|
|
return FALSE;
|
|
}
|
|
if (!fu_vli_usbhub_device_hd1_is_valid (&self->hd1_hdr)) {
|
|
g_debug ("backup header is also invalid, starting recovery");
|
|
return fu_vli_usbhub_device_update_v2_recovery (self, fw, error);
|
|
}
|
|
if (!fu_vli_usbhub_device_hd1_recover (self, &self->hd1_hdr, error)) {
|
|
g_prefix_error (error, "failed to get root header in backup zone: ");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* align the update fw address to the sector after the factory size */
|
|
hd1_fw_sz = GUINT16_FROM_BE(self->hd1_hdr.usb3_fw_sz);
|
|
if (hd1_fw_sz > 0xF000) {
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_FAILED,
|
|
"FW1 size abnormal 0x%x",
|
|
(guint) hd1_fw_sz);
|
|
return FALSE;
|
|
}
|
|
hd2_fw_addr = (hd1_fw_sz + 0xfff) & 0xf000;
|
|
hd2_fw_addr += VLI_USBHUB_FLASHMAP_ADDR_FW;
|
|
|
|
/* get the size and offset of the update firmware */
|
|
buf_fw = g_bytes_get_data (fw, &buf_fwsz);
|
|
memcpy (&hdr, buf_fw, sizeof(hdr));
|
|
hd2_fw_sz = GUINT16_FROM_BE(hdr.usb3_fw_sz);
|
|
hd2_fw_offset = GUINT16_FROM_BE(hdr.usb3_fw_addr);
|
|
g_debug ("FW2 @0x%x (length 0x%x, offset 0x%x)",
|
|
hd2_fw_addr, hd2_fw_sz, hd2_fw_offset);
|
|
|
|
/* make space */
|
|
fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_ERASE);
|
|
if (!fu_vli_device_spi_erase (FU_VLI_DEVICE (self), hd2_fw_addr, hd2_fw_sz, error))
|
|
return FALSE;
|
|
|
|
/* perform the actual write */
|
|
fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_WRITE);
|
|
if (!fu_vli_device_spi_write (FU_VLI_DEVICE (self),
|
|
hd2_fw_addr,
|
|
buf_fw + hd2_fw_offset,
|
|
hd2_fw_sz,
|
|
error)) {
|
|
g_prefix_error (error, "failed to write payload: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* map into header */
|
|
if (!fu_memcpy_safe ((guint8 *) &self->hd2_hdr, sizeof(hdr), 0x0,
|
|
buf_fw, buf_fwsz, 0x0, sizeof(hdr), error)) {
|
|
g_prefix_error (error, "failed to read header: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* write new HD2 */
|
|
self->hd2_hdr.usb3_fw_addr = GUINT16_TO_BE(hd2_fw_addr & 0xffff);
|
|
self->hd2_hdr.usb3_fw_addr_high = (guint8) (hd2_fw_addr >> 16);
|
|
self->hd2_hdr.prev_ptr = VLI_USBHUB_FLASHMAP_IDX_HD1;
|
|
self->hd2_hdr.next_ptr = VLI_USBHUB_FLASHMAP_IDX_INVALID;
|
|
self->hd2_hdr.checksum = fu_vli_usbhub_header_crc8 (&self->hd2_hdr);
|
|
if (!fu_vli_device_spi_erase_sector (FU_VLI_DEVICE (self), VLI_USBHUB_FLASHMAP_ADDR_HD2, error)) {
|
|
g_prefix_error (error, "failed to erase sectors for HD2: ");
|
|
return FALSE;
|
|
}
|
|
if (!fu_vli_device_spi_write_block (FU_VLI_DEVICE (self),
|
|
VLI_USBHUB_FLASHMAP_ADDR_HD2,
|
|
(const guint8 *) &self->hd2_hdr,
|
|
sizeof(self->hd2_hdr),
|
|
error)) {
|
|
g_prefix_error (error, "failed to write HD2: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static GBytes *
|
|
fu_vli_usbhub_device_dump_firmware (FuDevice *device, GError **error)
|
|
{
|
|
FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE (device);
|
|
fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_READ);
|
|
return fu_vli_device_spi_read (FU_VLI_DEVICE (self), 0x0,
|
|
fu_device_get_firmware_size_max (device),
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_vli_usbhub_device_write_firmware (FuDevice *device,
|
|
FuFirmware *firmware,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE (device);
|
|
|
|
/* disable powersaving if required */
|
|
if (self->disable_powersave) {
|
|
if (!fu_vli_usbhub_device_disable_u1u2 (self, error)) {
|
|
g_prefix_error (error, "disabling powersave failed: ");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* use correct method */
|
|
if (self->update_protocol == 0x1)
|
|
return fu_vli_usbhub_device_update_v1 (self, firmware, error);
|
|
if (self->update_protocol == 0x2)
|
|
return fu_vli_usbhub_device_update_v2 (self, firmware, error);
|
|
|
|
/* not sure what to do */
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"update protocol 0x%x not supported",
|
|
self->update_protocol);
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
fu_vli_usbhub_device_init (FuVliUsbhubDevice *self)
|
|
{
|
|
fu_device_add_icon (FU_DEVICE (self), "audio-card");
|
|
fu_device_add_protocol (FU_DEVICE (self), "com.vli.usbhub");
|
|
fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE);
|
|
fu_device_register_private_flag (FU_DEVICE (self),
|
|
FU_VLI_USBHUB_DEVICE_FLAG_ATTACH_WITH_GPIOB,
|
|
"attach-with-gpiob");
|
|
fu_device_register_private_flag (FU_DEVICE (self),
|
|
FU_VLI_USBHUB_DEVICE_FLAG_USB2,
|
|
"usb3");
|
|
fu_device_register_private_flag (FU_DEVICE (self),
|
|
FU_VLI_USBHUB_DEVICE_FLAG_USB3,
|
|
"usb2");
|
|
fu_device_register_private_flag (FU_DEVICE (self),
|
|
FU_VLI_USBHUB_DEVICE_FLAG_UNLOCK_LEGACY813,
|
|
"unlock-legacy813");
|
|
fu_device_register_private_flag (FU_DEVICE (self),
|
|
FU_VLI_USBHUB_DEVICE_FLAG_HAS_SHARED_SPI_PD,
|
|
"has-shared-spi-pd");
|
|
fu_device_register_private_flag (FU_DEVICE (self),
|
|
FU_VLI_USBHUB_DEVICE_FLAG_HAS_MSP430,
|
|
"has-msp430");
|
|
fu_device_register_private_flag (FU_DEVICE (self),
|
|
FU_VLI_USBHUB_DEVICE_FLAG_HAS_RTD21XX,
|
|
"has-rtd21xx");
|
|
}
|
|
|
|
static void
|
|
fu_vli_usbhub_device_class_init (FuVliUsbhubDeviceClass *klass)
|
|
{
|
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
|
|
FuVliDeviceClass *klass_vli_device = FU_VLI_DEVICE_CLASS (klass);
|
|
klass_device->probe = fu_vli_usbhub_device_probe;
|
|
klass_device->dump_firmware = fu_vli_usbhub_device_dump_firmware;
|
|
klass_device->write_firmware = fu_vli_usbhub_device_write_firmware;
|
|
klass_device->prepare_firmware = fu_vli_usbhub_device_prepare_firmware;
|
|
klass_device->attach = fu_vli_usbhub_device_attach;
|
|
klass_device->to_string = fu_vli_usbhub_device_to_string;
|
|
klass_device->ready = fu_vli_usbhub_device_ready;
|
|
klass_vli_device->spi_chip_erase = fu_vli_usbhub_device_spi_chip_erase;
|
|
klass_vli_device->spi_sector_erase = fu_vli_usbhub_device_spi_sector_erase;
|
|
klass_vli_device->spi_read_data = fu_vli_usbhub_device_spi_read_data;
|
|
klass_vli_device->spi_read_status = fu_vli_usbhub_device_spi_read_status;
|
|
klass_vli_device->spi_write_data = fu_vli_usbhub_device_spi_write_data;
|
|
klass_vli_device->spi_write_enable = fu_vli_usbhub_device_spi_write_enable;
|
|
klass_vli_device->spi_write_status = fu_vli_usbhub_device_spi_write_status;
|
|
}
|