mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-11 03:25:25 +00:00
138 lines
3.7 KiB
C
138 lines
3.7 KiB
C
/*
|
|
* Copyright (C) 2016 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <fwupdplugin.h>
|
|
|
|
#include "fu-ebitdo-firmware.h"
|
|
|
|
struct _FuEbitdoFirmware {
|
|
FuFirmwareClass parent_instance;
|
|
};
|
|
|
|
G_DEFINE_TYPE(FuEbitdoFirmware, fu_ebitdo_firmware, FU_TYPE_FIRMWARE)
|
|
|
|
/* little endian */
|
|
typedef struct __attribute__((packed)) {
|
|
guint32 version;
|
|
guint32 destination_addr;
|
|
guint32 destination_len;
|
|
guint32 reserved[4];
|
|
} FuEbitdoFirmwareHeader;
|
|
|
|
static gboolean
|
|
fu_ebitdo_firmware_parse(FuFirmware *firmware,
|
|
GBytes *fw,
|
|
guint64 addr_start,
|
|
guint64 addr_end,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
FuEbitdoFirmwareHeader *hdr;
|
|
guint32 payload_len;
|
|
g_autofree gchar *version = NULL;
|
|
g_autoptr(FuFirmware) img_hdr = fu_firmware_new();
|
|
g_autoptr(GBytes) fw_hdr = NULL;
|
|
g_autoptr(GBytes) fw_payload = NULL;
|
|
|
|
/* corrupt */
|
|
if (g_bytes_get_size(fw) < sizeof(FuEbitdoFirmwareHeader)) {
|
|
g_set_error_literal(error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"firmware too small for header");
|
|
return FALSE;
|
|
}
|
|
|
|
/* check the file size */
|
|
hdr = (FuEbitdoFirmwareHeader *)g_bytes_get_data(fw, NULL);
|
|
payload_len = (guint32)(g_bytes_get_size(fw) - sizeof(FuEbitdoFirmwareHeader));
|
|
if (payload_len != GUINT32_FROM_LE(hdr->destination_len)) {
|
|
g_set_error(error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"file size incorrect, expected 0x%04x got 0x%04x",
|
|
(guint)GUINT32_FROM_LE(hdr->destination_len),
|
|
(guint)payload_len);
|
|
return FALSE;
|
|
}
|
|
|
|
/* check if this is firmware */
|
|
for (guint i = 0; i < 4; i++) {
|
|
if (hdr->reserved[i] != 0x0) {
|
|
g_set_error(error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"data invalid, reserved[%u] = 0x%04x",
|
|
i,
|
|
hdr->reserved[i]);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* parse version */
|
|
version = g_strdup_printf("%.2f", GUINT32_FROM_LE(hdr->version) / 100.f);
|
|
fu_firmware_set_version(firmware, version);
|
|
fu_firmware_set_version_raw(firmware, GUINT32_FROM_LE(hdr->version));
|
|
|
|
/* add header */
|
|
fw_hdr = fu_common_bytes_new_offset(fw, 0x0, sizeof(FuEbitdoFirmwareHeader), error);
|
|
if (fw_hdr == NULL)
|
|
return FALSE;
|
|
fu_firmware_set_id(img_hdr, FU_FIRMWARE_ID_HEADER);
|
|
fu_firmware_set_bytes(img_hdr, fw_hdr);
|
|
fu_firmware_add_image(firmware, img_hdr);
|
|
|
|
/* add payload */
|
|
fw_payload =
|
|
fu_common_bytes_new_offset(fw, sizeof(FuEbitdoFirmwareHeader), payload_len, error);
|
|
if (fw_payload == NULL)
|
|
return FALSE;
|
|
fu_firmware_set_id(firmware, FU_FIRMWARE_ID_PAYLOAD);
|
|
fu_firmware_set_addr(firmware, GUINT32_FROM_LE(hdr->destination_addr));
|
|
fu_firmware_set_bytes(firmware, fw_payload);
|
|
return TRUE;
|
|
}
|
|
|
|
static GBytes *
|
|
fu_ebitdo_firmware_write(FuFirmware *firmware, GError **error)
|
|
{
|
|
g_autoptr(GByteArray) buf = g_byte_array_new();
|
|
g_autoptr(GBytes) blob = NULL;
|
|
|
|
/* header then payload */
|
|
blob = fu_firmware_get_bytes(firmware, error);
|
|
if (blob == NULL)
|
|
return NULL;
|
|
fu_byte_array_append_uint32(buf, fu_firmware_get_version_raw(firmware), G_LITTLE_ENDIAN);
|
|
fu_byte_array_append_uint32(buf, fu_firmware_get_addr(firmware), G_LITTLE_ENDIAN);
|
|
fu_byte_array_append_uint32(buf, g_bytes_get_size(blob), G_LITTLE_ENDIAN);
|
|
for (guint i = 0; i < 4; i++)
|
|
fu_byte_array_append_uint32(buf, 0, G_LITTLE_ENDIAN);
|
|
fu_byte_array_append_bytes(buf, blob);
|
|
return g_byte_array_free_to_bytes(g_steal_pointer(&buf));
|
|
}
|
|
|
|
static void
|
|
fu_ebitdo_firmware_init(FuEbitdoFirmware *self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
fu_ebitdo_firmware_class_init(FuEbitdoFirmwareClass *klass)
|
|
{
|
|
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass);
|
|
klass_firmware->parse = fu_ebitdo_firmware_parse;
|
|
klass_firmware->write = fu_ebitdo_firmware_write;
|
|
}
|
|
|
|
FuFirmware *
|
|
fu_ebitdo_firmware_new(void)
|
|
{
|
|
return FU_FIRMWARE(g_object_new(FU_TYPE_EBITDO_FIRMWARE, NULL));
|
|
}
|