<commit_before>/*
 * Copyright (C) 2018 <NAME> <<EMAIL>>
 *
 * SPDX-License-Identifier: LGPL-2.1+
 */

#include "config.h"

#include <string.h>

#include "fu-chunk.h"
#include "fu-wac-device.h"
#include "fu-wac-common.h"
#include "fu-wac-firmware.h"
#include "fu-wac-module-bluetooth.h"
#include "fu-wac-module-touch.h"

#include "dfu-common.h"
#include "dfu-firmware.h"

typedef struct __attribute__((packed)) {
	guint32		 start_addr;
	guint32		 block_sz;
	guint16		 write_sz; /* bit 15 is write protection flag */
} FuWacFlashDescriptor;

typedef enum {
	FU_WAC_STATUS_UNKNOWN			= 0,
	FU_WAC_STATUS_WRITING			= 1 << 0,
	FU_WAC_STATUS_ERASING			= 1 << 1,
	FU_WAC_STATUS_ERROR_WRITE		= 1 << 2,
	FU_WAC_STATUS_ERROR_ERASE		= 1 << 3,
	FU_WAC_STATUS_WRITE_PROTECTED		= 1 << 4,
	FU_WAC_STATUS_LAST
} FuWacStatus;

#define FU_WAC_DEVICE_TIMEOUT			5000	/* ms */

struct _FuWacDevice
{
	FuUsbDevice		 parent_instance;
	GPtrArray		*flash_descriptors;
	GArray			*checksums;
	guint32			 status_word;
	guint16			 firmware_index;
	guint16			 loader_ver;
	guint16			 read_data_sz;
	guint16			 write_word_sz;
	guint16			 write_block_sz;	/* usb transfer size */
	guint16			 nr_flash_blocks;
	guint16			 configuration;
};

G_DEFINE_TYPE (FuWacDevice, fu_wac_device, FU_TYPE_USB_DEVICE)

static GString *
fu_wac_device_status_to_string (guint32 status_word)
{
	GString *str = g_string_new (NULL);
	if (status_word & FU_WAC_STATUS_WRITING)
		g_string_append (str, "writing,");
	if (status_word & FU_WAC_STATUS_ERASING)
		g_string_append (str, "erasing,");
	if (status_word & FU_WAC_STATUS_ERROR_WRITE)
		g_string_append (str, "error-write,");
	if (status_word & FU_WAC_STATUS_ERROR_ERASE)
		g_string_append (str, "error-erase,");
	if (status_word & FU_WAC_STATUS_WRITE_PROTECTED)
		g_string_append (str, "write-protected,");
	if (str->len == 0) {
		g_string_append (str, "none");
		return str;
	}
	g_string_truncate (str, str->len - 1);
	return str;
}

static gboolean
fu_wav_device_flash_descriptor_is_wp (const FuWacFlashDescriptor *fd)
{
	return fd->write_sz & 0x8000;
}

static void
fu_wac_device_to_string (FuDevice *device, GString *str)
{
	GPtrArray *children;
	FuWacDevice *self = FU_WAC_DEVICE (device);
	g_autoptr(GString) status_str = NULL;

	g_string_append (str, "  FuWacDevice:\n");
	if (self->firmware_index != 0xffff) {
		g_string_append_printf (str, "    fw-index: 0x%04x\n",
					self->firmware_index);
	}
	if (self->loader_ver > 0) {
		g_string_append_printf (str, "    loader-ver: 0x%04x\n",
					(guint) self->loader_ver);
	}
	if (self->read_data_sz > 0) {
		g_string_append_printf (str, "    read-data-sz: 0x%04x\n",
					(guint) self->read_data_sz);
	}
	if (self->write_word_sz > 0) {
		g_string_append_printf (str, "    write-word-sz: 0x%04x\n",
					(guint) self->write_word_sz);
	}
	if (self->write_block_sz > 0) {
		g_string_append_printf (str, "    write-block-sz: 0x%04x\n",
					(guint) self->write_block_sz);
	}
	if (self->nr_flash_blocks > 0) {
		g_string_append_printf (str, "    nr-flash-blocks: 0x%04x\n",
					(guint) self->nr_flash_blocks);
	}
	if (self->configuration != 0xffff) {
		g_string_append_printf (str, "    configuration: 0x%04x\n",
					(guint) self->configuration);
	}
	for (guint i = 0; i < self->flash_descriptors->len; i++) {
		FuWacFlashDescriptor *fd = g_ptr_array_index (self->flash_descriptors, i);
		g_string_append_printf (str, "    flash-descriptor-%02u:\n", i);
		g_string_append_printf (str, "      start-addr:\t0x%08x\n",
					(guint) fd->start_addr);
		g_string_append_printf (str, "      block-sz:\t0x%08x\n",
					(guint) fd->block_sz);
		g_string_append_printf (str, "      write-sz:\t0x%04x\n",
					(guint) fd->write_sz & ~0x8000);
		g_string_append_printf (str, "      protected:\t%s\n",
					fu_wav_device_flash_descriptor_is_wp (fd) ? "yes" : "no");
	}
	status_str = fu_wac_device_status_to_string (self->status_word);
	g_string_append_printf (str, "    status:\t\t%s\n", status_str->str);

	/* print children also */
	children = fu_device_get_children (device);
	for (guint i = 0; i < children->len; i++) {
		FuDevice *child = g_ptr_array_index (children, i);
		g_autofree gchar *tmp = fu_device_to_string (FU_DEVICE (child));
		g_string_append (str, "  FuWacDeviceChild:\n");
		g_string_append (str, tmp);
	}
}

