diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..117dddb --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +p1load: p1load.c ploader.c osint_linux.c + cc -Wall -DMACOSX -o $@ p1load.c ploader.c osint_linux.c + +run: p1load + ./p1load blink.binary -r -t + +clean: + rm -rf p1load diff --git a/osint.h b/osint.h new file mode 100644 index 0000000..1ed95fd --- /dev/null +++ b/osint.h @@ -0,0 +1,55 @@ +/** + * @file osint.h + * + * Serial I/O functions used by PLoadLib.c + * + * Copyright (c) 2009 by John Steven Denson + * Modified in 2011 by David Michael Betz + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +#ifndef __SERIAL_IO_H__ +#define __SERIAL_IO_H__ + +#include + +/* serial i/o definitions */ +#define SERIAL_TIMEOUT -1 + +/* serial i/o routines */ +void serial_use_rts_for_reset(int use_rts); +int serial_find(const char* prefix, int (*check)(const char* port, void* data), void* data); +int serial_init(const char *port, unsigned long baud); +int serial_baud(unsigned long baud); +void serial_done(void); +int tx(uint8_t* buff, int n); +int rx(uint8_t* buff, int n); +int rx_timeout(uint8_t* buff, int n, int timeout); +void hwreset(void); + +/* terminal mode */ +void terminal_mode(int check_for_exit, int pst_mode); + +/* miscellaneous functions */ +void msleep(int ms); + +#endif diff --git a/osint_linux.c b/osint_linux.c new file mode 100644 index 0000000..87f076d --- /dev/null +++ b/osint_linux.c @@ -0,0 +1,467 @@ +/** + * @file osint_linux.c + * + * Serial I/O functions used by PLoadLib.c + * + * Copyright (c) 2009 by John Steven Denson + * Modified in 2011 by David Michael Betz + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "osint.h" + +typedef int HANDLE; +static HANDLE hSerial = -1; +static struct termios old_sparm; + +/* normally we use DTR for reset but setting this variable to non-zero will use RTS instead */ +static int use_rts_for_reset = 0; + +void serial_use_rts_for_reset(int use_rts) +{ + use_rts_for_reset = use_rts; +} + +static void chk(char *fun, int sts) +{ + if (sts != 0) + printf("%s failed\n", fun); +} + +int serial_find(const char* prefix, int (*check)(const char* port, void* data), void* data) +{ + char path[PATH_MAX]; + int prefixlen = strlen(prefix); + struct dirent *entry; + DIR *dirp; + + if (!(dirp = opendir("/dev"))) + return -1; + + while ((entry = readdir(dirp)) != NULL) { + if (strncmp(entry->d_name, prefix, prefixlen) == 0) { + sprintf(path, "/dev/%s", entry->d_name); + if ((*check)(path, data) == 0) { + closedir(dirp); + return 0; + } + } + } + + closedir(dirp); + return -1; +} + +static void sigint_handler(int signum) +{ + serial_done(); + exit(1); +} + +/** + * open serial port + * @param port - COMn port name + * @param baud - baud rate + * @returns 1 for success and 0 for failure + */ +int serial_init(const char* port, unsigned long baud) +{ + struct termios sparm; + + /* open the port */ +#ifdef MACOSX + hSerial = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK); +#else + hSerial = open(port, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); +#endif + if(hSerial == -1) { + //printf("error: opening '%s' -- %s\n", port, strerror(errno)); + return 0; + } + + signal(SIGINT, sigint_handler); + + /* set the terminal to exclusive mode */ + if (ioctl(hSerial, TIOCEXCL) != 0) { + //printf("error: can't open terminal for exclusive access\n"); + close(hSerial); + return 0; + } + + fcntl(hSerial, F_SETFL, 0); + + /* set the baud rate */ + if (!serial_baud(baud)) { + close(hSerial); + return 0; + } + + /* get the current options */ + chk("tcgetattr", tcgetattr(hSerial, &old_sparm)); + sparm = old_sparm; + + /* set raw input */ +#ifdef MACOSX + cfmakeraw(&sparm); + sparm.c_cc[VTIME] = 0; + sparm.c_cc[VMIN] = 1; +#else + memset(&sparm, 0, sizeof(sparm)); + sparm.c_cflag = CS8 | CLOCAL | CREAD; + sparm.c_lflag = 0; // &= ~(ICANON | ECHO | ECHOE | ISIG); + sparm.c_oflag = 0; // &= ~OPOST; + + sparm.c_iflag = IGNPAR | IGNBRK; + sparm.c_cc[VTIME] = 0; + sparm.c_cc[VMIN] = 1; +#endif + + /* set the options */ + chk("tcflush", tcflush(hSerial, TCIFLUSH)); + chk("tcsetattr", tcsetattr(hSerial, TCSANOW, &sparm)); + + return 1; +} + +/** + * change the baud rate of the serial port + * @param baud - baud rate + * @returns 1 for success and 0 for failure + */ +int serial_baud(unsigned long baud) +{ + struct termios sparm; + int tbaud = 0; + + switch(baud) { + case 0: // default + tbaud = B115200; + break; +#ifdef B921600 + case 921600: + tbaud = B921600; + break; +#endif +#ifdef B576000 + case 576000: + tbaud = B576000; + break; +#endif +#ifdef B500000 + case 500000: + tbaud = B500000; + break; +#endif +#ifdef B460800 + case 460800: + tbaud = B460800; + break; +#endif +#ifdef B230400 + case 230400: + tbaud = B230400; + break; +#endif + case 115200: + tbaud = B115200; + break; + case 57600: + tbaud = B57600; + break; + case 38400: + tbaud = B38400; + break; + case 19200: + tbaud = B19200; + break; + case 9600: + tbaud = B9600; + break; + default: + printf("Unsupported baudrate. Use "); + tbaud = baud; break; +#ifdef B921600 + printf("921600, "); +#endif +#ifdef B576000 + printf("576000, "); +#endif +#ifdef B500000 + printf("500000, "); +#endif +#ifdef B460800 + printf("460800, "); +#endif +#ifdef B230400 + printf("230400, "); +#endif + printf("115200, 57600, 38400, 19200, or 9600\n"); + serial_done(); + exit(2); + break; + } + + /* get the current options */ + chk("tcgetattr", tcgetattr(hSerial, &sparm)); + + /* set raw input */ +#ifdef MACOSX + chk("cfsetspeed", cfsetspeed(&sparm, tbaud)); +#else + chk("cfsetispeed", cfsetispeed(&sparm, tbaud)); + chk("cfsetospeed", cfsetospeed(&sparm, tbaud)); +#endif + + /* set the options */ + chk("tcflush", tcflush(hSerial, TCIFLUSH)); + chk("tcsetattr", tcsetattr(hSerial, TCSANOW, &sparm)); + + return 1; +} + +/** + * close serial port + */ +void serial_done(void) +{ + if (hSerial != -1) { + tcflush(hSerial, TCIOFLUSH); + tcsetattr(hSerial, TCSANOW, &old_sparm); + ioctl(hSerial, TIOCNXCL); + close(hSerial); + hSerial = -1; + } +} + +/** + * receive a buffer + * @param buff - char pointer to buffer + * @param n - number of bytes in buffer to read + * @returns number of bytes read + */ +int rx(uint8_t* buff, int n) +{ + ssize_t bytes = read(hSerial, buff, n); + if(bytes < 1) { + printf("Error reading port: %d\n", (int)bytes); + return 0; + } + return (int)bytes; +} + +/** + * transmit a buffer + * @param buff - char pointer to buffer + * @param n - number of bytes in buffer to send + * @returns zero on failure + */ +int tx(uint8_t* buff, int n) +{ + ssize_t bytes; +#if 0 + int j = 0; + while(j < n) { + printf("%02x ",buff[j++]); + } + printf("tx %d byte(s)\n",n); +#endif + bytes = write(hSerial, buff, n); + if(bytes != n) { + printf("Error writing port\n"); + return 0; + } + return (int)bytes; +} + +/** + * receive a buffer with a timeout + * @param buff - char pointer to buffer + * @param n - number of bytes in buffer to read + * @param timeout - timeout in milliseconds + * @returns number of bytes read or SERIAL_TIMEOUT + */ +int rx_timeout(uint8_t* buff, int n, int timeout) +{ + ssize_t bytes = 0; + struct timeval toval; + fd_set set; + + FD_ZERO(&set); + FD_SET(hSerial, &set); + + toval.tv_sec = timeout / 1000; + toval.tv_usec = (timeout % 1000) * 1000; + + if (select(hSerial + 1, &set, NULL, NULL, &toval) > 0) { + if (FD_ISSET(hSerial, &set)) + bytes = read(hSerial, buff, n); + } + + return (int)(bytes > 0 ? bytes : SERIAL_TIMEOUT); +} + +/** + * hwreset ... resets Propeller hardware using DTR or RTS + * @param sparm - pointer to DCB serial control struct + * @returns void + */ +void hwreset(void) +{ + int cmd = use_rts_for_reset ? TIOCM_RTS : TIOCM_DTR; + ioctl(hSerial, TIOCMBIS, &cmd); /* assert bit */ + msleep(10); + ioctl(hSerial, TIOCMBIC, &cmd); /* clear bit */ + msleep(90); + tcflush(hSerial, TCIFLUSH); +} + +/** + * sleep for ms milliseconds + * @param ms - time to wait in milliseconds + */ +void msleep(int ms) +{ +#if 0 + volatile struct timeb t0, t1; + do { + ftime((struct timeb*)&t0); + do { + ftime((struct timeb*)&t1); + } while (t1.millitm == t0.millitm); + } while(ms-- > 0); +#else + usleep(ms * 1000); +#endif +} + +#define ESC 0x1b /* escape from terminal mode */ + +/** + * simple terminal emulator + */ +void terminal_mode(int check_for_exit, int pst_mode) +{ + struct termios oldt, newt; + char buf[128], realbuf[256]; // double in case buf is filled with \r in PST mode + ssize_t cnt; + fd_set set; + int exit_char = 0xdead; /* not a valid character */ + int sawexit_char = 0; + int sawexit_valid = 0; + int exitcode = 0; + int continue_terminal = 1; + + tcgetattr(STDIN_FILENO, &oldt); + newt = oldt; + newt.c_lflag &= ~(ICANON | ECHO | ISIG); + newt.c_iflag &= ~(ICRNL | INLCR); + newt.c_oflag &= ~OPOST; + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + + if (check_for_exit) + { + exit_char = 0xff; + } + +#if 0 + /* make it possible to detect breaks */ + tcgetattr(hSerial, &newt); + newt.c_iflag &= ~IGNBRK; + newt.c_iflag |= PARMRK; + tcsetattr(hSerial, TCSANOW, &newt); +#endif + + do { + FD_ZERO(&set); + FD_SET(hSerial, &set); + FD_SET(STDIN_FILENO, &set); + if (select(hSerial + 1, &set, NULL, NULL, NULL) > 0) { + if (FD_ISSET(hSerial, &set)) { + if ((cnt = read(hSerial, buf, sizeof(buf))) > 0) { + int i; + // check for breaks + ssize_t realbytes = 0; + for (i = 0; i < cnt; i++) { + if (sawexit_valid) + { + exitcode = buf[i]; + continue_terminal = 0; + } + else if (sawexit_char) { + if (buf[i] == 0) { + sawexit_valid = 1; + } else { + realbuf[realbytes++] = exit_char; + realbuf[realbytes++] = buf[i]; + sawexit_char = 0; + } + } else if (((int)buf[i] & 0xff) == exit_char) { + sawexit_char = 1; + } else { + realbuf[realbytes++] = buf[i]; + if (pst_mode && buf[i] == '\r') + realbuf[realbytes++] = '\n'; + } + } + write(fileno(stdout), realbuf, realbytes); + } + } + if (FD_ISSET(STDIN_FILENO, &set)) { + if ((cnt = read(STDIN_FILENO, buf, sizeof(buf))) > 0) { + int i; + for (i = 0; i < cnt; ++i) { + //printf("%02x\n", buf[i]); + if (buf[i] == ESC) + goto done; + } + write(hSerial, buf, cnt); + } + } + } + } while (continue_terminal); + +done: + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + + if (sawexit_valid) + { + exit(exitcode); + } + +} + diff --git a/p1load.c b/p1load.c new file mode 100644 index 0000000..89a7c8e --- /dev/null +++ b/p1load.c @@ -0,0 +1,313 @@ +#include +#include +#include +#include +#include +#include +#include "ploader.h" +#include "osint.h" + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +/* port prefix */ +#if defined(CYGWIN) || defined(WIN32) || defined(MINGW) + #define PORT_PREFIX "" +#elif defined(LINUX) + #define PORT_PREFIX "ttyUSB" +#elif defined(MACOSX) + #define PORT_PREFIX "cu.usbserial" +#else + #define PORT_PREFIX "" +#endif + +/* defaults */ +#define BAUD_RATE 115200 + +/* CheckPort state structure */ +typedef struct { + int baud; + int verbose; + char *actualport; +} CheckPortInfo; + +/* CheckPort result codes */ +enum { + CHECK_PORT_OK, + CHECK_PORT_OPEN_FAILED, + CHECK_PORT_NO_PROPELLER +}; + +static void cb_reset(void *data) { hwreset(); } +static int cb_tx(void *data, uint8_t* buf, int n) { return tx(buf, n); } +static int cb_rx_timeout(void *data, uint8_t* buf, int n, int timeout) { return rx_timeout(buf, n, timeout); } + +static PL_serial serial = { cb_reset, cb_tx, cb_rx_timeout }; +static PL_state state; +static int version; + +static void Usage(void); +static uint8_t *ReadEntireFile(char *name, long *pSize); +static int ShowPort(const char* port, void* data); +static void ShowPorts(char *prefix); +static int CheckPort(const char* port, void* data); +static int InitPort(char *prefix, char *port, int baud, int verbose, char *actualport); +static int OpenPort(const char* port, int baud); + +int main(int argc, char *argv[]) +{ + char actualPort[PATH_MAX], *port, *p; + int baudRate, baudRate2, verbose, terminalMode, pstMode, i; + char *file = NULL; + int loadType = 0; + long imageSize; + uint8_t *image; + + /* initialize */ + baudRate = baudRate2 = BAUD_RATE; + verbose = terminalMode = pstMode = FALSE; + port = NULL; + + /* initialize the loader */ + PL_Init(&state, &serial, NULL); + + /* process the position-independent arguments */ + for (i = 1; i < argc; ++i) { + + /* handle switches */ + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case 'b': + if (argv[i][2]) + p = &argv[i][2]; + else if (++i < argc) + p = argv[i]; + else + Usage(); + if (*p != ':') + baudRate = baudRate2 = atoi(p); + if ((p = strchr(p, ':')) != NULL) { + if (*++p != ':' && *p != '\0') + baudRate2 = atoi(p); + } + break; + case 'e': + loadType |= LOAD_TYPE_EEPROM; + break; + case 'p': + if (argv[i][2]) + port = &argv[i][2]; + else if (++i < argc) + port = argv[i]; + else + Usage(); +#if defined(CYGWIN) || defined(WIN32) || defined(LINUX) + if (isdigit((int)port[0])) { +#if defined(CYGWIN) || defined(WIN32) + static char buf[10]; + sprintf(buf, "COM%d", atoi(port)); + port = buf; +#endif +#if defined(LINUX) + static char buf[64]; + sprintf(buf, "/dev/ttyUSB%d", atoi(port)); + port = buf; +#endif + } +#endif +#if defined(MACOSX) + if (port[0] != '/') { + static char buf[64]; + sprintf(buf, "/dev/cu.usbserial-%s", port); + port = buf; + } +#endif + break; + case 'P': + ShowPorts(PORT_PREFIX); + break; + case 'r': + loadType |= LOAD_TYPE_RUN; + break; + case 'T': + pstMode = TRUE; + // fall through + case 't': + terminalMode = TRUE; + break; + case 'v': + verbose = TRUE; + break; + case '?': + /* fall through */ + default: + Usage(); + break; + } + } + + /* handle the input filename */ + else { + if (file) + Usage(); + file = argv[i]; + } + } + + switch (InitPort(PORT_PREFIX, port, baudRate, verbose, actualPort)) { + case CHECK_PORT_OK: + printf("Found propeller version %d on %s\n", version, actualPort); + break; + case CHECK_PORT_OPEN_FAILED: + printf("error: opening serial port '%s'\n", port); + perror("Error is "); + return 1; + case CHECK_PORT_NO_PROPELLER: + if (port) + printf("error: no propeller chip on port '%s'\n", port); + else + printf("error: can't find a port with a propeller chip\n"); + return 1; + } + + if (file) { + if ((image = ReadEntireFile(file, &imageSize)) != NULL) { + printf("Loading '%s' (%ld bytes)\n", file, imageSize); + if (PL_LoadSpinBinary(&state, loadType, image, imageSize) != 0) + printf("Load failed!\n"); + } + } + + /* enter terminal mode if requested */ + if (terminalMode) { + printf("[ Entering terminal mode. Type ESC or Control-C to exit. ]\n"); + fflush(stdout); + if (baudRate2 != baudRate) + serial_baud(baudRate2); + terminal_mode(FALSE, pstMode); + } + + return 0; +} + +/* Usage - display a usage message and exit */ +static void Usage(void) +{ +printf("\ +p1load - a simple loader for the propeller - version 0.009, 2014-08-10\n\ +usage: p1load\n\ + [ -b baud ] baud rate (default is %d)\n\ + [ -p port ] serial port (default is to auto-detect the port)\n\ + [ -e ] write a bootable image to EEPROM\n\ + [ -P ] list available serial ports\n\ + [ -r ] run the program after loading\n\ + [ -t ] enter terminal mode after running the program\n\ + [ -T ] enter PST-compatible terminal mode\n\ + [ -v ] verbose output\n\ + [ -? ] display a usage message and exit\n\ + file[,addr]... files to load\n", BAUD_RATE); + exit(1); +} + +/* ReadEntireFile - read an entire file into an allocated buffer */ +static uint8_t *ReadEntireFile(char *name, long *pSize) +{ + uint8_t *buf; + long size; + FILE *fp; + + /* open the file in binary mode */ + if (!(fp = fopen(name, "rb"))) + return NULL; + + /* get the file size */ + fseek(fp, 0L, SEEK_END); + size = ftell(fp); + fseek(fp, 0L, SEEK_SET); + + /* allocate a buffer for the file contents */ + if (!(buf = (uint8_t *)malloc(size))) { + fclose(fp); + return NULL; + } + + /* read the contents of the file into the buffer */ + if (fread(buf, 1, size, fp) != size) { + free(buf); + fclose(fp); + return NULL; + } + + /* close the file ad return the buffer containing the file contents */ + *pSize = size; + fclose(fp); + return buf; +} + +static int ShowPort(const char* port, void* data) +{ + printf("%s\n", port); + return 1; +} + +static void ShowPorts(char *prefix) +{ + serial_find(prefix, ShowPort, NULL); +} + +static int CheckPort(const char* port, void* data) +{ + CheckPortInfo* info = (CheckPortInfo*)data; + int rc; + if (info->verbose) + printf("Trying %s \n", port); fflush(stdout); + if ((rc = OpenPort(port, info->baud)) != CHECK_PORT_OK) + return rc; + if (info->actualport) { + strncpy(info->actualport, port, PATH_MAX - 1); + info->actualport[PATH_MAX - 1] = '\0'; + } + return 0; +} + +static int InitPort(char *prefix, char *port, int baud, int verbose, char *actualport) +{ + int result; + + if (port) { + if (actualport) { + strncpy(actualport, port, PATH_MAX - 1); + actualport[PATH_MAX - 1] = '\0'; + } + result = OpenPort(port, baud); + } + else { + CheckPortInfo info; + info.baud = baud; + info.verbose = verbose; + info.actualport = actualport; + if (serial_find(prefix, CheckPort, &info) == 0) + result = CHECK_PORT_OK; + else + result = CHECK_PORT_NO_PROPELLER; + } + + return result; +} + +static int OpenPort(const char* port, int baud) +{ + /* open the port */ + if (serial_init(port, baud) == 0) + return CHECK_PORT_OPEN_FAILED; + + /* check for a propeller on this port */ + if (!PL_HardwareFound(&state, &version)) { + serial_done(); + return CHECK_PORT_NO_PROPELLER; + } + + return CHECK_PORT_OK; +} diff --git a/ploader.c b/ploader.c new file mode 100644 index 0000000..b924e94 --- /dev/null +++ b/ploader.c @@ -0,0 +1,175 @@ +#include +#include "ploader.h" + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#define ACK_TIMEOUT 20 + +static void SerialInit(PL_state *state); +static void TByte(PL_state *state, uint8_t x); +static void TLong(PL_state *state, uint32_t x); +static void TComm(PL_state *state); +static int RBit(PL_state *state, int timeout); +static int IterateLFSR(PL_state *state); + +void PL_Init(PL_state *state, PL_serial *serial, void *data) +{ + state->serial = serial; + state->serialData = data; +} + +/* PL_LoadSpinBinary - load a spin binary using the rom loader */ +int PL_LoadSpinBinary(PL_state *state, int loadType, uint8_t *image, int size) +{ + int retries = 100; + uint8_t buf[1]; + int i; + + TLong(state, loadType); + TLong(state, size / sizeof(uint32_t)); + + /* download the spin binary */ + for (i = 0; i < size; i += 4) { + uint32_t data = image[i] | (image[i + 1] << 8) | (image[i + 2] << 16) | (image[i + 3] << 24); + TLong(state, data); + } + TComm(state); + usleep(1000); + + /* wait for an ACK */ + while (--retries >= 0) { + TByte(state, 0xf9); + TComm(state); + if ((*state->serial->rx_timeout)(state->serialData, buf, 1, ACK_TIMEOUT) <= 0) + continue; + if (buf[0] == 0xfe) + break; + } + + return retries >= 0 ? 0 : -1; +} + +/* this code is adapted from Chip Gracey's PNut IDE */ + +int PL_HardwareFound(PL_state *state, int *pVersion) +{ + int version, i; + + /* initialize the serial buffers */ + SerialInit(state); + + /* reset the propeller */ + (*state->serial->reset)(state->serialData); + + /* send the connect string + blanks for echoes */ + TByte(state, 0xf9); + state->lfsr = 'P'; + for (i = 0; i < 250; ++i) + TByte(state, IterateLFSR(state) | 0xfe); + for (i = 0; i < 250 + 8; ++i) + TByte(state, 0xf9); + TComm(state); + + /* receive the connect string */ + for (i = 0; i < 250; ++i) + if (RBit(state, 100) != IterateLFSR(state)) + return FALSE; + + /* receive the chip version */ + for (version = i = 0; i < 8; ++i) { + int bit = RBit(state, 50); + if (bit < 0) { + return FALSE; + version = ((version >> 1) & 0x7f) | (bit << 7); + } + *pVersion = version; + + /* return successfully */ + return TRUE; +} + +/* SerialInit - initialize the serial buffers */ +static void SerialInit(PL_state *state) +{ + state->txcnt = 0; + state->rxnext = 0; + state->rxcnt = 0; +} + +/* TByte - add a byte to the transmit buffer */ +static void TByte(PL_state *state, uint8_t x) +{ + state->txbuf[state->txcnt++] = x; + if (state->txcnt >= sizeof(state->txbuf)) + TComm(state); +} + +/* TLong - add a long to the transmit buffer */ +static void TLong(PL_state *state, uint32_t x) +{ + int i; + for (i = 0; i < 11; ++i) { + +#if 0 + // p2 code + uint8_t byte = 0x92 + | ((i == 10 ? -1 : 0) & 0x60) + | ((x >> 31) & 1) + | (((x >> 30) & 1) << 3) + | (((x >> 29) & 1) << 6); + TByte(state, byte); + x <<= 3; +#else + // p1 code + uint8_t byte = 0x92 + | ((i == 10 ? -1 : 0) & 0x60) + | (x & 1) + | ((x & 2) << 2) + | ((x & 4) << 4); + TByte(state, byte); + x >>= 3; +#endif + + } +} + +/* TComm - write the transmit buffer to the port */ +static void TComm(PL_state *state) +{ + (*state->serial->tx)(state->serialData, state->txbuf, state->txcnt); + state->txcnt = 0; +} + +/* RBit - receive a bit with a timeout */ +static int RBit(PL_state *state, int timeout) +{ + int result; + for (;;) { + if (state->rxnext >= state->rxcnt) { + state->rxcnt = (*state->serial->rx_timeout)(state->serialData, state->rxbuf, sizeof(state->rxbuf), timeout); + if (state->rxcnt <= 0) { + /* hardware lost */ + return -1; + } + state->rxnext = 0; + } + result = state->rxbuf[state->rxnext++] - 0xfe; + if ((result & 0xfe) == 0) + return result; + /* checksum error */ + } +} + +/* IterateLFSR - get the next bit in the lfsr sequence */ +static int IterateLFSR(PL_state *state) +{ + uint8_t lfsr = state->lfsr; + int result = lfsr & 1; + state->lfsr = ((lfsr << 1) & 0xfe) | (((lfsr >> 7) ^ (lfsr >> 5) ^ (lfsr >> 4) ^ (lfsr >> 1)) & 1); + return result; +} + +/* end of code adapted from Chip Gracey's PNut IDE */ diff --git a/ploader.h b/ploader.h new file mode 100644 index 0000000..1fb6e2a --- /dev/null +++ b/ploader.h @@ -0,0 +1,51 @@ +#ifndef __PLOADER_H__ +#define __PLOADER_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define LOAD_TYPE_SHUTDOWN 0 +#define LOAD_TYPE_RUN 1 +#define LOAD_TYPE_EEPROM 2 +#define LOAD_TYPE_EEPROM_RUN 3 + +/* serial port interface - filled in by the user prior to calling PL_Init */ +typedef struct { + void (*reset)(void *data); + int (*tx)(void *data, uint8_t* buf, int n); + int (*rx_timeout)(void *data, uint8_t* buf, int n, int timeout); +} PL_serial; + +/* loader state structure - filled in by the loader functions */ +typedef struct { + PL_serial *serial; + void *serialData; + uint8_t txbuf[1024]; + int txcnt; + uint8_t rxbuf[1024]; + int rxnext; + int rxcnt; + uint8_t lfsr; +} PL_state; + +/* PL_Init - Initializes the loader state structure. */ +void PL_Init(PL_state *state, PL_serial *serial, void *data); + +/* PL_HardwareFound - Sends the handshake sequence and returns non-zero if a Propeller + chip is found on the serial interface and also sets the version parameter to the + chip version. +*/ +int PL_HardwareFound(PL_state *state, int *pVersion); + +/* PL_LoadSpinBinary - Loads a Spin binary image. Must be called immediatel following a + successful call to PL_HardwareFound. +*/ +int PL_LoadSpinBinary(PL_state *state, int loadType, uint8_t *image, int size); + +#ifdef __cplusplus +} +#endif + +#endif