mirror of
https://github.com/elua/elua.git
synced 2025-01-08 20:56:17 +08:00
e823c8d3fc
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.
231 lines
5.2 KiB
C
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;
|
|
}
|
|
|