From fe88b83e80df8be0351ff38ee6a77b855b0cd0a9 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 7 Jul 2011 17:39:32 +0200 Subject: dfu: fix EP0 IN trasnfers that are even multiple of 8 We have to send a ZLP at the end of such transfers, otherwise the host will time out at the end of the transfer. This resulted in multi-second delays for recognizing the SIMtrace hardware by the USB host. --- firmware/src/dfu/dfu.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'firmware/src/dfu/dfu.c') diff --git a/firmware/src/dfu/dfu.c b/firmware/src/dfu/dfu.c index f740800..8425c28 100644 --- a/firmware/src/dfu/dfu.c +++ b/firmware/src/dfu/dfu.c @@ -86,18 +86,20 @@ static void __dfufunc udp_init(void) AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUPv4); } +static void __dfufunc udp_ep0_send_zlp(void); + /* Send Data through the control endpoint */ static void __dfufunc udp_ep0_send_data(const char *pData, u_int32_t length) { AT91PS_UDP pUdp = AT91C_BASE_UDP; - u_int32_t cpt = 0; + u_int32_t cpt = 0, len_remain = length; AT91_REG csr; DEBUGE("send_data: %u bytes ", length); do { - cpt = MIN(length, 8); - length -= cpt; + cpt = MIN(len_remain, 8); + len_remain -= cpt; while (cpt--) pUdp->UDP_FDR[0] = *pData++; @@ -119,12 +121,20 @@ static void __dfufunc udp_ep0_send_data(const char *pData, u_int32_t length) } } while (!(csr & AT91C_UDP_TXCOMP)); - } while (length); + } while (len_remain); if (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) { pUdp->UDP_CSR[0] &= ~(AT91C_UDP_TXCOMP); while (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) ; } + + if ((length % 8) == 0) { + /* if the length is a multiple of the EP size, we need + * to send another ZLP (zero-length packet) to tell the + * host the transfer has completed. */ + DEBUGE("set_txpktrdy_zlp "); + udp_ep0_send_zlp(); + } } static void udp_ep0_recv_clean(void) -- cgit v1.2.3 From d56bb35e907e2d7c4507b7240e36d8b69d272bce Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 29 Jul 2011 23:30:00 +0200 Subject: sam7dfu: make sure we can use USB reset to get from DFU -> APP When we went through a Download -> Manifest cycle, we can switch back to dfuIDLE. However, we need to memorize that manifest had already happened and thus should treat a host-initiated bus reset as trigger to switch back into application mode. --- firmware/src/dfu/dfu.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'firmware/src/dfu/dfu.c') diff --git a/firmware/src/dfu/dfu.c b/firmware/src/dfu/dfu.c index 8425c28..e126539 100644 --- a/firmware/src/dfu/dfu.c +++ b/firmware/src/dfu/dfu.c @@ -69,6 +69,8 @@ #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 void __dfufunc udp_init(void) { /* Set the PLL USB Divider */ @@ -486,7 +488,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: @@ -858,12 +869,13 @@ static __dfufunc void dfu_udp_irq(void) cur_config = 0; if (dfu_state == DFU_STATE_dfuMANIFEST_WAIT_RST || - dfu_state == DFU_STATE_dfuMANIFEST) { + dfu_state == DFU_STATE_dfuMANIFEST || + past_manifest) { + /* reset back into the main application */ AT91F_RSTSoftReset(AT91C_BASE_RSTC, AT91C_RSTC_PROCRST| AT91C_RSTC_PERRST| AT91C_RSTC_EXTRST); } - } if (isr & AT91C_UDP_EPINT0) -- cgit v1.2.3 From a1e7c0c5efba4eebdb1ed178b5e3604226a1421a Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 29 Jul 2011 23:33:20 +0200 Subject: sam7dfu: update copyright years --- firmware/src/dfu/dfu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'firmware/src/dfu/dfu.c') diff --git a/firmware/src/dfu/dfu.c b/firmware/src/dfu/dfu.c index e126539..6a2f59c 100644 --- a/firmware/src/dfu/dfu.c +++ b/firmware/src/dfu/dfu.c @@ -914,7 +914,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 \n\r" + "(C) 2006-2011 by Harald Welte \n\r" "This software is FREE SOFTWARE licensed under GNU GPL\n\r"); AT91F_DBGU_Printk("Version " COMPILE_SVNREV " compiled " COMPILE_DATE -- cgit v1.2.3 From 58d958e60c429bd7a2b2d114a4f7a86a65bdacef Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 30 Jul 2011 02:56:36 +0200 Subject: DFU: Support re-flashing of DFU via DFU As we re-locate the entire sam7dfu program into RAM, we don't need to execute from flash and can thus re-program the DFU partition via the DFU protocol itself (alternate setting '1'). We also implement downloading executable code into RAM using alternate setting '2'. The latter part is not properly executed yet. --- firmware/src/dfu/dfu.c | 148 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 132 insertions(+), 16 deletions(-) (limited to 'firmware/src/dfu/dfu.c') diff --git a/firmware/src/dfu/dfu.c b/firmware/src/dfu/dfu.c index 6a2f59c..f65b00d 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 + * (C) 2006-2011 by Harald Welte * * 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 #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 */ @@ -70,6 +71,8 @@ #define led2off() AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_LED2) static int past_manifest = 0; +static u_int16_t usb_if_nr = 0; +static u_int16_t usb_if_alt_nr = 0; static void __dfufunc udp_init(void) { @@ -212,14 +215,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; @@ -240,13 +266,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; @@ -258,10 +291,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 */ @@ -276,6 +309,56 @@ 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) +{ + 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) { @@ -287,9 +370,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; @@ -606,7 +692,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, @@ -648,6 +734,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, }; @@ -831,9 +932,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: @@ -849,6 +952,8 @@ static __dfufunc void dfu_udp_ep0_handler(void) DEBUGE("\r\n"); } +const void (*ram_app_entry)(void) = AT91C_ISRAM + SAM7DFU_RAM_SIZE; + /* minimal USB IRQ handler in DFU mode */ static __dfufunc void dfu_udp_irq(void) { @@ -871,10 +976,21 @@ static __dfufunc void dfu_udp_irq(void) if (dfu_state == DFU_STATE_dfuMANIFEST_WAIT_RST || dfu_state == DFU_STATE_dfuMANIFEST || past_manifest) { - /* reset back into the main application */ - AT91F_RSTSoftReset(AT91C_BASE_RSTC, AT91C_RSTC_PROCRST| - AT91C_RSTC_PERRST| - AT91C_RSTC_EXTRST); + AT91F_DBGU_Printk("sam7dfu: switching to APP mode\r\n"); + switch (usb_if_alt_nr) { + case 2: + /* jump into RAM */ + DEBUGP("JUMP TO RAM ENTRY %p\r\n", ram_app_entry); + ram_app_entry(); + break; + default: + /* reset back into the main application */ + AT91F_RSTSoftReset(AT91C_BASE_RSTC, + AT91C_RSTC_PROCRST| + AT91C_RSTC_PERRST| + AT91C_RSTC_EXTRST); + break; + } } } -- cgit v1.2.3 From 1acaecb4cb1de370a4fee692baaf6b41b8d8ec8c Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 30 Jul 2011 18:53:43 +0200 Subject: DFU: Fix execution of software that was loaded to RAM via DFU --- firmware/src/dfu/dfu.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'firmware/src/dfu/dfu.c') diff --git a/firmware/src/dfu/dfu.c b/firmware/src/dfu/dfu.c index f65b00d..0e22256 100644 --- a/firmware/src/dfu/dfu.c +++ b/firmware/src/dfu/dfu.c @@ -71,8 +71,10 @@ #define led2off() AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_LED2) static int past_manifest = 0; -static u_int16_t usb_if_nr = 0; -static u_int16_t usb_if_alt_nr = 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) { @@ -351,6 +353,7 @@ static int __dfufunc handle_dnload_ram(u_int16_t val, u_int16_t len) 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); @@ -952,8 +955,6 @@ static __dfufunc void dfu_udp_ep0_handler(void) DEBUGE("\r\n"); } -const void (*ram_app_entry)(void) = AT91C_ISRAM + SAM7DFU_RAM_SIZE; - /* minimal USB IRQ handler in DFU mode */ static __dfufunc void dfu_udp_irq(void) { @@ -977,11 +978,9 @@ static __dfufunc void dfu_udp_irq(void) dfu_state == DFU_STATE_dfuMANIFEST || past_manifest) { AT91F_DBGU_Printk("sam7dfu: switching to APP mode\r\n"); - switch (usb_if_alt_nr) { + switch (usb_if_alt_nr_dnload) { case 2: - /* jump into RAM */ - DEBUGP("JUMP TO RAM ENTRY %p\r\n", ram_app_entry); - ram_app_entry(); + switch_to_ram = 1; break; default: /* reset back into the main application */ @@ -1073,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(); + } } } -- cgit v1.2.3