gboolean
fu_wac_device_get_feature_report (FuWacDevice *self,
				  guint8 *buf, gsize bufsz,
				  FuWacDeviceFeatureFlags flags,
				  GError **error)
{
	GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self));
	gsize sz = 0;
	guint8 cmd = buf[0];

	/* hit hardware */
	fu_wac_buffer_dump ("GET", cmd, buf, bufsz);
	if (!g_usb_device_control_transfer (usb_device,
					    G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
					    G_USB_DEVICE_REQUEST_TYPE_CLASS,
					    G_USB_DEVICE_RECIPIENT_INTERFACE,
					    HID_REPORT_GET,		/* bRequest */
					    HID_FEATURE | cmd,		/* wValue */
					    0x0000,			/* wIndex */
					    buf, bufsz, &sz,
					    FU_WAC_DEVICE_TIMEOUT,
					    NULL, error)) {
		g_prefix_error (error, "Failed to get feature report: ");
		return FALSE;
	}
	fu_wac_buffer_dump ("GE2", cmd, buf, sz);

	/* check packet */
	if (flags && FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC == 0 && sz != bufsz) {
		g_set_error (error,
			     FWUPD_ERROR,
			     FWUPD_ERROR_INTERNAL,
			     "packet get bytes %" G_GSIZE_FORMAT
			     " expected %" G_GSIZE_FORMAT,
			     sz, bufsz);
		return FALSE;
	}
	if (buf[0] != cmd) {
		g_set_error (error,
			     FWUPD_ERROR,
			     FWUPD_ERROR_INTERNAL,
			     "command response was %i expected %i",
			     buf[0], cmd);
		return FALSE;
	}
	return TRUE;
}

