fwupd/plugins/vli/fu-vli-device.c
2022-06-14 14:36:52 -05:00

769 lines
21 KiB
C

/*
* Copyright (C) 2017 VIA Corporation
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupdplugin.h>
#include "fu-vli-device.h"
typedef struct {
FuVliDeviceKind kind;
FuCfiDevice *cfi_device;
gboolean spi_auto_detect;
guint8 spi_cmd_read_id_sz;
guint32 flash_id;
} FuVliDevicePrivate;
G_DEFINE_TYPE_WITH_PRIVATE(FuVliDevice, fu_vli_device, FU_TYPE_USB_DEVICE)
#define GET_PRIVATE(o) (fu_vli_device_get_instance_private(o))
enum { PROP_0, PROP_KIND, PROP_LAST };
FuCfiDevice *
fu_vli_device_get_cfi_device(FuVliDevice *self)
{
FuVliDevicePrivate *priv = GET_PRIVATE(self);
return priv->cfi_device;
}
static gboolean
fu_vli_device_spi_write_enable(FuVliDevice *self, GError **error)
{
FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self);
if (klass->spi_write_enable != NULL) {
if (!klass->spi_write_enable(self, error)) {
g_prefix_error(error, "failed to write enable SPI: ");
return FALSE;
}
}
return TRUE;
}
static gboolean
fu_vli_device_spi_chip_erase(FuVliDevice *self, GError **error)
{
FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self);
if (klass->spi_chip_erase != NULL) {
if (!klass->spi_chip_erase(self, error)) {
g_prefix_error(error, "failed to erase SPI data: ");
return FALSE;
}
}
return TRUE;
}
static gboolean
fu_vli_device_spi_write_status(FuVliDevice *self, guint8 status, GError **error)
{
FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self);
if (klass->spi_write_status != NULL) {
if (!klass->spi_write_status(self, status, error)) {
g_prefix_error(error, "failed to write SPI status 0x%x: ", status);
return FALSE;
}
}
return TRUE;
}
static gboolean
fu_vli_device_spi_read_status(FuVliDevice *self, guint8 *status, GError **error)
{
FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self);
if (klass->spi_read_status != NULL) {
if (!klass->spi_read_status(self, status, error)) {
g_prefix_error(error, "failed to read status: ");
return FALSE;
}
}
return TRUE;
}
static gboolean
fu_vli_device_spi_sector_erase(FuVliDevice *self, guint32 addr, GError **error)
{
FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self);
if (klass->spi_sector_erase != NULL) {
if (!klass->spi_sector_erase(self, addr, error)) {
g_prefix_error(error, "failed to erase SPI data @0x%x: ", addr);
return FALSE;
}
}
return TRUE;
}
gboolean
fu_vli_device_spi_read_block(FuVliDevice *self,
guint32 addr,
guint8 *buf,
gsize bufsz,
GError **error)
{
FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self);
if (klass->spi_read_data != NULL) {
if (!klass->spi_read_data(self, addr, buf, bufsz, error)) {
g_prefix_error(error, "failed to read SPI data @0x%x: ", addr);
return FALSE;
}
}
return TRUE;
}
static gboolean
fu_vli_device_spi_write_data(FuVliDevice *self,
guint32 addr,
const guint8 *buf,
gsize bufsz,
GError **error)
{
FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self);
if (klass->spi_write_data != NULL) {
if (!klass->spi_write_data(self, addr, buf, bufsz, error)) {
g_prefix_error(error, "failed to write SPI data @0x%x: ", addr);
return FALSE;
}
}
return TRUE;
}
static gboolean
fu_vli_device_spi_wait_finish(FuVliDevice *self, GError **error)
{
const guint32 rdy_cnt = 2;
guint32 cnt = 0;
for (guint32 idx = 0; idx < 1000; idx++) {
guint8 status = 0x7f;
/* must get bit[1:0] == 0 twice in a row for success */
if (!fu_vli_device_spi_read_status(self, &status, error))
return FALSE;
if ((status & 0x03) == 0x00) {
if (cnt++ >= rdy_cnt)
return TRUE;
} else {
cnt = 0;
}
g_usleep(500 * 1000);
}
g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "failed to wait for SPI");
return FALSE;
}
gboolean
fu_vli_device_spi_erase_sector(FuVliDevice *self, guint32 addr, GError **error)
{
const guint32 bufsz = 0x1000;
/* erase sector */
if (!fu_vli_device_spi_write_enable(self, error)) {
g_prefix_error(error, "->spi_write_enable failed: ");
return FALSE;
}
if (!fu_vli_device_spi_write_status(self, 0x00, error)) {
g_prefix_error(error, "->spi_write_status failed: ");
return FALSE;
}
if (!fu_vli_device_spi_write_enable(self, error)) {
g_prefix_error(error, "->spi_write_enable failed: ");
return FALSE;
}
if (!fu_vli_device_spi_sector_erase(self, addr, error)) {
g_prefix_error(error, "->spi_sector_erase failed: ");
return FALSE;
}
if (!fu_vli_device_spi_wait_finish(self, error)) {
g_prefix_error(error, "->spi_wait_finish failed: ");
return FALSE;
}
/* verify it really was blanked */
for (guint32 offset = 0; offset < bufsz; offset += FU_VLI_DEVICE_TXSIZE) {
guint8 buf[FU_VLI_DEVICE_TXSIZE] = {0x0};
if (!fu_vli_device_spi_read_block(self, addr + offset, buf, sizeof(buf), error)) {
g_prefix_error(error, "failed to read back empty: ");
return FALSE;
}
for (guint i = 0; i < sizeof(buf); i++) {
if (buf[i] != 0xff) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"failed to check blank @0x%x",
addr + offset + i);
return FALSE;
}
}
}
/* success */
return TRUE;
}
GBytes *
fu_vli_device_spi_read(FuVliDevice *self,
guint32 address,
gsize bufsz,
FuProgress *progress,
GError **error)
{
g_autofree guint8 *buf = g_malloc0(bufsz);
g_autoptr(GPtrArray) chunks = NULL;
/* get data from hardware */
chunks = fu_chunk_array_mutable_new(buf, bufsz, address, 0x0, FU_VLI_DEVICE_TXSIZE);
fu_progress_set_steps(progress, chunks->len);
for (guint i = 0; i < chunks->len; i++) {
FuChunk *chk = g_ptr_array_index(chunks, i);
if (!fu_vli_device_spi_read_block(self,
fu_chunk_get_address(chk),
fu_chunk_get_data_out(chk),
fu_chunk_get_data_sz(chk),
error)) {
g_prefix_error(error,
"SPI data read failed @0x%x: ",
fu_chunk_get_address(chk));
return NULL;
}
fu_progress_step_done(progress);
}
return g_bytes_new_take(g_steal_pointer(&buf), bufsz);
}
gboolean
fu_vli_device_spi_write_block(FuVliDevice *self,
guint32 address,
const guint8 *buf,
gsize bufsz,
FuProgress *progress,
GError **error)
{
g_autofree guint8 *buf_tmp = g_malloc0(bufsz);
/* sanity check */
if (bufsz > FU_VLI_DEVICE_TXSIZE) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"cannot write 0x%x in one block",
(guint)bufsz);
return FALSE;
}
/* write */
if (g_getenv("FWUPD_VLI_USBHUB_VERBOSE") != NULL)
g_debug("writing 0x%x block @0x%x", (guint)bufsz, address);
if (!fu_vli_device_spi_write_enable(self, error)) {
g_prefix_error(error, "enabling SPI write failed: ");
return FALSE;
}
if (!fu_vli_device_spi_write_data(self, address, buf, bufsz, error)) {
g_prefix_error(error, "SPI data write failed: ");
return FALSE;
}
g_usleep(800);
/* verify */
if (!fu_vli_device_spi_read_block(self, address, buf_tmp, bufsz, error)) {
g_prefix_error(error, "SPI data read failed: ");
return FALSE;
}
return fu_memcmp_safe(buf, bufsz, buf_tmp, bufsz, error);
}
gboolean
fu_vli_device_spi_write(FuVliDevice *self,
guint32 address,
const guint8 *buf,
gsize bufsz,
FuProgress *progress,
GError **error)
{
FuChunk *chk;
g_autoptr(GPtrArray) chunks = NULL;
/* progress */
fu_progress_set_id(progress, G_STRLOC);
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 99, NULL);
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "device-write-chk0");
/* write SPI data, then CRC bytes last */
g_debug("writing 0x%x bytes @0x%x", (guint)bufsz, address);
chunks = fu_chunk_array_new(buf, bufsz, 0x0, 0x0, FU_VLI_DEVICE_TXSIZE);
if (chunks->len > 1) {
FuProgress *progress_local = fu_progress_get_child(progress);
fu_progress_set_id(progress_local, G_STRLOC);
fu_progress_set_steps(progress_local, chunks->len - 1);
for (guint i = 1; i < chunks->len; i++) {
chk = g_ptr_array_index(chunks, i);
if (!fu_vli_device_spi_write_block(self,
fu_chunk_get_address(chk) + address,
fu_chunk_get_data(chk),
fu_chunk_get_data_sz(chk),
fu_progress_get_child(progress_local),
error)) {
g_prefix_error(error,
"failed to write block 0x%x: ",
fu_chunk_get_idx(chk));
return FALSE;
}
fu_progress_step_done(progress_local);
}
}
fu_progress_step_done(progress);
/* chk0 */
chk = g_ptr_array_index(chunks, 0);
if (!fu_vli_device_spi_write_block(self,
fu_chunk_get_address(chk) + address,
fu_chunk_get_data(chk),
fu_chunk_get_data_sz(chk),
fu_progress_get_child(progress),
error)) {
g_prefix_error(error, "failed to write CRC block: ");
return FALSE;
}
fu_progress_step_done(progress);
return TRUE;
}
gboolean
fu_vli_device_spi_erase_all(FuVliDevice *self, FuProgress *progress, GError **error)
{
/* progress */
fu_progress_set_id(progress, G_STRLOC);
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 99, NULL);
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 1, NULL);
if (!fu_vli_device_spi_write_enable(self, error))
return FALSE;
if (!fu_vli_device_spi_write_status(self, 0x00, error))
return FALSE;
if (!fu_vli_device_spi_write_enable(self, error))
return FALSE;
if (!fu_vli_device_spi_chip_erase(self, error))
return FALSE;
fu_progress_sleep(fu_progress_get_child(progress), 4000);
fu_progress_step_done(progress);
/* verify chip was erased */
for (guint addr = 0; addr < 0x10000; addr += 0x1000) {
guint8 buf[FU_VLI_DEVICE_TXSIZE] = {0x0};
if (!fu_vli_device_spi_read_block(self, addr, buf, sizeof(buf), error)) {
g_prefix_error(error, "failed to read @0x%x: ", addr);
return FALSE;
}
for (guint i = 0; i < sizeof(buf); i++) {
if (buf[i] != 0xff) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"failed to verify erase @0x%x: ",
addr);
return FALSE;
}
}
fu_progress_set_percentage_full(fu_progress_get_child(progress),
(gsize)addr + 0x1000,
(gsize)0x10000);
}
fu_progress_step_done(progress);
return TRUE;
}
gboolean
fu_vli_device_spi_erase(FuVliDevice *self,
guint32 addr,
gsize sz,
FuProgress *progress,
GError **error)
{
g_autoptr(GPtrArray) chunks = fu_chunk_array_new(NULL, sz, addr, 0x0, 0x1000);
g_debug("erasing 0x%x bytes @0x%x", (guint)sz, addr);
fu_progress_set_steps(progress, chunks->len);
for (guint i = 0; i < chunks->len; i++) {
FuChunk *chk = g_ptr_array_index(chunks, i);
if (g_getenv("FWUPD_VLI_USBHUB_VERBOSE") != NULL)
g_debug("erasing @0x%x", fu_chunk_get_address(chk));
if (!fu_vli_device_spi_erase_sector(FU_VLI_DEVICE(self),
fu_chunk_get_address(chk),
error)) {
g_prefix_error(error,
"failed to erase FW sector @0x%x: ",
fu_chunk_get_address(chk));
return FALSE;
}
fu_progress_step_done(progress);
}
return TRUE;
}
static gchar *
fu_vli_device_get_flash_id_str(FuVliDevice *self)
{
FuVliDevicePrivate *priv = GET_PRIVATE(self);
if (priv->spi_cmd_read_id_sz == 4)
return g_strdup_printf("%08X", priv->flash_id);
if (priv->spi_cmd_read_id_sz == 2)
return g_strdup_printf("%04X", priv->flash_id);
if (priv->spi_cmd_read_id_sz == 1)
return g_strdup_printf("%02X", priv->flash_id);
return g_strdup_printf("%X", priv->flash_id);
}
void
fu_vli_device_set_kind(FuVliDevice *self, FuVliDeviceKind device_kind)
{
FuVliDevicePrivate *priv = GET_PRIVATE(self);
guint32 sz;
/* set and notify if different */
if (priv->kind != device_kind) {
priv->kind = device_kind;
g_object_notify(G_OBJECT(self), "kind");
}
/* newer chips use SHA-256 and ECDSA-256 */
switch (device_kind) {
case FU_VLI_DEVICE_KIND_MSP430:
case FU_VLI_DEVICE_KIND_PS186:
case FU_VLI_DEVICE_KIND_RTD21XX:
case FU_VLI_DEVICE_KIND_VL100:
case FU_VLI_DEVICE_KIND_VL101:
case FU_VLI_DEVICE_KIND_VL102:
case FU_VLI_DEVICE_KIND_VL103:
case FU_VLI_DEVICE_KIND_VL104:
case FU_VLI_DEVICE_KIND_VL105:
case FU_VLI_DEVICE_KIND_VL120:
case FU_VLI_DEVICE_KIND_VL210:
case FU_VLI_DEVICE_KIND_VL211:
case FU_VLI_DEVICE_KIND_VL212:
case FU_VLI_DEVICE_KIND_VL810:
case FU_VLI_DEVICE_KIND_VL811:
case FU_VLI_DEVICE_KIND_VL811PB0:
case FU_VLI_DEVICE_KIND_VL811PB3:
case FU_VLI_DEVICE_KIND_VL812B0:
case FU_VLI_DEVICE_KIND_VL812B3:
case FU_VLI_DEVICE_KIND_VL812Q4S:
case FU_VLI_DEVICE_KIND_VL813:
case FU_VLI_DEVICE_KIND_VL815:
case FU_VLI_DEVICE_KIND_VL817:
case FU_VLI_DEVICE_KIND_VL819Q7:
case FU_VLI_DEVICE_KIND_VL819Q8:
case FU_VLI_DEVICE_KIND_VL820Q7:
case FU_VLI_DEVICE_KIND_VL820Q8:
case FU_VLI_DEVICE_KIND_VL821Q7:
case FU_VLI_DEVICE_KIND_VL821Q8:
case FU_VLI_DEVICE_KIND_VL822Q5:
case FU_VLI_DEVICE_KIND_VL822Q7:
case FU_VLI_DEVICE_KIND_VL822Q8:
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
break;
case FU_VLI_DEVICE_KIND_VL107:
case FU_VLI_DEVICE_KIND_VL650:
case FU_VLI_DEVICE_KIND_VL830:
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD);
break;
default:
g_warning("device kind %s [0x%02x] does not indicate unsigned/signed payload",
fu_vli_common_device_kind_to_string(device_kind),
device_kind);
break;
}
/* set maximum firmware size */
sz = fu_vli_common_device_kind_get_size(device_kind);
if (sz > 0x0)
fu_device_set_firmware_size_max(FU_DEVICE(self), sz);
/* add extra DEV GUID too */
fu_device_add_instance_str(FU_DEVICE(self),
"DEV",
fu_vli_common_device_kind_to_string(priv->kind));
fu_device_build_instance_id(FU_DEVICE(self), NULL, "USB", "VID", "PID", "DEV", NULL);
}
void
fu_vli_device_set_spi_auto_detect(FuVliDevice *self, gboolean spi_auto_detect)
{
FuVliDevicePrivate *priv = GET_PRIVATE(self);
priv->spi_auto_detect = spi_auto_detect;
}
FuVliDeviceKind
fu_vli_device_get_kind(FuVliDevice *self)
{
FuVliDevicePrivate *priv = GET_PRIVATE(self);
return priv->kind;
}
guint32
fu_vli_device_get_offset(FuVliDevice *self)
{
FuVliDevicePrivate *priv = GET_PRIVATE(self);
return fu_vli_common_device_kind_get_offset(priv->kind);
}
static void
fu_vli_device_to_string(FuDevice *device, guint idt, GString *str)
{
FuVliDevice *self = FU_VLI_DEVICE(device);
FuVliDevicePrivate *priv = GET_PRIVATE(self);
/* parent */
FU_DEVICE_CLASS(fu_vli_device_parent_class)->to_string(device, idt, str);
if (priv->kind != FU_VLI_DEVICE_KIND_UNKNOWN) {
fu_string_append(str,
idt,
"DeviceKind",
fu_vli_common_device_kind_to_string(priv->kind));
}
fu_string_append_kb(str, idt, "SpiAutoDetect", priv->spi_auto_detect);
if (priv->flash_id != 0x0) {
g_autofree gchar *tmp = fu_vli_device_get_flash_id_str(self);
fu_string_append(str, idt, "FlashId", tmp);
}
fu_device_add_string(FU_DEVICE(priv->cfi_device), idt + 1, str);
}
static gboolean
fu_vli_device_spi_read_flash_id(FuVliDevice *self, GError **error)
{
FuVliDevicePrivate *priv = GET_PRIVATE(self);
GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self));
guint8 buf[4] = {0x0};
guint8 spi_cmd = 0x0;
if (!fu_cfi_device_get_cmd(priv->cfi_device, FU_CFI_DEVICE_CMD_READ_ID, &spi_cmd, error))
return FALSE;
if (!g_usb_device_control_transfer(usb_device,
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
G_USB_DEVICE_RECIPIENT_DEVICE,
0xc0 | (priv->spi_cmd_read_id_sz * 2),
spi_cmd,
0x0000,
buf,
sizeof(buf),
NULL,
FU_VLI_DEVICE_TIMEOUT,
NULL,
error)) {
g_prefix_error(error, "failed to read chip ID: ");
return FALSE;
}
if (g_getenv("FWUPD_VLI_USBHUB_VERBOSE") != NULL)
fu_dump_raw(G_LOG_DOMAIN, "SpiCmdReadId", buf, sizeof(buf));
if (priv->spi_cmd_read_id_sz == 4) {
if (!fu_memread_uint32_safe(buf,
sizeof(buf),
0x0,
&priv->flash_id,
G_BIG_ENDIAN,
error))
return FALSE;
} else if (priv->spi_cmd_read_id_sz == 2) {
guint16 tmp = 0;
if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &tmp, G_BIG_ENDIAN, error))
return FALSE;
priv->flash_id = tmp;
} else if (priv->spi_cmd_read_id_sz == 1) {
guint8 tmp = 0;
if (!fu_memread_uint8_safe(buf, sizeof(buf), 0x0, &tmp, error))
return FALSE;
priv->flash_id = tmp;
}
return TRUE;
}
static gboolean
fu_vli_device_setup(FuDevice *device, GError **error)
{
FuVliDevice *self = FU_VLI_DEVICE(device);
FuVliDevicePrivate *priv = GET_PRIVATE(self);
/* FuUsbDevice->setup */
if (!FU_DEVICE_CLASS(fu_vli_device_parent_class)->setup(device, error))
return FALSE;
/* get the flash chip attached */
if (priv->spi_auto_detect) {
if (!fu_vli_device_spi_read_flash_id(self, error)) {
g_prefix_error(error, "failed to read SPI chip ID: ");
return FALSE;
}
if (priv->flash_id != 0x0) {
g_autofree gchar *flash_id = fu_vli_device_get_flash_id_str(self);
/* use the correct flash device */
fu_cfi_device_set_flash_id(priv->cfi_device, flash_id);
if (!fu_device_setup(FU_DEVICE(priv->cfi_device), error))
return FALSE;
/* add extra instance IDs to include the SPI variant */
fu_device_add_instance_str(device, "SPI", flash_id);
if (!fu_device_build_instance_id(device,
error,
"USB",
"VID",
"PID",
"SPI",
NULL))
return FALSE;
fu_device_build_instance_id(device,
NULL,
"USB",
"VID",
"PID",
"SPI",
"REV",
NULL);
}
}
/* success */
return TRUE;
}
static gboolean
fu_vli_device_set_quirk_kv(FuDevice *device, const gchar *key, const gchar *value, GError **error)
{
FuVliDevice *self = FU_VLI_DEVICE(device);
FuVliDevicePrivate *priv = GET_PRIVATE(self);
guint64 tmp = 0;
if (g_strcmp0(key, "CfiDeviceCmdReadIdSz") == 0) {
if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error))
return FALSE;
priv->spi_cmd_read_id_sz = tmp;
return TRUE;
}
if (g_strcmp0(key, "VliSpiAutoDetect") == 0) {
if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, error))
return FALSE;
priv->spi_auto_detect = tmp > 0;
return TRUE;
}
if (g_strcmp0(key, "VliDeviceKind") == 0) {
FuVliDeviceKind device_kind;
device_kind = fu_vli_common_device_kind_from_string(value);
if (device_kind == FU_VLI_DEVICE_KIND_UNKNOWN) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"VliDeviceKind %s is not supported",
value);
return FALSE;
}
fu_vli_device_set_kind(self, device_kind);
return TRUE;
}
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"quirk key not supported");
return FALSE;
}
static void
fu_vli_device_report_metadata_pre(FuDevice *device, GHashTable *metadata)
{
FuVliDevice *self = FU_VLI_DEVICE(device);
g_hash_table_insert(metadata, g_strdup("GType"), g_strdup(G_OBJECT_TYPE_NAME(self)));
}
static void
fu_vli_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
FuVliDevice *self = FU_VLI_DEVICE(object);
FuVliDevicePrivate *priv = GET_PRIVATE(self);
switch (prop_id) {
case PROP_KIND:
g_value_set_uint(value, priv->kind);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void
fu_vli_device_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
FuVliDevice *self = FU_VLI_DEVICE(object);
switch (prop_id) {
case PROP_KIND:
fu_vli_device_set_kind(self, g_value_get_uint(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void
fu_vli_device_constructed(GObject *obj)
{
FuVliDevice *self = FU_VLI_DEVICE(obj);
FuVliDevicePrivate *priv = GET_PRIVATE(self);
priv->cfi_device = fu_cfi_device_new(fu_device_get_context(FU_DEVICE(self)), NULL);
}
static void
fu_vli_device_init(FuVliDevice *self)
{
FuVliDevicePrivate *priv = GET_PRIVATE(self);
priv->spi_cmd_read_id_sz = 2;
priv->spi_auto_detect = TRUE;
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS);
fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_NO_SERIAL_NUMBER);
}
static void
fu_vli_device_finalize(GObject *obj)
{
FuVliDevice *self = FU_VLI_DEVICE(obj);
FuVliDevicePrivate *priv = GET_PRIVATE(self);
g_object_unref(priv->cfi_device);
G_OBJECT_CLASS(fu_vli_device_parent_class)->finalize(obj);
}
static void
fu_vli_device_class_init(FuVliDeviceClass *klass)
{
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
GObjectClass *object_class = G_OBJECT_CLASS(klass);
GParamSpec *pspec;
/* properties */
object_class->get_property = fu_vli_device_get_property;
object_class->set_property = fu_vli_device_set_property;
object_class->constructed = fu_vli_device_constructed;
object_class->finalize = fu_vli_device_finalize;
/**
* FuVliDevice:kind:
*
* The kind of VLI device.
*/
pspec = g_param_spec_uint("kind",
NULL,
NULL,
0,
G_MAXUINT,
0,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
g_object_class_install_property(object_class, PROP_KIND, pspec);
klass_device->to_string = fu_vli_device_to_string;
klass_device->set_quirk_kv = fu_vli_device_set_quirk_kv;
klass_device->setup = fu_vli_device_setup;
klass_device->report_metadata_pre = fu_vli_device_report_metadata_pre;
}