diff --git a/plugins/pixart-rf/README.md b/plugins/pixart-rf/README.md index edc6e7699..342f9832d 100644 --- a/plugins/pixart-rf/README.md +++ b/plugins/pixart-rf/README.md @@ -26,6 +26,7 @@ Pixart Imaging, Inc and Primax Electronics, Ltd, e.g. * `HIDRAW\VEN_093A&DEV_2801` * `HIDRAW\VEN_0461&DEV_4EEF` * `HIDRAW\VEN_0461&DEV_4EEF&NAME_${NAME}` + * `HIDRAW\VEN_0461&DEV_4EEF&MODEL_${MODEL_NAME}` Additionaly, a custom GUID values including the name is used, e.g. diff --git a/plugins/pixart-rf/fu-pxi-device.c b/plugins/pixart-rf/fu-pxi-device.c index 74d4368c1..0873d2dfe 100644 --- a/plugins/pixart-rf/fu-pxi-device.c +++ b/plugins/pixart-rf/fu-pxi-device.c @@ -30,6 +30,7 @@ #define FU_PXI_DEVICE_CMD_FW_OTA_INIT_NEW 0x27u #define FU_PXI_DEVICE_CMD_FW_OTA_RETRANSMIT 0x28u #define FU_PXI_DEVICE_CMD_FW_OTA_DISCONNECT 0x29u +#define FU_PXI_DEVICE_CMD_FW_OTA_GET_MODEL 0x2bu #define FU_PXI_DEVICE_OBJECT_SIZE_MAX 4096 /* bytes */ #define FU_PXI_DEVICE_OTA_BUF_SZ 512 /* bytes */ @@ -52,6 +53,7 @@ enum ota_spec_check_result { OTA_PROCESS_ILLEGAL = 3, /* Illegal OTA process */ OTA_RECONNECT = 4, /* Inform OTA app do reconnect */ OTA_FW_IMG_VERSION_ERROR = 5, /* FW image file version check error */ + OTA_DEVICE_LOW_BATTERY = 6, /* Device is under low battery */ OTA_SPEC_CHECK_MAX_NUM, /* Max number of OTA driver defined error code */ }; @@ -72,6 +74,7 @@ struct _FuPxiDevice { guint16 mtu_size; guint16 prn_threshold; guint8 spec_check_result; + gchar *model_name; }; G_DEFINE_TYPE (FuPxiDevice, fu_pxi_device, FU_TYPE_UDEV_DEVICE) @@ -102,6 +105,8 @@ fu_pxi_device_spec_check_result_to_string (guint8 spec_check_result) return "reconnect"; if (spec_check_result == OTA_FW_IMG_VERSION_ERROR) return "fw-img-version-error"; + if (spec_check_result == OTA_DEVICE_LOW_BATTERY) + return "device battery is too low"; return NULL; } @@ -113,6 +118,7 @@ fu_pxi_device_to_string (FuDevice *device, guint idt, GString *str) /* FuUdevDevice->to_string */ FU_DEVICE_CLASS (fu_pxi_device_parent_class)->to_string (device, idt, str); + fu_common_string_append_kv (str, idt, "ModelName", self->model_name); fu_common_string_append_kx (str, idt, "Status", self->status); fu_common_string_append_kx (str, idt, "NewFlow", self->new_flow); fu_common_string_append_kx (str, idt, "CurrentObjectOffset", self->offset); @@ -130,9 +136,34 @@ fu_pxi_device_prepare_firmware (FuDevice *device, FwupdInstallFlags flags, GError **error) { + FuPxiDevice *self = FU_PXI_DEVICE (device); + const gchar *model_name; g_autoptr(FuFirmware) firmware = fu_pxi_firmware_new (); + if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; + + /* check is compatible with hardware */ + model_name = fu_pxi_firmware_get_model_name (FU_PXI_FIRMWARE (firmware)); + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if (self->model_name == NULL || model_name == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "legacy device or firmware detected, " + "--force required"); + return NULL; + } + if (g_strcmp0 (self->model_name, model_name) != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "incompatible firmware, got %s, expected %s.", + model_name, self->model_name); + return NULL; + } + } + return g_steal_pointer (&firmware); } @@ -396,8 +427,8 @@ fu_pxi_device_reset (FuPxiDevice *self, GError **error) { g_autoptr(GByteArray) req = g_byte_array_new (); fu_byte_array_append_uint8 (req, PXI_HID_DEV_OTA_FEATURE_REPORT_ID); - fu_byte_array_append_uint8 (req, FU_PXI_DEVICE_CMD_FW_MCU_RESET); - fu_byte_array_append_uint8 (req, OTA_RESET); + fu_byte_array_append_uint8 (req, FU_PXI_DEVICE_CMD_FW_MCU_RESET); /* OTA reset command */ + fu_byte_array_append_uint8 (req, OTA_RESET); /* OTA reset reason */ fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_RESTART); if (!fu_pxi_device_set_feature (self, req, error)) { @@ -477,7 +508,7 @@ fu_pxi_device_fw_ota_init_new (FuPxiDevice *self, gsize bufsz, GError **error) g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_READ, - "FwInitNew spec check fail with %s [0x%02x]", + "FwInitNew spec check fail: %s [0x%02x]", fu_pxi_device_spec_check_result_to_string (self->spec_check_result), self->spec_check_result); return FALSE; @@ -493,7 +524,7 @@ fu_pxi_device_fw_upgrade (FuPxiDevice *self, FuFirmware *firmware, GError **erro const gchar *version; const guint8 *buf; gsize bufsz = 0; - guint8 fw_version[10] = { 0x0 }; + guint8 fw_version[5] = { 0x0 }; guint8 opcode = 0; guint16 checksum; g_autoptr(GBytes) fw = NULL; @@ -523,7 +554,7 @@ fu_pxi_device_fw_upgrade (FuPxiDevice *self, FuFirmware *firmware, GError **erro if (g_getenv ("FWUPD_PIXART_RF_VERBOSE") != NULL) fu_common_dump_raw (G_LOG_DOMAIN, "fw upgrade", req->data, req->len); - /* read fw upgrade command result */ + /* wait fw upgrade command result */ if (!fu_pxi_device_wait_notify (self, 0x1, &opcode, NULL, error)) return FALSE; if (opcode != FU_PXI_DEVICE_CMD_FW_UPGRADE) { @@ -601,8 +632,13 @@ fu_pxi_device_fw_get_info (FuPxiDevice *self, GError **error) if (!fu_pxi_device_set_feature (self, req, error)) return FALSE; + + /* delay for BLE device read command */ + g_usleep (10 * 1000); + res[0] = PXI_HID_DEV_OTA_FEATURE_REPORT_ID; res[1] = FU_PXI_DEVICE_CMD_FW_GET_INFO; + if (!fu_pxi_device_get_feature (self, res, FU_PXI_DEVICE_FW_INFO_RET_LEN + 3, error)) return FALSE; if (!fu_common_read_uint8_safe (res, sizeof(res), 0x4, &opcode, error)) @@ -628,6 +664,46 @@ fu_pxi_device_fw_get_info (FuPxiDevice *self, GError **error) return TRUE; } +static gboolean +fu_pxi_device_get_model_info (FuPxiDevice *self, GError **error) +{ + guint8 res[FU_PXI_DEVICE_OTA_BUF_SZ] = { 0x0 }; + guint8 opcode = 0x0; + guint8 model_name[FU_PXI_DEVICE_MODEL_NAME_LEN] = { 0x0 }; + g_autoptr(GByteArray) req = g_byte_array_new (); + + fu_byte_array_append_uint8 (req, PXI_HID_DEV_OTA_FEATURE_REPORT_ID); + fu_byte_array_append_uint8 (req, FU_PXI_DEVICE_CMD_FW_OTA_GET_MODEL); + + if (!fu_pxi_device_set_feature (self, req, error)) + return FALSE; + + /* delay for BLE device read command */ + g_usleep (10 * 1000); + + res[0] = PXI_HID_DEV_OTA_FEATURE_REPORT_ID; + if (!fu_pxi_device_get_feature (self, res, sizeof(res), error)) + return FALSE; + if (!fu_common_read_uint8_safe (res, sizeof(res), 0x4, &opcode, error)) + return FALSE; + + /* old firmware */ + if (opcode != FU_PXI_DEVICE_CMD_FW_OTA_GET_MODEL) + return TRUE; + + /* get model from res */ + if (!fu_memcpy_safe (model_name, sizeof(model_name), 0x0, /* dst */ + (guint8 *) res, sizeof(res), 0x6, /* src */ + sizeof(model_name), error)) + return FALSE; + g_clear_pointer (&self->model_name, g_free); + if (model_name[0] != 0x00 && model_name[0] != 0xFF) + self->model_name = g_strndup ((gchar *) model_name, sizeof(model_name)); + + /* success */ + return TRUE; +} + static gboolean fu_pxi_device_probe (FuDevice *device, GError **error) { @@ -645,7 +721,7 @@ fu_pxi_device_setup_guid (FuPxiDevice *self, GError **error) g_autofree gchar *devid = NULL; g_autoptr(GString) dev_name = NULL; - /* extra GUID */ + /* extra GUID with device name */ if (!fu_pxi_device_get_raw_info (self, &hid_raw_info ,error)) return FALSE; dev_name = g_string_new (fu_device_get_name (FU_DEVICE (self))); @@ -656,6 +732,23 @@ fu_pxi_device_setup_guid (FuPxiDevice *self, GError **error) (guint) hid_raw_info.product, dev_name->str); fu_device_add_instance_id (FU_DEVICE (self), devid); + + /* extra GUID with model name*/ + if (self->model_name != NULL) { + g_autofree gchar *devid2 = NULL; + g_autoptr(GString) model_name = NULL; + model_name = g_string_new (self->model_name); + g_string_ascii_up (model_name); + fu_common_string_replace (model_name, " ", "_"); + devid2 = g_strdup_printf ("HIDRAW\\VEN_%04X&DEV_%04X&MODEL_%s", + (guint) hid_raw_info.vendor, + (guint) hid_raw_info.product, + dev_name->str); + fu_device_add_instance_id (FU_DEVICE (self), devid2); + } + + /* set logical id */ + fu_device_set_logical_id (FU_DEVICE (self), devid); #endif return TRUE; } @@ -665,10 +758,6 @@ fu_pxi_device_setup (FuDevice *device, GError **error) { FuPxiDevice *self = FU_PXI_DEVICE (device); - if (!fu_pxi_device_setup_guid (self ,error)) { - g_prefix_error (error, "failed to setup GUID: "); - return FALSE; - } if (!fu_pxi_device_fw_ota_init (self, error)) { g_prefix_error (error, "failed to OTA init: "); return FALSE; @@ -677,6 +766,14 @@ fu_pxi_device_setup (FuDevice *device, GError **error) g_prefix_error (error, "failed to get info: "); return FALSE; } + if (!fu_pxi_device_get_model_info (self ,error)) { + g_prefix_error (error, "failed to get model: "); + return FALSE; + } + if (!fu_pxi_device_setup_guid (self ,error)) { + g_prefix_error (error, "failed to setup GUID: "); + return FALSE; + } return TRUE; } @@ -689,10 +786,19 @@ fu_pxi_device_init (FuPxiDevice *self) fu_device_add_protocol (FU_DEVICE (self), "com.pixart.rf"); } +static void +fu_pxi_device_finalize (GObject *object) +{ + FuPxiDevice *self = FU_PXI_DEVICE (object); + g_free (self->model_name); +} + static void fu_pxi_device_class_init (FuPxiDeviceClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + object_class->finalize = fu_pxi_device_finalize; klass_device->probe = fu_pxi_device_probe; klass_device->setup = fu_pxi_device_setup; klass_device->to_string = fu_pxi_device_to_string; diff --git a/plugins/pixart-rf/fu-pxi-firmware.c b/plugins/pixart-rf/fu-pxi-firmware.c index cef887c7a..b0e8513cf 100644 --- a/plugins/pixart-rf/fu-pxi-firmware.c +++ b/plugins/pixart-rf/fu-pxi-firmware.c @@ -15,10 +15,25 @@ struct _FuPxiFirmware { FuFirmware parent_instance; + gchar *model_name; }; G_DEFINE_TYPE (FuPxiFirmware, fu_pxi_firmware, FU_TYPE_FIRMWARE) +const gchar * +fu_pxi_firmware_get_model_name (FuPxiFirmware *self) +{ + g_return_val_if_fail (FU_IS_PXI_FIRMWARE (self), NULL); + return self->model_name; +} + +static void +fu_pxi_firmware_to_string (FuFirmware *firmware, guint idt, GString *str) +{ + FuPxiFirmware *self = FU_PXI_FIRMWARE (firmware); + fu_common_string_append_kv (str, idt, "ModelName", self->model_name); +} + static gboolean fu_pxi_firmware_parse (FuFirmware *firmware, GBytes *fw, @@ -27,14 +42,16 @@ fu_pxi_firmware_parse (FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { + FuPxiFirmware *self = FU_PXI_FIRMWARE (firmware); const guint8 *buf; const guint8 tag[] = { 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, }; - gboolean header_ok = TRUE; gsize bufsz = 0; guint32 version_raw = 0; guint8 fw_header[PIXART_RF_FW_HEADER_SIZE]; + guint8 model_name[FU_PXI_DEVICE_MODEL_NAME_LEN] = { 0x0 }; + g_autofree gchar *version = NULL; /* get buf */ buf = g_bytes_get_data (fw, &bufsz); @@ -46,11 +63,11 @@ fu_pxi_firmware_parse (FuFirmware *firmware, return FALSE; } - /* get fw header */ + /* get fw header from buf */ if (!fu_memcpy_safe (fw_header, sizeof(fw_header), 0x0, buf, bufsz, bufsz - sizeof(fw_header), sizeof(fw_header), error)) { - g_prefix_error (error, "failed to read fw header "); + g_prefix_error (error, "failed to read fw header: "); return FALSE; } if (g_getenv ("FWUPD_PIXART_RF_VERBOSE") != NULL) { @@ -66,24 +83,31 @@ fu_pxi_firmware_parse (FuFirmware *firmware, &tmp, error)) return FALSE; if (tmp != tag[i]) { - header_ok = FALSE; - break; + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Fw tag is incorrect"); + return FALSE; } } - /* set the default version if can not find it in fw bin */ - if (header_ok) { - g_autofree gchar *version = NULL; - version_raw = (((guint32) (fw_header[0] - '0')) << 16) + - (((guint32) (fw_header[2] - '0')) << 8) + - (guint32) (fw_header[4] - '0'); - fu_firmware_set_version_raw (firmware, version_raw); - version = fu_common_version_from_uint32 (version_raw, - FWUPD_VERSION_FORMAT_DELL_BIOS); - fu_firmware_set_version (firmware, version); - } else { - fu_firmware_set_version (firmware, "0.0.0"); + /* set fw version */ + version_raw = (((guint32) (fw_header[0] - '0')) << 16) + + (((guint32) (fw_header[2] - '0')) << 8) + + (guint32) (fw_header[4] - '0'); + fu_firmware_set_version_raw (firmware, version_raw); + version = fu_common_version_from_uint32 (version_raw, + FWUPD_VERSION_FORMAT_DELL_BIOS); + fu_firmware_set_version (firmware, version); + + /* set fw model name */ + if (!fu_memcpy_safe (model_name, sizeof(model_name), 0x0, + fw_header, sizeof(fw_header), 0x05, + sizeof(model_name), error)) { + g_prefix_error (error, "failed to get fw model name: "); + return FALSE; } + self->model_name = g_strndup ((gchar *) model_name, sizeof(model_name)); /* success */ fu_firmware_set_bytes (firmware, fw); @@ -140,12 +164,22 @@ fu_pxi_firmware_init (FuPxiFirmware *self) { } +static void +fu_pxi_firmware_finalize (GObject *object) +{ + FuPxiFirmware *self = FU_PXI_FIRMWARE (object); + g_free (self->model_name); +} + static void fu_pxi_firmware_class_init (FuPxiFirmwareClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); + object_class->finalize = fu_pxi_firmware_finalize; klass_firmware->parse = fu_pxi_firmware_parse; klass_firmware->write = fu_pxi_firmware_write; + klass_firmware->to_string = fu_pxi_firmware_to_string; } FuFirmware * diff --git a/plugins/pixart-rf/fu-pxi-firmware.h b/plugins/pixart-rf/fu-pxi-firmware.h index fae736d41..b61b10e9a 100644 --- a/plugins/pixart-rf/fu-pxi-firmware.h +++ b/plugins/pixart-rf/fu-pxi-firmware.h @@ -8,7 +8,10 @@ #include "fu-firmware.h" +#define FU_PXI_DEVICE_MODEL_NAME_LEN 12 /* bytes */ + #define FU_TYPE_PXI_FIRMWARE (fu_pxi_firmware_get_type ()) G_DECLARE_FINAL_TYPE (FuPxiFirmware, fu_pxi_firmware, FU, PXI_FIRMWARE, FuFirmware) FuFirmware *fu_pxi_firmware_new (void); +const gchar *fu_pxi_firmware_get_model_name (FuPxiFirmware *self);