usb: dfu: introduce dfuMANIFEST state

on nand flash using ubi, after the download of the new image into
the flash, the "rest" of the nand sectors get erased while flushing
the medium. With current u-boot version dfu-util may show:

Starting download: [##################################################] finished!
state(7) = dfuMANIFEST, status(0) = No error condition is present
unable to read DFU status

as get_status is not answered while erasing sectors, if erasing
needs some time.

So do the following changes to prevent this:

- introduce dfuManifest state
  According to dfu specification
  ( http://www.usb.org/developers/devclass_docs/usbdfu10.pdf ) section 7:
  "the device enters the dfuMANIFEST-SYNC state and awaits the solicitation
   of the status report by the host. Upon receipt of the anticipated
   DFU_GETSTATUS, the device enters the dfuMANIFEST state, where it
   completes its reprogramming operations."

- when stepping into dfuManifest state, sending a PollTimeout
  DFU_MANIFEST_POLL_TIMEOUT in ms, to the host, so the host
  (dfu-util) waits the PollTimeout before sending a get_status again.

Signed-off-by: Heiko Schocher <hs@denx.de>
Cc: Lukasz Majewski <l.majewski@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Marek Vasut <marex@denx.de>
Cc: Pantelis Antoniou <panto@antoniou-consulting.com>
diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c
index a045864..de75ff1 100644
--- a/drivers/usb/gadget/f_dfu.c
+++ b/drivers/usb/gadget/f_dfu.c
@@ -164,9 +164,14 @@
 
 	dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf,
 		  req->length, f_dfu->blk_seq_num);
+}
 
-	if (req->length == 0)
-		puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n");
+static void dnload_request_flush(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_dfu *f_dfu = req->context;
+
+	dfu_flush(dfu_get_entity(f_dfu->altsetting), req->buf,
+		  req->length, f_dfu->blk_seq_num);
 }
 
 static void handle_getstatus(struct usb_request *req)
@@ -174,19 +179,22 @@
 	struct dfu_status *dstat = (struct dfu_status *)req->buf;
 	struct f_dfu *f_dfu = req->context;
 
+	dfu_set_poll_timeout(dstat, 0);
+
 	switch (f_dfu->dfu_state) {
 	case DFU_STATE_dfuDNLOAD_SYNC:
 	case DFU_STATE_dfuDNBUSY:
 		f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
 		break;
 	case DFU_STATE_dfuMANIFEST_SYNC:
+		f_dfu->dfu_state = DFU_STATE_dfuMANIFEST;
 		break;
+	case DFU_STATE_dfuMANIFEST:
+		dfu_set_poll_timeout(dstat, DFU_MANIFEST_POLL_TIMEOUT);
 	default:
 		break;
 	}
 
-	dfu_set_poll_timeout(dstat, 0);
-
 	if (f_dfu->poll_timeout)
 		if (!(f_dfu->blk_seq_num %
 		      (dfu_get_buf_size() / DFU_USB_BUFSIZ)))
@@ -446,10 +454,11 @@
 	switch (ctrl->bRequest) {
 	case USB_REQ_DFU_GETSTATUS:
 		/* We're MainfestationTolerant */
-		f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+		f_dfu->dfu_state = DFU_STATE_dfuMANIFEST;
 		handle_getstatus(req);
 		f_dfu->blk_seq_num = 0;
 		value = RET_STAT_LEN;
+		req->complete = dnload_request_flush;
 		break;
 	case USB_REQ_DFU_GETSTATE:
 		handle_getstate(req);
@@ -463,6 +472,33 @@
 	return value;
 }
 
+static int state_dfu_manifest(struct f_dfu *f_dfu,
+			      const struct usb_ctrlrequest *ctrl,
+			      struct usb_gadget *gadget,
+			      struct usb_request *req)
+{
+	int value = 0;
+
+	switch (ctrl->bRequest) {
+	case USB_REQ_DFU_GETSTATUS:
+		/* We're MainfestationTolerant */
+		f_dfu->dfu_state = DFU_STATE_dfuIDLE;
+		handle_getstatus(req);
+		f_dfu->blk_seq_num = 0;
+		value = RET_STAT_LEN;
+		puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n");
+		break;
+	case USB_REQ_DFU_GETSTATE:
+		handle_getstate(req);
+		break;
+	default:
+		f_dfu->dfu_state = DFU_STATE_dfuERROR;
+		value = RET_STALL;
+		break;
+	}
+	return value;
+}
+
 static int state_dfu_upload_idle(struct f_dfu *f_dfu,
 				 const struct usb_ctrlrequest *ctrl,
 				 struct usb_gadget *gadget,
@@ -539,7 +575,7 @@
 	state_dfu_dnbusy,        /* DFU_STATE_dfuDNBUSY */
 	state_dfu_dnload_idle,   /* DFU_STATE_dfuDNLOAD_IDLE */
 	state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */
-	NULL,                    /* DFU_STATE_dfuMANIFEST */
+	state_dfu_manifest,	 /* DFU_STATE_dfuMANIFEST */
 	NULL,                    /* DFU_STATE_dfuMANIFEST_WAIT_RST */
 	state_dfu_upload_idle,   /* DFU_STATE_dfuUPLOAD_IDLE */
 	state_dfu_error          /* DFU_STATE_dfuERROR */