fwupd/plugins/ccgx/fu-ccgx-hpi-device.c

852 lines
24 KiB
C

/*
* Copyright (C) 2020 Cypress Semiconductor Corporation.
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include "fu-ccgx-common.h"
#include "fu-ccgx-hpi-common.h"
#include "fu-ccgx-hpi-device.h"
#include "fu-ccgx-cyacd-firmware.h"
#include "fu-ccgx-cyacd-firmware-image.h"
struct _FuCcgxHpiDevice
{
FuUsbDevice parent_instance;
guint8 inf_num; /* USB interface number */
guint8 scb_index;
guint16 silicon_id;
guint16 fw_app_type;
guint8 hpi_addrsz; /* hpiv1: 1 byte, hpiv2: 2 byte */
guint8 num_ports; /* max number of ports */
FWMode fw_mode;
guint8 slave_address;
guint8 ep_bulk_in;
guint8 ep_bulk_out;
guint8 ep_intr_in;
guint32 flash_row_size;
guint32 flash_size;
};
G_DEFINE_TYPE (FuCcgxHpiDevice, fu_ccgx_hpi_device, FU_TYPE_USB_DEVICE)
#define HPI_CMD_SETUP_EVENT_WAIT_TIME_MS 200
#define HPI_CMD_RESET_COMPLETE_DELAY_US 150000
static void
fu_ccgx_hpi_device_to_string (FuDevice *device, guint idt, GString *str)
{
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE (device);
fu_common_string_append_kx (str, idt, "InfNum", self->inf_num);
fu_common_string_append_kx (str, idt, "ScbIndex", self->scb_index);
fu_common_string_append_kx (str, idt, "SiliconId", self->silicon_id);
fu_common_string_append_kx (str, idt, "FwAppType", self->fw_app_type);
fu_common_string_append_kx (str, idt, "HpiAddrsz", self->hpi_addrsz);
fu_common_string_append_kx (str, idt, "NumPorts", self->num_ports);
fu_common_string_append_kv (str, idt, "FWMode",
fu_ccgx_fw_mode_to_string (self->fw_mode));
fu_common_string_append_kx (str, idt, "EpBulkIn", self->ep_bulk_in);
fu_common_string_append_kx (str, idt, "EpBulkOut", self->ep_bulk_out);
fu_common_string_append_kx (str, idt, "EpIntrIn", self->ep_intr_in);
fu_common_string_append_kx (str, idt, "FlashRowSize", self->flash_row_size);
fu_common_string_append_kx (str, idt, "FlashSize", self->flash_size);
}
static gboolean
fu_ccgx_hpi_device_get_i2c_status (FuCcgxHpiDevice *self,
guint8 mode,
guint8 *i2c_status, /* out */
GError **error)
{
g_autoptr(GError) error_local = NULL;
if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
G_USB_DEVICE_RECIPIENT_DEVICE,
CY_I2C_GET_STATUS_CMD,
(((guint16) self->scb_index) << CY_SCB_INDEX_POS) | mode,
0x0,
(guint8 *) &i2c_status,
CY_I2C_GET_STATUS_LEN,
NULL,
FU_CCGX_HPI_WAIT_TIMEOUT,
NULL,
&error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"failed to get i2c status: %s",
error_local->message);
return FALSE;
}
return TRUE;
}
static gboolean
fu_ccgx_hpi_device_get_i2c_config (FuCcgxHpiDevice *self,
CyI2CConfig *i2c_config,
GError **error)
{
g_autoptr(GError) error_local = NULL;
if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
G_USB_DEVICE_RECIPIENT_DEVICE,
CY_I2C_GET_CONFIG_CMD,
((guint16) self->scb_index) << CY_SCB_INDEX_POS,
0x0,
(guint8 *) i2c_config,
sizeof(*i2c_config),
NULL,
FU_CCGX_HPI_WAIT_TIMEOUT,
NULL,
&error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"i2c get config error: control xfer: %s",
error_local->message);
return FALSE;
}
return TRUE;
}
static gboolean
fu_ccgx_hpi_device_set_i2c_config (FuCcgxHpiDevice *self,
CyI2CConfig *i2c_config,
GError **error)
{
g_autoptr(GError) error_local = NULL;
if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
G_USB_DEVICE_RECIPIENT_DEVICE,
CY_I2C_SET_CONFIG_CMD,
((guint16) self->scb_index) << CY_SCB_INDEX_POS,
0x0,
(guint8 *) i2c_config,
sizeof(*i2c_config),
NULL,
FU_CCGX_HPI_WAIT_TIMEOUT,
NULL,
&error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"i2c set config error: control xfer: %s",
error_local->message);
return FALSE;
}
return TRUE;
}
static gboolean
fu_ccgx_hpi_device_wait_for_notify (FuCcgxHpiDevice *self,
guint16 *bytes_pending,
GError **error)
{
guint8 buf[CY_I2C_EVENT_NOTIFICATION_LEN] = { 0x0 };
g_autoptr(GError) error_local = NULL;
if (!g_usb_device_interrupt_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
self->ep_intr_in,
buf, sizeof(buf), NULL,
FU_CCGX_HPI_WAIT_TIMEOUT,
NULL, &error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"failed to get i2c event: %s",
error_local->message);
return FALSE;
}
/* @bytes_pending available on failure */
if (buf[0] & CY_I2C_ERROR_BIT) {
if (bytes_pending != NULL) {
if (!fu_common_read_uint16_safe (buf, sizeof(buf), 0x01,
bytes_pending, G_LITTLE_ENDIAN,
error))
return FALSE;
}
/* write */
if (buf[0] & 0x80) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"i2c status error in i2c write [0x%x] event: %s",
(guint8) buf[0], error_local->message);
/* read */
} else {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"i2c status error in i2c read [0x%x] event: %s",
(guint8) buf[0], error_local->message);
}
return FALSE;
}
return TRUE;
}
static gboolean
fu_ccgx_hpi_device_i2c_read (FuCcgxHpiDevice *self,
guint8 *buf, gsize bufsz,
CyI2CDataConfigBits cfg_bits,
GError **error)
{
guint8 i2c_status = 0x0;
guint8 slave_address = 0;
if (!fu_ccgx_hpi_device_get_i2c_status (self, CY_I2C_MODE_READ, &i2c_status, error)) {
g_prefix_error (error, "i2c read error: ");
return FALSE;
}
slave_address = (self->slave_address & 0x7F) | (self->scb_index << 7);
if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
G_USB_DEVICE_RECIPIENT_DEVICE,
CY_I2C_READ_CMD,
(((guint16) slave_address) << 8) | cfg_bits,
bufsz, NULL, 0x0, NULL,
FU_CCGX_HPI_WAIT_TIMEOUT, NULL,
error)) {
g_prefix_error (error, "i2c read error: control xfer: ");
return FALSE;
}
if (!g_usb_device_bulk_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
self->ep_bulk_in,
buf, bufsz, NULL,
FU_CCGX_HPI_WAIT_TIMEOUT,
NULL, error)) {
g_prefix_error (error, "i2c read error: bulk xfer: ");
return FALSE;
}
/* 10 msec delay */
g_usleep (I2C_READ_WRITE_DELAY_US);
if (!fu_ccgx_hpi_device_wait_for_notify (self, NULL, error)) {
g_prefix_error (error, "i2c read error: ");
return FALSE;
}
return TRUE;
}
static gboolean
fu_ccgx_hpi_device_i2c_write (FuCcgxHpiDevice *self,
guint8 *buf, gsize bufsz,
CyI2CDataConfigBits cfg_bits,
GError **error)
{
guint8 i2c_status = 0x0;
guint8 slave_address;
g_autoptr(GError) error_local = NULL;
if (!fu_ccgx_hpi_device_get_i2c_status (self,
CY_I2C_MODE_WRITE,
&i2c_status,
error)) {
g_prefix_error (error, "i2c get status error: ");
return FALSE;
}
slave_address = (self->slave_address & 0x7F) | (self->scb_index << 7);
if (!g_usb_device_control_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
G_USB_DEVICE_RECIPIENT_DEVICE,
CY_I2C_WRITE_CMD,
((guint16) slave_address << 8) | (cfg_bits & CY_I2C_DATA_CONFIG_STOP),
bufsz, /* idx */
NULL, 0x0, NULL,
FU_CCGX_HPI_WAIT_TIMEOUT,
NULL, error)) {
g_prefix_error (error, "i2c write error: control xfer: ");
return FALSE;
}
if (!g_usb_device_bulk_transfer (fu_usb_device_get_dev (FU_USB_DEVICE (self)),
self->ep_bulk_out, buf, bufsz, NULL,
FU_CCGX_HPI_WAIT_TIMEOUT,
NULL, error)) {
g_prefix_error (error, "i2c write error: bulk xfer: ");
return FALSE;
}
/* 10 msec delay */
g_usleep (I2C_READ_WRITE_DELAY_US);
if (!fu_ccgx_hpi_device_wait_for_notify (self, NULL, error)) {
g_prefix_error (error, "i2c wait for notification error: ");
return FALSE;
}
return TRUE;
}
static gboolean
fu_ccgx_hpi_device_reg_read (FuCcgxHpiDevice *self,
guint16 addr,
guint8 *buf,
guint16 bufsz,
GError **error)
{
g_autofree guint8 *bufhw = g_malloc0 (self->hpi_addrsz + 1);
for (guint32 i = 0; i < self->hpi_addrsz; i++)
bufhw[i] = (guint8) (addr >> (8 * i));
if (!fu_ccgx_hpi_device_i2c_write (self, bufhw, self->hpi_addrsz,
CY_I2C_DATA_CONFIG_NAK, error)) {
g_prefix_error (error, "write error: ");
return FALSE;
}
if (!fu_ccgx_hpi_device_i2c_read (self, buf, bufsz,
CY_I2C_DATA_CONFIG_STOP | CY_I2C_DATA_CONFIG_NAK,
error)) {
g_prefix_error (error, "read error: ");
return FALSE;
}
return TRUE;
}
static gboolean
fu_ccgx_hpi_device_reg_write (FuCcgxHpiDevice *self,
guint16 addr,
guint8 *buf,
guint16 bufsz,
GError **error)
{
g_autofree guint8 *bufhw = g_malloc0 (bufsz + self->hpi_addrsz + 1);
for (guint32 i = 0; i < self->hpi_addrsz; i++)
bufhw[i] = (guint8) (addr >> (8*i));
memcpy (&bufhw[self->hpi_addrsz], buf, bufsz);
if (!fu_ccgx_hpi_device_i2c_write (self, bufhw, bufsz + self->hpi_addrsz,
CY_I2C_DATA_CONFIG_STOP | CY_I2C_DATA_CONFIG_NAK,
error)) {
g_prefix_error (error, "reg write error: ");
return FALSE;
}
return TRUE;
}
static gboolean
fu_ccgx_hpi_device_clear_intr (FuCcgxHpiDevice *self,
HPIRegSection section,
GError **error)
{
guint8 intr = 0;
for (guint8 i = 0; i <= self->num_ports; i++) {
if (i == section || section == HPI_REG_SECTION_ALL)
intr |= 1 << i;
}
if (!fu_ccgx_hpi_device_reg_write (self, HPI_DEV_REG_INTR_ADDR,
&intr, sizeof(intr),
error)) {
g_prefix_error (error, "failed to clear interrupt: ");
return FALSE;
}
return TRUE;
}
static guint16
fu_ccgx_hpi_device_reg_addr_gen (guint8 section, guint8 part, guint8 addr)
{
return (((guint16) section) << 12) | (((guint16) part) << 8) | addr;
}
static gboolean
fu_ccgx_hpi_device_read_event_reg (FuCcgxHpiDevice *self,
HPIRegSection section,
HPIEvent *event,
GError **error)
{
if (section != HPI_REG_SECTION_DEV) {
guint16 reg_addr;
guint8 buf[4] = { 0x0 };
/* first read the response register */
reg_addr = fu_ccgx_hpi_device_reg_addr_gen (section,
HPI_REG_PART_PDDATA_READ,
0);
if (!fu_ccgx_hpi_device_reg_read (self,
reg_addr,
buf, sizeof(buf),
error)) {
g_prefix_error (error, "read response reg error:");
return FALSE;
}
/* byte 1 is reserved and should read as zero */
buf[1] = 0;
memcpy ((guint8 *) event, buf, sizeof(buf));
if (event->event_length != 0) {
reg_addr = fu_ccgx_hpi_device_reg_addr_gen (section,
HPI_REG_PART_PDDATA_READ,
sizeof(buf));
if (!fu_ccgx_hpi_device_reg_read (self,
reg_addr,
event->event_data,
event->event_length,
error)) {
g_prefix_error (error, "read event data error:");
return FALSE;
}
}
} else {
guint8 buf[2] = { 0x0 };
if (!fu_ccgx_hpi_device_reg_read (self,
CY_PD_REG_RESPONSE_ADDR,
buf, sizeof(buf),
error)) {
g_prefix_error (error, "read response reg error:");
return FALSE;
}
event->event_code = buf[0];
event->event_length = buf[1];
if (event->event_length != 0) {
/* read the data memory */
if (!fu_ccgx_hpi_device_reg_read (self,
CY_PD_REG_BOOTDATA_MEMORY_ADDR,
event->event_data,
event->event_length,
error)) {
g_prefix_error (error, "read event data error:");
return FALSE;
}
}
}
/* success */
return fu_ccgx_hpi_device_clear_intr (self, section, error);
}
static gboolean
fu_ccgx_hpi_device_app_read_intr_reg (FuCcgxHpiDevice *self,
HPIRegSection section,
HPIEvent *event_array,
guint8 *event_count,
GError **error)
{
guint16 reg_addr;
guint8 event_count_tmp = 0;
guint8 intr_reg = 0;
reg_addr = fu_ccgx_hpi_device_reg_addr_gen (HPI_REG_SECTION_DEV,
HPI_REG_PART_REG,
HPI_DEV_REG_INTR_ADDR);
if (!fu_ccgx_hpi_device_reg_read (self,
reg_addr,
&intr_reg,
sizeof(intr_reg),
error)) {
g_prefix_error (error, "read intr reg error: ");
return FALSE;
}
/* device section will not come here */
for (guint8 i = 0; i <= self->num_ports; i++) {
/* check if this section is needed */
if (section == i || section == HPI_REG_SECTION_ALL) {
/* check whether this section has any event/response */
if ((1 << i) & intr_reg) {
if (!fu_ccgx_hpi_device_read_event_reg (self,
section,
&event_array[i],
error)) {
g_prefix_error (error, "read event error: ");
return FALSE;
}
event_count_tmp++;
}
}
}
if (event_count != NULL)
*event_count = event_count_tmp;
return TRUE;
}
static gboolean
fu_ccgx_hpi_device_wait_for_event (FuCcgxHpiDevice *self,
HPIRegSection section,
HPIEvent *event_array,
guint32 timeout_ms,
GError **error)
{
guint8 event_count = 0;
g_autoptr(GTimer) start_time = g_timer_new ();
do {
if (!fu_ccgx_hpi_device_app_read_intr_reg (self,
section,
event_array,
&event_count,
error))
return FALSE;
if (event_count > 0)
return TRUE;
} while (g_timer_elapsed (start_time, NULL) * 1000.f <= timeout_ms);
/* timed out */
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_TIMED_OUT,
"failed to wait for event in %ums",
timeout_ms);
return FALSE;
}
static gboolean
fu_ccgx_hpi_device_get_event (FuCcgxHpiDevice *self,
HPIRegSection reg_section,
CyPDResp *event,
guint32 io_timeout,
GError **error )
{
HPIEvent event_array[HPI_REG_SECTION_ALL + 1] = { 0x0 };
if (!fu_ccgx_hpi_device_wait_for_event (self,
reg_section,
event_array,
io_timeout,
error)) {
g_prefix_error (error, "failed to get event: ");
return FALSE;
}
*event = event_array[reg_section].event_code;
return TRUE;
}
static gboolean
fu_ccgx_hpi_device_attach (FuDevice *device, GError **error)
{
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"not supported");
return FALSE;
}
static FuFirmware *
fu_ccgx_hpi_device_prepare_firmware (FuDevice *device,
GBytes *fw,
FwupdInstallFlags flags,
GError **error)
{
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE (device);
g_autoptr(FuFirmware) firmware = fu_ccgx_cyacd_firmware_new ();
g_autoptr(GPtrArray) images = NULL;
/* parse all images */
if (!fu_firmware_parse (firmware, fw, flags, error))
return NULL;
/* check the silicon ID of all images */
images = fu_firmware_get_images (firmware);
for (guint i = 0; i < images->len; i++) {
FuFirmwareImage *img = g_ptr_array_index (images, i);
guint16 fw_app_type = fu_ccgx_cyacd_firmware_image_get_app_type (FU_CCGX_CYACD_FIRMWARE_IMAGE (img));
if (fu_firmware_image_get_addr (img) != self->silicon_id) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"silicon id mismatch on image %u, "
"expected 0x%x, got 0x%x",
i, self->silicon_id,
(guint) fu_firmware_image_get_addr (img));
return NULL;
}
if (fw_app_type != self->fw_app_type) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"app type mismatch on image %u, "
"expected 0x%x, got 0x%x",
i, self->fw_app_type, fw_app_type);
return NULL;
}
}
return g_steal_pointer (&firmware);
}
static gboolean
fu_ccgx_hpi_write_firmware (FuDevice *device,
FuFirmware *firmware,
FwupdInstallFlags flags,
GError **error)
{
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"not supported");
return FALSE;
}
static gboolean
fu_ccgx_hpi_device_ensure_silicon_id (FuCcgxHpiDevice *self, GError **error)
{
guint8 buf[2] = { 0x0 };
g_autofree gchar *instance_id = NULL;
if (!fu_ccgx_hpi_device_reg_read (self, CY_PD_SILICON_ID,
buf, sizeof(buf), error)) {
g_prefix_error (error, "get silicon id error: ");
return FALSE;
}
if (!fu_common_read_uint16_safe (buf, sizeof(buf),
0x0, &self->silicon_id,
G_LITTLE_ENDIAN, error))
return FALSE;
/* add quirks */
instance_id = g_strdup_printf ("CCGX\\SID_%X", self->silicon_id);
fu_device_add_instance_id_full (FU_DEVICE (self),
instance_id,
FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS);
/* sanity check */
if (self->flash_row_size == 0x0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Invalid row size for Instance ID: %s",
instance_id);
return FALSE;
}
/* success */
return TRUE;
}
static gboolean
fu_ccgx_hpi_device_setup (FuDevice *device, GError **error)
{
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE (device);
CyI2CConfig i2c_config = { 0x0 };
guint32 hpi_event = 0;
guint8 mode = 0;
g_autofree gchar *instance_id = NULL;
g_autoptr(GError) error_local = NULL;
/* set the new config */
if (!fu_ccgx_hpi_device_get_i2c_config (self, &i2c_config, error)) {
g_prefix_error (error, "get config error: ");
return FALSE;
}
i2c_config.frequency = FU_CCGX_HPI_FREQ;
i2c_config.is_master = TRUE;
i2c_config.is_msb_first = TRUE;
if (!fu_ccgx_hpi_device_set_i2c_config (self, &i2c_config, error)) {
g_prefix_error (error, "set config error: ");
return FALSE;
}
if (!fu_ccgx_hpi_device_reg_read (self, CY_PD_REG_DEVICE_MODE_ADDR,
&mode, 1, error)) {
g_prefix_error (error, "get device mode error: ");
return FALSE;
}
self->hpi_addrsz = mode & 0x80 ? 2 : 1;
self->num_ports = (mode >> 2) & 0x03 ? 2 : 1;
self->fw_mode = (FWMode) (mode & 0x03);
/* add extra instance ID */
instance_id = g_strdup_printf ("USB\\VID_%04X&PID_%04X&MODE_%s",
fu_usb_device_get_vid (FU_USB_DEVICE (device)),
fu_usb_device_get_pid (FU_USB_DEVICE (device)),
fu_ccgx_fw_mode_to_string (self->fw_mode));
fu_device_add_instance_id (device, instance_id);
/* get silicon ID */
if (!fu_ccgx_hpi_device_ensure_silicon_id (self, error))
return FALSE;
/* get correct version if not in boot mode */
if (self->fw_mode != FW_MODE_BOOT) {
guint16 bufsz;
guint32 ver_fw1 = 0;
guint32 ver_fw2 = 0;
guint8 bufver[HPI_DEVICE_VERSION_SIZE_HPIV2] = { 0x0 };
bufsz = self->hpi_addrsz == 1 ? HPI_DEVICE_VERSION_SIZE_HPIV1 :
HPI_DEVICE_VERSION_SIZE_HPIV2;
if (!fu_ccgx_hpi_device_reg_read (self,
CY_PD_GET_VERSION,
bufver, bufsz,
error)) {
g_prefix_error (error, "get version error: ");
return FALSE;
}
if (!fu_common_read_uint32_safe (bufver, sizeof(bufver),
0x0c, &ver_fw1,
G_LITTLE_ENDIAN, error))
return FALSE;
self->fw_app_type = ver_fw1 & 0xffff;
if (!fu_common_read_uint32_safe (bufver, sizeof(bufver),
0x14, &ver_fw2,
G_LITTLE_ENDIAN, error))
return FALSE;
/* these seem swapped, but we can only update the "other" image
* whilst running in the current image */
if (self->fw_mode == FW_MODE_FW2) {
g_autofree gchar *version = fu_ccgx_version_to_string (ver_fw1);
fu_device_set_version_raw (device, ver_fw1);
fu_device_set_version (device, version);
} else if (self->fw_mode == FW_MODE_FW1) {
g_autofree gchar *version = fu_ccgx_version_to_string (ver_fw2);
fu_device_set_version_raw (device, ver_fw2);
fu_device_set_version (device, version);
}
}
/* not supported in boot mode */
if (self->fw_mode == FW_MODE_BOOT) {
fu_device_remove_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE);
} else {
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE);
}
/* if we are coming back from reset, wait for hardware to settle */
if (!fu_ccgx_hpi_device_get_event (self,
HPI_REG_SECTION_DEV,
&hpi_event,
HPI_CMD_SETUP_EVENT_WAIT_TIME_MS,
&error_local)) {
if (!g_error_matches (error_local,
G_IO_ERROR,
G_IO_ERROR_TIMED_OUT)) {
g_propagate_error (error, g_steal_pointer (&error_local));
return FALSE;
}
} else {
if (hpi_event == CY_PD_RESP_RESET_COMPLETE)
g_usleep (HPI_CMD_RESET_COMPLETE_DELAY_US);
}
/* success */
return TRUE;
}
static gboolean
fu_ccgx_hpi_device_set_quirk_kv (FuDevice *device,
const gchar *key,
const gchar *value,
GError **error)
{
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE (device);
if (g_strcmp0 (key, "SiliconId") == 0) {
guint64 tmp = fu_common_strtoull (value);
if (tmp < G_MAXUINT16) {
self->silicon_id = tmp;
return TRUE;
}
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"invalid SiliconId");
return FALSE;
}
if (g_strcmp0 (key, "FlashRowSize") == 0) {
guint64 tmp = fu_common_strtoull (value);
if (tmp < G_MAXUINT32) {
self->flash_row_size = tmp;
return TRUE;
}
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"invalid FlashRowSize");
return FALSE;
}
if (g_strcmp0 (key, "FlashSize") == 0) {
guint64 tmp = fu_common_strtoull (value);
if (tmp < G_MAXUINT32) {
self->flash_size = tmp;
return TRUE;
}
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"invalid FlashSize");
return FALSE;
}
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"no supported");
return FALSE;
}
static gboolean
fu_ccgx_hpi_device_open (FuUsbDevice *device, GError **error)
{
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE (device);
g_autoptr(GError) error_local = NULL;
if (!g_usb_device_claim_interface (fu_usb_device_get_dev (device),
self->inf_num,
G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER,
&error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"cannot claim interface: %s",
error_local->message);
return FALSE;
}
return TRUE;
}
static gboolean
fu_ccgx_hpi_device_close (FuUsbDevice *device, GError **error)
{
FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE (device);
g_autoptr(GError) error_local = NULL;
if (!g_usb_device_release_interface (fu_usb_device_get_dev (device),
self->inf_num,
G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER,
&error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"cannot release interface: %s",
error_local->message);
return FALSE;
}
return TRUE;
}
static void
fu_ccgx_hpi_device_init (FuCcgxHpiDevice *self)
{
self->inf_num = 0x0;
self->hpi_addrsz = 1;
self->num_ports = 1;
self->slave_address = PD_I2C_SLAVE_ADDRESS;
self->ep_bulk_out = PD_I2C_USB_EP_BULK_OUT;
self->ep_bulk_in = PD_I2C_USB_EP_BULK_IN;
self->ep_intr_in = PD_I2C_USB_EP_INTR_IN;
fu_device_set_protocol (FU_DEVICE (self), "com.cypress.ccgx");
fu_device_set_install_duration (FU_DEVICE (self), 60);
fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_REQUIRE_AC);
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_DUAL_IMAGE);
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE);
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_SELF_RECOVERY);
/* this might not be true for future hardware */
if (self->inf_num > 0)
self->scb_index = 1;
}
static void
fu_ccgx_hpi_device_class_init (FuCcgxHpiDeviceClass *klass)
{
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass);
klass_device->to_string = fu_ccgx_hpi_device_to_string;
klass_device->write_firmware = fu_ccgx_hpi_write_firmware;
klass_device->prepare_firmware = fu_ccgx_hpi_device_prepare_firmware;
klass_device->attach = fu_ccgx_hpi_device_attach;
klass_device->setup = fu_ccgx_hpi_device_setup;
klass_device->set_quirk_kv = fu_ccgx_hpi_device_set_quirk_kv;
klass_usb_device->open = fu_ccgx_hpi_device_open;
klass_usb_device->close = fu_ccgx_hpi_device_close;
}