mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-06 16:43:15 +00:00
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:
parent
2031ce3bf6
commit
6bbc4c787b
@ -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
|
||||
|
@ -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|
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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
23
plugins/jabra/README.md
Normal 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 |
|
165
plugins/jabra/fu-jabra-device.c
Normal file
165
plugins/jabra/fu-jabra-device.c
Normal 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;
|
||||
}
|
12
plugins/jabra/fu-jabra-device.h
Normal file
12
plugins/jabra/fu-jabra-device.h
Normal 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)
|
55
plugins/jabra/fu-plugin-jabra.c
Normal file
55
plugins/jabra/fu-plugin-jabra.c
Normal 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
23
plugins/jabra/jabra.quirk
Normal 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
27
plugins/jabra/meson.build
Normal 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,
|
||||
],
|
||||
)
|
@ -4,6 +4,7 @@ subdir('colorhug')
|
||||
subdir('coreboot')
|
||||
subdir('ebitdo')
|
||||
subdir('fastboot')
|
||||
subdir('jabra')
|
||||
subdir('steelseries')
|
||||
subdir('dell-dock')
|
||||
subdir('nitrokey')
|
||||
|
Loading…
Reference in New Issue
Block a user