[U-Boot] [PATCH 1/2] dfu: usb: f_dfu: Set deferred call for dfu_flush() function
Heiko Schocher
hs at denx.de
Fri Jan 29 15:12:05 CET 2016
Hello Lukasz,
Am 28.01.2016 um 17:46 schrieb Lukasz Majewski:
> This patch fixes situation when one would like to write large file into
> medium with the file system (fat, ext4, etc).
> This change sets file size limitation to the DFU internal buffer size.
>
> Since u-boot is not supporting interrupts and seek on file systems, it
> becomes challenging to store large file appropriately.
>
> To reproduce this error - create large file (around 26 MiB) and sent it
> to the target board.
>
> Lets examine the flow of USB transactions:
>
> 0. DFU uses EP0 with 64B MPS [Max Packet Size]
>
> 1. Send file - OUT (PC->target) - dat_26MiB.img is sent with 4096 B transactions
>
> 2. Get status - OUT (PC->target) - wait for DFU_STATE_dfuDNLOAD_IDLE (0x05) sent
> from target board - IN transaction
> (target->PC)
>
> 3. The whole file content is sent to target - OUT (PC->target) with ZLP [Zero
> Length Packet]
>
> Now the interesting part starts:
>
> 4. OUT (PC->target) Setup transaction (request to share DFU state)
>
> 5. IN (target->PC) - reply the current DFU state
> - In the UDC driver the req->completion (with dfu_flush) is called
> after successful IN transfer.
> - The dfu_flush() (called from req->completion callback) saves the
> whole file at once (u-boot doesn't support seek on fs).
> Such operation takes considerable time. When the file
> is large - e.g. 26MiB - this time may be more than 5 seconds.
>
> 6. OUT (PC->target) - ZLP, is send in the same time when dfu_flush()
> writes data to eMMC memory.
> The dfu-util application has hard coded timeout on USB transaction
> completion set to 5 seconds (it uses libusb calls).
>
> When the file to store is large (e.g. 26 MiB) the time needed to write it
> may excess the dfu-util timeout and following error message will be displayed:
> "unable to read DFU status" on the HOST PC console.
>
> This change is supposed to leverage DFU's part responsible for storing files
> on file systems. Other DFU operations - i.e. raw/partition write to NAND and
> eMMC should work as before.
>
> The only functional change is the error reporting. When dfu_flush() fails
> the u-boot prompt will exit with error information and dfu-util application
> exits afterwards as well.
>
> Test HW:
> - Odroid XU3 (Exynos5433) - test with large file
> - Trats (Exynos4210) - test for regression - eMMC, raw,
>
> Signed-off-by: Lukasz Majewski <l.majewski at samsung.com>
> Reported-by: Alex Gdalevich <agdalevich at axion-biosystems.com>
> ---
> common/cmd_dfu.c | 20 ++++++++++++++++++++
> drivers/usb/gadget/f_dfu.c | 11 +++--------
> include/dfu.h | 25 +++++++++++++++++++++++++
> 3 files changed, 48 insertions(+), 8 deletions(-)
Tested on the dxr2 board with an etamin module containing a 4GiB DDP nand
with a download size from 400MiB ... worked.
(This test is not yet in my nightly builds ...)
Tested-by: Heiko Schocher <hs at denx.de>
bye,
Heiko
BTW:
I used not the "dfu_nand" module, instead I am just developing
a "dfu_mtd" for accessing mtd partitions. So mtd partitions with
underlying mtd concatenated nand devices for example can be programmed
with dfu ... seems first version works now ...
>
> diff --git a/common/cmd_dfu.c b/common/cmd_dfu.c
> index 6d95ce9..d8aae26 100644
> --- a/common/cmd_dfu.c
> +++ b/common/cmd_dfu.c
> @@ -79,6 +79,26 @@ static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> if (ctrlc())
> goto exit;
>
> + if (dfu_get_defer_flush()) {
> + /*
> + * Call to usb_gadget_handle_interrupts() is necessary
> + * to act on ZLP OUT transaction from HOST PC after
> + * transmitting the whole file.
> + *
> + * If this ZLP OUT packet is NAK'ed, the HOST libusb
> + * function fails after timeout (by default it is set to
> + * 5 seconds). In such situation the dfu-util program
> + * exits with error message.
> + */
> + usb_gadget_handle_interrupts(controller_index);
> + ret = dfu_flush(dfu_get_defer_flush(), NULL, 0, 0);
> + dfu_set_defer_flush(NULL);
> + if (ret) {
> + error("Deferred dfu_flush() failed!");
> + goto exit;
> + }
> + }
> +
> WATCHDOG_RESET();
> usb_gadget_handle_interrupts(controller_index);
> }
> diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c
> index 77a1567..7d88008 100644
> --- a/drivers/usb/gadget/f_dfu.c
> +++ b/drivers/usb/gadget/f_dfu.c
> @@ -44,6 +44,8 @@ struct f_dfu {
> unsigned int poll_timeout;
> };
>
> +struct dfu_entity *dfu_defer_flush;
> +
> typedef int (*dfu_state_fn) (struct f_dfu *,
> const struct usb_ctrlrequest *,
> struct usb_gadget *,
> @@ -167,14 +169,7 @@ static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req)
> static void dnload_request_flush(struct usb_ep *ep, struct usb_request *req)
> {
> struct f_dfu *f_dfu = req->context;
> - int ret;
> -
> - ret = dfu_flush(dfu_get_entity(f_dfu->altsetting), req->buf,
> - req->length, f_dfu->blk_seq_num);
> - if (ret) {
> - f_dfu->dfu_status = DFU_STATUS_errUNKNOWN;
> - f_dfu->dfu_state = DFU_STATE_dfuERROR;
> - }
> + dfu_set_defer_flush(dfu_get_entity(f_dfu->altsetting));
> }
>
> static inline int dfu_get_manifest_timeout(struct dfu_entity *dfu)
> diff --git a/include/dfu.h b/include/dfu.h
> index 6118dc2..f39d3f1 100644
> --- a/include/dfu.h
> +++ b/include/dfu.h
> @@ -163,6 +163,31 @@ int dfu_read(struct dfu_entity *de, void *buf, int size, int blk_seq_num);
> int dfu_write(struct dfu_entity *de, void *buf, int size, int blk_seq_num);
> int dfu_flush(struct dfu_entity *de, void *buf, int size, int blk_seq_num);
>
> +/*
> + * dfu_defer_flush - pointer to store dfu_entity for deferred flashing.
> + * It should be NULL when not used.
> + */
> +extern struct dfu_entity *dfu_defer_flush;
> +/**
> + * dfu_get_defer_flush - get current value of dfu_defer_flush pointer
> + *
> + * @return - value of the dfu_defer_flush pointer
> + */
> +static inline struct dfu_entity *dfu_get_defer_flush(void)
> +{
> + return dfu_defer_flush;
> +}
> +
> +/**
> + * dfu_set_defer_flush - set the dfu_defer_flush pointer
> + *
> + * @param dfu - pointer to the dfu_entity, which should be written
> + */
> +static inline void dfu_set_defer_flush(struct dfu_entity *dfu)
> +{
> + dfu_defer_flush = dfu;
> +}
> +
> /**
> * dfu_write_from_mem_addr - write data from memory to DFU managed medium
> *
>
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
More information about the U-Boot
mailing list