Skip to content

Commit

Permalink
fpc: Add support for devices that use FPC FF2
Browse files Browse the repository at this point in the history
  • Loading branch information
haoweilo authored and hughsie committed May 16, 2024
1 parent 8894b5a commit 0299fd9
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 3 deletions.
170 changes: 167 additions & 3 deletions plugins/fpc/fu-fpc-device.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "config.h"

#include "fu-fpc-device.h"
#include "fu-fpc-ff2-firmware.h"
#include "fu-fpc-struct.h"

#define FPC_USB_INTERFACE 0
Expand All @@ -19,6 +20,7 @@
#define FPC_CMD_DFU_GETSTATUS 0x03
#define FPC_CMD_DFU_CLRSTATUS 0x04
#define FPC_CMD_DFU_GET_FW_STATUS 0x09
#define FPC_CMD_DFU_DNLOAD_FF2 0x10

#define FPC_CMD_BOOT0 0x04
#define FPC_CMD_GET_STATE 0x0B
Expand All @@ -34,6 +36,8 @@
#define FPC_DEVICE_NORMAL_MODE_CLASS 0xFF
#define FPC_DEVICE_NORMAL_MODE_PORT 0xFF

#define FPC_FF2_BLK_SEC_LINK_LEN 100

/**
* FU_FPC_DEVICE_FLAG_MOH_DEVICE:
*
Expand Down Expand Up @@ -72,6 +76,22 @@ struct _FuFpcDevice {

G_DEFINE_TYPE(FuFpcDevice, fu_fpc_device, FU_TYPE_USB_DEVICE)

static FuFirmware *
fu_fpc_device_prepare_firmware(FuDevice *device,
GInputStream *stream,
FuProgress *progress,
FwupdInstallFlags flags,
GError **error)
{
return fu_firmware_new_from_gtypes(stream,
0x0,
flags,
error,
FU_TYPE_FPC_FF2_FIRMWARE,
FU_TYPE_FIRMWARE,
G_TYPE_INVALID);
}

static gboolean
fu_fpc_device_dfu_cmd(FuFpcDevice *self,
guint8 request,
Expand Down Expand Up @@ -104,8 +124,10 @@ fu_fpc_device_dfu_cmd(FuFpcDevice *self,
length ? &actual_len : NULL,
FPC_USB_TRANSFER_TIMEOUT,
NULL,
error))
error)) {
fu_error_convert(error);
return FALSE;
}
if (actual_len != length) {
g_set_error(error,
FWUPD_ERROR,
Expand Down Expand Up @@ -147,8 +169,10 @@ fu_fpc_device_fw_cmd(FuFpcDevice *self,
length ? &actual_len : NULL,
FPC_USB_TRANSFER_TIMEOUT,
NULL,
error))
error)) {
fu_error_convert(error);
return FALSE;
}
if (actual_len != length) {
g_set_error(error,
FWUPD_ERROR,
Expand Down Expand Up @@ -411,6 +435,136 @@ fu_fpc_device_setup(FuDevice *device, GError **error)
return TRUE;
}

static gboolean
fu_fpc_device_write_ff2_blocks(FuFpcDevice *self, GInputStream *stream, GError **error)
{
g_autoptr(FuChunkArray) chunks = NULL;

chunks = fu_chunk_array_new_from_stream(stream, 0x0, FPC_FLASH_BLOCK_SIZE_4096, error);
if (chunks == NULL)
return FALSE;
for (guint i = 0; i < fu_chunk_array_length(chunks); i++) {
g_autoptr(FuChunk) chk = NULL;
chk = fu_chunk_array_index(chunks, i, error);
if (chk == NULL)
return FALSE;
if (!fu_fpc_device_dfu_cmd(self,
FPC_CMD_DFU_DNLOAD_FF2,
0,
(guint8 *)fu_chunk_get_data(chk),
fu_chunk_get_data_sz(chk),
FALSE,
FALSE,
error)) {
g_prefix_error(error,
"failed to write at 0x%x: ",
(guint)fu_chunk_get_address(chk));
return FALSE;
}
}

/* success */
return TRUE;
}

