1
0
mirror of https://github.com/elua/elua.git synced 2025-01-08 20:56:17 +08:00
elua/rfs_server_src/serial_posix.c
Bogdan Marinescu e823c8d3fc RFS server fix for serial connections
On Linux, the serial connection was not setup properly, which led to
weird errors in the RFS server. This fix disables any input processing
in the termios layer, thus fixing the problem. Thanks to Markus Korber
for reporting and fixing the issue.
2011-12-17 17:17:46 +02:00

231 lines
5.2 KiB
C

// Serial inteface implementation for POSIX-compliant systems
#include "serial.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include "log.h"
// Open the serial port
ser_handler ser_open( const char* sername )
{
int fd;
if( ( fd = open( sername, O_RDWR | O_NOCTTY | O_NDELAY ) ) == -1 )
return SER_HANDLER_INVALID;
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, int flow )
{
struct termios termdata;
int hnd = ( int )id;
usleep( 200000 );
tcgetattr( hnd, &termdata );
tcdrain( hnd );
// Baud rate
cfsetispeed( &termdata, ser_baud_to_id( baud ) );
cfsetospeed( &termdata, ser_baud_to_id( baud ) );
// Parity / stop bits
if( stopbits == SER_STOPBITS_2 )
termdata.c_cflag |= CSTOPB;
else
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
if( flow == SER_FLOW_NONE )
termdata.c_cflag &= ~CRTSCTS;
else
termdata.c_cflag |= CRTSCTS;
termdata.c_iflag &= ~( IXON | IXOFF | IXANY );
termdata.c_iflag |= IGNBRK;
// Disable input processing
termdata.c_iflag &= ~( INLCR | ICRNL | IGNCR );
// 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 ] = 0;
termdata.c_cc[ VTIME ] = 0;
// Flush everything
tcflush( hnd, TCIOFLUSH );
// Set the attibutes now
tcsetattr( hnd, TCSANOW, &termdata );
// And set non-blocking mode by default
fcntl( hnd, F_SETFL, FNDELAY );
// 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, u32 timeout )
{
fd_set readfs;
struct timeval tv;
int retval;
u32 readbytes = 0;
while( readbytes < maxsize )
{
FD_ZERO( &readfs );
FD_SET( ( int )id, &readfs );
tv.tv_sec = timeout / 1000;
tv.tv_usec = ( timeout % 1000 ) * 1000;
retval = select( ( int )id + 1, &readfs, NULL, NULL, timeout == SER_INF_TIMEOUT ? NULL : &tv );
if( retval == -1 || retval == 0 )
break;
else
readbytes += ( u32 )read( id, dest + readbytes, maxsize - readbytes );
}
return readbytes;
}
// Read a single byte and return it (or -1 for error)
int ser_read_byte( ser_handler id, u32 timeout )
{
u8 data;
int res = ser_read( id, &data, 1, timeout );
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 );
tcdrain( ( int )id );
return res;
}
// Write a byte to the serial port
u32 ser_write_byte( ser_handler id, u8 data )
{
return ( u32 )write( id, &data, 1 );
}
// Perform 'select' on the specified handler(s), returning a single byte
// if it could be read (plus the object ID in the upper 8 bits) and -1
// otherwise
int ser_select_byte( ser_handler *pobjects, unsigned nobjects, int timeout )
{
int i, maxfd = -1;
fd_set readfs;
struct timeval tv;
int res = -1;
u8 data;
FD_ZERO( &readfs );
for( i = 0; i < nobjects; i ++ )
{
FD_SET( pobjects[ i ], &readfs );
if( pobjects[ i ] > maxfd )
maxfd = pobjects[ i ];
}
tv.tv_sec = timeout / 1000;
tv.tv_usec = ( timeout % 1000 ) * 1000;
res = select( maxfd + 1, &readfs, NULL, NULL, timeout == SER_INF_TIMEOUT ? NULL : &tv );
if( res <= 0 )
return -1;
res = -1;
for( i = 0; i < nobjects; i ++ )
if( FD_ISSET( pobjects[ i ], &readfs ) )
{
res = read( pobjects[ i ], &data, 1 );
if( res != 1 )
return -1;
res = data | ( i << 8 );
break;
}
return res;
}