diff options
Diffstat (limited to 'firmware/src/dfu')
-rw-r--r-- | firmware/src/dfu/dfu.c | 173 | ||||
-rw-r--r-- | firmware/src/dfu/dfu.h | 26 |
2 files changed, 178 insertions, 21 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(); + } } } diff --git a/firmware/src/dfu/dfu.h b/firmware/src/dfu/dfu.h index a4184b4..c898197 100644 --- a/firmware/src/dfu/dfu.h +++ b/firmware/src/dfu/dfu.h @@ -61,6 +61,16 @@ .bInterfaceSubClass = 0x01, \ .bInterfaceProtocol = 0x01, \ .iInterface = 2, \ + }, { \ + .bLength = USB_DT_INTERFACE_SIZE, \ + .bDescriptorType = USB_DT_INTERFACE, \ + .bInterfaceNumber = 0x03, \ + .bAlternateSetting = 0x00, \ + .bNumEndpoints = 0x00, \ + .bInterfaceClass = 0xfe, \ + .bInterfaceSubClass = 0x01, \ + .bInterfaceProtocol = 0x01, \ + .iInterface = 3, \ }, \ } #else @@ -78,8 +88,18 @@ }, { \ .bLength = USB_DT_INTERFACE_SIZE, \ .bDescriptorType = USB_DT_INTERFACE, \ - .bInterfaceNumber = 0x02, \ - .bAlternateSetting = 0x00, \ + .bInterfaceNumber = 0x01, \ + .bAlternateSetting = 0x01, \ + .bNumEndpoints = 0x00, \ + .bInterfaceClass = 0xfe, \ + .bInterfaceSubClass = 0x01, \ + .bInterfaceProtocol = 0x01, \ + .iInterface = 0, \ + }, { \ + .bLength = USB_DT_INTERFACE_SIZE, \ + .bDescriptorType = USB_DT_INTERFACE, \ + .bInterfaceNumber = 0x01, \ + .bAlternateSetting = 0x02, \ .bNumEndpoints = 0x00, \ .bInterfaceClass = 0xfe, \ .bInterfaceSubClass = 0x01, \ @@ -98,7 +118,7 @@ struct _dfu_desc { struct usb_config_descriptor ucfg; - struct usb_interface_descriptor uif[2]; + struct usb_interface_descriptor uif[3]; struct usb_dfu_func_descriptor func_dfu; }; |