// Serial inteface implementation for POSIX-compliant systems #include "serial.h" #include #include #include #include #include #include #include #include #include static u32 ser_timeout = SER_INF_TIMEOUT; // Open the serial port ser_handler ser_open( const char* sername ) { int fd; if( ( fd = open( sername, O_RDWR | O_NOCTTY | O_NDELAY ) ) == -1 ) perror( "ser_open: unable to open port" ); else fcntl( fd, F_SETFL, 0 ); return ( ser_handler )fd; } // Close the serial port void ser_close( ser_handler id ) { close( ( int )id ); } // Helper function: get baud ID from actual baud rate #define BAUDCASE(x) case x: return B##x static u32 ser_baud_to_id( u32 baud ) { switch( baud ) { BAUDCASE( 1200 ); BAUDCASE( 1800 ); BAUDCASE( 2400 ); BAUDCASE( 4800 ); BAUDCASE( 9600 ); BAUDCASE( 19200 ); BAUDCASE( 38400 ); BAUDCASE( 57600 ); BAUDCASE( 115200 ); BAUDCASE( 230400 ); } return 0; } // Helper function: get number of bits ID from actual number of bits #define NBCASE(x) case x: return CS##x static int ser_number_of_bits_to_id( int nb ) { switch( nb ) { NBCASE( 5 ); NBCASE( 6 ); NBCASE( 7 ); NBCASE( 8 ); } return 0; } int ser_setup( ser_handler id, u32 baud, int databits, int parity, int stopbits ) { struct termios termdata; int hnd = ( int )id; usleep( 200000 ); tcgetattr( hnd, &termdata ); // Baud rate cfsetispeed( &termdata, ser_baud_to_id( baud ) ); cfsetospeed( &termdata, ser_baud_to_id( baud ) ); // Parity / stop bits termdata.c_cflag &= ~CSTOPB; if( parity == SER_PARITY_NONE ) // no parity { termdata.c_cflag &= ~PARENB; } else if( parity == SER_PARITY_EVEN ) // even parity { termdata.c_cflag |= PARENB; termdata.c_cflag &= ~PARODD; } else if( parity == SER_PARITY_ODD ) // odd parity { termdata.c_cflag |= PARENB; termdata.c_cflag |= PARODD; } // Data bits termdata.c_cflag |= ( CLOCAL | CREAD ); termdata.c_cflag &= ~CSIZE; termdata.c_cflag |= ser_number_of_bits_to_id( databits ); // Disable HW and SW flow control termdata.c_cflag &= ~CRTSCTS; termdata.c_iflag &= ~( IXON | IXOFF | IXANY ); // Raw input termdata.c_lflag &= ~( ICANON | ECHO | ECHOE | ISIG ); // Raw output termdata.c_oflag &= ~OPOST; // Check and strip parity bit if( parity == SER_PARITY_NONE ) termdata.c_iflag &= ~( INPCK | ISTRIP ); else termdata.c_iflag |= ( INPCK | ISTRIP ); termdata.c_cc[ VMIN ] = 1; termdata.c_cc[ VTIME ] = 0; // Set the attibutes now tcsetattr( hnd, TCSANOW, &termdata ); // Flush everything tcflush( hnd, TCIOFLUSH ); // And set blocking mode by default fcntl( id, F_SETFL, 0 ); // All done return SER_OK; } // Read up to the specified number of bytes, return bytes actually read u32 ser_read( ser_handler id, u8* dest, u32 maxsize ) { struct termios termdata; tcgetattr( id, &termdata ); if( ser_timeout == SER_INF_TIMEOUT ) { termdata.c_cc[ VMIN ] = maxsize; termdata.c_cc[ VTIME ] = 0; tcsetattr( id, TCSANOW, &termdata ); return ( u32 )read( ( int )id, dest, maxsize ); } else { fd_set readfs; struct timeval tv; int retval; termdata.c_cc[ VMIN ] = 1; termdata.c_cc[ VTIME ] = 0; tcsetattr( id, TCSANOW, &termdata ); FD_ZERO( &readfs ); FD_SET( ( int )id, &readfs ); tv.tv_sec = ser_timeout / 1000000; tv.tv_usec = ( ser_timeout % 1000000 ) * 1000; retval = select( ( int )id + 1, &readfs, NULL, NULL, &tv ); if( retval == -1 || retval == 0 ) return 0; else return ( u32 )read( ( int )id, dest, maxsize ); } } // Read a single byte and return it (or -1 for error) int ser_read_byte( ser_handler id ) { u8 data; int res = ser_read( id, &data, 1 ); return res == 1 ? data : -1; } // Write up to the specified number of bytes, return bytes actually written u32 ser_write( ser_handler id, const u8 *src, u32 size ) { u32 res; res = ( u32 )write( ( int )id, src, size ); return res; } // Write a byte to the serial port u32 ser_write_byte( ser_handler id, u8 data ) { return ( u32 )write( id, &data, 1 ); } // Set communication timeout void ser_set_timeout_ms( ser_handler id, u32 timeout ) { ser_timeout = timeout; }