static gboolean
fu_fpc_device_write_ff2_firmware(FuFpcDevice *self,
FuFpcFf2Firmware *firmware,
FuProgress *progress,
FwupdInstallFlags flags,
GError **error)
{
gsize offset = 0;
guint32 blocks_num;
g_autoptr(GInputStream) stream = NULL;

stream = fu_firmware_get_stream(FU_FIRMWARE(firmware), error);
if (stream == NULL)
return FALSE;
blocks_num = fu_fpc_ff2_firmware_get_blocks_num(firmware);
offset += FU_STRUCT_FPC_FF2_HDR_SIZE;

/* progress */
fu_progress_set_id(progress, G_STRLOC);
fu_progress_set_steps(progress, blocks_num);
for (guint i = 0; i < blocks_num; i++) {
FuFpcFf2BlockDir direction;
g_autoptr(FuStructFpcFf2BlockHdr) st_blkhdr = NULL;
g_autoptr(FuStructFpcFf2BlockSec) st_blksec = NULL;
guint16 payload_len;

/* parse dfu_meta_content_hdr_t */
st_blkhdr = fu_struct_fpc_ff2_block_hdr_parse_stream(stream, offset, error);
if (st_blkhdr == NULL)
return FALSE;
direction = fu_struct_fpc_ff2_block_hdr_get_dir(st_blkhdr);
offset += st_blkhdr->len;

/* validate dfu_sec_link_t and include the size in payload */
st_blksec = fu_struct_fpc_ff2_block_sec_parse_stream(stream, offset, error);
if (st_blksec == NULL)
return FALSE;
payload_len = fu_struct_fpc_ff2_block_sec_get_payload_len(st_blksec);
payload_len += st_blksec->len;

if (direction == FU_FPC_FF2_BLOCK_DIR_OUT) {
g_autoptr(GInputStream) partial_stream = NULL;
g_autoptr(GByteArray) buf_sec = NULL;

/* write sec-link chunk? */
buf_sec = fu_input_stream_read_byte_array(stream,
offset,
FPC_FF2_BLK_SEC_LINK_LEN,
error);
if (buf_sec == NULL)
return FALSE;
if (!fu_fpc_device_dfu_cmd(self,
FPC_CMD_DFU_DNLOAD_FF2,
0,
buf_sec->data,
buf_sec->len,
FALSE,
FALSE,
error)) {
g_prefix_error(error, "failed to write sec-link: ");
return FALSE;
}

/* write data in 4k blocks */
partial_stream =
fu_partial_input_stream_new(stream,
offset + FPC_FF2_BLK_SEC_LINK_LEN,
payload_len - FPC_FF2_BLK_SEC_LINK_LEN,
error);
if (partial_stream == NULL)
return FALSE;
if (!fu_fpc_device_write_ff2_blocks(self, partial_stream, error))
return FALSE;
} else if (direction == FU_FPC_FF2_BLOCK_DIR_IN) {
if (!fu_device_retry_full(FU_DEVICE(self),
fu_fpc_device_check_dfu_status_cb,
FPC_DFU_MAX_ATTEMPTS,
20,
NULL,
error))
return FALSE;
} else {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"unsupported direction");
return FALSE;
}

/* there is a block terminator of 0xFF */
offset += payload_len + sizeof(guint8);
fu_progress_step_done(progress);
}

/* success */
return TRUE;
}

static gboolean
fu_fpc_device_write_firmware(FuDevice *device,
FuFirmware *firmware,
Expand All @@ -429,7 +583,16 @@ fu_fpc_device_write_firmware(FuDevice *device,
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 95, NULL);
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "check");

/* get default image */
/* new format */
if (FU_IS_FPC_FF2_FIRMWARE(firmware)) {
return fu_fpc_device_write_ff2_firmware(self,
FU_FPC_FF2_FIRMWARE(firmware),
progress,
flags,
error);
}

