mirror of
https://github.com/elua/elua.git
synced 2025-01-25 01:02:54 +08:00
a0ecdc124b
Read from the UART file descriptor until no data can be read anymore (POSIX only). This is needed because sometimes the RFS server didn't read all the available data and signaled errors.
228 lines
5.1 KiB
C
228 lines
5.1 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;
|
|
|
|
// 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;
|
|
}
|
|
|