diff --git a/libdfu/dfu-device-private.h b/libdfu/dfu-device-private.h index f6efbbfc8..1f89c9d99 100644 --- a/libdfu/dfu-device-private.h +++ b/libdfu/dfu-device-private.h @@ -41,6 +41,7 @@ G_BEGIN_DECLS * @DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO: Fix up the protocol number * @DFU_DEVICE_QUIRK_NO_PID_CHANGE: Accept the same VID:PID when changing modes * @DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD: Do not do GetStatus when uploading + * @DFU_DEVICE_QUIRK_NO_DFU_RUNTIME: No DFU runtime interface is provided * * The workarounds for different devices. **/ @@ -52,6 +53,7 @@ typedef enum { DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO = (1 << 3), DFU_DEVICE_QUIRK_NO_PID_CHANGE = (1 << 4), DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD = (1 << 5), + DFU_DEVICE_QUIRK_NO_DFU_RUNTIME = (1 << 6), /*< private >*/ DFU_DEVICE_QUIRK_LAST } DfuDeviceQuirks; @@ -90,6 +92,7 @@ void dfu_device_error_fixup (DfuDevice *device, GCancellable *cancellable, GError **error); guint dfu_device_get_download_timeout (DfuDevice *device); +gchar *dfu_device_get_quirks_as_string (DfuDevice *device); gboolean dfu_device_set_new_usb_dev (DfuDevice *device, GUsbDevice *dev, GCancellable *cancellable, diff --git a/libdfu/dfu-device.c b/libdfu/dfu-device.c index 6306d2002..1e05edf9c 100644 --- a/libdfu/dfu-device.c +++ b/libdfu/dfu-device.c @@ -150,6 +150,7 @@ static void dfu_device_init (DfuDevice *device) { DfuDevicePrivate *priv = GET_PRIVATE (device); + priv->iface_number = 0xff; priv->runtime_pid = 0xffff; priv->runtime_vid = 0xffff; priv->runtime_release = 0xffff; @@ -384,6 +385,16 @@ dfu_device_add_targets (DfuDevice *device) if (g_bytes_get_size (iface_data) > 0) dfu_device_parse_iface_data (device, iface_data); } + + /* the device has no DFU runtime, so cheat */ + if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { + if (priv->targets->len == 0) { + g_debug ("no DFU runtime, so faking device"); + priv->iface_number = 0xff; + } + return TRUE; + } + return priv->targets->len > 0; } @@ -586,6 +597,7 @@ dfu_target_set_quirks (DfuDevice *device) pid >= 0x5117 && pid <= 0x5126) priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT | DFU_DEVICE_QUIRK_NO_PID_CHANGE | + DFU_DEVICE_QUIRK_NO_DFU_RUNTIME | DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD; /* OpenPCD Reader */ @@ -631,11 +643,16 @@ dfu_device_new (GUsbDevice *dev) priv = GET_PRIVATE (device); priv->dev = g_object_ref (dev); priv->platform_id = g_strdup (g_usb_device_get_platform_id (dev)); + + /* set any quirks on the device before adding targets */ + dfu_target_set_quirks (device); + + /* add each alternate interface, although typically there will + * be only one */ if (!dfu_device_add_targets (device)) { g_object_unref (device); return NULL; } - dfu_target_set_quirks (device); return device; } @@ -905,6 +922,15 @@ dfu_device_refresh (DfuDevice *device, GCancellable *cancellable, GError **error return FALSE; } + /* the device has no DFU runtime, so cheat */ + if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { + g_set_error_literal (error, + DFU_ERROR, + DFU_ERROR_NOT_SUPPORTED, + "not supported as no DFU runtime"); + return FALSE; + } + if (!g_usb_device_control_transfer (priv->dev, G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, G_USB_DEVICE_REQUEST_TYPE_CLASS, @@ -978,6 +1004,15 @@ dfu_device_detach (DfuDevice *device, GCancellable *cancellable, GError **error) return FALSE; } + /* the device has no DFU runtime, so cheat */ + if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { + g_set_error_literal (error, + DFU_ERROR, + DFU_ERROR_NOT_SUPPORTED, + "not supported as no DFU runtime"); + return FALSE; + } + /* inform UI there's going to be a detach:attach */ dfu_device_set_state (device, DFU_STATE_APP_DETACH); @@ -1042,6 +1077,15 @@ dfu_device_abort (DfuDevice *device, GCancellable *cancellable, GError **error) return FALSE; } + /* the device has no DFU runtime, so cheat */ + if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { + g_set_error_literal (error, + DFU_ERROR, + DFU_ERROR_NOT_SUPPORTED, + "not supported as no DFU runtime"); + return FALSE; + } + if (!g_usb_device_control_transfer (priv->dev, G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, G_USB_DEVICE_REQUEST_TYPE_CLASS, @@ -1097,6 +1141,15 @@ dfu_device_clear_status (DfuDevice *device, GCancellable *cancellable, GError ** return FALSE; } + /* the device has no DFU runtime, so cheat */ + if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { + g_set_error_literal (error, + DFU_ERROR, + DFU_ERROR_NOT_SUPPORTED, + "not supported as no DFU runtime"); + return FALSE; + } + if (!g_usb_device_control_transfer (priv->dev, G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, G_USB_DEVICE_REQUEST_TYPE_CLASS, @@ -1196,14 +1249,19 @@ dfu_device_open (DfuDevice *device, DfuDeviceOpenFlags flags, return FALSE; } - /* claim the correct interface */ - if (!g_usb_device_claim_interface (priv->dev, (gint) priv->iface_number, 0, &error_local)) { - g_set_error (error, - DFU_ERROR, - DFU_ERROR_INVALID_DEVICE, - "cannot claim interface %i: %s", - priv->iface_number, error_local->message); - return FALSE; + /* claim the correct interface if set */ + if (priv->iface_number != 0xff) { + if (!g_usb_device_claim_interface (priv->dev, + (gint) priv->iface_number, + 0, + &error_local)) { + g_set_error (error, + DFU_ERROR, + DFU_ERROR_INVALID_DEVICE, + "cannot claim interface %i: %s", + priv->iface_number, error_local->message); + return FALSE; + } } /* get product name if it exists */ @@ -1211,6 +1269,13 @@ dfu_device_open (DfuDevice *device, DfuDeviceOpenFlags flags, if (idx != 0x00) priv->display_name = g_usb_device_get_string_descriptor (priv->dev, idx, NULL); + /* the device has no DFU runtime, so cheat */ + if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) { + priv->state = DFU_STATE_APP_IDLE; + priv->status = DFU_STATUS_OK; + flags |= DFU_DEVICE_OPEN_FLAG_NO_AUTO_REFRESH; + } + /* automatically abort any uploads or downloads */ if ((flags & DFU_DEVICE_OPEN_FLAG_NO_AUTO_REFRESH) == 0) { if (!dfu_device_refresh (device, cancellable, error)) @@ -1825,3 +1890,47 @@ dfu_device_error_fixup (DfuDevice *device, break; } } + +/** + * dfu_device_get_quirks_as_string: (skip) + * @device: a #DfuDevice + * + * Gets a string describing the quirks set for a device. + * + * Return value: string, or %NULL for no quirks + * + * Since: 0.5.4 + **/ +gchar * +dfu_device_get_quirks_as_string (DfuDevice *device) +{ + DfuDevicePrivate *priv = GET_PRIVATE (device); + GString *str; + + /* just append to a string */ + str = g_string_new (""); + if (priv->quirks & DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT) + g_string_append_printf (str, "ignore-polltimeout|"); + if (priv->quirks & DFU_DEVICE_QUIRK_FORCE_DFU_MODE) + g_string_append_printf (str, "force-dfu-mode|"); + if (priv->quirks & DFU_DEVICE_QUIRK_IGNORE_INVALID_VERSION) + g_string_append_printf (str, "ignore-invalid-version|"); + if (priv->quirks & DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO) + g_string_append_printf (str, "use-protocol-zero|"); + if (priv->quirks & DFU_DEVICE_QUIRK_NO_PID_CHANGE) + g_string_append_printf (str, "no-pid-change|"); + if (priv->quirks & DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD) + g_string_append_printf (str, "no-get-status-upload|"); + if (priv->quirks & DFU_DEVICE_QUIRK_NO_DFU_RUNTIME) + g_string_append_printf (str, "no-dfu-runtime|"); + + /* a well behaved device */ + if (str->len == 0) { + g_string_free (str, TRUE); + return NULL; + } + + /* remove trailing pipe */ + g_string_truncate (str, str->len - 1); + return g_string_free (str, FALSE); +} diff --git a/libdfu/dfu-tool.c b/libdfu/dfu-tool.c index b6b2f6355..80155b9ed 100644 --- a/libdfu/dfu-tool.c +++ b/libdfu/dfu-tool.c @@ -1423,6 +1423,7 @@ dfu_tool_list (DfuToolPrivate *priv, gchar **values, GError **error) GPtrArray *dfu_targets; const gchar *tmp; guint j; + g_autofree gchar *quirks = NULL; g_autofree gchar *version = NULL; g_autoptr(GError) error_local = NULL; @@ -1467,6 +1468,14 @@ dfu_tool_list (DfuToolPrivate *priv, gchar **values, GError **error) /* TRANSLATORS: device state, i.e. appIDLE */ dfu_tool_print_indent (_("State"), tmp, 1); + /* quirks are NULL if none are set */ + quirks = dfu_device_get_quirks_as_string (device); + if (quirks != NULL) { + /* TRANSLATORS: device quirks, i.e. things that + * it does that we have to work around */ + dfu_tool_print_indent (_("Quirks"), quirks, 1); + } + /* list targets */ dfu_targets = dfu_device_get_targets (device); for (j = 0; j < dfu_targets->len; j++) {