corsair: add support for new devices

* KATAR PRO XT
* SABRE PRO
* KATAR PRO WIRELESS
This commit is contained in:
Andrii Dushko 2022-05-12 11:59:29 +03:00 committed by Richard Hughes
parent 67c77d7ec1
commit 41f5b7b563
9 changed files with 927 additions and 336 deletions

View File

@ -6,9 +6,55 @@ This plugin allows to update firmware on Corsair mice and receivers:
* SABRE RGB PRO WIRELESS
* SLIPSTREAM WIRELESS USB Receiver
* KATAR PRO WIRELESS
* KATAR PRO XT Gaming Mouse
* SABRE PRO Gaming Mouse
## Update Behavior
## Code structure
All devices handled by one object (FuCorsairDevice). Receivers with wireless-only
devices will be shown as two entities: parent device as a receiver and wireless
device as a child. Difference in behavior is handled by private flags.
FuCorsairBp contains low-level protocol related routines. Device objects should
call correct versions of these routines in order to update firmware. Correct
routines chosen by device quirsks and private flags.
## Wired mice update behavior
Mice and/or it's wireless adapter must be connected to host via USB cable
to apply an update. The device is switched to bootloader mode to flash
updates, and is reset automatically to new firmware after flashing.
## Wireless mice update behavior
The receiver should be connected to host and the mouse should be turned on
and not sleeping.
## Quirk Use
This plugin uses the following plugin-specific quirks:
### CorsairVendorInterfaceId
Some devices have non-standard USB interface for protocol communication.
This quirk should be set if protocol interface is not 1.
Since: 1.8.0
### CorsairSubdeviceId
Specifies ID of any wireless child device which can be updated. Polling will
be turned on if a subdevice is not connected when parent is being probed.
### Flags:legacy-attach
This flag is used if legacy attach command should be used
### Flags:no-version-in-bl
This flag handles cases if device reports incorrect firmware version in bootloader mode.
### Flags:is-subdevice
This flag tells device that it is a child device. All subdevice behavior tweaks will be applied.

View File

@ -13,3 +13,35 @@ GType = FuCorsairDevice
Name = SLIPSTREAM WIRELESS USB Receiver
Icon = usb-receiver
CorsairDeviceKind = receiver
# KATAR PRO WIRELESS receiver
[USB\VID_1B1C&PID_1B94]
Plugin = corsair
GType = FuCorsairDevice
Name = KATAR PRO WIRELESS receiver
CorsairDeviceKind = mouse
Flags = legacy-attach
CorsairSubdeviceId = USB\VID_1B1C&PID_1B94&WIRELESS
# KATAR PRO WIRELESS mouse
[USB\VID_1B1C&PID_1B94&WIRELESS]
Plugin = corsair
GType = FuCorsairDevice
Name = KATAR PRO WIRELESS mouse
CorsairDeviceKind = mouse
BatteryThreshold = 30
Flags = is-subdevice,legacy-attach,no-version-in-bl
[USB\VID_1B1C&PID_1BAC]
Plugin = corsair
GType = FuCorsairDevice
Name = KATAR PRO XT Gaming Mouse
CorsairDeviceKind = mouse
Flags = legacy-attach,no-version-in-bl
[USB\VID_1B1C&PID_1B7A]
Plugin = corsair
GType = FuCorsairDevice
Name = SABRE PRO Gaming Mouse
CorsairDeviceKind = mouse
Flags = legacy-attach,no-version-in-bl

View File

