funkboot/funkboot.c

456 lines
12 KiB
C
Raw Normal View History

2014-12-26 16:19:29 +01:00
/***************************************************************************
* Copyright (C) 11/2014 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License, *
* *
* 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. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/boot.h>
#include <avr/pgmspace.h>
#include <string.h>
#include "target.h"
#include "rfm12.h"
/* *********************************************************************** */
#define BOOTWAIT_EXPIRED 0x0000
#define BOOTWAIT_INTERRUPTED 0xFFFF
#define MSG_TYPE_REQUEST 0x00 /* master -> slave req */
#define MSG_TYPE_CONFIRMATION 0x40 /* master -> slave rsp */
#define MSG_TYPE_INDICATION 0x80 /* slave -> master req */
#define MSG_TYPE_RESPONSE 0xC0 /* slave -> master rsp */
#define MSG_TYPE_MASK 0xC0
#define MSG_CMD_MASK 0x3F
#define MSG_CMD_SWITCHAPP_REQUEST (MSG_TYPE_REQUEST | 0x20)
#define MSG_CMD_SWITCHAPP_RESPONSE (MSG_TYPE_RESPONSE | 0x20)
#define MSG_CMD_VERSION_REQUEST (MSG_TYPE_REQUEST | 0x21)
#define MSG_CMD_VERSION_RESPONSE (MSG_TYPE_RESPONSE | 0x21)
#define MSG_CMD_CHIPINFO_REQUEST (MSG_TYPE_REQUEST | 0x22)
#define MSG_CMD_CHIPINFO_RESPONSE (MSG_TYPE_RESPONSE | 0x22)
#define MSG_CMD_READ_REQUEST (MSG_TYPE_REQUEST | 0x23)
#define MSG_CMD_READ_RESPONSE (MSG_TYPE_RESPONSE | 0x23)
#define MSG_CMD_WRITE_REQUEST (MSG_TYPE_REQUEST | 0x24)
#define MSG_CMD_WRITE_RESPONSE (MSG_TYPE_RESPONSE | 0x24)
#define CAUSE_SUCCESS 0x00
#define CAUSE_NOT_SUPPORTED 0xF0
#define CAUSE_INVALID_PARAMETER 0xF1
#define CAUSE_UNSPECIFIED_ERROR 0xFF
#define BOOTMODE_BOOTLOADER 0x00
#define BOOTMODE_APPLICATION 0x80
#define MEMTYPE_FLASH 0x01
#define MEMTYPE_EEPROM 0x02
/* *********************************************************************** */
struct bootloader_msg
{
uint8_t command;
uint8_t seqnum;
uint8_t cause;
union {
struct {
uint8_t app;
} switchapp;
struct {
uint8_t data[16];
} version;
struct {
uint8_t data[8];
} chipinfo;
struct {
uint16_t address;
uint8_t mem_type;
uint8_t size;
} read_req;
struct {
uint8_t data[32];
} read_rsp;
struct {
uint16_t address;
uint8_t mem_type;
uint8_t size;
uint8_t data[32];
} write_req;
} p;
};
/* *********************************************************************** */
#define LED_RX 0
#define LED_TX 1
static uint8_t led_timer[2];
volatile static uint8_t clock_tick;
const static uint8_t version_info[16] = VERSION_STRING;
const static uint8_t chip_info[8] = {
SIGNATURE_BYTES,
SPM_PAGESIZE,
((BOOTLOADER_START) >> 8) & 0xFF,
(BOOTLOADER_START) & 0xFF,
((E2END +1) >> 8 & 0xFF),
(E2END +1) & 0xFF
};
/* *********************************************************************** */
static void read_flash(uint8_t *data, uint16_t address, uint8_t size)
{
while (size--)
{
*data++ = pgm_read_byte_near(address++);
}
} /* read_flash_byte */
static void write_flash(uint8_t *data, uint16_t address, uint8_t size)
{
static uint16_t pagestart;
static uint8_t pagesize;
if ((address & (SPM_PAGESIZE -1)) == 0x00)
{
pagestart = address;
pagesize = SPM_PAGESIZE;
boot_page_erase(pagestart);
boot_spm_busy_wait();
}
while (size && pagesize)
{
uint16_t dataword;
dataword = (*data++);
dataword |= (*data++) << 8;
boot_page_fill(address, dataword);
address += 2;
pagesize -= 2;
size -= 2;
}
if (pagesize == 0)
{
boot_page_write(pagestart);
boot_spm_busy_wait();
boot_rww_enable();
}
} /* write_flash */
static void read_eeprom(uint8_t *data, uint16_t address, uint8_t size)
{
while (size--)
{
EEARL = address;
EEARH = (address >> 8);
EECR |= (1<<EERE);
address++;
*data++ = EEDR;
}
} /* read_eeprom */
static void write_eeprom(uint8_t *data, uint16_t address, uint8_t size)
{
while (size--)
{
EEARL = address;
EEARH = (address >> 8);
EEDR = *data++;
address++;
cli();
#if defined (__AVR_ATmega168__)
EECR |= (1<<EEMPE);
EECR |= (1<<EEPE);
#endif
sei();
eeprom_busy_wait();
}
} /* write_eeprom */
ISR(TIMER0_OVF_vect)
{
TCNT0 = TIMER_RELOAD;
clock_tick = 1;
} /* TIMER0_OVF_vect */
static void (*jump_to_app)(void) __attribute__ ((noreturn)) = 0x0000;
#if defined(__AVR_ATmega168__)
/*
* For newer devices the watchdog timer remains active even after a
* system reset. So disable it as soon as possible.
* automagically called on startup
*/
void disable_wdt_timer(void) __attribute__((naked, section(".init3")));
void disable_wdt_timer(void)
{
MCUSR = 0;
WDTCSR = (1<<WDCE) | (1<<WDE);
WDTCSR = (0<<WDE);
} /* disable_wdt_timer */
#endif
int main(void) __attribute__ ((noreturn));
int main(void)
{
/* init LEDs */
LED_INIT();
#if defined (__AVR_ATmega168__)
/* move interrupt-vectors to bootloader */
MCUCR = (1<<IVCE);
MCUCR = (1<<IVSEL);
/* timer0, FCPU/64, overflow interrupt */
TCCR0B = (1<<CS01) | (1<<CS00);
TIMSK0 = (1<<TOIE0);
#endif
rfm12_init(RFM12_ADDRESS);
sei();
uint16_t boot_timeout = TIMEOUT;
while (1)
{
if (clock_tick == 0)
continue;
clock_tick = 0;
if (led_timer[LED_RX] > 0)
{
led_timer[LED_RX]--;
LED_GN_ON();
}
else
{
LED_GN_OFF();
}
if (led_timer[LED_TX] > 0)
{
led_timer[LED_TX]--;
LED_RT_ON();
}
else
{
LED_RT_OFF();
}
/* do periodic work (wait for 5 ticks silence before start TX) */
rfm12_tick(5);
/* get TX buffer */
struct rfm12_packet *rsp_pkt = rfm12_get_txpkt();
if (boot_timeout == BOOTWAIT_EXPIRED)
{
/* timeout elapsed and TX buffer available (tx completed) -> start application */
if (rsp_pkt != NULL)
{
break;
}
}
else if (boot_timeout != BOOTWAIT_INTERRUPTED)
{
boot_timeout--;
}
if ((boot_timeout & 0x3F) == 0)
{
led_timer[LED_TX] = 5;
}
/* get RX data */
struct rfm12_packet *req_pkt = rfm12_get_rxpkt();
if (req_pkt == NULL)
{
/* no data available */
continue;
}
else
{
led_timer[LED_RX] = 5;
/* no tx buffer available, ignore request */
if (rsp_pkt == NULL)
{
rfm12_clear_rx();
continue;
}
}
/* stay in bootloader */
boot_timeout = BOOTWAIT_INTERRUPTED;
struct bootloader_msg *req_msg = (struct bootloader_msg *)req_pkt->data;
struct bootloader_msg *rsp_msg = (struct bootloader_msg *)rsp_pkt->data;
/* retransmitted request -> retransmit response */
if ((req_pkt->source_address == rsp_pkt->dest_address) &&
((req_msg->command & MSG_CMD_MASK) == (rsp_msg->command & MSG_CMD_MASK)) &&
(req_msg->seqnum == rsp_msg->seqnum)
)
{
/* RX packet no longer needed */
rfm12_clear_rx();
/* transmit response */
if (rfm12_start_tx())
{
led_timer[LED_TX] = 5;
}
continue;
}
rsp_pkt->dest_address = req_pkt->source_address;
rsp_pkt->data_length = 3;
rsp_msg->command = req_msg->command | MSG_TYPE_RESPONSE;
rsp_msg->seqnum = req_msg->seqnum;
rsp_msg->cause = CAUSE_SUCCESS;
switch (req_msg->command)
{
case MSG_CMD_SWITCHAPP_REQUEST:
if (req_msg->p.switchapp.app == BOOTMODE_APPLICATION)
{
boot_timeout = BOOTWAIT_EXPIRED;
}
else if (req_msg->p.switchapp.app != BOOTMODE_BOOTLOADER)
{
rsp_msg->cause = CAUSE_INVALID_PARAMETER;
}
break;
case MSG_CMD_VERSION_REQUEST:
memcpy(rsp_msg->p.version.data, version_info, sizeof(rsp_msg->p.version.data));
rsp_pkt->data_length += sizeof(rsp_msg->p.version.data);
break;
case MSG_CMD_CHIPINFO_REQUEST:
memcpy(rsp_msg->p.chipinfo.data, chip_info, sizeof(rsp_msg->p.chipinfo.data));
rsp_pkt->data_length += sizeof(rsp_msg->p.chipinfo.data);
break;
case MSG_CMD_READ_REQUEST:
if (req_msg->p.read_req.mem_type == MEMTYPE_FLASH)
{
read_flash(rsp_msg->p.read_rsp.data,
req_msg->p.read_req.address,
req_msg->p.read_req.size);
rsp_pkt->data_length += req_msg->p.read_req.size;
}
else if (req_msg->p.read_req.mem_type == MEMTYPE_EEPROM)
{
read_eeprom(rsp_msg->p.read_rsp.data,
req_msg->p.read_req.address,
req_msg->p.read_req.size);
rsp_pkt->data_length += req_msg->p.read_req.size;
}
else
{
rsp_msg->cause = CAUSE_INVALID_PARAMETER;
}
break;
case MSG_CMD_WRITE_REQUEST:
if (req_msg->p.write_req.mem_type == MEMTYPE_FLASH)
{
write_flash(req_msg->p.write_req.data,
req_msg->p.write_req.address,
req_msg->p.write_req.size);
}
else if (req_msg->p.write_req.mem_type == MEMTYPE_EEPROM)
{
write_eeprom(req_msg->p.write_req.data,
req_msg->p.write_req.address,
req_msg->p.write_req.size);
}
else
{
rsp_msg->cause = CAUSE_INVALID_PARAMETER;
}
break;
default:
rsp_msg->cause = CAUSE_NOT_SUPPORTED;
break;
}
/* RX packet no longer needed */
rfm12_clear_rx();
/* transmit response */
if (rfm12_start_tx())
{
led_timer[LED_TX] = 5;
}
}
cli();
#if defined (__AVR_ATmega168__)
/* disable timer0 */
TIMSK0 = 0x00;
TCCR0B = 0x00;
/* move interrupt vectors back to application */
MCUCR = (1<<IVCE);
MCUCR = (0<<IVSEL);
#endif
LED_OFF();
jump_to_app();
} /* main */