327 lines
9.7 KiB
C
327 lines
9.7 KiB
C
/* 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$
|
|
*/
|
|
|
|
#include <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
#include <avr/pgmspace.h>
|
|
#include <avr/wdt.h>
|
|
#include <avr/boot.h>
|
|
#include <avr/eeprom.h>
|
|
#include <util/delay.h>
|
|
#include <string.h>
|
|
|
|
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()
|
|
{
|
|
DBG1(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 = (usbMsgPtr_t)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: */
|
|
usbDeviceDisconnect(); /* do this while interrupts are disabled */
|
|
while(--i){ /* fake USB disconnect for > 250 ms */
|
|
wdt_reset();
|
|
_delay_ms(1);
|
|
}
|
|
usbDeviceConnect();
|
|
sei();
|
|
}
|
|
|
|
int __attribute__((noreturn)) 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()){
|
|
uchar i = 0, j = 0;
|
|
initForUsbConnectivity();
|
|
do{
|
|
usbPoll();
|
|
#if BOOTLOADER_CAN_EXIT
|
|
if(requestBootLoaderExit){
|
|
if(--i == 0){
|
|
if(--j == 0)
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}while(bootLoaderCondition()); /* main event loop */
|
|
}
|
|
leaveBootloader();
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|