summaryrefslogtreecommitdiff
path: root/firmware/src/dfu/dfu.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/src/dfu/dfu.c')
-rw-r--r--firmware/src/dfu/dfu.c173
1 files changed, 155 insertions, 18 deletions
diff --git a/firmware/src/dfu/dfu.c b/firmware/src/dfu/dfu.c
index 8425c28..0e22256 100644
--- a/firmware/src/dfu/dfu.c
+++ b/firmware/src/dfu/dfu.c
@@ -1,5 +1,5 @@
/* USB Device Firmware Update Implementation for OpenPCD
- * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ * (C) 2006-2011 by Harald Welte <hwelte@hmw-consulting.de>
*
* This ought to be compliant to the USB DFU Spec 1.0 as available from
* http://www.usb.org/developers/devclass_docs/usbdfu10.pdf
@@ -37,6 +37,7 @@
#include <compile.h>
#define SAM7DFU_SIZE 0x4000
+#define SAM7DFU_RAM_SIZE 0x2000
/* If debug is enabled, we need to access debug functions from flash
* and therefore have to omit flashing */
@@ -69,6 +70,12 @@
#define led2on() AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPCD_PIO_LED2)
#define led2off() AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_LED2)
+static int past_manifest = 0;
+static int switch_to_ram = 0; /* IRQ handler requests main to jump to RAM */
+static u_int16_t usb_if_nr = 0; /* last SET_INTERFACE */
+static u_int16_t usb_if_alt_nr = 0; /* last SET_INTERFACE AltSetting */
+static u_int16_t usb_if_alt_nr_dnload = 0; /* AltSetting during last dnload */
+
static void __dfufunc udp_init(void)
{
/* Set the PLL USB Divider */
@@ -210,14 +217,37 @@ static void __dfufunc udp_ep0_send_stall(void)
}
-static u_int8_t *ptr = (u_int8_t *) AT91C_IFLASH + SAM7DFU_SIZE;
+static int first_download = 1;
+static u_int8_t *ptr, *ptr_max;
static __dfudata u_int8_t dfu_status;
__dfudata u_int32_t dfu_state = DFU_STATE_appIDLE;
static u_int32_t pagebuf32[AT91C_IFLASH_PAGE_SIZE/4];
-static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len)
+static void chk_first_dnload_set_ptr(void)
+{
+ if (!first_download)
+ return;
+
+ switch (usb_if_alt_nr) {
+ case 0:
+ ptr = (u_int8_t *) AT91C_IFLASH + SAM7DFU_SIZE;
+ ptr_max = AT91C_IFLASH + AT91C_IFLASH_SIZE - ENVIRONMENT_SIZE;
+ break;
+ case 1:
+ ptr = (u_int8_t *) AT91C_IFLASH;
+ ptr_max = AT91C_IFLASH + SAM7DFU_SIZE;
+ break;
+ case 2:
+ ptr = (u_int8_t *) AT91C_ISRAM + SAM7DFU_RAM_SIZE;
+ ptr_max = AT91C_ISRAM + AT91C_ISRAM_SIZE;
+ break;
+ }
+ first_download = 0;
+}
+
+static int __dfufunc handle_dnload_flash(u_int16_t val, u_int16_t len)
{
- volatile u_int32_t *p = (volatile u_int32_t *)ptr;
+ volatile u_int32_t *p;
u_int8_t *pagebuf = (u_int8_t *) pagebuf32;
int i;
@@ -238,13 +268,20 @@ static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len)
dfu_status = DFU_STATUS_errADDRESS;
return RET_STALL;
}
+ chk_first_dnload_set_ptr();
+ p = (volatile u_int32_t *)ptr;
+
if (len == 0) {
DEBUGP("zero-size write -> MANIFEST_SYNC ");
- flash_page(p);
+ if (((unsigned long)p % AT91C_IFLASH_PAGE_SIZE) != 0)
+ flash_page(p);
dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
+ first_download = 1;
return RET_ZLP;
}
- if (ptr + len >= (u_int8_t *) AT91C_IFLASH + AT91C_IFLASH_SIZE - ENVIRONMENT_SIZE ) {
+
+ /* check if we would exceed end of memory */
+ if (ptr + len > ptr_max) {
DEBUGP("end of write exceeds flash end ");
dfu_state = DFU_STATE_dfuERROR;
dfu_status = DFU_STATUS_errADDRESS;
@@ -256,10 +293,10 @@ static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len)
DEBUGR(hexdump(pagebuf, len));
- /* we can only access the write buffer with correctly aligned
- * 32bit writes ! */
#ifndef DEBUG_DFU_NOFLASH
DEBUGP("copying ");
+ /* we can only access the write buffer with correctly aligned
+ * 32bit writes ! */
for (i = 0; i < len/4; i++) {
*p++ = pagebuf32[i];
/* If we have filled a page buffer, flash it */
@@ -274,6 +311,57 @@ static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len)
return RET_ZLP;
}
+static int __dfufunc handle_dnload_ram(u_int16_t val, u_int16_t len)
+{
+ DEBUGE("download ");
+
+ if (len > AT91C_IFLASH_PAGE_SIZE) {
+ /* Too big. Not that we'd really care, but it's a
+ * DFU protocol violation */
+ DEBUGP("length exceeds flash page size ");
+ dfu_state = DFU_STATE_dfuERROR;
+ dfu_status = DFU_STATUS_errADDRESS;
+ return RET_STALL;
+ }
+ chk_first_dnload_set_ptr();
+
+ if (len == 0) {
+ DEBUGP("zero-size write -> MANIFEST_SYNC ");
+ dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
+ first_download = 1;
+ return RET_ZLP;
+ }
+
+ /* check if we would exceed end of memory */
+ if (ptr + len >= ptr_max) {
+ DEBUGP("end of write exceeds RAM end ");
+ dfu_state = DFU_STATE_dfuERROR;
+ dfu_status = DFU_STATUS_errADDRESS;
+ return RET_STALL;
+ }
+
+ /* drectly copy into RAM */
+ DEBUGP("try_to_recv=%u ", len);
+ udp_ep0_recv_data(ptr, len);
+
+ DEBUGR(hexdump(ptr, len));
+
+ ptr += len;
+
+ return RET_ZLP;
+}
+
+static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len)
+{
+ usb_if_alt_nr_dnload = usb_if_alt_nr;
+ switch (usb_if_alt_nr) {
+ case 2:
+ return handle_dnload_ram(val, len);
+ default:
+ return handle_dnload_flash(val, len);
+ }
+}
+
#define AT91C_IFLASH_END ((u_int8_t *)AT91C_IFLASH + AT91C_IFLASH_SIZE)
static __dfufunc int handle_upload(u_int16_t val, u_int16_t len)
{
@@ -285,9 +373,12 @@ static __dfufunc int handle_upload(u_int16_t val, u_int16_t len)
udp_ep0_send_stall();
return -EINVAL;
}
+ chk_first_dnload_set_ptr();
- if (ptr + len > AT91C_IFLASH_END)
+ if (ptr + len > AT91C_IFLASH_END) {
len = AT91C_IFLASH_END - (u_int8_t *)ptr;
+ first_download = 1;
+ }
udp_ep0_send_data((char *)ptr, len);
ptr+= len;
@@ -486,7 +577,16 @@ int __dfufunc dfu_ep0_handler(u_int8_t req_type, u_int8_t req,
case DFU_STATE_dfuMANIFEST:
switch (req) {
case USB_REQ_DFU_GETSTATUS:
+ /* we don't want to change to WAIT_RST, as it
+ * would mean that we can not support another
+ * DFU transaction before doing the actual
+ * reset. Instead, we switch to idle and note
+ * that we've already been through MANIFST in
+ * the global variable 'past_manifest'.
+ */
+ //dfu_state = DFU_STATE_dfuMANIFEST_WAIT_RST;
dfu_state = DFU_STATE_dfuIDLE;
+ past_manifest = 1;
handle_getstatus();
break;
case USB_REQ_DFU_GETSTATE:
@@ -595,7 +695,7 @@ __dfustruct const struct _dfu_desc dfu_cfg_descriptor = {
.bLength = USB_DT_CONFIG_SIZE,
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = USB_DT_CONFIG_SIZE +
- 2* USB_DT_INTERFACE_SIZE +
+ 3* USB_DT_INTERFACE_SIZE +
USB_DT_DFU_SIZE,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
@@ -637,6 +737,21 @@ __dfustruct const struct _dfu_desc dfu_cfg_descriptor = {
.iInterface = 0,
#endif
},
+ .uif[2] = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0x00,
+ .bAlternateSetting = 0x02,
+ .bNumEndpoints = 0x00,
+ .bInterfaceClass = 0xfe,
+ .bInterfaceSubClass = 0x01,
+ .bInterfaceProtocol = 0x02,
+#ifdef CONFIG_USB_STRING
+ .iInterface = 6,
+#else
+ .iInterface = 0,
+#endif
+ },
.func_dfu = DFU_FUNC_DESC,
};
@@ -820,9 +935,11 @@ static __dfufunc void dfu_udp_ep0_handler(void)
udp_ep0_send_stall();
break;
case STD_SET_INTERFACE:
- DEBUGE("SET INTERFACE ");
- /* FIXME: store the interface number somewhere, once
+ DEBUGE("SET INTERFACE(if=%d, alt=%d) ", wIndex, wValue);
+ /* store the interface number somewhere, once
* we need to support DFU flashing DFU */
+ usb_if_alt_nr = wValue;
+ usb_if_nr = wIndex;
udp_ep0_send_zlp();
break;
default:
@@ -858,12 +975,22 @@ static __dfufunc void dfu_udp_irq(void)
cur_config = 0;
if (dfu_state == DFU_STATE_dfuMANIFEST_WAIT_RST ||
- dfu_state == DFU_STATE_dfuMANIFEST) {
- AT91F_RSTSoftReset(AT91C_BASE_RSTC, AT91C_RSTC_PROCRST|
- AT91C_RSTC_PERRST|
- AT91C_RSTC_EXTRST);
+ dfu_state == DFU_STATE_dfuMANIFEST ||
+ past_manifest) {
+ AT91F_DBGU_Printk("sam7dfu: switching to APP mode\r\n");
+ switch (usb_if_alt_nr_dnload) {
+ case 2:
+ switch_to_ram = 1;
+ break;
+ default:
+ /* reset back into the main application */
+ AT91F_RSTSoftReset(AT91C_BASE_RSTC,
+ AT91C_RSTC_PROCRST|
+ AT91C_RSTC_PERRST|
+ AT91C_RSTC_EXTRST);
+ break;
+ }
}
-
}
if (isr & AT91C_UDP_EPINT0)
@@ -902,7 +1029,7 @@ void __dfufunc dfu_main(void)
AT91F_DBGU_Init();
AT91F_DBGU_Printk("\n\r\n\rsam7dfu - AT91SAM7 USB DFU bootloader\n\r"
- "(C) 2006-2008 by Harald Welte <hwelte@hmw-consulting.de>\n\r"
+ "(C) 2006-2011 by Harald Welte <hwelte@hmw-consulting.de>\n\r"
"This software is FREE SOFTWARE licensed under GNU GPL\n\r");
AT91F_DBGU_Printk("Version " COMPILE_SVNREV
" compiled " COMPILE_DATE
@@ -945,6 +1072,16 @@ void __dfufunc dfu_main(void)
if( i== 0) {
AT91F_WDTRestart(AT91C_BASE_WDTC);
}
+ if (switch_to_ram) {
+ void (*ram_app_entry)(void);
+ int i;
+ for (i = 0; i < 32; i++)
+ AT91F_AIC_DisableIt(AT91C_BASE_AIC, i);
+ /* jump into RAM */
+ AT91F_DBGU_Printk("JUMP TO RAM\r\n");
+ ram_app_entry = AT91C_ISRAM + SAM7DFU_RAM_SIZE;
+ ram_app_entry();
+ }
}
}
personal git repositories of Harald Welte. Your mileage may vary