fwupd/plugins/wacom-usb/fu-wac-module-bluetooth-id6.c

203 lines
5.6 KiB
C

/*
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
* Copyright (C) 2021 Jason Gerecke <killertofu@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <string.h>
#include "fu-wac-common.h"
#include "fu-wac-device.h"
#include "fu-wac-module-bluetooth-id6.h"
struct _FuWacModuleBluetoothId6 {
FuWacModule parent_instance;
};
G_DEFINE_TYPE(FuWacModuleBluetoothId6, fu_wac_module_bluetooth_id6, FU_TYPE_WAC_MODULE)
#define FU_WAC_MODULE_BLUETOOTH_ID6_CRC8_POLYNOMIAL 0x31
#define FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ 256
#define FU_WAC_MODULE_BLUETOOTH_ID6_START_NORMAL 0x00
#define FU_WAC_MODULE_BLUETOOTH_ID6_START_FULLERASE 0xFE
typedef struct {
guint8 preamble[2];
guint8 crc;
guint8 addr[4];
guint8 cdata[FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ];
} FuWacModuleBluetoothId6BlockData;
static guint8
fu_wac_module_bluetooth_id6_reverse_bits(guint8 value)
{
guint8 reverse = 0;
for (gint i = 0; i < 8; i++) {
reverse <<= 1;
reverse |= (value & 0x01);
value >>= 1;
}
return reverse;
}
static guint8
fu_wac_module_bluetooth_id6_calculate_crc(const guint8 *data, gsize sz)
{
guint8 crc = ~fu_crc8_full(data, sz, 0x00, FU_WAC_MODULE_BLUETOOTH_ID6_CRC8_POLYNOMIAL);
return fu_wac_module_bluetooth_id6_reverse_bits(crc);
}
static GPtrArray *
fu_wac_module_bluetooth_id6_parse_blocks(const guint8 *data, gsize sz, GError **error)
{
const guint8 preamble[] = {0x00, 0x01};
GPtrArray *blocks = g_ptr_array_new_with_free_func(g_free);
for (guint addr = 0x0; addr < sz; addr += FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ) {
g_autofree FuWacModuleBluetoothId6BlockData *bd = NULL;
gsize cdata_sz = FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ;
bd = g_new0(FuWacModuleBluetoothId6BlockData, 1);
memcpy(bd->preamble, preamble, sizeof(preamble));
bd->addr[0] = 0;
bd->addr[1] = 0;
bd->addr[2] = 0;
bd->addr[3] = 0;
memset(bd->cdata, 0xff, FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ);
/* if file is not in multiples of payload size */
if (addr + FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ >= sz)
cdata_sz = sz - addr;
if (!fu_memcpy_safe(bd->cdata,
sizeof(bd->cdata),
0x0, /* dst */
data,
sz,
addr, /* src */
cdata_sz,
error))
return NULL;
bd->crc = fu_wac_module_bluetooth_id6_calculate_crc(
bd->cdata,
FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ);
g_ptr_array_add(blocks, g_steal_pointer(&bd));
}
return blocks;
}
static gboolean
fu_wac_module_bluetooth_id6_write_firmware(FuDevice *device,
FuFirmware *firmware,
FuProgress *progress,
FwupdInstallFlags flags,
GError **error)
{
FuWacModule *self = FU_WAC_MODULE(device);
const guint8 *data;
gsize len = 0;
const guint8 buf_start[] = {FU_WAC_MODULE_BLUETOOTH_ID6_START_NORMAL};
g_autoptr(GPtrArray) blocks = NULL;
g_autoptr(GBytes) blob_start = g_bytes_new_static(buf_start, 1);
g_autoptr(GBytes) fw = NULL;
/* progress */
fu_progress_set_id(progress, G_STRLOC);
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 8, NULL);
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 59, NULL);
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 33, NULL);
/* get default image */
fw = fu_firmware_get_bytes(firmware, error);
if (fw == NULL)
return FALSE;
/* build each data packet */
data = g_bytes_get_data(fw, &len);
blocks = fu_wac_module_bluetooth_id6_parse_blocks(data, len, error);
if (blocks == NULL)
return FALSE;
/* start, which will erase the module */
if (!fu_wac_module_set_feature(self,
FU_WAC_MODULE_COMMAND_START,
blob_start,
fu_progress_get_child(progress),
FU_WAC_MODULE_ERASE_TIMEOUT,
error))
return FALSE;
fu_progress_step_done(progress);
/* data */
for (guint i = 0; i < blocks->len; i++) {
FuWacModuleBluetoothId6BlockData *bd = g_ptr_array_index(blocks, i);
guint8 buf[FU_WAC_MODULE_BLUETOOTH_ID6_PAYLOAD_SZ + 7];
g_autoptr(GBytes) blob_chunk = NULL;
/* build data packet */
memset(buf, 0xff, sizeof(buf));
memcpy(&buf[0], bd->preamble, 2);
buf[2] = bd->crc;
memcpy(&buf[3], bd->addr, 4);
memcpy(&buf[7], bd->cdata, sizeof(bd->cdata));
blob_chunk = g_bytes_new(buf, sizeof(buf));
if (!fu_wac_module_set_feature(self,
FU_WAC_MODULE_COMMAND_DATA,
blob_chunk,
fu_progress_get_child(progress),
FU_WAC_MODULE_WRITE_TIMEOUT,
error))
return FALSE;
/* update progress */
fu_progress_set_percentage_full(fu_progress_get_child(progress),
i + 1,
blocks->len);
}
fu_progress_step_done(progress);
/* end */
if (!fu_wac_module_set_feature(self,
FU_WAC_MODULE_COMMAND_END,
NULL,
fu_progress_get_child(progress),
FU_WAC_MODULE_COMMIT_TIMEOUT,
error))
return FALSE;
fu_progress_step_done(progress);
/* success */
return TRUE;
}
static void
fu_wac_module_bluetooth_id6_init(FuWacModuleBluetoothId6 *self)
{
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_set_install_duration(FU_DEVICE(self), 120);
}
static void
fu_wac_module_bluetooth_id6_class_init(FuWacModuleBluetoothId6Class *klass)
{
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
klass_device->write_firmware = fu_wac_module_bluetooth_id6_write_firmware;
}
FuWacModule *
fu_wac_module_bluetooth_id6_new(FuContext *context, GUsbDevice *usb_device)
{
FuWacModule *module = NULL;
module = g_object_new(FU_TYPE_WAC_MODULE_BLUETOOTH_ID6,
"context",
context,
"usb-device",
usb_device,
"fw-type",
FU_WAC_MODULE_FW_TYPE_BLUETOOTH_ID6,
NULL);
return module;
}