jabra: Move the Jabra-specific detach out into its own plugin

This also lets us remove the call to dfu_device_wait_for_replug() which was
causing a deadlock due to unsafe main context usage. Splitting the code allows
us to use the device list to watch for replug, without adding even more Jabra-
specific plugin code to the DFU plugin.

Looking at this with a 40,000ft view, the Jabra runtime really doesn't have
much in common with DFU and the reason it was originally all lumped together
was that the daemon couldn't "change" plugins between detach and update.

It's unfortunate that we have to include a sleep() in the DFU code after the
DFU probe, but this is specified by Jabra themselves. Attempting to open the
device without waiting reboots the hub back into runtime firmware mode, so we
can't even retry the failing setup action.
This commit is contained in:
Richard Hughes 2019-10-17 11:20:39 +01:00
parent 2031ce3bf6
commit 6bbc4c787b
13 changed files with 362 additions and 228 deletions

View File

@ -316,6 +316,7 @@ rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom
%if 0%{?enable_flashrom}
%{_libdir}/fwupd-plugins-3/libfu_plugin_flashrom.so
%endif
%{_libdir}/fwupd-plugins-3/libfu_plugin_jabra.so
%if 0%{?have_modem_manager}
%{_libdir}/fwupd-plugins-3/libfu_plugin_modem_manager.so
%endif

View File

@ -35,4 +35,3 @@ This plugin uses the following plugin-specific quirks:
|------------------------|---------------------------------------------|-----------------------|
|`DfuFlags` | Optional quirks for a DFU device which doesn't follow the DFU 1.0 or 1.1 specification | 1.0.1|
|`DfuForceVersion` | Forces a specific DFU version for the hardware device. This is required if the device does not set, or sets incorrectly, items in the DFU functional descriptor. |1.0.1|
|`DfuJabraDetach` | Assigns the two magic bytes sent to the Jabra hardware when the device is in runtime mode to make it switch into DFU mode.|1.0.1|

View File

