mirror of
				https://git.proxmox.com/git/fwupd
				synced 2025-10-30 18:16:37 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			189 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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 "fu-usb-device.h"
 | |
| #include "fwupd-error.h"
 | |
| 
 | |
| #include "fu-dell-dock-common.h"
 | |
| 
 | |
| struct _FuDellDockHub {
 | |
| 	FuHidDevice			 parent_instance;
 | |
| 	guint8				 unlock_target;
 | |
| 	guint64				 blob_major_offset;
 | |
| 	guint64				 blob_minor_offset;
 | |
| };
 | |
| 
 | |
| G_DEFINE_TYPE (FuDellDockHub, fu_dell_dock_hub, FU_TYPE_HID_DEVICE)
 | |
| 
 | |
| static gboolean
 | |
| fu_dell_dock_hub_probe (FuDevice *device, GError **error)
 | |
| {
 | |
| 	g_autofree gchar *devid = NULL;
 | |
| 
 | |
| 	devid = g_strdup_printf ("USB\\VID_%04X&PID_%04X&hub",
 | |
| 			     (guint) fu_usb_device_get_vid (FU_USB_DEVICE (device)),
 | |
| 			     (guint) fu_usb_device_get_pid (FU_USB_DEVICE (device)));
 | |
| 
 | |
| 	fu_device_set_logical_id (device, "hub");
 | |
| 	fu_device_add_instance_id (device, devid);
 | |
| 	fu_device_set_protocol (device, "com.dell.dock");
 | |
| 
 | |
| 	return TRUE;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| fu_dell_dock_hub_write_fw (FuDevice *device,
 | |
| 			   FuFirmware *firmware,
 | |
| 			   FwupdInstallFlags flags,
 | |
| 			   GError **error)
 | |
| {
 | |
| 	FuDellDockHub *self = FU_DELL_DOCK_HUB (device);
 | |
| 	gsize fw_size = 0;
 | |
| 	const guint8 *data;
 | |
| 	gsize write_size;
 | |
| 	gsize nwritten = 0;
 | |
| 	guint32 address = 0;
 | |
| 	gboolean result = FALSE;
 | |
| 	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);
 | |
| 
 | |
| 	/* get default image */
 | |
| 	fw = fu_firmware_get_image_default_bytes (firmware, error);
 | |
| 	if (fw == NULL)
 | |
| 		return FALSE;
 | |
| 	data = g_bytes_get_data (fw, &fw_size);
 | |
| 	write_size = (fw_size / HIDI2C_MAX_WRITE) >= 1 ?
 | |
| 				 HIDI2C_MAX_WRITE : fw_size;
 | |
| 
 | |
| 	dynamic_version = g_strdup_printf ("%02x.%02x",
 | |
| 					   data[self->blob_major_offset],
 | |
| 					   data[self->blob_minor_offset]);
 | |
| 	g_debug ("writing hub firmware version %s", dynamic_version);
 | |
| 
 | |
| 	if (!fu_dell_dock_set_power (device, self->unlock_target, TRUE, error))
 | |
| 		return FALSE;
 | |
| 
 | |
| 	if (!fu_dell_dock_hid_raise_mcu_clock (device, TRUE, error))
 | |
| 		return FALSE;
 | |
| 
 | |
| 	fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE);
 | |
| 	if (!fu_dell_dock_hid_erase_bank (device, 1, error))
 | |
| 		return FALSE;
 | |
| 
 | |
| 	fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);
 | |
| 	do {
 | |
| 		/* last packet */
 | |
| 		if (fw_size - nwritten < write_size)
 | |
| 			write_size = fw_size - nwritten;
 | |
| 
 | |
| 		if (!fu_dell_dock_hid_write_flash (device, address, data,
 | |
| 						   write_size, error))
 | |
| 			return FALSE;
 | |
| 		nwritten += write_size;
 | |
| 		data += write_size;
 | |
| 		address += write_size;
 | |
| 		fu_device_set_progress_full (device, nwritten, fw_size);
 | |
| 	} while (nwritten < fw_size);
 | |
| 
 | |
| 	fu_device_set_status (device, FWUPD_STATUS_DEVICE_BUSY);
 | |
| 	if (!fu_dell_dock_hid_verify_update (device, &result, error))
 | |
| 		return FALSE;
 | |
| 	if (!result) {
 | |
| 		g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL,
 | |
| 				     "Failed to verify the update");
 | |
| 		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_PAIR);
 | |
| 	fu_device_set_version (device, dynamic_version);
 | |
| 	return TRUE;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| fu_dell_dock_hub_set_quirk_kv (FuDevice *device,
 | |
| 			       const gchar *key,
 | |
| 			       const gchar *value,
 | |
| 			       GError **error)
 | |
| {
 | |
| 	FuDellDockHub *self = FU_DELL_DOCK_HUB (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;
 | |
| 	}
 | |
| 
 | |
| 	/* failed */
 | |
| 	g_set_error_literal (error,
 | |
| 			     G_IO_ERROR,
 | |
| 			     G_IO_ERROR_NOT_SUPPORTED,
 | |
| 			     "quirk key not supported");
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| fu_dell_dock_hub_finalize (GObject *object)
 | |
| {
 | |
| 	G_OBJECT_CLASS (fu_dell_dock_hub_parent_class)->finalize (object);
 | |
| }
 | |
| 
 | |
| static void
 | |
| fu_dell_dock_hub_init (FuDellDockHub *self)
 | |
| {
 | |
| 	fu_device_retry_set_delay (FU_DEVICE (self), 1000);
 | |
| }
 | |
| 
 | |
| static void
 | |
| fu_dell_dock_hub_class_init (FuDellDockHubClass *klass)
 | |
| {
 | |
| 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 | |
| 	FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
 | |
| 	object_class->finalize = fu_dell_dock_hub_finalize;
 | |
| 	klass_device->setup = fu_dell_dock_hid_get_hub_version;
 | |
| 	klass_device->probe = fu_dell_dock_hub_probe;
 | |
| 	klass_device->write_firmware = fu_dell_dock_hub_write_fw;
 | |
| 	klass_device->set_quirk_kv = fu_dell_dock_hub_set_quirk_kv;
 | |
| }
 | |
| 
 | |
| FuDellDockHub *
 | |
| fu_dell_dock_hub_new (FuUsbDevice *device)
 | |
| {
 | |
| 	FuDellDockHub *self = g_object_new (FU_TYPE_DELL_DOCK_HUB, NULL);
 | |
| 	fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device));
 | |
| 	return self;
 | |
| }
 | 