gboolean
fu_wac_device_set_feature_report (FuWacDevice *self,
				  guint8 *buf, gsize bufsz,
				  FuWacDeviceFeatureFlags flags,
				  GError **error)
{
	GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self));
	gsize sz = 0;
	guint8 cmd = buf[0];

	/* hit hardware */
	fu_wac_buffer_dump ("SET", cmd, buf, bufsz);
	if (g_getenv ("FWUPD_WAC_EMULATE") != NULL)
		return TRUE;
	if (!g_usb_device_control_transfer (usb_device,
					    G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
					    G_USB_DEVICE_REQUEST_TYPE_CLASS,
					    G_USB_DEVICE_RECIPIENT_INTERFACE,
					    HID_REPORT_SET,		/* bRequest */
					    HID_FEATURE | cmd,		/* wValue */
					    0x0000,			/* wIndex */
					    buf, bufsz, &sz,
					    FU_WAC_DEVICE_TIMEOUT,
					    NULL, error)) {
		g_prefix_error (error, "Failed to set feature report: ");
		return FALSE;
	}

	/* check packet */
	if (flags && FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC == 0 && sz != bufsz) {
		g_set_error (error,
			     FWUPD_ERROR,
			     FWUPD_ERROR_INTERNAL,
			     "packet sent bytes %" G_GSIZE_FORMAT
			     " expected %" G_GSIZE_FORMAT,<commit_msg>wacomhid: fix allow trunc flag check<commit_after>/*
 * Copyright (C) 2018 <NAME> <<EMAIL>>
 *
 * SPDX-License-Identifier: LGPL-2.1+
 */

#include "config.h"

#include <string.h>

#include "fu-chunk.h"
#include "fu-wac-device.h"
#include "fu-wac-common.h"
#include "fu-wac-firmware.h"
#include "fu-wac-module-bluetooth.h"
#include "fu-wac-module-touch.h"

#include "dfu-common.h"
#include "dfu-firmware.h"

typedef struct __attribute__((packed)) {
	guint32		 start_addr;
	guint32		 block_sz;
	guint16		 write_sz; /* bit 15 is write protection flag */
} FuWacFlashDescriptor;

typedef enum {
	FU_WAC_STATUS_UNKNOWN			= 0,
	FU_WAC_STATUS_WRITING			= 1 << 0,
	FU_WAC_STATUS_ERASING			= 1 << 1,
	FU_WAC_STATUS_ERROR_WRITE		= 1 << 2,
	FU_WAC_STATUS_ERROR_ERASE		= 1 << 3,
	FU_WAC_STATUS_WRITE_PROTECTED		= 1 << 4,
	FU_WAC_STATUS_LAST
} FuWacStatus;

#define FU_WAC_DEVICE_TIMEOUT			5000	/* ms */

struct _FuWacDevice
{
	FuUsbDevice		 parent_instance;
	GPtrArray		*flash_descriptors;
	GArray			*checksums;
	guint32			 status_word;
	guint16			 firmware_index;
	guint16			 loader_ver;
	guint16			 read_data_sz;
	guint16			 write_word_sz;
	guint16			 write_block_sz;	/* usb transfer size */
	guint16			 nr_flash_blocks;
	guint16			 configuration;
};

G_DEFINE_TYPE (FuWacDevice, fu_wac_device, FU_TYPE_USB_DEVICE)

static GString *
fu_wac_device_status_to_string (guint32 status_word)
{
	GString *str = g_string_new (NULL);
	if (status_word & FU_WAC_STATUS_WRITING)
		g_string_append (str, "writing,");
	if (status_word & FU_WAC_STATUS_ERASING)
		g_string_append (str, "erasing,");
	if (status_word & FU_WAC_STATUS_ERROR_WRITE)
		g_string_append (str, "error-write,");
	if (status_word & FU_WAC_STATUS_ERROR_ERASE)
		g_string_append (str, "error-erase,");
	if (status_word & FU_WAC_STATUS_WRITE_PROTECTED)
		g_string_append (str, "write-protected,");
	if (str->len == 0) {
		g_string_append (str, "none");
		return str;
	}
	g_string_truncate (str, str->len - 1);
	return str;
}

static gboolean
fu_wav_device_flash_descriptor_is_wp (const FuWacFlashDescriptor *fd)
{
	return fd->write_sz & 0x8000;
}

static void
fu_wac_device_to_string (FuDevice *device, GString *str)
{
	GPtrArray *children;
	FuWacDevice *self = FU_WAC_DEVICE (device);
	g_autoptr(GString) status_str = NULL;

	g_string_append (str, "  FuWacDevice:\n");
	if (self->firmware_index != 0xffff) {
		g_string_append_printf (str, "    fw-index: 0x%04x\n",
					self->firmware_index);
	}
	if (self->loader_ver > 0) {
		g_string_append_printf (str, "    loader-ver: 0x%04x\n",
					(guint) self->loader_ver);
	}
	if (self->read_data_sz > 0) {
		g_string_append_printf (str, "    read-data-sz: 0x%04x\n",
					(guint) self->read_data_sz);
	}
	if (self->write_word_sz > 0) {
		g_string_append_printf (str, "    write-word-sz: 0x%04x\n",
					(guint) self->write_word_sz);
	}
	if (self->write_block_sz > 0) {
		g_string_append_printf (str, "    write-block-sz: 0x%04x\n",
					(guint) self->write_block_sz);
	}
	if (self->nr_flash_blocks > 0) {
		g_string_append_printf (str, "    nr-flash-blocks: 0x%04x\n",
					(guint) self->nr_flash_blocks);
	}
	if (self->configuration != 0xffff) {
		g_string_append_printf (str, "    configuration: 0x%04x\n",
					(guint) self->configuration);
	}
	for (guint i = 0; i < self->flash_descriptors->len; i++) {
		FuWacFlashDescriptor *fd = g_ptr_array_index (self->flash_descriptors, i);
		g_string_append_printf (str, "    flash-descriptor-%02u:\n", i);
		g_string_append_printf (str, "      start-addr:\t0x%08x\n",
					(guint) fd->start_addr);
		g_string_append_printf (str, "      block-sz:\t0x%08x\n",
					(guint) fd->block_sz);
		g_string_append_printf (str, "      write-sz:\t0x%04x\n",
					(guint) fd->write_sz & ~0x8000);
		g_string_append_printf (str, "      protected:\t%s\n",
					fu_wav_device_flash_descriptor_is_wp (fd) ? "yes" : "no");
	}
	status_str = fu_wac_device_status_to_string (self->status_word);
	g_string_append_printf (str, "    status:\t\t%s\n", status_str->str);

	/* print children also */
	children = fu_device_get_children (device);
	for (guint i = 0; i < children->len; i++) {
		FuDevice *child = g_ptr_array_index (children, i);
		g_autofree gchar *tmp = fu_device_to_string (FU_DEVICE (child));
		g_string_append (str, "  FuWacDeviceChild:\n");
		g_string_append (str, tmp);
	}
}

gboolean
fu_wac_device_get_feature_report (FuWacDevice *self,
				  guint8 *buf, gsize bufsz,
				  FuWacDeviceFeatureFlags flags,
				  GError **error)
{
	GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self));
	gsize sz = 0;
	guint8 cmd = buf[0];

	/* hit hardware */
	fu_wac_buffer_dump ("GET", cmd, buf, bufsz);
	if (!g_usb_device_control_transfer (usb_device,
					    G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
					    G_USB_DEVICE_REQUEST_TYPE_CLASS,
					    G_USB_DEVICE_RECIPIENT_INTERFACE,
					    HID_REPORT_GET,		/* bRequest */
					    HID_FEATURE | cmd,		/* wValue */
					    0x0000,			/* wIndex */
					    buf, bufsz, &sz,
					    FU_WAC_DEVICE_TIMEOUT,
					    NULL, error)) {
		g_prefix_error (error, "Failed to get feature report: ");
		return FALSE;
	}
	fu_wac_buffer_dump ("GE2", cmd, buf, sz);

	/* check packet */
	if ((flags & FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC) == 0 && sz != bufsz) {
		g_set_error (error,
			     FWUPD_ERROR,
			     FWUPD_ERROR_INTERNAL,
			     "packet get bytes %" G_GSIZE_FORMAT
			     " expected %" G_GSIZE_FORMAT,
			     sz, bufsz);
		return FALSE;
	}
	if (buf[0] != cmd) {
		g_set_error (error,
			     FWUPD_ERROR,
			     FWUPD_ERROR_INTERNAL,
			     "command response was %i expected %i",
			     buf[0], cmd);
		return FALSE;
	}
	return TRUE;
}

