/**************************************************************************** * * Copyright (c) 2006 Dave Hylands * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. * ****************************************************************************/ /** * * sertest.c * * PURPOSE: * * This implements a sample program for accessing the serial port. * *****************************************************************************/ /* ---- Include Files ---------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include /* ---- Public Variables ------------------------------------------------- */ int gFd = -1; int gVal; /* ---- Private Constants and Types -------------------------------------- */ /* ---- Private Variables ------------------------------------------------ */ struct option gLongOption[] = { // option A Flag V (has_arg, flag, val) // ----------- - ---- --- { "baud", 1, NULL, 'b' }, { "debug", 0, NULL, 'd' }, { "help", 0, NULL, 'h' }, { "port", 1, NULL, 'p' }, { "verbose", 0, NULL, 'v' }, { 0 }, }; struct { speed_t speed; unsigned baudRate; } gBaudTable[] = { { B50, 50 }, { B75, 75 }, { B110, 110 }, { B134, 134 }, { B150, 150 }, { B200, 200 }, { B300, 300 }, { B600, 600 }, { B1200, 1200 }, { B1800, 1800 }, { B2400, 2400 }, { B4800, 4800 }, { B9600, 9600 }, { B19200, 19200 }, { B38400, 38400 }, { B57600, 57600 }, { B115200, 115200 }, { B230400, 230400 } }; #define ARRAY_LEN(x) ( sizeof( x ) / sizeof( x[ 0 ])) int gVerbose = 0; int gDebug = 0; int gPortFd = -1; /* ---- Private Function Prototypes -------------------------------------- */ void *ReaderThread( void *param ); char *StrMaxCpy( char *dst, const char *src, size_t maxLen ); char *StrMaxCat( char *dst, const char *src, size_t maxLen ); void Usage( void ); /* ---- Functions -------------------------------------------------------- */ /*************************************************************************** * * main * ****************************************************************************/ int main( int argc, char **argv ) { int rc; int opt; char devName[ 40 ]; const char *baudStr = NULL; const char *portStr = "ttyS2"; speed_t baudRate; pthread_t readerThreadId; struct termios attr; // Parse the command line options while (( opt = getopt_long( argc, argv, "b:dhp:v", gLongOption, NULL )) > 0 ) { switch ( opt ) { case 'b': { baudStr = optarg; break; } case 'd': { gDebug = 1; break; } case 'p': { portStr = optarg; break; } case 'v': { gVerbose = 1; break; } case '?': case 'h': { Usage(); return 1; } } } devName[ 0 ] = '\0'; if ( portStr[ 0 ] != '/' ) { StrMaxCpy( devName, "/dev/", sizeof( devName )); } StrMaxCat( devName, portStr, sizeof( devName )); baudRate = B0; if ( baudStr == NULL ) { baudRate = B9600; } else { int baudIdx; int testBaud = atoi( baudStr ); for ( baudIdx = 0; baudIdx < ARRAY_LEN( gBaudTable ); baudIdx++ ) { if ( gBaudTable[ baudIdx ].baudRate == testBaud ) { baudRate = gBaudTable[ baudIdx ].speed; break; } } if ( baudRate == B0 ) { fprintf( stderr, "Unrecognized baud rate: '%s'\n", baudStr ); exit( 1 ); } } if (( gPortFd = open( devName, O_RDWR | O_EXCL )) < 0 ) { fprintf( stderr, "Unable to open serial port '%s': %s\n", devName, strerror( errno )); exit( 2 ); } if ( tcgetattr( gPortFd, &attr ) < 0 ) { fprintf( stderr, "Call to tcgetattr failed: %s\n", strerror( errno )); exit( 3 ); } attr.c_iflag = 0; attr.c_oflag = 0; attr.c_cflag = CLOCAL | CREAD | CS8; attr.c_lflag = 0; attr.c_cc[ VTIME ] = 0; // timeout in tenths of a second attr.c_cc[ VMIN ] = 1; // Only wait for a single char cfsetispeed( &attr, baudRate ); cfsetospeed( &attr, baudRate ); if ( tcsetattr( gPortFd, TCSAFLUSH, &attr ) < 0 ) { fprintf( stderr, "Call to tcsetattr failed: %s\n", strerror( errno )); exit( 4 ); } // Put stdin & stdout in unbuffered mode. setbuf( stdin, NULL ); setbuf( stdout, NULL ); // Put stdin in raw mode (i.e. turn off canonical mode). Canonical mode // causes the driver to wait for the RETURN character so that line editing // can take place. We also want to turn off ECHO. { struct termios tio; if ( tcgetattr( fileno( stdin ), &tio ) < 0 ) { fprintf( stderr, "Unable to retrieve terminal settings: %s\n", strerror( errno )); exit( 5 ); } tio.c_lflag &= ~( ICANON | ECHO ); tio.c_cc[VTIME] = 0; tio.c_cc[VMIN] = 1; if ( tcsetattr( fileno( stdin ), TCSANOW, &tio ) < 0 ) { fprintf( stderr, "Unable to update terminal settings: %s\n", strerror( errno )); exit( 6 ); } } // Kick off the serial port reader thread. rc = pthread_create( &readerThreadId, NULL, ReaderThread, NULL ); if ( rc != 0 ) { fprintf( stderr, "Error creating ReaderThread: %s\n", strerror( rc )); exit( 7 ); } // Read stdin and send rcvd chars to the serial port while ( 1 ) { char ch; int chInt = fgetc( stdin ); if ( chInt < 0 ) { fprintf( stderr, "Exiting...\n" ); break; } ch = (char)chInt; if ( gDebug ) { if (( ch < ' ' ) || ( ch > '~' )) { fprintf( stderr, "stdin Read: 0x%02x '.'\n", ch ); } else { fprintf( stderr, "stdin Read: 0x%02x '%c'\n", ch, ch ); } } if ( write( gPortFd, &ch, 1 ) != 1 ) { fprintf( stderr, "write to serial port failed: %s\n", strerror( errno )); break; } } close( gPortFd ); exit( 0 ); return 0; // Get rid of warning about not returning anything } /***************************************************************************/ /** * Thread which processes the incoming serial data. */ void *ReaderThread( void *param ) { while ( 1 ) { char ch; int bytesRead; if (( bytesRead = read( gPortFd, &ch, 1 )) < 0 ) { fprintf( stderr, "Serial port read failed: %s\n", strerror( errno )); exit( 1 ); } if ( gDebug ) { if (( ch < ' ' ) || ( ch > '~' )) { fprintf( stderr, "Serial Read: 0x%02x '.'\n", ch ); } else { fprintf( stderr, "Serial Read: 0x%02x '%c'\n", ch, ch ); } } putc( ch, stdout ); } return 0; } // ReaderThread /***************************************************************************/ /** * Concatenates source to the destination, but makes sure that the * destination string (including terminating null), doesn't exceed maxLen. * * @param dst (mod) String to concatnate onto. * @param src (in) String to being added to the end of @a dst. * @param maxLen (in) Maximum length that @a dst is allowed to be. * * @return A pointer to the destination string. */ char *StrMaxCat( char *dst, const char *src, size_t maxLen ) { size_t dstLen = strlen( dst ); if ( dstLen < maxLen ) { StrMaxCpy( &dst[ dstLen ], src, maxLen - dstLen ); } return dst; } /* StrMaxCat */ /***************************************************************************/ /** * Copies the source to the destination, but makes sure that the * destination string (including terminating null), doesn't exceed * maxLen. * * @param dst (out) Place to store the string copy. * @param src (in) String to copy. * @param maxLen (in) Maximum number of characters to copy into @a dst. * * @return A pointer to the destination string. */ char *StrMaxCpy( char *dst, const char *src, size_t maxLen ) { if ( maxLen < 1 ) { /* * There's no room in the buffer? */ return ""; } if ( maxLen == 1 ) { /* * There's only room for the terminating null character */ dst[ 0 ] = '\0'; return dst; } /* * The Visual C++ version of strncpy writes to every single character * of the destination buffer, so we use a length one character smaller * and write in our own null (if required). * * This allows the caller to store a sentinel in the last byte of the * buffer to detect overflows (if desired). */ strncpy( dst, src, maxLen - 1 ); if (( strlen( src ) + 1 ) >= maxLen ) { /* * The string exactly fits, or probably overflows the buffer. * Write in the terminating null character since strncpy doesn't in * this particular case. * * We don't do this arbitrarily so that the caller can use a sentinel * in the very end of the buffer to detect buffer overflows. */ dst[ maxLen - 1 ] = '\0'; } return dst; } /* StrMaxCpy */ /*************************************************************************** * * Usage * ****************************************************************************/ void Usage() { fprintf( stderr, "Usage: sertest [option(s)]\n" ); fprintf( stderr, " Download a program via serial/i2c\n" ); fprintf( stderr, "\n" ); fprintf( stderr, " -b, --baud=baud Set the baudrate used\n" ); fprintf( stderr, " -d, --debug Turn on debug output\n" ); fprintf( stderr, " -h, --help Display this message\n" ); fprintf( stderr, " -p, --port=port Set the I/O port\n" ); fprintf( stderr, " -v, --verbose Turn on verbose messages\n" ); } // Usage