#define VERSION "1.0 (October 16, 2006)" /*---------------------------------------------------------------------- * serial_writer.c - pass some data to a serial port * * - Rick Reynolds * * This program was written for Tyler Jones and Rasmus Birn. *---------------------------------------------------------------------- */ static char g_history[] = "----------------------------------------------------------------------\n" " history:\n" "\n" " 0.1 October 12, 2006 [rickr] - initial program\n" " 1.0 October 16, 2006 [rickr] - added -ms_sleep, -nblocks and -swap\n" "----------------------------------------------------------------------\n"; #include /* Standard input/output definitions */ #include /* String function definitions */ #include /* POSIX terminal control definitions */ #include /* UNIX standard function definitions */ #include /* File control definitions */ #include /* Error number definitions */ #include #include #include #include #include #include #include typedef struct{ char * sport_fname; /* serial port file name */ char * in_fname; /* input filename (stdin if NULL) */ int make_data; /* create data */ int block_len; /* length of writing block */ int nblocks; /* number of blocks to write (0 for inf) */ int swap; /* swap 'n' bytes (0 to do no swap) */ int ms_sleep; /* sleep this number of ms betweeen blocks */ int debug; } opts_t; typedef struct{ int spfd; /* serial port file descriptor */ int infd; /* 0 if stdin */ } control_t; opts_t gopts = { NULL, NULL, 0, 12, 0, 0, 0, 0 }; control_t gcontr = { -1, 0 }; /* uninit, stdin */ #define SW_USE_SHORT 1 #define SW_USE_LONG 2 #define SW_USE_HIST 3 #define SW_USE_VER 4 #define CHECK_NULL_STR(str) ( str ? str : "(NULL)" ) /* protos */ void cleanup (int sig_num); int close_ports( void ); int disp_hex_bytes( char * mesg, char * data, int len ); int disp_opts_t( char * info, opts_t * O ); int get_opts (opts_t *opt, int argc, char *argv[]); int ms_sleep( int ms ); int open_serial(opts_t *opt, control_t * contr); int send_serial(opts_t * opt, control_t * contr, char * data, int len); int set_data ( opts_t * opts, control_t * contr, char * data ); int swap_2( char * data, int nshort ); int swap_4( char * data, int nint ); int usage ( char * prog, int level ); int main(int argc, char *argv[]) { int rv, len, nblocks, done = 0; char * data = NULL; if ( (rv = get_opts(&gopts, argc, argv)) != 0 ) return rv; /* register interrupt trap */ signal(SIGTERM, cleanup); /* so need global variables */ signal(SIGINT, cleanup); /* open or set input file */ if( gopts.in_fname ) { gcontr.infd = open(gopts.in_fname, O_RDONLY); if( gcontr.infd < 0 ) { fprintf(stderr,"** failed to open infile '%s'\n",gopts.in_fname); return 1; } } else gcontr.infd = 0; /* stdin */ /* allocate data block */ data = (char *)malloc(gopts.block_len * sizeof(char)); if( !data ) { fprintf(stderr,"** failed to alloc %d bytes for block\n", gopts.block_len); return 1; } if( gopts.swap == 2 ) swap_2(data, gopts.block_len/2); else if( gopts.swap == 4 ) swap_4(data, gopts.block_len/4); if( (rv = open_serial(&gopts, &gcontr)) != 0 ) return rv; nblocks = 0; while(! done && (gopts.nblocks <= 0 || nblocks < gopts.nblocks) ) { len = set_data(&gopts, &gcontr, data); if( len > 0 ) done = send_serial(&gopts, &gcontr, data, len); else done = 1; nblocks++; if( gopts.ms_sleep ) ms_sleep(gopts.ms_sleep); } free(data); close_ports(); if(gopts.debug) fprintf(stderr,"-d wrote %d blocks of data\n",nblocks); return 0; } /* ---------------------------------------------------------------------- * set_data * ---------------------------------------------------------------------- */ int set_data( opts_t * opts, control_t * contr, char * data ) { static short counter = 0, size = sizeof(counter); short * sp; int c; if ( opts->make_data ) { int ints = opts->block_len / size; if( opts->block_len % size ) { fprintf(stderr,"** block_len should be multiple of %d\n",size); return -1; } if( opts->debug ) fprintf(stderr,"-d setting %d ints from %8d to %8d\n", ints, counter, counter+ints-1); sp = (short *)data; for( c = 0; c < ints; c++ ) *sp++ = counter++; return opts->block_len; } c = read(contr->infd, data, opts->block_len); if( c < 0 ) fprintf(stderr,"** read nothing from input stream\n"); /* maybe the user would like to verify incoming data */ if( opts->debug > 2 ) disp_hex_bytes( "-d all received data: ", data, c ); else if( opts->debug > 1 ) disp_hex_bytes( "-d first <= 16 bytes: ", data, c < 16 ? c : 16 ); return c; } /* ---------------------------------------------------------------------- * close serial port and data socket * ---------------------------------------------------------------------- */ int close_ports( void ) { if ( gcontr.spfd >= 0 ) close(gcontr.spfd); if ( gcontr.infd >= 0 ) close(gcontr.infd); return 0; } /* close any open ports (to possibly catch an interrupt) */ void cleanup(int sig_num) { if ( gopts.debug > 0 ) { fprintf(stderr, "-- received signal %d: closing ports\n", sig_num); if ( gopts.debug > 1 ) fprintf(stderr," descriptors: ser = %d, file = %d\n", gcontr.spfd, gcontr.infd); } close_ports(); exit(0); } #define CHECK_ARG_COUNT(ac,str) \ do { \ if ((ac+1) >= argc) { \ fputs(str,stderr); \ return -1; \ } \ } while (0) \ int get_opts(opts_t *opt, int argc, char *argv[]) { char * prog = argv[0]; int ac; if ( argc < 2 ) return usage(prog, SW_USE_SHORT); for ( ac = 1; ac < argc; ac++ ) /* help, hist first, rest alphabetical */ { if ( !strncmp(argv[ac], "-help", 5) ) return usage(prog, SW_USE_LONG); else if ( !strncmp(argv[ac], "-hist", 5) ) return usage(prog, SW_USE_HIST); else if ( !strncmp(argv[ac], "-ver", 4) ) return usage(prog, SW_USE_VER); /* execution options */ else if ( !strncmp(argv[ac], "-block_len", 6) ) { CHECK_ARG_COUNT(ac, "opt use: -block_len LENGTH\n"); opt->block_len = atoi(argv[++ac]); if( opt->block_len <= 0 ) { fprintf(stderr,"** -block_len LENGTH needs to be positive\n"); return -1; } } else if ( !strncmp(argv[ac], "-debug", 6) ) { CHECK_ARG_COUNT(ac, "opt use: -debug DEBUG_LEVEL\n"); opt->debug = atoi(argv[++ac]); } else if ( !strncmp(argv[ac], "-infile", 7) ) { CHECK_ARG_COUNT(ac, "opt use: -infile INPUT_FILENAME\n"); opt->in_fname = argv[++ac]; } else if ( !strncmp(argv[ac], "-make_data", 7) ) { opt->make_data = 1; } else if ( !strncmp(argv[ac], "-ms_sleep", 9) ) { CHECK_ARG_COUNT(ac, "opt use: -ms_sleep MS_TO_SLEEP\n"); opt->ms_sleep = atoi(argv[++ac]); if( opt->ms_sleep <= 0 ) { fprintf(stderr,"** -ms_sleep MS_TO_SLEEP must be positive\n"); return -1; } } else if ( !strncmp(argv[ac], "-nblocks", 7) ) { CHECK_ARG_COUNT(ac, "opt use: -nblocks NUM_BLOCKS\n"); opt->nblocks = atoi(argv[++ac]); if( opt->nblocks <= 0 ) { fprintf(stderr,"** -nblocks NUM_BLOCKS needs to be positive\n"); return -1; } } else if ( !strncmp(argv[ac], "-serial_port", 7) ) { CHECK_ARG_COUNT(ac, "opt use: -serial_port SERIAL_FILENAME\n"); opt->sport_fname = argv[++ac]; } else if ( !strncmp(argv[ac], "-swap", 5) ) { CHECK_ARG_COUNT(ac, "opt use: -swap NUM_BYTES\n"); opt->swap = atoi(argv[++ac]); } else { fprintf(stderr,"** invalid option '%s', exiting...\n", argv[ac]); return -1; } } if ( opt->debug > 1 ) disp_opts_t( "options read: ", opt ); if ( opt->swap != 0 && opt->swap != 2 && opt->swap != 4 ) { fprintf(stderr,"** invalid -swap '%d', must be one of 0, 2 or 4\n", opt->swap); return 1; } return 0; } /* ---------------------------------------------------------------------- * display usage * * SW_USE_SHORT : most basic usage info * SW_USE_LONG : display complete description of program and options * SW_USE_VER : display version and compile date * ---------------------------------------------------------------------- */ int usage( char * prog, int level ) { if ( level == SW_USE_SHORT ) printf( "usage: %s -help\n" "usage: %s [options] -serial_port FILENAME\n", prog, prog ); else if ( level == SW_USE_HIST ) fputs(g_history, stdout); else if ( level == SW_USE_VER ) printf("%s version %s, compiled %s\n", prog, VERSION, __DATE__); else if ( level == SW_USE_LONG ) { printf( "------------------------------------------------------------\n" "%s - send data to a serial port\n" "\n" " Write data from a file, piped to stdin, or made up to a\n" " user specified serial port (given by filename).\n" "\n" " The program terminates when input reaches end of file, or\n" " when the user terminates with ctrl-c.\n" "\n" " usage: %s [options] -serial_port FILENAME\n" "------------------------------------------------------------\n" " examples:\n" "\n" " %s -serial_port /dev/ttyS0\n" "\n" " MAKE_SOME_DATA | %s -serial_port /dev/ttyS0\n" "\n" " %s -serial_port /dev/ttyS0 -infile my_data\n" " %s -serial_port /dev/ttyS0 -infile my_data -debug 2\n" " %s -serial_port /dev/ttyS0 -make_data\n" "\n" "------------------------------------------------------------\n" " I/O options:\n" "\n" " -block_len LEN : specify the length of a block\n" " : -block_len 64\n" "\n" " -infile FILENAME : specify name of file for input\n" " : -infile my_data\n" "\n" " The FILENAME contains data to be sent over the serial\n" " port. The program terminates at end-of-file.\n" "\n" " -make_data : create data\n" "\n" " Create data, instead of getting it from a file.\n" " Currently, this will output increasing 4 byte ints.\n" "\n" " -ms_sleep MS_TO_SLEEP : sleep between blocks\n" "\n" " Sleep for this number of miliseconds between each\n" " block of data.\n" "\n" " -swap NUM_BYTES : specify the number of bytes to swap\n" " : -swap 2\n" "\n" " Swap bytes before sending. NUM_BYTES can be either\n" " 2 or 4 (or 0, for no swapping).\n" "\n" "\n" " -nblocks NUM_BLOCKS : specify the number of blocks\n" " : -nblocks 1024\n" "------------------------------------------------------------\n" " required output port:\n" "\n" " -serial_port FILENAME : specify output serial port\n" " : -serial_port /dev/ttyS0\n" "\n" " The FILENAME is the device file for the serial port\n" " which will be used for output.\n" "------------------------------\n" " other options:\n" "\n" " -debug LEVEL : set the debugging level to LEVEL\n" " : e.g. -debug 2\n" " : default is 1, max is 3\n" "\n" " -help : show this help information\n" "\n" " -hist : show modification history\n" "\n" " -ver : display current version\n" "------------------------------------------------------------\n" " Author: R. Reynolds, (Oct 12, 2006)\n" "------------------------------------------------------------\n", prog, prog, prog, prog, prog, prog, prog ); } else fprintf(stderr,"** usage error: invalid level %d\n", level); return 1; } int send_serial(opts_t * opt, control_t * contr, char * data, int len) { int i = write(contr->spfd, data, len); if(i < len) { fprintf(stderr, "warning: wrote %d of %d bytes to serial port\n", i, len); return 1; } if( opt->debug > 1) fprintf(stderr,"+d wrote %d bytes to port\n", len); return 0; } int open_serial(opts_t *opt, control_t * contr) { struct termios topt; contr->spfd = open(opt->sport_fname, O_RDWR | O_NOCTTY | O_NDELAY); if (contr->spfd < 0) { perror("pe: Failed to open the serial port "); return -1; } fcntl(contr->spfd, F_SETFL, FNDELAY); /* nonblocking reads */ /* get the current options for the port */ tcgetattr(contr->spfd, &topt); /* set the baud rates to 9600 (fim scanner is 115200) */ cfsetispeed(&topt, B115200); cfsetospeed(&topt, B115200); /* enable the receiver and set local mode */ topt.c_cflag |= (CLOCAL | CREAD ); /* set 8 bit N parity */ topt.c_cflag &= ~PARENB; /* topt.c_cflag &= ~CSTOPB; */ topt.c_cflag |= CSTOPB; /* fim */ topt.c_cflag &= ~CSIZE; topt.c_cflag |= CS8; /* raw data input and output */ topt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); topt.c_oflag &= ~OPOST; /*Set the new options for the port*/ if (tcsetattr(contr->spfd, TCSANOW, &topt) == -1) { perror("pe: Failed to set attributes on the serial port "); close(contr->spfd); return -1; } if( opt->debug ) fprintf(stderr,"+d successfully opened serial port %s\n", opt->sport_fname); return 0; } /* ---------------------------------------------------------------------- * display contents of optiondata struct * ---------------------------------------------------------------------- */ int disp_opts_t( char * info, opts_t * O ) { if ( info ) fputs(info, stderr); if ( ! O ) { fprintf(stderr,"** disp_opts_t: O == NULL\n"); return -1; } fprintf(stderr, " opts_t at %p :\n" " sport_fname = %s\n" " in_fname = %s\n" " make_data = %d\n" " debug = %d\n", O, CHECK_NULL_STR(O->sport_fname), CHECK_NULL_STR(O->in_fname), O->make_data, O->debug); return 0; } /* ---------------------------------------------------------------------- * display hex bytes to stderr * ---------------------------------------------------------------------- */ int disp_hex_bytes( char * mesg, char * data, int len ) { int c1, c2; if( !data ) return 1; if( mesg ) fputs(mesg, stderr); for( c1 = 0; c1 < len; /* inner loop */ ) { if( (c1 % 16) == 0 ) fprintf(stderr, "\n offset 0x%08x:", c1); if( (c1 % 4) == 0 ) fprintf(stderr, " "); for( c2 = 0; c2 < 4 && c1 < len; c1++, c2++) fprintf(stderr, "%02x", data[c1]); } if( mesg || len > 0 ) fputc('\n', stderr); return 0; } int ms_sleep( int ms ) { struct timeval tv; if( ms <= 0 ) return 0; tv.tv_sec = ms / 1000; tv.tv_usec = (ms%1000)*1000 ; select( 1 , NULL,NULL,NULL , &tv ) ; return 0; } int swap_2( char * data, int nshort ) { int count; char c, *cp = data; for( count = 0; count < nshort; count++) { c = *cp; cp[0] = cp[1]; cp[1] = c; cp += 2; } return 0; } int swap_4( char * data, int nint ) { int count; char c, *cp = data; for( count = 0; count < nint; count++) { c = cp[0]; cp[0] = cp[3]; cp[3] = c; c = cp[1]; cp[1] = cp[2]; cp[2] = c; cp += 4; } return 0; }