/*
 * Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
 *
 * SPDX-License-Identifier: LGPL-2.1+
 */

#define G_LOG_DOMAIN "FuUsbDeviceDs20"

#include "config.h"

#include "fu-mem.h"
#include "fu-usb-device-ms-ds20.h"

struct _FuUsbDeviceMsDs20 {
	FuUsbDeviceDs20 parent_instance;
};

G_DEFINE_TYPE(FuUsbDeviceMsDs20, fu_usb_device_ms_ds20, FU_TYPE_USB_DEVICE_DS20)

#define USB_OS_20_SET_HEADER_DESCRIPTOR	      0x00
#define USB_OS_20_SUBSET_HEADER_CONFIGURATION 0x01
#define USB_OS_20_SUBSET_HEADER_FUNCTION      0x02
#define USB_OS_20_FEATURE_COMPATIBLE_ID	      0x03
#define USB_OS_20_FEATURE_REG_PROPERTY	      0x04
#define USB_OS_20_FEATURE_MIN_RESUME_TIME     0x05
#define USB_OS_20_FEATURE_MODEL_ID	      0x06
#define USB_OS_20_FEATURE_CCGP_DEVICE	      0x07
#define USB_OS_20_FEATURE_VENDOR_REVISION     0x08

static const gchar *
fu_usb_device_os20_type_to_string(guint16 type)
{
	if (type == USB_OS_20_SET_HEADER_DESCRIPTOR)
		return "set-header-descriptor";
	if (type == USB_OS_20_SUBSET_HEADER_CONFIGURATION)
		return "subset-header-configuration";
	if (type == USB_OS_20_SUBSET_HEADER_FUNCTION)
		return "subset-header-function";
	if (type == USB_OS_20_FEATURE_COMPATIBLE_ID)
		return "feature-compatible-id";
	if (type == USB_OS_20_FEATURE_REG_PROPERTY)
		return "feature-reg-property";
	if (type == USB_OS_20_FEATURE_MIN_RESUME_TIME)
		return "feature-min-resume-time";
	if (type == USB_OS_20_FEATURE_MODEL_ID)
		return "feature-model-id";
	if (type == USB_OS_20_FEATURE_CCGP_DEVICE)
		return "feature-ccgp-device";
	if (type == USB_OS_20_FEATURE_VENDOR_REVISION)
		return "feature-vendor-revision";
	return NULL;
}

static gboolean
fu_usb_device_ms_ds20_parse(FuUsbDeviceDs20 *self,
			    GBytes *blob,
			    FuUsbDevice *device,
			    GError **error)
{
	gsize bufsz = 0;
	const guint8 *buf = g_bytes_get_data(blob, &bufsz);

	/* get length and type only */
	for (gsize offset = 0; offset < bufsz;) {
		guint16 desc_sz = 0;
		guint16 desc_type = 0;
		if (!fu_memread_uint16_safe(buf,
					    bufsz,
					    offset + 0x0,
					    &desc_sz,
					    G_LITTLE_ENDIAN,
					    error))
			return FALSE;
		if (desc_sz == 0)
			break;
		if (!fu_memread_uint16_safe(buf,
					    bufsz,
					    offset + 0x2,
					    &desc_type,
					    G_LITTLE_ENDIAN,
					    error))
			return FALSE;
		g_debug("USB OS descriptor type 0x%04x [%s], length 0x%04x",
			desc_type,
			fu_usb_device_os20_type_to_string(desc_type),
			desc_sz);
		offset += desc_sz;
	}

	/* success */
	return TRUE;
}

static void
fu_usb_device_ms_ds20_class_init(FuUsbDeviceMsDs20Class *klass)
{
	FuUsbDeviceDs20Class *usb_device_ds20_klass = FU_USB_DEVICE_DS20_CLASS(klass);
	usb_device_ds20_klass->parse = fu_usb_device_ms_ds20_parse;
}

static void
fu_usb_device_ms_ds20_init(FuUsbDeviceMsDs20 *self)
{
	fu_firmware_set_version_raw(FU_FIRMWARE(self), 0x06030000); /* Windows 8.1 */
	fu_firmware_set_id(FU_FIRMWARE(self), "d8dd60df-4589-4cc7-9cd2-659d9e648a9f");
}

/**
 * fu_usb_device_ms_ds20_new:
 *
 * Creates a new #FuUsbDeviceMsDs20.
 *
 * Returns: (transfer full): a #FuFirmware
 *
 * Since: 1.8.5
 **/
FuFirmware *
fu_usb_device_ms_ds20_new(void)
{
	return g_object_new(FU_TYPE_USB_DEVICE_MS_DS20, NULL);
}
