fwupd/plugins/unifying/fu-unifying-bootloader-texas.c
Richard Hughes 9d3f791727 unifying: Use the new daemon-provided functionality
Until now, the unifying plugin was a 'special snowflake' and did things in very
different ways to the other plugins. Refactor each object to derive from either
FuUsbDevice or FuUdevDevice, and then remove LuDevice and the LuContext layers.
2018-09-07 16:22:38 +01:00

232 lines
6.8 KiB
C

/*
* Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <string.h>
#include "fu-unifying-common.h"
#include "fu-unifying-bootloader-texas.h"
struct _FuUnifyingBootloaderTexas
{
FuUnifyingBootloader parent_instance;
};
G_DEFINE_TYPE (FuUnifyingBootloaderTexas, fu_unifying_bootloader_texas, FU_TYPE_UNIFYING_BOOTLOADER)
static gboolean
fu_unifying_bootloader_texas_erase_all (FuUnifyingBootloader *self, GError **error)
{
g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new ();
req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM;
req->len = 0x01; /* magic number */
req->data[0] = 0x00; /* magic number */
if (!fu_unifying_bootloader_request (self, req, error)) {
g_prefix_error (error, "failed to erase all pages: ");
return FALSE;
}
return TRUE;
}
static gboolean
fu_unifying_bootloader_texas_compute_and_test_crc (FuUnifyingBootloader *self, GError **error)
{
g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new ();
req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM;
req->len = 0x01; /* magic number */
req->data[0] = 0x03; /* magic number */
if (!fu_unifying_bootloader_request (self, req, error)) {
g_prefix_error (error, "failed to compute and test CRC: ");
return FALSE;
}
if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_WRONG_CRC) {
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"CRC is incorrect");
return FALSE;
}
return TRUE;
}
static gboolean
fu_unifying_bootloader_texas_flash_ram_buffer (FuUnifyingBootloader *self, guint16 addr, GError **error)
{
g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new ();
req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM;
req->addr = addr;
req->len = 0x01; /* magic number */
req->data[0] = 0x01; /* magic number */
if (!fu_unifying_bootloader_request (self, req, error)) {
g_prefix_error (error, "failed to flash ram buffer @%04x: ", addr);
return FALSE;
}
if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ADDR) {
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"failed to flash ram buffer @%04x: invalid flash page",
addr);
return FALSE;
}
if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_PAGE0_INVALID) {
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"failed to flash ram buffer @%04x: invalid App JMP vector",
addr);
return FALSE;
}
if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM_INVALID_ORDER) {
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"failed to flash ram buffer @%04x: page flashed before page 0",
addr);
return FALSE;
}
return TRUE;
}
static gboolean
fu_unifying_bootloader_texas_clear_ram_buffer (FuUnifyingBootloader *self, GError **error)
{
g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new ();
req->cmd = FU_UNIFYING_BOOTLOADER_CMD_FLASH_RAM;
req->addr = 0x0000;
req->len = 0x01; /* magic number */
req->data[0] = 0x02; /* magic number */
if (!fu_unifying_bootloader_request (self, req, error)) {
g_prefix_error (error, "failed to clear ram buffer @%04x: ", req->addr);
return FALSE;
}
return TRUE;
}
static gboolean
fu_unifying_bootloader_texas_write_firmware (FuDevice *device, GBytes *fw, GError **error)
{
FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device);
const FuUnifyingBootloaderRequest *payload;
g_autoptr(GPtrArray) reqs = NULL;
g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new ();
/* transfer payload */
reqs = fu_unifying_bootloader_parse_requests (self, fw, error);
if (reqs == NULL)
return FALSE;
/* erase all flash pages */
fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE);
if (!fu_unifying_bootloader_texas_erase_all (self, error))
return FALSE;
/* set existing RAM buffer to 0xff's */
if (!fu_unifying_bootloader_texas_clear_ram_buffer (self, error))
return FALSE;
/* write to RAM buffer */
fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);
for (guint i = 0; i < reqs->len; i++) {
payload = g_ptr_array_index (reqs, i);
/* check size */
if (payload->len != 16) {
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"payload size invalid @%04x: got 0x%02x",
payload->addr, payload->len);
return FALSE;
}
/* build packet */
req->cmd = payload->cmd;
/* signature addresses do not need to fit inside 128 bytes */
if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE)
req->addr = payload->addr;
else
req->addr = payload->addr % 0x80;
req->len = payload->len;
memcpy (req->data, payload->data, payload->len);
if (!fu_unifying_bootloader_request (self, req, error)) {
g_prefix_error (error,
"failed to write ram bufer @0x%02x: ",
req->addr);
return FALSE;
}
if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_INVALID_ADDR) {
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"failed to write ram buffer @%04x: invalid location",
req->addr);
return FALSE;
}
if (req->cmd == FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER_OVERFLOW) {
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"failed to write ram buffer @%04x: invalid size 0x%02x",
req->addr, req->len);
return FALSE;
}
/* flush RAM buffer to EEPROM */
if ((payload->addr + 0x10) % 0x80 == 0 &&
req->cmd != FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE) {
guint16 addr_start = payload->addr - (7 * 0x10);
g_debug ("addr flush @ 0x%04x for 0x%04x",
payload->addr, addr_start);
if (!fu_unifying_bootloader_texas_flash_ram_buffer (self,
addr_start,
error)) {
g_prefix_error (error,
"failed to flash ram buffer @0x%04x: ",
addr_start);
return FALSE;
}
}
/* update progress */
fu_device_set_progress_full (device, i * 32, reqs->len * 32);
}
/* check CRC */
if (!fu_unifying_bootloader_texas_compute_and_test_crc (self, error))
return FALSE;
/* mark as complete */
fu_device_set_progress_full (device, reqs->len * 32, reqs->len * 32);
/* success! */
return TRUE;
}
static gboolean
fu_unifying_bootloader_texas_setup (FuUnifyingBootloader *self, GError **error)
{
fu_device_set_version (FU_DEVICE (self), "RQR24.00_B0000");
return TRUE;
}
static void
fu_unifying_bootloader_texas_class_init (FuUnifyingBootloaderTexasClass *klass)
{
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
FuUnifyingBootloaderClass *klass_device_bootloader = FU_UNIFYING_BOOTLOADER_CLASS (klass);
klass_device->write_firmware = fu_unifying_bootloader_texas_write_firmware;
klass_device_bootloader->setup = fu_unifying_bootloader_texas_setup;
}
static void
fu_unifying_bootloader_texas_init (FuUnifyingBootloaderTexas *self)
{
}