From d6e453b0ba46f0a0826a07f7a77600ea919cd2e8 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 11 Jan 2019 08:42:52 -0600 Subject: [PATCH] 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. --- plugins/dell-dock/dell-dock.quirk | 5 + plugins/dell-dock/fu-dell-dock-common.h | 1 + plugins/dell-dock/fu-dell-dock-hid.c | 160 +++++++++++ plugins/dell-dock/fu-dell-dock-hid.h | 15 + plugins/dell-dock/fu-dell-dock-i2c-ec.c | 26 +- plugins/dell-dock/fu-dell-dock-i2c-ec.h | 2 +- plugins/dell-dock/fu-dell-dock-i2c-tbt.c | 295 ++++++++++++++++++++ plugins/dell-dock/fu-dell-dock-i2c-tbt.h | 34 +++ plugins/dell-dock/fu-plugin-dell-dock.c | 10 + plugins/dell-dock/meson.build | 1 + plugins/thunderbolt/fu-plugin-thunderbolt.c | 3 + 11 files changed, 548 insertions(+), 4 deletions(-) create mode 100644 plugins/dell-dock/fu-dell-dock-i2c-tbt.c create mode 100644 plugins/dell-dock/fu-dell-dock-i2c-tbt.h diff --git a/plugins/dell-dock/dell-dock.quirk b/plugins/dell-dock/dell-dock.quirk index 199d430f7..2bd2f23c9 100644 --- a/plugins/dell-dock/dell-dock.quirk +++ b/plugins/dell-dock/dell-dock.quirk @@ -114,3 +114,8 @@ FirmwareSizeMin=0x40000 FirmwareSizeMax=0x80000 Flags = require-ac InstallDuration = 22 +DellDockInstallDurationI2C = 181 +DellDockUnlockTarget = 10 +DellDockHubVersionLowest = 1.31 +DellDockBlobMajorOffset = 0x400a +DellDockBlobMinorOffset = 0x4009 diff --git a/plugins/dell-dock/fu-dell-dock-common.h b/plugins/dell-dock/fu-dell-dock-common.h index 8f132b5a8..d809b0d43 100644 --- a/plugins/dell-dock/fu-dell-dock-common.h +++ b/plugins/dell-dock/fu-dell-dock-common.h @@ -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" diff --git a/plugins/dell-dock/fu-dell-dock-hid.c b/plugins/dell-dock/fu-dell-dock-hid.c index 1dff057af..8cb17367a 100644 --- a/plugins/dell-dock/fu-dell-dock-hid.c +++ b/plugins/dell-dock/fu-dell-dock-hid.c @@ -17,6 +17,7 @@ #include "config.h" #include +#include #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; +} diff --git a/plugins/dell-dock/fu-dell-dock-hid.h b/plugins/dell-dock/fu-dell-dock-hid.h index c732f5992..f69936820 100644 --- a/plugins/dell-dock/fu-dell-dock-hid.h +++ b/plugins/dell-dock/fu-dell-dock-hid.h @@ -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 */ diff --git a/plugins/dell-dock/fu-dell-dock-i2c-ec.c b/plugins/dell-dock/fu-dell-dock-i2c-ec.c index 5c3f95e67..c71266371 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-ec.c +++ b/plugins/dell-dock/fu-dell-dock-i2c-ec.c @@ -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<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; } diff --git a/plugins/dell-dock/fu-dell-dock-i2c-ec.h b/plugins/dell-dock/fu-dell-dock-i2c-ec.h index a76bcff52..123976946 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-ec.h +++ b/plugins/dell-dock/fu-dell-dock-i2c-ec.h @@ -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, diff --git a/plugins/dell-dock/fu-dell-dock-i2c-tbt.c b/plugins/dell-dock/fu-dell-dock-i2c-tbt.c new file mode 100644 index 000000000..e8696275c --- /dev/null +++ b/plugins/dell-dock/fu-dell-dock-i2c-tbt.c @@ -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 + +#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; +} diff --git a/plugins/dell-dock/fu-dell-dock-i2c-tbt.h b/plugins/dell-dock/fu-dell-dock-i2c-tbt.h new file mode 100644 index 000000000..62f91c0bd --- /dev/null +++ b/plugins/dell-dock/fu-dell-dock-i2c-tbt.h @@ -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 */ diff --git a/plugins/dell-dock/fu-plugin-dell-dock.c b/plugins/dell-dock/fu-plugin-dell-dock.c index 6992add1a..9576f412b 100644 --- a/plugins/dell-dock/fu-plugin-dell-dock.c +++ b/plugins/dell-dock/fu-plugin-dell-dock.c @@ -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; } diff --git a/plugins/dell-dock/meson.build b/plugins/dell-dock/meson.build index 9e719680a..0f00003d3 100644 --- a/plugins/dell-dock/meson.build +++ b/plugins/dell-dock/meson.build @@ -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 : [ diff --git a/plugins/thunderbolt/fu-plugin-thunderbolt.c b/plugins/thunderbolt/fu-plugin-thunderbolt.c index 3fa0613ce..81d545447 100644 --- a/plugins/thunderbolt/fu-plugin-thunderbolt.c +++ b/plugins/thunderbolt/fu-plugin-thunderbolt.c @@ -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