mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 22:52:45 +01:00
1191 lines
37 KiB
C
1191 lines
37 KiB
C
/*
|
|
* LPC 2000 Loader, http://www.pjrc.com/arm/lpc2k_pgm
|
|
* Copyright (c) 2004, PJRC.COM, LLC, <paul@pjrc.com>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/* If this code fails to build, please provide at least the following
|
|
* information when requesting (free) technical support.
|
|
*
|
|
* 1: Complete copy of all messages during the build.
|
|
* 2: Output of "gtk-config --version"
|
|
* 3: Output of "gtk-config --libs"
|
|
* 4: Output of "gtk-config --cflags"
|
|
* 5: Output of "uname -a"
|
|
* 6: Version of GTK installed... eg, type: ls -l /lib/libgtk*
|
|
* 7: Other info... which linux distribution, version, other software
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
|
|
#include "lpc2k_pgm.h"
|
|
#include "download.h"
|
|
#include "serial.h"
|
|
#include "ihex.h"
|
|
#include "uuencode.h"
|
|
//#include "gui.h"
|
|
#include "chipinfo.h"
|
|
#include "boot.h"
|
|
#include "control_2xxx.h"
|
|
|
|
|
|
// This will cause all bytes send and received to be printed as hex.
|
|
// It's a LOT of extra output, but useful for difficult debugging
|
|
// of what's _really_ being sent and received.
|
|
//#define PRINT_TX_RX_BYTES
|
|
|
|
|
|
static void download_main(int event);
|
|
static void xmit_cmd(const char *cmd, int max_time);
|
|
static void mk_valid_code_vector(void);
|
|
static unsigned int sum(unsigned char *data, int num);
|
|
|
|
|
|
static int state = 0;
|
|
static int reboot_only = 0;
|
|
static char expected_echo_buf[4096];
|
|
static char *expected_echo_ptr = NULL;
|
|
static char parsed_response_buf[4096];
|
|
static char *parsed_response_ptr = NULL;
|
|
static int response_timer = 0;
|
|
|
|
extern int programming_done;
|
|
extern int done_program(int);
|
|
|
|
|
|
char *port_name = "/dev/ttyUSB1";
|
|
char *file_name = "";
|
|
char *crystal = "16";
|
|
|
|
/****************************************************************/
|
|
/* */
|
|
/* Main Download Section */
|
|
/* */
|
|
/****************************************************************/
|
|
|
|
// possible states
|
|
#define SYNC_1 1
|
|
#define SYNC_2 2
|
|
#define SYNC_3 3
|
|
#define CHIP_ID 4
|
|
#define UNLOCK 5
|
|
#define BLANK_CHECK_SECTOR 6
|
|
#define ERASE_PREPARE 7
|
|
#define ERASE_SECTOR 8
|
|
#define DOWNLOAD_CODE 9
|
|
#define XMIT_DATA 10
|
|
#define XMIT_CKSUM 11
|
|
#define WRITE_PREPARE 12
|
|
#define WRITE_SECTOR 13
|
|
#define BOOT_HARD 14
|
|
#define BOOT_SOFT 15
|
|
#define BOOT_XMIT_DATA 16
|
|
#define BOOT_XMIT_CKSUM 17
|
|
#define BOOT_RUN_CODE 18
|
|
|
|
|
|
// possible input values for "event"
|
|
#define BEGIN 1
|
|
#define RESPONSE 2
|
|
#define TIMEOUT 3
|
|
#define RETRY 4
|
|
|
|
|
|
|
|
|
|
int download_begin(char *file)
|
|
{
|
|
int r;
|
|
|
|
file_name = file;
|
|
|
|
printf("\r\nEntering Bootloader Mode\r\n");
|
|
hard_reset_to_bootloader();
|
|
printf("Read \"%s\"", file_name);
|
|
r = read_intel_hex(file_name);
|
|
|
|
if (r < 0) {
|
|
/* abort on ioerror */
|
|
return 0;
|
|
}
|
|
|
|
printf(": %d bytes\r\n", r);
|
|
mk_valid_code_vector();
|
|
state = SYNC_1;
|
|
reboot_only = 0;
|
|
download_main(BEGIN);
|
|
return 1;
|
|
}
|
|
|
|
|
|
void soft_reboot_begin(void)
|
|
{
|
|
printf("\r\nEntering Bootloader Mode\r\n");
|
|
hard_reset_to_bootloader();
|
|
state = SYNC_1;
|
|
reboot_only = 1;
|
|
download_main(BEGIN);
|
|
}
|
|
|
|
static void mk_valid_code_vector(void)
|
|
{
|
|
unsigned char b[4];
|
|
unsigned int sum = 0;
|
|
int addr;
|
|
|
|
for (addr = 0; addr < 0x20; addr += 4) {
|
|
if (addr != 0x14) {
|
|
get_ihex_data(addr, 4, b);
|
|
sum += (b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24));
|
|
}
|
|
}
|
|
|
|
sum ^= 0xFFFFFFFF;
|
|
sum++;
|
|
b[0] = (sum >> 0) & 255;
|
|
b[1] = (sum >> 8) & 255;
|
|
b[2] = (sum >> 16) & 255;
|
|
b[3] = (sum >> 24) & 255;
|
|
put_ihex_data(0x14, 4, b);
|
|
}
|
|
|
|
|
|
static unsigned int sum(unsigned char *data, int num)
|
|
{
|
|
unsigned int sum = 0;
|
|
|
|
while (num > 0) {
|
|
sum += *data++;
|
|
num--;
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|
|
|
|
static int num_lines(const char *buf)
|
|
{
|
|
const char *p;
|
|
int count = 0;
|
|
|
|
p = buf;
|
|
|
|
while (p != NULL) {
|
|
p = strstr(p, "\r\n");
|
|
|
|
if (p != NULL) {
|
|
count++;
|
|
p += 2;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
void trim_crlf(char *str)
|
|
{
|
|
char *p;
|
|
p = strstr(str, "\r\n");
|
|
|
|
if (p != NULL) {
|
|
*p = '\0';
|
|
}
|
|
}
|
|
|
|
void copy_boot_code_to_memory(struct chip_info_struct *chip)
|
|
{
|
|
int i;
|
|
unsigned char c[4];
|
|
|
|
for (i = 0; i < chip->bootprog[0]; i++) {
|
|
c[3] = (chip->bootprog[i + 1] >> 24) & 255;
|
|
c[2] = (chip->bootprog[i + 1] >> 16) & 255;
|
|
c[1] = (chip->bootprog[i + 1] >> 8) & 255;
|
|
c[0] = (chip->bootprog[i + 1]) & 255;
|
|
put_ihex_data(i * 4, 4, c);
|
|
}
|
|
}
|
|
|
|
|
|
#define NO_SYNC_ERR "\r\n\
|
|
ERROR: Unable to sync to baud rate.\r\n\
|
|
This probably means the LPC2xxx chip is not connected\r\n\
|
|
or it is not being reset, or P0.14 is not low after\r\n\
|
|
reset to cause it to enter the bootloader mode.\r\n\r\n\
|
|
Please check the serial port connection, make sure\r\n\
|
|
pin P0.14 is low (or tied to RTS via RS-232 level\r\n\
|
|
translator), and the chip has been reset (or reset\r\n\
|
|
is tied to DTR via RS-232 level translator).\r\n"
|
|
|
|
#define UNKNOWN_CHIP_ERROR "\r\n\
|
|
Unknown chip ID: \"%s\".\r\n\r\n\
|
|
Perhaps you have a new Philips LPC chip which does not\r\n\
|
|
have its ID string and sector map defined in this program?\r\n\
|
|
Please contact paul@pjrc.com. Please include an exact copy\r\n\
|
|
of this message and any info about the chip and other\r\n\
|
|
hardware you may be using. Thanks :-)\r\n"
|
|
|
|
|
|
static void download_main(int event)
|
|
{
|
|
char buf[4096 + 32];
|
|
unsigned char bytes[256];
|
|
double xtal;
|
|
int n;
|
|
static unsigned int cksum;
|
|
static int retry = 0;
|
|
static int sector; // current sector we're doing
|
|
static int sector_offset;
|
|
static struct chip_info_struct *chip; // which chip
|
|
static int current_addr, num_to_xmit, linecount;
|
|
|
|
|
|
while (1) {
|
|
switch (state) {
|
|
case SYNC_1:
|
|
switch (event) {
|
|
case BEGIN:
|
|
printf("Attempting baud sync");
|
|
retry = 0;
|
|
|
|
case RETRY:
|
|
printf(".");
|
|
fflush(stdout);
|
|
xmit_cmd("?", 2);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (strcmp(parsed_response_buf, "Synchronized\r\n") == 0) {
|
|
//printf("response: sync'd\n");
|
|
state = SYNC_2;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "?") == 0) {
|
|
//printf("response: echo only\n");
|
|
retry++;
|
|
|
|
if (retry > 150) {
|
|
download_cancel(NO_SYNC_ERR);
|
|
return;
|
|
}
|
|
|
|
event = RETRY;
|
|
usleep(30000);
|
|
break;
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "Unexpected response to sync, \"%s\"",
|
|
parsed_response_buf);
|
|
download_cancel(buf);
|
|
return;
|
|
|
|
case TIMEOUT:
|
|
if (retry < 100) {
|
|
retry++;
|
|
event = RETRY;
|
|
break;
|
|
}
|
|
|
|
download_cancel(NO_SYNC_ERR);
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case SYNC_2:
|
|
switch (event) {
|
|
case BEGIN:
|
|
xmit_cmd("Synchronized\r\n", 3);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) != 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "OK\r\n") == 0) {
|
|
state = SYNC_3;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
else {
|
|
snprintf(buf, sizeof(buf), "Unable to complete baud sync, %s",
|
|
parsed_response_buf);
|
|
download_cancel(buf);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response to complete baud sync");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case SYNC_3:
|
|
switch (event) {
|
|
case BEGIN:
|
|
if (sscanf(crystal, "%20lf", &xtal) != 1) {
|
|
printf("\r\n");
|
|
download_cancel("Crystal frequency is required for 3rd step of baud rate sync");
|
|
return;
|
|
}
|
|
|
|
if (xtal < 10.0 || xtal > 25.0) {
|
|
printf("\r\n");
|
|
printf("Warning: crystal frequency out of range (10.0 to 25.0), continuing anyway! (hope you know what you're doing)\r\n");
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "%d\r\n", (int)(xtal * 1000.0 + 0.5));
|
|
xmit_cmd(buf, 3);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) != 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "OK\r\n") == 0) {
|
|
printf("Baud sync successful\r\n");
|
|
state = CHIP_ID;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
else {
|
|
snprintf(buf, sizeof(buf), "wrong response to crystal: %s",
|
|
parsed_response_buf);
|
|
download_cancel(buf);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response to crystal speed");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case CHIP_ID:
|
|
switch (event) {
|
|
case BEGIN:
|
|
xmit_cmd("J\r\n", 3);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) < 2) {
|
|
return;
|
|
}
|
|
|
|
if (strncmp(parsed_response_buf, "0\r\n", 3) == 0) {
|
|
trim_crlf(parsed_response_buf + 3);
|
|
|
|
for (chip = chip_info; chip->part_number != NULL; chip++) {
|
|
if (strcmp(parsed_response_buf + 3, chip->id_string) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (chip->part_number == NULL) {
|
|
snprintf(buf, sizeof(buf), UNKNOWN_CHIP_ERROR,
|
|
parsed_response_buf + 3);
|
|
download_cancel(buf);
|
|
break;
|
|
}
|
|
|
|
printf("Found chip: \"%s\"\r\n", chip->part_number);
|
|
//download_cancel("stop here, remove this later");
|
|
state = UNLOCK;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
else {
|
|
snprintf(buf, sizeof(buf), "wrong response to ID: %s",
|
|
parsed_response_buf);
|
|
download_cancel(buf);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response to unlock command");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case UNLOCK:
|
|
switch (event) {
|
|
case BEGIN:
|
|
xmit_cmd("U 23130\r\n", 3);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) != 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "0\r\n") == 0) {
|
|
printf("Device Unlocked\r\n");
|
|
|
|
if (reboot_only) {
|
|
state = BOOT_SOFT;
|
|
}
|
|
else {
|
|
state = BLANK_CHECK_SECTOR;
|
|
printf("Erasing....\r\n");
|
|
sector = 0;
|
|
}
|
|
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
else {
|
|
snprintf(buf, sizeof(buf), "wrong response unlock: %s",
|
|
parsed_response_buf);
|
|
download_cancel(buf);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response to unlock command");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case BLANK_CHECK_SECTOR:
|
|
switch (event) {
|
|
case BEGIN:
|
|
if (sector >= chip->num_sector) {
|
|
printf("Programming....\r\n");
|
|
state = DOWNLOAD_CODE;
|
|
sector = sector_offset = 0;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
|
|
printf(" Sector %2d: ", sector);
|
|
fflush(stdout);
|
|
|
|
if (!bytes_within_range(chip->layout[sector].address,
|
|
chip->layout[sector].address + chip->layout[sector].size - 1)) {
|
|
printf("not used\r\n");
|
|
sector++;
|
|
break;
|
|
}
|
|
|
|
if (sector == 0) {
|
|
// can't blank check sector 0, so always erase it
|
|
state = ERASE_PREPARE;
|
|
break;
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "I %d %d\r\n", sector, sector);
|
|
xmit_cmd(buf, 5);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) == 1 &&
|
|
strcmp(parsed_response_buf, "0\r\n") == 0) {
|
|
printf("already blank\r\n");
|
|
sector++;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
else {
|
|
if (num_lines(parsed_response_buf) < 3) {
|
|
return;
|
|
}
|
|
|
|
state = ERASE_PREPARE;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response to blank check");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ERASE_PREPARE:
|
|
switch (event) {
|
|
case BEGIN:
|
|
printf("prep, ");
|
|
fflush(stdout);
|
|
snprintf(buf, sizeof(buf), "P %d %d\r\n", sector, sector);
|
|
xmit_cmd(buf, 8);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) != 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "0\r\n") == 0) {
|
|
state = ERASE_SECTOR;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
else {
|
|
download_cancel("Unable to prep for write");
|
|
return;
|
|
}
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ERASE_SECTOR:
|
|
switch (event) {
|
|
case BEGIN:
|
|
printf("erase... ");
|
|
fflush(stdout);
|
|
snprintf(buf, sizeof(buf), "E %d %d\r\n", sector, sector);
|
|
xmit_cmd(buf, 25);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) < 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "0\r\n") == 0) {
|
|
printf("Ok\r\n");
|
|
sector++;
|
|
state = BLANK_CHECK_SECTOR;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
else {
|
|
printf("Error\r\n");
|
|
download_cancel("Unable to erase flash");
|
|
return;
|
|
}
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DOWNLOAD_CODE:
|
|
switch (event) {
|
|
case BEGIN:
|
|
if (sector >= chip->num_sector) {
|
|
state = BOOT_HARD;
|
|
sector = 0;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
|
|
printf(" Sector %2d (0x%08X-0x%08X): ", sector,
|
|
chip->layout[sector].address + sector_offset,
|
|
chip->layout[sector].address + sector_offset + chip->chunk_size - 1);
|
|
fflush(stdout);
|
|
|
|
if (!bytes_within_range(chip->layout[sector].address + sector_offset,
|
|
chip->layout[sector].address + sector_offset + chip->chunk_size - 1)) {
|
|
printf("not used\r\n");
|
|
sector_offset += chip->chunk_size;
|
|
|
|
if (sector_offset >= chip->layout[sector].size) {
|
|
sector_offset = 0;
|
|
sector++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "W %u %d\r\n", chip->ram_addr, chip->chunk_size);
|
|
xmit_cmd(buf, 4);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) != 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "0\r\n") == 0) {
|
|
state = XMIT_DATA;
|
|
printf("xmit");
|
|
current_addr = chip->layout[sector].address + sector_offset;
|
|
num_to_xmit = chip->chunk_size;
|
|
linecount = 0;
|
|
cksum = 0;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
else {
|
|
download_cancel("can't xmit to ram");
|
|
return;
|
|
}
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case XMIT_DATA:
|
|
switch (event) {
|
|
case BEGIN:
|
|
n = num_to_xmit;
|
|
|
|
if (n > 45) {
|
|
n = 45;
|
|
}
|
|
|
|
get_ihex_data(current_addr, n, bytes);
|
|
cksum += sum(bytes, n);
|
|
uuencode(buf, bytes, n);
|
|
current_addr += n;
|
|
num_to_xmit -= n;
|
|
linecount++;
|
|
xmit_cmd(buf, 5);
|
|
write_serial_port("\r\n", 2);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) != 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "\r\n") == 0) {
|
|
if (linecount >= 20 || num_to_xmit <= 0) {
|
|
state = XMIT_CKSUM;
|
|
}
|
|
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
else {
|
|
download_cancel("data xmit did not echo");
|
|
return;
|
|
}
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case XMIT_CKSUM:
|
|
switch (event) {
|
|
case BEGIN:
|
|
snprintf(buf, sizeof(buf), "%u\r\n", cksum);
|
|
xmit_cmd(buf, 3);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) != 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "OK\r\n") == 0) {
|
|
if (num_to_xmit > 0) {
|
|
printf(".");
|
|
fflush(stdout);
|
|
state = XMIT_DATA;
|
|
event = BEGIN;
|
|
linecount = 0;
|
|
cksum = 0;
|
|
break;
|
|
}
|
|
|
|
state = WRITE_PREPARE;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
else {
|
|
download_cancel("bad checksum");
|
|
return;
|
|
}
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case WRITE_PREPARE:
|
|
switch (event) {
|
|
case BEGIN:
|
|
printf("prep, ");
|
|
fflush(stdout);
|
|
snprintf(buf, sizeof(buf), "P %d %d\r\n", sector, sector);
|
|
xmit_cmd(buf, 5);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) != 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "0\r\n") == 0) {
|
|
state = WRITE_SECTOR;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
else {
|
|
download_cancel("Unable to prep for write");
|
|
return;
|
|
}
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case WRITE_SECTOR:
|
|
switch (event) {
|
|
case BEGIN:
|
|
printf("write, ");
|
|
fflush(stdout);
|
|
snprintf(buf, sizeof(buf), "C %d %u %d\r\n",
|
|
chip->layout[sector].address + sector_offset,
|
|
chip->ram_addr, chip->chunk_size);
|
|
xmit_cmd(buf, 5);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) != 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "0\r\n") == 0) {
|
|
printf("Ok\r\n");
|
|
sector_offset += chip->chunk_size;
|
|
|
|
if (sector_offset >= chip->layout[sector].size) {
|
|
sector_offset = 0;
|
|
sector++;
|
|
}
|
|
|
|
state = DOWNLOAD_CODE;
|
|
event = BEGIN;
|
|
}
|
|
else {
|
|
download_cancel("Unable to prep for write");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case BOOT_HARD:
|
|
// if (chip->bootprog) {
|
|
// state = BOOT_SOFT;
|
|
// break;
|
|
// }
|
|
// else {
|
|
printf("Booting (hardware reset)...\r\n\r\n");
|
|
hard_reset_to_user_code();
|
|
done_program(0);
|
|
return;
|
|
// }
|
|
|
|
case BOOT_SOFT:
|
|
switch (event) {
|
|
case BEGIN:
|
|
printf("Booting (soft jump)...\r\n");
|
|
printf("loading jump code\r\n");
|
|
// would be nice if we could simply jump to the user's code, but
|
|
// Philips didn't think of that. The interrupt vector table stays
|
|
// mapped to the bootloader, so jumping to zero only runs the
|
|
// bootloader again. Instead, we need to download a tiny ARM
|
|
// program that reconfigures the hardware and then jumps to zero.
|
|
//snprintf(buf, sizeof(buf), "G %d A\r\n", 0);
|
|
snprintf(buf, sizeof(buf), "W %u %u\r\n", chip->ram_addr, chip->bootprog[0] * 4);
|
|
xmit_cmd(buf, 4);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) < 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "0\r\n") == 0) {
|
|
current_addr = 0;
|
|
num_to_xmit = chip->bootprog[0] * 4;
|
|
copy_boot_code_to_memory(chip);
|
|
linecount = 0;
|
|
cksum = 0;
|
|
state = BOOT_XMIT_DATA;
|
|
event = BEGIN;
|
|
}
|
|
else {
|
|
download_cancel("can't xmit to ram");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case BOOT_XMIT_DATA:
|
|
switch (event) {
|
|
case BEGIN:
|
|
n = num_to_xmit;
|
|
|
|
if (n > 45) {
|
|
n = 45;
|
|
}
|
|
|
|
get_ihex_data(current_addr, n, bytes);
|
|
cksum += sum(bytes, n);
|
|
uuencode(buf, bytes, n);
|
|
current_addr += n;
|
|
num_to_xmit -= n;
|
|
linecount++;
|
|
//printf("send: %s\r\n", buf);
|
|
xmit_cmd(buf, 5);
|
|
write_serial_port("\r\n", 2);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) != 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "\r\n") == 0) {
|
|
if (linecount >= 20 || num_to_xmit <= 0) {
|
|
state = BOOT_XMIT_CKSUM;
|
|
}
|
|
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
else {
|
|
download_cancel("data xmit did not echo");
|
|
return;
|
|
}
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case BOOT_XMIT_CKSUM:
|
|
switch (event) {
|
|
case BEGIN:
|
|
snprintf(buf, sizeof(buf), "%u\r\n", cksum);
|
|
//printf("send: %s", buf);
|
|
xmit_cmd(buf, 3);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) != 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "OK\r\n") == 0) {
|
|
if (num_to_xmit > 0) {
|
|
printf(".");
|
|
fflush(stdout);
|
|
state = BOOT_XMIT_DATA;
|
|
event = BEGIN;
|
|
linecount = 0;
|
|
cksum = 0;
|
|
break;
|
|
}
|
|
|
|
state = BOOT_RUN_CODE;
|
|
event = BEGIN;
|
|
break;
|
|
}
|
|
else {
|
|
download_cancel("bad checksum");
|
|
return;
|
|
}
|
|
|
|
case TIMEOUT:
|
|
download_cancel("No response");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case BOOT_RUN_CODE:
|
|
switch (event) {
|
|
case BEGIN:
|
|
printf("jumping now!\r\n");
|
|
snprintf(buf, sizeof(buf), "G %u A\r\n", chip->ram_addr);
|
|
xmit_cmd(buf, 4);
|
|
return;
|
|
|
|
case RESPONSE:
|
|
if (num_lines(parsed_response_buf) < 1) {
|
|
return;
|
|
}
|
|
|
|
if (strcmp(parsed_response_buf, "0\r\n") == 0) {
|
|
done_program(0);
|
|
return;
|
|
}
|
|
else {
|
|
printf("response = %s", parsed_response_buf);
|
|
download_cancel("couldn't run program");
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
case TIMEOUT:
|
|
done_program(0);
|
|
return;
|
|
// Philips user name says it responds, but it does not.
|
|
// It seems to just immediately jump to the code without
|
|
// any "0" response.
|
|
//download_cancel("No response"); return;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
snprintf(buf, sizeof(buf), "unknown state %d\r\n", state);
|
|
download_cancel(buf);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void download_cancel(const char *mesg)
|
|
{
|
|
printf("\r\nDownload Canceled");
|
|
|
|
if (mesg && *mesg) {
|
|
printf(": %s", mesg);
|
|
}
|
|
|
|
printf("\r\n");
|
|
// need to do some cleanup for various states???
|
|
done_program(1);
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
/* */
|
|
/* Transmit Commands to Bootloader */
|
|
/* */
|
|
/****************************************************************/
|
|
|
|
|
|
|
|
static void xmit_cmd(const char *cmd, int max_time)
|
|
{
|
|
int len;
|
|
|
|
if (cmd == NULL || *cmd == '\0') {
|
|
return;
|
|
}
|
|
|
|
len = strlen(cmd);
|
|
|
|
#ifdef PRINT_TX_RX_BYTES
|
|
printf("tx %d bytes: %s\n", len, cmd);
|
|
#endif
|
|
|
|
input_flush_serial_port();
|
|
|
|
write_serial_port(cmd, len);
|
|
|
|
snprintf(expected_echo_buf, sizeof(expected_echo_buf), "%s", cmd);
|
|
|
|
if (state == SYNC_1) {
|
|
// special case, baud sync doesn't echo
|
|
expected_echo_buf[0] = '\0';
|
|
}
|
|
|
|
expected_echo_ptr = expected_echo_buf;
|
|
parsed_response_ptr = parsed_response_buf;
|
|
|
|
response_timer = max_time;
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
/* */
|
|
/* Handlers that respond to input */
|
|
/* */
|
|
/****************************************************************/
|
|
|
|
|
|
/*
|
|
Whenever the main gtk event loop detects more input has arrived from the
|
|
serial port, and we're in the process of a download, it calls here to
|
|
hand off the data. We're supposed to match it up to the echo buffer,
|
|
and then store it into the parsed response buffer and if it looks like
|
|
this might be a complete response, call download_main with a response
|
|
event.
|
|
*/
|
|
void download_rx_port(const unsigned char *buf, int num)
|
|
{
|
|
int i = 0;
|
|
|
|
if (num <= 0) {
|
|
return;
|
|
}
|
|
|
|
// echo the data
|
|
//write(term_fd, buf, num);
|
|
|
|
#ifdef PRINT_TX_RX_BYTES
|
|
printf("rx %d bytes:", num);
|
|
|
|
for (i = 0; i < num; i++) {
|
|
printf(" %02X", *(buf + i));
|
|
}
|
|
|
|
printf("\r\n");
|
|
#endif
|
|
|
|
// ignore extra incoming garbage we didn't expect
|
|
if (expected_echo_ptr == NULL) {
|
|
return;
|
|
}
|
|
|
|
// special case, echo of '?' during unsuccessful sync
|
|
if (state == SYNC_1 && num == 1 && buf[0] == '?') {
|
|
*parsed_response_ptr++ = '?';
|
|
*parsed_response_ptr = '\0';
|
|
response_timer = 0;
|
|
download_main(RESPONSE);
|
|
return;
|
|
}
|
|
|
|
// parse it
|
|
for (i = 0; i < num; i++) {
|
|
// if we're still expecting the echo, gobble it up
|
|
if (*expected_echo_ptr) {
|
|
if (buf[i] != *expected_echo_ptr) {
|
|
#ifdef PRINT_TX_RX_BYTES
|
|
printf(" <echo_err> ");
|
|
#endif
|
|
// ignore incorrect echo (will timeout)
|
|
expected_echo_ptr = NULL;
|
|
return;
|
|
}
|
|
|
|
expected_echo_ptr++;
|
|
continue;
|
|
}
|
|
|
|
// store this into a parsed response buffer
|
|
*parsed_response_ptr++ = buf[i];
|
|
}
|
|
|
|
// if the last two characters of the response are "\r\n",
|
|
// then it's likely we've got a complete response.
|
|
*parsed_response_ptr = '\0';
|
|
|
|
if (parsed_response_ptr > parsed_response_buf + 1
|
|
&& *(parsed_response_ptr - 2) == '\r'
|
|
&& *(parsed_response_ptr - 1) == '\n') {
|
|
//response_timer = 0;
|
|
download_main(RESPONSE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
During a download, this is supposed to get called at 100 Hz. Whenever
|
|
something is transmitted and we expect a response, the response_timer
|
|
is initialized to the maximum time we will wait.
|
|
*/
|
|
void download_timer(void)
|
|
{
|
|
if (response_timer > 0) {
|
|
response_timer--;
|
|
|
|
if (response_timer == 0) {
|
|
expected_echo_ptr = NULL;
|
|
download_main(TIMEOUT);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
During a download, all input the user types into the terminal is sent
|
|
to this function, instead of passing it to xterm for display
|
|
*/
|
|
void download_rx_term(const unsigned char *buf, int num)
|
|
{
|
|
// discard anything the user types into the terminal
|
|
// while we are in the middle of downloading. Maybe
|
|
// we should look for CTRL-C and abort??
|
|
}
|