libdfu: Add a quirk to support DFU runtime-less devices

This commit is contained in:
Richard Hughes 2015-11-26 18:59:59 +00:00
parent 60b7eeeca9
commit 385bba0ada
3 changed files with 130 additions and 9 deletions

View File

@ -41,6 +41,7 @@ G_BEGIN_DECLS
* @DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO: Fix up the protocol number * @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_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_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. * The workarounds for different devices.
**/ **/
@ -52,6 +53,7 @@ typedef enum {
DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO = (1 << 3), DFU_DEVICE_QUIRK_USE_PROTOCOL_ZERO = (1 << 3),
DFU_DEVICE_QUIRK_NO_PID_CHANGE = (1 << 4), DFU_DEVICE_QUIRK_NO_PID_CHANGE = (1 << 4),
DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD = (1 << 5), DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD = (1 << 5),
DFU_DEVICE_QUIRK_NO_DFU_RUNTIME = (1 << 6),
/*< private >*/ /*< private >*/
DFU_DEVICE_QUIRK_LAST DFU_DEVICE_QUIRK_LAST
} DfuDeviceQuirks; } DfuDeviceQuirks;
@ -90,6 +92,7 @@ void dfu_device_error_fixup (DfuDevice *device,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
guint dfu_device_get_download_timeout (DfuDevice *device); 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, gboolean dfu_device_set_new_usb_dev (DfuDevice *device,
GUsbDevice *dev, GUsbDevice *dev,
GCancellable *cancellable, GCancellable *cancellable,

View File

@ -150,6 +150,7 @@ static void
dfu_device_init (DfuDevice *device) dfu_device_init (DfuDevice *device)
{ {
DfuDevicePrivate *priv = GET_PRIVATE (device); DfuDevicePrivate *priv = GET_PRIVATE (device);
priv->iface_number = 0xff;
priv->runtime_pid = 0xffff; priv->runtime_pid = 0xffff;
priv->runtime_vid = 0xffff; priv->runtime_vid = 0xffff;
priv->runtime_release = 0xffff; priv->runtime_release = 0xffff;
@ -384,6 +385,16 @@ dfu_device_add_targets (DfuDevice *device)
if (g_bytes_get_size (iface_data) > 0) if (g_bytes_get_size (iface_data) > 0)
dfu_device_parse_iface_data (device, iface_data); 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; return priv->targets->len > 0;
} }
@ -586,6 +597,7 @@ dfu_target_set_quirks (DfuDevice *device)
pid >= 0x5117 && pid <= 0x5126) pid >= 0x5117 && pid <= 0x5126)
priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT | priv->quirks |= DFU_DEVICE_QUIRK_IGNORE_POLLTIMEOUT |
DFU_DEVICE_QUIRK_NO_PID_CHANGE | DFU_DEVICE_QUIRK_NO_PID_CHANGE |
DFU_DEVICE_QUIRK_NO_DFU_RUNTIME |
DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD; DFU_DEVICE_QUIRK_NO_GET_STATUS_UPLOAD;
/* OpenPCD Reader */ /* OpenPCD Reader */
@ -631,11 +643,16 @@ dfu_device_new (GUsbDevice *dev)
priv = GET_PRIVATE (device); priv = GET_PRIVATE (device);
priv->dev = g_object_ref (dev); priv->dev = g_object_ref (dev);
priv->platform_id = g_strdup (g_usb_device_get_platform_id (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)) { if (!dfu_device_add_targets (device)) {
g_object_unref (device); g_object_unref (device);
return NULL; return NULL;
} }
dfu_target_set_quirks (device);
return device; return device;
} }
@ -905,6 +922,15 @@ dfu_device_refresh (DfuDevice *device, GCancellable *cancellable, GError **error
return FALSE; 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, if (!g_usb_device_control_transfer (priv->dev,
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
G_USB_DEVICE_REQUEST_TYPE_CLASS, G_USB_DEVICE_REQUEST_TYPE_CLASS,
@ -978,6 +1004,15 @@ dfu_device_detach (DfuDevice *device, GCancellable *cancellable, GError **error)
return FALSE; 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 */ /* inform UI there's going to be a detach:attach */
dfu_device_set_state (device, DFU_STATE_APP_DETACH); dfu_device_set_state (device, DFU_STATE_APP_DETACH);
@ -1042,6 +1077,15 @@ dfu_device_abort (DfuDevice *device, GCancellable *cancellable, GError **error)
return FALSE; 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, if (!g_usb_device_control_transfer (priv->dev,
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
G_USB_DEVICE_REQUEST_TYPE_CLASS, G_USB_DEVICE_REQUEST_TYPE_CLASS,
@ -1097,6 +1141,15 @@ dfu_device_clear_status (DfuDevice *device, GCancellable *cancellable, GError **
return FALSE; 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, if (!g_usb_device_control_transfer (priv->dev,
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
G_USB_DEVICE_REQUEST_TYPE_CLASS, G_USB_DEVICE_REQUEST_TYPE_CLASS,
@ -1196,14 +1249,19 @@ dfu_device_open (DfuDevice *device, DfuDeviceOpenFlags flags,
return FALSE; return FALSE;
} }
/* claim the correct interface */ /* claim the correct interface if set */
if (!g_usb_device_claim_interface (priv->dev, (gint) priv->iface_number, 0, &error_local)) { if (priv->iface_number != 0xff) {
g_set_error (error, if (!g_usb_device_claim_interface (priv->dev,
DFU_ERROR, (gint) priv->iface_number,
DFU_ERROR_INVALID_DEVICE, 0,
"cannot claim interface %i: %s", &error_local)) {
priv->iface_number, error_local->message); g_set_error (error,
return FALSE; 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 */ /* get product name if it exists */
@ -1211,6 +1269,13 @@ dfu_device_open (DfuDevice *device, DfuDeviceOpenFlags flags,
if (idx != 0x00) if (idx != 0x00)
priv->display_name = g_usb_device_get_string_descriptor (priv->dev, idx, NULL); 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 */ /* automatically abort any uploads or downloads */
if ((flags & DFU_DEVICE_OPEN_FLAG_NO_AUTO_REFRESH) == 0) { if ((flags & DFU_DEVICE_OPEN_FLAG_NO_AUTO_REFRESH) == 0) {
if (!dfu_device_refresh (device, cancellable, error)) if (!dfu_device_refresh (device, cancellable, error))
@ -1825,3 +1890,47 @@ dfu_device_error_fixup (DfuDevice *device,
break; 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);
}

View File

@ -1423,6 +1423,7 @@ dfu_tool_list (DfuToolPrivate *priv, gchar **values, GError **error)
GPtrArray *dfu_targets; GPtrArray *dfu_targets;
const gchar *tmp; const gchar *tmp;
guint j; guint j;
g_autofree gchar *quirks = NULL;
g_autofree gchar *version = NULL; g_autofree gchar *version = NULL;
g_autoptr(GError) error_local = 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 */ /* TRANSLATORS: device state, i.e. appIDLE */
dfu_tool_print_indent (_("State"), tmp, 1); 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 */ /* list targets */
dfu_targets = dfu_device_get_targets (device); dfu_targets = dfu_device_get_targets (device);
for (j = 0; j < dfu_targets->len; j++) { for (j = 0; j < dfu_targets->len; j++) {