@ -63,18 +63,6 @@
*/
#define FU_QUIRKS_DFU_FORCE_VERSION "DfuForceVersion"
/**
* FU_QUIRKS_DFU_JABRA_DETACH:
* @key: the USB device ID, e.g. `USB\VID_0763&PID_2806`
* @value: the two uint8_t unlock values, encoded in base 16, e.g. `0201`
*
* Assigns the two magic bytes sent to the Jabra hardware when the device is
* in runtime mode to make it switch into DFU mode.
*
* Since: 1.0.1
*/
#define FU_QUIRKS_DFU_JABRA_DETACH "DfuJabraDetach"
#include "config.h"
#include <string.h>
@ -97,11 +85,9 @@ typedef struct {
DfuState state;
DfuStatus status;
GPtrArray *targets;
GUsbContext *usb_context;
gboolean done_upload_or_download;
gboolean claimed_interface;
gchar *chip_id;
gchar *jabra_detach;
guint16 version;
guint16 force_version;
guint16 runtime_pid;
@ -545,20 +531,6 @@ dfu_device_remove_attribute (DfuDevice *device, DfuDeviceAttributes attribute)
priv->attributes &= ~attribute;
}
void
dfu_device_set_usb_context (DfuDevice *device, GUsbContext *quirks)
{
DfuDevicePrivate *priv = GET_PRIVATE (device);
g_set_object (&priv->usb_context, quirks);
}
GUsbContext *
dfu_device_get_usb_context (DfuDevice *device)
{
DfuDevicePrivate *priv = GET_PRIVATE (device);
return priv->usb_context;
}
/**
* dfu_device_new:
*
@ -943,23 +915,6 @@ dfu_device_refresh (DfuDevice *device, GError **error)
return TRUE;
}
static guint8
_g_usb_device_get_interface_for_class (GUsbDevice *dev,
guint8 intf_class,
GError **error)
{
g_autoptr(GPtrArray) intfs = NULL;
intfs = g_usb_device_get_interfaces (dev, error);
if (intfs == NULL)
return 0xff;
for (guint i = 0; i < intfs->len; i++) {
GUsbInterface *intf = g_ptr_array_index (intfs, i);
if (g_usb_interface_get_class (intf) == intf_class)
return g_usb_interface_get_number (intf);
}
return 0xff;
}
static gboolean
dfu_device_request_detach (DfuDevice *self, GError **error)
{
@ -1007,7 +962,6 @@ dfu_device_detach (FuDevice *device, GError **error)
DfuDevice *self = DFU_DEVICE (device);
DfuDevicePrivate *priv = GET_PRIVATE (self);
GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device));
g_autoptr(GError) error_local = NULL;
g_return_val_if_fail (DFU_IS_DEVICE (self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
@ -1028,75 +982,6 @@ dfu_device_detach (FuDevice *device, GError **error)
return FALSE;
}
/* handle Jabra devices that need a magic HID packet */
if (priv->jabra_detach != NULL) {
guint8 adr = 0x00;
guint8 rep = 0x00;
guint8 iface_hid;
g_autofree guint8 *buf = g_malloc0 (33);
g_autoptr(GError) error_jabra = NULL;
/* parse string and create magic packet */
rep = fu_firmware_strparse_uint8 (priv->jabra_detach + 0);
adr = fu_firmware_strparse_uint8 (priv->jabra_detach + 2);
buf[0] = rep;
buf[1] = adr;
buf[2] = 0x00;
buf[3] = 0x01;
buf[4] = 0x85;
buf[5] = 0x07;
/* detach the HID interface from the kernel driver */
iface_hid = _g_usb_device_get_interface_for_class (usb_device,
G_USB_DEVICE_CLASS_HID,
&error_local);
if (iface_hid == 0xff) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"cannot find HID interface: %s",
error_local->message);
return FALSE;
}
g_debug ("claiming interface 0x%02x", iface_hid);
if (!g_usb_device_claim_interface (usb_device, (gint) iface_hid,
G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER,
&error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"cannot claim interface 0x%02x: %s",
iface_hid, error_local->message);
return FALSE;
}
/* send magic to device */
if (!g_usb_device_control_transfer (usb_device,
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
G_USB_DEVICE_REQUEST_TYPE_CLASS,
G_USB_DEVICE_RECIPIENT_INTERFACE,
0x09,
0x0200 | rep,
0x0003,
buf, 33, NULL,
FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE,
NULL, /* cancellable */
&error_jabra)) {
g_debug ("whilst sending magic: %s, ignoring",
error_jabra->message);
}
/* wait for device to re-appear */
fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART);
if (!dfu_device_wait_for_replug (self, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error))
return FALSE;
/* wait 10 seconds for DFU mode to settle */
g_debug ("waiting for Jabra device to settle...");
fu_device_set_status (device, FWUPD_STATUS_DEVICE_BUSY);
g_usleep (10 * G_USEC_PER_SEC);
}
/* the device has no DFU runtime, so cheat */
if (priv->state == DFU_STATE_APP_IDLE &&
fu_device_has_custom_flag (FU_DEVICE (self), "no-dfu-runtime"))
@ -1357,46 +1242,10 @@ dfu_device_probe (FuUsbDevice *device, GError **error)
g_usb_device_get_pid (usb_device));
}
/* success */
return TRUE;
}
/**
* dfu_device_wait_for_replug:
* @device: a #DfuDevice
* @timeout: the maximum amount of time to wait
* @error: a #GError, or %NULL
*
* Waits for a DFU device to disconnect and reconnect.
* This does rely on a #GUsbContext being set up before this is called.
*
* Return value: %TRUE for success
**/
gboolean
dfu_device_wait_for_replug (DfuDevice *device, guint timeout, GError **error)
{
DfuDevicePrivate *priv = GET_PRIVATE (device);
GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device));
g_autoptr(GUsbDevice) usb_device2 = NULL;
/* close */
fu_device_close (FU_DEVICE (device), NULL);
/* watch the device disappear and re-appear */
usb_device2 = g_usb_context_wait_for_replug (priv->usb_context,
usb_device,
FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE,
error);
if (usb_device2 == NULL)
return FALSE;
/* re-open with new device set */
fu_device_set_status (FU_DEVICE (device), FWUPD_STATUS_IDLE);
fu_usb_device_set_dev (FU_USB_DEVICE (device), usb_device2);
if (!fu_device_open (FU_DEVICE (device), error))
return FALSE;
if (!dfu_device_refresh_and_clear (device, error))
return FALSE;
/* hardware rom Jabra literally reboots if you try to retry a failed
* write -- there's no way to avoid blocking the daemon like this... */
if (fu_device_has_custom_flag (FU_DEVICE (device), "attach-extra-reset"))
g_usleep (10 * G_USEC_PER_SEC);
/* success */
return TRUE;
@ -1495,16 +1344,6 @@ dfu_device_attach (FuDevice *device, GError **error)
if (!dfu_target_attach (target, error))
return FALSE;
/* some devices need yet another reset */
if (fu_device_has_custom_flag (FU_DEVICE (self), "attach-extra-reset")) {
if (!dfu_device_wait_for_replug (self,
FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE,
error))
return FALSE;
if (!dfu_device_reset (self, error))
return FALSE;
}
/* success */
priv->force_version = 0x0;
fu_device_set_status (device, FWUPD_STATUS_IDLE);
@ -1874,17 +1713,6 @@ dfu_device_set_quirk_kv (FuDevice *device,
DfuDevice *self = DFU_DEVICE (device);
DfuDevicePrivate *priv = GET_PRIVATE (self);
if (g_strcmp0 (key, FU_QUIRKS_DFU_JABRA_DETACH) == 0) {
if (value != NULL && strlen (value) == 4) {
priv->jabra_detach = g_strdup (value);
return TRUE;
}
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"unsupported jabra quirk format");
return FALSE;
}
if (g_strcmp0 (key, FU_QUIRKS_DFU_FORCE_VERSION) == 0) {
if (value != NULL && strlen (value) == 4) {
priv->force_version = fu_firmware_strparse_uint16 (value);
@ -1943,10 +1771,7 @@ dfu_device_finalize (GObject *object)
DfuDevice *device = DFU_DEVICE (object);
DfuDevicePrivate *priv = GET_PRIVATE (device);
if (priv->usb_context != NULL)
g_object_unref (priv->usb_context);
g_free (priv->chip_id);
g_free (priv->jabra_detach);
g_ptr_array_unref (priv->targets);
G_OBJECT_CLASS (dfu_device_parent_class)->finalize (object);

View File

@ -63,9 +63,6 @@ guint16 dfu_device_get_runtime_pid (DfuDevice *device);
guint16 dfu_device_get_runtime_release (DfuDevice *device);
gboolean dfu_device_reset (DfuDevice *device,
GError **error);
gboolean dfu_device_wait_for_replug (DfuDevice *device,
guint timeout,
GError **error);
DfuFirmware *dfu_device_upload (DfuDevice *device,
DfuTargetTransferFlags flags,
GError **error);
@ -96,9 +93,6 @@ void dfu_device_set_transfer_size (DfuDevice *device,
guint16 transfer_size);
void dfu_device_set_timeout (DfuDevice *device,
guint timeout_ms);
void dfu_device_set_usb_context (DfuDevice *device,
GUsbContext *quirks);
GUsbContext *dfu_device_get_usb_context (DfuDevice *device);
void dfu_device_error_fixup (DfuDevice *device,
GError **error);
guint dfu_device_get_download_timeout (DfuDevice *device);

View File

@ -239,7 +239,6 @@ dfu_tool_get_default_device (DfuToolPrivate *priv, GError **error)
}
device = dfu_device_new (usb_device);
fu_device_set_quirks (FU_DEVICE (device), priv->quirks);
dfu_device_set_usb_context (device, usb_context);
return device;
}
@ -249,7 +248,6 @@ dfu_tool_get_default_device (DfuToolPrivate *priv, GError **error)
GUsbDevice *usb_device = g_ptr_array_index (devices, i);
g_autoptr(DfuDevice) device = dfu_device_new (usb_device);
fu_device_set_quirks (FU_DEVICE (device), priv->quirks);
dfu_device_set_usb_context (device, usb_context);
if (fu_device_probe (FU_DEVICE (device), NULL))
return g_steal_pointer (&device);
}
@ -262,6 +260,41 @@ dfu_tool_get_default_device (DfuToolPrivate *priv, GError **error)
return NULL;
}
static gboolean
dfu_device_wait_for_replug (DfuToolPrivate *priv, DfuDevice *device, guint timeout, GError **error)
{
GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device));
g_autoptr(GUsbDevice) usb_device2 = NULL;
g_autoptr(GUsbContext) usb_context = NULL;
/* get all the DFU devices */
usb_context = g_usb_context_new (error);
if (usb_context == NULL)
return FALSE;
/* close */
fu_device_close (FU_DEVICE (device), NULL);
/* watch the device disappear and re-appear */
usb_device2 = g_usb_context_wait_for_replug (usb_context,
usb_device,
FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE,
error);
if (usb_device2 == NULL)
return FALSE;
/* re-open with new device set */
fu_device_set_status (FU_DEVICE (device), FWUPD_STATUS_IDLE);
fu_usb_device_set_dev (FU_USB_DEVICE (device), usb_device2);
if (!fu_device_open (FU_DEVICE (device), error))
return FALSE;
if (!dfu_device_refresh_and_clear (device, error))
return FALSE;
/* success */
return TRUE;
}
static gboolean
dfu_tool_set_vendor (DfuToolPrivate *priv, gchar **values, GError **error)
{
@ -671,7 +704,7 @@ dfu_tool_read_alt (DfuToolPrivate *priv, gchar **values, GError **error)
g_debug ("detaching");
if (!fu_device_detach (FU_DEVICE (device), error))
return FALSE;
if (!dfu_device_wait_for_replug (device,
if (!dfu_device_wait_for_replug (priv, device,
FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE,
error))
return FALSE;
@ -707,7 +740,7 @@ dfu_tool_read_alt (DfuToolPrivate *priv, gchar **values, GError **error)
/* do host reset */
if (!fu_device_attach (FU_DEVICE (device), error))
return FALSE;
if (!dfu_device_wait_for_replug (device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error))
if (!dfu_device_wait_for_replug (priv, device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error))
return FALSE;
/* create new firmware object */
@ -796,7 +829,7 @@ dfu_tool_read (DfuToolPrivate *priv, gchar **values, GError **error)
if (!fu_device_has_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) {
if (!fu_device_detach (FU_DEVICE (device), error))
return FALSE;
if (!dfu_device_wait_for_replug (device,
if (!dfu_device_wait_for_replug (priv, device,
FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE,
error)) {
return FALSE;
@ -815,7 +848,7 @@ dfu_tool_read (DfuToolPrivate *priv, gchar **values, GError **error)
/* do host reset */
if (!fu_device_attach (FU_DEVICE (device), error))
return FALSE;
if (!dfu_device_wait_for_replug (device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error))
if (!dfu_device_wait_for_replug (priv, device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error))
return FALSE;
/* save file */
@ -1034,7 +1067,7 @@ dfu_tool_write_alt (DfuToolPrivate *priv, gchar **values, GError **error)
g_debug ("detaching");
if (!fu_device_detach (FU_DEVICE (device), error))
return FALSE;
if (!dfu_device_wait_for_replug (device, 5000, error))
if (!dfu_device_wait_for_replug (priv, device, 5000, error))
return FALSE;
}
@ -1096,7 +1129,7 @@ dfu_tool_write_alt (DfuToolPrivate *priv, gchar **values, GError **error)
/* do host reset */
if (!fu_device_attach (FU_DEVICE (device), error))
return FALSE;
if (!dfu_device_wait_for_replug (device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error))
if (!dfu_device_wait_for_replug (priv, device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error))
return FALSE;
/* success */
@ -1141,7 +1174,7 @@ dfu_tool_write (DfuToolPrivate *priv, gchar **values, GError **error)
if (!fu_device_has_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) {
if (!fu_device_detach (FU_DEVICE (device), error))
return FALSE;
if (!dfu_device_wait_for_replug (device,
if (!dfu_device_wait_for_replug (priv, device,
FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE,
error)) {
return FALSE;
@ -1163,7 +1196,7 @@ dfu_tool_write (DfuToolPrivate *priv, gchar **values, GError **error)
/* do host reset */
if (!fu_device_attach (FU_DEVICE (device), error))
return FALSE;
if (!dfu_device_wait_for_replug (device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error))
if (!dfu_device_wait_for_replug (priv, device, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE, error))
return FALSE;
/* success */

View File

@ -68,49 +68,25 @@ Flags = attach-upload-download
Plugin = dfu
Flags = no-dfu-runtime,needs-bootloader
# Jabra 410
[DeviceInstanceId=USB\VID_0B0E&PID_0412]
Plugin = dfu
Flags = no-dfu-runtime
DfuJabraDetach = 0201
CounterpartGuid = USB\VID_0B0E&PID_0411
# Jabra 510
[DeviceInstanceId=USB\VID_0B0E&PID_0420]
Plugin = dfu
Flags = no-dfu-runtime
DfuJabraDetach = 0201
CounterpartGuid = USB\VID_0B0E&PID_0421
# Jabra 710
[DeviceInstanceId=USB\VID_0B0E&PID_2475]
Plugin = dfu
Flags = no-dfu-runtime
DfuJabraDetach = 0508
CounterpartGuid = USB\VID_0B0E&PID_0982
# Jabra 810
[DeviceInstanceId=USB\VID_0B0E&PID_2456]
Plugin = dfu
Flags = no-dfu-runtime
DfuJabraDetach = 0508
CounterpartGuid = USB\VID_0B0E&PID_0971
# Jabra 410 [appIDLE & dfuIDLE]
[DeviceInstanceId=USB\VID_0B0E&PID_0411]
Plugin = dfu
Flags = no-pid-change,force-dfu-mode,ignore-upload,attach-extra-reset
Flags = no-pid-change,ignore-upload,attach-extra-reset
# Jabra 510 [appIDLE & dfuIDLE]
[DeviceInstanceId=USB\VID_0B0E&PID_0421]
Plugin = dfu
Flags = no-pid-change,force-dfu-mode,ignore-upload,attach-extra-reset
Flags = no-pid-change,ignore-upload,attach-extra-reset
# Jabra 710 [appIDLE & dfuIDLE]
[DeviceInstanceId=USB\VID_0B0E&PID_0982]
Plugin = dfu
Flags = no-pid-change,force-dfu-mode,ignore-upload,attach-extra-reset
Flags = no-pid-change,ignore-upload,attach-extra-reset
# Jabra 810 [appIDLE & dfuIDLE]
[DeviceInstanceId=USB\VID_0B0E&PID_0971]
Plugin = dfu
Flags = no-pid-change,force-dfu-mode,ignore-upload,attach-extra-reset
Flags = no-pid-change,ignore-upload,attach-extra-reset
# Atmel AT90USB Bootloader
[DeviceInstanceId=USB\VID_03EB&PID_2FF7]

23
plugins/jabra/README.md Normal file
View File

@ -0,0 +1,23 @@
Jabra Support
=============
Introduction
------------
This plugin is used to detach the Jabra device to DFU mode.
GUID Generation
---------------
These devices use the standard USB DeviceInstanceId values, e.g.
* `USB\VID_0B0E&PID_0412`
Quirk use
---------
This plugin uses the following plugin-specific quirks:
| Quirk | Description | fwupd version |
|---------------|----------------------------------------------|---------------|
|`JabraMagic` | Two magic bytes sent to detach into DFU mode.|1.3.3 |

View File

@ -0,0 +1,165 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include "fu-firmware-common.h"
#include "fu-jabra-device.h"
struct _FuJabraDevice {
FuUsbDevice parent_instance;
gchar *magic;
};
G_DEFINE_TYPE (FuJabraDevice, fu_jabra_device, FU_TYPE_USB_DEVICE)
static void
fu_jabra_device_to_string (FuDevice *device, guint idt, GString *str)
{
FuJabraDevice *self = FU_JABRA_DEVICE (device);
fu_common_string_append_kv (str, idt, "Magic", self->magic);
}
static guint8
_g_usb_device_get_interface_for_class (GUsbDevice *dev,
guint8 intf_class,
GError **error)
{
g_autoptr(GPtrArray) intfs = NULL;
intfs = g_usb_device_get_interfaces (dev, error);
if (intfs == NULL)
return 0xff;
for (guint i = 0; i < intfs->len; i++) {
GUsbInterface *intf = g_ptr_array_index (intfs, i);
if (g_usb_interface_get_class (intf) == intf_class)
return g_usb_interface_get_number (intf);
}
return 0xff;
}
/* slightly weirdly, this magic turns the device into appIDLE, so we
* need the DFU plugin to further detach us into dfuIDLE */
static gboolean
fu_jabra_device_prepare (FuDevice *device, FwupdInstallFlags flags, GError **error)
{
FuJabraDevice *self = FU_JABRA_DEVICE (device);
GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device));
guint8 adr = 0x00;
guint8 rep = 0x00;
guint8 iface_hid;
guint8 buf[33] = { 0x00 };
g_autoptr(GError) error_local = NULL;
/* parse string and create magic packet */
rep = fu_firmware_strparse_uint8 (self->magic + 0);
adr = fu_firmware_strparse_uint8 (self->magic + 2);
buf[0] = rep;
buf[1] = adr;
buf[2] = 0x00;
buf[3] = 0x01;
buf[4] = 0x85;
buf[5] = 0x07;
/* detach the HID interface from the kernel driver */
iface_hid = _g_usb_device_get_interface_for_class (usb_device,
G_USB_DEVICE_CLASS_HID,
&error_local);
if (iface_hid == 0xff) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"cannot find HID interface: %s",
error_local->message);
return FALSE;
}
g_debug ("claiming interface 0x%02x", iface_hid);
if (!g_usb_device_claim_interface (usb_device, (gint) iface_hid,
G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER,
&error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"cannot claim interface 0x%02x: %s",
iface_hid, error_local->message);
return FALSE;
}
/* send magic to device */
if (!g_usb_device_control_transfer (usb_device,
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
G_USB_DEVICE_REQUEST_TYPE_CLASS,
G_USB_DEVICE_RECIPIENT_INTERFACE,
0x09,
0x0200 | rep,
0x0003,
buf, 33, NULL,
FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE,
NULL, /* cancellable */
&error_local)) {
g_debug ("whilst sending magic: %s, ignoring",
error_local->message);
}
/* wait for device to re-appear and be added to the dfu plugin */
fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART);
fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
return TRUE;
}
static gboolean
fu_jabra_device_set_quirk_kv (FuDevice *device,
const gchar *key,
const gchar *value,
GError **error)
{
FuJabraDevice *self = FU_JABRA_DEVICE (device);
if (g_strcmp0 (key, "JabraMagic") == 0) {
if (value != NULL && strlen (value) == 4) {
self->magic = g_strdup (value);
return TRUE;
}
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"unsupported jabra quirk format");
return FALSE;
}
/* failed */
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"quirk key not supported");
return FALSE;
}
static void
fu_jabra_device_init (FuJabraDevice *self)
{
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_set_remove_delay (FU_DEVICE (self), 20000); /* 10+10s! */
}
static void
fu_jabra_device_finalize (GObject *object)
{
FuJabraDevice *self = FU_JABRA_DEVICE (object);
g_free (self->magic);
G_OBJECT_CLASS (fu_jabra_device_parent_class)->finalize (object);
}
static void
fu_jabra_device_class_init (FuJabraDeviceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
object_class->finalize = fu_jabra_device_finalize;
klass_device->to_string = fu_jabra_device_to_string;
klass_device->prepare = fu_jabra_device_prepare;
klass_device->set_quirk_kv = fu_jabra_device_set_quirk_kv;
}

View File

@ -0,0 +1,12 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include "fu-plugin.h"
#define FU_TYPE_JABRA_DEVICE (fu_jabra_device_get_type ())
G_DECLARE_FINAL_TYPE (FuJabraDevice, fu_jabra_device, FU, JABRA_DEVICE, FuUsbDevice)

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include "fu-plugin-vfuncs.h"
#include "fu-jabra-device.h"
void
fu_plugin_init (FuPlugin *plugin)
{
fu_plugin_set_build_hash (plugin, FU_BUILD_HASH);
fu_plugin_set_device_gtype (plugin, FU_TYPE_JABRA_DEVICE);
}
/* slightly weirdly, this takes us from appIDLE back into the actual
* runtime mode where the device actually works */
gboolean
fu_plugin_update_cleanup (FuPlugin *plugin,
FwupdInstallFlags flags,
FuDevice *device,
GError **error)
{
GUsbDevice *usb_device;
g_autoptr(FuDeviceLocker) locker = NULL;
g_autoptr(GError) error_local = NULL;
/* check for a property on the *dfu* FuDevice, which is also why we
* can't just rely on using FuDevice->cleanup() */
if (!fu_device_has_custom_flag (device, "attach-extra-reset"))
return TRUE;
locker = fu_device_locker_new (device, error);
if (locker == NULL)
return FALSE;
g_debug ("performing extra reset into firmware mode");
usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device));
if (!g_usb_device_reset (usb_device, &error_local)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"cannot reset USB device: %s [%i]",
error_local->message,
error_local->code);
return FALSE;
}
/* wait for device to re-appear */
fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART);
fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
return TRUE;
}

