/** * tvm.c * By Jose Solorzano. * Some functions adapded from Kekoa Proudfoot's firmdl.c, * described in comment below. License information also below. */ /* * firmdl.c * * A hack to download firmware to the RCX. * * usage: firmdl Firm0309.lgo * * Under IRIX, Linux, and Solaris, you should be able to compile this * program with cc firmdl.c -o firmdl. I don't know about other versions * of Unix, although I'd be interested in hearing about compatibility * issues that you are able to fix. * * Set DEFAULTTTY to the serial device you want to use. * Set the RCXTTY environment variable to override DEFAULTTTY. * * Based on send.c and srec.c. Maybe someday I will distribute my tools * as multiple files... * * Some additional documentation is available at: * * http://graphics.stanford.edu/~kekoa/rcx/tools.html * * Acknowledgements: * * Laurent Demailly pointed out I didn't transfer some fixes from * send.c over to this file. He also mentioned that this program * compiles fine under Solaris 2.6. * Allen Martin mentioned his modification of not sending all 4K if the * firmware is shorter than that. I discovered that the correct way * to do this was to send everything but the trailing zero bytes, * and before I had a chance to implement this, Markus Noga sent me * the changes needed to implement this. I incorporated the changes * with modifications, plus a few others to make the software a bit * more robust. * Markus forwarded a message from Gavin Smyth that pointed out a * problem with an uninitialized variable. Gavin also pointed out * that this program compiles fine under Cygwin. * In a separate message, Gavin suggested a small change to shorten the * 0.3 ms pause during the download. * Luis Villa noticed a problem with certain s-record files; the * problem turned out to be long S0 records generated by the linker. * Changed the source to allow these (improper?) records. */ /* * Copyright (C) 1998, 1999, Kekoa Proudfoot. All Rights Reserved. * * License to copy, use, and modify this software is granted provided that * this notice is retained in any copies of any part of this software. * * The author makes no guarantee that this software will compile or * function correctly. Also, if you use this software, you do so at your * own risk. * * Kekoa Proudfoot * kekoa@graphics.stanford.edu * 10/3/98 */ #include #include #include #include #include #include #include #include #include #include #include #include typedef unsigned char byte; // This is for CygWin: #ifndef O_BINARY #define O_BINARY 0 #endif #if defined(LINUX) || defined(linux) #define DEFAULTTTY "/dev/ttyS0" /* Linux - COM1 */ #elif defined (_WIN32) || defined(__CYGWIN32__) #define DEFAULTTTY "com1" /* Cygwin - COM1 */ #elif defined (sun) #define DEFAULTTTY "/dev/ttya" /* Solaris - first serial port - untested */ #else #define DEFAULTTTY "/dev/ttyd2" /* IRIX - second serial port */ #endif #define DEBUG 0 #define ERR_NOECHO -1 #define ERR_BADECHO -2 char *progname; /* RCX routines */ #define BUFFERSIZE 4096 #define TOWRITEMAX 100 #define RETRIES 5 #define RESPONSE_LENGTH 11 long receive_packet (long fd, byte *buffer, long length); long rcx_init(char *tty) { long fd; struct termios ios; if ((fd = open(tty, O_RDWR)) < 0) { perror("open"); exit(1); } if (!isatty(fd)) { close(fd); fprintf(stderr, "%s: not a tty\n", tty); exit(1); } memset(&ios, 0, sizeof(ios)); ios.c_cflag = CREAD | CLOCAL | CS8 | PARENB | PARODD; cfsetispeed(&ios, B2400); cfsetospeed(&ios, B2400); if (tcsetattr(fd, TCSANOW, &ios) == -1) { perror("tcsetattr"); exit(1); } return fd; } static void sleep_us (unsigned long us) { struct timeval tval; tval.tv_sec = us / 1000000; tval.tv_usec = us % 1000000; select (0, NULL, NULL, NULL, &tval); } void rcx_close(long fd) { close(fd); } long send_message (long fd, byte opcode, byte *buffer, long length) { byte *actualBuffer; byte *actualEcho; byte checkSum; long actualLength; long numWriten; long i, response; // Compute checksum checkSum = 0; for (i = 0; i < length; i++) checkSum += buffer[i]; checkSum += opcode; // Create header of actual bufer to be sent actualLength = (length + 2) * 2 + 3; actualBuffer = (byte *) malloc (actualLength); actualEcho = (byte *) malloc (actualLength); actualBuffer[0] = 0x55; actualBuffer[1] = 0xFF; actualBuffer[2] = 0x00; actualBuffer[3] = opcode; actualBuffer[4] = ~opcode; // Create rest of packet for (i = 0; i < length; i++) { actualBuffer[i*2 + 5] = buffer[i]; actualBuffer[i*2 + 6] = ~buffer[i]; } actualBuffer[i*2 + 5] = checkSum; actualBuffer[i*2 + 6] = ~checkSum; #if DEBUG > 1 for (i = 0; i < actualLength; i++) { printf ("%X ", (int) actualBuffer[i]); } printf ("\n"); #endif numWriten = write (fd, actualBuffer, actualLength); if (numWriten != actualLength) { fprintf (stderr, "Write error: Wrote only %d of %d bytes. Probably a " "resource allocation problem.\n", (int) numWriten, (int) actualLength); exit (1); } #if DEBUG > 1 printf ("Wrote %d bytes.\n", (int) numWriten); #endif response = receive_packet (fd, actualEcho, actualLength); if (response == -1) response = ERR_NOECHO; else { for (i = 0; i < actualLength; i++) { if (actualBuffer[i] != actualEcho[i]) { #if DEBUG > 1 printf ("%d != %d\n", (int) actualBuffer[i], (int) actualEcho[i]); #endif break; } } if (i < actualLength) response = ERR_BADECHO; else response = numWriten; } free (actualBuffer); free (actualEcho); return response; } long send_byte (long fd, byte data) { return send_message (fd, (byte) 0xF7, &data, 1); } ushort get_checksum (byte *data, long length) { long i; ushort checksum = 0; for (i = 0; i < length; i++) checksum += data[i]; return checksum; } long start_firmware_download (long fd, ushort address, byte *data, long length) { ushort checksum; byte buffer[5]; checksum = get_checksum (data, length); buffer[0] = (byte) ((address >> 0) & 0xFF); buffer[1] = (byte) ((address >> 8) & 0xFF); buffer[2] = (byte) ((checksum >> 0) & 0xFF); buffer[3] = (byte) ((checksum >> 8) & 0xFF); buffer[4] = 0; return send_message (fd, 0x75, buffer, 5); } long transfer_data (long fd, ushort index, byte *buffer, long length) { byte *actualBuffer; byte checkSum; long actualLength; long r, i; actualLength = length + 5; actualBuffer = (byte *) malloc (actualLength); actualBuffer[0] = (byte) ((index >> 0) & 0xFF); actualBuffer[1] = (byte) ((index >> 8) & 0xFF); actualBuffer[2] = (byte) ((length >> 0) & 0xFF); actualBuffer[3] = (byte) ((length >> 8) & 0xFF); checkSum = 0; for (i = 0; i < length; i++) { checkSum += buffer[i]; actualBuffer[4 + i] = buffer[i]; } actualBuffer[4 + i] = checkSum; r = send_message (fd, 0x45, actualBuffer, actualLength); free (actualBuffer); return r; } long receive_packet (long fd, byte *buffer, long length) { long i; fd_set fds; struct timeval tv; long count; long offset; long numTries; offset = 0; numTries = 10; while (numTries--) { while (offset < length) { FD_ZERO(&fds); FD_SET(fd, &fds); tv.tv_sec = 0; tv.tv_usec = 300000; if (select(FD_SETSIZE, &fds, NULL, NULL, &tv) == -1) { perror("select"); exit(1); } if (!FD_ISSET(fd, &fds)) { #if DEBUG > 1 printf ("Retrying: %d\n", (int) numTries); #endif break; } if ((count = read(fd, &buffer[offset], length - offset)) == -1) { printf ("read error\n"); exit(1); } #if DEBUG > 2 printf ("Received %d bytes\n", (int) count); printf (" "); for (i = offset; i < offset + count; i++) { printf ("%X ", (int) buffer[i]); } printf ("\n"); #endif offset += count; if (offset >= length) { #if DEBUG > 1 printf ("RCX responds: "); for (i = 0; i < length; i++) { printf ("%X ", (int) buffer[i]); } printf ("\n"); #endif return offset; } } } return -1; } void check_status (int code) { switch (code) { case ERR_NOECHO: printf ("No echo from RCX. Try again.\n"); exit(1); case ERR_BADECHO: printf ("Bad echo from RCX. Try again.\n"); exit(1); } } int main(int argc, char **argv) { byte *pBinary; byte *pSend; byte response[RESPONSE_LENGTH]; long i; long numRead; char *tty; long fd; long pDesc, pLength, pTotal; long r, index, rest, numToWrite, offset; if (argc != 2) { fprintf(stderr, "%s downloads a file written by the linker.\n", argv[0]); fprintf(stderr, "Use: %s filename\n", argv[0]); exit(1); } if ((pDesc = open(argv[1], O_RDONLY | O_BINARY)) == -1) { fprintf(stderr, "%s: failed to open file %s\n", argv[0], argv[1]); exit(1); } /* Open the serial port */ tty = getenv("RCXTTY"); if (tty == NULL) { printf ("RCXTTY not defined. Using: %s\n", DEFAULTTTY); tty = DEFAULTTTY; } fd = rcx_init(tty); pLength = lseek (pDesc, 0, SEEK_END); if (pLength > 0xFFFF) { printf ("Huge file: %d bytes\n", (int) pLength); exit (1); } lseek (pDesc, 0, SEEK_SET); pBinary = (void *) malloc (pLength); pTotal = 0; while (pTotal < pLength) { r = read (pDesc, pBinary + pTotal, pLength - pTotal); if (r == -1 || r == 0) { printf ("Unexpected EOF in %s. Read only %ld bytes.\n", argv[1], pTotal); exit (1); } pTotal += r; } if (pBinary[0] != ((MAGIC >> 8) & 0xFF) || pBinary[1] != ((MAGIC >> 0) & 0xFF)) { printf ("Magic number is not right. Linker used was for emulation only?\n"); exit (1); } // Set program-download message response[0] = (byte) (MAGIC >> 8); response[1] = (byte) (MAGIC & 0xFF); send_message (fd, (byte) 0x12, response, 2); numRead = receive_packet (fd, response, RESPONSE_LENGTH); sleep_us (1000); if (numRead != RESPONSE_LENGTH) { printf (numRead == -1 ? "No response from RCX. " : "Bad response from RCX. "); printf ("Please make sure RCX has leJOS firmware " "and is in range. The firmware must be in program download mode. " "Turn RCX off and on if necessary.\n"); exit (1); } #if 0 if (numRead != -1) { for (index = 0; index < numRead; index++) { printf ("byte %d = 0x%X\n", (int) index, (int) response[index]); } } #endif if (response[5] != (byte) (MAGIC >> 8) || response[7] != (byte) (MAGIC & 0xFF)) { printf ("Unexpected response from RCX. The RCX either doesn't have valid leJOS firmware or " "it is not in program-download mode. (lejosfirmdl downloads firmware).\n"); exit (1); } sleep_us (10000); // Transfer data index = 0; rest = pLength; offset = 0; do { numToWrite = (rest > TOWRITEMAX) ? TOWRITEMAX : rest; index = (rest > TOWRITEMAX) ? index + 1 : 0; //printf ("transfering %ld\n", (long) index); transfer_data (fd, index, pBinary + offset, numToWrite); if (index != 0) { sleep_us (10000); numRead = receive_packet (fd, response, 9); //printf ("numread = %ld\n", numRead); if (numRead != 9) { fprintf (stderr, "transfer data: invalid response length.\n"); exit (1); } if (response[5] != 0) { fprintf (stderr, "transfer data: RCX says there's an error.\n"); exit (1); } } rest -= numToWrite; offset += numToWrite; sleep_us (1000); } while (index != 0); rcx_close(fd); exit(0); }