#include #include #include #include #include #include #include #include #include #include #ifdef __CYGWIN__ #undef WIN32 #endif #ifndef WIN32 #include #include #else #include #include #include #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