gboolean
fu_wac_device_set_feature_report (FuWacDevice *self,
				  guint8 *buf, gsize bufsz,
				  FuWacDeviceFeatureFlags flags,
				  GError **error)
{
	GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self));
	gsize sz = 0;
	guint8 cmd = buf[0];

	/* hit hardware */
	fu_wac_buffer_dump ("SET", cmd, buf, bufsz);
	if (g_getenv ("FWUPD_WAC_EMULATE") != NULL)
		return TRUE;
	if (!g_usb_device_control_transfer (usb_device,
					    G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
					    G_USB_DEVICE_REQUEST_TYPE_CLASS,
					    G_USB_DEVICE_RECIPIENT_INTERFACE,
					    HID_REPORT_SET,		/* bRequest */
					    HID_FEATURE | cmd,		/* wValue */
					    0x0000,			/* wIndex */
					    buf, bufsz, &sz,
					    FU_WAC_DEVICE_TIMEOUT,
					    NULL, error)) {
		g_prefix_error (error, "Failed to set feature report: ");
		return FALSE;
	}

	/* check packet */
	if ((flags & FU_WAC_DEVICE_FEATURE_FLAG_ALLOW_TRUNC) == 0 && sz != bufsz) {
		g_set_error (error,
			     FWUPD_ERROR,
			     FWUPD_ERROR_INTERNAL,
			     "packet sent bytes %" G_GSIZE_FORMAT
			     " expected %" G_GSIZE_FORMAT,
{'fraction_non_alphanumeric': 0.12614422862246036, 'fraction_numerical': 0.01830765795936593, 'mean_word_length': 3.7150877192982454, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1861148', 'n_tokens_mistral': 6203, 'n_tokens_neox': 5454, 'n_words': 1014}