440 lines
9.7 KiB
C
440 lines
9.7 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 <stdio.h>
|
|
|
|
extern int cdi_programmer(conf_opts_t *conf, char *filename);
|
|
|
|
void usage(char *prg_name)
|
|
{
|
|
printf("\nUsage: %s [-d device]\n", prg_name);
|
|
printf("General options:\n");
|
|
printf(" -V/--version Get programmer version\n");
|
|
printf(" -1/--d100 Use D100 board (default D200)\n");
|
|
printf("Operating modes:\n");
|
|
printf(" -b/--bios to get programmer BIOS version\n");
|
|
printf(" -P/--program [ihex file] Do a complete programming sequence (write and verify)\n");
|
|
printf(" -v/--verify [ihex file] Verify against ihex file\n");
|
|
printf(" -r/--read [ihex file] Read program code into ihex file\n");
|
|
printf(" -m/--mac Read device MAC address\n");
|
|
printf(" -Q/--write-mac [MAC address] Write device MAC address\n");
|
|
printf(" -e/--erase Erase flash (erases MAC address!)\n");
|
|
printf("Programming options:\n");
|
|
printf(" -l/--linear Force linear model for extended addresses (not SDCC file)\n");
|
|
printf(" -s/--sdcc Force SDCC model for extended addresses (SDCC file)\n");
|
|
printf("Defaults:\n");
|
|
#ifndef PLATFORM_WINDOWS
|
|
printf("device /dev/ttyUSB0\n");
|
|
#else
|
|
printf("device 0\n");
|
|
#endif
|
|
}
|
|
|
|
conf_opts_t conf_opts;
|
|
|
|
static int option_index = 0;
|
|
|
|
int do_exit = 0;
|
|
|
|
#define OPTIONS_STRING "d:ec1lsmVbP:v:r:Q:"
|
|
/* long option list */
|
|
static struct option long_options[] =
|
|
{
|
|
{"device", 1, NULL, 'd'},
|
|
{"psoc", 0, NULL, 'p'},
|
|
{"d100", 0, NULL, '1'},
|
|
{"erase", 0, NULL, 'e'},
|
|
{"mac", 0, NULL, 'm'},
|
|
{"linear", 0, NULL, 'l'},
|
|
{"sdcc", 0, NULL, 's'},
|
|
{"cdi", 0, NULL, 'c'},
|
|
{"version", 0, NULL, 'V'},
|
|
{"bios", 0, NULL, 'b'},
|
|
{"program", 1, NULL, 'P'},
|
|
{"verify", 1, NULL, 'v'},
|
|
{"read", 1, NULL, 'r'},
|
|
{"write-mac", 1, NULL, 'Q'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
int parse_opts(int count, char* param[])
|
|
{
|
|
int opt;
|
|
int error=0;
|
|
|
|
conf_opts.target_type = CDI;
|
|
while ((opt = getopt_long(count, param, OPTIONS_STRING,
|
|
long_options, &option_index)) != -1)
|
|
{
|
|
switch(opt)
|
|
{
|
|
case 'V':
|
|
conf_opts.target_type = VERSION;
|
|
break;
|
|
|
|
case '1':
|
|
conf_opts.prg_type = 1;
|
|
break;
|
|
|
|
case 'c':
|
|
conf_opts.target_type = CDI;
|
|
break;
|
|
|
|
case 'd':
|
|
#ifdef PLATFORM_WINDOWS
|
|
if (sscanf(optarg, "%d", &conf_opts.device) != 1)
|
|
{
|
|
printf("Device ID must be a positive integer.\n");
|
|
conf_opts.action = ' ';
|
|
}
|
|
#else
|
|
printf("device:%s\n", optarg);
|
|
strcpy(conf_opts.device, optarg);
|
|
#endif
|
|
break;
|
|
|
|
case 'P':
|
|
printf("Programming mode.\n");
|
|
conf_opts.action = 'P';
|
|
strcpy(conf_opts.ihex_file, optarg);
|
|
break;
|
|
|
|
case 's':
|
|
if (conf_opts.page_mode == PAGE_UNDEFINED)
|
|
{
|
|
conf_opts.page_mode = PAGE_SDCC;
|
|
}
|
|
else
|
|
{
|
|
printf("Only one paging option allowed.\n");
|
|
error = -1;
|
|
}
|
|
break;
|
|
|
|
case 'l':
|
|
if (conf_opts.page_mode == PAGE_UNDEFINED)
|
|
{
|
|
conf_opts.page_mode = PAGE_LINEAR;
|
|
}
|
|
else
|
|
{
|
|
printf("Only one paging option allowed.\n");
|
|
error = -1;
|
|
}
|
|
break;
|
|
|
|
case 'e':
|
|
printf("Erase.\n");
|
|
conf_opts.action = 'e';
|
|
break;
|
|
|
|
case 'm':
|
|
printf("Get MAC.\n");
|
|
conf_opts.action = 'm';
|
|
break;
|
|
|
|
case 'b':
|
|
printf("Get BIOS version\n");
|
|
conf_opts.action = 'b';
|
|
break;
|
|
|
|
case 'Q':
|
|
printf("Write MAC.\n");
|
|
conf_opts.action = 'Q';
|
|
if (sscanf(optarg, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
|
&conf_opts.write_mac[0], &conf_opts.write_mac[1],
|
|
&conf_opts.write_mac[2], &conf_opts.write_mac[3],
|
|
&conf_opts.write_mac[4], &conf_opts.write_mac[5],
|
|
&conf_opts.write_mac[6], &conf_opts.write_mac[7]) != 8)
|
|
{
|
|
printf("Invalid MAC.\n");
|
|
conf_opts.action = ' ';
|
|
}
|
|
break;
|
|
|
|
case 'v':
|
|
printf("Verify by comparing to ihex file:%s\n", optarg);
|
|
conf_opts.action = 'v';
|
|
strcpy(conf_opts.ihex_file, optarg);
|
|
break;
|
|
|
|
case 'r':
|
|
printf("Read program to ihex file:%s\n", optarg);
|
|
conf_opts.action = 'r';
|
|
strcpy(conf_opts.ihex_file, optarg);
|
|
break;
|
|
|
|
case '?':
|
|
printf("Duh\n");
|
|
error = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!error && (conf_opts.target_type == CDI) )
|
|
{
|
|
#ifdef PLATFORM_WINDOWS
|
|
printf("Setup: Device %d.\n", conf_opts.device);
|
|
#else
|
|
printf("Setup: Device %s.\n", conf_opts.device);
|
|
#endif
|
|
}
|
|
|
|
return error;
|
|
}
|
|
/*
|
|
int port_write_8byte_echo(port_t *port, char *string)
|
|
{
|
|
int length = 0;
|
|
int total_len;
|
|
int i, j;
|
|
struct pollfd pfds;
|
|
unsigned int nfds = 1;
|
|
int rval = 0;
|
|
|
|
int wrbytes = 2;
|
|
unsigned char byte8[wrbytes];
|
|
|
|
pfds.fd = (int)(port->handle);
|
|
pfds.events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL;
|
|
|
|
total_len = strlen(string);
|
|
|
|
while(total_len > length)
|
|
{
|
|
if(total_len - length >= wrbytes)
|
|
{
|
|
i=0;
|
|
port_write(port, (unsigned char *)&string[length], wrbytes);
|
|
|
|
if((rval = poll(&pfds, nfds, 100)) == 0)
|
|
{
|
|
printf("Timed out...\n");
|
|
return(-1);
|
|
}
|
|
else
|
|
{
|
|
while(i<wrbytes)
|
|
{
|
|
i += port_read(port, &byte8[i], wrbytes);
|
|
}
|
|
|
|
if(i != wrbytes || memcmp(byte8, &string[length], wrbytes) != 0)
|
|
{
|
|
printf("Got wrong length (%d) or wrong bytes back.\n", i);
|
|
for(j=0;j<wrbytes;j++)
|
|
{
|
|
printf("%.2x <-> %.2x\n", string[length+j], byte8[j]);
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
else
|
|
{
|
|
printf("8Bwok - ");
|
|
fflush(stdout);
|
|
length += wrbytes;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
i=0;
|
|
port_write(port, (unsigned char *)&string[length], total_len - length);
|
|
|
|
if((rval = poll(&pfds, nfds, 100)) == 0)
|
|
{
|
|
printf("Timed out...\n");
|
|
return(-1);
|
|
}
|
|
else
|
|
{
|
|
while(i<(total_len-length))
|
|
{
|
|
i = port_read(port, &byte8[i], total_len - length);
|
|
}
|
|
if(i != total_len - length || memcmp(byte8, &string[length], total_len - length) != 0)
|
|
{
|
|
printf("Got wrong length or wrong bytes back.\n");
|
|
for(j=0;j<total_len - length;j++)
|
|
{
|
|
printf("%.2x <-> %.2x\n", string[length+j], byte8[j]);
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
else
|
|
{
|
|
printf("<8Bwok - \n");
|
|
fflush(stdout);
|
|
length += (total_len - length);
|
|
}
|
|
}
|
|
}
|
|
|
|
usleep(5000);
|
|
|
|
}
|
|
|
|
return(length);
|
|
}
|
|
*/
|
|
#ifdef PLATFORM_WINDOWS
|
|
int programmer_init(int device, port_t **port)
|
|
#else
|
|
int programmer_init(char *device, port_t **port)
|
|
#endif
|
|
{
|
|
int error = port_open(port, device);
|
|
uint8_t buffer[8];
|
|
|
|
buffer[0] = 0;
|
|
|
|
if (error >= 0)
|
|
{
|
|
if (conf_opts.prg_type == 1)
|
|
{
|
|
int retry = 0;
|
|
port_set_params(*port, 9600, 0);
|
|
// Activate programming...
|
|
port_dtr_clear(*port);
|
|
port_rts_clear(*port);
|
|
sleep(1);
|
|
printf("Select D100.\n");
|
|
port_rts_set(*port);
|
|
sleep(1);
|
|
buffer[0] = '\r';
|
|
while (retry++ < 3)
|
|
{
|
|
int length;
|
|
port_write_echo(*port, "q\r");
|
|
length = port_readline(*port, buffer, sizeof(buffer), 800);
|
|
if (length)
|
|
{
|
|
if (*buffer == '!')
|
|
{
|
|
printf("D100 found.\n");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
printf("No programmer found.\n");
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
port_set_params(*port, 57600, 0);
|
|
// Activate programming...
|
|
port_dtr_clear(*port);
|
|
port_rts_clear(*port);
|
|
usleep(300000);
|
|
printf("Select D200.\n");
|
|
port_rts_set(*port);
|
|
usleep(200000);
|
|
port_set_params(*port, 57600, 1);
|
|
port_write(*port, (uint8_t *)"\r", 1);
|
|
usleep(100000);
|
|
if ((port_get(*port, buffer, 500) >= 1) && (buffer[0] == '!'))
|
|
{
|
|
printf("D200 found.\n");
|
|
return 0;
|
|
}
|
|
printf("No programmer found.\n");
|
|
return -1;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
void programmer_close(port_t *port)
|
|
{
|
|
if (port)
|
|
{
|
|
port_rts_clear(port);
|
|
port_dtr_set(port);
|
|
sleep(1);
|
|
port_close(port);
|
|
printf("Port closed.\n");
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int error = 0;
|
|
|
|
conf_opts.target_type = 0;
|
|
conf_opts.action = 0;
|
|
conf_opts.prg_type = 2;
|
|
|
|
|
|
#ifndef PLATFORM_WINDOWS
|
|
strncpy(conf_opts.device, "/dev/ttyUSB0", 12);
|
|
/* Install a new handler for SIGIO.
|
|
*
|
|
* According to man 7 signal this signal is by default ignored by most systems.
|
|
* It seems that pre FC7 this was true for Fedoras also. We have noticed that at least
|
|
* on some FC7 installations the default action has changed. We avoid abnormal program
|
|
* exits by defining the SIGIO as SIG_IGN (ignore). - mjs
|
|
*/
|
|
if(signal(SIGIO, SIG_IGN) == SIG_ERR)
|
|
{
|
|
printf("%s error: failed to install SIGIO handler. Exit.\n", argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
#else
|
|
conf_opts.device = 0;
|
|
#endif
|
|
|
|
conf_opts.page_mode = PAGE_UNDEFINED;
|
|
|
|
if ( (argc <= 1) || (error = parse_opts(argc, argv)) )
|
|
{
|
|
usage(argv[0]);
|
|
if (error < 0) return error;
|
|
else return 0;
|
|
}
|
|
|
|
if(conf_opts.target_type == CDI)
|
|
{ /*CDI*/
|
|
error = cdi_programmer(&conf_opts, conf_opts.ihex_file);
|
|
}
|
|
else
|
|
{
|
|
printf("\nSensinode Nano series programmer "PROGRAMMER_VERSION "\n");
|
|
}
|
|
return error;
|
|
}
|