mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-06 14:32:56 +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
|
||||
Flags = require-ac
|
||||
InstallDuration = 22
|
||||
DellDockInstallDurationI2C = 181
|
||||
DellDockUnlockTarget = 10
|
||||
DellDockHubVersionLowest = 1.31
|
||||
DellDockBlobMajorOffset = 0x400a
|
||||
DellDockBlobMinorOffset = 0x4009
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "fu-device.h"
|
||||
#include "fu-dell-dock-i2c-ec.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-hid.h"
|
||||
#include "fu-dell-dock-status.h"
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "fu-usb-device.h"
|
||||
#include "fwupd-error.h"
|
||||
@ -25,6 +26,7 @@
|
||||
|
||||
#define HIDI2C_MAX_REGISTER 4
|
||||
#define HID_MAX_RETRIES 5
|
||||
#define TBT_MAX_RETRIES 2
|
||||
#define HIDI2C_TRANSACTION_TIMEOUT 2000
|
||||
|
||||
#define HUB_CMD_READ_DATA 0xC0
|
||||
@ -36,6 +38,11 @@
|
||||
#define HUB_EXT_I2C_READ 0xD6
|
||||
#define HUB_EXT_VERIFYUPDATE 0xD9
|
||||
#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)) {
|
||||
guint8 cmd;
|
||||
@ -55,6 +62,20 @@ typedef struct __attribute__ ((packed)) {
|
||||
guint8 data[192];
|
||||
} 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
|
||||
fu_dell_dock_hid_set_report (FuDevice *self,
|
||||
guint8 *outbuffer,
|
||||
@ -374,3 +395,142 @@ fu_dell_dock_hid_i2c_read (FuDevice *self,
|
||||
|
||||
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,
|
||||
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 */
|
||||
|
@ -37,6 +37,11 @@
|
||||
#define EXPECTED_DOCK_INFO_SIZE 0xb7
|
||||
#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 {
|
||||
FW_UPDATE_IN_PROGRESS,
|
||||
FW_UPDATE_COMPLETE,
|
||||
@ -130,6 +135,7 @@ struct _FuDellDockEc {
|
||||
guint8 board_min;
|
||||
gchar *ec_minimum_version;
|
||||
guint64 blob_version_offset;
|
||||
guint32 dock_unlock_status;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (FuDellDockEc, fu_dell_dock_ec, FU_TYPE_DEVICE)
|
||||
@ -165,11 +171,17 @@ fu_dell_dock_ec_get_symbiote (FuDevice *device)
|
||||
}
|
||||
|
||||
gboolean
|
||||
fu_dell_dock_ec_has_tbt (FuDevice *device)
|
||||
fu_dell_dock_ec_needs_tbt (FuDevice *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*
|
||||
@ -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[3]);
|
||||
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 */
|
||||
if (!fu_dell_dock_test_valid_byte (device_entry[i].version.version_8, 2)) {
|
||||
g_warning ("[EC bug] EC read invalid Thunderbolt version %08x",
|
||||
@ -504,6 +517,7 @@ fu_dell_dock_ec_modify_lock (FuDevice *device,
|
||||
gboolean unlocked,
|
||||
GError **error)
|
||||
{
|
||||
FuDellDockEc *self = FU_DELL_DOCK_EC (device);
|
||||
guint32 cmd;
|
||||
|
||||
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_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;
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ FuDellDockEc *fu_dell_dock_ec_new (FuDevice *symbiote);
|
||||
|
||||
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,
|
||||
guint8 target,
|
||||
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))
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ shared_module('fu_plugin_dell_dock',
|
||||
'fu-dell-dock-status.c',
|
||||
'fu-dell-dock-i2c-ec.c',
|
||||
'fu-dell-dock-hub.c',
|
||||
'fu-dell-dock-i2c-tbt.c',
|
||||
'fu-dell-dock-i2c-mst.c'
|
||||
],
|
||||
include_directories : [
|
||||
|
@ -590,6 +590,9 @@ fu_plugin_init (FuPlugin *plugin)
|
||||
data->udev = g_udev_client_new (subsystems);
|
||||
g_signal_connect (data->udev, "uevent",
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user