U-Boot DFU: The Flush Callback Is Not a Successful-Commit Notification
A board hook named dfu_flush_callback() can run even
when the medium’s flush operation has returned an error. In U-Boot
v2025.01, that callback marks a point in the DFU transaction lifecycle;
its invocation alone does not prove that the downloaded image was
committed successfully.
If board code uses the hook to mark an update usable, trace the
return value of dfu_flush() separately. The distinction
matters especially when transfer progress and USB status handling finish
before a deferred storage operation reports its outcome.
Follow the two
different failure branches
The DFU
core implements dfu_flush() in a short but
consequential sequence. First it drains the write buffer. A failure at
that stage returns immediately, before the flush callback and the final
transaction cleanup in this function.
After a successful drain, the function calls the medium’s
flush_medium operation when one is provided and retains its
return value. It then optionally prints the accumulated hash result,
invokes dfu_flush_callback(), cleans up the transaction and
returns the retained result.
There is no success-only conditional around that callback after the
medium operation. A nonzero medium-flush result therefore still reaches
both the callback and cleanup. The hook receives the entity pointer, not
a separate success parameter. A hook implementation cannot derive the
retained local return value merely from having been called.
This also means absence of the hook and presence of the hook are not
symmetric status signals. A drain failure exits early, whereas a later
medium failure can still invoke it. Do not reduce both paths to a single
callback-fired flag.

A
completion-looking message has the same boundary
When a hash algorithm is enabled, the core prints its
completion-looking hash line after the medium flush call, without
conditioning that print on a zero return value. That line identifies the
accumulated transfer hash result; it does not override the medium’s
error.
Likewise, transaction cleanup describes resource and state handling,
not acceptance of the new firmware. A board-specific update policy needs
an explicit success decision at a layer that has the actual result.
Image format validation, authenticity and boot suitability remain
separate requirements beyond successful storage operations.
Understand why USB
flushing is deferred
The DFU
gadget state machine schedules the flush through a
request-completion callback during manifestation handling.
dnload_request_flush() records the entity for deferred work
rather than performing the medium operation itself.
In the USB
download loop, the deferred path services gadget interrupts before
calling dfu_flush(). The code explains that this permits
the host’s zero-length status transaction to complete. It then clears
the deferred entity and checks the returned flush result; a nonzero
result is logged and exits that loop.
This ordering explains why a USB-level milestone and a device-side
storage outcome must be recorded separately. It does not establish
exactly what every host version displays for every late failure. That
requires a trace of the chosen gadget, backend and host interaction,
rather than inference from a progress indicator.
Put the
acceptance decision where the result exists
Review any board override of the flush callback for actions such as
setting an update-ready flag or selecting a new image. If an action
requires successful storage, callback occurrence is insufficient
evidence under this implementation. Preserve the actual medium result
through the caller’s decision path.
A proposed fault-injection fixture should distinguish buffer-drain
failure, medium-flush failure and complete success. For each, capture
callback occurrence, the returned result and any persistent acceptance
marker. No DFU transfer, failure injection or media write was performed
here. The intended outcome is a policy that cannot mistake lifecycle
cleanup for a committed update.