Logo Search packages:      
Sourcecode: jpnevulator version File versions  Download package

jpnevulator.c

/* jpnevulator - serial reader/writer
 * Copyright (C) 2006-2009 Freddy Spierenburg
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <ctype.h>

#include "jpnevulator.h"
#include "byte.h"
#include "io.h"
#include "tty.h"
#include "checksum.h"
#include "crc16.h"
#include "crc8.h"
#include "misc.h"

struct jpnevulatorOptions _jpnevulatorOptions;

static void messageChecksumAdd(unsigned char *message,int *size) {
      unsigned short checksum;
      switch(_jpnevulatorOptions.checksum) {
            case checksumTypeCrc8: {
                  checksum=crc8Calculate(message,*size);
                  /* Really dirty trick to put a 0x0D (CR) at the end of the message. But
                   * it's Friday afternoon and frankly I don't care to code this once in
                   * a life time usage properly. */
                  checksum|=0x0D00;
                  break;
            }
            case checksumTypeCrc16: {
                  checksum=crc16Calculate(message,*size);
                  break;
            }
            default:
            case checksumTypeChecksum: {
                  checksum=checksumCalculate(message,*size);
                  break;
            }
      }
      message[(*size)++]=checksum&0xFF;
      message[(*size)++]=(checksum>>8)&0xFF;
}

/* Nice way of leaving no traces...
 * ...the more we know, the more we return. */
#define jpnevulatorGarbageCollect() { \
      ttyDestroy(); \
      if(input!=NULL) { \
            ioClose(input); \
      } \
      if(message!=NULL) { \
            free(message); \
      } \
}
enum jpnevulatorRtrn jpnevulatorWrite(void) {
      int byte; 
      struct tty *tty;
      FILE *input=NULL;
      unsigned char *message=NULL;
      int index;
      int line;

      /* Open our input file. */
      input=ioOpen("r");
      if(input==NULL) {
            perror(PROGRAM_NAME": Unable to open input");
            jpnevulatorGarbageCollect();
            return(jpnevulatorRtrnNoInput);
      }

      /* Allocate memory for the messages to send. */
      message=(unsigned char *)malloc(sizeof(message[0])*_jpnevulatorOptions.size);
      if(message==NULL) {
            perror(PROGRAM_NAME": Unable to allocate memory for message");
            jpnevulatorGarbageCollect();
            return(jpnevulatorRtrnNoMessage);
      }

      /* Collect and send the messages. We collect the messages byte by byte from the
       * input and send them on the line when we receive an end-of-line. */
      line=1;
      for(index=0;(byte=byteGet(input))!=byteRtrnEOF;) {
            switch(byte) {
                  case byteRtrnEOL: {
                        int n;

                        /* Add a checksum to the message if requested. */
                        if(_jpnevulatorOptions.checksum!=checksumTypeNone) {
                              messageChecksumAdd(message,&index);
                              if(boolIsSet(_jpnevulatorOptions.checksumFuckup)) {
                                    /* Subtract one from the last checksum byte of the message if the user
                                     * request to fuck up the checksum. */
                                    message[index-1]-=1;
                              }
                        }

                        /* Send the message on the line. */
                        if(boolIsSet(_jpnevulatorOptions.send)) {
                              if((tty=(struct tty *)listFirst(&_jpnevulatorOptions.tty))!=NULL) {
                                    do {
                                          /* Delay between bytes if requested. */
                                          if(_jpnevulatorOptions.delayByte>0) {
                                                int byteIndex;
                                                for(byteIndex=0;byteIndex<index;byteIndex++) {
                                                      n=write(tty->fd,&(message[byteIndex]),1);
                                                      if(n<0) {
                                                            fprintf(stderr,"%s: %s: write of line %d byte %d failed(%d).\n",PROGRAM_NAME,ttyPrint(tty),line,byteIndex,n);
                                                      }
                                                      usleep(_jpnevulatorOptions.delayByte);
                                                }
                                          } else {
                                                n=write(tty->fd,message,sizeof(message[0])*index);
                                                if(n<0) {
                                                      fprintf(stderr,"%s: %s: write of line %d failed(%d).\n",PROGRAM_NAME,ttyPrint(tty),line,n);
                                                }
                                          }
                                    } while((tty=(struct tty *)listNext(&_jpnevulatorOptions.tty))!=NULL);
                              }
                        }

                        /* Print the message if requested. */
                        if(boolIsSet(_jpnevulatorOptions.print)) {
                              for(n=0;n<index;n++) {
                                    printf("%02X%c",message[n],n!=(index-1)?' ':'\n');
                              }
                        }

                        /* Delay between messages if requested. */
                        if(_jpnevulatorOptions.delayLine>0) {
                              usleep(_jpnevulatorOptions.delayLine);
                        }

                        /* Start again with a new message and increase the line counter. */
                        index=0;
                        line++;
                        break;
                  }
                  case byteRtrnUnknown: {
                        /* Warn the user if we read invalid characters in the input file. We only give a warning and still
                         * send the message. The user might now what he or she is doing :-) */
                        fprintf(stderr,"%s: invalid characters on input line %d. Message can be corrupted.\n",PROGRAM_NAME,line);
                        break;
                  }
                  default: {
                        /* Place the new input byte into the message. Check if there is enough room for
                         * this new byte and do leave some room(2 bytes) for the checksum if necessary.
                         * Nice trick ;-) */
                        if(index<(_jpnevulatorOptions.size-(_jpnevulatorOptions.checksum*2))) {
                              message[index++]=byte;
                        } else {
                              fprintf(stderr,"%s: Input line %d too big. Increase message size (--size).\n",PROGRAM_NAME,line);
                        }
                        break;
                  }
            }
      }