@ -0,0 +1,447 @@
/*
* Copyright (C) 2022 Andrii Dushko <andrii.dushko@developex.net>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <string.h>
#include "fu-corsair-bp.h"
#include "fu-corsair-common.h"
#define CORSAIR_DEFAULT_VENDOR_INTERFACE_ID 1
#define CORSAIR_ACTIVATION_TIMEOUT 30000
#define CORSAIR_MODE_BOOTLOADER 3
#define CORSAIR_FIRST_CHUNK_HEADER_SIZE 7
#define CORSAIR_NEXT_CHUNKS_HEADER_SIZE 3
#define CORSAIR_TRANSACTION_TIMEOUT 10000
#define CORSAIR_DEFAULT_CMD_SIZE 64
#define CORSAIR_OFFSET_CMD_PROPERTY_ID 0x02
#define CORSAIR_OFFSET_CMD_PROPERTY_VALUE 0x03
#define CORSAIR_OFFSET_CMD_VERSION 0x03
#define CORSAIR_OFFSET_CMD_CRC 0x08
#define CORSAIR_OFFSET_CMD_MODE 0x03
#define CORSAIR_OFFSET_CMD_STATUS 0x02
#define CORSAIR_OFFSET_CMD_FIRMWARE_SIZE 0x03
#define CORSAIR_OFFSET_CMD_SET_MODE 0x04
#define CORSAIR_OFFSET_CMD_DESTINATION 0x00
typedef enum {
FU_CORSAIR_BP_DESTINATION_SELF = 0x08,
FU_CORSAIR_BP_DESTINATION_SUBDEVICE = 0x09
} FuCorsairBpDestination;
struct _FuCorsairBp {
FuUsbDevice parent_instance;
guint8 destination;
guint8 epin;
guint8 epout;
guint16 cmd_write_size;
guint16 cmd_read_size;
gboolean is_legacy_attach;
};
G_DEFINE_TYPE(FuCorsairBp, fu_corsair_bp, FU_TYPE_USB_DEVICE)
static gboolean
fu_corsair_bp_command(FuCorsairBp *self,
guint8 *data,
guint timeout,
gboolean need_reply,
GError **error)
{
gsize actual_len = 0;
gboolean ret;
GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(self));
data[CORSAIR_OFFSET_CMD_DESTINATION] = self->destination;
fu_common_dump_raw(G_LOG_DOMAIN, "corsair: command", data, self->cmd_write_size);
ret = g_usb_device_interrupt_transfer(usb_device,
self->epout,
data,
self->cmd_write_size,
&actual_len,
timeout,
NULL,
error);
if (!ret) {
g_prefix_error(error, "failed to write command: ");
return FALSE;
}
if (actual_len != self->cmd_write_size) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"wrong size written: %" G_GSIZE_FORMAT,
actual_len);
return FALSE;
}
if (!need_reply)
return TRUE;
memset(data, 0, FU_CORSAIR_MAX_CMD_SIZE);
ret = g_usb_device_interrupt_transfer(usb_device,
self->epin,
data,
self->cmd_read_size,
&actual_len,
timeout,
NULL,
error);
if (!ret) {
g_prefix_error(error, "failed to get command response: ");
return FALSE;
}
if (actual_len != self->cmd_read_size) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"wrong size read: %" G_GSIZE_FORMAT,
actual_len);
return FALSE;
}
fu_common_dump_raw(G_LOG_DOMAIN, "corsair: response", data, self->cmd_write_size);
if (data[CORSAIR_OFFSET_CMD_STATUS] != 0) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"device replied with error: %" G_GSIZE_FORMAT,
data[CORSAIR_OFFSET_CMD_STATUS]);
return FALSE;
}
return TRUE;
}
static gboolean
fu_corsair_bp_write_first_chunk(FuCorsairBp *self,
FuChunk *chunk,
guint32 firmware_size,
GError **error)
{
guint8 init_cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x0d, 0x00, 0x03};
guint8 write_cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x06, 0x00};
if (!fu_corsair_bp_command(self, init_cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) {
g_prefix_error(error, "firmware init fail: ");
return FALSE;
}
if (!fu_common_write_uint32_safe(write_cmd,
sizeof(write_cmd),
CORSAIR_OFFSET_CMD_FIRMWARE_SIZE,
firmware_size,
G_LITTLE_ENDIAN,
error)) {
g_prefix_error(error, "cannot serialize firmware size: ");
return FALSE;
}
if (!fu_memcpy_safe(write_cmd,
sizeof(write_cmd),
CORSAIR_FIRST_CHUNK_HEADER_SIZE,
fu_chunk_get_data(chunk),
fu_chunk_get_data_sz(chunk),
0,
fu_chunk_get_data_sz(chunk),
error)) {
g_prefix_error(error, "cannot set data: ");
return FALSE;
}
if (!fu_corsair_bp_command(self, write_cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) {
g_prefix_error(error, "write command fail: ");
return FALSE;
}
return TRUE;
}
static gboolean
fu_corsair_bp_write_chunk(FuCorsairBp *self, FuChunk *chunk, GError **error)
{
guint8 cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x07};
if (!fu_memcpy_safe(cmd,
sizeof(cmd),
CORSAIR_NEXT_CHUNKS_HEADER_SIZE,
fu_chunk_get_data(chunk),
fu_chunk_get_data_sz(chunk),
0,
fu_chunk_get_data_sz(chunk),
error)) {
g_prefix_error(error, "cannot set data: ");
return FALSE;
}
if (!fu_corsair_bp_command(self, cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) {
g_prefix_error(error, "write command fail: ");
return FALSE;
}
return TRUE;
}
static void
fu_corsair_bp_incorporate(FuDevice *self, FuDevice *donor)
{
FuCorsairBp *bp_self = FU_CORSAIR_BP(self);
FuCorsairBp *bp_donor = FU_CORSAIR_BP(donor);
bp_self->epin = bp_donor->epin;
bp_self->epout = bp_donor->epout;
bp_self->cmd_write_size = bp_donor->cmd_write_size;
bp_self->cmd_read_size = bp_donor->cmd_read_size;
}
static void
fu_corsair_bp_init(FuCorsairBp *self)
{
self->cmd_read_size = CORSAIR_DEFAULT_CMD_SIZE;
self->cmd_write_size = CORSAIR_DEFAULT_CMD_SIZE;
self->destination = FU_CORSAIR_BP_DESTINATION_SELF;
}
gboolean
fu_corsair_bp_get_property(FuCorsairBp *self,
FuCorsairBpProperty property,
guint32 *value,
GError **error)
{
guint8 data[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x02};
fu_common_write_uint16(&data[CORSAIR_OFFSET_CMD_PROPERTY_ID],
(guint16)property,
G_LITTLE_ENDIAN);
if (!fu_corsair_bp_command(self, data, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error))
return FALSE;
*value = fu_common_read_uint32(&data[CORSAIR_OFFSET_CMD_PROPERTY_VALUE], G_LITTLE_ENDIAN);
return TRUE;
}
static gboolean
fu_corsair_bp_set_mode(FuCorsairBp *self, FuCorsairDeviceMode mode, GError **error)
{
guint8 cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x01, 0x03};
cmd[CORSAIR_OFFSET_CMD_SET_MODE] = mode;
if (!fu_corsair_bp_command(self, cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) {
g_prefix_error(error, "set mode command fail: ");
return FALSE;
}
return TRUE;
}
static gboolean
fu_corsair_bp_write_firmware_chunks(FuCorsairBp *self,
FuChunk *first_chunk,
GPtrArray *chunks,
FuProgress *progress,
guint32 firmware_size,
GError **error)
{
fu_progress_set_id(progress, G_STRLOC);
fu_progress_set_steps(progress, chunks->len + 1);
if (!fu_corsair_bp_write_first_chunk(self, first_chunk, firmware_size, error)) {
g_prefix_error(error, "cannot write first chunk: ");
return FALSE;
}
fu_progress_step_done(progress);
for (guint id = 0; id < chunks->len; id++) {
FuChunk *chunk = g_ptr_array_index(chunks, id);
if (!fu_corsair_bp_write_chunk(self, chunk, error)) {
g_prefix_error(error, "cannot write chunk %u", id);
return FALSE;
}
fu_progress_step_done(progress);
}
return TRUE;
}
static gboolean
fu_corsair_bp_commit_firmware(FuCorsairBp *self, GError **error)
{
guint8 commit_cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x05, 0x01, 0x00};
if (!fu_corsair_bp_command(self, commit_cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) {
g_prefix_error(error, "firmware commit fail: ");
return FALSE;
}
return TRUE;
}
static gboolean
fu_corsair_bp_write_firmware(FuDevice *device,
FuFirmware *firmware,
FuProgress *progress,
FwupdInstallFlags flags,
GError **error)
{
const guint8 *firmware_raw;
gsize firmware_size;
g_autoptr(GBytes) blob = NULL;
g_autoptr(GPtrArray) chunks = NULL;
g_autoptr(FuChunk) firstChunk = NULL;
g_autoptr(GBytes) rest_of_firmware = NULL;
FuCorsairBp *self = FU_CORSAIR_BP(device);
guint32 first_chunk_size = self->cmd_write_size - CORSAIR_FIRST_CHUNK_HEADER_SIZE;
blob = fu_firmware_get_bytes(firmware, error);
if (blob == NULL) {
g_prefix_error(error, "cannot get firmware data");
return FALSE;
}
firmware_raw = fu_bytes_get_data_safe(blob, &firmware_size, error);
if (firmware_raw == NULL) {
g_prefix_error(error, "cannot get firmware data: ");
return FALSE;
}
/* the firmware size should be greater than 1 chunk */
if (firmware_size <= first_chunk_size) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"update file should be bigger");
return FALSE;
}
firstChunk = fu_chunk_new(0, 0, 0, g_bytes_get_data(blob, NULL), first_chunk_size);
rest_of_firmware = fu_common_bytes_new_offset(blob,
first_chunk_size,
firmware_size - first_chunk_size,
error);
if (rest_of_firmware == NULL) {
g_prefix_error(error, "cannot get firmware past first chunk: ");
return FALSE;
}
chunks =
fu_chunk_array_new_from_bytes(rest_of_firmware,
first_chunk_size,
0,
self->cmd_write_size - CORSAIR_NEXT_CHUNKS_HEADER_SIZE);
if (!fu_corsair_bp_write_firmware_chunks(self,
firstChunk,
chunks,
progress,
g_bytes_get_size(blob),
error))
return FALSE;
if (!fu_corsair_bp_commit_firmware(self, error))
return FALSE;
return TRUE;
}
gboolean
fu_corsair_bp_activate_firmware(FuCorsairBp *self, FuFirmware *firmware, GError **error)
{
guint32 crc;
gsize firmware_size;
const guint8 *firmware_raw;
g_autoptr(GBytes) blob = NULL;
guint8 cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x16, 0x00, 0x01, 0x03, 0x00, 0x01, 0x01};
blob = fu_firmware_get_bytes(firmware, error);
if (blob == NULL) {
g_prefix_error(error, "cannot get firmware bytes");
return FALSE;
}
firmware_raw = fu_bytes_get_data_safe(blob, &firmware_size, error);
if (firmware_raw == NULL) {
g_prefix_error(error, "cannot get firmware data: ");
return FALSE;
}
crc = fu_corsair_calculate_crc(firmware_raw, firmware_size);
fu_common_write_uint32(&cmd[CORSAIR_OFFSET_CMD_CRC], crc, G_LITTLE_ENDIAN);
return fu_corsair_bp_command(self, cmd, CORSAIR_ACTIVATION_TIMEOUT, TRUE, error);
}
static gboolean
fu_corsair_bp_attach(FuDevice *device, FuProgress *progress, GError **error)
{
FuCorsairBp *self = FU_CORSAIR_BP(device);
if (self->is_legacy_attach) {
guint8 cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x10, 0x01, 0x00, 0x03, 0x00, 0x01};
return fu_corsair_bp_command(self, cmd, CORSAIR_TRANSACTION_TIMEOUT, FALSE, error);
} else {
return fu_corsair_bp_set_mode(self, FU_CORSAIR_DEVICE_MODE_APPLICATION, error);
}
}
static gboolean
fu_corsair_bp_detach(FuDevice *device, FuProgress *progress, GError **error)
{
FuCorsairBp *self = FU_CORSAIR_BP(device);
return fu_corsair_bp_set_mode(self, FU_CORSAIR_DEVICE_MODE_BOOTLOADER, error);
}
static void
fu_corsair_bp_to_string(FuDevice *device, guint idt, GString *str)
{
FuCorsairBp *self = FU_CORSAIR_BP(device);
FU_DEVICE_CLASS(fu_corsair_bp_parent_class)->to_string(device, idt, str);
fu_common_string_append_kx(str, idt, "InEndpoint", self->epin);
fu_common_string_append_kx(str, idt, "OutEndpoint", self->epout);
}
static void
fu_corsair_bp_class_init(FuCorsairBpClass *klass)
{
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
klass_device->incorporate = fu_corsair_bp_incorporate;
klass_device->write_firmware = fu_corsair_bp_write_firmware;
klass_device->attach = fu_corsair_bp_attach;
klass_device->detach = fu_corsair_bp_detach;
klass_device->to_string = fu_corsair_bp_to_string;
}
FuCorsairBp *
fu_corsair_bp_new(GUsbDevice *usb_device, gboolean is_subdevice)
{
FuCorsairBp *self = g_object_new(FU_TYPE_CORSAIR_BP, "usb_device", usb_device, NULL);
if (is_subdevice) {
self->destination = FU_CORSAIR_BP_DESTINATION_SUBDEVICE;
} else {
self->destination = FU_CORSAIR_BP_DESTINATION_SELF;
}
return self;
}
void
fu_corsair_bp_set_cmd_size(FuCorsairBp *self, guint16 write_size, guint16 read_size)
{
self->cmd_write_size = write_size;
self->cmd_read_size = read_size;
}
void
fu_corsair_bp_set_endpoints(FuCorsairBp *self, guint8 epin, guint8 epout)
{
self->epin = epin;
self->epout = epout;
}
void
fu_corsair_bp_set_legacy_attach(FuCorsairBp *self, gboolean is_legacy_attach)
{
self->is_legacy_attach = is_legacy_attach;
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2021 Andrii Dushko <andrii.dushko@developex.net>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <fwupdplugin.h>
#include "fu-corsair-common.h"
#define FU_TYPE_CORSAIR_BP (fu_corsair_bp_get_type())
G_DECLARE_FINAL_TYPE(FuCorsairBp, fu_corsair_bp, FU, CORSAIR_BP, FuUsbDevice)
struct _FuCorsairBpClass {
FuUsbDeviceClass parent_class;
};
gboolean
fu_corsair_bp_get_property(FuCorsairBp *self,
FuCorsairBpProperty property,
guint32 *value,
GError **error);
gboolean
fu_corsair_bp_activate_firmware(FuCorsairBp *self, FuFirmware *firmware, GError **error);
void
fu_corsair_bp_set_cmd_size(FuCorsairBp *self, guint16 write_size, guint16 read_size);
void
fu_corsair_bp_set_endpoints(FuCorsairBp *self, guint8 epin, guint8 epout);
void
fu_corsair_bp_set_legacy_attach(FuCorsairBp *self, gboolean is_legacy_attach);
FuCorsairBp *
fu_corsair_bp_new(GUsbDevice *usb_device, gboolean is_subdevice);

View File

@ -6,7 +6,13 @@
#pragma once
#include <glib.h>
#include <fwupdplugin.h>
#define FU_CORSAIR_DEVICE_FLAG_LEGACY_ATTACH (1 << 0)
#define FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE (1 << 1)
#define FU_CORSAIR_DEVICE_FLAG_NO_VERSION_IN_BOOTLOADER (1 << 2)
#define FU_CORSAIR_MAX_CMD_SIZE 1024
typedef enum {
FU_CORSAIR_DEVICE_UNKNOWN = 0,
@ -14,6 +20,19 @@ typedef enum {
FU_CORSAIR_DEVICE_RECEIVER
} FuCorsairDeviceKind;
typedef enum {
FU_CORSAIR_BP_PROPERTY_MODE = 3,
FU_CORSAIR_BP_PROPERTY_BATTERY_LEVEL = 15,
FU_CORSAIR_BP_PROPERTY_VERSION = 19,
FU_CORSAIR_BP_PROPERTY_BOOTLOADER_VERSION = 20,
FU_CORSAIR_BP_PROPERTY_SUBDEVICES = 54,
} FuCorsairBpProperty;
typedef enum {
FU_CORSAIR_DEVICE_MODE_APPLICATION = 1,
FU_CORSAIR_DEVICE_MODE_BOOTLOADER = 3
} FuCorsairDeviceMode;
FuCorsairDeviceKind
fu_corsair_device_type_from_string(const gchar *kind);

View File

@ -8,104 +8,27 @@
#include <string.h>
#include "fu-corsair-bp.h"
#include "fu-corsair-common.h"
#include "fu-corsair-device.h"
#define CORSAIR_DEFAULT_VENDOR_INTERFACE_ID 1
#define CORSAIR_ACTIVATION_TIMEOUT 30000
#define CORSAIR_MODE_BOOTLOADER 3
#define CORSAIR_FIRST_CHUNK_HEADER_SIZE 7
#define CORSAIR_NEXT_CHUNKS_HEADER_SIZE 3
#define CORSAIR_MAX_CMD_SIZE 1024
#define CORSAIR_TRANSACTION_TIMEOUT 2000
#define CORSAIR_OFFSET_CMD_VERSION 0x03
#define CORSAIR_OFFSET_CMD_CRC 0x08
#define CORSAIR_OFFSET_CMD_MODE 0x03
#define CORSAIR_OFFSET_CMD_STATUS 0x02
#define CORSAIR_OFFSET_CMD_FIRMWARE_SIZE 0x03
#define CORSAIR_OFFSET_CMD_SET_MODE 0x04
#define CORSAIR_TRANSACTION_TIMEOUT 4000
#define CORSAIR_SUBDEVICE_POLL_PERIOD 30000
#define CORSAIR_SUBDEVICE_REBOOT_DELAY (4 * G_USEC_PER_SEC)
#define CORSAIR_SUBDEVICE_RECONNECT_RETRIES 30
#define CORSAIR_SUBDEVICE_RECONNECT_PERIOD 1000
struct _FuCorsairDevice {
FuUsbDevice parent_instance;
FuCorsairDeviceKind device_kind;
guint8 vendor_interface;
guint8 epin;
guint8 epout;
guint16 cmd_write_size;
guint16 cmd_read_size;
gchar *subdevice_id;
FuCorsairBp *bp;
};
G_DEFINE_TYPE(FuCorsairDevice, fu_corsair_device, FU_TYPE_USB_DEVICE)
typedef enum {
FU_CORSAIR_DEVICE_MODE_APPLICATION = 1,
FU_CORSAIR_DEVICE_MODE_BOOTLOADER = 3
} FuCorsairDeviceMode;
static gboolean
fu_corsair_device_command(FuDevice *device, guint8 *data, guint timeout, GError **error)
{
FuCorsairDevice *self = FU_CORSAIR_DEVICE(device);
GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device));
gsize actual_len = 0;
gboolean ret;
ret = g_usb_device_interrupt_transfer(usb_device,
self->epout,
data,
self->cmd_write_size,
&actual_len,
timeout,
NULL,
error);
if (!ret) {
g_prefix_error(error, "failed to write command: ");
return FALSE;
}
if (actual_len != self->cmd_write_size) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"wrong size written: %" G_GSIZE_FORMAT,
actual_len);
return FALSE;
}
memset(data, 0, CORSAIR_MAX_CMD_SIZE);
ret = g_usb_device_interrupt_transfer(usb_device,
self->epin,
data,
self->cmd_read_size,
&actual_len,
timeout,
NULL,
error);
if (!ret) {
g_prefix_error(error, "failed to get command response: ");
return FALSE;
}
if (actual_len != self->cmd_read_size) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"wrong size read: %" G_GSIZE_FORMAT,
actual_len);
return FALSE;
}
if (data[CORSAIR_OFFSET_CMD_STATUS] != 0) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"device replied with error: 0x%02x",
data[CORSAIR_OFFSET_CMD_STATUS]);
return FALSE;
}
return TRUE;
}
static gboolean
fu_corsair_device_probe(FuDevice *device, GError **error)
{
@ -116,6 +39,15 @@ fu_corsair_device_probe(FuDevice *device, GError **error)
GUsbEndpoint *ep2 = NULL;
g_autoptr(GPtrArray) ifaces = NULL;
g_autoptr(GPtrArray) endpoints = NULL;
g_autoptr(FuCorsairBp) bp = NULL;
guint16 cmd_write_size;
guint16 cmd_read_size;
guint8 epin;
guint8 epout;
/* probing are skipped for subdevices */
if (fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE))
return TRUE;
if (!FU_DEVICE_CLASS(fu_corsair_device_parent_class)->probe(device, error))
return FALSE;
@ -143,19 +75,18 @@ fu_corsair_device_probe(FuDevice *device, GError **error)
ep1 = g_ptr_array_index(endpoints, 0);
ep2 = g_ptr_array_index(endpoints, 1);
if (g_usb_endpoint_get_direction(ep1) == G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST) {
self->epin = g_usb_endpoint_get_address(ep1);
self->epout = g_usb_endpoint_get_address(ep2);
self->cmd_read_size = g_usb_endpoint_get_maximum_packet_size(ep1);
self->cmd_write_size = g_usb_endpoint_get_maximum_packet_size(ep2);
epin = g_usb_endpoint_get_address(ep1);
epout = g_usb_endpoint_get_address(ep2);
cmd_read_size = g_usb_endpoint_get_maximum_packet_size(ep1);
cmd_write_size = g_usb_endpoint_get_maximum_packet_size(ep2);
} else {
self->epin = g_usb_endpoint_get_address(ep2);
self->epout = g_usb_endpoint_get_address(ep1);
self->cmd_read_size = g_usb_endpoint_get_maximum_packet_size(ep2);
self->cmd_write_size = g_usb_endpoint_get_maximum_packet_size(ep1);
epin = g_usb_endpoint_get_address(ep2);
epout = g_usb_endpoint_get_address(ep1);
cmd_read_size = g_usb_endpoint_get_maximum_packet_size(ep2);
cmd_write_size = g_usb_endpoint_get_maximum_packet_size(ep1);
}
if (self->cmd_write_size > CORSAIR_MAX_CMD_SIZE ||
self->cmd_read_size > CORSAIR_MAX_CMD_SIZE) {
if (cmd_write_size > FU_CORSAIR_MAX_CMD_SIZE || cmd_read_size > FU_CORSAIR_MAX_CMD_SIZE) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
@ -164,56 +95,223 @@ fu_corsair_device_probe(FuDevice *device, GError **error)
}
fu_usb_device_add_interface(FU_USB_DEVICE(self), self->vendor_interface);
self->bp = fu_corsair_bp_new(usb_device, FALSE);
fu_corsair_bp_set_cmd_size(self->bp, cmd_write_size, cmd_read_size);
fu_corsair_bp_set_endpoints(self->bp, epin, epout);
return TRUE;
}
static gboolean
fu_corsair_poll_subdevice(FuDevice *device, gboolean *subdevice_added, GError **error)
{
guint32 subdevices;
g_autoptr(FuCorsairDevice) child = NULL;
FuCorsairDevice *self = FU_CORSAIR_DEVICE(device);
g_autoptr(FuCorsairBp) child_bp = NULL;
GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device));
if (!fu_corsair_bp_get_property(self->bp,
FU_CORSAIR_BP_PROPERTY_SUBDEVICES,
&subdevices,
error)) {
g_prefix_error(error, "cannot get subdevices: ");
return FALSE;
}
if (subdevices == 0) {
*subdevice_added = FALSE;
return TRUE;
}
child_bp = fu_corsair_bp_new(usb_device, TRUE);
fu_device_incorporate(FU_DEVICE(child_bp), FU_DEVICE(self->bp));
child = fu_corsair_device_new(self, child_bp);
fu_device_add_instance_id(FU_DEVICE(child), self->subdevice_id);
fu_device_set_physical_id(FU_DEVICE(child), fu_device_get_physical_id(device));
fu_device_set_logical_id(FU_DEVICE(child), "subdevice");
fu_device_add_internal_flag(FU_DEVICE(child), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN);
if (!fu_device_probe(FU_DEVICE(child), error))
return FALSE;
if (!fu_device_setup(FU_DEVICE(child), error))
return FALSE;
fu_device_add_child(device, FU_DEVICE(child));
*subdevice_added = TRUE;
return TRUE;
}
static gchar *
fu_corsair_get_version(FuDevice *device, GError **error)
fu_corsair_device_get_version(FuDevice *device, GError **error)
{
FuCorsairDevice *self = FU_CORSAIR_DEVICE(device);
guint32 version_raw;
guint8 data[CORSAIR_MAX_CMD_SIZE] = {0x08, 0x02, 0x13};
if (!fu_corsair_device_command(device, data, CORSAIR_TRANSACTION_TIMEOUT, error))
return NULL;
if (!fu_common_read_uint32_safe(data,
sizeof(data),
CORSAIR_OFFSET_CMD_VERSION,
if (!fu_corsair_bp_get_property(self->bp,
FU_CORSAIR_BP_PROPERTY_VERSION,
&version_raw,
G_LITTLE_ENDIAN,
error)) {
g_prefix_error(error, "parse fail: ");
error))
return NULL;
if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) {
gboolean broken_by_flag =
fu_device_has_private_flag(device,
FU_CORSAIR_DEVICE_FLAG_NO_VERSION_IN_BOOTLOADER);
/* Version 0xffffffff means that previous update was interrupted.
Set version to 0.0.0 in both broken and interrupted cases to make sure that new
firmware will not be rejected because of older version. It is safe to always
pass firmware because setup in bootloader mode can only happen during
emergency update */
if (broken_by_flag || version_raw == G_MAXUINT32) {
version_raw = 0;
}
}
return fu_corsair_version_from_uint32(version_raw);
}
static gchar *
fu_corsair_get_bootloader_version(FuDevice *device, GError **error)
fu_corsair_device_get_bootloader_version(FuCorsairBp *self, GError **error)
{
guint32 version_raw;
guint8 data[CORSAIR_MAX_CMD_SIZE] = {0x08, 0x02, 0x14};
if (!fu_corsair_device_command(device, data, CORSAIR_TRANSACTION_TIMEOUT, error))
return NULL;
if (!fu_common_read_uint32_safe(data,
sizeof(data),
CORSAIR_OFFSET_CMD_VERSION,
if (!fu_corsair_bp_get_property(self,
FU_CORSAIR_BP_PROPERTY_BOOTLOADER_VERSION,
&version_raw,
G_LITTLE_ENDIAN,
error)) {
g_prefix_error(error, "parse fail: ");
error))
return NULL;
}
return fu_corsair_version_from_uint32(version_raw);
}
static gboolean
fu_corsair_device_setup(FuDevice *device, GError **error)
{
guint32 mode;
guint32 battery_level;
g_autofree gchar *bootloader_version = NULL;
g_autofree gchar *version = NULL;
FuCorsairDevice *self = FU_CORSAIR_DEVICE(device);
if (!fu_corsair_bp_get_property(self->bp, FU_CORSAIR_BP_PROPERTY_MODE, &mode, error))
return FALSE;
if (mode == FU_CORSAIR_DEVICE_MODE_BOOTLOADER)
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
version = fu_corsair_device_get_version(device, error);
if (version == NULL) {
g_prefix_error(error, "cannot get version: ");
return FALSE;
}
fu_device_set_version(device, version);
bootloader_version = fu_corsair_device_get_bootloader_version(self->bp, error);
if (bootloader_version == NULL) {
g_prefix_error(error, "cannot get bootloader version: ");
return FALSE;
}
fu_device_set_version_bootloader(device, bootloader_version);
if (fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE) &&
!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) {
if (!fu_corsair_bp_get_property(self->bp,
FU_CORSAIR_BP_PROPERTY_BATTERY_LEVEL,
&battery_level,
error)) {
g_prefix_error(error, "cannot get battery level: ");
return FALSE;
}
fu_device_set_battery_level(device, battery_level / 10);
}
fu_corsair_bp_set_legacy_attach(
self->bp,
fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_LEGACY_ATTACH));
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
/* check for a subdevice */
if (self->subdevice_id != NULL) {
gboolean subdevice_added = FALSE;
g_autoptr(GError) local_error = NULL;
if (!fu_corsair_poll_subdevice(device, &subdevice_added, &local_error)) {
g_warning("error polling subdevice: %s", local_error->message);
} else {
if (!subdevice_added)
fu_device_set_poll_interval(device, CORSAIR_SUBDEVICE_POLL_PERIOD);
}
}
return TRUE;
}
static gboolean
fu_corsair_device_reload(FuDevice *device, GError **error)
{
if (fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE)) {
return fu_corsair_device_setup(device, error);
}
/* USB devices will be reloaded by FWUPD after reenumeration */
return TRUE;
}
static gboolean
fu_corsair_is_subdevice_connected_cb(FuDevice *device, gpointer user_data, GError **error)
{
guint32 subdevices = 0;
FuCorsairDevice *self = FU_CORSAIR_DEVICE(device);
if (!fu_corsair_bp_get_property(self->bp,
FU_CORSAIR_BP_PROPERTY_SUBDEVICES,
&subdevices,
error)) {
g_prefix_error(error, "cannot get subdevices: ");
return FALSE;
}
if (subdevices == 0) {
g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "subdevice is not connected");
return FALSE;
}
return TRUE;
}
static gboolean
fu_corsair_reconnect_subdevice(FuDevice *device, GError **error)
{
FuDevice *parent = fu_device_get_parent(device);
if (parent == NULL) {
g_prefix_error(error, "cannot get parent: ");
return FALSE;
}
/* Wait some time to make sure that a subdevice was disconnected. */
g_usleep(CORSAIR_SUBDEVICE_REBOOT_DELAY);
if (!fu_device_retry_full(parent,
fu_corsair_is_subdevice_connected_cb,
CORSAIR_SUBDEVICE_RECONNECT_RETRIES,
CORSAIR_SUBDEVICE_RECONNECT_PERIOD,
NULL,
error)) {
g_prefix_error(error, "a subdevice did not reconnect after attach: ");
return FALSE;
}
return TRUE;
}
static gboolean
fu_corsair_ensure_mode(FuDevice *device, FuCorsairDeviceMode mode, GError **error)
{
FuCorsairDevice *self = FU_CORSAIR_DEVICE(device);
FuCorsairDeviceMode current_mode;
guint8 set_mode_cmd[CORSAIR_MAX_CMD_SIZE] = {0x08, 0x01, 0x03};
if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) {
current_mode = FU_CORSAIR_DEVICE_MODE_BOOTLOADER;
@ -224,55 +322,31 @@ fu_corsair_ensure_mode(FuDevice *device, FuCorsairDeviceMode mode, GError **erro
if (mode == current_mode)
return TRUE;
set_mode_cmd[CORSAIR_OFFSET_CMD_SET_MODE] = mode;
if (!fu_corsair_device_command(device, set_mode_cmd, CORSAIR_TRANSACTION_TIMEOUT, error)) {
g_prefix_error(error, "set mode command fail: ");
if (mode == FU_CORSAIR_DEVICE_MODE_APPLICATION) {
if (!fu_device_attach(FU_DEVICE(self->bp), error)) {
g_prefix_error(error, "attach failed: ");
return FALSE;
}
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
return TRUE;
}
static gboolean
fu_corsair_device_setup(FuDevice *device, GError **error)
{
guint32 mode;
g_autofree gchar *bootloader_version = NULL;
g_autofree gchar *version = NULL;
guint8 get_mode_cmd[CORSAIR_MAX_CMD_SIZE] = {0x08, 0x02, 0x03};
version = fu_corsair_get_version(device, error);
if (version == NULL) {
g_prefix_error(error, "cannot get version: ");
} else {
if (!fu_device_detach(FU_DEVICE(self->bp), error)) {
g_prefix_error(error, "detach failed: ");
return FALSE;
}
fu_device_set_version(FU_DEVICE(device), version);
bootloader_version = fu_corsair_get_bootloader_version(device, error);
if (bootloader_version == NULL) {
g_prefix_error(error, "cannot get bootloader version: ");
return FALSE;
}
fu_device_set_version_bootloader(device, bootloader_version);
if (!fu_corsair_device_command(device, get_mode_cmd, CORSAIR_TRANSACTION_TIMEOUT, error))
return FALSE;
if (!fu_common_read_uint32_safe(get_mode_cmd,
sizeof(get_mode_cmd),
CORSAIR_OFFSET_CMD_MODE,
&mode,
G_LITTLE_ENDIAN,
error)) {
g_prefix_error(error, "cannot parse device mode: ");
return FALSE;
}
if (mode == FU_CORSAIR_DEVICE_MODE_BOOTLOADER)
if (fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE)) {
if (!fu_corsair_reconnect_subdevice(device, error)) {
g_prefix_error(error, "subdevice did not reconnect: ");
return FALSE;
}
if (mode == FU_CORSAIR_DEVICE_MODE_BOOTLOADER) {
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
} else {
fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
}
} else {
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
}
return TRUE;
}
@ -289,120 +363,6 @@ fu_corsair_device_detach(FuDevice *device, FuProgress *progress, GError **error)
return fu_corsair_ensure_mode(device, FU_CORSAIR_DEVICE_MODE_BOOTLOADER, error);
}
static gboolean
fu_corsair_activate_firmware(FuDevice *device, guint32 crc, GError **error)
{
guint8 commit_cmd[CORSAIR_MAX_CMD_SIZE] = {0x08, 0x05, 0x01, 0x00};
guint8 activate_cmd[CORSAIR_MAX_CMD_SIZE] =
{0x08, 0x16, 0x00, 0x01, 0x03, 0x00, 0x01, 0x01};
if (!fu_corsair_device_command(device, commit_cmd, CORSAIR_TRANSACTION_TIMEOUT, error)) {
g_prefix_error(error, "firmware commit fail: ");
return FALSE;
}
if (!fu_common_write_uint32_safe(activate_cmd,
sizeof(activate_cmd),
CORSAIR_OFFSET_CMD_CRC,
crc,
G_LITTLE_ENDIAN,
error)) {
g_prefix_error(error, "cannot serialize CRC: ");
return FALSE;
}
return fu_corsair_device_command(device, activate_cmd, CORSAIR_ACTIVATION_TIMEOUT, error);
}
static gboolean
fu_corsair_write_first_chunk(FuDevice *device,
FuChunk *chunk,
guint32 firmware_size,
GError **error)
{
guint8 init_cmd[CORSAIR_MAX_CMD_SIZE] = {0x08, 0x0d, 0x00, 0x03};
guint8 write_cmd[CORSAIR_MAX_CMD_SIZE] = {0x08, 0x06, 0x00};
if (!fu_corsair_device_command(device, init_cmd, CORSAIR_TRANSACTION_TIMEOUT, error)) {
g_prefix_error(error, "firmware init fail: ");
return FALSE;
}
if (!fu_common_write_uint32_safe(write_cmd,
sizeof(write_cmd),
CORSAIR_OFFSET_CMD_FIRMWARE_SIZE,
firmware_size,
G_LITTLE_ENDIAN,
error)) {
g_prefix_error(error, "cannot serialize firmware size: ");
return FALSE;
}
if (!fu_memcpy_safe(write_cmd,
sizeof(write_cmd),
CORSAIR_FIRST_CHUNK_HEADER_SIZE,
fu_chunk_get_data(chunk),
fu_chunk_get_data_sz(chunk),
0,
fu_chunk_get_data_sz(chunk),
error)) {
g_prefix_error(error, "cannot set data: ");
return FALSE;
}
if (!fu_corsair_device_command(device, write_cmd, CORSAIR_TRANSACTION_TIMEOUT, error)) {
g_prefix_error(error, "write command fail: ");
return FALSE;
}
return TRUE;
}
static gboolean
fu_corsair_write_chunk(FuDevice *device, FuChunk *chunk, GError **error)
{
guint8 cmd[CORSAIR_MAX_CMD_SIZE] = {0x08, 0x07};
if (!fu_memcpy_safe(cmd,
sizeof(cmd),
CORSAIR_NEXT_CHUNKS_HEADER_SIZE,
fu_chunk_get_data(chunk),
fu_chunk_get_data_sz(chunk),
0,
fu_chunk_get_data_sz(chunk),
error)) {
g_prefix_error(error, "cannot set data: ");
return FALSE;
}
if (!fu_corsair_device_command(device, cmd, CORSAIR_TRANSACTION_TIMEOUT, error)) {
g_prefix_error(error, "write command fail: ");
return FALSE;
}
return TRUE;
}
static gboolean
fu_corsair_device_write_firmware_chunks(FuDevice *device,
FuChunk *first_chunk,
GPtrArray *chunks,
FuProgress *progress,
guint32 firmware_size,
GError **error)
{
fu_progress_set_id(progress, G_STRLOC);
fu_progress_set_steps(progress, chunks->len + 1);
if (!fu_corsair_write_first_chunk(device, first_chunk, firmware_size, error)) {
g_prefix_error(error, "cannot write first chunk: ");
return FALSE;
}
fu_progress_step_done(progress);
for (guint id = 0; id < chunks->len; id++) {
FuChunk *chunk = g_ptr_array_index(chunks, id);
if (!fu_corsair_write_chunk(device, chunk, error)) {
g_prefix_error(error, "cannot write chunk %u", id);
return FALSE;
}
fu_progress_step_done(progress);
}
return TRUE;
}
static gboolean
fu_corsair_device_write_firmware(FuDevice *device,
FuFirmware *firmware,
@ -410,71 +370,38 @@ fu_corsair_device_write_firmware(FuDevice *device,
FwupdInstallFlags flags,
GError **error)
{
const guint8 *firmware_raw;
guint32 crc;
gsize firmware_size;
g_autoptr(GBytes) blob = NULL;
g_autoptr(GPtrArray) chunks = NULL;
g_autoptr(FuChunk) firstChunk = NULL;
g_autoptr(GBytes) rest_of_firmware = NULL;
FuCorsairDevice *self = FU_CORSAIR_DEVICE(device);
guint32 first_chunk_size = self->cmd_write_size - CORSAIR_FIRST_CHUNK_HEADER_SIZE;
g_autoptr(GBytes) firmware_bytes = fu_firmware_get_bytes(firmware, error);
blob = fu_firmware_get_bytes(firmware, error);
if (blob == NULL) {
if (firmware_bytes == NULL) {
g_prefix_error(error, "cannot get firmware data");
return FALSE;
}
firmware_raw = fu_bytes_get_data_safe(blob, &firmware_size, error);
if (firmware_raw == NULL) {
g_prefix_error(error, "cannot get firmware data: ");
return FALSE;
}
/* the firmware size should be greater than 1 chunk */
if (firmware_size <= first_chunk_size) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"update file should be bigger");
return FALSE;
}
firstChunk = fu_chunk_new(0, 0, 0, g_bytes_get_data(blob, NULL), first_chunk_size);
rest_of_firmware = fu_common_bytes_new_offset(blob,
first_chunk_size,
firmware_size - first_chunk_size,
error);
if (rest_of_firmware == NULL) {
g_prefix_error(error, "cannot get firmware past first chunk: ");
return FALSE;
}
chunks =
fu_chunk_array_new_from_bytes(rest_of_firmware,
first_chunk_size,
0,
self->cmd_write_size - CORSAIR_NEXT_CHUNKS_HEADER_SIZE);
fu_progress_set_id(progress, G_STRLOC);
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 95);
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5);
crc = fu_corsair_calculate_crc(firmware_raw, firmware_size);
if (!fu_corsair_device_write_firmware_chunks(device,
firstChunk,
chunks,
if (!fu_device_write_firmware(FU_DEVICE(self->bp),
firmware_bytes,
fu_progress_get_child(progress),
g_bytes_get_size(blob),
error))
flags,
error)) {
g_prefix_error(error, "cannot write firmware");
return FALSE;
}
fu_progress_step_done(progress);
if (!fu_corsair_activate_firmware(device, crc, error)) {
if (!fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_LEGACY_ATTACH)) {
if (!fu_corsair_bp_activate_firmware(self->bp, firmware, error)) {
g_prefix_error(error, "firmware activation fail: ");
return FALSE;
}
fu_progress_step_done(progress);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
}
fu_progress_step_done(progress);
return TRUE;
}
@ -490,8 +417,8 @@ fu_corsair_device_to_string(FuDevice *device, guint idt, GString *str)
idt,
"DeviceKind",
fu_corsair_device_type_to_string(self->device_kind));
fu_common_string_append_kx(str, idt, "InEndpoint", self->epin);
fu_common_string_append_kx(str, idt, "OutEndpoint", self->epout);
fu_device_add_string(FU_DEVICE(self->bp), idt, str);
}
static void
@ -527,25 +454,70 @@ fu_corsair_set_quirk_kv(FuDevice *device, const gchar *key, const gchar *value,
}
self->vendor_interface = vendor_interface;
return TRUE;
} else if (g_strcmp0(key, "CorsairSubdeviceId") == 0) {
self->subdevice_id = g_strdup(value);
return TRUE;
}
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "quirk key not supported");
return FALSE;
}
static gboolean
fu_corsair_device_poll(FuDevice *device, GError **error)
{
gboolean subdevice_added = FALSE;
g_autoptr(FuDeviceLocker) locker = NULL;
locker = fu_device_locker_new(device, error);
if (locker == NULL) {
g_prefix_error(error, "cannot open device: ");
return FALSE;
}
if (!fu_corsair_poll_subdevice(device, &subdevice_added, error)) {
return FALSE;
}
/* stop polling if a subdevice was added */
if (subdevice_added) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"subdevice added successfully");
return FALSE;
}
return TRUE;
}
static void
fu_corsair_device_finalize(GObject *object)
{
FuCorsairDevice *self = FU_CORSAIR_DEVICE(object);
g_free(self->subdevice_id);
g_object_unref(self->bp);
}
static void
fu_corsair_device_class_init(FuCorsairDeviceClass *klass)
{
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
GObjectClass *object_class = G_OBJECT_CLASS(klass);
klass_device->poll = fu_corsair_device_poll;
klass_device->probe = fu_corsair_device_probe;
klass_device->set_quirk_kv = fu_corsair_set_quirk_kv;
klass_device->setup = fu_corsair_device_setup;
klass_device->reload = fu_corsair_device_reload;
klass_device->attach = fu_corsair_device_attach;
klass_device->detach = fu_corsair_device_detach;
klass_device->write_firmware = fu_corsair_device_write_firmware;
klass_device->to_string = fu_corsair_device_to_string;
klass_device->set_progress = fu_corsair_device_set_progress;
object_class->finalize = fu_corsair_device_finalize;
}
static void
@ -556,6 +528,16 @@ fu_corsair_device_init(FuCorsairDevice *device)
self->device_kind = FU_CORSAIR_DEVICE_MOUSE;
self->vendor_interface = CORSAIR_DEFAULT_VENDOR_INTERFACE_ID;
fu_device_register_private_flag(FU_DEVICE(device),
FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE,
"is-subdevice");
fu_device_register_private_flag(FU_DEVICE(device),
FU_CORSAIR_DEVICE_FLAG_LEGACY_ATTACH,
"legacy-attach");
fu_device_register_private_flag(FU_DEVICE(device),
FU_CORSAIR_DEVICE_FLAG_NO_VERSION_IN_BOOTLOADER,
"no-version-in-bl");
fu_device_set_remove_delay(FU_DEVICE(device), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE);
fu_device_set_version_format(FU_DEVICE(device), FWUPD_VERSION_FORMAT_TRIPLET);
@ -564,3 +546,19 @@ fu_corsair_device_init(FuCorsairDevice *device)
fu_device_add_internal_flag(FU_DEVICE(device), FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID);
fu_device_add_protocol(FU_DEVICE(device), "com.corsair.bp");
}
FuCorsairDevice *
fu_corsair_device_new(FuCorsairDevice *parent, FuCorsairBp *bp)
{
FuCorsairDevice *self = NULL;
FuDevice *device = FU_DEVICE(parent);
self = g_object_new(FU_TYPE_CORSAIR_DEVICE,
"context",
fu_device_get_context(device),
"usb_device",
fu_usb_device_get_dev(FU_USB_DEVICE(device)),
NULL);
self->bp = g_object_ref(bp);
return self;
}

View File

@ -8,5 +8,15 @@
#include <fwupdplugin.h>
#include "fu-corsair-bp.h"
#include "fu-corsair-common.h"
#define FU_TYPE_CORSAIR_DEVICE (fu_corsair_device_get_type())
G_DECLARE_FINAL_TYPE(FuCorsairDevice, fu_corsair_device, FU, CORSAIR_DEVICE, FuUsbDevice)
struct _FuCorsairDeviceClass {
FuUsbDeviceClass parent_class;
};
FuCorsairDevice *
fu_corsair_device_new(FuCorsairDevice *parent, FuCorsairBp *bp);

View File

@ -21,6 +21,7 @@ fu_plugin_corsair_load(FuContext *ctx)
{
fu_context_add_quirk_key(ctx, "CorsairDeviceKind");
fu_context_add_quirk_key(ctx, "CorsairVendorInterfaceId");
fu_context_add_quirk_key(ctx, "CorsairSubdeviceId");
}
void

View File

@ -11,6 +11,7 @@ shared_module('fu_plugin_corsair',
'fu-plugin-corsair.c',
'fu-corsair-common.c',
'fu-corsair-device.c',
'fu-corsair-bp.c',
],
include_directories : [
root_incdir,