diff options
Diffstat (limited to 'usb/device/dfu')
-rw-r--r-- | usb/device/dfu/dfu.h | 7 | ||||
-rw-r--r-- | usb/device/dfu/dfu_driver.c | 124 |
2 files changed, 124 insertions, 7 deletions
diff --git a/usb/device/dfu/dfu.h b/usb/device/dfu/dfu.h index 34afa4a..bf07d43 100644 --- a/usb/device/dfu/dfu.h +++ b/usb/device/dfu/dfu.h @@ -116,13 +116,16 @@ struct dfu { uint8_t status; uint32_t state; int past_manifest; + unsigned int total_bytes; }; extern struct dfu dfu; /* call-backs by the board/SOC */ -extern int USBDFU_handle_dnload(uint16_t val, uint16_t len, int first); -extern int USBDFU_handle_upload(uint16_t val, uint16_t len, int first); +extern int USBDFU_handle_dnload(uint8_t altif, unsigned int offset, + uint8_t *data, unsigned int len); +extern int USBDFU_handle_upload(uint8_t altif, unsigned int offset, + uint8_t *data, unsigned int req_len); void USBDFU_Runtime_RequestHandler(const USBGenericRequest *request); void USBDFU_DFU_RequestHandler(const USBGenericRequest *request); diff --git a/usb/device/dfu/dfu_driver.c b/usb/device/dfu/dfu_driver.c index f7705ac..53f690c 100644 --- a/usb/device/dfu/dfu_driver.c +++ b/usb/device/dfu/dfu_driver.c @@ -41,6 +41,7 @@ static USBDDriver usbdDriver; __dfudata struct dfu dfu = { .state = DFU_STATE_appIDLE, .past_manifest = 0, + .total_bytes = 0, }; static __dfufunc void handle_getstatus(void) @@ -82,6 +83,119 @@ static void TerminateCtrlInWithNull(void *pArg, (void *) 0); } +static uint8_t dfu_buf[BOARD_DFU_PAGE_SIZE]; + +/* download of a single page has completed */ +static void dnload_cb(void *arg, unsigned char status, unsigned int transferred, + unsigned int remaining) +{ + int rc; + + TRACE_DEBUG("COMPLETE\n\r"); + + if (status != USBD_STATUS_SUCCESS) { + TRACE_ERROR("USBD download callback status %d\n\r", status); + USBD_Stall(0); + return; + } + + rc = USBDFU_handle_dnload(0, dfu.total_bytes, dfu_buf, transferred); + switch (rc) { + case DFU_RET_ZLP: + dfu.total_bytes += transferred; + dfu.state = DFU_STATE_dfuDNLOAD_IDLE; + TerminateCtrlInWithNull(0,0,0,0); + break; + case DFU_RET_STALL: + dfu.state = DFU_STATE_dfuERROR; + USBD_Stall(0); + break; + case DFU_RET_NOTHING: + break; + } + +} + +static int handle_dnload(uint16_t val, uint16_t len, int first) +{ + int rc; + + if (len > BOARD_DFU_PAGE_SIZE) { + TRACE_ERROR("DFU length exceeds flash page size\n\r"); + dfu.state = DFU_STATE_dfuERROR; + dfu.status = DFU_STATUS_errADDRESS; + return DFU_RET_STALL; + } + + if (len & 0x03) { + TRACE_ERROR("DFU length not four-byte-aligned\n\r"); + dfu.state = DFU_STATE_dfuERROR; + dfu.status = DFU_STATUS_errADDRESS; + return DFU_RET_STALL; + } + + if (first) + dfu.total_bytes = 0; + + if (len == 0) { + TRACE_DEBUG("zero-size write -> MANIFEST_SYNC\n\r"); + dfu.state = DFU_STATE_dfuMANIFEST_SYNC; + return DFU_RET_ZLP; + } + + /* else: actually read data */ + rc = USBD_Read(0, dfu_buf, len, &dnload_cb, 0); + if (rc == USBD_STATUS_SUCCESS) + return DFU_RET_NOTHING; + else + return DFU_RET_STALL; +} + +/* upload of a single page has completed */ +static void upload_cb(void *arg, unsigned char status, unsigned int transferred, + unsigned int remaining) +{ + int rc; + + TRACE_DEBUG("COMPLETE\n\r"); + + if (status != USBD_STATUS_SUCCESS) { + TRACE_ERROR("USBD upload callback status %d\n\r", status); + USBD_Stall(0); + return; + } + + dfu.total_bytes += transferred; +} + +static int handle_upload(uint16_t val, uint16_t len, int first) +{ + int rc; + + if (first) + dfu.total_bytes = 0; + + if (len > BOARD_DFU_PAGE_SIZE) { + TRACE_ERROR("DFU length exceeds flash page size\n\r"); + dfu.state = DFU_STATE_dfuERROR; + dfu.status = DFU_STATUS_errADDRESS; + return DFU_RET_STALL; + } + + /* FIXME: altif */ + rc = USBDFU_handle_upload(0, dfu.total_bytes, dfu_buf, len); + if (rc < 0) { + TRACE_ERROR("application handle_upload() returned %d\n\r", rc); + return DFU_RET_STALL; + } + + rc = USBD_Write(0, dfu_buf, rc, &upload_cb, 0); + if (rc == USBD_STATUS_SUCCESS) + return DFU_RET_NOTHING; + else + return DFU_RET_STALL; +} + /* this function gets daisy-chained into processing EP0 requests */ void USBDFU_DFU_RequestHandler(const USBGenericRequest *request) { @@ -90,7 +204,7 @@ void USBDFU_DFU_RequestHandler(const USBGenericRequest *request) uint16_t val = USBGenericRequest_GetValue(request); int rc, ret; - TRACE_DEBUG("type=0x%x, recipient=0x%x val=0x%x len=%u\n", + TRACE_DEBUG("type=0x%x, recipient=0x%x val=0x%x len=%u\n\r", USBGenericRequest_GetType(request), USBGenericRequest_GetRecipient(request), val, len); @@ -166,11 +280,11 @@ void USBDFU_DFU_RequestHandler(const USBGenericRequest *request) goto out; } dfu.state = DFU_STATE_dfuDNLOAD_SYNC; - ret = USBDFU_handle_dnload(val, len, 1); + ret = handle_dnload(val, len, 1); break; case USB_REQ_DFU_UPLOAD: dfu.state = DFU_STATE_dfuUPLOAD_IDLE; - USBDFU_handle_upload(val, len, 1); + handle_upload(val, len, 1); break; case USB_REQ_DFU_ABORT: /* no zlp? */ @@ -221,7 +335,7 @@ void USBDFU_DFU_RequestHandler(const USBGenericRequest *request) switch (req) { case USB_REQ_DFU_DNLOAD: dfu.state = DFU_STATE_dfuDNLOAD_SYNC; - ret = USBDFU_handle_dnload(val, len, 0); + ret = handle_dnload(val, len, 0); break; case USB_REQ_DFU_ABORT: dfu.state = DFU_STATE_dfuIDLE; @@ -284,7 +398,7 @@ void USBDFU_DFU_RequestHandler(const USBGenericRequest *request) switch (req) { case USB_REQ_DFU_UPLOAD: /* state transition if less data then requested */ - rc = USBDFU_handle_upload(val, len, 0); + rc = handle_upload(val, len, 0); if (rc >= 0 && rc < len) dfu.state = DFU_STATE_dfuIDLE; break; |