mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-24 06:26:57 +00:00
638 lines
18 KiB
C
638 lines
18 KiB
C
/*
|
|
* Copyright (C) 2021, TUXEDO Computers GmbH
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <fwupdplugin.h>
|
|
|
|
#include "fu-superio-common.h"
|
|
#include "fu-superio-it55-device.h"
|
|
|
|
/* ROM of IT5570 consists of 64KB blocks. Blocks can be further subdivided in
|
|
* 256-byte chunks, which is especially visible when erasing the ROM. This is
|
|
* because in case of erasure offset within a block is specified in chunks (even
|
|
* though erasure is done one kilobyte at a time).
|
|
*
|
|
* Accessing ROM requires entering a special mode, which should be always left
|
|
* to restore normal operation of EC (handling of buttons, keyboard, etc.). */
|
|
|
|
#define SIO_CMD_EC_WRITE_BLOCK 0x02
|
|
#define SIO_CMD_EC_READ_BLOCK 0x03
|
|
#define SIO_CMD_EC_ERASE_KBYTE 0x05
|
|
#define SIO_CMD_EC_WRITE_1ST_KBYTE 0x06
|
|
#define EC_ROM_ACCESS_ON_1 0xDE
|
|
#define EC_ROM_ACCESS_ON_2 0xDC
|
|
#define EC_ROM_ACCESS_OFF 0xFE
|
|
|
|
#define BLOCK_SIZE 0x10000
|
|
#define CHUNK_SIZE 0x100
|
|
#define CHUNKS_IN_KBYTE 0x4
|
|
#define CHUNKS_IN_BLOCK 0x100
|
|
|
|
#define MAX_FLASHING_ATTEMPTS 5
|
|
|
|
typedef enum {
|
|
AUTOLOAD_NO_ACTION,
|
|
AUTOLOAD_DISABLE,
|
|
AUTOLOAD_SET_ON,
|
|
AUTOLOAD_SET_OFF,
|
|
} AutoloadAction;
|
|
|
|
struct _FuEcIt55Device {
|
|
FuSuperioDevice parent_instance;
|
|
gchar *prj_name;
|
|
AutoloadAction autoload_action;
|
|
};
|
|
|
|
G_DEFINE_TYPE (FuEcIt55Device, fu_superio_it55_device, FU_TYPE_SUPERIO_DEVICE)
|
|
|
|
|
|
static void
|
|
fu_superio_it55_device_to_string (FuDevice *device, guint idt, GString *str)
|
|
{
|
|
FuEcIt55Device *self = FU_SUPERIO_IT55_DEVICE (device);
|
|
|
|
/* FuSuperioDevice->to_string */
|
|
FU_DEVICE_CLASS (fu_superio_it55_device_parent_class)->to_string (device, idt, str);
|
|
|
|
fu_common_string_append_kx (str, idt, "AutoloadAction", self->autoload_action);
|
|
}
|
|
|
|
static gboolean
|
|
fu_superio_it55_device_ec_project (FuSuperioDevice *device, GError **error)
|
|
{
|
|
FuEcIt55Device *self = FU_SUPERIO_IT55_DEVICE (device);
|
|
gchar project[16] = { 0x0 };
|
|
|
|
if (!fu_superio_device_ec_write_cmd (device, SIO_CMD_EC_GET_NAME_STR, error))
|
|
return FALSE;
|
|
|
|
for (guint i = 0; i < sizeof(project) - 1; ++i) {
|
|
guint8 tmp = 0;
|
|
if (!fu_superio_device_ec_read_data (device, &tmp, error)) {
|
|
g_prefix_error (error, "failed to read firmware project: ");
|
|
return FALSE;
|
|
}
|
|
if (tmp == '$')
|
|
break;
|
|
project[i] = tmp;
|
|
}
|
|
|
|
self->prj_name = g_strdup (project);
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_superio_it55_device_ec_version (FuSuperioDevice *self, GError **error)
|
|
{
|
|
gchar version[16] = { '1', '.', '\0' };
|
|
|
|
if (!fu_superio_device_ec_write_cmd (self, SIO_CMD_EC_GET_VERSION_STR, error))
|
|
return FALSE;
|
|
|
|
for (guint i = 2; i < sizeof(version) - 1; i++) {
|
|
guint8 tmp = 0;
|
|
if (!fu_superio_device_ec_read_data (self, &tmp, error)) {
|
|
g_prefix_error (error, "failed to read firmware version: ");
|
|
return FALSE;
|
|
}
|
|
if (tmp == '$')
|
|
break;
|
|
|
|
version[i] = tmp;
|
|
}
|
|
|
|
fu_device_set_version (FU_DEVICE (self), version);
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_superio_it55_device_ec_size (FuSuperioDevice *self, GError **error)
|
|
{
|
|
guint8 tmp = 0;
|
|
|
|
if (!fu_superio_device_reg_read (self, 0xf9, &tmp, error))
|
|
return FALSE;
|
|
switch (tmp & 0xf0) {
|
|
case 0xf0:
|
|
fu_device_set_firmware_size (FU_DEVICE (self), BLOCK_SIZE*4);
|
|
break;
|
|
case 0x40:
|
|
fu_device_set_firmware_size (FU_DEVICE (self), BLOCK_SIZE*3);
|
|
break;
|
|
default:
|
|
fu_device_set_firmware_size (FU_DEVICE (self), BLOCK_SIZE*2);
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_superio_it55_device_setup (FuDevice *device, GError **error)
|
|
{
|
|
FuSuperioDevice *self = FU_SUPERIO_DEVICE (device);
|
|
|
|
/* FuSuperioDevice->setup */
|
|
if (!FU_DEVICE_CLASS (fu_superio_it55_device_parent_class)->setup (device, error))
|
|
return FALSE;
|
|
|
|
/* basic initialization */
|
|
if (!fu_superio_device_reg_write (self, 0xf9, 0x20, error) ||
|
|
!fu_superio_device_reg_write (self, 0xfa, 0x02, error) ||
|
|
!fu_superio_device_reg_write (self, 0xfb, 0x00, error) ||
|
|
!fu_superio_device_reg_write (self, 0xf8, 0xb1, error)) {
|
|
g_prefix_error (error, "initialization: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Order of interactions with EC below matters. Additionally, reading EC
|
|
* project seems to be mandatory for successful firmware operations.
|
|
* Test after making changes here! */
|
|
|
|
/* get size from the EC */
|
|
if (!fu_superio_it55_device_ec_size (self, error))
|
|
return FALSE;
|
|
|
|
/* get installed firmware project from the EC */
|
|
if (!fu_superio_it55_device_ec_project (self, error))
|
|
return FALSE;
|
|
|
|
/* get installed firmware version from the EC */
|
|
if (!fu_superio_it55_device_ec_version (self, error))
|
|
return FALSE;
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
fu_superio_it55_device_progress_cb (goffset current, goffset total, gpointer user_data)
|
|
{
|
|
FuDevice *device = FU_DEVICE (user_data);
|
|
fu_device_set_progress_full (device, (gsize) current, (gsize) total);
|
|
}
|
|
|
|
static GBytes *
|
|
fu_plugin_superio_patch_autoload (FuDevice *device, GBytes *fw, GError **error)
|
|
{
|
|
FuEcIt55Device *self = FU_SUPERIO_IT55_DEVICE (device);
|
|
guint offset;
|
|
gsize sz = 0;
|
|
const guint8 *unpatched = g_bytes_get_data (fw, &sz);
|
|
gboolean small_flash = (sz <= BLOCK_SIZE*2);
|
|
g_autofree guint8 *patched = NULL;
|
|
|
|
if (self->autoload_action == AUTOLOAD_NO_ACTION)
|
|
return g_bytes_ref (fw);
|
|
|
|
for (offset = 0; offset < sz - 6; ++offset) {
|
|
if (unpatched[offset] == 0xa5 &&
|
|
(unpatched[offset + 1] == 0xa5 || unpatched[offset + 1] == 0xa4) &&
|
|
unpatched[offset + 5] == 0x5a)
|
|
break;
|
|
}
|
|
|
|
if (offset >= sz - 6)
|
|
return g_bytes_ref (fw);
|
|
|
|
/* not big enough */
|
|
if (offset + 8 >= sz) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"image is too small to patch");
|
|
return NULL;
|
|
}
|
|
|
|
patched = fu_memdup_safe (unpatched, sz, error);
|
|
if (patched == NULL)
|
|
return NULL;
|
|
|
|
if (self->autoload_action == AUTOLOAD_DISABLE) {
|
|
patched[offset + 2] = (small_flash ? 0x94 : 0x85);
|
|
patched[offset + 8] = 0x00;
|
|
} else if (self->autoload_action == AUTOLOAD_SET_ON) {
|
|
patched[offset + 2] = (small_flash ? 0x94 : 0x85);
|
|
patched[offset + 8] = (small_flash ? 0x7f : 0xbe);
|
|
} else if (self->autoload_action == AUTOLOAD_SET_OFF) {
|
|
patched[offset + 2] = (small_flash ? 0xa5 : 0xb5);
|
|
patched[offset + 8] = 0xaa;
|
|
}
|
|
|
|
return g_bytes_new_take (g_steal_pointer (&patched), sz);
|
|
}
|
|
|
|
/* progress callback is optional to not affect device progress during writing
|
|
* firmware */
|
|
static GBytes *
|
|
fu_superio_it55_device_get_firmware (FuDevice *device,
|
|
GFileProgressCallback progress_cb,
|
|
GError **error)
|
|
{
|
|
FuSuperioDevice *self = FU_SUPERIO_DEVICE (device);
|
|
guint64 fwsize = fu_device_get_firmware_size_min (device);
|
|
guint64 block_count = (fwsize + BLOCK_SIZE - 1)/BLOCK_SIZE;
|
|
goffset offset = 0;
|
|
g_autofree guint8 *buf = NULL;
|
|
|
|
buf = g_malloc0 (fwsize);
|
|
for (guint i = 0; i < block_count; ++i) {
|
|
if (!fu_superio_device_ec_write_cmd (self, SIO_CMD_EC_READ_BLOCK, error) ||
|
|
!fu_superio_device_ec_write_cmd (self, i, error))
|
|
return NULL;
|
|
|
|
for (guint j = 0; j < BLOCK_SIZE; ++j, ++offset) {
|
|
if (!fu_superio_device_ec_read_data (self, &buf[offset], error))
|
|
return NULL;
|
|
|
|
if (progress_cb != NULL)
|
|
progress_cb (offset, (goffset) fwsize, self);
|
|
}
|
|
}
|
|
|
|
return g_bytes_new_take (g_steal_pointer (&buf), fwsize);
|
|
}
|
|
|
|
static GBytes *
|
|
fu_superio_it55_device_dump_firmware (FuDevice *device, GError **error)
|
|
{
|
|
g_autoptr(FuDeviceLocker) locker = NULL;
|
|
|
|
/* require detach -> attach */
|
|
locker = fu_device_locker_new_full (device,
|
|
(FuDeviceLockerFunc) fu_device_detach,
|
|
(FuDeviceLockerFunc) fu_device_attach,
|
|
error);
|
|
if (locker == NULL)
|
|
return NULL;
|
|
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_READ);
|
|
return fu_superio_it55_device_get_firmware (device,
|
|
fu_superio_it55_device_progress_cb,
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_superio_it55_device_attach (FuDevice *device, GError **error)
|
|
{
|
|
FuSuperioDevice *self = FU_SUPERIO_DEVICE (device);
|
|
|
|
if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER))
|
|
return TRUE;
|
|
|
|
/* leave ROM access mode */
|
|
if (!fu_superio_device_ec_write_cmd (self, EC_ROM_ACCESS_OFF, error))
|
|
return FALSE;
|
|
|
|
/* success */
|
|
fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_superio_it55_device_detach (FuDevice *device, GError **error)
|
|
{
|
|
FuSuperioDevice *self = FU_SUPERIO_DEVICE (device);
|
|
|
|
if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER))
|
|
return TRUE;
|
|
|
|
/* enter ROM access mode */
|
|
if (!fu_superio_device_ec_write_cmd (self, EC_ROM_ACCESS_ON_1, error) ||
|
|
!fu_superio_device_ec_write_cmd (self, EC_ROM_ACCESS_ON_2, error))
|
|
return FALSE;
|
|
|
|
/* success */
|
|
fu_device_add_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_superio_it55_device_erase (FuDevice *device, GError **error)
|
|
{
|
|
FuSuperioDevice *self = FU_SUPERIO_DEVICE (device);
|
|
guint64 fwsize = fu_device_get_firmware_size_min (device);
|
|
guint64 chunk_count = (fwsize + CHUNK_SIZE - 1)/CHUNK_SIZE;
|
|
|
|
for (guint i = 0; i < chunk_count; i += CHUNKS_IN_KBYTE) {
|
|
if (!fu_superio_device_ec_write_cmd (self, SIO_CMD_EC_ERASE_KBYTE, error) ||
|
|
!fu_superio_device_ec_write_cmd (self, i/CHUNKS_IN_BLOCK, error) ||
|
|
!fu_superio_device_ec_write_cmd (self, i%CHUNKS_IN_BLOCK, error) ||
|
|
!fu_superio_device_ec_write_cmd (self, 0x00, error))
|
|
return FALSE;
|
|
|
|
g_usleep (1000);
|
|
}
|
|
|
|
g_usleep (100000);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_superio_it55_device_write_attempt (FuDevice *device, GBytes *firmware, GError **error)
|
|
{
|
|
FuSuperioDevice *self = FU_SUPERIO_DEVICE (device);
|
|
gsize fwsize = g_bytes_get_size (firmware);
|
|
guint64 total = (fwsize + CHUNK_SIZE - 1)/CHUNK_SIZE;
|
|
const guint8 *fw_data = NULL;
|
|
g_autoptr(GBytes) erased_fw = NULL;
|
|
g_autoptr(GBytes) written_fw = NULL;
|
|
g_autoptr(GPtrArray) blocks = NULL;
|
|
|
|
if (!fu_superio_it55_device_erase (device, error))
|
|
return FALSE;
|
|
|
|
erased_fw = fu_superio_it55_device_get_firmware (device, NULL, error);
|
|
if (erased_fw == NULL) {
|
|
g_prefix_error (error, "failed to read erased firmware");
|
|
return FALSE;
|
|
}
|
|
if (!fu_common_bytes_is_empty (erased_fw)) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_READ,
|
|
"firmware was not erased");
|
|
return FALSE;
|
|
}
|
|
|
|
/* write everything but the first kilobyte */
|
|
blocks = fu_chunk_array_new_from_bytes (firmware, 0x00, 0x00, BLOCK_SIZE);
|
|
for (guint i = 0; i < blocks->len; ++i) {
|
|
FuChunk *block = g_ptr_array_index (blocks, i);
|
|
gboolean first = (i == 0);
|
|
guint32 offset = 0;
|
|
guint32 bytes_left = fu_chunk_get_data_sz (block);
|
|
const guint8 *data = fu_chunk_get_data (block);
|
|
|
|
if (!fu_superio_device_ec_write_cmd (self, SIO_CMD_EC_WRITE_BLOCK, error) ||
|
|
!fu_superio_device_ec_write_cmd (self, 0x00, error) ||
|
|
!fu_superio_device_ec_write_cmd (self, i, error) ||
|
|
!fu_superio_device_ec_write_cmd (self, first ? 0x04 : 0x00, error) ||
|
|
!fu_superio_device_ec_write_cmd (self, 0x00, error))
|
|
return FALSE;
|
|
|
|
for (guint j = 0; j < CHUNKS_IN_BLOCK; ++j) {
|
|
gsize progress = i*CHUNKS_IN_BLOCK + j;
|
|
|
|
if (first && j < CHUNKS_IN_KBYTE) {
|
|
offset += CHUNK_SIZE;
|
|
bytes_left -= CHUNK_SIZE;
|
|
fu_device_set_progress_full (device, progress, (gsize) total);
|
|
continue;
|
|
}
|
|
|
|
for (guint k = 0; k < CHUNK_SIZE; ++k) {
|
|
if (bytes_left == 0) {
|
|
if (!fu_superio_device_ec_write_data (self, 0xff, error))
|
|
return FALSE;
|
|
continue;
|
|
}
|
|
|
|
if (!fu_superio_device_ec_write_data (self, data[offset], error))
|
|
return FALSE;
|
|
|
|
++offset;
|
|
--bytes_left;
|
|
}
|
|
|
|
fu_device_set_progress_full (device, progress, (gsize) total);
|
|
}
|
|
}
|
|
|
|
/* now write the first kilobyte */
|
|
if (!fu_superio_device_ec_write_cmd (self, SIO_CMD_EC_WRITE_1ST_KBYTE, error))
|
|
return FALSE;
|
|
fw_data = g_bytes_get_data (firmware, NULL);
|
|
for (guint i = 0; i < CHUNK_SIZE*CHUNKS_IN_KBYTE; ++i)
|
|
if (!fu_superio_device_ec_write_data (self, fw_data[i], error))
|
|
return FALSE;
|
|
|
|
g_usleep (1000);
|
|
|
|
written_fw = fu_superio_it55_device_get_firmware (device, NULL, error);
|
|
if (written_fw == NULL) {
|
|
g_prefix_error (error, "failed to read erased firmware");
|
|
return FALSE;
|
|
}
|
|
if (!fu_common_bytes_compare (written_fw, firmware, error)) {
|
|
g_prefix_error (error, "firmware verification");
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
fu_device_set_progress (device, 100);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_superio_it55_device_write_firmware (FuDevice *device,
|
|
FuFirmware *firmware,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
gsize fwsize;
|
|
g_autoptr(GBytes) fw = NULL;
|
|
g_autoptr(GBytes) fw_patched = NULL;
|
|
g_autoptr(FuDeviceLocker) locker = NULL;
|
|
|
|
/* require detach -> attach */
|
|
locker = fu_device_locker_new_full (device,
|
|
(FuDeviceLockerFunc) fu_device_detach,
|
|
(FuDeviceLockerFunc) fu_device_attach,
|
|
error);
|
|
if (locker == NULL)
|
|
return FALSE;
|
|
|
|
/* get default image */
|
|
fw = fu_firmware_get_bytes (firmware, error);
|
|
if (fw == NULL)
|
|
return FALSE;
|
|
|
|
fwsize = g_bytes_get_size (fw);
|
|
if (fwsize < 1024) {
|
|
g_prefix_error (error, "firmware is too small: %u", (guint) fwsize);
|
|
return FALSE;
|
|
}
|
|
|
|
fw_patched = fu_plugin_superio_patch_autoload (device, fw, error);
|
|
if (fw_patched == NULL)
|
|
return FALSE;
|
|
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);
|
|
|
|
/* try this many times; the failure-to-flash case leaves you without a
|
|
* keyboard and future boot may completely fail */
|
|
for (guint i = 1;; ++i) {
|
|
g_autoptr(GError) error_chk = NULL;
|
|
if (fu_superio_it55_device_write_attempt (device, fw_patched, &error_chk))
|
|
break;
|
|
|
|
if (i == MAX_FLASHING_ATTEMPTS) {
|
|
g_propagate_error (error, g_steal_pointer (&error_chk));
|
|
return FALSE;
|
|
}
|
|
|
|
g_warning ("failure %u: %s", i, error_chk->message);
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static gchar *
|
|
fu_ec_extract_field (GBytes *fw, const gchar *name, GError **error)
|
|
{
|
|
guint offset;
|
|
gsize prefix_len;
|
|
gsize fwsz = 0;
|
|
const gchar *value;
|
|
const guint8 *buf = g_bytes_get_data (fw, &fwsz);
|
|
g_autofree gchar *field = g_strdup_printf ("%s:", name);
|
|
|
|
prefix_len = strlen (field);
|
|
|
|
for (offset = 0; offset < fwsz - prefix_len; ++offset) {
|
|
if (memcmp (&buf[offset], field, prefix_len) == 0)
|
|
break;
|
|
}
|
|
|
|
if (offset >= fwsz - prefix_len) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"did not find %s field in the firmware image",
|
|
name);
|
|
return NULL;
|
|
}
|
|
|
|
offset += prefix_len;
|
|
value = (const gchar *) &buf[offset];
|
|
|
|
for (; offset < fwsz; ++offset) {
|
|
if (buf[offset] == '$')
|
|
return g_strndup (value, (const gchar *)&buf[offset] - value);
|
|
}
|
|
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"couldn't extract %s field value from the firmware image",
|
|
name);
|
|
return NULL;
|
|
}
|
|
|
|
static FuFirmware *
|
|
fu_superio_it55_device_prepare_firmware (FuDevice *device,
|
|
GBytes *fw,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
FuEcIt55Device *self = FU_SUPERIO_IT55_DEVICE (device);
|
|
g_autofree gchar *date = NULL;
|
|
g_autofree gchar *prj_name = NULL;
|
|
g_autofree gchar *version = NULL;
|
|
|
|
prj_name = fu_ec_extract_field (fw, "PRJ", error);
|
|
if (prj_name == NULL)
|
|
return NULL;
|
|
|
|
version = fu_ec_extract_field (fw, "VER", error);
|
|
if (version == NULL)
|
|
version = g_strdup ("(unknown version)");
|
|
|
|
date = fu_ec_extract_field (fw, "DATE", error);
|
|
if (date == NULL)
|
|
date = g_strdup ("(unknown build date)");
|
|
|
|
g_debug ("New firmware: %s %s built on %s", prj_name, version, date);
|
|
if (g_strcmp0 (prj_name, self->prj_name) != 0) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"firmware targets %s instead of %s",
|
|
prj_name,
|
|
self->prj_name);
|
|
return NULL;
|
|
}
|
|
|
|
return fu_firmware_new_from_bytes (fw);
|
|
}
|
|
|
|
static gboolean
|
|
fu_superio_it55_device_set_quirk_kv (FuDevice *device,
|
|
const gchar *key,
|
|
const gchar *value,
|
|
GError **error)
|
|
{
|
|
FuEcIt55Device *self = FU_SUPERIO_IT55_DEVICE (device);
|
|
|
|
/* FuSuperioDevice->set_quirk_kv */
|
|
if (!FU_DEVICE_CLASS (fu_superio_it55_device_parent_class)->set_quirk_kv (device, key, value, error))
|
|
return FALSE;
|
|
|
|
if (g_strcmp0 (key, "SuperioAutoloadAction") == 0) {
|
|
if (g_strcmp0 (value, "none") == 0) {
|
|
self->autoload_action = AUTOLOAD_NO_ACTION;
|
|
} else if (g_strcmp0 (value, "disable") == 0) {
|
|
self->autoload_action = AUTOLOAD_DISABLE;
|
|
} else if (g_strcmp0 (value, "on") == 0) {
|
|
self->autoload_action = AUTOLOAD_SET_ON;
|
|
} else if (g_strcmp0 (value, "off") == 0) {
|
|
self->autoload_action = AUTOLOAD_SET_OFF;
|
|
} else {
|
|
g_set_error_literal (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"invalid value");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
fu_superio_it55_device_init (FuEcIt55Device *self)
|
|
{
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE);
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_ONLY_OFFLINE);
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_REQUIRE_AC);
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT);
|
|
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_REQUIRE_AC);
|
|
/* version string example: 1.07.02TR1 */
|
|
fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_PLAIN);
|
|
}
|
|
|
|
static void
|
|
fu_superio_it55_device_finalize (GObject *obj)
|
|
{
|
|
FuEcIt55Device *self = FU_SUPERIO_IT55_DEVICE (obj);
|
|
g_free (self->prj_name);
|
|
}
|
|
|
|
static void
|
|
fu_superio_it55_device_class_init (FuEcIt55DeviceClass *klass)
|
|
{
|
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
|
|
G_OBJECT_CLASS (klass)->finalize = fu_superio_it55_device_finalize;
|
|
klass_device->to_string = fu_superio_it55_device_to_string;
|
|
klass_device->attach = fu_superio_it55_device_attach;
|
|
klass_device->detach = fu_superio_it55_device_detach;
|
|
klass_device->dump_firmware = fu_superio_it55_device_dump_firmware;
|
|
klass_device->write_firmware = fu_superio_it55_device_write_firmware;
|
|
klass_device->setup = fu_superio_it55_device_setup;
|
|
klass_device->prepare_firmware = fu_superio_it55_device_prepare_firmware;
|
|
klass_device->set_quirk_kv = fu_superio_it55_device_set_quirk_kv;
|
|
}
|