summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2012-01-07 17:38:11 +0100
committerHarald Welte <laforge@gnumonks.org>2012-01-07 17:38:11 +0100
commit141a11409e125abeca98d069eec6fcfee480d5fd (patch)
tree926227c0bf397ca2f28b8e9faea9cc0b83abe19d
parent541bc5bdaea55365b290f2995fc865371577dc0d (diff)
DFU: include more common infrastructure for download and upload handling
-rw-r--r--usb/device/dfu/dfu.h7
-rw-r--r--usb/device/dfu/dfu_driver.c124
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;
personal git repositories of Harald Welte. Your mileage may vary