23
plugins/jabra/jabra.quirk Normal file
View File

@ -0,0 +1,23 @@
# Jabra 410 [runtime]
[DeviceInstanceId=USB\VID_0B0E&PID_0412]
Plugin = jabra
JabraMagic = 0201
CounterpartGuid = USB\VID_0B0E&PID_0411
# Jabra 510 [runtime]
[DeviceInstanceId=USB\VID_0B0E&PID_0420]
Plugin = jabra
JabraMagic = 0201
CounterpartGuid = USB\VID_0B0E&PID_0421
# Jabra 710 [runtime]
[DeviceInstanceId=USB\VID_0B0E&PID_2475]
Plugin = jabra
JabraMagic = 0508
CounterpartGuid = USB\VID_0B0E&PID_0982
# Jabra 810 [runtime]
[DeviceInstanceId=USB\VID_0B0E&PID_2456]
Plugin = jabra
JabraMagic = 0508
CounterpartGuid = USB\VID_0B0E&PID_0971

27
plugins/jabra/meson.build Normal file
View File

@ -0,0 +1,27 @@
cargs = ['-DG_LOG_DOMAIN="FuPluginJabra"']
install_data(['jabra.quirk'],
install_dir: join_paths(datadir, 'fwupd', 'quirks.d')
)
shared_module('fu_plugin_jabra',
fu_hash,
sources : [
'fu-plugin-jabra.c',
'fu-jabra-device.c',
],
include_directories : [
include_directories('../..'),
include_directories('../../src'),
include_directories('../../libfwupd'),
],
install : true,
install_dir: plugin_dir,
link_with : [
libfwupdprivate,
],
c_args : cargs,
dependencies : [
plugin_deps,
],
)

View File

@ -4,6 +4,7 @@ subdir('colorhug')
subdir('coreboot')
subdir('ebitdo')
subdir('fastboot')
subdir('jabra')
subdir('steelseries')
subdir('dell-dock')
subdir('nitrokey')