/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See the file COPYING * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Copyright (C) 2010 Frank van Maarseveen */ #include #include #include "common.h" #include "hardware.h" #include "types.h" #include "bits.h" #include "timer0.h" #include "receive.h" #include "spm.h" #include "boot.h" #define FLASH_BAD 0xff static uint8_t flash_state(void) { EEAR = EEAR_FLASH_STATE; BIT_SET(EECR, EERE); return EEDR; } __attribute__((noinline)) static void set_flash_state(uint8_t state) { uint8_t sreg; EEAR = EEAR_FLASH_STATE; EEDR = state; saveicli(sreg); BIT_SET(EECR, EEMPE); BIT_SET(EECR, EEPE); restorei(sreg); while (BIT_TST(EECR, EEPE)) ; } static void reprogram(void) { uint32_t start; int8_t len; struct spm *spm; bool_t flashing; /* * Wait at most 10 seconds for incoming data. After the first succesful SPM * we're going to wait indefinately until all pages have been reprogrammed. */ EECR = 0; /* eeprom: atomic byte programming */ flashing = false; start = t0_ms(0); while (flash_state() == FLASH_BAD || t0_ms(start) < 10000) { len = rx_packet(); if (len < 0) { OUTPUT(LED); SET(LED); continue; } if (len == sizeof (struct spm)) { spm = (struct spm *)rx_buffer; if (!flashing) { flashing = spm_setup(spm); if (flashing) set_flash_state(FLASH_BAD); } if (flashing) { OUTPUT(LED); SET(LED); spm_program(spm); /* may take 4-5 ms */ if (spm_complete()) { set_flash_state((uint8_t)~FLASH_BAD); t0_mswait(3000); /* LED on until application switches it off */ return; } } } } CLR(LED); } __attribute__((noreturn)) void main(void) { hw_init(); if (BIT_TST(MCUSR, PORF) || !MCUSR) { /* * Initialize, enable interrupts and let power stabilize. Then reprogram. * Program _must_ clear PORF when using watchdog timer reset. */ t0_init(); do_sei(); t0_mswait(100); reprogram(); t0_cleanup(); SREG = 0; /* must disable interrupts */ } exec_program(); } // 0x00, RESET_vect ISR_PROGRAM(0x01, INT0_vect); // External Interrupt Request 0 ISR_PROGRAM(0x02, PCINT0_vect); // Pin Change Interrupt Request 0 ISR_PROGRAM(0x03, TIMER1_COMPA_vect); // Timer/Counter1 Compare Match A ISR_PROGRAM(0x04, TIMER1_OVF_vect); // Timer/Counter1 Overflow ISR_PROGRAM(0x05, TIMER0_OVF_vect); // Timer/Counter0 Overflow ISR_PROGRAM(0x06, EE_RDY_vect); // EEPROM Ready ISR_PROGRAM(0x07, ANA_COMP_vect); // Analog Comparator ISR_PROGRAM(0x08, ADC_vect); // ADC Conversion Complete ISR_PROGRAM(0x09, TIMER1_COMPB_vect); // Timer/Counter1 Compare Match B ISR_SHARED(0x0A, TIMER0_COMPA_vect); // Timer/Counter0 Compare Match A ISR_PROGRAM(0x0B, TIMER0_COMPB_vect); // Timer/Counter0 Compare Match B ISR_PROGRAM(0x0C, WDT_vect); // Watchdog Time-out ISR_PROGRAM(0x0D, USI_START_vect); // USI START ISR_PROGRAM(0x0E, USI_OVF_vect); // USI Overflow