      /* Free allocated memory and close files opened. */
      jpnevulatorGarbageCollect();

      return(jpnevulatorRtrnOk);
}
#undef jpnevulatorGarbageCollect

/* Nice way of leaving no traces...
 * ...the more we know, the more we return. */
#define jpnevulatorGarbageCollect() { \
      ttyDestroy(); \
      if(output!=NULL) { \
            ioClose(output); \
      } \
      if(message!=NULL) { \
            free(message); \
      } \
      if(ascii!=NULL) { \
            free(ascii); \
      } \
}
enum jpnevulatorRtrn jpnevulatorRead(void) {
      FILE *output=NULL;
      unsigned char *message=NULL;
      char *ascii=NULL;
      int asciiSize;
      ssize_t bytesRead;
      int bytesWritten;
      struct timeval timeCurrent,timeLast,*timeoutPtr,timeout;
      struct tm *time;
      fd_set readfdsReal,readfdsCopy;
      struct tty *ttyReader,*ttyWriter;
      char ttyNameCopy[sizeof(ttyReader->name)];
      int nfds;

      /* Open our output file. */
      output=ioOpen("w");
      if(output==NULL) {
            perror(PROGRAM_NAME": Unable to open output");
            jpnevulatorGarbageCollect();
            return(jpnevulatorRtrnNoOutput);
      }

      /* Allocate memory for the messages to receive. */
      message=(unsigned char *)malloc(sizeof(message[0])*_jpnevulatorOptions.size);
      if(message==NULL) {
            perror(PROGRAM_NAME": Unable to allocate memory for message");
            jpnevulatorGarbageCollect();
            return(jpnevulatorRtrnNoMessage);
      }

      /* Allocate memory for the ascii data to print if desired. */
      if(boolIsSet(_jpnevulatorOptions.ascii)) {
            asciiSize=(sizeof(ascii[0])*_jpnevulatorOptions.width)+1;
            ascii=(char *)malloc(asciiSize);
            if(ascii==NULL) {
                  perror(PROGRAM_NAME": Unable to allocate memory for ascii data");
                  jpnevulatorGarbageCollect();
                  return(jpnevulatorRtrnNoAscii);
            }
            memset(ascii,'\0',asciiSize);
      }

      /* Initialize our last time to be far enough from the current time. Far
       * enough is a little bit more than --timing-delta away from it. This way
       * our first data will always gets it's timing information if requested. Take
       * notice that I use timeCurrent here, since that one will be assigned to
       * timeLast before gettimeofday() in the loop. */
      gettimeofday(&timeCurrent,NULL);
      timeCurrent.tv_sec-=(_jpnevulatorOptions.timingDelta/1000000L)+1;

