From f6f78676fa1b3e392c6eb1e9f04a7980738a2f98 Mon Sep 17 00:00:00 2001 From: mesheets <16882600+mesheets@users.noreply.github.com> Date: Tue, 25 Feb 2020 22:35:11 -0500 Subject: [PATCH] Initial commit of legoshrink --- Makefile.linux | 9 + Makefile.windows | 9 + README.md | 36 ++ legoshrink.c | 842 +++++++++++++++++++++++++++++++++++++++++++++++ rcx_comm.c | 600 +++++++++++++++++++++++++++++++++ rcx_comm.h | 86 +++++ 6 files changed, 1582 insertions(+) create mode 100644 Makefile.linux create mode 100644 Makefile.windows create mode 100644 README.md create mode 100644 legoshrink.c create mode 100644 rcx_comm.c create mode 100644 rcx_comm.h diff --git a/Makefile.linux b/Makefile.linux new file mode 100644 index 0000000..3aac68c --- /dev/null +++ b/Makefile.linux @@ -0,0 +1,9 @@ + +all: legoshrink + +legoshrink: legoshrink.c rcx_comm.c + $(CC) $^ -o $@ -O2 -Wall + +clean: + rm -f *.o *~ *.bak legoshrink + diff --git a/Makefile.windows b/Makefile.windows new file mode 100644 index 0000000..933614e --- /dev/null +++ b/Makefile.windows @@ -0,0 +1,9 @@ + +all: legoshrink + +legoshrink: legoshrink.c rcx_comm.c + $(CC) $^ -o $@ -O2 -Wall -D_WIN32 + +clean: + rm -f *.o *~ *.bak legoshrink + diff --git a/README.md b/README.md new file mode 100644 index 0000000..a34089b --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# legoshrink + +[Archived Website](http://web.archive.org/web/20070825203927/http://tarpit.rmc.ca/lesauvage/eee243/labs/resources/legoshrink.html) + +This archive contains the Windows binary for legoshrink, a program for +receiving integrity or addressed messages from a Lego Mindstorms RCX brick +using the brickOS LNP protocol. + +This particular binary has been compiled with English output. You may +recompile for an French output by uncommenting the #define FRENCH 1 +at the top of the file legoshrink.c. + +The source code has been included as per the terms of the Mozilla Public +License. If you alter this code for your own purposes, then you are also +bound by the MPL. Check the header in legoshrink.c for more information. +If you only want to run the program under Windows, then the only file you need +from this archive is legoshrink.exe. Try legoshrink -h to see the available +options. + +You may be able to compile this program to run under Linux, but that has not +been tested. Testing was only performed on Win 2000 and Win XP PCs with the +USB tower. + +To compile under either platform, place the source in a subdirectory of your +Cygwin directory. Rename the appropriate windows or linux makefile to +Makefile and type make on the command line. Good luck! + +Reports of success/failure for linux and/or the serial tower would be +appreciated. + +Good luck and enjoy! + + +Mike LeSauvage +michael lesauvage rmc ca //This hides me from spam! +14 Feb 2004. diff --git a/legoshrink.c b/legoshrink.c new file mode 100644 index 0000000..1198b10 --- /dev/null +++ b/legoshrink.c @@ -0,0 +1,842 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// File: legoshrink.c +// +// Author: Mike LeSauvage (based on work done by those in the description.) +// +// Description: This program is used to collect information from a Lego +// robot, print it to the screen, and write it to a log (if +// desired). +// This program is derived from ir3, a program written by +// Pavel Petrovic(ppetrovic@acm.org). He in turn based it on +// firmdl3.c and ir.c, the firmware dowloader for the RCX. His +// code was tested on brickOS 0.2.6.10.6 and Debian Linux. +// (http://www.idi.ntnu.no/~petrovic/ir/) +// +// I modified his program for my purposes, which at the moment, +// only involve reading messages from the brick. I documented +// work that was already implemented, researched and documented +// the Lego Network Protocol (see lnp_integrity_byte), removed +// the functions related to sending data, changed the program +// options, and added address packet support (which was the main +// reason I started poking around in the first place). +// +// This program was tested on brickOS 0.2.6.10 on Windows 2000. +// Since I do not have a serial tower, I don't know if the +// program will function with one. I tried to preserve serial +// related code. Your mileage may vary. +// +// As this is code derived from code based firmdl3, the Mozilla +// Public License still applies. The original firmdl3.c licence +// is included below for your viewing pleasure. +// +// Modifications: 10 Feb 2005 - Major revision of original code complete. +// 15 Feb 2005 - Program may now be compiled in either +// English or French. +// 12 Mar 2005 - Changed program so it can send a file, line +// by line. +// +/////////////////////////////////////////////////////////////////////////////// + + /* -----------------------------------------begin-firmdl3.c-licence----- + * firmdl3.c + * + * A firmware downloader for the RCX. Version 3.0. Supports single and + * quad speed downloading. + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Firmdl code, released October 3, 1998. + * + * The Initial Developer of the Original Code is Kekoa Proudfoot. + * Portions created by Kekoa Proudfoot are Copyright (C) 1998, 1999 + * Kekoa Proudfoot. All Rights Reserved. + * + * Contributor(s): Kekoa Proudfoot + * Laurent Demailly + * Allen Martin + * Markus Noga + * Gavin Smyth + * Luis Villa + * + * ------------------------------------------end-firmdl3.c-licence----- + * modifications for ir3: Pavel Petrovic, ppetrovic@acm.org + */ + +//#define FRENCH 1 +#ifdef FRENCH + #define STR_RAW "\nMessage de données non traité:\n" + #define STR_INTEGRITY "\n(Intégrité) %s\n" + #define STR_VERBOSE "\nHôte de dest: %d, Port de dest: %d, Hôte de srce: %d, Port de srce: %d\n%s\n" + #define STR_UNKNOWN "\n(Type inconnu) %s\n" + #define STR_BAD_CHECKSUM "\n *** Mais Checksum! Message(s) perdu(s)! ***\n" + #define STR_SIZE_MISMATCH "\n *** Message Size Incorrect! Message(s) Lost! ***\n" + #define STR_OPEN_ERR "Ne pouvait pas ouvrir %s. Le programme termine." + #define STR_WRITE_ERR "Ne pouvait pas écrire à %s. Le programme termine." + #define STR_CLOSE_ERR "Ne pouvait pas fermer %s. Le programme termine." + #define STR_ACCESS_ERR "Erreur dans l'acces %s. Le programme termine." + #define STR_HOST_ADD_RANGE "L'adresse de l'hôte est hors limite %s." + #define STR_NO_PARAMETER "Paramètre manquant pour l'option %s." + #define STR_HOST_PORT_RANGE "Le port de l'hôte est hors limite %s." + #define STR_UNRECOGNIZED_OPT "Option inconnue: %s." + #define STR_FILE_NOT_DELETED "\nInfo de déboggage: Fichier non effacé.\n" + #define STR_DEFAULT_TTY "Utilise le tty par défaut connexion peut faillir! Utilisez -h pour les options.\n" +#else + #define STR_RAW "\nRaw message data:\n" + #define STR_INTEGRITY "\n(Integrity) %s\n" + #define STR_VERBOSE "\nDest Host: %d, Dest Port: %d, Source Host: %d, Source Port: %d\n%s\n" + #define STR_UNKNOWN "\n(Unknown Type) %s\n" + #define STR_BAD_CHECKSUM "\n *** Bad Checksum! Message(s) Lost! ***\n" + #define STR_SIZE_MISMATCH "\n *** Message Size Incorrect! Message(s) Lost! ***\n" + #define STR_OPEN_ERR "Could not open %s. Program will exit." + #define STR_WRITE_ERR "Could not write to %s. Program will exit." + #define STR_CLOSE_ERR "Could not close %s. Program will exit." + #define STR_ACCESS_ERR "Error in accessing %s. Program will exit." + #define STR_HOST_ADD_RANGE "Host address is out of range %s." + #define STR_NO_PARAMETER "Parameter missing for %s option." + #define STR_HOST_PORT_RANGE "Host port is out of range %s." + #define STR_UNRECOGNIZED_OPT "Unrecognized option: %s" + #define STR_FILE_NOT_DELETED "\nDebug Info: File not deleted.\n" + #define STR_DEFAULT_TTY "Using default tty...may fail to connect! Use -h for options.\n" +#endif + + +#if defined(_WIN32) + #include +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rcx_comm.h" + +//State of the LNP protocol when receiving a packet. +typedef enum { LNPwaitHeader, LNPwaitLength, + LNPwaitDest, LNPwaitHost, + LNPwaitData, LNPwaitCRC } lnp_integrity_state_t; + +//Used to return the type of packet received. +typedef enum { msg_BAD_CHECKSUM, msg_PROCESSING, + msg_INTEGRITY, msg_ADDRESSED, + msg_UNKNOWN, msg_SIZE_MISMATCH } MSG_TYPES; + + +//Global variables +int tty_usb = 0; //0 if using serial port, 1 for usb. +extern int __comm_debug; +static char packet[257]; //Mike notes: Packet has max 255 bytes + // data, then add 1 for null char, so why 257? +static int destHost=0, destPort=0; //Globals to record the packet's source +static int sourceHost=0, sourcePort=0; //and destination addresses. +static char *progname; +static int showDebugInfo=0; + + +//Defines +#define BUFFERSIZE 4000 +#define WAKEUP_TIMEOUT 4000 +#define DEFAULTTTY "/dev/ttyS0" //Linux - COM1 + + +//Prototypes +void LogString(char *fileName, char *aPacket); +unsigned char lnp_checksum( const unsigned char *data, unsigned length); +int lnp_integrity_byte(unsigned char b, int size); +int ReceiveMessage(FILEDESCR fd, int myAdd, int myPort, + int verbose, char *aFile, char *match); +void SendFile(FILEDESCR fd, char *sendFile, char *rtrMsg, int verbose); + + +/////////////////////////////////////////////////////////////////////////////// +// +// Function: lnp_checksum +// +// Description: Original docs for this function stated "modified originally +// LegOS sources. It calculates a checksum based on the entire +// contents of the incoming packet (except, of course, the last +// byte holding the checksum itself). +// +// Parameters: data - A pointer to the packet. +// length - The number of bytes contained in the packet. +// +// Returns: unsigned char - The checksum. +// +/////////////////////////////////////////////////////////////////////////////// +unsigned char lnp_checksum( const unsigned char *data, unsigned length) +{ + unsigned char a = 0xff; + while (length > 0) { + a = a + *data; + data++; + length--; + } + return a; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Function: lnp_integrity_byte +// +// Description: The previous documentation stated that this is +// "LNP, bit modified from LegOS sources". From examination, this +// code is used to examine an incoming message, byte by byte and +// track the "state" of the packet until it is fully processed. +// Since only one byte is examined at a time, static variables +// are used to remember state. +// +// Modifications: Mike LeSauvage(ML) changed this function to identify +// addressed packets, properly copy their values, and return +// different codes for different packet types. +// +// Notes: LNP packets are 1 of the 2 following formats (all I've seen so far) +// +// INTEGRITY: header|length|destadd|sourceadd|up to 253 bytes data|checksum +// +// ADDRESSED: header|length|up to 255 bytes data|checksum +// +// Each section is one byte long, so a message at max data is 258 bytes. +// +// header: Format is 11110xxx (base 2), where xxx may be changed to allow +// different header types. The only types identified by ML are +// the integrity packet(000) and an addressed packet(001). +// +// length: The length in bytes of the data and address sections combined. +// +// destadd: The destination address in the format hhhhpppp where hhhh is +// the host and pppp is the port for that host. (base 2) +// +// sourceadd: This field follows the same format as destadd. +// +// checksum: A value calculated by the sending protocol. It should conform +// to the lnp_checksum() function in this file. +// +// +// Parameters: b - A single byte for inspection. +// size - The number of bytes total in the packet +// +// Returns: int: Use a value from MSG_TYPE: msg_BAD_CHECKSUM +// msg_PROCESSING +// msg_INTEGRITY +// msg_ADDRESSED +// msg_UNKNOWN +// +/////////////////////////////////////////////////////////////////////////////// +int lnp_integrity_byte(unsigned char b, int size) +{ + static unsigned char buffer[259]; + static int bytesRead,endOfData; + static int lnp_integrity_state = LNPwaitHeader; + static unsigned char packetType; //Holds the non-masked portion of header + + if(lnp_integrity_state==LNPwaitHeader) + bytesRead=0; + + buffer[bytesRead++]=b; + + + switch(lnp_integrity_state) + { + case LNPwaitHeader: //Check top five + if((b & (unsigned char) 0xf8) == (unsigned char) 0xf0)//bits="11110" + { + packetType = b & (unsigned char)0x7; //Extract the packet type + lnp_integrity_state=LNPwaitLength; //from the header + break; //(bottom three bits) + } + + case LNPwaitLength: + endOfData=b+2; + if(endOfData+1 != size) //size is number of bytes rec'd + { //and it should match what the + lnp_integrity_state=LNPwaitHeader; //length byte states (+3 because + return msg_SIZE_MISMATCH; //that byte doesn't include the + } //header, size, and checksum.) + if(packetType==1) //If this is an addressed packet + lnp_integrity_state=LNPwaitDest; //then wait on the destination byte + else //otherwise, go on to receive data. + lnp_integrity_state=LNPwaitData; + break; + + case LNPwaitDest: //Record the host and port to + destHost=b>>4; //which this message was addressed. + destPort=b&0xf; + lnp_integrity_state=LNPwaitHost; + break; + + case LNPwaitHost: //Record the source host and port. + sourceHost=b>>4; + sourcePort=b&0xf; + lnp_integrity_state=LNPwaitData; + break; + + case LNPwaitData: + if(bytesRead==endOfData) + lnp_integrity_state=LNPwaitCRC; + break; + + case LNPwaitCRC: + if(b == (unsigned char)lnp_checksum(buffer,endOfData)) + { + buffer[buffer[1]+2]=0; //Put terminating null at string end + if(packetType==1) //Copy position from the buffer is + { //4 bytes in for an addressed + strcpy(packet, buffer+4); //packet. Reset integrity state + lnp_integrity_state=LNPwaitHeader;//and return the addressed msg + return msg_ADDRESSED; //flag. + } + else if(packetType==0) //Copy position from the buffer is + { //2 bytes for an integrity packet. + strcpy(packet,buffer+2); + lnp_integrity_state=LNPwaitHeader;//Reset integrity state and return + return msg_INTEGRITY; //the integrity msg flag. + } + else //Unknown packet type. Make a + { //guess as to buffer contents and + strcpy(packet,buffer+2); //return the unknown flag. + lnp_integrity_state=LNPwaitHeader; + return msg_UNKNOWN; + } + } + else //Bad checksum. + { + lnp_integrity_state=LNPwaitHeader; + return msg_BAD_CHECKSUM; + } + } + return msg_PROCESSING; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Function: ReceiveMessage +// +// Description: Original documentation for this function stated +// "receive next LNP message to global array packet[]" +// This function receives messages from the USB tower and +// prints them to the screen until the program is exited. +// Originally, this function sat in a loop receiving messages +// until the program was closed. It has been changed to +// receive a single message. +// +// Parameters: fd - A file descriptor for the USB tower. +// myAdd - Address of this PC; ignore packets with wrong dest add. +// myPort - Port of this PC; ignore packets with wrong dest port. +// verbose - Provide legend for incoming address info. +// aFile - Name of a file to log the data. NULL if not logging. +// match - A pointer to a string that should be compared to the +// received message. Pass NULL for no comparison. +// +// Returns: int - 1 if the received packet matches the provided string +// "match", false otherwise. +// +/////////////////////////////////////////////////////////////////////////////// +int ReceiveMessage(FILEDESCR fd, int myAdd, int myPort, + int verbose, char *aFile, char *match) +{ + int status, len, i, j=0, messageStatus; + unsigned char recv[BUFFERSIZE + 1]; + unsigned char outputString[256 + 80]=""; //256 for packet + + //terminating null. 80 for + //extra info ie: address + //static as this fn will + //be called frequently. + + int receive_timeout=100; //No idea how this timeout actually works. + //Changing the value doesn't seem to + //make any difference. + + if(tty_usb==0) //Wake up the + { //serial tower. + if ((status = rcx_wakeup_tower(fd, WAKEUP_TIMEOUT)) < 0) //Return if it + { //can't be woken. + fprintf(stderr, "%s: %s\n", progname, rcx_strerror(status)); + return 0; + } + } + + packet[0] = 0; //Ensure packet starts with terminating null? + len = nbread(fd, recv, BUFFERSIZE, receive_timeout); //Read in a message. + + i = 0; j++; messageStatus=msg_PROCESSING; + while(i 0) + { + printf("%s", outputString); + if(aFile != NULL) + LogString(aFile, outputString); + } + + + if (tty_usb == 0) //This appears to be some + { //code related to the serial + if (j * receive_timeout > 3000) //tower. It executes every + { //30 messages as is. My + j = 0; //guess is it has something + recv[0] = 0x10; //to do with serial tower sleep + mywrite(fd, recv, 1); //but I don't really know so + } //I'm just leaving it alone. + } +#ifdef _WIN32 + FlushFileBuffers(fd); +#endif + + if(match != NULL) + if(!strcmp(packet, match)) + return 1; + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Function: SendFile +// +// Description: This function sends a file over the USB tower, one line of text +// at a time. There is a limit of 255 characters per line, any +// excess will be truncated. Only integrity communication is +// currently supported. +// The robot must send a message requesting each line. +// +// Parameters: fd - A file descriptor for the USB tower. +// sendFile - The path/name of the file to send. +// rtrMsg - A string to be matched. Before sending a line, this +// waits to receive a match to this string. When it does, +// the next line in the file is sent. +// verbose - Does nothing in this function; is used to tell the +// ReceiveMessage function if verbose reporting should +// be used. +// +// Returns: Nothing. +// +/////////////////////////////////////////////////////////////////////////////// +void SendFile(FILEDESCR fd, char *sendFile, char *rtrMsg, int verbose) +{ + int i; + long fileLen; + int msgLen; + int fileHandle; + int ncRead; + int curPos=0; + char *fileBuffer; + char msgBuf[258]; //255 char message, 1 char header, + //1 char length, 1 char checksum. + + fileHandle=open(sendFile, O_RDONLY|O_BINARY); //Try to open the file. + if(fileHandle == -1) + { + fprintf(stderr, STR_OPEN_ERR, sendFile); + exit(1); + } + + fileLen=lseek(fileHandle, 0, SEEK_END); //Determine the size of the file. + if(fileLen == -1L) + { + fprintf(stderr, STR_ACCESS_ERR, sendFile); + exit(1); + } + + lseek(fileHandle, 0, SEEK_SET); //Seek back to start. + fileBuffer = (char *)malloc(fileLen + 1); //Allocate enough space for + //the file plus an end null. + + ncRead = read(fileHandle, fileBuffer, fileLen);//Read in the file and + if(ncRead != fileLen) //check to see if the entire + { //thing was read in. If not, + fprintf(stderr, STR_ACCESS_ERR, sendFile); //an error has ocurred. + exit(1); + } + fileBuffer[fileLen]=0; //Set an end null to be sure. + + close(fileHandle); + + for(i=0; i 255) //If more than 255, set 255. + msgLen = 255; + for(i=0; i Règle l'adresse du PC hôte, valide de 0-15. Défaut 0.\n" + " -p Règle le port du PC hôte pour écouter, valide de 0-15.\n" + " Utilise -1 pour écouter pour des messages dirigées à\n" + " n'importe quel port (default).\n" + " -l Enregistre tout les messages au fichier spécifique.\n" + " -ld Enregistre tout les messages au fichier; efface le fichier\n" + " si il existe déjà.\n" + " -sf Envoie fichier. Fichier envoyé ligne par ligne (caractères\n" + " excédent 255 sont enlevés). Avant d'envoyer chaque \n" + " ligne, legoshrink attend de recevoir le mot par (prêt à \n" + " recevoir) du robot. Seul le mote intégrité est supporté.\n" + " -t Spécifie le type de connexion (TTY ou usb).\n" + " -f Communique en mode rapide (sériel seulement).\n" + " -v Verbose (Plus d'information sur les messages adressés)\n" + " -h Affiche cet aide et sort\n" + " --déboguage Montre les octets non-traités pour tout les messages\n" + " qui arrivent.\n" + "\n" + "Exemples: legoshrink -t usb -p 6 -l crashlog.txt\n" + " legoshrink -t usb -sf directives.txt ENVOIE_TDS\n" + ; +#else + char *usageString = + "Usage: legoshrink [options]\n\n" + "Options:\n" + " -a
Set PC host address, valid from 0-15. Default 0.\n" + " -p Set PC host port to listen on, valid from 0-15.\n" + " Use -1 to listen for messages directed to any port(default).\n" + " -l Log all messages to the specified file.\n" + " -ld Log messages; first delete named file if it already exists.\n" + " -sf Send file. The file is sent one line at a time (characters\n" + " in excess of 255 will be truncated). Before sending each\n" + " line, legoshrink waits to receive the rtr word (ready to\n" + " receive) from the bot. Only integrity mode is supported.\n" + " -t Specify connection type (TTY or usb).\n" + " -f Communicate in fast mode (serial only).\n" + " -v Verbose (More information on addressed messages)\n" + " -h Display this help and exit\n" + " --debug Show raw bytes for all incoming messages.\n" + "\n" + "Examples: legoshrink -t usb -p 6 -l crashlog.txt\n" + " legoshrink -t usb -sf commands.txt SENDNOW\n" + ; +#endif + + + //Parse the command line. + progname=argv[0]; + argc--; + argv++; + + while(argc > 0 && strlen(errorMsg)==0) + { + if(!strcmp(argv[0], "--debug")) //Extract debug option. + showDebugInfo = 1; + else if(!strcmp(argv[0], "-a")) //Extract host address option. + { + if(argc > 1) + { + argc--; + argv++; + addressPC=atoi(argv[0]); + if(addressPC<0 || addressPC>15) + sprintf(errorMsg, STR_HOST_ADD_RANGE, "0-15"); + } + else + sprintf(errorMsg, STR_NO_PARAMETER, "-a"); + } + else if(!strcmp(argv[0], "-p")) //Extract host port setting. + { + if(argc > 1) + { + argc--; + argv++; + portPC=atoi(argv[0]); + if(portPC<-1 || portPC>15) + sprintf(errorMsg, STR_HOST_PORT_RANGE, "(-1)-15"); + } + else + sprintf(errorMsg, STR_NO_PARAMETER, "-p"); + } + else if(!strcmp(argv[0], "-l")) //Extract file option. + { + if(argc > 1) + { + argc--; + argv++; + logFileName=argv[0]; + } + else + sprintf(errorMsg, STR_NO_PARAMETER, "-l"); + } + else if(!strcmp(argv[0], "-ld")) //Extract file option. + { //along with the + if(argc > 1) //overwrite option. + { + argc--; + argv++; + logFileName=argv[0]; + overWrite=1; + } + else + sprintf(errorMsg, STR_NO_PARAMETER, "-ld"); + } + else if(!strcmp(argv[0], "-sf")) //Extract send file option + { //along with its two parameters: + if(argc > 2) //the file to send and the + { //"ready to receive" word. + argc--; + argv++; + sendFileName=argv[0]; + argc--; + argv++; + rtrWord=argv[0]; + } + else + sprintf(errorMsg, STR_NO_PARAMETER, "-sf"); + } + else if(!strcmp(argv[0], "-t")) //Extract tty option. + { + if(argc > 1) + { + argc--; + argv++; + tty = argv[0]; + } + else + sprintf(errorMsg, STR_NO_PARAMETER, "-t"); + } + else if(!strcmp(argv[0], "-f")) //Extract fast option. + useFast=1; + else if(!strcmp(argv[0], "-v")) //Extract verbose option. + verbose=1; + else if(!strcmp(argv[0], "-h")) //Extract help option. + sprintf(errorMsg, " "); + else + sprintf(errorMsg, STR_UNRECOGNIZED_OPT, argv[0]); + + argc--; + argv++; + } + + if(strlen(errorMsg)>0) //Produce error messages + { //(if any), display usage + fprintf(stderr, "\n%s\n", errorMsg); //information, and quit. + fprintf(stderr, "\n%s", usageString); + exit(1); + } + + if(logFileName != NULL && overWrite==1) //If the user specified overWrite + { //then delete the log file if + int err; //one exists. + err=remove(logFileName); + if(err==-1 && showDebugInfo) + fprintf(stderr, STR_FILE_NOT_DELETED); + } + + if (!tty) //Get the tty name. + { + tty = getenv("RCXTTY"); + printf(STR_DEFAULT_TTY); + } + if (!tty) //No environment var set, use default. + tty = DEFAULTTTY; + + if(strcmp(tty,"usb")==0) //Check if usb was chosen. + { + tty_usb = 1; //Mark it as the communications method. + tty = "\\\\.\\legotower1"; //This is the default tower. Systems with + //more than one would have to change this. + } + + if (useFast && (tty_usb==0)) + fd = rcx_init(tty, 1); //Only serial has a fast mode. + else + fd = rcx_init(tty, 0); //Either fast not selected or USB in use. + + + + if(sendFileName == NULL) + { + while(1) + ReceiveMessage(fd, addressPC, portPC, verbose, logFileName, NULL); + } + else + { + SendFile(fd, sendFileName, rtrWord, verbose); + } + + rcx_close(fd); + exit(0); +} + diff --git a/rcx_comm.c b/rcx_comm.c new file mode 100644 index 0000000..80540b1 --- /dev/null +++ b/rcx_comm.c @@ -0,0 +1,600 @@ +/* + * rcx_comm.c + * + * RCX communication routines. + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Firmdl code, released October 3, 1998. + * + * The Initial Developer of the Original Code is Kekoa Proudfoot. + * Portions created by Kekoa Proudfoot are Copyright (C) 1998, 1999 + * Kekoa Proudfoot. All Rights Reserved. + * + * Contributor(s): Kekoa Proudfoot + */ + +/* 2002.04.01 + * + * Modifications to the original loader.c file in LegOS 0.2.4 include: + * + * Hary D. Mahesan's update to support USB IR firmware downloading + * using RCX 2.0's USB tower under WIN32 on Cygwin. + * + * + * + * CVS inclusion, revision and modification by Paolo Masetti. + * + * + */ + +/* 2004/07/14: modified for ir3, make nbread() non-static, ppetrovic@acm.org */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) + #include +#endif + +#include "rcx_comm.h" + +/* Defines */ + +#define BUFFERSIZE 4096 + +/* Globals */ + +int __comm_debug = 0; +extern int tty_usb; + +/* Timer routines */ + +typedef struct timeval timeval_t; + +#define tvupdate(tv) gettimeofday(tv,NULL) +#define tvsec(tv) ((tv)->tv_sec) +#define tvmsec(tv) ((tv)->tv_usec * 1e-3) + +static float +timer_reset(timeval_t *timer) +{ + tvupdate(timer); + return 0; +} + +static float +timer_read(timeval_t *timer) +{ + timeval_t now; + tvupdate(&now); + return tvsec(&now) - tvsec(timer) + (tvmsec(&now) - tvmsec(timer)) * 1e-3; +} + +void myperror(char *str) { +#if defined(_WIN32) + fprintf(stderr, "Error %lu: %s\n", (unsigned long) GetLastError(), str); +#else + perror(str); +#endif +} + +/* Timeout read routine */ + +int nbread (FILEDESCR fd, void *buf, int maxlen, int timeout) +{ + char *bufp = (char *)buf; + int len = 0; + + while (len < maxlen) { + +#if defined(_WIN32) + if(tty_usb) { + // USB Stuff here + // We can't use Serial timeouts. + DWORD count = 0; + struct timeval timebegin ,timenow; + unsigned long elapsed; // for timeout values + + gettimeofday(&timebegin,0); + while(count==0) { + ReadFile( fd, &bufp[len], maxlen - len, &count, NULL); + gettimeofday(&timenow,0); + elapsed = (timenow.tv_sec - timebegin.tv_sec ) + (timenow.tv_usec - timebegin.tv_usec); + if(elapsed > timeout) + break; + } + if(count==0) { + if(__comm_debug) + printf("Hary Mahesan - USB mode: nbread(len=%d, maxlen=%d) break...timed out\n", len, maxlen); + break; + } + len += count; //update len + } else { + // Serial port stuff now. + DWORD count = 0; + COMMTIMEOUTS CommTimeouts; + + GetCommTimeouts (fd, &CommTimeouts); + + // Change the COMMTIMEOUTS structure settings. + CommTimeouts.ReadIntervalTimeout = MAXDWORD; + CommTimeouts.ReadTotalTimeoutMultiplier = 0; + CommTimeouts.ReadTotalTimeoutConstant = timeout; + CommTimeouts.WriteTotalTimeoutMultiplier = 10; + CommTimeouts.WriteTotalTimeoutConstant = 1000; + + // Set the time-out parameters for all read and write operations + // on the port. + SetCommTimeouts(fd, &CommTimeouts); + + if (ReadFile(fd, &bufp[len], maxlen - len, &count, NULL) == FALSE) { + myperror("ReadFile"); + fprintf(stderr, "nb_read - error reading tty: %lu\n", (unsigned long) GetLastError()); + exit(1); + } + + len += count; + + if (count == 0) { + if(__comm_debug) + printf("Serial mode: nbread(len=%d, maxlen=%d) break...timed out\n", len, maxlen); + break; + } + } +#else + int count; + fd_set fds; + struct timeval tv; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + if (select(fd+1, &fds, NULL, NULL, &tv) < 0) { + perror("select"); + exit(1); + } + + if (!FD_ISSET(fd, &fds)) + break; + + if ((count = read(fd, &bufp[len], maxlen - len)) < 0) { + perror("read"); + exit(1); + } + + len += count; +#endif + + } + + return len; +} + +/* discard all characters in the input queue of tty */ +static void rx_flush(FILEDESCR fd) +{ +#if defined(_WIN32) + if (tty_usb == 0) { + PurgeComm(fd, PURGE_RXABORT | PURGE_RXCLEAR); + } else { + char echo[BUFFERSIZE]; + nbread(fd, echo, BUFFERSIZE, 200); + } +#else + char echo[BUFFERSIZE]; + nbread(fd, echo, BUFFERSIZE, 200); +#endif +} + +int mywrite(FILEDESCR fd, const void *buf, size_t len) { +#if defined(_WIN32) + DWORD nBytesWritten=0; + WriteFile(fd, buf, len, &nBytesWritten, NULL); + return nBytesWritten; +#else + return write(fd, buf, len); +#endif +} + +/* RCX routines */ + +FILEDESCR rcx_init(char *tty, int is_fast) +{ + FILEDESCR fd; + +#if defined(_WIN32) + DCB dcb; +#else + struct termios ios; +#endif + + if (__comm_debug) printf("mode = %s\n", is_fast ? "fast" : "slow"); + +#if defined(_WIN32) + if ((fd = CreateFile(tty, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, + 0, NULL)) == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Error %lu: Opening %s\n", (unsigned long) GetLastError(), tty); + exit(1); + } + PurgeComm(fd, PURGE_RXCLEAR|PURGE_TXCLEAR); + + + //These settings don't apply to the USB tower, so if{} them out. + if(tty_usb==0) { + // Serial settings + FillMemory(&dcb, sizeof(dcb), 0); + if (!GetCommState(fd, &dcb)) { // get current DCB + // Error in GetCommState + myperror("GetCommState"); + exit(1); + } else { + dcb.ByteSize = 8; + dcb.Parity = (is_fast ? 0 : 1); // 0-4=no,odd,even,mark,space + dcb.StopBits = 0; // 0,1,2 = 1, 1.5, 2 + dcb.fBinary = TRUE ; + dcb.fParity = (is_fast ? FALSE : TRUE) ; + dcb.fAbortOnError = FALSE ; + dcb.BaudRate = (is_fast ? CBR_4800 : CBR_2400); // Update DCB rate. + + // Set new state. + if (!SetCommState(fd, &dcb)) { + // Error in SetCommState. Possibly a problem with the communications + // port handle or a problem with the DCB structure itself. + myperror("SetCommState"); + exit(1); + } + } // GetCommState + } // usb + +#else + + if ((fd = open(tty, O_RDWR)) < 0) { + perror(tty); + exit(1); + } + + if (!isatty(fd)) { + close(fd); + fprintf(stderr, "%s: not a tty\n", tty); + exit(1); + } + + memset(&ios, 0, sizeof(ios)); + + if (is_fast) { + ios.c_cflag = CREAD | CLOCAL | CS8; + cfsetispeed(&ios, B4800); + cfsetospeed(&ios, B4800); + } + else { + ios.c_cflag = CREAD | CLOCAL | CS8 | PARENB | PARODD; + cfsetispeed(&ios, B2400); + cfsetospeed(&ios, B2400); + } + + if (tcsetattr(fd, TCSANOW, &ios) == -1) { + perror("tcsetattr"); + exit(1); + } +#endif + + return fd; +} + +void rcx_close(FILEDESCR fd) +{ +#if defined(_WIN32) + CloseHandle(fd); +#else + close(fd); +#endif +} + +int rcx_wakeup_tower (FILEDESCR fd, int timeout) +{ + char msg[] = { 0x10, 0xfe, 0x10, 0xfe }; + char keepalive = 0xff; + char buf[BUFFERSIZE]; + timeval_t timer; + int count = 0; + int len; + + // First, I send a KeepAlive Byte to settle IR Tower... + mywrite(fd, &keepalive, 1); + usleep(20000); + rx_flush(fd); + + timer_reset(&timer); + + do { + if (__comm_debug) { + printf("writelen = %d\n", sizeof(msg)); + hexdump("W", msg, sizeof(msg)); + } + if (mywrite(fd, msg, sizeof(msg)) != sizeof(msg)) { + myperror("write"); + exit(1); + } + count += len = nbread(fd, buf, BUFFERSIZE, 50); + if (len == sizeof(msg) && !memcmp(buf, msg, sizeof(msg))) + return RCX_OK; /* success */ + if (__comm_debug) { + printf("recvlen = %d\n", len); + hexdump("R", buf, len); + } + rx_flush(fd); + } while (timer_read(&timer) < (float)timeout / 1000.0f); + + if (!count) + return RCX_NO_TOWER; /* tower not responding */ + else + return RCX_BAD_LINK; /* bad link */ +} + +/* Hexdump routine */ + +#define LINE_SIZE 16 +#define GROUP_SIZE 4 +#define UNPRINTABLE '.' + +void hexdump(char *prefix, void *buf, int len) +{ + unsigned char *b = (unsigned char *)buf; + int i, j, w; + + for (i = 0; i < len; i += w) { + w = len - i; + if (w > LINE_SIZE) + w = LINE_SIZE; + if (prefix) + printf("%s ", prefix); + printf("%04x: ", i); + for (j = 0; j < w; j++, b++) { + printf("%02x ", *b); + if ((j + 1) % GROUP_SIZE == 0) + putchar(' '); + } + putchar('\n'); + } +} + + +int rcx_send (FILEDESCR fd, void *buf, int len, int use_comp) +{ + char *bufp = (char *)buf; + char buflen = len; + char msg[BUFFERSIZE]; + char echo[BUFFERSIZE]; + int msglen, echolen; + int sum; + int tty_usb_echo = 0; // USB do not echos + + /* Encode message */ + + msglen = 0; + sum = 0; + + if (use_comp) { + msg[msglen++] = 0x55; + msg[msglen++] = 0xff; + msg[msglen++] = 0x00; + while (buflen--) { + msg[msglen++] = *bufp; + msg[msglen++] = (~*bufp) & 0xff; + sum += *bufp++; + } + msg[msglen++] = sum; + msg[msglen++] = ~sum; + } + else { + msg[msglen++] = 0xff; + while (buflen--) { + msg[msglen++] = *bufp; + sum += *bufp++; + } + msg[msglen++] = sum; + } + + /* Send message */ + + if (mywrite(fd, msg, msglen) != msglen) { + myperror("write"); + exit(1); + } + + /* Receive echo */ + + // USB Tower doesn't echo + if(tty_usb == tty_usb_echo) { + + echolen = nbread(fd, echo, msglen, 100); + + if (__comm_debug) { + printf("msglen = %d, echolen = %d\n", msglen, echolen); + hexdump("C", echo, echolen); + } + + /* Check echo */ + /* Ignore data, since rcx might send ack even if echo data is wrong */ + + if (echolen != msglen /* || memcmp(echo, msg, msglen) */ ) { + /* Flush connection if echo is bad */ + rx_flush(fd); + return RCX_BAD_ECHO; + } + } // USB + + return len; +} + +int rcx_recv (FILEDESCR fd, void *buf, int maxlen, int timeout, int use_comp) +{ + char *bufp = (char *)buf; + unsigned char msg[BUFFERSIZE]; + int msglen; + int sum; + int pos; + int len; + + /* Receive message */ + + msglen = nbread(fd, msg, BUFFERSIZE, timeout); + + if (__comm_debug) { + printf("recvlen = %d\n", msglen); + hexdump("R", msg, msglen); + } + + /* Check for message */ + + if (!msglen) + return RCX_NO_RESPONSE; + + /* Verify message */ + + if (use_comp) { + if (msglen < 5 || (msglen - 3) % 2 != 0) + return RCX_BAD_RESPONSE; + + if (msg[0] != 0x55 || msg[1] != 0xff || msg[2] != 0x00) + return RCX_BAD_RESPONSE; + + for (sum = 0, len = 0, pos = 3; pos < msglen - 2; pos += 2) { + if (msg[pos] != ((~msg[pos+1]) & 0xff)) + return RCX_BAD_RESPONSE; + sum += msg[pos]; + if (len < maxlen) + bufp[len++] = msg[pos]; + } + + if (msg[pos] != ((~msg[pos+1]) & 0xff)) + return RCX_BAD_RESPONSE; + + if (msg[pos] != (sum & 0xff)) + return RCX_BAD_RESPONSE; + + /* Success */ + return len; + } + else { + if (msglen < 4) + return RCX_BAD_RESPONSE; + + if (msg[0] != 0x55 || msg[1] != 0xff || msg[2] != 0x00) + return RCX_BAD_RESPONSE; + + for (sum = 0, len = 0, pos = 3; pos < msglen - 1; pos++) { + sum += msg[pos]; + if (len < maxlen) + bufp[len++] = msg[pos]; + } + + /* Return success if checksum matches */ + if (msg[pos] == (sum & 0xff)) + return len; + + /* Failed. Possibly a 0xff byte queued message? (unlock firmware) */ + for (sum = 0, len = 0, pos = 3; pos < msglen - 2; pos++) { + sum += msg[pos]; + if (len < maxlen) + bufp[len++] = msg[pos]; + } + + /* Return success if checksum matches */ + if (msg[pos] == (sum & 0xff)) + return len; + + /* Failed. Possibly a long message? */ + /* Long message if opcode is complemented and checksum okay */ + /* If long message, checksum does not include opcode complement */ + for (sum = 0, len = 0, pos = 3; pos < msglen - 1; pos++) { + if (pos == 4) { + if (msg[3] != ((~msg[4]) & 0xff)) + return RCX_BAD_RESPONSE; + } + else { + sum += msg[pos]; + if (len < maxlen) + bufp[len++] = msg[pos]; + } + } + + if (msg[pos] != (sum & 0xff)) + return RCX_BAD_RESPONSE; + + /* Success */ + return len; + } +} + +int rcx_sendrecv (FILEDESCR fd, void *send, int slen, void *recv, int rlen, + int timeout, int retries, int use_comp) +{ + int status = 0; + + if (__comm_debug) printf("sendrecv %d:\n", slen); + + while (retries--) { + if ((status = rcx_send(fd, send, slen, use_comp)) < 0) { + if (__comm_debug) printf("status = %s\n", rcx_strerror(status)); + continue; + } + if ((status = rcx_recv(fd, recv, rlen, timeout, use_comp)) < 0) { + if (__comm_debug) printf("status = %s\n", rcx_strerror(status)); + continue; + } + break; + } + + if (__comm_debug) { + if (status > 0) + printf("status = %s\n", rcx_strerror(0)); + else + printf("status = %s\n", rcx_strerror(status)); + } + + return status; +} + +int rcx_is_alive (FILEDESCR fd, int use_comp) +{ + unsigned char send[1] = { 0x10 }; + unsigned char recv[1]; + + return (rcx_sendrecv(fd, send, 1, recv, 1, 50, 5, use_comp) == 1); +} + +char *rcx_strerror (int error) +{ + switch (error) { + case RCX_OK: return "no error"; + case RCX_NO_TOWER: return "tower not responding"; + case RCX_BAD_LINK: return "bad ir link"; + case RCX_BAD_ECHO: return "bad ir echo"; + case RCX_NO_RESPONSE: return "no response from rcx"; + case RCX_BAD_RESPONSE: return "bad response from rcx"; + default: return "unknown error"; + } +} + diff --git a/rcx_comm.h b/rcx_comm.h new file mode 100644 index 0000000..24f7281 --- /dev/null +++ b/rcx_comm.h @@ -0,0 +1,86 @@ +/* + * rcx_comm.h + * + * RCX communication routines. + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Firmdl code, released October 3, 1998. + * + * The Initial Developer of the Original Code is Kekoa Proudfoot. + * Portions created by Kekoa Proudfoot are Copyright (C) 1998, 1999 + * Kekoa Proudfoot. All Rights Reserved. + * + * Contributor(s): Kekoa Proudfoot + */ + +/* 2004/07/14: modified for ir3, make nbread() non-static, ppetrovic@acm.org */ + + +#ifndef RCX_COMM_H_INCLUDED +#define RCX_COMM_H_INCLUDED + +#define RCX_OK 0 +#define RCX_NO_TOWER -1 +#define RCX_BAD_LINK -2 +#define RCX_BAD_ECHO -3 +#define RCX_NO_RESPONSE -4 +#define RCX_BAD_RESPONSE -5 + +#if defined(_WIN32) + #define FILEDESCR HANDLE + #define BADFILE NULL +#else + #define FILEDESCR int + #define BADFILE -1 +#endif + + +/* Get a file descriptor for the named tty, exits with message on error */ +extern FILEDESCR rcx_init (char *tty, int is_fast); + +/* Close a file descriptor allocated by rcx_init */ +extern void rcx_close (FILEDESCR fd); + +/* Try to wakeup the tower for timeout ms, returns error code */ +extern int rcx_wakeup_tower (FILEDESCR fd, int timeout); + +/* Try to send a message, returns error code */ +/* Set use_comp=1 to send complements, use_comp=0 to suppress them */ +extern int rcx_send (FILEDESCR fd, void *buf, int len, int use_comp); + +/* Try to receive a message, returns error code */ +/* Set use_comp=1 to expect complements */ +/* Waits for timeout ms before detecting end of response */ +extern int rcx_recv (FILEDESCR fd, void *buf, int maxlen, int timeout, int use_comp); + +/* Try to send a message and receive its response, returns error code */ +/* Set use_comp=1 to send and receive complements, use_comp=0 otherwise */ +/* Waits for timeout ms before detecting end of response */ +extern int rcx_sendrecv (FILEDESCR fd, void *send, int slen, void *recv, int rlen, int timeout, int retries, int use_comp); + +/* Test whether or not the rcx is alive, returns 1=yes, 0=no */ +/* Set use_comp=1 to send complements, use_comp=0 to suppress them */ +extern int rcx_is_alive (FILEDESCR fd, int use_comp); + +/* Convert an error code to a string */ +extern char *rcx_strerror(int error); + +/* Hexdump routine */ +extern void hexdump(char *prefix, void *buf, int len); + +extern int nbread (FILEDESCR fd, void *buf, int maxlen, int timeout); + +extern int mywrite(FILEDESCR fd, const void *buf, size_t len); + +#endif /* RCX_COMM_H_INCLUDED */ + +