dfu: Support devices with truncated DFU interface data

We can assume they support DFU 1.1. Fixes flashing the Jabra Speak 410.
This commit is contained in:
Richard Hughes 2017-10-27 13:51:30 +01:00
parent 54c7c0cc9f
commit f3e27065e6

View File

@ -271,26 +271,38 @@ static void
dfu_device_parse_iface_data (DfuDevice *device, GBytes *iface_data) dfu_device_parse_iface_data (DfuDevice *device, GBytes *iface_data)
{ {
DfuDevicePrivate *priv = GET_PRIVATE (device); DfuDevicePrivate *priv = GET_PRIVATE (device);
const DfuFuncDescriptor *desc; DfuFuncDescriptor desc;
gsize iface_data_length; const guint8 *buf;
gsize sz;
/* parse the functional descriptor */ /* parse the functional descriptor */
desc = g_bytes_get_data (iface_data, &iface_data_length); buf = g_bytes_get_data (iface_data, &sz);
if (iface_data_length != 0x09) { if (sz == sizeof(DfuFuncDescriptor)) {
memcpy (&desc, buf, sz);
} else if (sz == sizeof(DfuFuncDescriptor) - 2) {
g_warning ("truncated DFU interface data, no bcdDFUVersion");
memcpy (&desc, buf, sz);
desc.bcdDFUVersion = DFU_VERSION_DFU_1_1;
} else {
g_autoptr(GString) bufstr = g_string_new (NULL);
for (gsize i = 0; i < sz; i++)
g_string_append_printf (bufstr, "%02x ", buf[i]);
if (bufstr->len > 0)
g_string_truncate (bufstr, bufstr->len - 1);
g_warning ("interface found, but not the correct length for " g_warning ("interface found, but not the correct length for "
"functional data: %" G_GSIZE_FORMAT " bytes", "functional data: %" G_GSIZE_FORMAT " bytes: %s",
iface_data_length); sz, bufstr->str);
return; return;
} }
/* check sanity */ /* check sanity */
if (desc->bLength != 0x09) { if (desc.bLength != sz) {
g_warning ("DFU interface data has incorrect length: 0x%02x", g_warning ("DFU interface data has incorrect length: 0x%02x",
desc->bLength); desc.bLength);
} }
/* check transfer size */ /* check transfer size */
priv->transfer_size = GUINT16_FROM_LE (desc->wTransferSize); priv->transfer_size = GUINT16_FROM_LE (desc.wTransferSize);
if (priv->transfer_size == 0xffff) { if (priv->transfer_size == 0xffff) {
priv->transfer_size = 0x0400; priv->transfer_size = 0x0400;
g_debug ("DFU transfer size unspecified, guessing"); g_debug ("DFU transfer size unspecified, guessing");
@ -299,12 +311,12 @@ dfu_device_parse_iface_data (DfuDevice *device, GBytes *iface_data)
g_debug ("using DFU transfer size 0x%04x bytes", priv->transfer_size); g_debug ("using DFU transfer size 0x%04x bytes", priv->transfer_size);
} else { } else {
g_warning ("DFU transfer size 0x%04x invalid, using default", g_warning ("DFU transfer size 0x%04x invalid, using default",
desc->wTransferSize); desc.wTransferSize);
priv->transfer_size = 64; priv->transfer_size = 64;
} }
/* check DFU version */ /* check DFU version */
priv->version = GUINT16_FROM_LE (desc->bcdDFUVersion); priv->version = GUINT16_FROM_LE (desc.bcdDFUVersion);
if (priv->quirks & DFU_DEVICE_QUIRK_IGNORE_INVALID_VERSION) { if (priv->quirks & DFU_DEVICE_QUIRK_IGNORE_INVALID_VERSION) {
g_debug ("ignoring quirked DFU version"); g_debug ("ignoring quirked DFU version");
} else { } else {
@ -324,11 +336,11 @@ dfu_device_parse_iface_data (DfuDevice *device, GBytes *iface_data)
/* ST-specific */ /* ST-specific */
if (priv->version == DFU_VERSION_DFUSE && if (priv->version == DFU_VERSION_DFUSE &&
desc->bmAttributes & DFU_DEVICE_ATTRIBUTE_CAN_ACCELERATE) desc.bmAttributes & DFU_DEVICE_ATTRIBUTE_CAN_ACCELERATE)
priv->transfer_size = 0x1000; priv->transfer_size = 0x1000;
/* get attributes about the DFU operation */ /* get attributes about the DFU operation */
priv->attributes = desc->bmAttributes; priv->attributes = desc.bmAttributes;
} }
static gboolean static gboolean