dfu: Support polling the status from device in dfuManifest state

Some devices may accumulate the firmware image and perform the
entire reprogramming operation at one time. In this case, the
device enters dfuMANIFEST-SYNC or dfuMANIFEST state after
dfuDNLOAD-IDLE.

The fwupd shall be able to poll the status from the device via
DFU_GETSTATUS until the device completes the reprogramming or
reports an error.

For details, please refer to Section 7. Manifestation Phase and
A.1 Interface State Transition Diagram in the USB DFU protocol.
https://www.usb.org/sites/default/files/DFU_1.1.pdf

For not affecting the other DFU capable devices, introduce a quirk
"manifest-poll" to limit the logic.
This commit is contained in:
Jerry Zhang 2020-08-31 12:33:24 -05:00 committed by Richard Hughes
parent 7286951771
commit 409a2c958c
3 changed files with 88 additions and 0 deletions

View File

@ -44,6 +44,7 @@
* * `legacy-protocol`: Use a legacy protocol version
* * `detach-for-attach`: Requires a DFU_REQUEST_DETACH to attach
* * `absent-sector-size`: In absence of sector size, assume byte
* * `manifest-poll`: Requires polling via GetStatus in dfuManifest state
*
* Default value: `none`
*

View File

@ -30,6 +30,8 @@
#include "fwupd-error.h"
#define DFU_TARGET_MANIFEST_MAX_POLLING_TRIES 200
static void dfu_target_finalize (GObject *object);
typedef struct {
@ -472,6 +474,46 @@ dfu_target_status_to_error_msg (DfuStatus status)
return NULL;
}
static gboolean
dfu_target_manifest_wait (DfuTarget *target, GError **error)
{
DfuTargetPrivate *priv = GET_PRIVATE (target);
guint polling_count = 0;
/* get the status */
if (!dfu_device_refresh (priv->device, error))
return FALSE;
/* wait for DFU_STATE_DFU_MANIFEST to not be set */
while (dfu_device_get_state (priv->device) == DFU_STATE_DFU_MANIFEST_SYNC ||
dfu_device_get_state (priv->device) == DFU_STATE_DFU_MANIFEST) {
g_debug ("waiting for DFU_STATE_DFU_MANIFEST to clear");
if (polling_count++ > DFU_TARGET_MANIFEST_MAX_POLLING_TRIES) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"reach to max polling tries");
return FALSE;
}
g_usleep ((dfu_device_get_download_timeout (priv->device) + 1000) * 1000);
if (!dfu_device_refresh (priv->device, error))
return FALSE;
}
/* in an error state */
if (dfu_device_get_state (priv->device) == DFU_STATE_DFU_ERROR) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
dfu_target_status_to_error_msg (dfu_device_get_status (priv->device)));
return FALSE;
}
return TRUE;
}
gboolean
dfu_target_check_status (DfuTarget *target, GError **error)
{
@ -1285,6 +1327,11 @@ dfu_target_download (DfuTarget *target, DfuImage *image,
return FALSE;
}
if (fu_device_has_custom_flag (FU_DEVICE (dfu_target_get_device (target)), "manifest-poll") &&
dfu_device_has_attribute (priv->device, DFU_DEVICE_ATTRIBUTE_MANIFEST_TOL))
if (!dfu_target_manifest_wait (target, error))
return FALSE;
/* success */
return TRUE;
}

View File

@ -329,3 +329,43 @@ Flags = absent-sector-size
Plugin = dfu
DfuForceVersion = 011a
DfuForceTimeout = 5000
# Poly Studio
[DeviceInstanceId=USB\VID_095D&PID_9217]
Plugin = dfu
Flags = manifest-poll
[DeviceInstanceId=USB\VID_095D&PID_9218]
Plugin = dfu
Flags = manifest-poll
# Poly Eagle Eye Cube
[DeviceInstanceId=USB\VID_095D&PID_9212]
Plugin = dfu
Flags = manifest-poll
[DeviceInstanceId=USB\VID_095D&PID_9213]
Plugin = dfu
Flags = manifest-poll
# Poly P30
[DeviceInstanceId=USB\VID_095D&PID_9290]
Plugin = dfu
Flags = manifest-poll
[DeviceInstanceId=USB\VID_095D&PID_9291]
Plugin = dfu
Flags = manifest-poll
# Poly ULCC
[DeviceInstanceId=USB\VID_095D&PID_9160]
Plugin = dfu
Flags = manifest-poll
[DeviceInstanceId=USB\VID_095D&PID_927B]
Plugin = dfu
Flags = manifest-poll
# Poly Eagle Eye Mini
[DeviceInstanceId=USB\VID_095D&PID_3001]
Plugin = dfu
Flags = manifest-poll
[DeviceInstanceId=USB\VID_095D&PID_3002]
Plugin = dfu
Flags = manifest-poll