mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-15 07:24:46 +00:00

Doing this unconditionally means we accidentally 'bleed' one device mode into another in a non-obvious way. For instance, a device might have two operating modes with different GUIDs. If firmware is supplied for both modes in the same cabinet archive then we might accidentally match the 'wrong' firmware when the daemon has observed a mode switch and added the counterpart GUIDs. We only really need the counterpart GUIDs when switching between Jabra, 8bitdo and DFU devices where the DFU bootloader VID:PID is not manually tagged with `CounterpartGuid` in a quirk file. In the general case lets keep it simple to avoid difficult to find bugs.
148 lines
4.2 KiB
C
148 lines
4.2 KiB
C
/*
|
|
* Copyright (C) 2016-2017 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "fu-nitrokey-common.h"
|
|
#include "fu-nitrokey-device.h"
|
|
|
|
G_DEFINE_TYPE (FuNitrokeyDevice, fu_nitrokey_device, FU_TYPE_HID_DEVICE)
|
|
|
|
typedef struct {
|
|
guint8 command;
|
|
const guint8 *buf_in;
|
|
gsize buf_in_sz;
|
|
guint8 *buf_out;
|
|
gsize buf_out_sz;
|
|
} NitrokeyRequest;
|
|
|
|
static gboolean
|
|
nitrokey_execute_cmd_cb (FuDevice *device, gpointer user_data, GError **error)
|
|
{
|
|
NitrokeyRequest *req = (NitrokeyRequest *) user_data;
|
|
NitrokeyHidResponse res;
|
|
guint32 crc_tmp;
|
|
guint8 buf[64];
|
|
|
|
/* create the request */
|
|
memset (buf, 0x00, sizeof(buf));
|
|
buf[0] = req->command;
|
|
if (req->buf_in != NULL)
|
|
memcpy (&buf[1], req->buf_in, req->buf_in_sz);
|
|
crc_tmp = fu_nitrokey_perform_crc32 (buf, sizeof(buf) - 4);
|
|
fu_common_write_uint32 (&buf[NITROKEY_REQUEST_DATA_LENGTH + 1], crc_tmp, G_LITTLE_ENDIAN);
|
|
|
|
/* send request */
|
|
if (!fu_hid_device_set_report (FU_HID_DEVICE (device),
|
|
0x0002, buf, sizeof(buf),
|
|
NITROKEY_TRANSACTION_TIMEOUT,
|
|
FU_HID_DEVICE_FLAG_IS_FEATURE,
|
|
error))
|
|
return FALSE;
|
|
|
|
/* get response */
|
|
memset (buf, 0x00, sizeof(buf));
|
|
if (!fu_hid_device_get_report (FU_HID_DEVICE (device),
|
|
0x0002, buf, sizeof(buf),
|
|
NITROKEY_TRANSACTION_TIMEOUT,
|
|
FU_HID_DEVICE_FLAG_IS_FEATURE,
|
|
error))
|
|
return FALSE;
|
|
|
|
/* verify this is the answer to the question we asked */
|
|
memcpy (&res, buf, sizeof(buf));
|
|
if (GUINT32_FROM_LE (res.last_command_crc) != crc_tmp) {
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"got response CRC %x, expected %x",
|
|
GUINT32_FROM_LE (res.last_command_crc), crc_tmp);
|
|
return FALSE;
|
|
}
|
|
|
|
/* verify the response checksum */
|
|
crc_tmp = fu_nitrokey_perform_crc32 (buf, sizeof(res) - 4);
|
|
if (GUINT32_FROM_LE (res.crc) != crc_tmp) {
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"got packet CRC %x, expected %x",
|
|
GUINT32_FROM_LE (res.crc), crc_tmp);
|
|
return FALSE;
|
|
}
|
|
|
|
/* copy out the payload */
|
|
if (req->buf_out != NULL)
|
|
memcpy (req->buf_out, &res.payload, req->buf_out_sz);
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
nitrokey_execute_cmd_full (FuDevice *device, guint8 command,
|
|
const guint8 *buf_in, gsize buf_in_sz,
|
|
guint8 *buf_out, gsize buf_out_sz,
|
|
GError **error)
|
|
{
|
|
NitrokeyRequest req = {
|
|
.command = command,
|
|
.buf_in = buf_in,
|
|
.buf_in_sz = buf_in_sz,
|
|
.buf_out = buf_out,
|
|
.buf_out_sz = buf_out_sz,
|
|
};
|
|
g_return_val_if_fail (buf_in_sz <= NITROKEY_REQUEST_DATA_LENGTH, FALSE);
|
|
g_return_val_if_fail (buf_out_sz <= NITROKEY_REPLY_DATA_LENGTH, FALSE);
|
|
return fu_device_retry (device, nitrokey_execute_cmd_cb,
|
|
NITROKEY_NR_RETRIES, &req, error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_nitrokey_device_setup (FuDevice *device, GError **error)
|
|
{
|
|
NitrokeyGetDeviceStatusPayload payload;
|
|
guint8 buf_reply[NITROKEY_REPLY_DATA_LENGTH];
|
|
g_autofree gchar *version = NULL;
|
|
|
|
/* get firmware version */
|
|
if (!nitrokey_execute_cmd_full (device,
|
|
NITROKEY_CMD_GET_DEVICE_STATUS,
|
|
NULL, 0,
|
|
buf_reply, sizeof(buf_reply),
|
|
error)) {
|
|
g_prefix_error (error, "failed to do get firmware version: ");
|
|
return FALSE;
|
|
}
|
|
if (g_getenv ("FWUPD_NITROKEY_VERBOSE") != NULL)
|
|
fu_common_dump_raw (G_LOG_DOMAIN, "payload", buf_reply, sizeof(buf_reply));
|
|
memcpy (&payload, buf_reply, sizeof(payload));
|
|
version = g_strdup_printf ("%u.%u", payload.VersionMajor, payload.VersionMinor);
|
|
fu_device_set_version (FU_DEVICE (device), version);
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
fu_nitrokey_device_init (FuNitrokeyDevice *device)
|
|
{
|
|
fu_device_set_remove_delay (FU_DEVICE (device), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE);
|
|
fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_UPDATABLE);
|
|
fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_ADD_COUNTERPART_GUIDS);
|
|
fu_device_set_version_format (FU_DEVICE (device), FWUPD_VERSION_FORMAT_PAIR);
|
|
fu_device_retry_set_delay (FU_DEVICE (device), 100);
|
|
}
|
|
|
|
static void
|
|
fu_nitrokey_device_class_init (FuNitrokeyDeviceClass *klass)
|
|
{
|
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
|
|
klass_device->setup = fu_nitrokey_device_setup;
|
|
}
|