diff options
Diffstat (limited to 'firmware/src')
| -rw-r--r-- | firmware/src/dfu/dfu.c | 118 | ||||
| -rw-r--r-- | firmware/src/dfu/dfu.h | 15 | ||||
| -rw-r--r-- | firmware/src/dfu/flash.c | 10 | 
3 files changed, 109 insertions, 34 deletions
diff --git a/firmware/src/dfu/dfu.c b/firmware/src/dfu/dfu.c index 763aa04..09c8784 100644 --- a/firmware/src/dfu/dfu.c +++ b/firmware/src/dfu/dfu.c @@ -23,24 +23,29 @@  #include <errno.h>  #include <usb_ch9.h>  #include <usb_dfu.h> +#include <board.h>  #include <lib_AT91SAM7.h>  #include <dfu/dfu.h> +#include <dfu/dbgu.h>  #include <os/pcd_enumerate.h>  #include "../openpcd.h"  /* If debug is enabled, we need to access debug functions from flash   * and therefore have to omit flashing */ -//#define DEBUG_DFU +#define DEBUG_DFU -#ifdef DEBUG_DFU  #define DEBUGE DEBUGP -#define DEBUGI DEBUGP -#else -#define DEBUGE(x, args ...) do { } while (0) -#define DEBUGI(x, args ...) do { } while (0) -#endif +static void flash_init(void) +{ +	unsigned int fmcn = AT91F_MC_EFC_ComputeFMCN(MCK); + +	AT91F_MC_EFC_CfgModeReg(AT91C_BASE_MC, fmcn << 16 | AT91C_MC_FWS_3FWS | +				AT91C_MC_FRDY | AT91C_MC_LOCKE |  +				AT91C_MC_PROGE); + +}  static void __dfufunc udp_init(void)  { @@ -97,6 +102,32 @@ static void __dfufunc udp_ep0_send_data(const char *pData, u_int32_t length)  	}  } +/* receive data from EP0 */ +static int __dfufunc udp_ep0_recv_data(char *data, u_int32_t len) +{ +	AT91PS_UDP pUdp = AT91C_BASE_UDP; +	u_int16_t i, num_rcv; +	u_int32_t num_rcv_total = 0; + +	do { +		/* FIXME: do we need to check whether we've been interrupted +		 * by a RX SETUP stage? */ +		while (!(pUdp->UDP_CSR[0] & AT91C_UDP_RX_DATA_BK0)) ; +	 +		num_rcv = pUdp->UDP_CSR[0] >> 16; +		for (i = 0; i < num_rcv; i++) +			*data++ = pUdp->UDP_FDR[0]; +		pUdp->UDP_CSR[0] &= ~(AT91C_UDP_RX_DATA_BK0); + +		num_rcv_total += num_rcv; + +		/* we need to continue to pull data until we either receive  +		 * a packet < endpoint size or == 0 */ +	} while (num_rcv == 8); + +	return num_rcv_total; +} +  /* Send zero length packet through the control endpoint */  static void __dfufunc udp_ep0_send_zlp(void)  { @@ -119,12 +150,31 @@ static void __dfufunc udp_ep0_send_stall(void)  static u_int8_t status; -static u_int8_t *ptr; +static u_int8_t *ptr = (u_int8_t *)0x00101000;  static __dfudata u_int32_t dfu_state; +static u_int8_t pagebuf[AT91C_IFLASH_PAGE_SIZE]; + +static u_int16_t page_from_ramaddr(const void *addr) +{ +	u_int32_t ramaddr = (u_int32_t) ptr; +	ramaddr -= (u_int32_t) AT91C_IFLASH; +	return (ramaddr >> AT91C_IFLASH_PAGE_SHIFT); +} +#define PAGES_PER_LOCKREGION	(AT91C_IFLASH_LOCK_REGION_SIZE>>AT91C_IFLASH_PAGE_SHIFT) +#define IS_FIRST_PAGE_OF_LOCKREGION(x)	((x % PAGES_PER_LOCKREGION) == 0) +#define LOCKREGION_FROM_PAGE(x)	(x / PAGES_PER_LOCKREGION) + +static int is_page_locked(u_int16_t page) +{ +	u_int16_t lockregion = LOCKREGION_FROM_PAGE(page); + +	return (AT91C_BASE_MC->MC_FSR & (lockregion << 16)); +}  static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len)  {  	volatile u_int32_t *p = (volatile u_int32_t *)ptr; +	u_int16_t page = page_from_ramaddr(ptr);  	DEBUGE("download "); @@ -136,6 +186,7 @@ static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len)  		return -EINVAL;  	}  	if (len & 0x3) { +		/* reject non-four-byte-aligned writes */  		dfu_state = DFU_STATE_dfuERROR;  		status = DFU_STATUS_errADDRESS;  		udp_ep0_send_stall(); @@ -145,14 +196,27 @@ static int __dfufunc handle_dnload(u_int16_t val, u_int16_t len)  		dfu_state = DFU_STATE_dfuMANIFEST_SYNC;  		return 0;  	} + +	udp_ep0_recv_data(pagebuf, sizeof(pagebuf)); + +	DEBUGP(hexdump(pagebuf, len)); + +	/* we can only access the write buffer with correctly aligned +	 * 32bit writes ! */  #if 0  	for (i = 0; i < len/4; i++)  		p[i] =  #endif -	/* FIXME: get data packet from FIFO, (erase+)write flash */ + +	/* unlock, (erase+)write flash */  #ifndef DEBUG_DFU +	if (is_page_locked(page)) +		AT91F_MC_EFC_PerformCmd(AT91C_BASE_MC, AT91C_MC_FCMD_UNLOCK | +					AT91C_MC_CORRECT_KEY | page); +	AT91F_MC_EFC_PerformCmd(AT91C_BASE_MC, AT91C_MC_FCMD_START_PROG | +				AT91C_MC_CORRECT_KEY | page);  #endif  	return 0; @@ -182,13 +246,34 @@ static __dfufunc int handle_upload(u_int16_t val, u_int16_t len)  static __dfufunc void handle_getstatus(void)  {  	struct dfu_status dstat; +	u_int32_t fsr = AT91F_MC_EFC_GetStatus(AT91C_BASE_MC);  	DEBUGE("getstatus "); +	switch (dfu_state) { +	case DFU_STATE_dfuDNLOAD_SYNC: +	case DFU_STATE_dfuDNBUSY: +		if (fsr & AT91C_MC_PROGE) { +			status = DFU_STATUS_errPROG; +			dfu_state = DFU_STATE_dfuERROR; +		} else if (fsr & AT91C_MC_LOCKE) { +			status = DFU_STATUS_errWRITE; +			dfu_state = DFU_STATE_dfuERROR; +		} else if (fsr & AT91C_MC_FRDY) +			dfu_state = DFU_STATE_dfuDNLOAD_IDLE; +		else +			dfu_state = DFU_STATE_dfuDNBUSY; +		break; +	case DFU_STATE_dfuMANIFEST_SYNC: +		dfu_state = DFU_STATE_dfuMANIFEST; +		break; +	} +  	/* send status response */  	dstat.bStatus = status;  	dstat.bState = dfu_state;  	dstat.iString = 0; +  	udp_ep0_send_data((char *)&dstat, sizeof(dstat));  } @@ -196,6 +281,7 @@ static void __dfufunc handle_getstate(void)  {  	u_int8_t u8 = dfu_state;  	DEBUGE("getstate "); +  	udp_ep0_send_data((char *)&u8, sizeof(u8));  } @@ -237,6 +323,7 @@ int __dfufunc dfu_ep0_handler(u_int8_t req_type, u_int8_t req,  				goto send_stall;  			}  			handle_dnload(val, len); +			dfu_state = DFU_STATE_dfuDNLOAD_SYNC;  			break;  		case USB_REQ_DFU_UPLOAD:  			ptr = 0x00101000; /* Flash base address for app */ @@ -280,8 +367,7 @@ int __dfufunc dfu_ep0_handler(u_int8_t req_type, u_int8_t req,  	case DFU_STATE_dfuDNLOAD_IDLE:  		switch (req) {  		case USB_REQ_DFU_DNLOAD: -			if (handle_dnload(val, len)) -			/* FIXME: state transition */ +			handle_dnload(val, len);  			break;  		case USB_REQ_DFU_ABORT:  			dfu_state = DFU_STATE_dfuIDLE; @@ -302,6 +388,7 @@ int __dfufunc dfu_ep0_handler(u_int8_t req_type, u_int8_t req,  	case DFU_STATE_dfuMANIFEST_SYNC:  		switch (req) {  		case USB_REQ_DFU_GETSTATUS: +			handle_getstatus();  			break;  		case USB_REQ_DFU_GETSTATE:  			handle_getstate(); @@ -516,6 +603,11 @@ static __dfufunc void dfu_udp_ep0_handler(void)  					  MIN(sizeof(dfu_if_descriptor),  					      wLength));  #endif +		} else if (wValue == 0x2100) { +			/* Return Function descriptor */ +			udp_ep0_send_data((const char *) &dfu_cfg_descriptor.func_dfu, +					  MIN(sizeof(dfu_cfg_descriptor.func_dfu), +					      wLength));  		} else  			udp_ep0_send_stall();  		break; @@ -654,6 +746,8 @@ static void dfu_switch(void)  void __dfufunc dfu_main(void)  { +	AT91F_DBGU_Init(); +  	udp_init();  	/* This implements  @@ -675,6 +769,8 @@ void __dfufunc dfu_main(void)  	/* Clear for set the Pull up resistor */  	AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP); +	flash_init(); +  	/* do nothing, since all of DFU is interrupt driven */  	while (1) ;  } diff --git a/firmware/src/dfu/dfu.h b/firmware/src/dfu/dfu.h index 4b7508f..e587fe4 100644 --- a/firmware/src/dfu/dfu.h +++ b/firmware/src/dfu/dfu.h @@ -54,19 +54,8 @@  #define __dfudata __attribute__ ((section (".data.shared")))  #define __dfufunc   #define __dfustruct const -	 -#if 0 -extern void __dfufunc udp_ep0_send_data(const char *data, u_int32_t length); -extern void __dfufunc udp_ep0_send_zlp(void); -extern void __dfufunc udp_ep0_send_stall(void); -extern __dfustruct struct usb_device_descriptor dfu_dev_descriptor; -extern __dfustruct struct _dfu_desc dfu_cfg_descriptor; -extern void dfu_switch(void); -extern int __dfufunc dfu_ep0_handler(u_int8_t req_type, u_int8_t req, -				     u_int16_t val, u_int16_t len); -extern static u_int8_t dfu_state; -struct udp_pcd; -#endif + +#define DFU_API_LOCATION	((const struct dfuapi *) 0x00100fd0)  struct _dfu_desc {  	struct usb_config_descriptor ucfg; diff --git a/firmware/src/dfu/flash.c b/firmware/src/dfu/flash.c index 2aaf760..675552d 100644 --- a/firmware/src/dfu/flash.c +++ b/firmware/src/dfu/flash.c @@ -32,14 +32,4 @@ int flash_sector(unsigned int sector, const u_int8_t *data, unsigned int len)  } -void flash_init(void) -{ -	unsigned int fmcn = AT91F_MC_EFC_ComputerFMCN(48000000); - -	AT91F_MC_EFC_CfgModeReg(ff, fmcn << 16 | AT91C_MC_FWS_3FWS | -				AT91C_MC_FRDY | AT91C_MC_LOCKE |  -				AT91C_MC_PROGE); -	AT91F_AIC_EnableIt(); - -}  | 
