/* * Copyright (C) 2018 Synaptics * Copyright (C) 2018 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 "fu-common.h" #include "fu-dell-dock-common.h" #define I2C_MST_ADDRESS 0x72 /* MST registers */ #define MST_RC_TRIGGER_ADDR 0x2000fc #define MST_CORE_MCU_BOOTLOADER_STS 0x20010c #define MST_RC_COMMAND_ADDR 0x200110 #define MST_RC_OFFSET_ADDR 0x200114 #define MST_RC_LENGTH_ADDR 0x200118 #define MST_RC_DATA_ADDR 0x200120 #define MST_CORE_MCU_FW_VERSION 0x200160 #define MST_REG_QUAD_DISABLE 0x200fc0 #define MST_REG_HDCP22_DISABLE 0x200f90 /* MST remote control commands */ #define MST_CMD_ENABLE_REMOTE_CONTROL 0x1 #define MST_CMD_DISABLE_REMOTE_CONTROL 0x2 #define MST_CMD_CHECKSUM 0x11 #define MST_CMD_ERASE_FLASH 0x14 #define MST_CMD_WRITE_FLASH 0x20 #define MST_CMD_READ_FLASH 0x30 #define MST_CMD_WRITE_MEMORY 0x21 #define MST_CMD_READ_MEMORY 0x31 /* Arguments related to flashing */ #define FLASH_SECTOR_ERASE_4K 0x1000 #define FLASH_SECTOR_ERASE_32K 0x2000 #define FLASH_SECTOR_ERASE_64K 0x3000 #define EEPROM_TAG_OFFSET 0x1fff0 #define EEPROM_BANK_OFFSET 0x20000 #define EEPROM_ESM_OFFSET 0x40000 /* Flash offsets */ #define MST_BOARDID_OFFSET 0x10e /* Remote control offsets */ #define MST_CHIPID_OFFSET 0x1500 /* magic triggers */ #define MST_TRIGGER_WRITE 0xf2 #define MST_TRIGGER_REBOOT 0xf5 /* IDs used in DELL_DOCK */ #define EXPECTED_CHIPID 0x5331 /* firmware file offsets */ #define MST_BLOB_VERSION_OFFSET 0x06F0 typedef enum { Bank0, Bank1, ESM, } MSTBank; typedef struct { guint start; guint length; } MSTBankAttributes; const MSTBankAttributes bank0_attributes = { .start = 0, .length = EEPROM_BANK_OFFSET, }; const MSTBankAttributes bank1_attributes = { .start = EEPROM_BANK_OFFSET, .length = EEPROM_BANK_OFFSET, }; const MSTBankAttributes esm_attributes = { .start = EEPROM_ESM_OFFSET, .length = 0x3ffff }; FuHIDI2CParameters mst_base_settings = { .i2cslaveaddr = I2C_MST_ADDRESS, .regaddrlen = 0, .i2cspeed = I2C_SPEED_400K, }; struct _FuDellDockMst { FuDevice parent_instance; guint8 unlock_target; guint64 blob_major_offset; guint64 blob_minor_offset; guint64 blob_build_offset; }; G_DEFINE_TYPE (FuDellDockMst, fu_dell_dock_mst, FU_TYPE_DEVICE) /** * fu_dell_dock_mst_get_bank_attribs: * @bank: An MSTBank * @out (out): The MSTBankAttributes attribute that matches * @error: the #GError, or %NULL * * Returns a structure that corresponds to the attributes for a bank * * Returns: %TRUE for success **/ static gboolean fu_dell_dock_mst_get_bank_attribs (MSTBank bank, const MSTBankAttributes **out, GError **error) { switch (bank) { case Bank0: *out = &bank0_attributes; break; case Bank1: *out = &bank1_attributes; break; case ESM: *out = &esm_attributes; break; default: g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Invalid bank specified %u", bank); return FALSE; } return TRUE; } static gboolean fu_dell_dock_mst_rc_command (FuDevice *proxy, guint8 cmd, guint32 length, guint32 offset, const guint8 *data, GError **error); static gboolean fu_dell_dock_mst_read_register (FuDevice *proxy, guint32 address, gsize length, GBytes **bytes, GError **error) { g_return_val_if_fail (proxy != NULL, FALSE); g_return_val_if_fail (bytes != NULL, FALSE); g_return_val_if_fail (length <= 32, FALSE); /* write the offset we're querying */ if (!fu_dell_dock_hid_i2c_write (proxy, (guint8 *) &address, 4, &mst_base_settings, error)) return FALSE; /* read data for the result */ if (!fu_dell_dock_hid_i2c_read (proxy, 0, length, bytes, &mst_base_settings, error)) return FALSE; return TRUE; } static gboolean fu_dell_dock_mst_write_register (FuDevice *proxy, guint32 address, guint8 *data, gsize length, GError **error) { g_autofree guint8 *buffer = g_malloc0 (length + 4); g_return_val_if_fail (proxy != NULL, FALSE); g_return_val_if_fail (data != NULL, FALSE); memcpy (buffer, &address, 4); memcpy (buffer + 4, data, length); /* write the offset we're querying */ return fu_dell_dock_hid_i2c_write (proxy, buffer, length + 4, &mst_base_settings, error); } static gboolean fu_dell_dock_mst_query_active_bank (FuDevice *proxy, MSTBank *active, GError **error) { g_autoptr(GBytes) bytes = NULL; const guint32 *data = NULL; gsize length = 4; if (!fu_dell_dock_mst_read_register (proxy, MST_CORE_MCU_BOOTLOADER_STS, length, &bytes, error)) { g_prefix_error (error, "Failed to query active bank: "); return FALSE; } data = g_bytes_get_data (bytes, &length); if ((data[0] & (1 << 7)) || (data[0] & (1 << 30))) *active = Bank1; else *active = Bank0; g_debug ("MST: active bank is: %u", *active); return TRUE; } static gboolean fu_dell_dock_mst_disable_remote_control (FuDevice *proxy, GError **error) { g_debug ("MST: Disabling remote control"); return fu_dell_dock_mst_rc_command (proxy, MST_CMD_DISABLE_REMOTE_CONTROL, 0, 0, NULL, error); } static gboolean fu_dell_dock_mst_enable_remote_control (FuDevice *proxy, GError **error) { g_autoptr(GError) error_local = NULL; const gchar *data = "PRIUS"; g_debug ("MST: Enabling remote control"); if (!fu_dell_dock_mst_rc_command (proxy, MST_CMD_ENABLE_REMOTE_CONTROL, 5, 0, (guint8 *) data, &error_local)) { g_debug ("Failed to enable remote control: %s", error_local->message); /* try to disable / re-enable */ if (!fu_dell_dock_mst_disable_remote_control (proxy, error)) return FALSE; return fu_dell_dock_mst_enable_remote_control (proxy, error); } return TRUE; } static gboolean fu_dell_dock_trigger_rc_command (FuDevice *proxy, GError **error) { const guint8 *result = NULL; guint32 tmp; /* Trigger the write */ tmp = MST_TRIGGER_WRITE; if (!fu_dell_dock_mst_write_register (proxy, MST_RC_TRIGGER_ADDR, (guint8 *) &tmp, sizeof(guint32), error)) { g_prefix_error (error, "Failed to write MST_RC_TRIGGER_ADDR: "); return FALSE; } /* poll for completion */ tmp = 0xffff; for (guint i = 0; i < 1000; i++) { g_autoptr(GBytes) bytes = NULL; if (!fu_dell_dock_mst_read_register (proxy, MST_RC_COMMAND_ADDR, sizeof(guint32), &bytes, error)) { g_prefix_error (error, "Failed to poll MST_RC_COMMAND_ADDR"); return FALSE; } result = g_bytes_get_data (bytes, NULL); /* complete */ if ((result[2] & 0x80) == 0) { tmp = result[3]; break; } g_usleep (2000); } switch (tmp) { /* need to enable remote control */ case 4: return fu_dell_dock_mst_enable_remote_control (proxy, error); /* error scenarios */ case 3: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown error"); return FALSE; case 2: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unsupported command"); return FALSE; case 1: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid argument"); return FALSE; /* success scenario */ case 0: return TRUE; default: g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Command timed out or unknown failure: %x", tmp); return FALSE; } } static gboolean fu_dell_dock_mst_rc_command (FuDevice *proxy, guint8 cmd, guint32 length, guint32 offset, const guint8 *data, GError **error) { /* 4 for cmd, 4 for offset, 4 for length, 4 for garbage */ gint buffer_len = (data == NULL) ? 12 : length + 16; g_autofree guint8 *buffer = g_malloc0 (buffer_len); guint32 tmp; g_return_val_if_fail (proxy != NULL, FALSE); /* command */ tmp = (cmd | 0x80) << 16; memcpy (buffer, &tmp, 4); /* offset */ memcpy (buffer + 4, &offset, 4); /* length */ memcpy (buffer + 8, &length, 4); /* data */ if (data != NULL) memcpy (buffer + 16, data, length); /* write the combined register stream */ if (!fu_dell_dock_mst_write_register (proxy, MST_RC_COMMAND_ADDR, buffer, buffer_len, error)) return FALSE; return fu_dell_dock_trigger_rc_command (proxy, error); } static gboolean fu_dell_dock_mst_read_chipid (FuDevice *proxy, guint16 *chip_id, GError **error) { g_autoptr(GBytes) bytes = NULL; const guint8 *data; gsize length = 4; g_return_val_if_fail (chip_id != NULL, FALSE); /* run an RC command to get data from memory */ if (!fu_dell_dock_mst_rc_command (proxy, MST_CMD_READ_MEMORY, length, MST_CHIPID_OFFSET, NULL, error)) return FALSE; if (!fu_dell_dock_mst_read_register (proxy, MST_RC_DATA_ADDR, length, &bytes, error)) return FALSE; data = g_bytes_get_data (bytes, &length); *chip_id = (data[1] << 8) | data[2]; return TRUE; } static gboolean fu_dell_dock_mst_check_offset (guint8 byte, guint8 offset) { if ((byte & offset) != 0) return TRUE; return FALSE; } static gboolean fu_d19_mst_check_fw (FuDevice *proxy, GError **error) { g_autoptr(GBytes) bytes = NULL; const guint8 *data; gsize length = 4; if (!fu_dell_dock_mst_read_register (proxy, MST_CORE_MCU_BOOTLOADER_STS, length, &bytes, error)) return FALSE; data = g_bytes_get_data (bytes, &length); g_debug ("MST: firmware check: %d", fu_dell_dock_mst_check_offset (data[0], 0x01)); g_debug ("MST: HDCP key check: %d", fu_dell_dock_mst_check_offset (data[0], 0x02)); g_debug ("MST: Config0 check: %d", fu_dell_dock_mst_check_offset (data[0], 0x04)); g_debug ("MST: Config1 check: %d", fu_dell_dock_mst_check_offset (data[0], 0x08)); if (fu_dell_dock_mst_check_offset (data[0], 0xF0)) g_debug ("MST: running in bootloader"); else g_debug ("MST: running in firmware"); g_debug ("MST: Error code: %x", data[1]); g_debug ("MST: GPIO boot strap record: %d", data[2]); g_debug ("MST: Bootloader version number %x", data[3]); return TRUE; } static gboolean fu_dell_dock_mst_checksum_bank (FuDevice *proxy, GBytes *blob_fw, MSTBank bank, gboolean *checksum, GError **error) { g_autoptr(GBytes) csum_bytes = NULL; const MSTBankAttributes *attribs = NULL; gsize length = 0; const guint8 *data = g_bytes_get_data (blob_fw, &length); guint32 payload_sum = 0; guint32 bank_sum = 0; g_return_val_if_fail (blob_fw != NULL, FALSE); g_return_val_if_fail (checksum != NULL, FALSE); if (!fu_dell_dock_mst_get_bank_attribs (bank, &attribs, error)) return FALSE; /* bank is specified outside of payload */ if (attribs->start + attribs->length > length) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Payload %u is bigger than bank %u", attribs->start + attribs->length, bank); return FALSE; } /* checksum the file */ for (guint i = attribs->start; i < attribs->length + attribs->start; i++) { payload_sum += data[i]; } g_debug ("MST: Payload checksum: 0x%x", payload_sum); /* checksum the bank */ if (!fu_dell_dock_mst_rc_command (proxy, MST_CMD_CHECKSUM, attribs->length, attribs->start, NULL, error)) { g_prefix_error (error, "Failed to checksum bank %u: ", bank); return FALSE; } /* read result from data register */ if (!fu_dell_dock_mst_read_register (proxy, MST_RC_DATA_ADDR, 4, &csum_bytes, error)) return FALSE; data = g_bytes_get_data (csum_bytes, NULL); bank_sum = GUINT32_FROM_LE (data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24); g_debug ("MST: Bank %u checksum: 0x%x", bank, bank_sum); *checksum = (bank_sum == payload_sum); return TRUE; } static gboolean fu_dell_dock_mst_erase_bank (FuDevice *proxy, MSTBank bank, GError **error) { const MSTBankAttributes *attribs = NULL; guint32 sector; if (!fu_dell_dock_mst_get_bank_attribs (bank, &attribs, error)) return FALSE; for (guint32 i = attribs->start; i < attribs->start + attribs->length; i += 0x10000) { sector = FLASH_SECTOR_ERASE_64K | (i / 0x10000); g_debug ("MST: Erasing sector 0x%x", sector); if (!fu_dell_dock_mst_rc_command (proxy, MST_CMD_ERASE_FLASH, 4, 0, (guint8 *) §or, error)) { g_prefix_error ( error, "Failed to erase sector 0x%x: ", sector); return FALSE; } } g_debug ("MST: Waiting for flash clear to settle"); g_usleep (5000000); return TRUE; } static gboolean fu_dell_dock_write_flash_bank (FuDevice *device, GBytes *blob_fw, MSTBank bank, GError **error) { const MSTBankAttributes *attribs = NULL; gsize write_size = 32; guint end; const guint8 *data = g_bytes_get_data (blob_fw, NULL); g_return_val_if_fail (blob_fw != NULL, FALSE); if (!fu_dell_dock_mst_get_bank_attribs (bank, &attribs, error)) return FALSE; end = attribs->start + attribs->length; g_debug ("MST: Writing payload to bank %u", bank); for (guint i = attribs->start; i < end; i += write_size) { if (!fu_dell_dock_mst_rc_command (fu_device_get_proxy (device), MST_CMD_WRITE_FLASH, write_size, i, data + i, error)) { g_prefix_error ( error, "Failed to write bank %u payload offset 0x%x: ", bank, i); return FALSE; } fu_device_set_progress_full (device, i - attribs->start, end - attribs->start); } return TRUE; } static gboolean fu_dell_dock_mst_stop_esm (FuDevice *proxy, GError **error) { g_autoptr(GBytes) quad_bytes = NULL; g_autoptr(GBytes) hdcp_bytes = NULL; guint32 payload = 0x21; gsize length = sizeof(guint32); const guint8 *data; guint8 data_out[sizeof(guint32)]; /* disable ESM first */ if (!fu_dell_dock_mst_rc_command (proxy, MST_CMD_WRITE_MEMORY, length, MST_RC_TRIGGER_ADDR, (guint8 *) &payload, error)) return FALSE; /* waiting for ESM exit */ g_usleep(200); /* disable QUAD mode */ if (!fu_dell_dock_mst_rc_command (proxy, MST_CMD_READ_MEMORY, length, MST_REG_QUAD_DISABLE, NULL, error)) return FALSE; if (!fu_dell_dock_mst_read_register (proxy, MST_RC_DATA_ADDR, length, &quad_bytes, error)) return FALSE; data = g_bytes_get_data (quad_bytes, &length); memcpy (data_out, data, length); data_out[0] = 0x00; if (!fu_dell_dock_mst_rc_command (proxy, MST_CMD_WRITE_MEMORY, length, MST_REG_QUAD_DISABLE, data_out, error)) return FALSE; /* disable HDCP2.2 */ if (!fu_dell_dock_mst_rc_command (proxy, MST_CMD_READ_MEMORY, length, MST_REG_HDCP22_DISABLE, NULL, error)) return FALSE; if (!fu_dell_dock_mst_read_register (proxy, MST_RC_DATA_ADDR, length, &hdcp_bytes, error)) return FALSE; data = g_bytes_get_data (hdcp_bytes, &length); memcpy (data_out, data, length); data_out[0] = data[0] & (1 << 2); if (!fu_dell_dock_mst_rc_command (proxy, MST_CMD_WRITE_MEMORY, length, MST_REG_HDCP22_DISABLE, data_out, error)) return FALSE; return TRUE; } static gboolean fu_dell_dock_mst_invalidate_bank (FuDevice *proxy, MSTBank bank_in_use, GError **error) { const MSTBankAttributes *attribs; g_autoptr(GBytes) bytes = NULL; const guint8 *crc_tag; const guint8 *new_tag; guint32 crc_offset; guint retries = 2; if (!fu_dell_dock_mst_get_bank_attribs (bank_in_use, &attribs, error)) { g_prefix_error (error, "unable to invalidate bank: "); return FALSE; } /* we need to write 4 byte increments over I2C so this differs from DP aux */ crc_offset = attribs->start + EEPROM_TAG_OFFSET + 12; /* Read CRC byte to flip */ if (!fu_dell_dock_mst_rc_command (proxy, MST_CMD_READ_FLASH, 4, crc_offset, NULL, error)) { g_prefix_error (error, "failed to read tag from flash: "); return FALSE; } if (!fu_dell_dock_mst_read_register (proxy, MST_RC_DATA_ADDR, 1, &bytes, error)) { return FALSE; } crc_tag = g_bytes_get_data (bytes, NULL); g_debug ("CRC byte is currently 0x%x", crc_tag[3]); for (guint32 retries_cnt = 0; ; retries_cnt++) { g_autoptr(GBytes) bytes_new = NULL; /* CRC8 is not 0xff, erase last 4k of bank# */ if (crc_tag[3] != 0xff) { guint32 sector = FLASH_SECTOR_ERASE_4K + (attribs->start + attribs->length - 0x1000) / 0x1000; g_debug ("Erasing 4k from sector 0x%x invalidate bank %u", sector, bank_in_use); /* offset for last 4k of bank# */ if (!fu_dell_dock_mst_rc_command (proxy, MST_CMD_ERASE_FLASH, 4, 0, (guint8 *) §or, error)) { g_prefix_error (error, "failed to erase sector 0x%x: ", sector); return FALSE; } /* CRC8 is 0xff, set it to 0x00 */ } else { guint32 write = 0x00; g_debug ("Writing 0x00 byte to 0x%x to invalidate bank %u", crc_offset, bank_in_use); if (!fu_dell_dock_mst_rc_command (proxy, MST_CMD_WRITE_FLASH, 4, crc_offset, (guint8*) &write, error)) { g_prefix_error (error, "failed to clear CRC byte: "); return FALSE; } } /* re-read for comparison */ if (!fu_dell_dock_mst_rc_command (proxy, MST_CMD_READ_FLASH, 4, crc_offset, NULL, error)) { g_prefix_error (error, "failed to read tag from flash: "); return FALSE; } if (!fu_dell_dock_mst_read_register (proxy, MST_RC_DATA_ADDR, 4, &bytes_new, error)) { return FALSE; } new_tag = g_bytes_get_data (bytes_new, NULL); g_debug ("CRC byte is currently 0x%x", new_tag[3]); /* tag successfully cleared */ if ((new_tag[3] == 0xff && crc_tag[3] != 0xff) || (new_tag[3] == 0x00 && crc_tag[3] == 0xff)) { break; } if (retries_cnt > retries) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "set tag invalid fail (new 0x%x; old 0x%x)", new_tag[3], crc_tag[3]); return FALSE; } } return TRUE; } static gboolean fu_dell_dock_mst_write_fw (FuDevice *device, FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuDellDockMst *self = FU_DELL_DOCK_MST (device); MSTBank bank_in_use = 0; guint retries = 2; gboolean checksum = FALSE; guint8 order[2] = {ESM, Bank0}; guint16 chip_id; const guint8 *data; g_autofree gchar *dynamic_version = NULL; g_autoptr(GBytes) fw = NULL; g_return_val_if_fail (device != NULL, FALSE); g_return_val_if_fail (FU_IS_FIRMWARE (firmware), FALSE); g_return_val_if_fail (fu_device_get_proxy (device) != NULL, FALSE); /* get default image */ fw = fu_firmware_get_image_default_bytes (firmware, error); if (fw == NULL) return FALSE; data = g_bytes_get_data (fw, NULL); dynamic_version = g_strdup_printf ("%02x.%02x.%02x", data[self->blob_major_offset], data[self->blob_minor_offset], data[self->blob_build_offset]); g_debug ("writing MST firmware version %s", dynamic_version); /* determine the flash order */ if (!fu_dell_dock_mst_query_active_bank (fu_device_get_proxy (device), &bank_in_use, error)) return FALSE; if (bank_in_use == Bank0) order[1] = Bank1; /* enable remote control */ if (!fu_dell_dock_mst_enable_remote_control (fu_device_get_proxy (device), error)) return FALSE; /* Read Synaptics MST chip ID */ if (!fu_dell_dock_mst_read_chipid (fu_device_get_proxy (device), &chip_id, error)) return FALSE; if (chip_id != EXPECTED_CHIPID) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown MST chip found %x", chip_id); return FALSE; } /* ESM needs special handling during flash process*/ if (!fu_dell_dock_mst_stop_esm (fu_device_get_proxy (device), error)) return FALSE; /* Write each bank in order */ for (guint phase = 0; phase < 2; phase++) { g_debug ("MST: Checking bank %u", order[phase]); if (!fu_dell_dock_mst_checksum_bank (fu_device_get_proxy (device), fw, order[phase], &checksum, error)) return FALSE; if (checksum) { g_debug ("MST: bank %u is already up to date", order[phase]); continue; } g_debug ("MST: bank %u needs to be updated", order[phase]); for (guint i = 0; i < retries; i++) { fu_device_set_progress_full (device, 0, 100); fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); if (!fu_dell_dock_mst_erase_bank (fu_device_get_proxy (device), order[phase], error)) return FALSE; fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); if (!fu_dell_dock_write_flash_bank (device, fw, order[phase], error)) return FALSE; if (!fu_dell_dock_mst_checksum_bank (fu_device_get_proxy (device), fw, order[phase], &checksum, error)) return FALSE; fu_device_set_status (device, FWUPD_STATUS_DEVICE_BUSY); if (!checksum) { g_debug ( "MST: Failed to verify checksum on bank %u", order[phase]); continue; } g_debug ("MST: Bank %u successfully flashed", order[phase]); break; } /* failed after all our retries */ if (!checksum) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to write to bank %u", order[phase]); return FALSE; } } /* invalidate the previous bank */ if (!fu_dell_dock_mst_invalidate_bank (fu_device_get_proxy (device), bank_in_use, error)) { g_prefix_error (error, "failed to invalidate bank %u: ", bank_in_use); return FALSE; } /* dock will reboot to re-read; this is to appease the daemon */ fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); fu_device_set_version_format (device, FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_version (device, dynamic_version); /* disable remote control now */ return fu_dell_dock_mst_disable_remote_control (fu_device_get_proxy (device), error); } static gboolean fu_dell_dock_mst_set_quirk_kv (FuDevice *device, const gchar *key, const gchar *value, GError **error) { FuDellDockMst *self = FU_DELL_DOCK_MST (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; } if (g_strcmp0 (key, "DellDockBlobMajorOffset") == 0) { self->blob_major_offset = fu_common_strtoull (value); return TRUE; } if (g_strcmp0 (key, "DellDockBlobMinorOffset") == 0) { self->blob_minor_offset = fu_common_strtoull (value); return TRUE; } if (g_strcmp0 (key, "DellDockBlobBuildOffset") == 0) { self->blob_build_offset = fu_common_strtoull (value); return TRUE; } else if (g_strcmp0 (key, "DellDockInstallDurationI2C") == 0) { guint64 tmp = fu_common_strtoull (value); fu_device_set_install_duration (device, tmp); 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_mst_setup (FuDevice *device, GError **error) { FuDevice *parent; const gchar *version; /* sanity check that we can talk to MST */ if (!fu_d19_mst_check_fw (fu_device_get_proxy (device), error)) return FALSE; /* set version from EC if we know it */ parent = fu_device_get_parent (device); version = fu_dell_dock_ec_get_mst_version (parent); if (version != NULL) { fu_device_set_version_format (device, FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_version (device, version); } fu_dell_dock_clone_updatable (device); return TRUE; } static gboolean fu_dell_dock_mst_probe (FuDevice *device, GError **error) { fu_device_set_logical_id (FU_DEVICE (device), "mst"); return TRUE; } static gboolean fu_dell_dock_mst_open (FuDevice *device, GError **error) { FuDellDockMst *self = FU_DELL_DOCK_MST (device); FuDevice *parent = fu_device_get_parent (device); g_return_val_if_fail (self->unlock_target != 0, FALSE); g_return_val_if_fail (parent != NULL, FALSE); if (fu_device_get_proxy (device) == NULL) fu_device_set_proxy (device, fu_device_get_proxy (parent)); if (!fu_device_open (fu_device_get_proxy (device), error)) return FALSE; /* open up access to controller bus */ if (!fu_dell_dock_set_power (device, self->unlock_target, TRUE, error)) return FALSE; return TRUE; } static gboolean fu_dell_dock_mst_close (FuDevice *device, GError **error) { FuDellDockMst *self = FU_DELL_DOCK_MST (device); /* close access to controller bus */ if (!fu_dell_dock_set_power (device, self->unlock_target, FALSE, error)) return FALSE; return fu_device_close (fu_device_get_proxy (device), error); } static void fu_dell_dock_mst_init (FuDellDockMst *self) { fu_device_set_protocol (FU_DEVICE (self), "com.synaptics.mst"); } static void fu_dell_dock_mst_class_init (FuDellDockMstClass *klass) { FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); klass_device->probe = fu_dell_dock_mst_probe; klass_device->open = fu_dell_dock_mst_open; klass_device->close = fu_dell_dock_mst_close; klass_device->setup = fu_dell_dock_mst_setup; klass_device->probe = fu_dell_dock_mst_probe; klass_device->write_firmware = fu_dell_dock_mst_write_fw; klass_device->set_quirk_kv = fu_dell_dock_mst_set_quirk_kv; } FuDellDockMst * fu_dell_dock_mst_new (void) { FuDellDockMst *device = NULL; device = g_object_new (FU_TYPE_DELL_DOCK_MST, NULL); return device; }