      /* Setup our set of read file descriptors to watch. We set up the copy
       * so we don't have to parse our list of tty devices every time we iterate. */
      FD_ZERO(&readfdsCopy);
      nfds=0;
      if((ttyReader=(struct tty *)listFirst(&_jpnevulatorOptions.tty))!=NULL) {
            do {
                  FD_SET(ttyReader->fd,&readfdsCopy);
                  nfds=max(nfds,ttyReader->fd);
            } while((ttyReader=(struct tty *)listNext(&_jpnevulatorOptions.tty))!=NULL);
      } else {
            fprintf(stderr,"%s: No available tty to read from\n",PROGRAM_NAME);
            jpnevulatorGarbageCollect();
            return(jpnevulatorRtrnNoTTY);
      }

      /* Clear our copy of the tty name, so if multiple tty devices are
       * given it will print the first one and only on a change the name
       * of the tty device will be printed. */
      memset(ttyNameCopy,'\0',sizeof(ttyNameCopy));

      /* Do we need a timeout? We only need this when we also display the ASCII
       * values for the received bytes. In that case we use the timeout to display
       * the ASCII values automatically, otherwise they will never appear when less
       * then the line length bytes are received and no more bytes are coming. */
      if(boolIsSet(_jpnevulatorOptions.ascii)) {
            timeoutPtr=&timeout;
      } else {
            timeoutPtr=NULL;
      }
      /* Receive our messages. */
      bytesWritten=0;
      for(;;) {
            int index;
            int rtrn;
            /* Restore our set of read file descriptors. */
            readfdsReal=readfdsCopy;
            if(timeoutPtr!=NULL) {
                  timeoutPtr->tv_sec=_jpnevulatorOptions.timingDelta/1000000L;
                  timeoutPtr->tv_usec=_jpnevulatorOptions.timingDelta%1000000L;
            }
            /* Wait and see if anything flows in. */
            rtrn=select(nfds+1,&readfdsReal,NULL,NULL,timeoutPtr);
            if(rtrn==-1) {
            } else if(rtrn) {
                  if((ttyReader=(struct tty *)listFirst(&_jpnevulatorOptions.tty))!=NULL) {
                        do {
                              if(FD_ISSET(ttyReader->fd,&readfdsReal)) {
                                    bytesRead=read(ttyReader->fd,message,_jpnevulatorOptions.size);
                                    if(bytesRead>0) {
                                          timeLast=timeCurrent;
                                          gettimeofday(&timeCurrent,NULL);
                                          if(
                                                boolIsSet(_jpnevulatorOptions.timingPrint)&&
                                                ((memcmp(ttyNameCopy,ttyReader->name,sizeof(ttyNameCopy))!=0)||
                                                (((((timeCurrent.tv_sec-timeLast.tv_sec)*1000000L)+timeCurrent.tv_usec)-timeLast.tv_usec)>_jpnevulatorOptions.timingDelta))
                                          ) {
                                                time=localtime(&timeCurrent.tv_sec);
                                                if(bytesWritten!=0) {
                                                      if(boolIsSet(_jpnevulatorOptions.ascii)) {
                                                            for(index=bytesWritten;index<_jpnevulatorOptions.width;index++) {
                                                                  fprintf(output,"   ");
                                                            }
                                                            fprintf(output,"\t%s",ascii);
                                                            memset(ascii,'\0',asciiSize);
                                                      }
                                                      fprintf(output,"\n");
                                                }
                                                fprintf(
                                                      output,
                                                      "%04d-%02d-%02d %02d:%02d:%02d.%06ld:",
                                                      time->tm_year+1900,time->tm_mon+1,time->tm_mday,
                                                      time->tm_hour,time->tm_min,time->tm_sec,timeCurrent.tv_usec
                                                );
                                                /* If more than one tty device is given we want it always to
                                                 * be displayed as part of the printing of the timing. It's
                                                 * way to confusing otherwise. */
                                                if(listElements(&_jpnevulatorOptions.tty)>1) {
                                                      fprintf(output," %s",ttyPrint(ttyReader));
                                                }
                                                memcpy(ttyNameCopy,ttyReader->name,sizeof(ttyNameCopy));
                                                fprintf(output,"\n");
                                                bytesWritten=0;
                                          } else {
                                                if(
                                                      (listElements(&_jpnevulatorOptions.tty)>1)&&
                                                      (memcmp(ttyNameCopy,ttyReader->name,sizeof(ttyNameCopy))!=0)
                                                ) {
                                                      if(bytesWritten!=0) {
                                                            if(boolIsSet(_jpnevulatorOptions.ascii)) {
                                                                  for(index=bytesWritten;index<_jpnevulatorOptions.width;index++) {
                                                                        fprintf(output,"   ");
                                                                  }
                                                                  fprintf(output,"\t%s",ascii);
                                                                  memset(ascii,'\0',asciiSize);
                                                            }
                                                            fprintf(output,"\n");
                                                      }
                                                      fprintf(output,"%s\n",ttyPrint(ttyReader));
                                                      memcpy(ttyNameCopy,ttyReader->name,sizeof(ttyNameCopy));
                                                      bytesWritten=0;
                                                }
                                          }
                                          for(index=0;index<bytesRead;index++) {
                                                if(bytesWritten>=_jpnevulatorOptions.width) {
                                                      if(boolIsSet(_jpnevulatorOptions.ascii)) {
                                                            fprintf(output,"\t%s",ascii);
                                                            memset(ascii,'\0',asciiSize);
                                                      }
                                                      fprintf(output,"\n");
                                                      bytesWritten=0;
                                                } else {
                                                      if(bytesWritten!=0) fprintf(output," ");
                                                }
                                                if((bytesWritten==0)&&boolIsSet(_jpnevulatorOptions.byteCountDisplay)) {
                                                      fprintf(output,"%08lX\t",ttyReader->byteCount);
                                                }
                                                fprintf(output,"%02X",message[index]);
                                                /* Increase the byte count for this tty. */
                                                ttyReader->byteCount++;
                                                if(boolIsSet(_jpnevulatorOptions.ascii)) {
                                                      ascii[bytesWritten]=isprint(message[index])?message[index]:'.';
                                                }
                                                bytesWritten++;
                                          }
                                          /* Does the user want to pass the data between all the tty's? */
                                          if(boolIsSet(_jpnevulatorOptions.pass)) {
                                                struct listElement *ttyListPosition;
                                                /* Save the current position in the tty list, since we are about to traverse
                                                       it again. */
                                                ttyListPosition=listCurrentPositionSave(&_jpnevulatorOptions.tty);
                                                /* Traverse the tty list again in search for all the other (not this read) tty's. For
                                                 * every tty found write the read message to it. */
                                                if((ttyWriter=(struct tty *)listFirst(&_jpnevulatorOptions.tty))!=NULL) {
                                                      do {
                                                            if(ttyWriter->fd!=ttyReader->fd) {
                                                                  ssize_t n;
                                                                  n=write(ttyWriter->fd,message,bytesRead);
                                                                  if(n<0) {
                                                                        fprintf(stderr,"%s: %s: write of %ld bytes failed(%ld).\n",PROGRAM_NAME,ttyPrint(ttyWriter),bytesRead,n);
                                                                  }
                                                            }
                                                      } while((ttyWriter=(struct tty *)listNext(&_jpnevulatorOptions.tty))!=NULL);
                                                }
                                                /* Restore the current position for the tty list again. */
                                                listCurrentPositionLoad(&_jpnevulatorOptions.tty,ttyListPosition);
                                          }
                                          fflush(output);
                                    }
                              }
                        } while((ttyReader=(struct tty *)listNext(&_jpnevulatorOptions.tty))!=NULL);
                  }
            } else {
                  /* No more bytes received within the timeout, so let's write our
                   * ASCII data if necessary. */
                  if(bytesWritten!=0) {
                        if(boolIsSet(_jpnevulatorOptions.ascii)) {
                              for(index=bytesWritten;index<_jpnevulatorOptions.width;index++) {
                                    fprintf(output,"   ");
                              }
                              fprintf(output,"\t%s",ascii);
                              memset(ascii,'\0',asciiSize);
                        }
                        fprintf(output,"\n");
                        bytesWritten=0;
                  }
            }
      }

      /* Close files opened. */
      jpnevulatorGarbageCollect();

      return(jpnevulatorRtrnOk);
}
#undef jpnevulatorGarbageCollect

Generated by  Doxygen 1.6.0   Back to index