608 lines
13 KiB
C
608 lines
13 KiB
C
/*
|
|
NanoStack: MCU software and PC tools for IP-based wireless sensor networking.
|
|
|
|
Copyright (C) 2006-2007 Sensinode Ltd.
|
|
|
|
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; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
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.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
Address:
|
|
Sensinode Ltd.
|
|
Teknologiantie 6
|
|
90570 Oulu, Finland
|
|
|
|
E-mail:
|
|
info@sensinode.com
|
|
*/
|
|
|
|
|
|
#include <unistd.h>
|
|
#include <getopt.h>
|
|
#include <strings.h>
|
|
|
|
#include "port.h"
|
|
#include "programmer.h"
|
|
#include "ihex.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
extern void crc_add(unsigned char *crc, unsigned char byte);
|
|
|
|
int cdi_page_write(port_t *port, unsigned long page_addr, unsigned char *page_buffer);
|
|
int cdi_write(port_t *port, conf_opts_t *conf, FILE *ihex);
|
|
|
|
int cdi_programmer(conf_opts_t *conf, char *filename)
|
|
{
|
|
int error = 0;
|
|
port_t *port = 0;
|
|
unsigned char buffer[256];
|
|
int length = 0;
|
|
FILE *ihex = 0;
|
|
|
|
error = programmer_init(conf->device, &port);
|
|
|
|
if (error < 0)
|
|
{
|
|
return error;
|
|
}
|
|
|
|
if((!error) && (conf->action != 'b'))
|
|
{
|
|
length = port_write_echo(port, "CDI\r");
|
|
bzero(buffer, sizeof(buffer));
|
|
if (length >= 4)
|
|
{
|
|
length = port_readline(port, buffer, sizeof(buffer), 100);
|
|
}
|
|
else length = 0;
|
|
|
|
if(memcmp(buffer, "OK", 2) == 0)
|
|
{
|
|
error = 0;
|
|
}
|
|
else
|
|
{
|
|
printf("Programming mode selection failed.\n");
|
|
error = -1;
|
|
}
|
|
}
|
|
|
|
if((!error) && (conf->action != 'b'))
|
|
{
|
|
printf("Initialize.\n");
|
|
// Succesfully in mode 1
|
|
sleep(1);
|
|
port_write_echo(port, "i\r");
|
|
|
|
bzero(buffer, 256);
|
|
length = port_readline(port, buffer, sizeof(buffer), 100);
|
|
|
|
if(memcmp(buffer, "85", 2) == 0)
|
|
{ /*Found CC2430 device*/
|
|
printf("Found CC2430 device revision %c%c.\n", buffer[2], buffer[3]);
|
|
}
|
|
else if (memcmp(buffer, "89", 2) == 0)
|
|
{
|
|
printf("Found CC2431 device revision %c%c.\n", buffer[2], buffer[3]);
|
|
}
|
|
else
|
|
{
|
|
printf("CC2430 not found.\n");
|
|
error = -1;
|
|
}
|
|
}
|
|
|
|
if (error) conf->action = ' ';
|
|
|
|
switch(conf->action)
|
|
{
|
|
case 'e':
|
|
// Erase programming
|
|
port_write_echo(port, "e\r");
|
|
bzero(buffer, 256);
|
|
length = port_readline(port, buffer, sizeof(buffer), 100);
|
|
|
|
if(memcmp(buffer, "OK", 2) == 0)
|
|
{
|
|
// Erase successful
|
|
printf("Erase successful.\n");
|
|
error = 0;
|
|
}
|
|
else
|
|
{
|
|
// Erase failed
|
|
printf("Erase failed: %s.\n", buffer);
|
|
error = -1;
|
|
}
|
|
if ((conf->action != 'P') || error)
|
|
break;
|
|
|
|
case 'P':
|
|
case 'w':
|
|
ihex = fopen(conf->ihex_file, "rb");
|
|
if(ihex == NULL)
|
|
{
|
|
printf("Failed to open ihex file %s.\n", conf->ihex_file);
|
|
error = -1;
|
|
}
|
|
else error = 0;
|
|
|
|
if (!error)
|
|
{
|
|
error = cdi_write(port, conf, ihex);
|
|
if (error) printf("Programming failed.\n");
|
|
}
|
|
|
|
|
|
|
|
if (ihex != NULL) fclose(ihex);
|
|
break;
|
|
|
|
case 'b':
|
|
length = port_write_echo(port, "V\r");
|
|
bzero(buffer, sizeof(buffer));
|
|
if (length >= 2)
|
|
{
|
|
length = port_readline(port, buffer, sizeof(buffer), 100);
|
|
}
|
|
else length = 0;
|
|
|
|
if(length > 4)
|
|
{
|
|
buffer[length] = 0;
|
|
printf("BIOS: %s\n", buffer);
|
|
error = 0;
|
|
}
|
|
else
|
|
{
|
|
printf("Failed to get BIOS version. Upgrade recommended.\n");
|
|
error = -1;
|
|
}
|
|
break;
|
|
|
|
case 'v':
|
|
break;
|
|
|
|
case 'r':
|
|
ihex = fopen(conf->ihex_file, "wb");
|
|
if(ihex == NULL)
|
|
{
|
|
printf("Failed to open ihex file %s.\n", conf->ihex_file);
|
|
error = -1;
|
|
}
|
|
else
|
|
{
|
|
port_write_echo(port, "a000000\r");
|
|
bzero(buffer, sizeof(buffer));
|
|
length = port_readline(port, buffer, sizeof(buffer), 200);
|
|
if (length <0) length = 0;
|
|
if(memcmp(buffer, "OK", 2) == 0)
|
|
{
|
|
uint32_t address = 0;
|
|
uint8_t check = 0;
|
|
for (address = 0; address < 128*1024; address += 64)
|
|
{
|
|
uint8_t i;
|
|
|
|
if ((address) && ((address & 0xFFFF)==0))
|
|
{
|
|
fprintf(ihex, ":02000004%4.4X%2.2X\r\n",
|
|
(int)(address>>16), (int)(0xFA-(address>>16)));
|
|
}
|
|
port_write_echo(port, "r\r");
|
|
bzero(buffer, 256);
|
|
length = 0;
|
|
while (length < 64)
|
|
{
|
|
length += port_readline(port, &buffer[length], sizeof(buffer)-length, 100);
|
|
}
|
|
for (i=0; i<64; i++)
|
|
{
|
|
if ((i & 0x0F) == 0)
|
|
{
|
|
check = 0;
|
|
check -= 0x10;
|
|
check -= (uint8_t) (address >> 8);
|
|
check -= (uint8_t) (address + i);
|
|
printf("%4.4X", (int) address + i);
|
|
fprintf(ihex, ":10%4.4X00", (int) (address + i) & 0xFFFF);
|
|
}
|
|
fprintf(ihex, "%2.2X", buffer[i]);
|
|
check -= buffer[i];
|
|
if ((i & 0x0F) == 0x0F)
|
|
{
|
|
fprintf(ihex, "%2.2X\r\n", check);
|
|
if (i > 0x30) printf("\n");
|
|
else printf(" ");
|
|
}
|
|
}
|
|
}
|
|
fprintf(ihex, ":00000001FF\r\n");
|
|
}
|
|
else
|
|
{
|
|
printf("Failed to set read address.\n");
|
|
error = -1;
|
|
}
|
|
fclose(ihex);
|
|
}
|
|
break;
|
|
/*skip for error case*/
|
|
case ' ':
|
|
break;
|
|
|
|
case 'm':
|
|
port_write_echo(port, "a01F800\r");
|
|
bzero(buffer, sizeof(buffer));
|
|
length = port_readline(port, buffer, sizeof(buffer), 200);
|
|
if (length <0) length = 0;
|
|
if(memcmp(buffer, "OK", 2) == 0)
|
|
{
|
|
uint8_t i;
|
|
uint32_t address = 0;
|
|
|
|
for (address = 0x01F800; address < 128*1024; address += 64)
|
|
{
|
|
|
|
port_write_echo(port, "r\r");
|
|
bzero(buffer, 256);
|
|
length = 0;
|
|
while (length < 64)
|
|
{
|
|
length += port_readline(port, &buffer[length], sizeof(buffer)-length, 100);
|
|
}
|
|
if ((address & 0xff) == 0)
|
|
{ printf(".");
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
printf("\nDevice MAC: ");
|
|
for (i=56; i<64; i++)
|
|
{
|
|
if (i != 56) printf(":");
|
|
printf("%2.2X", buffer[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
break;
|
|
|
|
case 'Q':
|
|
port_write_echo(port, "a01F800\r");
|
|
bzero(buffer, sizeof(buffer));
|
|
length = port_readline(port, buffer, sizeof(buffer), 200);
|
|
if (length <0) length = 0;
|
|
if(memcmp(buffer, "OK", 2) == 0)
|
|
{
|
|
uint8_t p_buffer[2048];
|
|
int error;
|
|
|
|
memset(p_buffer, 0xff, sizeof(p_buffer));
|
|
memcpy(&p_buffer[2040], conf->write_mac, 8);
|
|
|
|
printf("\rWriting MAC: ");
|
|
error = cdi_page_write(port, 0x01F800, p_buffer);
|
|
if (!error)
|
|
{
|
|
printf("Write complete.\n");
|
|
}
|
|
else
|
|
{
|
|
printf("Write failed.\n");
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
printf("Unknown CDI action.\n");
|
|
break;
|
|
}
|
|
|
|
printf("Close programmer.\n");
|
|
usleep(100000);
|
|
port_write_echo(port, "q\r");
|
|
programmer_close(port);
|
|
return error;
|
|
}
|
|
|
|
int cdi_write(port_t *port, conf_opts_t *conf, FILE *ihex)
|
|
{
|
|
int error = 0;
|
|
unsigned char buffer[256];
|
|
int length;
|
|
int i;
|
|
int pages;
|
|
|
|
unsigned long ext_addr=0;
|
|
unsigned short int addr=0;
|
|
unsigned char page_buffer[128*1024];
|
|
unsigned char page_table[64];
|
|
|
|
bzero(buffer, sizeof(buffer));
|
|
|
|
/*initialize page data*/
|
|
memset(page_table, 0, 64);
|
|
memset(page_buffer, 0xFF, sizeof(page_buffer));
|
|
pages = 0;
|
|
|
|
error = 0;
|
|
|
|
if (conf->page_mode == PAGE_UNDEFINED)
|
|
{
|
|
int retval;
|
|
|
|
while((!error) && ((retval = fscanf(ihex, "%s", buffer)) == 1) )
|
|
{
|
|
unsigned char data_len = 0;
|
|
|
|
if (memcmp(&buffer[7], "00", 2) == 0)
|
|
{ /*Data record*/
|
|
}
|
|
else if (memcmp(&buffer[7], "01", 2) == 0)
|
|
{ /*end file*/
|
|
printf("\nFile read complete.\n");
|
|
break;
|
|
}
|
|
else if (memcmp(&buffer[7], "04", 2) == 0)
|
|
{
|
|
sscanf((char *)&(buffer[3]),"%4hx", &addr);
|
|
sscanf((char *)&(buffer[9]),"%4lx", &ext_addr);
|
|
|
|
if (ext_addr >= 0x0002)
|
|
{
|
|
conf->page_mode = PAGE_SDCC;
|
|
}
|
|
else
|
|
{
|
|
if (conf->page_mode == PAGE_UNDEFINED) conf->page_mode = PAGE_LINEAR;
|
|
}
|
|
}
|
|
}
|
|
if (retval == -1)
|
|
{
|
|
printf("Read error\n");
|
|
return -1;
|
|
}
|
|
rewind(ihex);
|
|
retval = 0;
|
|
error = 0;
|
|
}
|
|
switch (conf->page_mode)
|
|
{
|
|
case PAGE_SDCC:
|
|
printf("SDCC banked file.\n");
|
|
break;
|
|
case PAGE_LINEAR:
|
|
printf("Linear banked file.\n");
|
|
break;
|
|
case PAGE_UNDEFINED:
|
|
printf("Non-banked file, assuming linear.\n");
|
|
conf->page_mode = PAGE_LINEAR;
|
|
break;
|
|
}
|
|
|
|
while( (fscanf(ihex, "%s", buffer) == 1) && !error)
|
|
{
|
|
unsigned char data_len = 0;
|
|
|
|
if (memcmp(&buffer[7], "00", 2) == 0)
|
|
{ /*Data record*/
|
|
i=0;
|
|
sscanf((char *)&buffer[1], "%2hhx", &data_len);
|
|
sscanf((char *)&(buffer[3]),"%4hx", &addr);
|
|
while(i<data_len)
|
|
{
|
|
uint32_t absolute_address = ext_addr+addr+i;
|
|
|
|
if (page_table[absolute_address/2048] == 0)
|
|
{
|
|
page_table[absolute_address/2048] = 1;
|
|
pages++;
|
|
}
|
|
sscanf((char *)&buffer[2*i+9], "%2hhx", &page_buffer[absolute_address]);
|
|
i++;
|
|
}
|
|
}
|
|
else if (memcmp(&buffer[7], "01", 2) == 0)
|
|
{ /*end file*/
|
|
printf("\nFile read complete.\n");
|
|
printf("Writing %d pages.\n", pages);
|
|
break;
|
|
}
|
|
else if (memcmp(&buffer[7], "04", 2) == 0)
|
|
{
|
|
sscanf((char *)&(buffer[3]),"%4hx", &addr);
|
|
sscanf((char *)&(buffer[9]),"%4lx", &ext_addr);
|
|
if (conf->page_mode == PAGE_SDCC)
|
|
{
|
|
if (ext_addr) ext_addr--;
|
|
ext_addr *= 0x8000;
|
|
}
|
|
else
|
|
{
|
|
ext_addr *= 0x10000;
|
|
}
|
|
printf("\rExtended page address: 0x%8.8lX\r", ext_addr);
|
|
}
|
|
}
|
|
|
|
if (pages)
|
|
{
|
|
int retry = 0;
|
|
// Successfully in mode 3 (programming)
|
|
printf("Starting programming.\n");
|
|
error = 0;
|
|
for (i=0; i<64; i++)
|
|
{
|
|
if (page_table[i] != 0)
|
|
{
|
|
ext_addr = 2048*i;
|
|
|
|
bzero(buffer, sizeof(buffer));
|
|
|
|
// Write the start address and check return
|
|
usleep(3000);
|
|
sprintf((char *)buffer, "a%6.6lX\r", ext_addr);
|
|
port_write_echo(port, (char *)buffer);
|
|
|
|
if((length = port_readline(port, buffer, sizeof(buffer), 200)) < 0)
|
|
{
|
|
printf("Read from serial timed out without data.\n");
|
|
error = -1;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if(strncmp((char *)buffer, "OK\r\n", 4) == 0)
|
|
{
|
|
printf("\r \r");
|
|
printf("\rWriting @ 0x%6.6lX: ", ext_addr);
|
|
fflush(stdout);
|
|
error = cdi_page_write(port, ext_addr, &page_buffer[ext_addr]);
|
|
if (error)
|
|
{
|
|
usleep(20000);
|
|
port_write_echo(port, "i\r");
|
|
|
|
bzero(buffer, 256);
|
|
length = port_readline(port, buffer, sizeof(buffer), 100);
|
|
|
|
if(memcmp(buffer, "85", 2) == 0)
|
|
{ /*Found CC2430 device*/
|
|
}
|
|
else
|
|
{
|
|
printf("Reinit failed.\n");
|
|
error = -1;
|
|
}
|
|
if (retry++ < 3)
|
|
{
|
|
error = 0;
|
|
i--;
|
|
}
|
|
}
|
|
else retry = 0;
|
|
fflush(stdout);
|
|
usleep(20000);
|
|
}
|
|
else
|
|
{
|
|
printf("Failed to set CDI programming start address.\n");
|
|
error = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (error) break;
|
|
}
|
|
usleep(200000);
|
|
printf("\n");
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
int cdi_page_write(port_t *port, unsigned long page_addr, unsigned char *page_buffer)
|
|
{
|
|
int error = 0;
|
|
unsigned char buffer[80];
|
|
unsigned char cmd[16];
|
|
unsigned char block, i;
|
|
int length;
|
|
int retry = 0;
|
|
|
|
// Write page
|
|
port_write_echo(port, "w\r");
|
|
usleep(10000);
|
|
for (block=0; block<(2048/64); block++)
|
|
{
|
|
sprintf((char *)cmd, "%6.6lX", page_addr + (64*block));
|
|
bzero(buffer, sizeof(buffer));
|
|
length = port_readline(port, buffer, sizeof(buffer), 2000);
|
|
if (length <0)
|
|
{ length = 0;
|
|
printf("l!");fflush(stdout);
|
|
}
|
|
buffer[length] = 0;
|
|
if (block & 1)
|
|
{
|
|
}
|
|
if(memcmp(buffer, cmd, 6) == 0)
|
|
{
|
|
#define WRITE_SIZE 64
|
|
for (i=0; i<64; i+=WRITE_SIZE)
|
|
{
|
|
port_write(port, &page_buffer[(unsigned int)(block*64)+i], WRITE_SIZE);
|
|
usleep(1250);
|
|
}
|
|
|
|
bzero(buffer, sizeof(buffer));
|
|
printf(".");
|
|
fflush(stdout);
|
|
length = port_readline(port, buffer, sizeof(buffer), 200);
|
|
if(memcmp(buffer, "OK", 2) == 0)
|
|
{
|
|
retry = 0;
|
|
}
|
|
else
|
|
{
|
|
block--;
|
|
if (retry++ >= 8)
|
|
{
|
|
error = -1;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
buffer[length] = 0;
|
|
printf("%s",buffer);
|
|
port_rts_clear(port);
|
|
usleep(300000);
|
|
port_rts_set(port);
|
|
bzero(buffer, sizeof(buffer));
|
|
length = port_readline(port, buffer, sizeof(buffer), 800);
|
|
if(memcmp(buffer, "CDI", 3) == 0)
|
|
{
|
|
printf("R");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!error)
|
|
{
|
|
printf("w"); fflush(stdout);
|
|
bzero(buffer, sizeof(buffer));
|
|
length = port_readline(port, buffer, sizeof(buffer), 800);
|
|
if(memcmp(buffer, "WROK", 4) == 0)
|
|
{
|
|
error = 0;
|
|
}
|
|
else
|
|
{
|
|
printf("%c%c", buffer[0], buffer[1]);
|
|
error = -1;
|
|
}
|
|
}
|
|
|
|
if (!error) printf("OK\r");
|
|
return error;
|
|
}
|
|
|