diff --git a/cmd/dfu.c b/cmd/dfu.c index 6d95ce9..d8aae26 100644 --- a/cmd/dfu.c +++ b/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 *