diff options
| -rw-r--r-- | firmware/link/AT91SAM7S128-RAM-sam7dfu-app.ld | 4 | ||||
| -rw-r--r-- | firmware/src/dfu/dfu.c | 148 | 
2 files changed, 134 insertions, 18 deletions
diff --git a/firmware/link/AT91SAM7S128-RAM-sam7dfu-app.ld b/firmware/link/AT91SAM7S128-RAM-sam7dfu-app.ld index 674ee00..ad02776 100644 --- a/firmware/link/AT91SAM7S128-RAM-sam7dfu-app.ld +++ b/firmware/link/AT91SAM7S128-RAM-sam7dfu-app.ld @@ -5,7 +5,7 @@ MEMORY    /* reserve 16K DFU area on top  of flash */    /* FLASH  (rx) : ORIGIN = 0x00104000, LENGTH = (0x00020000 - 0x4000 - 0x400) */    /* reserve 1k DFU area on top of RAM */ -  DATA   (rw) : ORIGIN = 0x00200400, LENGTH = (0x00008000 - 0x400) +  DATA   (rw) : ORIGIN = 0x00202000, LENGTH = (0x00008000 - 0x2000)    STACK  (rw) : ORIGIN = 0x00208000, LENGTH = 0x00000000  } @@ -16,7 +16,7 @@ SECTIONS  {  	. = 0x00000000;  	/* first section is .text which is used for code */ -	.text 0x00200400: AT ( 0x00000000 ) {  +	.text 0x00202000: AT ( 0x00000000 ) {     		src/start/Cstartup_app.o (.text)  		* (.text)  		* (.rodata*) 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 <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 */ @@ -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; +			}  		}  	}  | 
