Files
cctl/cctl-prog/cctl-prog.c
T

885 lines
19 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdbool.h>
#include <getopt.h>
#include <sys/time.h>
#ifdef __CYGWIN__
#undef WIN32
#endif
#ifndef WIN32
#include <termios.h>
#include <sys/select.h>
#else
#include <windows.h>
#include <wincon.h>
#include <time.h>
#define B115200 115200
#endif
#include "hex.h"
static struct option long_options[] =
{
{"help", no_argument, 0, 'h'},
{"console", no_argument, 0, 'c'},
{"flash", required_argument, 0, 'f'},
{"device", required_argument, 0, 'd'},
{"timeout", required_argument, 0, 't'},
{"passthrough", no_argument, 0, 'p'},
{"wireless", no_argument, 0, 'w'},
{0, 0, 0, 0}
};
void usage(void)
{
fprintf(stderr, "ChipCon Tiny Loader Programmer\n");
#ifdef WIN32
fprintf(stderr, "cctl-prog -d COMx [-c] [-f file.hex]\n");
#else
fprintf(stderr, "cctl-prog -d /dev/ttyXYZ [-c] [-f file.hex]\n");
#endif
fprintf(stderr, " --help -h This help\n");
fprintf(stderr, " --console -c Connect console to serial port on device\n");
fprintf(stderr, " --flash=file.hex -f file.hex Reflash device with intel hex file\n");
fprintf(stderr, " --timeout=n -t n Search for bootload string for n seconds\n");
fprintf(stderr, " --passthrough -p Program remote device over passthrough\n");
fprintf(stderr, " --wireless -w Program remote device over wireless ccrl\n");
}
static bool opt_console = false;
static bool opt_flash = false;
static int opt_timeout = 10;
static bool opt_device = false;
static char *flash_filename = NULL;
static char *device_name = NULL;
static bool opt_passthrough = 0;
static bool opt_wireless = 0;
static int serial_timeout = 2;
#ifndef WIN32
static struct termios orig_termios;
#endif
int parse_options(int argc, char **argv)
{
int c;
int option_index;
while(1)
{
c = getopt_long (argc, argv, "hcf:d:t:p:w", long_options, &option_index);
if (c == -1)
break;
switch(c)
{
case 'h':
return 1;
break;
case 'w':
opt_wireless = 1;
serial_timeout = 4;
break;
case 'p':
opt_passthrough = 1;
break;
case 't':
opt_timeout = atoi(optarg);
break;
case 'c':
opt_console = true;
break;
case 'f':
opt_flash = true;
flash_filename = strdup(optarg);
break;
case 'd':
opt_device = true;
device_name = strdup(optarg);
break;
default:
return 1;
break;
}
}
if (!opt_device)
return 1;
if (!opt_flash && !opt_console)
return 1;
return 0;
}
static void do_exit(void)
{
#ifndef WIN32
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
#endif
puts("exiting\n");
}
#ifndef WIN32
static int enableRawMode(void)
{
struct termios raw;
if (!isatty(STDIN_FILENO))
return -1;
if (tcgetattr(STDIN_FILENO,&orig_termios) == -1)
return -1;
raw = orig_termios; /* modify the original mode */
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
raw.c_cc[VMIN] = 1;
raw.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO,TCSAFLUSH,&raw) < 0)
return -1;
return 0;
}
#endif
int serialOpen(char *port)
{
int fd;
#ifndef WIN32
struct termios t_opt;
#else
char path[1024];
HANDLE hCom = NULL;
#endif
#ifdef WIN32
if (port[0] != '\\')
{
snprintf(path, sizeof(path), "\\\\.\\%s", port);
port = path;
}
#endif
#ifndef WIN32
if ((fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0)
{
fprintf(stderr, "Could not open serial port %s\n", port);
return -1;
}
#else
hCom = CreateFile(port, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (!hCom || hCom == INVALID_HANDLE_VALUE )
{
fprintf(stderr, "Invalid handle for serial port\n");
return -1;
}
else
fd = (int)hCom;
#endif
#ifndef WIN32
fcntl(fd, F_SETFL, 0);
tcgetattr(fd, &t_opt);
cfsetispeed(&t_opt, B115200);
cfsetospeed(&t_opt, B115200);
t_opt.c_cflag |= (CLOCAL | CREAD);
t_opt.c_cflag &= ~PARENB;
t_opt.c_cflag &= ~CSTOPB;
t_opt.c_cflag &= ~CSIZE;
t_opt.c_cflag |= CS8;
t_opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
t_opt.c_iflag &= ~(IXON | IXOFF | IXANY);
t_opt.c_oflag &= ~OPOST;
t_opt.c_cc[VMIN] = 0;
t_opt.c_cc[VTIME] = 10;
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &t_opt);
return fd;
#else
{
DCB dcb = {0};
HANDLE hCom = (HANDLE)fd;
COMMTIMEOUTS cto = { 2, 1, 1, 0, 0 };
if(!SetCommTimeouts(hCom,&cto))
{
fprintf(stderr, "SetCommTimeouts failed\n");
return -1;
}
dcb.DCBlength = sizeof(dcb);
dcb.BaudRate = B115200;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.fBinary = 1;
if (!SetCommState(hCom, &dcb))
{
fprintf(stderr, "Failed to setup serial port\n");
return -1;
}
return (int)hCom;
}
#endif
}
#ifdef WIN32
int serialRead(int fd, void* buf, int len)
{
HANDLE hCom = (HANDLE)fd;
int res = 0;
unsigned long bread = 0;
res = ReadFile(hCom, buf, len, &bread, NULL);
if (res == FALSE )
return -1;
else
return bread;
}
#else
int serialRead(int fd, void* buf, int len)
{
struct timeval start, now;
int rc;
gettimeofday(&start, NULL);
do
{
if ((rc = read(fd, buf, len)) < 0)
return rc;
if (rc > 0)
return rc;
gettimeofday(&now, NULL);
}
while((now.tv_sec - start.tv_sec) < serial_timeout);
return 1;
}
#endif
#ifdef WIN32
int serialWrite(int fd, const void* buf, int len)
{
HANDLE hCom = (HANDLE)fd;
int res = 0;
unsigned long bwritten = 0;
res = WriteFile(hCom, buf, len, &bwritten, NULL);
if (res == FALSE )
return -1;
else
return bwritten;
}
#else
int serialWrite(int fd, void* buf, int len)
{
struct timeval start, now;
int rc;
gettimeofday(&start, NULL);
do
{
if ((rc = write(fd, buf, len)) < 0)
return rc;
if (rc > 0)
return rc;
gettimeofday(&now, NULL);
}
while((now.tv_sec - start.tv_sec) < serial_timeout);
return 1;
}
#endif
#ifdef WIN32
int serialClose(int fd)
{
HANDLE hCom = (HANDLE)fd;
CloseHandle(hCom);
return 0;
}
#else
#define serialClose close
#endif
int program_page(int fd, int page)
{
char cmd = 'p';
char rsp;
if (serialWrite(fd, &cmd, 1) <= 0)
return 1;
if (serialWrite(fd, &page, 1) <= 0)
return 1;
if (serialRead(fd, &rsp, 1) <= 0 || rsp != 0)
return 1;
return 0;
}
int read_page(int fd, uint8_t page, uint8_t *data)
{
int remaining;
int rc;
char cmd = 'r';
char rsp;
if (serialWrite(fd, &cmd, 1) <= 0)
return 1;
if (serialWrite(fd, &page, 1) <= 0)
return 1;
remaining = 1024;
while(remaining > 0)
{
rc = serialRead(fd, data + (1024 - remaining), remaining);
if (rc <= 0)
return 1;
remaining -= rc;
}
if (serialRead(fd, &rsp, 1) <= 0 || rsp != 0)
return 1;
return 0;
}
static int already_erased = 0; // passthrough programmer only supports mass erase
int erase_page(int fd, uint8_t page)
{
char cmd = 'e';
char rsp;
if (already_erased && opt_passthrough)
return 0;
if (serialWrite(fd, &cmd, 1) <= 0)
return 1;
if (serialWrite(fd, &page, 1) <= 0)
return 1;
if (serialRead(fd, &rsp, 1) <= 0 || rsp != 0)
{
fprintf(stderr, "erase_page rsp=%02X\n", rsp);
return 1;
}
already_erased = 1;
return 0;
}
int load_data(int fd, uint8_t *data)
{
int remaining;
int rc;
char cmd = 'l';
char rsp;
if (serialWrite(fd, &cmd, 1) <= 0)
return 1;
remaining = 1024;
while(remaining > 0)
{
rc = serialWrite(fd, data + (1024 - remaining), remaining);
if (rc <= 0)
return 1;
remaining -= rc;
}
if (serialRead(fd, &rsp, 1) <= 0 || rsp != 0)
return 1;
return 0;
}
void dump(uint8_t *p, size_t len)
{
while(len--)
printf("%02X", *p++);
printf("\n");
}
int erase_program_verify_page(int fd, uint8_t *data, uint8_t page)
{
uint8_t verbuf[1024];
if (0 != load_data(fd, data))
{
fprintf(stderr, "load_data failed\n");
return 1;
}
if (0 != erase_page(fd, page))
{
fprintf(stderr, "erase_page failed\n");
return 1;
}
if (0 != program_page(fd, page))
{
fprintf(stderr, "program_page failed\n");
return 1;
}
if (0 != read_page(fd, page, verbuf))
{
fprintf(stderr, "read_page failed\n");
return 1;
}
if (0!=memcmp(verbuf, data, 1024))
{
fprintf(stderr, "verify failed\n");
printf("verbuf = ");
dump(verbuf, 1024);
printf("expected = ");
dump(data, 1024);
return 1;
}
return 0;
}
int wait_for_bootloader(int fd, int timeout)
{
uint8_t c = 0;
uint8_t prev_c = 0;
struct timeval start, now;
int rc;
int last_sec = -1;
gettimeofday(&start, NULL);
if (!opt_passthrough && !opt_wireless)
serialWrite(fd, "+++", 5);
printf("Waiting %ds for bootloader, reset board now\n", timeout);
do
{
gettimeofday(&now, NULL);
if (((now.tv_sec - start.tv_sec)) != last_sec)
{
printf(".");
fflush(stdout);
last_sec = (now.tv_sec - start.tv_sec);
}
if ((rc = serialRead(fd, &c, 1)) < 0)
{
fprintf(stderr, "read failed\n");
return 1;
}
else
if (rc == 1)
{
if (opt_passthrough)
{
if (c == 'P' && prev_c == 'P')
break;
}
else if (opt_wireless)
{
if (c == 'W' && prev_c == 'W')
break;
}
else
{
if (c == 'B' && prev_c == 'B')
break;
}
prev_c = c;
}
}
while((now.tv_sec - start.tv_sec) < timeout);
if (now.tv_sec - start.tv_sec >= timeout)
return 1;
c = 0x00;
if (serialWrite(fd, &c, 1) <= 0)
return 1;
return 0;
}
int send_jump(int fd)
{
uint8_t cmd = 'j';
if (serialWrite(fd, &cmd, 1) <= 0)
return 1;
return 0;
}
#ifndef WIN32
void do_console(int fd)
{
fd_set rfds;
struct timeval tv;
int rc;
if (0 != enableRawMode())
{
fprintf(stderr, "Not a TTY?\n");
return;
}
while(1)
{
uint8_t c;
tv.tv_sec = 1;
tv.tv_usec = 0;
int maxfd = 1;
FD_ZERO(&rfds);
FD_SET(0, &rfds);
#ifndef WIN32
FD_SET(fd, &rfds);
maxfd = fd+1;
#endif
rc = select(maxfd, &rfds, NULL, NULL, &tv);
if (rc == -1)
{
fprintf(stderr, "select failed\n");
return;
}
else
if (rc)
{
if (FD_ISSET(0, &rfds))
{
if(read(0, &c, 1) > 0)
{
if (c == 0x03 || c == 0x04) // ctrl-d, ctrl-c
{
return;
}
if (1 != serialWrite(fd, &c, 1))
{
fprintf(stderr, "write error\n");
return;
}
}
}
else
if (FD_ISSET(fd, &rfds))
{
if(serialRead(fd, &c, 1) > 0)
{
if (1 != write(1, &c, 1))
{
fprintf(stderr, "write error\n");
return;
}
}
}
}
else
{
// timeout
}
}
}
#else
DWORD CALLBACK SerialRxThread( HANDLE h )
{
OVERLAPPED ov;
HANDLE hconn = GetStdHandle(STD_INPUT_HANDLE);
BOOL quit = FALSE;
char kb;
INPUT_RECORD b1;
if (hconn == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "Invalid input handle\n");
exit(1);
}
ZeroMemory(&ov,sizeof(ov));
ov.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
if (ov.hEvent == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "CreateEvent failed\n");
SetCommMask(h,0);
return 0;
}
SetConsoleMode(hconn,0);
do
{
char buf[2] = {0,0};
DWORD rd = 0;
WaitForSingleObject(hconn,INFINITE);
// fetch input
if(!ReadConsoleInput(hconn,&b1,1,&rd))
{
fprintf(stderr, "Failed to read from console, perhaps we're not running in cmd.exe?\n");
quit = TRUE;
}
else
{
kb=b1.Event.KeyEvent.uChar.AsciiChar;
buf[0] = kb;
}
if(rd)
{
DWORD wr = 1;
if (buf[0] == 0x3 ) // ctrl-c
{
quit = TRUE;
break;
}
else
{
if (buf[0] != 0x0 && b1.Event.KeyEvent.bKeyDown)
{
if (!WriteFile(h,buf,wr,&wr,&ov) )
{
if(GetLastError() == ERROR_IO_PENDING)
{
if (!GetOverlappedResult(h,&ov,&wr,TRUE) )
{
fprintf(stderr, "GetOverlappedResult failed\n");
quit = TRUE;
}
}
}
else
{
fprintf(stderr, "WriteFile failed\n");
quit = TRUE;
}
}
}
}
}
while(!quit);
if (!SetCommMask(h,0)) // send request to terminate thread
fprintf(stderr, "SetCommMask-GetLastError: %ld\n", GetLastError());
return 0;
}
void do_console(int fd)
{
DWORD mask;
DWORD id;
DWORD i;
OVERLAPPED ov;
HANDLE h = (HANDLE)fd;
// start serial receiver thread
HANDLE hconin = CreateThread(NULL, 0, SerialRxThread, h, 0, &id);
if (hconin == INVALID_HANDLE_VALUE )
{
fprintf(stderr, "CreateThread failed\n");
return;
}
CloseHandle(hconin);
// Overlapped IO event
ZeroMemory(&ov, sizeof(ov));
ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(ov.hEvent == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "CreateThread failed\n");
return;
}
// only interested in rx
if(!SetCommMask(h, EV_RXCHAR))
{
fprintf(stderr, "SetCommMask failed\n");
return;
}
while (1)
{
if (!WaitCommEvent(h, &mask, &ov))
{
if (GetLastError() == ERROR_IO_PENDING )
{
DWORD r;
if (!GetOverlappedResult(h,&ov,&r,TRUE) )
{
fprintf(stderr, "GetOverlappedResult failed\n");
break;
}
}
else
{
break;
}
}
// See if we've been asked to terminate
if ( mask == 0 )
break;
if (mask & EV_RXCHAR) // received data
{
char buf[1024] = {0};
DWORD readcount;
do
{
readcount = 0;
if (!ReadFile(h, buf, sizeof(buf), &readcount, &ov))
{
if (GetLastError() == ERROR_IO_PENDING)
{
if (!GetOverlappedResult(h, &ov, &readcount, TRUE))
{
fprintf(stderr, "GetOverlappedResult failed\n");
break;
}
}
else
{
fprintf(stderr, "ReadFile failed\n");
break;
}
}
for (i=0;i<readcount;i++)
{
putchar(buf[i]);
fflush(stdout);
}
}
while(readcount);
}
mask = 0;
}
CloseHandle(ov.hEvent);
}
#endif
int main(int argc, char *argv[])
{
int fd;
uint8_t *buf;
int i, j;
if (NULL == (buf=malloc(32*1024)))
{
fprintf(stderr, "out of ram\n");
return 1;
}
if (0 != parse_options(argc, argv))
{
usage();
return 1;
}
if ((fd = serialOpen(device_name)) < 0)
{
fprintf(stderr, "Failed to open %s\n", device_name);
return 1;
}
if (opt_flash)
{
memset(buf, 0xFF, 32*1024);
if (0 != read_hexfile(buf, 32*1024, flash_filename))
{
fprintf(stderr, "Failed to read %s\n", flash_filename);
return 1;
}
if (0 != wait_for_bootloader(fd, opt_timeout))
{
fprintf(stderr, "No bootloader detected\n");
return 1;
}
else
{
printf("Bootloader detected\n");
}
for (i=0x400;i<32*1024;i+=1024)
{
bool all_empty = true;
for (j=i;j<i+1024;j++)
{
if (buf[j] != 0xFF)
{
all_empty = false;
break;
}
}
if (!all_empty)
{
printf("Erasing, programming and verifying page %d\n", i/1024);
if (0 != erase_program_verify_page(fd, buf + i, i/1024))
{
fprintf(stderr, "erase_program_verify_page failed\n");
return 1;
}
}
else
{
printf("Erasing page %d\n", i/1024);
if (0 != erase_page(fd, i/1024))
{
fprintf(stderr, "erase failed\n");
return 1;
}
}
}
if (0 != send_jump(fd))
{
fprintf(stderr, "send jump failed\n");
return 1;
}
printf("Programming complete\n");
}
if (opt_console)
{
atexit(do_exit);
printf("Connected to %s, ctrl-c to exit\n", device_name);
do_console(fd);
}
serialClose(fd);
return 0;
}