/* write old fw format */
stream = fu_firmware_get_stream(firmware, error);
if (stream == NULL)
return FALSE;
Expand Down Expand Up @@ -572,6 +735,7 @@ fu_fpc_device_class_init(FuFpcDeviceClass *klass)
{
FuDeviceClass *device_class = FU_DEVICE_CLASS(klass);
device_class->to_string = fu_fpc_device_to_string;
device_class->prepare_firmware = fu_fpc_device_prepare_firmware;
device_class->write_firmware = fu_fpc_device_write_firmware;
device_class->setup = fu_fpc_device_setup;
device_class->reload = fu_fpc_device_setup;
Expand Down
72 changes: 72 additions & 0 deletions plugins/fpc/fu-fpc-ff2-firmware.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 2024 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/

#include "config.h"

#include <fwupdplugin.h>

#include "fu-fpc-ff2-firmware.h"
#include "fu-fpc-struct.h"

struct _FuFpcFf2Firmware {
FuFirmware parent_instance;
guint32 blocks_num;
};

G_DEFINE_TYPE(FuFpcFf2Firmware, fu_fpc_ff2_firmware, FU_TYPE_FIRMWARE)

static void
fu_fpc_ff2_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
{
FuFpcFf2Firmware *self = FU_FPC_FF2_FIRMWARE(firmware);
fu_xmlb_builder_insert_kx(bn, "blocks_num", self->blocks_num);
}

static gboolean
fu_fpc_ff2_validate(FuFirmware *firmware, GInputStream *stream, gsize offset, GError **error)
{
return fu_struct_fpc_ff2_hdr_validate_stream(stream, offset, error);
}

static gboolean
fu_fpc_ff2_firmware_parse(FuFirmware *firmware,
GInputStream *stream,
gsize offset,
FwupdInstallFlags flags,
GError **error)
{
FuFpcFf2Firmware *self = FU_FPC_FF2_FIRMWARE(firmware);
g_autoptr(FuStructFpcFf2Hdr) st_hdr = NULL;

st_hdr = fu_struct_fpc_ff2_hdr_parse_stream(stream, offset, error);
if (st_hdr == NULL)
return FALSE;
self->blocks_num = fu_struct_fpc_ff2_hdr_get_blocks_num(st_hdr);

/* success */
return TRUE;
}

guint32
fu_fpc_ff2_firmware_get_blocks_num(FuFpcFf2Firmware *self)
{
g_return_val_if_fail(FU_IS_FPC_FF2_FIRMWARE(self), G_MAXUINT16);
return self->blocks_num;
}

static void
fu_fpc_ff2_firmware_init(FuFpcFf2Firmware *self)
{
}

static void
fu_fpc_ff2_firmware_class_init(FuFpcFf2FirmwareClass *klass)
{
FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
firmware_class->validate = fu_fpc_ff2_validate;
firmware_class->parse = fu_fpc_ff2_firmware_parse;
firmware_class->export = fu_fpc_ff2_firmware_export;
}
15 changes: 15 additions & 0 deletions plugins/fpc/fu-fpc-ff2-firmware.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright 2024 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/

#pragma once

#include <fwupdplugin.h>

#define FU_TYPE_FPC_FF2_FIRMWARE (fu_fpc_ff2_firmware_get_type())
G_DECLARE_FINAL_TYPE(FuFpcFf2Firmware, fu_fpc_ff2_firmware, FU, FPC_FF2_FIRMWARE, FuFirmware)

guint32
fu_fpc_ff2_firmware_get_blocks_num(FuFpcFf2Firmware *self);
2 changes: 2 additions & 0 deletions plugins/fpc/fu-fpc-plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "config.h"

#include "fu-fpc-device.h"
#include "fu-fpc-ff2-firmware.h"
#include "fu-fpc-plugin.h"

struct _FuFpcPlugin {
Expand All @@ -25,6 +26,7 @@ fu_fpc_constructed(GObject *obj)
{
FuPlugin *plugin = FU_PLUGIN(obj);
fu_plugin_add_device_gtype(plugin, FU_TYPE_FPC_DEVICE);
fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_FPC_FF2_FIRMWARE);
}

static void
Expand Down
30 changes: 30 additions & 0 deletions plugins/fpc/fu-fpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,33 @@ struct FuStructFpcDfu {
state: FuFpcDfuState,
_reserved2: u8,
}

#[derive(ValidateStream, ParseStream)]
struct FuStructFpcFf2Hdr {
compat_sig: [char; 7] == "FPC0001",
reserved: [u8; 20],
blocks_num: u32le,
reserved: [u8; 6],
}

#[repr(u8)]
enum FuFpcFf2BlockDir {
Out = 0x0,
In = 0x1,
}

// dfu_meta_content_hdr_t
#[derive(ParseStream)]
struct FuStructFpcFf2BlockHdr {
meta_type: u8 == 0xCD,
meta_id: u8,
dir: FuFpcFf2BlockDir,
}

// dfu_sec_link_t
#[derive(ParseStream)]
struct FuStructFpcFf2BlockSec {
header: u8 == 0xEE,
type: u8,
payload_len: u16le,
}
1 change: 1 addition & 0 deletions plugins/fpc/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ plugin_builtins += static_library('fu_plugin_fpc',
sources: [
'fu-fpc-device.c',
'fu-fpc-plugin.c',
'fu-fpc-ff2-firmware.c',
],
include_directories: plugin_incdirs,
link_with: plugin_libs,
Expand Down

0 comments on commit 0299fd9

Please sign in to comment.