/* Name: main.c * Project: USBaspLoader * Author: Christian Starkjohann * Creation Date: 2007-12-08 * Tabsize: 4 * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH * License: GNU GPL v2 (see License.txt) * This Revision: $Id: main.c 730 2009-03-20 09:05:11Z cs $ */ #include #include #include #include #include #include #include #include static void leaveBootloader() __attribute__((__noreturn__)); #include "bootloaderconfig.h" #include "usbdrv/usbdrv.c" /* ------------------------------------------------------------------------ */ /* Request constants used by USBasp */ #define USBASP_FUNC_CONNECT 1 #define USBASP_FUNC_DISCONNECT 2 #define USBASP_FUNC_TRANSMIT 3 #define USBASP_FUNC_READFLASH 4 #define USBASP_FUNC_ENABLEPROG 5 #define USBASP_FUNC_WRITEFLASH 6 #define USBASP_FUNC_READEEPROM 7 #define USBASP_FUNC_WRITEEEPROM 8 #define USBASP_FUNC_SETLONGADDRESS 9 /* ------------------------------------------------------------------------ */ #ifndef ulong # define ulong unsigned long #endif #ifndef uint # define uint unsigned int #endif /* defaults if not in config file: */ #ifndef HAVE_EEPROM_PAGED_ACCESS # define HAVE_EEPROM_PAGED_ACCESS 0 #endif #ifndef HAVE_EEPROM_BYTE_ACCESS # define HAVE_EEPROM_BYTE_ACCESS 0 #endif #ifndef BOOTLOADER_CAN_EXIT # define BOOTLOADER_CAN_EXIT 0 #endif /* allow compatibility with avrusbboot's bootloaderconfig.h: */ #ifdef BOOTLOADER_INIT # define bootLoaderInit() BOOTLOADER_INIT # define bootLoaderExit() #endif #ifdef BOOTLOADER_CONDITION # define bootLoaderCondition() BOOTLOADER_CONDITION #endif /* device compatibility: */ #ifndef GICR /* ATMega*8 don't have GICR, use MCUCR instead */ # define GICR MCUCR #endif /* ------------------------------------------------------------------------ */ #if (FLASHEND) > 0xffff /* we need long addressing */ # define CURRENT_ADDRESS currentAddress.l # define addr_t ulong #else # define CURRENT_ADDRESS currentAddress.w[0] # define addr_t uint #endif typedef union longConverter{ addr_t l; uint w[sizeof(addr_t)/2]; uchar b[sizeof(addr_t)]; }longConverter_t; static uchar requestBootLoaderExit; static longConverter_t currentAddress; /* in bytes */ static uchar bytesRemaining; static uchar isLastPage; #if HAVE_EEPROM_PAGED_ACCESS static uchar currentRequest; #else static const uchar currentRequest = 0; #endif static const uchar signatureBytes[4] = { #ifdef SIGNATURE_BYTES SIGNATURE_BYTES #elif defined (__AVR_ATmega8__) || defined (__AVR_ATmega8HVA__) 0x1e, 0x93, 0x07, 0 #elif defined (__AVR_ATmega48__) || defined (__AVR_ATmega48P__) 0x1e, 0x92, 0x05, 0 #elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega88P__) 0x1e, 0x93, 0x0a, 0 #elif defined (__AVR_ATmega168__) || defined (__AVR_ATmega168P__) 0x1e, 0x94, 0x06, 0 #elif defined (__AVR_ATmega328P__) 0x1e, 0x95, 0x0f, 0 #else # error "Device signature is not known, please edit main.c!" #endif }; /* ------------------------------------------------------------------------ */ static void (*nullVector)(void) __attribute__((__noreturn__)); static void leaveBootloader() { #define OBDEBUG 1DBG1(0x01, 0, 0); bootLoaderExit(); cli(); USB_INTR_ENABLE = 0; USB_INTR_CFG = 0; /* also reset config bits */ GICR = (1 << IVCE); /* enable change of interrupt vectors */ GICR = (0 << IVSEL); /* move interrupts to application flash section */ /* We must go through a global function pointer variable instead of writing * ((void (*)(void))0)(); * because the compiler optimizes a constant 0 to "rcall 0" which is not * handled correctly by the assembler. */ nullVector(); } /* ------------------------------------------------------------------------ */ uchar usbFunctionSetup(uchar data[8]) { usbRequest_t *rq = (void *)data; uchar len = 0; static uchar replyBuffer[4]; usbMsgPtr = replyBuffer; if(rq->bRequest == USBASP_FUNC_TRANSMIT){ /* emulate parts of ISP protocol */ uchar rval = 0; usbWord_t address; address.bytes[1] = rq->wValue.bytes[1]; address.bytes[0] = rq->wIndex.bytes[0]; if(rq->wValue.bytes[0] == 0x30){ /* read signature */ rval = rq->wIndex.bytes[0] & 3; rval = signatureBytes[rval]; #if HAVE_EEPROM_BYTE_ACCESS }else if(rq->wValue.bytes[0] == 0xa0){ /* read EEPROM byte */ rval = eeprom_read_byte((void *)address.word); }else if(rq->wValue.bytes[0] == 0xc0){ /* write EEPROM byte */ eeprom_write_byte((void *)address.word, rq->wIndex.bytes[1]); #endif #if HAVE_CHIP_ERASE }else if(rq->wValue.bytes[0] == 0xac && rq->wValue.bytes[1] == 0x80){ /* chip erase */ addr_t addr; for(addr = 0; addr < FLASHEND + 1 - 2048; addr += SPM_PAGESIZE) { /* wait and erase page */ //DBG1(0x33, 0, 0); # ifndef NO_FLASH_WRITE boot_spm_busy_wait(); cli(); boot_page_erase(addr); sei(); # endif } #endif }else{ /* ignore all others, return default value == 0 */ } replyBuffer[3] = rval; len = 4; }else if(rq->bRequest == USBASP_FUNC_ENABLEPROG){ /* replyBuffer[0] = 0; is never touched and thus always 0 which means success */ len = 1; }else if(rq->bRequest >= USBASP_FUNC_READFLASH && rq->bRequest <= USBASP_FUNC_SETLONGADDRESS){ currentAddress.w[0] = rq->wValue.word; if(rq->bRequest == USBASP_FUNC_SETLONGADDRESS){ #if (FLASHEND) > 0xffff currentAddress.w[1] = rq->wIndex.word; #endif }else{ bytesRemaining = rq->wLength.bytes[0]; /* if(rq->bRequest == USBASP_FUNC_WRITEFLASH) only evaluated during writeFlash anyway */ isLastPage = rq->wIndex.bytes[1] & 0x02; #if HAVE_EEPROM_PAGED_ACCESS currentRequest = rq->bRequest; #endif len = 0xff; /* hand over to usbFunctionRead() / usbFunctionWrite() */ } #if BOOTLOADER_CAN_EXIT }else if(rq->bRequest == USBASP_FUNC_DISCONNECT){ requestBootLoaderExit = 1; /* allow proper shutdown/close of connection */ #endif }else{ /* ignore: USBASP_FUNC_CONNECT */ } return len; } uchar usbFunctionWrite(uchar *data, uchar len) { uchar isLast; //DBG1(0x31, (void *)¤tAddress.l, 4); if(len > bytesRemaining) len = bytesRemaining; bytesRemaining -= len; isLast = bytesRemaining == 0; if(currentRequest >= USBASP_FUNC_READEEPROM){ uchar i; for(i = 0; i < len; i++){ eeprom_write_byte((void *)(currentAddress.w[0]++), *data++); } }else{ uchar i; for(i = 0; i < len;){ #if !HAVE_CHIP_ERASE if((currentAddress.w[0] & (SPM_PAGESIZE - 1)) == 0){ /* if page start: erase */ //DBG1(0x33, 0, 0); # ifndef NO_FLASH_WRITE cli(); boot_page_erase(CURRENT_ADDRESS); /* erase page */ sei(); boot_spm_busy_wait(); /* wait until page is erased */ # endif } #endif i += 2; //DBG1(0x32, 0, 0); cli(); boot_page_fill(CURRENT_ADDRESS, *(short *)data); sei(); CURRENT_ADDRESS += 2; data += 2; /* write page when we cross page boundary or we have the last partial page */ if((currentAddress.w[0] & (SPM_PAGESIZE - 1)) == 0 || (isLast && i >= len && isLastPage)){ //DBG1(0x34, 0, 0); #ifndef NO_FLASH_WRITE cli(); boot_page_write(CURRENT_ADDRESS - 2); sei(); boot_spm_busy_wait(); cli(); boot_rww_enable(); sei(); #endif } } //DBG1(0x35, (void *)¤tAddress.l, 4); } return isLast; } uchar usbFunctionRead(uchar *data, uchar len) { uchar i; if(len > bytesRemaining) len = bytesRemaining; bytesRemaining -= len; for(i = 0; i < len; i++){ if(currentRequest >= USBASP_FUNC_READEEPROM){ *data = eeprom_read_byte((void *)currentAddress.w[0]); }else{ *data = pgm_read_byte((void *)CURRENT_ADDRESS); } data++; CURRENT_ADDRESS++; } return len; } /* ------------------------------------------------------------------------ */ static void initForUsbConnectivity(void) { uchar i = 0; usbInit(); /* enforce USB re-enumerate: */ #if 0 usbDeviceDisconnect(); /* do this while interrupts are disabled */ while(--i){ /* fake USB disconnect for > 250 ms */ wdt_reset(); _delay_ms(1); } #endif usbDeviceConnect(); sei(); } int main(void) { /* initialize */ wdt_disable(); /* main app may have enabled watchdog */ bootLoaderInit(); odDebugInit(); DBG1(0x00, 0, 0); #ifndef NO_FLASH_WRITE GICR = (1 << IVCE); /* enable change of interrupt vectors */ GICR = (1 << IVSEL); /* move interrupts to boot flash section */ #endif if(bootLoaderCondition()){ #if BOOTLOADER_CAN_EXIT uchar i = 0, j = 0; #endif initForUsbConnectivity(); do{ usbPoll(); #if BOOTLOADER_CAN_EXIT if(requestBootLoaderExit){ if(--i == 0){ if(--j == 0) break; } } #endif }while(bootLoaderCondition()); /* main event loop */ } leaveBootloader(); return 0; } /* ------------------------------------------------------------------------ */