mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-07 10:27:36 +00:00
dell-dock: Add support for flashing Thunderbolt over I2C
This uses an API in the Realtek USB 3.1G2 controller to perform the flash procedure.
This commit is contained in:
parent
367f4590d6
commit
d6e453b0ba
@ -114,3 +114,8 @@ FirmwareSizeMin=0x40000
|
|||||||
FirmwareSizeMax=0x80000
|
FirmwareSizeMax=0x80000
|
||||||
Flags = require-ac
|
Flags = require-ac
|
||||||
InstallDuration = 22
|
InstallDuration = 22
|
||||||
|
DellDockInstallDurationI2C = 181
|
||||||
|
DellDockUnlockTarget = 10
|
||||||
|
DellDockHubVersionLowest = 1.31
|
||||||
|
DellDockBlobMajorOffset = 0x400a
|
||||||
|
DellDockBlobMinorOffset = 0x4009
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "fu-device.h"
|
#include "fu-device.h"
|
||||||
#include "fu-dell-dock-i2c-ec.h"
|
#include "fu-dell-dock-i2c-ec.h"
|
||||||
#include "fu-dell-dock-i2c-mst.h"
|
#include "fu-dell-dock-i2c-mst.h"
|
||||||
|
#include "fu-dell-dock-i2c-tbt.h"
|
||||||
#include "fu-dell-dock-hub.h"
|
#include "fu-dell-dock-hub.h"
|
||||||
#include "fu-dell-dock-hid.h"
|
#include "fu-dell-dock-hid.h"
|
||||||
#include "fu-dell-dock-status.h"
|
#include "fu-dell-dock-status.h"
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#include "fu-usb-device.h"
|
#include "fu-usb-device.h"
|
||||||
#include "fwupd-error.h"
|
#include "fwupd-error.h"
|
||||||
@ -25,6 +26,7 @@
|
|||||||
|
|
||||||
#define HIDI2C_MAX_REGISTER 4
|
#define HIDI2C_MAX_REGISTER 4
|
||||||
#define HID_MAX_RETRIES 5
|
#define HID_MAX_RETRIES 5
|
||||||
|
#define TBT_MAX_RETRIES 2
|
||||||
#define HIDI2C_TRANSACTION_TIMEOUT 2000
|
#define HIDI2C_TRANSACTION_TIMEOUT 2000
|
||||||
|
|
||||||
#define HUB_CMD_READ_DATA 0xC0
|
#define HUB_CMD_READ_DATA 0xC0
|
||||||
@ -36,6 +38,11 @@
|
|||||||
#define HUB_EXT_I2C_READ 0xD6
|
#define HUB_EXT_I2C_READ 0xD6
|
||||||
#define HUB_EXT_VERIFYUPDATE 0xD9
|
#define HUB_EXT_VERIFYUPDATE 0xD9
|
||||||
#define HUB_EXT_ERASEBANK 0xE8
|
#define HUB_EXT_ERASEBANK 0xE8
|
||||||
|
#define HUB_EXT_WRITE_TBT_FLASH 0xFF
|
||||||
|
|
||||||
|
#define TBT_COMMAND_WAKEUP 0x00000000
|
||||||
|
#define TBT_COMMAND_AUTHENTICATE 0xFFFFFFFF
|
||||||
|
#define TBT_COMMAND_AUTHENTICATE_STATUS 0xFFFFFFFE
|
||||||
|
|
||||||
typedef struct __attribute__ ((packed)) {
|
typedef struct __attribute__ ((packed)) {
|
||||||
guint8 cmd;
|
guint8 cmd;
|
||||||
@ -55,6 +62,20 @@ typedef struct __attribute__ ((packed)) {
|
|||||||
guint8 data[192];
|
guint8 data[192];
|
||||||
} FuHIDCmdBuffer;
|
} FuHIDCmdBuffer;
|
||||||
|
|
||||||
|
typedef struct __attribute__ ((packed)) {
|
||||||
|
guint8 cmd;
|
||||||
|
guint8 ext;
|
||||||
|
guint8 i2cslaveaddr;
|
||||||
|
guint8 i2cspeed;
|
||||||
|
union {
|
||||||
|
guint32 startaddress;
|
||||||
|
guint32 tbt_command;
|
||||||
|
};
|
||||||
|
guint8 bufferlen;
|
||||||
|
guint8 extended_cmdarea[55];
|
||||||
|
guint8 data[192];
|
||||||
|
} FuTbtCmdBuffer;
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
fu_dell_dock_hid_set_report (FuDevice *self,
|
fu_dell_dock_hid_set_report (FuDevice *self,
|
||||||
guint8 *outbuffer,
|
guint8 *outbuffer,
|
||||||
@ -374,3 +395,142 @@ fu_dell_dock_hid_i2c_read (FuDevice *self,
|
|||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
fu_dell_dock_hid_tbt_wake (FuDevice *self,
|
||||||
|
const FuHIDI2CParameters *parameters,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuTbtCmdBuffer cmd_buffer = {
|
||||||
|
.cmd = HUB_CMD_READ_DATA, /* special write command that reads status result */
|
||||||
|
.ext = HUB_EXT_WRITE_TBT_FLASH,
|
||||||
|
.i2cslaveaddr = parameters->i2cslaveaddr,
|
||||||
|
.i2cspeed = parameters->i2cspeed, /* unlike other commands doesn't need | 0x80 */
|
||||||
|
.tbt_command = TBT_COMMAND_WAKEUP,
|
||||||
|
.bufferlen = 0,
|
||||||
|
.extended_cmdarea[0 ... 53] = 0,
|
||||||
|
.data[0 ... 191] = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!fu_dell_dock_hid_set_report (self, (guint8 *) &cmd_buffer, error)) {
|
||||||
|
g_prefix_error (error, "failed to set wake thunderbolt: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!fu_dell_dock_hid_get_report (self, cmd_buffer.data, error)) {
|
||||||
|
g_prefix_error (error, "failed to get wake thunderbolt status: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
g_debug ("thunderbolt wake result: 0x%x", cmd_buffer.data[1]);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const gchar *
|
||||||
|
fu_dell_dock_hid_tbt_map_error (guint32 code)
|
||||||
|
{
|
||||||
|
if (code == 1)
|
||||||
|
return g_strerror (EINVAL);
|
||||||
|
else if (code == 2)
|
||||||
|
return g_strerror (EPERM);
|
||||||
|
|
||||||
|
return g_strerror (EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
fu_dell_dock_hid_tbt_write (FuDevice *self,
|
||||||
|
guint32 start_addr,
|
||||||
|
const guint8 *input,
|
||||||
|
gsize write_size,
|
||||||
|
const FuHIDI2CParameters *parameters,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuTbtCmdBuffer cmd_buffer = {
|
||||||
|
.cmd = HUB_CMD_READ_DATA, /* It's a special write command that reads status result */
|
||||||
|
.ext = HUB_EXT_WRITE_TBT_FLASH,
|
||||||
|
.i2cslaveaddr = parameters->i2cslaveaddr,
|
||||||
|
.i2cspeed = parameters->i2cspeed, /* unlike other commands doesn't need | 0x80 */
|
||||||
|
.startaddress = GUINT32_TO_LE (start_addr),
|
||||||
|
.bufferlen = write_size,
|
||||||
|
.extended_cmdarea[0 ... 53] = 0,
|
||||||
|
};
|
||||||
|
guint8 result;
|
||||||
|
|
||||||
|
g_return_val_if_fail (input != NULL, FALSE);
|
||||||
|
g_return_val_if_fail (write_size <= HIDI2C_MAX_WRITE, FALSE);
|
||||||
|
|
||||||
|
memcpy (cmd_buffer.data, input, write_size);
|
||||||
|
|
||||||
|
for (gint i = 1; i <= TBT_MAX_RETRIES; i++) {
|
||||||
|
if (!fu_dell_dock_hid_set_report (self, (guint8 *) &cmd_buffer, error)) {
|
||||||
|
g_prefix_error (error, "failed to run TBT update: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!fu_dell_dock_hid_get_report (self, cmd_buffer.data, error)) {
|
||||||
|
g_prefix_error (error, "failed to get TBT flash status: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
result = cmd_buffer.data[1] & 0xf;
|
||||||
|
if (result == 0)
|
||||||
|
break;
|
||||||
|
g_debug ("attempt %d/%d: Thunderbolt write failed: %x",
|
||||||
|
i, TBT_MAX_RETRIES, result);
|
||||||
|
}
|
||||||
|
if (result != 0) {
|
||||||
|
g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL,
|
||||||
|
"Writing address 0x%04x failed: %s",
|
||||||
|
start_addr, fu_dell_dock_hid_tbt_map_error (result));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
fu_dell_dock_hid_tbt_authenticate (FuDevice *self,
|
||||||
|
const FuHIDI2CParameters *parameters,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuTbtCmdBuffer cmd_buffer = {
|
||||||
|
.cmd = HUB_CMD_READ_DATA, /* It's a special write command that reads status result */
|
||||||
|
.ext = HUB_EXT_WRITE_TBT_FLASH,
|
||||||
|
.i2cslaveaddr = parameters->i2cslaveaddr,
|
||||||
|
.i2cspeed = parameters->i2cspeed, /* unlike other commands doesn't need | 0x80 */
|
||||||
|
.tbt_command = GUINT32_TO_LE (TBT_COMMAND_AUTHENTICATE),
|
||||||
|
.bufferlen = 0,
|
||||||
|
.extended_cmdarea[0 ... 53] = 0,
|
||||||
|
};
|
||||||
|
guint8 result;
|
||||||
|
|
||||||
|
if (!fu_dell_dock_hid_set_report (self, (guint8 *) &cmd_buffer, error)) {
|
||||||
|
g_prefix_error (error, "failed to send authentication: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_buffer.tbt_command = GUINT32_TO_LE (TBT_COMMAND_AUTHENTICATE_STATUS);
|
||||||
|
/* needs at least 2 seconds */
|
||||||
|
g_usleep (2000000);
|
||||||
|
for (gint i = 1; i <= TBT_MAX_RETRIES; i++) {
|
||||||
|
if (!fu_dell_dock_hid_set_report (self, (guint8 *) &cmd_buffer, error)) {
|
||||||
|
g_prefix_error (error, "failed to set check authentication: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!fu_dell_dock_hid_get_report (self, cmd_buffer.data, error)) {
|
||||||
|
g_prefix_error (error, "failed to get check authentication: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
result = cmd_buffer.data[1] & 0xf;
|
||||||
|
if (result == 0)
|
||||||
|
break;
|
||||||
|
g_debug ("attempt %d/%d: Thunderbolt authenticate failed: %x",
|
||||||
|
i, TBT_MAX_RETRIES, result);
|
||||||
|
g_usleep (500000);
|
||||||
|
}
|
||||||
|
if (result != 0) {
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Thunderbolt authentication failed: %s",
|
||||||
|
fu_dell_dock_hid_tbt_map_error (result));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
@ -78,4 +78,19 @@ gboolean fu_dell_dock_hid_verify_update (FuDevice *self,
|
|||||||
gboolean *result,
|
gboolean *result,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
|
gboolean fu_dell_dock_hid_tbt_wake (FuDevice *self,
|
||||||
|
const FuHIDI2CParameters *parameters,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gboolean fu_dell_dock_hid_tbt_write (FuDevice *self,
|
||||||
|
guint32 start_addr,
|
||||||
|
const guint8 *input,
|
||||||
|
gsize write_size,
|
||||||
|
const FuHIDI2CParameters *parameters,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gboolean fu_dell_dock_hid_tbt_authenticate (FuDevice *self,
|
||||||
|
const FuHIDI2CParameters *parameters,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
#endif /* __FU_DELL_DOCK_HID_H */
|
#endif /* __FU_DELL_DOCK_HID_H */
|
||||||
|
@ -37,6 +37,11 @@
|
|||||||
#define EXPECTED_DOCK_INFO_SIZE 0xb7
|
#define EXPECTED_DOCK_INFO_SIZE 0xb7
|
||||||
#define EXPECTED_DOCK_TYPE 0x04
|
#define EXPECTED_DOCK_TYPE 0x04
|
||||||
|
|
||||||
|
#define TBT_MODE_MASK 0x01
|
||||||
|
|
||||||
|
#define BIT_SET(x,y) (x |= (1<<y))
|
||||||
|
#define BIT_CLEAR(x,y) (x &= (~1<<(y)))
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FW_UPDATE_IN_PROGRESS,
|
FW_UPDATE_IN_PROGRESS,
|
||||||
FW_UPDATE_COMPLETE,
|
FW_UPDATE_COMPLETE,
|
||||||
@ -130,6 +135,7 @@ struct _FuDellDockEc {
|
|||||||
guint8 board_min;
|
guint8 board_min;
|
||||||
gchar *ec_minimum_version;
|
gchar *ec_minimum_version;
|
||||||
guint64 blob_version_offset;
|
guint64 blob_version_offset;
|
||||||
|
guint32 dock_unlock_status;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE (FuDellDockEc, fu_dell_dock_ec, FU_TYPE_DEVICE)
|
G_DEFINE_TYPE (FuDellDockEc, fu_dell_dock_ec, FU_TYPE_DEVICE)
|
||||||
@ -165,11 +171,17 @@ fu_dell_dock_ec_get_symbiote (FuDevice *device)
|
|||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
fu_dell_dock_ec_has_tbt (FuDevice *device)
|
fu_dell_dock_ec_needs_tbt (FuDevice *device)
|
||||||
{
|
{
|
||||||
FuDellDockEc *self = FU_DELL_DOCK_EC (device);
|
FuDellDockEc *self = FU_DELL_DOCK_EC (device);
|
||||||
|
gboolean port0_tbt_mode = self->data->port0_dock_status & TBT_MODE_MASK;
|
||||||
|
|
||||||
return self->data->module_type == MODULE_TYPE_TBT;
|
/* check for TBT module type */
|
||||||
|
if (self->data->module_type != MODULE_TYPE_TBT)
|
||||||
|
return FALSE;
|
||||||
|
g_debug ("found thunderbolt dock, port mode: %d", port0_tbt_mode);
|
||||||
|
|
||||||
|
return !port0_tbt_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const gchar*
|
static const gchar*
|
||||||
@ -348,7 +360,8 @@ fu_dell_dock_ec_get_dock_info (FuDevice *device,
|
|||||||
device_entry[i].version.version_8[2],
|
device_entry[i].version.version_8[2],
|
||||||
device_entry[i].version.version_8[3]);
|
device_entry[i].version.version_8[3]);
|
||||||
g_debug ("\tParsed version %s", self->mst_version);
|
g_debug ("\tParsed version %s", self->mst_version);
|
||||||
} else if (map->device_type == FU_DELL_DOCK_DEVICETYPE_TBT && fu_dell_dock_ec_has_tbt (device)) {
|
} else if (map->device_type == FU_DELL_DOCK_DEVICETYPE_TBT &&
|
||||||
|
self->data->module_type == MODULE_TYPE_TBT) {
|
||||||
/* guard against invalid Thunderbolt version read from EC */
|
/* guard against invalid Thunderbolt version read from EC */
|
||||||
if (!fu_dell_dock_test_valid_byte (device_entry[i].version.version_8, 2)) {
|
if (!fu_dell_dock_test_valid_byte (device_entry[i].version.version_8, 2)) {
|
||||||
g_warning ("[EC bug] EC read invalid Thunderbolt version %08x",
|
g_warning ("[EC bug] EC read invalid Thunderbolt version %08x",
|
||||||
@ -504,6 +517,7 @@ fu_dell_dock_ec_modify_lock (FuDevice *device,
|
|||||||
gboolean unlocked,
|
gboolean unlocked,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
|
FuDellDockEc *self = FU_DELL_DOCK_EC (device);
|
||||||
guint32 cmd;
|
guint32 cmd;
|
||||||
|
|
||||||
g_return_val_if_fail (device != NULL, FALSE);
|
g_return_val_if_fail (device != NULL, FALSE);
|
||||||
@ -524,6 +538,12 @@ fu_dell_dock_ec_modify_lock (FuDevice *device,
|
|||||||
fu_device_get_name (device),
|
fu_device_get_name (device),
|
||||||
fu_device_get_id (device));
|
fu_device_get_id (device));
|
||||||
|
|
||||||
|
if (unlocked)
|
||||||
|
BIT_SET (self->dock_unlock_status, target);
|
||||||
|
else
|
||||||
|
BIT_CLEAR (self->dock_unlock_status, target);
|
||||||
|
g_debug ("current overall unlock status: 0x%08x", self->dock_unlock_status);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ FuDellDockEc *fu_dell_dock_ec_new (FuDevice *symbiote);
|
|||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
gboolean fu_dell_dock_ec_has_tbt (FuDevice *device);
|
gboolean fu_dell_dock_ec_needs_tbt (FuDevice *device);
|
||||||
gboolean fu_dell_dock_ec_modify_lock (FuDevice *self,
|
gboolean fu_dell_dock_ec_modify_lock (FuDevice *self,
|
||||||
guint8 target,
|
guint8 target,
|
||||||
gboolean unlocked,
|
gboolean unlocked,
|
||||||
|
295
plugins/dell-dock/fu-dell-dock-i2c-tbt.c
Normal file
295
plugins/dell-dock/fu-dell-dock-i2c-tbt.c
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Intel Corporation.
|
||||||
|
* Copyright (C) 2019 Dell Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This software and associated documentation (if any) is furnished
|
||||||
|
* under a license and may only be used or copied in accordance
|
||||||
|
* with the terms of the license.
|
||||||
|
*
|
||||||
|
* This file is provided under a dual MIT/LGPLv2 license. When using or
|
||||||
|
* redistributing this file, you may do so under either license.
|
||||||
|
* Dell Chooses the MIT license part of Dual MIT/LGPLv2 license agreement.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+ OR MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "fwupd-error.h"
|
||||||
|
#include "fu-device.h"
|
||||||
|
#include "fu-common.h"
|
||||||
|
#include "fu-common-version.h"
|
||||||
|
|
||||||
|
#include "fu-dell-dock-common.h"
|
||||||
|
|
||||||
|
#define I2C_TBT_ADDRESS 0xa2
|
||||||
|
|
||||||
|
const FuHIDI2CParameters tbt_base_settings = {
|
||||||
|
.i2cslaveaddr = I2C_TBT_ADDRESS,
|
||||||
|
.regaddrlen = 1,
|
||||||
|
.i2cspeed = I2C_SPEED_400K,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TR Device ID */
|
||||||
|
#define PID_OFFSET 0x05
|
||||||
|
#define INTEL_PID 0x15ef
|
||||||
|
|
||||||
|
/* earlier versions have bugs */
|
||||||
|
#define MIN_NVM "36.01"
|
||||||
|
|
||||||
|
struct _FuDellDockTbt {
|
||||||
|
FuDevice parent_instance;
|
||||||
|
FuDevice *symbiote;
|
||||||
|
guint8 unlock_target;
|
||||||
|
guint64 blob_major_offset;
|
||||||
|
guint64 blob_minor_offset;
|
||||||
|
gchar *hub_minimum_version;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (FuDellDockTbt, fu_dell_dock_tbt, FU_TYPE_DEVICE)
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_dell_dock_tbt_write_fw (FuDevice *device,
|
||||||
|
GBytes *blob_fw,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuDellDockTbt *self = FU_DELL_DOCK_TBT (device);
|
||||||
|
guint32 start_offset = 0;
|
||||||
|
gsize image_size;
|
||||||
|
const guint8 *buffer = g_bytes_get_data (blob_fw, &image_size);
|
||||||
|
guint16 target_system = 0;
|
||||||
|
g_autoptr(GTimer) timer = g_timer_new ();
|
||||||
|
g_autofree gchar *dynamic_version = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail (device != NULL, FALSE);
|
||||||
|
g_return_val_if_fail (blob_fw != NULL, FALSE);
|
||||||
|
|
||||||
|
dynamic_version = g_strdup_printf ("%02x.%02x",
|
||||||
|
buffer[self->blob_major_offset],
|
||||||
|
buffer[self->blob_minor_offset]);
|
||||||
|
g_debug ("writing Thunderbolt firmware version %s", dynamic_version);
|
||||||
|
g_debug ("Total Image size: %" G_GSIZE_FORMAT, image_size);
|
||||||
|
|
||||||
|
memcpy (&start_offset, buffer, sizeof (guint32));
|
||||||
|
g_debug ("Header size 0x%x", start_offset);
|
||||||
|
if (start_offset > image_size) {
|
||||||
|
g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE,
|
||||||
|
"Image header is too big (0x%x)",
|
||||||
|
start_offset);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy (&target_system, buffer + start_offset + PID_OFFSET, sizeof (guint16));
|
||||||
|
if (target_system != INTEL_PID) {
|
||||||
|
g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE,
|
||||||
|
"Image is not intended for this system (0x%x)",
|
||||||
|
target_system);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer += start_offset;
|
||||||
|
image_size -= start_offset;
|
||||||
|
|
||||||
|
g_debug ("waking Thunderbolt controller");
|
||||||
|
if (!fu_dell_dock_hid_tbt_wake (self->symbiote, &tbt_base_settings, error))
|
||||||
|
return FALSE;
|
||||||
|
g_usleep (2000000);
|
||||||
|
|
||||||
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);
|
||||||
|
for (guint i = 0; i < image_size; i+= HIDI2C_MAX_WRITE, buffer += HIDI2C_MAX_WRITE) {
|
||||||
|
guint8 write_size = (image_size - i) > HIDI2C_MAX_WRITE ?
|
||||||
|
HIDI2C_MAX_WRITE : (image_size - i);
|
||||||
|
|
||||||
|
if (!fu_dell_dock_hid_tbt_write (self->symbiote,
|
||||||
|
i,
|
||||||
|
buffer,
|
||||||
|
write_size,
|
||||||
|
&tbt_base_settings,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
fu_device_set_progress_full (device, i, image_size);
|
||||||
|
}
|
||||||
|
g_debug ("writing took %f seconds",
|
||||||
|
g_timer_elapsed (timer, NULL));
|
||||||
|
|
||||||
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_BUSY);
|
||||||
|
|
||||||
|
if (fu_device_has_custom_flag (device, "skip-restart")) {
|
||||||
|
g_debug ("skipping TBT reset per quirk request");
|
||||||
|
} else if (!fu_dell_dock_hid_tbt_authenticate (self->symbiote,
|
||||||
|
&tbt_base_settings,
|
||||||
|
error)) {
|
||||||
|
g_prefix_error (error, "failed to authenticate: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dock will reboot to re-read; this is to appease the daemon */
|
||||||
|
fu_device_set_version (device, dynamic_version);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_dell_dock_tbt_set_quirk_kv (FuDevice *device,
|
||||||
|
const gchar *key,
|
||||||
|
const gchar *value,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuDellDockTbt *self = FU_DELL_DOCK_TBT (device);
|
||||||
|
|
||||||
|
if (g_strcmp0 (key, "DellDockUnlockTarget") == 0) {
|
||||||
|
guint64 tmp = fu_common_strtoull (value);
|
||||||
|
if (tmp < G_MAXUINT8) {
|
||||||
|
self->unlock_target = tmp;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"invalid DellDockUnlockTarget");
|
||||||
|
return FALSE;
|
||||||
|
} else if (g_strcmp0 (key, "DellDockInstallDurationI2C") == 0) {
|
||||||
|
guint64 tmp = fu_common_strtoull (value);
|
||||||
|
fu_device_set_install_duration (device, tmp);
|
||||||
|
return TRUE;
|
||||||
|
} else if (g_strcmp0 (key, "DellDockHubVersionLowest") == 0) {
|
||||||
|
self->hub_minimum_version = g_strdup (value);
|
||||||
|
return TRUE;
|
||||||
|
} else if (g_strcmp0 (key, "DellDockBlobMajorOffset") == 0) {
|
||||||
|
self->blob_major_offset = fu_common_strtoull (value);
|
||||||
|
return TRUE;
|
||||||
|
} else if (g_strcmp0 (key, "DellDockBlobMinorOffset") == 0) {
|
||||||
|
self->blob_minor_offset = fu_common_strtoull (value);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
/* failed */
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"quirk key not supported");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_dell_dock_tbt_setup (FuDevice *device, GError **error)
|
||||||
|
{
|
||||||
|
FuDellDockTbt *self = FU_DELL_DOCK_TBT (device);
|
||||||
|
FuDevice *parent;
|
||||||
|
const gchar *version;
|
||||||
|
const gchar *hub_version;
|
||||||
|
|
||||||
|
/* set version from EC if we know it */
|
||||||
|
parent = fu_device_get_parent (device);
|
||||||
|
version = fu_dell_dock_ec_get_tbt_version (parent);
|
||||||
|
|
||||||
|
if (version != NULL)
|
||||||
|
fu_device_set_version (device, version);
|
||||||
|
|
||||||
|
/* minimum version of NVM that supports this feature */
|
||||||
|
if (version == NULL || fu_common_vercmp (version, MIN_NVM) < 0) {
|
||||||
|
fu_device_set_update_error (device,
|
||||||
|
"Updates over I2C are disabled due to insuffient NVM version");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
/* minimum Hub2 version that supports this feature */
|
||||||
|
hub_version = fu_device_get_version (self->symbiote);
|
||||||
|
if (fu_common_vercmp (hub_version, self->hub_minimum_version) < 0) {
|
||||||
|
fu_device_set_update_error (device,
|
||||||
|
"Updates over I2C are disabled due to insufficient USB 3.1 G2 hub version");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_dell_dock_tbt_probe (FuDevice *device, GError **error)
|
||||||
|
{
|
||||||
|
FuDevice *parent = fu_device_get_parent (device);
|
||||||
|
fu_device_set_physical_id (device, fu_device_get_physical_id (parent));
|
||||||
|
fu_device_set_logical_id (FU_DEVICE (device), "tbt");
|
||||||
|
fu_device_add_guid (device, DELL_DOCK_TBT_GUID);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_dell_dock_tbt_open (FuDevice *device, GError **error)
|
||||||
|
{
|
||||||
|
FuDellDockTbt *self = FU_DELL_DOCK_TBT (device);
|
||||||
|
FuDevice *parent;
|
||||||
|
|
||||||
|
g_return_val_if_fail (self->unlock_target != 0, FALSE);
|
||||||
|
|
||||||
|
parent = fu_device_get_parent (device);
|
||||||
|
if (parent == NULL) {
|
||||||
|
g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no parent");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->symbiote == NULL)
|
||||||
|
self->symbiote = g_object_ref (fu_dell_dock_ec_get_symbiote (parent));
|
||||||
|
|
||||||
|
if (!fu_device_open (self->symbiote, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* adjust to access controller */
|
||||||
|
if (!fu_dell_dock_set_power (device, self->unlock_target, TRUE, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_dell_dock_tbt_close (FuDevice *device, GError **error)
|
||||||
|
{
|
||||||
|
FuDellDockTbt *self = FU_DELL_DOCK_TBT (device);
|
||||||
|
|
||||||
|
/* adjust to access controller */
|
||||||
|
if (!fu_dell_dock_set_power (device, self->unlock_target, FALSE, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return fu_device_close (self->symbiote, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_dell_dock_tbt_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
FuDellDockTbt *self = FU_DELL_DOCK_TBT (object);
|
||||||
|
g_object_unref (self->symbiote);
|
||||||
|
g_free (self->hub_minimum_version);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (fu_dell_dock_tbt_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_dell_dock_tbt_init (FuDellDockTbt *device)
|
||||||
|
{}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_dell_dock_tbt_class_init (FuDellDockTbtClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
|
||||||
|
object_class->finalize = fu_dell_dock_tbt_finalize;
|
||||||
|
klass_device->probe = fu_dell_dock_tbt_probe;
|
||||||
|
klass_device->setup = fu_dell_dock_tbt_setup;
|
||||||
|
klass_device->open = fu_dell_dock_tbt_open;
|
||||||
|
klass_device->close = fu_dell_dock_tbt_close;
|
||||||
|
klass_device->write_firmware = fu_dell_dock_tbt_write_fw;
|
||||||
|
klass_device->set_quirk_kv = fu_dell_dock_tbt_set_quirk_kv;
|
||||||
|
}
|
||||||
|
|
||||||
|
FuDellDockTbt *
|
||||||
|
fu_dell_dock_tbt_new (void)
|
||||||
|
{
|
||||||
|
FuDellDockTbt *device = NULL;
|
||||||
|
device = g_object_new (FU_TYPE_DELL_DOCK_TBT, NULL);
|
||||||
|
return device;
|
||||||
|
}
|
34
plugins/dell-dock/fu-dell-dock-i2c-tbt.h
Normal file
34
plugins/dell-dock/fu-dell-dock-i2c-tbt.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Intel Corporation.
|
||||||
|
* Copyright (C) 2019 Dell Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This software and associated documentation (if any) is furnished
|
||||||
|
* under a license and may only be used or copied in accordance
|
||||||
|
* with the terms of the license.
|
||||||
|
*
|
||||||
|
* This file is provided under a dual MIT/LGPLv2 license. When using or
|
||||||
|
* redistributing this file, you may do so under either license.
|
||||||
|
* Dell Chooses the MIT license part of Dual MIT/LGPLv2 license agreement.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+ OR MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __FU_DELLDOCK_I2C_TBT_H
|
||||||
|
#define __FU_DELLDOCK_I2C_TBT_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "fu-device.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define FU_TYPE_DELL_DOCK_TBT (fu_dell_dock_tbt_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (FuDellDockTbt, fu_dell_dock_tbt, FU, DELL_DOCK_TBT, FuDevice)
|
||||||
|
|
||||||
|
FuDellDockTbt *fu_dell_dock_tbt_new (void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* __FU_DELLDOCK_I2C_TBT_H */
|
@ -68,6 +68,16 @@ fu_plugin_dell_dock_probe (FuPlugin *plugin,
|
|||||||
error))
|
error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
/* create TBT endpoint if Thunderbolt SKU and Thunderbolt link inactive */
|
||||||
|
if (fu_dell_dock_ec_needs_tbt (FU_DEVICE (ec_device))) {
|
||||||
|
g_autoptr(FuDellDockTbt) tbt_device = fu_dell_dock_tbt_new ();
|
||||||
|
fu_device_add_child (FU_DEVICE (ec_device), FU_DEVICE (tbt_device));
|
||||||
|
if (!fu_plugin_dell_dock_create_node (plugin,
|
||||||
|
FU_DEVICE (tbt_device),
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ shared_module('fu_plugin_dell_dock',
|
|||||||
'fu-dell-dock-status.c',
|
'fu-dell-dock-status.c',
|
||||||
'fu-dell-dock-i2c-ec.c',
|
'fu-dell-dock-i2c-ec.c',
|
||||||
'fu-dell-dock-hub.c',
|
'fu-dell-dock-hub.c',
|
||||||
|
'fu-dell-dock-i2c-tbt.c',
|
||||||
'fu-dell-dock-i2c-mst.c'
|
'fu-dell-dock-i2c-mst.c'
|
||||||
],
|
],
|
||||||
include_directories : [
|
include_directories : [
|
||||||
|
@ -590,6 +590,9 @@ fu_plugin_init (FuPlugin *plugin)
|
|||||||
data->udev = g_udev_client_new (subsystems);
|
data->udev = g_udev_client_new (subsystems);
|
||||||
g_signal_connect (data->udev, "uevent",
|
g_signal_connect (data->udev, "uevent",
|
||||||
G_CALLBACK (udev_uevent_cb), plugin);
|
G_CALLBACK (udev_uevent_cb), plugin);
|
||||||
|
|
||||||
|
/* dell-dock plugin uses a slower bus for flashing */
|
||||||
|
fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_BETTER_THAN, "dell_dock");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
Loading…
Reference in New Issue
Block a user