1
0
mirror of https://github.com/elua/elua.git synced 2025-01-08 20:56:17 +08:00
elua/mux_src/main.c

378 lines
11 KiB
C
Raw Normal View History

// Service multiplexer
#include "net.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <ctype.h>
#include "config.h"
#include "log.h"
#include "type.h"
#include "rfs_serial.h"
#include "sermux.h"
#include "rfs.h"
#include "deskutils.h"
// ****************************************************************************
// Data structures and local variables
#define MODE_MUX 1
#define MODE_RFSMUX 2
#define HND_TRANSPORT_OFFSET 0
#define HND_FIRST_VOFFSET 1
#define RFS_PSEUDO_SELIDX 0xFF
// Send/receive/init function pointers
typedef u32 ( *p_recv_func )( u8 *p, u32 size );
typedef u32 ( *p_send_func )( const u8 *p, u32 size );
typedef int ( *p_init_func )( void );
// Serial thread buffer structure
typedef struct {
const char *pname;
ser_handler fd;
} SERVICE_DATA;
// Serial transport data structure
typedef struct {
ser_handler fd;
const char *pname;
long speed;
int flow;
} TRANSPORT_SER;
static SERVICE_DATA *services;
static unsigned vport_num;
static TRANSPORT_SER *transport_data;
static p_send_func transport_send;
static p_init_func transport_init;
static int service_id_in = -1, service_id_out = -1;
static ser_handler transport_hnd = SER_HANDLER_INVALID;
static int mux_mode;
static int verbose_mode;
static int rfs_service_id = -1, service_offset;
// ***************************************************************************
// Serial transport implementation
static u32 transport_ser_send( const u8 *p, u32 size )
{
TRANSPORT_SER *pser = ( TRANSPORT_SER* )transport_data;
return ser_write( pser->fd, p, size );
}
static int transport_ser_init()
{
TRANSPORT_SER *pser = ( TRANSPORT_SER* )transport_data;
if( ( pser->fd = ser_open( pser->pname ) ) == ( ser_handler )-1 )
{
log_err( "Unable to open %s\n", pser->pname );
return 0;
}
if( ser_setup( pser->fd, pser->speed, 8, SER_PARITY_NONE, SER_STOPBITS_1, pser->flow ) != SER_OK )
{
log_err( "Unable to setup serial port %s\n", pser->pname );
return 0;
}
transport_hnd = pser->fd;
while( ser_read_byte( pser->fd, SER_NO_TIMEOUT ) != -1 );
log_msg( "Running serial transport on port %s at %u baud (8N1)\n", pser->pname, ( unsigned )pser->speed );
return 1;
}
// ****************************************************************************
// Utility functions and helpers
static void transport_send_byte( u8 data )
{
transport_send( &data, 1 );
}
// Transport parser
static int parse_transport( const char* s )
{
const char *c, *c2;
static TRANSPORT_SER tser;
char *temp;
if( ( c = strchr( s, ',' ) ) == NULL )
{
log_err( "Invalid serial transport syntax.\n" );
return 0;
}
tser.pname = l_strndup( s, c - s );
if( ( c2 = strchr( c + 1, ',' ) ) == NULL )
{
log_err( "Invalid serial transport syntax.\n" );
return 0;
}
temp = l_strndup( c + 1, c2 - c - 1 );
if( secure_atoi( temp, &tser.speed ) == 0 )
{
log_err( "Invalid port speed\n" );
return 0;
}
free( temp );
if( !strcmp( c2 + 1, "none" ) )
tser.flow = SER_FLOW_NONE;
else if( !strcmp( c2 + 1, "rtscts" ) )
tser.flow = SER_FLOW_RTSCTS;
else
{
log_err( "Invalid flow control type.\n" );
return 0;
}
transport_data = &tser;
transport_send = transport_ser_send;
transport_init = transport_ser_init;
return 1;
}
// ****************************************************************************
// Program entry point
#define MODE_IDX 1
#define MAIN_TRANSPORT_IDX 2
#define FIRST_SERVICE_IDX 3
#define MIN_ARGC_COUNT 4
#ifdef _MSC_VER
#define strcasecmp _stricmp
int main( int argc, char *argv[] )
#else
int main( int argc, char **argv )
#endif
{
unsigned i;
SERVICE_DATA *tservice;
int c, prev_sent = -1;
int temp;
int got_esc = 0;
char* rfs_dir_name;
ser_handler *phandlers;
int selidx;
u16 rfs_size = 0;
u8 *rfs_ptr;
// Interpret arguments
setvbuf( stdout, NULL, _IONBF, 0 );
if( argc < MIN_ARGC_COUNT )
{
log_err( "Usage: %s <mode> <transport> <vcom1> [<vcom2>] ... [<vcomn>] [-v]\n", argv[ 0 ] );
log_err( " mode: \n" );
log_err( " 'mux': serial multiplexer mode\n" );
log_err( " 'rfsmux:<directory>': combined RFS and multiplexer mode.\n" );
log_err( " transport: '<port>,<baud>,<flow>' ('flow' specifies the flow control type and can be 'none' or 'rtscts').\n" );
log_err( " vcom1, ..., vcomn: multiplexer serial ports.\n" );
log_err( " Use '-v' for verbose output.\n" );
return 1;
}
// Check mode
if( !strcmp( argv[ MODE_IDX ], "mux" ) )
mux_mode = MODE_MUX;
else if( !strncmp( argv[ MODE_IDX ], "rfsmux:", strlen( "rfsmux:" ) ) )
{
rfs_dir_name = argv[ MODE_IDX ] + strlen( "rfsmux:" );
mux_mode = MODE_RFSMUX;
rfs_service_id = SERMUX_SERVICE_ID_FIRST;
service_offset = 1;
}
else
{
log_err( "Invalid mode.\n" );
return 1;
}
// Check verbose
i = argc - 1;
if( !strcasecmp( argv[ i ], "-v" ) )
{
i --;
log_init( LOG_ALL );
verbose_mode = 1;
}
else
log_init( LOG_NONE );
// Get number of virtual UARTs
if( ( vport_num = i - FIRST_SERVICE_IDX + 1 ) > SERMUX_SERVICE_MAX )
{
log_err( "Too many service ports, maximum is %d\n", SERMUX_SERVICE_MAX );
return 1;
}
// Parse transport data and initialize it
if( parse_transport( argv[ MAIN_TRANSPORT_IDX ] ) == 0 )
return 1;
if( transport_init() == 0 )
return 1;
// Open all the service ports
if( ( services = ( SERVICE_DATA* )malloc( sizeof( SERVICE_DATA ) * vport_num ) ) == NULL )
{
log_err( "Not enough memory\n" );
return 1;
}
if( ( phandlers = ( ser_handler* )malloc( sizeof( ser_handler ) * ( vport_num + 1 ) ) ) == NULL )
{
log_err( "Not enough memory\n" );
return 1;
}
phandlers[ HND_TRANSPORT_OFFSET ] = transport_hnd;
memset( services, 0, sizeof( SERVICE_DATA ) * vport_num );
for( i = 0; i < vport_num; i ++ )
{
tservice = services + i;
if( ( tservice->fd = ser_open( argv[ i + FIRST_SERVICE_IDX ] ) ) == SER_HANDLER_INVALID )
{
log_err( "Unable to open port %s\n", argv[ i + FIRST_SERVICE_IDX ] );
return 1;
}
if( ser_setup( tservice->fd, transport_data->speed, SER_DATABITS_8, SER_PARITY_NONE, SER_STOPBITS_1, SER_FLOW_NONE ) != SER_OK )
{
log_err( "Unable to setup serial port %s\n", argv[ i + FIRST_SERVICE_IDX ] );
return 1;
}
tservice->pname = argv[ i + FIRST_SERVICE_IDX ];
phandlers[ i + HND_FIRST_VOFFSET ] = tservice->fd;
}
// Setup RFS server in RFSMUX mode
if( mux_mode == MODE_RFSMUX )
{
char *args[] = { "dummy", "mem", rfs_dir_name, NULL };
if( verbose_mode )
args[ 3 ] = "-v";
if( rfs_init( verbose_mode ? 4 : 3, ( const char ** )args ) != 0 )
return 1;
}
log_msg( "Starting service multiplexer on %u port(s)\n", vport_num );
// Main service thread
while( 1 )
{
if( rfs_size > 0 ) // Response packet from RFS
{
c = *rfs_ptr ++;
rfs_size --;
selidx = RFS_PSEUDO_SELIDX;
}
else
{
if( ( c = ser_select_byte( phandlers, vport_num + 1, SER_INF_TIMEOUT ) ) == -1 )
{
log_err( "Error on select, aborting program\n" );
return 1;
}
selidx = c >> 8;
c = c & 0xFF;
}
//log_msg( "Got byte %d from idx %d\n", c, selidx );
if( selidx == HND_TRANSPORT_OFFSET ) // Got byte on transport interface
{
// Interpret byte
if( c != SERMUX_ESCAPE_CHAR )
{
if( c >= SERMUX_SERVICE_ID_FIRST && c <= SERMUX_SERVICE_ID_LAST )
{
log_msg( "Changed service_id_in from %d(%X) to %d(%X).\n", service_id_in, service_id_in, c, c );
service_id_in = c;
}
else if( c == SERMUX_FORCE_SID_CHAR )
{
if( prev_sent == -1 )
{
log_err( "Protocol error: got request to resend service ID when the last char sent was not set.\n" );
return 1;
}
log_msg( "Got request to resend service_id_out %d(%X).\n", service_id_out, service_id_out );
// Re-transmit the last data AND the service ID
transport_send_byte( service_id_out );
if( prev_sent & SERMUX_ESC_MASK )
transport_send_byte( SERMUX_ESCAPE_CHAR );
transport_send_byte( prev_sent & 0xFF );
prev_sent = -1;
}
else
{
if( got_esc )
{
// Got an escape last time, check the char now (with the 5th bit flipped)
c ^= SERMUX_ESCAPE_XOR_MASK;
if( c != SERMUX_ESCAPE_CHAR && c != SERMUX_FORCE_SID_CHAR && ( c < SERMUX_SERVICE_ID_FIRST || c > SERMUX_SERVICE_ID_LAST ) )
{
log_err( "Protocol error: invalid escape sequence\n" );
return 1;
}
got_esc = 0;
}
if( service_id_in == -1 )
{
transport_send_byte( SERMUX_FORCE_SID_CHAR );
log_msg( "Requested resend of service ID for byte %3d ('%c').\n", c, isprint( c ) ? c : ' ' );
}
else
{
if( service_id_in == rfs_service_id ) // this request is for the RFS server
{
rfs_mem_read_request_packet( c );
if( rfs_mem_has_response() ) // we have a response from the RFS server
{
rfs_mem_write_response( &rfs_size, &rfs_ptr );
rfs_mem_start_request(); // initialize the RFS server for a new request
}
}
else
{
//log_msg( "Sending byte %d to %s\n", c, services[ service_id_in - SERMUX_SERVICE_ID_FIRST - service_offset ].pname );
ser_write_byte( services[ service_id_in - SERMUX_SERVICE_ID_FIRST - service_offset ].fd, c );
}
}
}
}
else
got_esc = 1;
}
else
{
// No byte to read, there must be something to send
if( selidx == RFS_PSEUDO_SELIDX )
temp = SERMUX_SERVICE_ID_FIRST;
else
temp = SERMUX_SERVICE_ID_FIRST + selidx - HND_FIRST_VOFFSET + service_offset;
prev_sent = c;
// Send the service ID first if needed
if( temp != service_id_out )
{
log_msg( "Changed service_id_out from %d(%X) to %d(%X).\n", service_id_out, service_id_out, temp, temp );
transport_send_byte( temp );
}
// Then send the actual data byte, escaping it if needed
if( c == SERMUX_ESCAPE_CHAR || c == SERMUX_FORCE_SID_CHAR || ( c >= SERMUX_SERVICE_ID_FIRST && c <= SERMUX_SERVICE_ID_LAST ) )
{
transport_send_byte( SERMUX_ESCAPE_CHAR );
transport_send_byte( ( u8 )c ^ SERMUX_ESCAPE_XOR_MASK );
prev_sent = SERMUX_ESC_MASK | ( ( u8 )c ^ SERMUX_ESCAPE_XOR_MASK );
}
else
transport_send_byte( c );
service_id_out = temp;
}
}
return 0;
}