kopia lustrzana https://github.com/Hamlib/Hamlib
3508 wiersze
76 KiB
C
3508 wiersze
76 KiB
C
#ifdef HAVE_CONFIG_H
|
||
#include <config.h>
|
||
#endif
|
||
|
||
#if defined(WIN32) && !defined(HAVE_TERMIOS_H)
|
||
|
||
#undef DEBUG
|
||
#undef TRACE
|
||
|
||
#ifdef DEBUG
|
||
#define DEBUG_VERBOSE
|
||
#define DEBUG_ERRORS
|
||
#define report(a) fprintf(stderr,a)
|
||
#define report_warning(a) fprintf(stderr,a)
|
||
#define report_error(a) fprintf(stderr,a)
|
||
#else
|
||
#define report(a) do {} while (0)
|
||
#define report_warning(a) do {} while (0)
|
||
#define report_error(a) do {} while (0)
|
||
#endif /* DEBUG */
|
||
/*-------------------------------------------------------------------------
|
||
| rxtx is a native interface to serial ports in java.
|
||
| Copyright 1997-2002 by Trent Jarvi taj@www.linux.org.uk.
|
||
| Copyright 1997-2006 by Trent Jarvi taj@www.linux.org.uk.
|
||
|
|
||
| This library is free software; you can redistribute it and/or
|
||
| modify it under the terms of the GNU Lesser General Public
|
||
| License as published by the Free Software Foundation; either
|
||
| version 2.1 of the License, or (at your option) any later version.
|
||
|
|
||
| If you compile this program with cygwin32 tools this package falls
|
||
| under the GPL. See COPYING.CYGNUS for details.
|
||
|
|
||
| This library is distributed in the hope that it will be useful,
|
||
| but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
| Lesser General Public License for more details.
|
||
|
|
||
| You should have received a copy of the GNU Lesser General Public
|
||
| License along with this library; if not, write to the Free Software
|
||
| Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
|
||
| This file was taken from rxtx-2.1-7 and adapted for Hamlib.
|
||
--------------------------------------------------------------------------*/
|
||
#include <windows.h>
|
||
#include <stdio.h>
|
||
#include <errno.h>
|
||
#include <time.h>
|
||
#include <limits.h>
|
||
#include "win32termios.h"
|
||
|
||
/*
|
||
* odd malloc.h error with lcc compiler
|
||
* winsock has FIONREAD with lcc
|
||
*/
|
||
|
||
#ifdef __LCC__
|
||
# include <winsock.h>
|
||
#else
|
||
# include <malloc.h>
|
||
#endif /* __LCC__ */
|
||
|
||
#define SIGIO 0
|
||
|
||
int my_errno;
|
||
extern int errno;
|
||
struct termios_list
|
||
{
|
||
char filename[80];
|
||
int my_errno;
|
||
int interrupt;
|
||
int event_flag;
|
||
int tx_happened;
|
||
unsigned long *hComm;
|
||
struct termios *ttyset;
|
||
struct serial_struct *sstruct;
|
||
/* for DTR DSR */
|
||
unsigned char MSR;
|
||
struct async_struct *astruct;
|
||
struct serial_icounter_struct *sis;
|
||
int open_flags;
|
||
OVERLAPPED rol;
|
||
OVERLAPPED wol;
|
||
OVERLAPPED sol;
|
||
int fd;
|
||
struct termios_list *next;
|
||
struct termios_list *prev;
|
||
};
|
||
struct termios_list *first_tl = NULL;
|
||
|
||
/*----------------------------------------------------------
|
||
serial_test
|
||
|
||
accept: filename to test
|
||
perform:
|
||
return: 1 on success 0 on failure
|
||
exceptions:
|
||
win32api: CreateFile CloseHandle
|
||
comments: if the file opens it should be ok.
|
||
----------------------------------------------------------*/
|
||
int win32_serial_test( char * filename )
|
||
{
|
||
unsigned long *hcomm;
|
||
int ret;
|
||
hcomm = CreateFile( filename, GENERIC_READ |GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0 );
|
||
if ( hcomm == INVALID_HANDLE_VALUE )
|
||
{
|
||
if (GetLastError() == ERROR_ACCESS_DENIED)
|
||
{
|
||
ret = 1;
|
||
}
|
||
else
|
||
{
|
||
ret = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ret = 1;
|
||
}
|
||
CloseHandle( hcomm );
|
||
return(ret);
|
||
}
|
||
|
||
void termios_setflags( int fd, int termios_flags[] )
|
||
{
|
||
struct termios_list *index = find_port( fd );
|
||
int i, result;
|
||
int windows_flags[11] = { 0, EV_RXCHAR, EV_TXEMPTY, EV_CTS, EV_DSR,
|
||
EV_RING|0x2000, EV_RLSD, EV_ERR,
|
||
EV_ERR, EV_ERR, EV_BREAK
|
||
};
|
||
if( !index )
|
||
{
|
||
LEAVE( "termios_setflags" );
|
||
return;
|
||
}
|
||
index->event_flag = 0;
|
||
for(i=0;i<11;i++)
|
||
if( termios_flags[i] )
|
||
index->event_flag |= windows_flags[i];
|
||
result = SetCommMask( index->hComm, index->event_flag );
|
||
/*
|
||
This is rank. 0x2000 was used to detect the trailing edge of ring.
|
||
The leading edge is detedted by EV_RING.
|
||
|
||
The trailing edge is reliable. The leading edge is not.
|
||
Softie no longer allows the trailing edge to be detected in NTsp2
|
||
and beyond.
|
||
|
||
So... Try the reliable option above and if it fails, use the less
|
||
reliable means.
|
||
|
||
The screams for a giveio solution that bypasses the kernel.
|
||
*/
|
||
if( index->event_flag & 0x2000 && result == 0 )
|
||
{
|
||
index->event_flag &= ~0x2000;
|
||
SetCommMask( index->hComm, index->event_flag );
|
||
}
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
get_fd()
|
||
|
||
accept: filename
|
||
perform: find the file descriptor associated with the filename
|
||
return: fd
|
||
exceptions:
|
||
win32api: None
|
||
comments: This is not currently called by anything
|
||
----------------------------------------------------------*/
|
||
|
||
int get_fd( char *filename )
|
||
{
|
||
struct termios_list *index = first_tl;
|
||
|
||
ENTER( "get_fd" );
|
||
if( !index )
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
while( strcmp( index->filename, filename ) )
|
||
{
|
||
index = index->next;
|
||
if( !index->next )
|
||
return( -1 );
|
||
}
|
||
LEAVE( "get_fd" );
|
||
return( index->fd );
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
get_filename()
|
||
|
||
accept: file descriptor
|
||
perform: find the filename associated with the file descriptor
|
||
return: the filename associated with the fd
|
||
exceptions: None
|
||
win32api: None
|
||
comments: This is not currently called by anything
|
||
----------------------------------------------------------*/
|
||
|
||
char *get_filename( int fd )
|
||
{
|
||
struct termios_list *index = first_tl;
|
||
|
||
ENTER( "get_filename" );
|
||
if( !index )
|
||
return( "bad" );
|
||
while( index->fd != fd )
|
||
{
|
||
if( index->next == NULL )
|
||
return( "bad" );
|
||
index = index->next;
|
||
}
|
||
LEAVE( "get_filename" );
|
||
return( index->filename );
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
dump_termios_list()
|
||
|
||
accept: string to print out.
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments: used only for debugging eg serial_close()
|
||
----------------------------------------------------------*/
|
||
|
||
void dump_termios_list( char *foo )
|
||
{
|
||
#ifdef DEBUG
|
||
struct termios_list *index = first_tl;
|
||
printf( "============== %s start ===============\n", foo );
|
||
if ( index )
|
||
{
|
||
printf( "%i filename | %s\n", index->fd, index->filename );
|
||
}
|
||
/*
|
||
if ( index->next )
|
||
{
|
||
printf( "%i filename | %s\n", index->fd, index->filename );
|
||
}
|
||
*/
|
||
printf( "============== %s end ===============\n", foo );
|
||
#endif
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
set_errno()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments: FIXME
|
||
----------------------------------------------------------*/
|
||
|
||
void set_errno( int error )
|
||
{
|
||
my_errno = error;
|
||
}
|
||
|
||
#if 0
|
||
/*----------------------------------------------------------
|
||
usleep()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: Sleep()
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
void usleep( unsigned long usec )
|
||
{
|
||
Sleep( usec/1000 );
|
||
}
|
||
#endif
|
||
|
||
/*----------------------------------------------------------
|
||
CBR_toB()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int CBR_to_B( int Baud )
|
||
{
|
||
ENTER( "CBR_to_B" );
|
||
switch ( Baud )
|
||
{
|
||
|
||
case 0: return( B0 );
|
||
case 50: return( B50 );
|
||
case 75: return( B75 );
|
||
case CBR_110: return( B110 );
|
||
case 134: return( B134 );
|
||
case 150: return( B150 );
|
||
case 200: return( B200 );
|
||
case CBR_300: return( B300 );
|
||
case CBR_600: return( B600 );
|
||
case CBR_1200: return( B1200 );
|
||
case 1800: return( B1800 );
|
||
case CBR_2400: return( B2400 );
|
||
case CBR_4800: return( B4800 );
|
||
case CBR_9600: return( B9600 );
|
||
case CBR_14400: return( B14400 );
|
||
case CBR_19200: return( B19200 );
|
||
case CBR_28800: return( B28800 );
|
||
case CBR_38400: return( B38400 );
|
||
case CBR_57600: return( B57600 );
|
||
case CBR_115200: return( B115200 );
|
||
case CBR_128000: return( B128000 );
|
||
case CBR_230400: return( B230400 );
|
||
case CBR_256000: return( B256000 );
|
||
case CBR_460800: return( B460800 );
|
||
case CBR_500000: return( B500000 );
|
||
case CBR_576000: return( B576000 );
|
||
case CBR_921600: return( B921600 );
|
||
case CBR_1000000: return( B1000000 );
|
||
case CBR_1152000: return( B1152000 );
|
||
case CBR_1500000: return( B1500000 );
|
||
case CBR_2000000: return( B2000000 );
|
||
case CBR_2500000: return( B2500000 );
|
||
case CBR_3000000: return( B3000000 );
|
||
case CBR_3500000: return( B3500000 );
|
||
case CBR_4000000: return( B4000000 );
|
||
default:
|
||
set_errno(EINVAL );
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
B_to_CBR()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api:
|
||
comments: None
|
||
----------------------------------------------------------*/
|
||
|
||
int B_to_CBR( int Baud )
|
||
{
|
||
int ret;
|
||
ENTER( "B_to_CBR" );
|
||
switch ( Baud )
|
||
{
|
||
case 0: ret = 0; break;
|
||
case B50: ret = 50; break;
|
||
case B75: ret = 75; break;
|
||
case B110: ret = CBR_110; break;
|
||
case B134: ret = 134; break;
|
||
case B150: ret = 150; break;
|
||
case B200: ret = 200; break;
|
||
case B300: ret = CBR_300; break;
|
||
case B600: ret = CBR_600; break;
|
||
case B1200: ret = CBR_1200; break;
|
||
case B1800: ret = 1800; break;
|
||
case B2400: ret = CBR_2400; break;
|
||
case B4800: ret = CBR_4800; break;
|
||
case B9600: ret = CBR_9600; break;
|
||
case B14400: ret = CBR_14400; break;
|
||
case B19200: ret = CBR_19200; break;
|
||
case B38400: ret = CBR_38400; break;
|
||
case B57600: ret = CBR_57600; break;
|
||
case B115200: ret = CBR_115200; break;
|
||
case B128000: ret = CBR_128000; break;
|
||
case B256000: ret = CBR_256000; break;
|
||
case B230400: ret = CBR_230400; break;
|
||
case B460800: ret = CBR_460800; break;
|
||
case B500000: ret = CBR_500000; break;
|
||
case B576000: ret = CBR_576000; break;
|
||
case B921600: ret = CBR_921600; break;
|
||
case B1000000: ret = CBR_1000000; break;
|
||
case B1152000: ret = CBR_1152000; break;
|
||
case B1500000: ret = CBR_1500000; break;
|
||
case B2000000: ret = CBR_2000000; break;
|
||
case B2500000: ret = CBR_2500000; break;
|
||
case B3000000: ret = CBR_3000000; break;
|
||
case B3500000: ret = CBR_3500000; break;
|
||
case B4000000: ret = CBR_4000000; break;
|
||
|
||
default:
|
||
fprintf( stderr, "B_to_CBR: invalid baudrate: %#o\n",
|
||
Baud );
|
||
set_errno( EINVAL );
|
||
return -1;
|
||
}
|
||
LEAVE( "B_to_CBR" );
|
||
return ret;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
bytesize_to_termios()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int bytesize_to_termios( int ByteSize )
|
||
{
|
||
ENTER( "bytesize_to_termios" );
|
||
switch ( ByteSize )
|
||
{
|
||
case 5: return( CS5 );
|
||
case 6: return( CS6 );
|
||
case 7: return( CS7 );
|
||
case 8:
|
||
default: return( CS8 );
|
||
}
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
termios_to_bytesize()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int termios_to_bytesize( int cflag )
|
||
{
|
||
ENTER( "termios_to_bytesize" );
|
||
switch ( cflag & CSIZE )
|
||
{
|
||
case CS5: return( 5 );
|
||
case CS6: return( 6 );
|
||
case CS7: return( 7 );
|
||
case CS8:
|
||
default: return( 8 );
|
||
}
|
||
}
|
||
|
||
#if 0
|
||
/*----------------------------------------------------------
|
||
get_dos_port()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
const char *get_dos_port( char const *name )
|
||
{
|
||
ENTER( "get_dos_port" );
|
||
if ( !strcmp( name, "/dev/cua0" ) ) return( "COM1" );
|
||
if ( !strcmp( name, "/dev/cua1" ) ) return( "COM2" );
|
||
if ( !strcmp( name, "/dev/cua2" ) ) return( "COM3" );
|
||
if ( !strcmp( name, "/dev/cua3" ) ) return( "COM4" );
|
||
LEAVE( "get_dos_port" );
|
||
return( ( const char * ) name );
|
||
}
|
||
#endif
|
||
|
||
/*----------------------------------------------------------
|
||
ClearErrors()
|
||
|
||
accept:
|
||
perform: keep track of errors for the eventLoop() (SerialImp.c)
|
||
return: the return value of ClearCommError()
|
||
exceptions:
|
||
win32api: ClearCommError()
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int ClearErrors( struct termios_list *index, COMSTAT *Stat )
|
||
{
|
||
unsigned long ErrCode;
|
||
int ret;
|
||
|
||
ret = ClearCommError( index->hComm, &ErrCode, Stat );
|
||
if ( ret == 0 )
|
||
{
|
||
YACK();
|
||
return( ret );
|
||
}
|
||
#ifdef DEBUG_ERRORS
|
||
if ( ErrCode )
|
||
{
|
||
printf("%i frame %i %i overrun %i %i parity %u %i brk %i %i\n",
|
||
(int) ErrCode,
|
||
(int) ErrCode & CE_FRAME,
|
||
index->sis->frame,
|
||
(int) (ErrCode & CE_OVERRUN) | ( ErrCode & CE_RXOVER ),
|
||
index->sis->overrun,
|
||
(int) ErrCode & CE_RXPARITY,
|
||
index->sis->parity,
|
||
(int) ErrCode & CE_BREAK,
|
||
index->sis->brk
|
||
);
|
||
}
|
||
#endif /* DEBUG_ERRORS */
|
||
if( ErrCode & CE_FRAME )
|
||
{
|
||
index->sis->frame++;
|
||
ErrCode &= ~CE_FRAME;
|
||
}
|
||
#ifdef LIFE_IS_GOOD
|
||
FIXME OVERRUN is spewing
|
||
if( ErrCode & CE_OVERRUN )
|
||
{
|
||
index->sis->overrun++;
|
||
ErrCode &= ~CE_OVERRUN;
|
||
}
|
||
/* should this be here? */
|
||
else if( ErrCode & CE_RXOVER )
|
||
{
|
||
index->sis->overrun++;
|
||
ErrCode &= ~CE_OVERRUN;
|
||
}
|
||
#endif /* LIFE_IS_GOOD */
|
||
if( ErrCode & CE_RXPARITY )
|
||
{
|
||
index->sis->parity++;
|
||
ErrCode &= ~CE_RXPARITY;
|
||
}
|
||
if( ErrCode & CE_BREAK )
|
||
{
|
||
index->sis->brk++;
|
||
ErrCode &= ~CE_BREAK;
|
||
}
|
||
return( ret );
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
FillDCB()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: GetCommState(), SetCommState(), SetCommTimeouts()
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
BOOL FillDCB( DCB *dcb, unsigned long *hCommPort, COMMTIMEOUTS Timeout )
|
||
{
|
||
|
||
ENTER( "FillDCB" );
|
||
dcb->DCBlength = sizeof( dcb );
|
||
if ( !GetCommState( hCommPort, dcb ) )
|
||
{
|
||
report( "GetCommState\n" );
|
||
return( -1 );
|
||
}
|
||
dcb->BaudRate = CBR_9600 ;
|
||
dcb->ByteSize = 8;
|
||
dcb->Parity = NOPARITY;
|
||
dcb->StopBits = ONESTOPBIT;
|
||
dcb->fDtrControl = DTR_CONTROL_ENABLE;
|
||
dcb->fRtsControl = RTS_CONTROL_ENABLE;
|
||
dcb->fOutxCtsFlow = FALSE;
|
||
dcb->fOutxDsrFlow = FALSE;
|
||
dcb->fDsrSensitivity = FALSE;
|
||
dcb->fOutX = FALSE;
|
||
dcb->fInX = FALSE;
|
||
dcb->fTXContinueOnXoff = FALSE;
|
||
dcb->XonChar = 0x11;
|
||
dcb->XoffChar = 0x13;
|
||
dcb->XonLim = 0;
|
||
dcb->XoffLim = 0;
|
||
dcb->fParity = TRUE;
|
||
if ( EV_BREAK|EV_CTS|EV_DSR|EV_ERR|EV_RING|( EV_RLSD & EV_RXFLAG ) )
|
||
dcb->EvtChar = '\n';
|
||
else dcb->EvtChar = '\0';
|
||
if ( !SetCommState( hCommPort, dcb ) )
|
||
{
|
||
report( "SetCommState\n" );
|
||
YACK();
|
||
return( -1 );
|
||
}
|
||
if ( !SetCommTimeouts( hCommPort, &Timeout ) )
|
||
{
|
||
YACK();
|
||
report( "SetCommTimeouts\n" );
|
||
return( -1 );
|
||
}
|
||
LEAVE( "FillDCB" );
|
||
return ( TRUE ) ;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
serial_close()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: SetCommMask(), CloseHandle()
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int win32_serial_close( int fd )
|
||
{
|
||
struct termios_list *index;
|
||
/* char message[80]; */
|
||
|
||
ENTER( "serial_close" );
|
||
if( !first_tl || !first_tl->hComm )
|
||
{
|
||
report( "gotit!" );
|
||
return( 0 );
|
||
}
|
||
index = find_port( fd );
|
||
if ( !index )
|
||
{
|
||
LEAVE( "serial_close" );
|
||
return -1;
|
||
}
|
||
|
||
/* WaitForSingleObject( index->wol.hEvent, INFINITE ); */
|
||
/*
|
||
if ( index->hComm != INVALID_HANDLE_VALUE )
|
||
{
|
||
if ( !SetCommMask( index->hComm, EV_RXCHAR ) )
|
||
{
|
||
YACK();
|
||
report( "eventLoop hung\n" );
|
||
}
|
||
CloseHandle( index->hComm );
|
||
}
|
||
else
|
||
{
|
||
sprintf( message, "serial_ close(): Invalid Port Reference for %s\n",
|
||
index->filename );
|
||
report( message );
|
||
}
|
||
*/
|
||
if ( index->next && index->prev )
|
||
{
|
||
index->next->prev = index->prev;
|
||
index->prev->next = index->next;
|
||
}
|
||
else if ( index->prev )
|
||
{
|
||
index->prev->next = NULL;
|
||
}
|
||
else if ( index->next )
|
||
{
|
||
index->next->prev = NULL;
|
||
first_tl = index->next;
|
||
}
|
||
else
|
||
first_tl = NULL;
|
||
if ( index )
|
||
{
|
||
if ( index->rol.hEvent ) CloseHandle( index->rol.hEvent );
|
||
if ( index->wol.hEvent ) CloseHandle( index->wol.hEvent );
|
||
if ( index->sol.hEvent ) CloseHandle( index->sol.hEvent );
|
||
if ( index->hComm ) CloseHandle( index->hComm );
|
||
if ( index->ttyset ) free( index->ttyset );
|
||
if ( index->astruct ) free( index->astruct );
|
||
if ( index->sstruct ) free( index->sstruct );
|
||
if ( index->sis ) free( index->sis );
|
||
/* had problems with strdup
|
||
if ( index->filename ) free( index->filename );
|
||
*/
|
||
free( index );
|
||
}
|
||
LEAVE( "serial_close" );
|
||
return 0;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
cfmakeraw()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
void cfmakeraw( struct termios *s_termios )
|
||
{
|
||
ENTER( "cfmakeraw" );
|
||
s_termios->c_iflag &= ~( IGNBRK|BRKINT|PARMRK|ISTRIP
|
||
|INLCR|IGNCR|ICRNL|IXON );
|
||
s_termios->c_oflag &= ~OPOST;
|
||
s_termios->c_lflag &= ~( ECHO|ECHONL|ICANON|ISIG|IEXTEN );
|
||
s_termios->c_cflag &= ~( CSIZE|PARENB );
|
||
s_termios->c_cflag |= CS8;
|
||
LEAVE( "cfmakeraw" );
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
init_termios()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api:
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
BOOL init_serial_struct( struct serial_struct *sstruct )
|
||
{
|
||
ENTER( "init_serial_struct" );
|
||
|
||
/*
|
||
FIXME
|
||
|
||
This needs to use inb() to read the actual baud_base
|
||
and divisor from the UART registers. Question is how
|
||
far do we take this?
|
||
|
||
*/
|
||
|
||
sstruct->custom_divisor = 0;
|
||
sstruct->baud_base = 115200;
|
||
|
||
/* not currently used check values before using */
|
||
|
||
/* unsigned short */
|
||
|
||
sstruct->close_delay = 0;
|
||
sstruct->closing_wait = 0;
|
||
sstruct->iomem_reg_shift = 0;
|
||
|
||
/* int */
|
||
|
||
sstruct->type = 0;
|
||
sstruct->line = 0;
|
||
sstruct->irq = 0;
|
||
sstruct->flags = 0;
|
||
sstruct->xmit_fifo_size = 0;
|
||
sstruct->hub6 = 0;
|
||
|
||
/* unsigned int */
|
||
|
||
sstruct->port = 0;
|
||
sstruct->port_high = 0;
|
||
|
||
/* char */
|
||
|
||
sstruct->io_type = 0;
|
||
|
||
/* unsigned char * */
|
||
|
||
sstruct->iomem_base = NULL;
|
||
|
||
LEAVE( "init_serial_struct" );
|
||
return TRUE;
|
||
|
||
}
|
||
/*----------------------------------------------------------
|
||
init_termios()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api:
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
BOOL init_termios(struct termios *ttyset )
|
||
{
|
||
ENTER( "init_termios" );
|
||
if ( !ttyset )
|
||
return FALSE;
|
||
memset( ttyset, 0, sizeof( struct termios ) );
|
||
cfsetospeed( ttyset, B9600 );
|
||
cfmakeraw( ttyset );
|
||
ttyset->c_cc[VINTR] = 0x03; /* 0: C-c */
|
||
ttyset->c_cc[VQUIT] = 0x1c; /* 1: C-\ */
|
||
ttyset->c_cc[VERASE] = 0x7f; /* 2: <del> */
|
||
ttyset->c_cc[VKILL] = 0x15; /* 3: C-u */
|
||
ttyset->c_cc[VEOF] = 0x04; /* 4: C-d */
|
||
ttyset->c_cc[VTIME] = 0; /* 5: read timeout */
|
||
ttyset->c_cc[VMIN] = 1; /* 6: read returns after this
|
||
many bytes */
|
||
ttyset->c_cc[VSUSP] = 0x1a; /* 10: C-z */
|
||
ttyset->c_cc[VEOL] = '\r'; /* 11: */
|
||
ttyset->c_cc[VREPRINT] = 0x12; /* 12: C-r */
|
||
/*
|
||
ttyset->c_cc[VDISCARD] = 0x; 13: IEXTEN only
|
||
*/
|
||
ttyset->c_cc[VWERASE] = 0x17; /* 14: C-w */
|
||
ttyset->c_cc[VLNEXT] = 0x16; /* 15: C-w */
|
||
ttyset->c_cc[VEOL2] = '\n'; /* 16: */
|
||
LEAVE( "init_termios" );
|
||
return TRUE;
|
||
/* default VTIME = 0, VMIN = 1: read blocks forever until one byte */
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
port_opened()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int port_opened( const char *filename )
|
||
{
|
||
struct termios_list *index = first_tl;
|
||
|
||
ENTER( "port_opened" );
|
||
if ( ! index )
|
||
return 0;
|
||
if( !strcmp( index->filename, filename ) )
|
||
return index->fd;
|
||
while ( index->next )
|
||
{
|
||
index = index->next;
|
||
if( !strcmp( index->filename, filename ) )
|
||
return index->fd;
|
||
}
|
||
LEAVE( "port_opened" );
|
||
return 0;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
open_port()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: CreateFile(), SetupComm(), CreateEvent()
|
||
comments:
|
||
FILE_FLAG_OVERLAPPED allows one to break out the select()
|
||
so RXTXPort.close() does not hang.
|
||
|
||
The setDTR() and setDSR() are the functions that noticed
|
||
to be blocked in the java close. Basically ioctl(TIOCM[GS]ET)
|
||
are where it hangs.
|
||
|
||
FILE_FLAG_OVERLAPPED also means we need to create valid OVERLAPPED
|
||
structure in Serial_select.
|
||
----------------------------------------------------------*/
|
||
|
||
int open_port( struct termios_list *port )
|
||
{
|
||
ENTER( "open_port" );
|
||
port->hComm = CreateFile( port->filename,
|
||
GENERIC_READ | GENERIC_WRITE,
|
||
0,
|
||
0,
|
||
OPEN_EXISTING,
|
||
FILE_FLAG_OVERLAPPED,
|
||
0
|
||
);
|
||
if ( port->hComm == INVALID_HANDLE_VALUE )
|
||
{
|
||
YACK();
|
||
set_errno( EINVAL );
|
||
/*
|
||
printf( "serial_open failed %s\n", port->filename );
|
||
*/
|
||
return -1;
|
||
}
|
||
if( !SetupComm( port->hComm, 2048, 1024 ) )
|
||
{
|
||
YACK();
|
||
return -1;
|
||
}
|
||
|
||
memset( &port->rol, 0, sizeof( OVERLAPPED ) );
|
||
memset( &port->wol, 0, sizeof( OVERLAPPED ) );
|
||
memset( &port->sol, 0, sizeof( OVERLAPPED ) );
|
||
|
||
port->rol.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
|
||
|
||
if ( !port->rol.hEvent )
|
||
{
|
||
YACK();
|
||
report( "Could not create read overlapped\n" );
|
||
goto fail;
|
||
}
|
||
|
||
port->sol.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
|
||
|
||
if ( !port->sol.hEvent )
|
||
{
|
||
YACK();
|
||
report( "Could not create select overlapped\n" );
|
||
goto fail;
|
||
}
|
||
port->wol.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
|
||
|
||
if ( !port->wol.hEvent )
|
||
{
|
||
YACK();
|
||
report( "Could not create write overlapped\n" );
|
||
goto fail;
|
||
}
|
||
LEAVE( "open_port" );
|
||
return( 0 );
|
||
fail:
|
||
return( -1 );
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
termios_list()
|
||
|
||
accept: fd which is a fake # for the port assigned when the port
|
||
is opened
|
||
perform: walk through a double linked list to see if the given
|
||
fd is in any of the termios_list members.
|
||
return: the termios_list if it is found.
|
||
NULL if no matches are found.
|
||
exceptions: None
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
struct termios_list *find_port( int fd )
|
||
{
|
||
|
||
char message[80];
|
||
struct termios_list *index = first_tl;
|
||
|
||
ENTER( "find_port" );
|
||
if ( fd <= 0 || !first_tl ) goto fail;
|
||
|
||
while( index->fd )
|
||
{
|
||
if ( index->fd == fd )
|
||
{
|
||
LEAVE( "find_port" );
|
||
return index;
|
||
}
|
||
if ( !index->next )
|
||
break;
|
||
index = index->next;
|
||
}
|
||
fail:
|
||
sprintf( message, "No info known about the port. %i\n", fd );
|
||
report( message );
|
||
set_errno( EBADF );
|
||
LEAVE( "find_port" );
|
||
return NULL;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
get_free_fd()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int get_free_fd()
|
||
{
|
||
int next, last;
|
||
struct termios_list *index = first_tl;
|
||
|
||
ENTER( "get_free_fd" );
|
||
if ( !index )
|
||
{
|
||
return( 1 );
|
||
}
|
||
if ( !index->fd )
|
||
{
|
||
report( "!index->fd\n" );
|
||
return( 1 );
|
||
}
|
||
if ( index->fd > 1)
|
||
{
|
||
first_tl = index;
|
||
return ( 1 );
|
||
}
|
||
|
||
last = index->fd;
|
||
|
||
while( index->next )
|
||
{
|
||
next = index->next->fd;
|
||
if ( next != last + 1 )
|
||
{
|
||
return( last + 1 );
|
||
|
||
}
|
||
index = index->next;
|
||
last = next;
|
||
}
|
||
LEAVE( "get_free_fd" );
|
||
return( index->fd + 1 );
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
add_port()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
struct termios_list *add_port( const char *filename )
|
||
{
|
||
struct termios_list *index = first_tl;
|
||
struct termios_list *port;
|
||
|
||
ENTER( "add_port" );
|
||
|
||
port = malloc( sizeof( struct termios_list ) );
|
||
if( !port )
|
||
goto fail;
|
||
memset( port, 0, sizeof( struct termios_list ) );
|
||
|
||
port->ttyset = malloc( sizeof( struct termios ) );
|
||
if( ! port->ttyset )
|
||
goto fail;
|
||
memset( port->ttyset, 0, sizeof( struct termios ) );
|
||
|
||
port->sstruct = malloc( sizeof( struct serial_struct ) );
|
||
if( ! port->sstruct )
|
||
goto fail;
|
||
memset( port->sstruct, 0, sizeof( struct serial_struct ) );
|
||
port->sis = malloc( sizeof( struct serial_icounter_struct ) );
|
||
if( ! port->sis )
|
||
goto fail;
|
||
memset( port->sis, 0, sizeof( struct serial_icounter_struct ) );
|
||
|
||
/* FIXME the async_struct is being defined by mingw32 headers?
|
||
port->astruct = malloc( sizeof( struct async_struct ) );
|
||
if( ! port->astruct )
|
||
goto fail;
|
||
memset( port->astruct, 0, sizeof( struct async_struct ) );
|
||
*/
|
||
port->MSR = 0;
|
||
|
||
strcpy(port->filename, filename );
|
||
/* didnt free well? strdup( filename ); */
|
||
if( ! port->filename )
|
||
goto fail;
|
||
|
||
port->fd = get_free_fd();
|
||
|
||
|
||
if ( !first_tl )
|
||
{
|
||
port->prev = NULL;
|
||
first_tl = port;
|
||
}
|
||
else
|
||
{
|
||
while ( index->next )
|
||
index = index->next;
|
||
if ( port == first_tl )
|
||
{
|
||
port->prev = NULL;
|
||
port->next = first_tl;
|
||
first_tl->prev = port;
|
||
first_tl = port;
|
||
}
|
||
else
|
||
{
|
||
port->prev = index;
|
||
index->next = port;
|
||
}
|
||
}
|
||
port->next = NULL;
|
||
LEAVE( "add_port" );
|
||
return port;
|
||
|
||
fail:
|
||
report( "add_port: Out Of Memory\n");
|
||
if ( port->ttyset ) free( port->ttyset );
|
||
if ( port->astruct ) free( port->astruct );
|
||
if ( port->sstruct ) free( port->sstruct );
|
||
if ( port->sis ) free( port->sis );
|
||
/* had problems with strdup
|
||
if ( port->filename ) free( port->filename );
|
||
*/
|
||
if ( port ) free( port );
|
||
return port;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
check_port_capabilities()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: GetCommProperties(), GetCommState()
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int check_port_capabilities( struct termios_list *index )
|
||
{
|
||
COMMPROP cp;
|
||
DCB dcb;
|
||
char message[80];
|
||
|
||
ENTER( "check_port_capabilities" );
|
||
/* check for capabilities */
|
||
GetCommProperties( index->hComm, &cp );
|
||
if ( !( cp.dwProvCapabilities & PCF_DTRDSR ) )
|
||
{
|
||
sprintf( message,
|
||
"%s: no DTR & DSR support\n", index->filename );
|
||
report( message );
|
||
}
|
||
if ( !( cp.dwProvCapabilities & PCF_RLSD ) )
|
||
{
|
||
sprintf( message, "%s: no carrier detect (RLSD) support\n",
|
||
index->filename );
|
||
report( message );
|
||
}
|
||
if ( !( cp.dwProvCapabilities & PCF_RTSCTS ) )
|
||
{
|
||
sprintf( message,
|
||
"%s: no RTS & CTS support\n", index->filename );
|
||
report( message );
|
||
}
|
||
if ( !( cp.dwProvCapabilities & PCF_TOTALTIMEOUTS ) )
|
||
{
|
||
sprintf( message, "%s: no timeout support\n", index->filename );
|
||
report( message );
|
||
}
|
||
if ( !GetCommState( index->hComm, &dcb ) )
|
||
{
|
||
YACK();
|
||
report( "GetCommState\n" );
|
||
return -1;
|
||
}
|
||
LEAVE( "check_port_capabilities" );
|
||
return 0;
|
||
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
serial_open()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int win32_serial_open( const char *filename, int flags, ... )
|
||
{
|
||
struct termios_list *index;
|
||
char message[80];
|
||
|
||
ENTER( "serial_open" );
|
||
if ( port_opened( filename ) )
|
||
{
|
||
report( "Port is already opened\n" );
|
||
return( -1 );
|
||
}
|
||
index = add_port( filename );
|
||
if( !index )
|
||
{
|
||
report( "serial_open !index\n" );
|
||
return( -1 );
|
||
}
|
||
|
||
index->interrupt = 0;
|
||
index->tx_happened = 0;
|
||
if ( open_port( index ) )
|
||
{
|
||
sprintf( message, "serial_open(): Invalid Port Reference for %s\n",
|
||
filename );
|
||
report( message );
|
||
win32_serial_close( index->fd );
|
||
return -1;
|
||
}
|
||
|
||
if( check_port_capabilities( index ) )
|
||
{
|
||
report( "check_port_capabilites!" );
|
||
win32_serial_close( index->fd );
|
||
return -1;
|
||
}
|
||
|
||
init_termios( index->ttyset );
|
||
init_serial_struct( index->sstruct );
|
||
|
||
/* set default condition */
|
||
tcsetattr( index->fd, 0, index->ttyset );
|
||
|
||
/* if opened with non-blocking, then operating non-blocking */
|
||
if ( flags & O_NONBLOCK )
|
||
index->open_flags = O_NONBLOCK;
|
||
else
|
||
index->open_flags = 0;
|
||
|
||
|
||
if( !first_tl->hComm )
|
||
{
|
||
sprintf( message, "open(): Invalid Port Reference for %s\n",
|
||
index->filename );
|
||
report( message );
|
||
}
|
||
if ( first_tl->hComm == INVALID_HANDLE_VALUE )
|
||
report( "serial_open: test\n" );
|
||
LEAVE( "serial_open" );
|
||
return( index->fd );
|
||
}
|
||
|
||
|
||
/*----------------------------------------------------------
|
||
serial_write()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: WriteFile(), GetLastError(),
|
||
WaitForSingleObject(), GetOverlappedResult(),
|
||
FlushFileBuffers(), Sleep()
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int win32_serial_write( int fd, const char *Str, int length )
|
||
{
|
||
unsigned long nBytes;
|
||
struct termios_list *index;
|
||
/* COMSTAT Stat; */
|
||
int old_flag;
|
||
|
||
ENTER( "serial_write" );
|
||
|
||
if ( fd <= 0 )
|
||
{
|
||
return 0;
|
||
}
|
||
index = find_port( fd );
|
||
if ( !index )
|
||
{
|
||
LEAVE( "serial_write");
|
||
return -1;
|
||
}
|
||
old_flag = index->event_flag;
|
||
/*
|
||
index->event_flag &= ~EV_TXEMPTY;
|
||
SetCommMask( index->hComm, index->event_flag );
|
||
index->tx_happened = 1;
|
||
*/
|
||
index->wol.Offset = index->wol.OffsetHigh = 0;
|
||
ResetEvent( index->wol.hEvent );
|
||
if ( !WriteFile( index->hComm, Str, length, &nBytes, &index->wol ) )
|
||
{
|
||
WaitForSingleObject( index->wol.hEvent,100 );
|
||
if ( GetLastError() != ERROR_IO_PENDING )
|
||
{
|
||
/* ClearErrors( index, &Stat ); */
|
||
report( "serial_write error\n" );
|
||
/* report("Condition 1 Detected in write()\n"); */
|
||
YACK();
|
||
errno = EIO;
|
||
nBytes=-1;
|
||
goto end;
|
||
}
|
||
/* This is breaking on Win2K, WinXP for some reason */
|
||
else while( !GetOverlappedResult( index->hComm, &index->wol,
|
||
&nBytes, TRUE ) )
|
||
{
|
||
if ( GetLastError() != ERROR_IO_INCOMPLETE )
|
||
{
|
||
/* report("Condition 2 Detected in write()\n"); */
|
||
YACK();
|
||
errno = EIO;
|
||
nBytes = -1;
|
||
goto end;
|
||
/* ClearErrors( index, &Stat ); */
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Write finished synchronously. That is ok!
|
||
* I have seen this with USB to Serial
|
||
* devices like TI's.
|
||
*/
|
||
}
|
||
end:
|
||
/* FlushFileBuffers( index->hComm ); */
|
||
index->event_flag |= EV_TXEMPTY;
|
||
/* ClearErrors( index, &Stat ); */
|
||
SetCommMask( index->hComm, index->event_flag );
|
||
/* ClearErrors( index, &Stat ); */
|
||
index->event_flag = old_flag;
|
||
index->tx_happened = 1;
|
||
LEAVE( "serial_write" );
|
||
return nBytes;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
serial_read()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: ReadFile(), GetLastError(), WaitForSingleObject()
|
||
GetOverLappedResult()
|
||
comments: If setting errno make sure not to use EWOULDBLOCK
|
||
In that case use EAGAIN. See SerialImp.c:testRead()
|
||
----------------------------------------------------------*/
|
||
|
||
int win32_serial_read( int fd, void *vb, int size )
|
||
{
|
||
long start, now;
|
||
unsigned long nBytes = 0, total = 0, error;
|
||
/* unsigned long waiting = 0; */
|
||
int err, vmin;
|
||
struct termios_list *index;
|
||
char message[80];
|
||
COMSTAT stat;
|
||
clock_t c;
|
||
unsigned char *dest = vb;
|
||
|
||
start = GetTickCount();
|
||
ENTER( "serial_read" );
|
||
|
||
if ( fd <= 0 )
|
||
{
|
||
return 0;
|
||
}
|
||
index = find_port( fd );
|
||
if ( !index )
|
||
{
|
||
LEAVE( "serial_read" );
|
||
return -1;
|
||
}
|
||
|
||
/* FIXME: CREAD: without this, data cannot be read
|
||
FIXME: PARMRK: mark framing & parity errors
|
||
FIXME: IGNCR: ignore \r
|
||
FIXME: ICRNL: convert \r to \n
|
||
FIXME: INLCR: convert \n to \r
|
||
*/
|
||
|
||
if ( index->open_flags & O_NONBLOCK )
|
||
{
|
||
int ret;
|
||
vmin = 0;
|
||
/* pull mucho-cpu here? */
|
||
do {
|
||
#ifdef DEBUG_VERBOSE
|
||
report( "vmin=0\n" );
|
||
#endif /* DEBUG_VERBOSE */
|
||
ret = ClearErrors( index, &stat);
|
||
/*
|
||
usleep(1000);
|
||
usleep(50);
|
||
*/
|
||
/* we should use -1 instead of 0 for disabled timeout */
|
||
now = GetTickCount();
|
||
if ( index->ttyset->c_cc[VTIME] &&
|
||
now-start >= (index->ttyset->c_cc[VTIME]*100)) {
|
||
/*
|
||
sprintf( message, "now = %i start = %i time = %i total =%i\n", now, start, index->ttyset->c_cc[VTIME]*100, total);
|
||
report( message );
|
||
*/
|
||
return total; /* read timeout */
|
||
}
|
||
} while( stat.cbInQue < size && size > 1 );
|
||
}
|
||
else
|
||
{
|
||
/* VTIME is in units of 0.1 seconds */
|
||
|
||
#ifdef DEBUG_VERBOSE
|
||
report( "vmin!=0\n" );
|
||
#endif /* DEBUG_VERBOSE */
|
||
vmin = index->ttyset->c_cc[VMIN];
|
||
|
||
c = clock() + index->ttyset->c_cc[VTIME] * CLOCKS_PER_SEC / 10;
|
||
do {
|
||
error = ClearErrors( index, &stat);
|
||
usleep(1000);
|
||
} while ( c > clock() );
|
||
|
||
}
|
||
|
||
total = 0;
|
||
while ( size > 0 )
|
||
{
|
||
nBytes = 0;
|
||
/* ret = ClearErrors( index, &stat); */
|
||
|
||
index->rol.Offset = index->rol.OffsetHigh = 0;
|
||
ResetEvent( index->rol.hEvent );
|
||
|
||
err = ReadFile( index->hComm, dest + total, size, &nBytes, &index->rol );
|
||
#ifdef DEBUG_VERBOSE
|
||
/* warning Roy Rogers! */
|
||
sprintf(message, " ========== ReadFile = %i %s\n",
|
||
( int ) nBytes, (char *) dest + total );
|
||
report( message );
|
||
#endif /* DEBUG_VERBOSE */
|
||
size -= nBytes;
|
||
total += nBytes;
|
||
|
||
if ( !err )
|
||
{
|
||
switch ( GetLastError() )
|
||
{
|
||
case ERROR_BROKEN_PIPE:
|
||
report( "ERROR_BROKEN_PIPE\n ");
|
||
nBytes = 0;
|
||
break;
|
||
case ERROR_MORE_DATA:
|
||
/*
|
||
usleep(1000);
|
||
*/
|
||
report( "ERROR_MORE_DATA\n" );
|
||
break;
|
||
case ERROR_IO_PENDING:
|
||
while( ! GetOverlappedResult(
|
||
index->hComm,
|
||
&index->rol,
|
||
&nBytes,
|
||
TRUE ) )
|
||
{
|
||
if( GetLastError() !=
|
||
ERROR_IO_INCOMPLETE )
|
||
{
|
||
ClearErrors(
|
||
index,
|
||
&stat);
|
||
return( total );
|
||
}
|
||
}
|
||
size -= nBytes;
|
||
total += nBytes;
|
||
if (size > 0) {
|
||
now = GetTickCount();
|
||
sprintf(message, "size > 0: spent=%ld have=%d\n", now-start, index->ttyset->c_cc[VTIME]*100);
|
||
report( message );
|
||
/* we should use -1 for disabled
|
||
timouts */
|
||
if ( index->ttyset->c_cc[VTIME] && now-start >= (index->ttyset->c_cc[VTIME]*100)) {
|
||
report( "TO " );
|
||
/* read timeout */
|
||
return total;
|
||
}
|
||
}
|
||
sprintf(message, "end nBytes=%ld] ", nBytes);
|
||
report( message );
|
||
/*
|
||
usleep(1000);
|
||
*/
|
||
report( "ERROR_IO_PENDING\n" );
|
||
break;
|
||
default:
|
||
/*
|
||
usleep(1000);
|
||
*/
|
||
YACK();
|
||
return -1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
usleep(1000);
|
||
*/
|
||
ClearErrors( index, &stat);
|
||
return( total );
|
||
}
|
||
}
|
||
LEAVE( "serial_read" );
|
||
return total;
|
||
}
|
||
|
||
#ifdef asdf
|
||
int win32_serial_read( int fd, void *vb, int size )
|
||
{
|
||
long start, now;
|
||
unsigned long nBytes = 0, total = 0, error;
|
||
/* unsigned long waiting = 0; */
|
||
int err, vmin;
|
||
struct termios_list *index;
|
||
char message[80];
|
||
COMSTAT Stat;
|
||
clock_t c;
|
||
unsigned char *dest = vb;
|
||
|
||
start = GetTickCount();
|
||
ENTER( "serial_read" );
|
||
|
||
if ( fd <= 0 )
|
||
{
|
||
printf("1\n");
|
||
return 0;
|
||
}
|
||
index = find_port( fd );
|
||
if ( !index )
|
||
{
|
||
LEAVE( "serial_read 7" );
|
||
errno = EIO;
|
||
printf("2\n");
|
||
return -1;
|
||
}
|
||
|
||
/* FIXME: CREAD: without this, data cannot be read
|
||
FIXME: PARMRK: mark framing & parity errors
|
||
FIXME: IGNCR: ignore \r
|
||
FIXME: ICRNL: convert \r to \n
|
||
FIXME: INLCR: convert \n to \r
|
||
*/
|
||
|
||
ClearErrors( index, &Stat );
|
||
if ( index->open_flags & O_NONBLOCK )
|
||
{
|
||
int ret;
|
||
vmin = 0;
|
||
/* pull mucho-cpu here? */
|
||
do {
|
||
#ifdef DEBUG_VERBOSE
|
||
report( "vmin=0\n" );
|
||
#endif /* DEBUG_VERBOSE */
|
||
ret = ClearErrors( index, &Stat);
|
||
/*
|
||
usleep(1000);
|
||
usleep(50);
|
||
*/
|
||
/* we should use -1 instead of 0 for disabled timeout */
|
||
now = GetTickCount();
|
||
if ( index->ttyset->c_cc[VTIME] &&
|
||
now-start >= (index->ttyset->c_cc[VTIME]*100)) {
|
||
/*
|
||
sprintf( message, "now = %i start = %i time = %i total =%i\n", now, start, index->ttyset->c_cc[VTIME]*100, total);
|
||
report( message );
|
||
*/
|
||
errno = EAGAIN;
|
||
printf("3\n");
|
||
return -1; /* read timeout */
|
||
}
|
||
} while( Stat.cbInQue < size && size > 1 );
|
||
}
|
||
else
|
||
{
|
||
/* VTIME is in units of 0.1 seconds */
|
||
|
||
#ifdef DEBUG_VERBOSE
|
||
report( "vmin!=0\n" );
|
||
#endif /* DEBUG_VERBOSE */
|
||
vmin = index->ttyset->c_cc[VMIN];
|
||
|
||
c = clock() + index->ttyset->c_cc[VTIME] * CLOCKS_PER_SEC / 10;
|
||
do {
|
||
error = ClearErrors( index, &Stat);
|
||
usleep(1000);
|
||
} while ( c > clock() );
|
||
|
||
}
|
||
|
||
total = 0;
|
||
while ( size > 0 )
|
||
{
|
||
nBytes = 0;
|
||
/* ret = ClearErrors( index, &Stat); */
|
||
|
||
index->rol.Offset = index->rol.OffsetHigh = 0;
|
||
ResetEvent( index->rol.hEvent );
|
||
|
||
err = ReadFile( index->hComm, dest + total, size, &nBytes, &index->rol );
|
||
#ifdef DEBUG_VERBOSE
|
||
/* warning Roy Rogers! */
|
||
sprintf(message, " ========== ReadFile = %i %s\n",
|
||
( int ) nBytes, (char *) dest + total );
|
||
report( message );
|
||
#endif /* DEBUG_VERBOSE */
|
||
size -= nBytes;
|
||
total += nBytes;
|
||
|
||
if ( !err )
|
||
{
|
||
switch ( GetLastError() )
|
||
{
|
||
case ERROR_BROKEN_PIPE:
|
||
report( "ERROR_BROKEN_PIPE\n ");
|
||
nBytes = 0;
|
||
break;
|
||
case ERROR_MORE_DATA:
|
||
/*
|
||
usleep(1000);
|
||
*/
|
||
report( "ERROR_MORE_DATA\n" );
|
||
break;
|
||
case ERROR_IO_PENDING:
|
||
while( ! GetOverlappedResult(
|
||
index->hComm,
|
||
&index->rol,
|
||
&nBytes,
|
||
TRUE ) )
|
||
{
|
||
if( GetLastError() !=
|
||
ERROR_IO_INCOMPLETE )
|
||
{
|
||
ClearErrors(
|
||
index,
|
||
&Stat);
|
||
printf("4\n");
|
||
return( total );
|
||
}
|
||
}
|
||
size -= nBytes;
|
||
total += nBytes;
|
||
if (size > 0) {
|
||
now = GetTickCount();
|
||
sprintf(message, "size > 0: spent=%ld have=%d\n", now-start, index->ttyset->c_cc[VTIME]*100);
|
||
report( message );
|
||
/* we should use -1 for disabled
|
||
timouts */
|
||
if ( index->ttyset->c_cc[VTIME] && now-start >= (index->ttyset->c_cc[VTIME]*100)) {
|
||
report( "TO " );
|
||
/* read timeout */
|
||
printf("5\n");
|
||
return total;
|
||
}
|
||
}
|
||
sprintf(message, "end nBytes=%ld] ", nBytes);
|
||
report( message );
|
||
/*
|
||
usleep(1000);
|
||
*/
|
||
report( "ERROR_IO_PENDING\n" );
|
||
break;
|
||
default:
|
||
/*
|
||
usleep(1000);
|
||
*/
|
||
YACK();
|
||
errno = EIO;
|
||
printf("6\n");
|
||
return -1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
usleep(1000);
|
||
*/
|
||
ClearErrors( index, &Stat);
|
||
printf("7\n");
|
||
return( total );
|
||
}
|
||
}
|
||
LEAVE( "serial_read" );
|
||
ClearErrors( index, &Stat);
|
||
return total;
|
||
}
|
||
#endif /* asdf */
|
||
|
||
/*----------------------------------------------------------
|
||
cfsetospeed()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int cfsetospeed( struct termios *s_termios, speed_t speed )
|
||
{
|
||
char message[80];
|
||
ENTER( "cfsetospeed" );
|
||
if ( speed & ~CBAUD )
|
||
{
|
||
sprintf( message, "cfsetospeed: not speed: %#o\n", speed );
|
||
report( message );
|
||
return 0;
|
||
}
|
||
s_termios->c_ispeed = s_termios->c_ospeed = speed;
|
||
/* clear baudrate */
|
||
s_termios->c_cflag &= ~CBAUD;
|
||
if( speed )
|
||
{
|
||
s_termios->c_cflag |= speed;
|
||
}
|
||
else
|
||
{
|
||
/* PC blows up with speed 0 handled in Java */
|
||
s_termios->c_cflag |= B9600;
|
||
}
|
||
LEAVE( "cfsetospeed" );
|
||
return 1;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
cfsetispeed()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int cfsetispeed( struct termios *s_termios, speed_t speed )
|
||
{
|
||
return cfsetospeed( s_termios, speed );
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
cfsetspeed()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int cfsetspeed( struct termios *s_termios, speed_t speed )
|
||
{
|
||
return cfsetospeed( s_termios, speed );
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
cfgetospeed()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
speed_t cfgetospeed( struct termios *s_termios )
|
||
{
|
||
ENTER( "cfgetospeed" );
|
||
return s_termios->c_ospeed;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
cfgetispeed()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
speed_t cfgetispeed( struct termios *s_termios )
|
||
{
|
||
ENTER( "cfgetospeed" );
|
||
return s_termios->c_ispeed;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
serial_struct_to_DCB()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
int serial_struct_to_DCB( struct serial_struct *sstruct, DCB *dcb )
|
||
{
|
||
/* 5 Baud rate fix
|
||
sstruct.baud_base
|
||
sstruct.custom_divisor = ( sstruct.baud_base/cspeed );
|
||
*/
|
||
return(0);
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
termios_to_DCB()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
int termios_to_DCB( struct termios *s_termios, DCB *dcb )
|
||
{
|
||
ENTER( "termios_to_DCB" );
|
||
s_termios->c_ispeed = s_termios->c_ospeed = s_termios->c_cflag & CBAUD;
|
||
dcb->BaudRate = B_to_CBR( s_termios->c_ispeed );
|
||
dcb->ByteSize = termios_to_bytesize( s_termios->c_cflag );
|
||
|
||
if ( s_termios->c_cflag & PARENB )
|
||
{
|
||
if ( s_termios->c_cflag & PARODD
|
||
&& s_termios->c_cflag & CMSPAR )
|
||
{
|
||
dcb->Parity = MARKPARITY;
|
||
}
|
||
else if ( s_termios->c_cflag & PARODD )
|
||
{
|
||
dcb->Parity = ODDPARITY;
|
||
}
|
||
else if ( s_termios->c_cflag & CMSPAR )
|
||
{
|
||
dcb->Parity = SPACEPARITY;
|
||
}
|
||
else
|
||
{
|
||
dcb->Parity = EVENPARITY;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
dcb->Parity = NOPARITY;
|
||
}
|
||
|
||
if ( s_termios->c_cflag & CSTOPB ) dcb->StopBits = TWOSTOPBITS;
|
||
else dcb->StopBits = ONESTOPBIT;
|
||
|
||
if ( s_termios->c_cflag & HARDWARE_FLOW_CONTROL )
|
||
{
|
||
dcb->fRtsControl = RTS_CONTROL_HANDSHAKE;
|
||
dcb->fOutxCtsFlow = TRUE;
|
||
}
|
||
else
|
||
{
|
||
dcb->fRtsControl = RTS_CONTROL_DISABLE;
|
||
dcb->fOutxCtsFlow = FALSE;
|
||
}
|
||
|
||
LEAVE( "termios_to_DCB" );
|
||
return 0;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
DCB_to_serial_struct()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
int DCB_to_serial_struct( DCB *dcb, struct serial_struct *sstruct )
|
||
{
|
||
return( 0 );
|
||
}
|
||
/*----------------------------------------------------------
|
||
DCB_to_termios()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
void DCB_to_termios( DCB *dcb, struct termios *s_termios )
|
||
{
|
||
ENTER( "DCB_to_termios" );
|
||
s_termios->c_ispeed = CBR_to_B( dcb->BaudRate );
|
||
s_termios->c_ospeed = s_termios->c_ispeed;
|
||
s_termios->c_cflag |= s_termios->c_ispeed & CBAUD;
|
||
LEAVE( "DCB_to_termios" );
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
show_DCB()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
void show_DCB( myDCB )
|
||
{
|
||
|
||
#ifdef DEBUG_HOSED
|
||
char message[80];
|
||
|
||
sprintf( message, "DCBlength: %ld\n", myDCB.DCBlength );
|
||
report( message );
|
||
sprintf( "BaudRate: %ld\n", myDCB.BaudRate );
|
||
report( message );
|
||
if ( myDCB.fBinary )
|
||
report( "fBinary\n" );
|
||
if ( myDCB.fParity )
|
||
{
|
||
report( "fParity: " );
|
||
if ( myDCB.fErrorChar )
|
||
{
|
||
sprintf( message, "fErrorChar: %#x\n", myDCB.ErrorChar );
|
||
report( message );
|
||
}
|
||
else
|
||
{
|
||
report( "fErrorChar == false\n" );
|
||
}
|
||
}
|
||
if ( myDCB.fOutxCtsFlow )
|
||
report( "fOutxCtsFlow\n" );
|
||
if ( myDCB.fOutxDsrFlow )
|
||
report( "fOutxDsrFlow\n" );
|
||
if ( myDCB.fDtrControl & DTR_CONTROL_HANDSHAKE );
|
||
report( "DTR_CONTROL_HANDSHAKE\n" );
|
||
if ( myDCB.fDtrControl & DTR_CONTROL_ENABLE );
|
||
report( "DTR_CONTROL_ENABLE\n" );
|
||
if ( myDCB.fDtrControl & DTR_CONTROL_DISABLE );
|
||
report( "DTR_CONTROL_DISABLE\n" );
|
||
if ( myDCB.fDsrSensitivity )
|
||
report( "fDsrSensitivity\n" );
|
||
if ( myDCB.fTXContinueOnXoff )
|
||
report( "fTXContinueOnXoff\n" );
|
||
if ( myDCB.fOutX )
|
||
report( "fOutX\n" );
|
||
if ( myDCB.fInX )
|
||
report( "fInX\n" );
|
||
if ( myDCB.fNull )
|
||
report( "fNull\n" );
|
||
if ( myDCB.fRtsControl & RTS_CONTROL_TOGGLE )
|
||
report( "RTS_CONTROL_TOGGLE\n" );
|
||
if ( myDCB.fRtsControl == 0 )
|
||
report( "RTS_CONTROL_HANDSHAKE ( fRtsControl==0 )\n" );
|
||
if ( myDCB.fRtsControl & RTS_CONTROL_HANDSHAKE )
|
||
report( "RTS_CONTROL_HANDSHAKE\n" );
|
||
if ( myDCB.fRtsControl & RTS_CONTROL_ENABLE )
|
||
report( "RTS_CONTROL_ENABLE\n" );
|
||
if ( myDCB.fRtsControl & RTS_CONTROL_DISABLE )
|
||
report( "RTS_CONTROL_DISABLE\n" );
|
||
if ( myDCB.fAbortOnError )
|
||
report( "fAbortOnError\n" );
|
||
sprintf( message, "XonLim: %d\n", myDCB.XonLim );
|
||
report( message );
|
||
sprintf( message, "XoffLim: %d\n", myDCB.XoffLim );
|
||
report( message );
|
||
sprintf( message, "ByteSize: %d\n", myDCB.ByteSize );
|
||
report( message );
|
||
switch ( myDCB.Parity )
|
||
{
|
||
case EVENPARITY:
|
||
report( "EVENPARITY" );
|
||
break;
|
||
case MARKPARITY:
|
||
report( "MARKPARITY" );
|
||
break;
|
||
case NOPARITY:
|
||
report( "NOPARITY" );
|
||
break;
|
||
case ODDPARITY:
|
||
report( "ODDPARITY" );
|
||
break;
|
||
default:
|
||
sprintf( message,
|
||
"unknown Parity (%#x ):", myDCB.Parity );
|
||
report( message );
|
||
break;
|
||
}
|
||
report( "\n" );
|
||
switch( myDCB.StopBits )
|
||
{
|
||
case ONESTOPBIT:
|
||
report( "ONESTOPBIT" );
|
||
break;
|
||
case ONE5STOPBITS:
|
||
report( "ONE5STOPBITS" );
|
||
break;
|
||
case TWOSTOPBITS:
|
||
report( "TWOSTOPBITS" );
|
||
break;
|
||
default:
|
||
report( "unknown StopBits (%#x ):", myDCB.StopBits );
|
||
break;
|
||
}
|
||
report( "\n" );
|
||
sprintf( message, "XonChar: %#x\n", myDCB.XonChar );
|
||
report( message );
|
||
sprintf( message, "XoffChar: %#x\n", myDCB.XoffChar );
|
||
report( message );
|
||
sprintf( message, "EofChar: %#x\n", myDCB.EofChar );
|
||
report( message );
|
||
sprintf( message, "EvtChar: %#x\n", myDCB.EvtChar );
|
||
report( message );
|
||
report( "\n" );
|
||
#endif /* DEBUG_HOSED */
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
tcgetattr()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: GetCommState(), GetCommTimeouts()
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int tcgetattr( int fd, struct termios *s_termios )
|
||
{
|
||
DCB myDCB;
|
||
COMMTIMEOUTS timeouts;
|
||
struct termios_list *index;
|
||
char message[80];
|
||
|
||
ENTER( "tcgetattr" );
|
||
if ( fd <= 0 )
|
||
return 0;
|
||
index = find_port( fd );
|
||
if ( !index )
|
||
{
|
||
LEAVE( "tcgetattr" );
|
||
return -1;
|
||
}
|
||
if ( !GetCommState( index->hComm, &myDCB ) )
|
||
{
|
||
sprintf( message, "GetCommState failed\n" );
|
||
report( message );
|
||
return -1;
|
||
}
|
||
memcpy( s_termios, index->ttyset, sizeof( struct termios ) );
|
||
|
||
show_DCB( myDCB );
|
||
|
||
/***** input mode flags (c_iflag ) ****/
|
||
/* parity check enable */
|
||
if ( myDCB.fParity )
|
||
{
|
||
s_termios->c_iflag |= INPCK;
|
||
s_termios->c_iflag &= ~IGNPAR;
|
||
} else {
|
||
s_termios->c_iflag &= ~INPCK;
|
||
s_termios->c_iflag |= IGNPAR;
|
||
}
|
||
/* FIXME: IGNBRK: ignore break */
|
||
/* FIXME: BRKINT: interrupt on break */
|
||
|
||
if ( myDCB.fOutX )
|
||
{
|
||
s_termios->c_iflag |= IXON;
|
||
}
|
||
else
|
||
{
|
||
/* IXON: output start/stop control */
|
||
s_termios->c_iflag &= ~IXON;
|
||
}
|
||
if ( myDCB.fInX )
|
||
{
|
||
s_termios->c_iflag |= IXOFF;
|
||
}
|
||
else
|
||
{
|
||
/* IXOFF: input start/stop control */
|
||
s_termios->c_iflag &= ~IXOFF;
|
||
}
|
||
|
||
if ( myDCB.fTXContinueOnXoff )
|
||
{
|
||
s_termios->c_iflag |= IXANY;
|
||
}
|
||
else
|
||
{
|
||
/* IXANY: any char restarts output */
|
||
s_termios->c_iflag &= ~IXANY;
|
||
}
|
||
/* FIXME: IMAXBEL: if input buffer full, send bell */
|
||
|
||
/***** control mode flags (c_cflag ) *****/
|
||
/* FIXME: CLOCAL: DONT send SIGHUP on modem disconnect */
|
||
/* FIXME: HUPCL: generate modem disconnect when all has closed or
|
||
exited */
|
||
/* CSTOPB two stop bits ( otherwise one) */
|
||
if ( myDCB.StopBits == TWOSTOPBITS )
|
||
{
|
||
s_termios->c_cflag |= CSTOPB;
|
||
}
|
||
if ( myDCB.StopBits == ONESTOPBIT )
|
||
{
|
||
s_termios->c_cflag &= ~CSTOPB;
|
||
}
|
||
|
||
|
||
/* PARENB enable parity bit */
|
||
s_termios->c_cflag &= ~( PARENB | PARODD | CMSPAR );
|
||
myDCB.fParity = 1;
|
||
if( myDCB.fParity )
|
||
{
|
||
report( "tcgetattr getting parity\n" );
|
||
s_termios->c_cflag |= PARENB;
|
||
if ( myDCB.Parity == MARKPARITY )
|
||
{
|
||
s_termios->c_cflag |= ( PARODD | CMSPAR );
|
||
}
|
||
else if ( myDCB.Parity == SPACEPARITY )
|
||
{
|
||
s_termios->c_cflag |= CMSPAR;
|
||
}
|
||
else if ( myDCB.Parity == ODDPARITY )
|
||
{
|
||
report( "ODDPARITY\n" );
|
||
s_termios->c_cflag |= PARODD;
|
||
}
|
||
else if ( myDCB.Parity == EVENPARITY )
|
||
{
|
||
report( "EVENPARITY\n" );
|
||
s_termios->c_cflag &= ~PARODD;
|
||
}
|
||
else if ( myDCB.Parity == NOPARITY )
|
||
{
|
||
s_termios->c_cflag &= ~(PARODD | CMSPAR | PARENB);
|
||
}
|
||
} else
|
||
{
|
||
s_termios->c_cflag &= ~PARENB;
|
||
}
|
||
/* CSIZE */
|
||
s_termios->c_cflag |= bytesize_to_termios( myDCB.ByteSize );
|
||
/* HARDWARE_FLOW_CONTROL: hardware flow control */
|
||
if (( myDCB.fOutxCtsFlow == TRUE ) ||
|
||
( myDCB.fRtsControl == RTS_CONTROL_HANDSHAKE))
|
||
{
|
||
s_termios->c_cflag |= HARDWARE_FLOW_CONTROL;
|
||
}
|
||
else
|
||
{
|
||
s_termios->c_cflag &= ~HARDWARE_FLOW_CONTROL;
|
||
}
|
||
/* MDMBUF: carrier based flow control of output */
|
||
/* CIGNORE: tcsetattr will ignore control modes & baudrate */
|
||
|
||
/***** NOT SUPPORTED: local mode flags (c_lflag) *****/
|
||
/* ICANON: canonical (not raw) mode */
|
||
/* ECHO: echo back to terminal */
|
||
/* ECHOE: echo erase */
|
||
/* ECHOPRT: hardcopy echo erase */
|
||
/* ECHOK: show KILL char */
|
||
/* ECHOKE: BSD ECHOK */
|
||
/* ECHONL: ICANON only: echo newline even with no ECHO */
|
||
/* ECHOCTL: if ECHO, then control-A are printed as '^A' */
|
||
/* ISIG: recognize INTR, QUIT & SUSP */
|
||
/* IEXTEN: implmentation defined */
|
||
/* NOFLSH: dont clear i/o queues on INTR, QUIT or SUSP */
|
||
/* TOSTOP: background process generate SIGTTOU */
|
||
/* ALTWERASE: alt-w erase distance */
|
||
/* FLUSHO: user DISCARD char */
|
||
/* NOKERNINFO: disable STATUS char */
|
||
/* PENDIN: input line needsd reprinting, set by REPRINT char */
|
||
/***** END - NOT SUPPORTED *****/
|
||
|
||
/***** control characters (c_cc[NCCS] ) *****/
|
||
|
||
if ( !GetCommTimeouts( index->hComm, &timeouts ) )
|
||
{
|
||
YACK();
|
||
report( "GetCommTimeouts\n" );
|
||
return -1;
|
||
}
|
||
|
||
s_termios->c_cc[VTIME] = timeouts.ReadTotalTimeoutConstant/100;
|
||
/*
|
||
handled in SerialImp.c?
|
||
s_termios->c_cc[VMIN] = ?
|
||
*/
|
||
|
||
s_termios->c_cc[VSTART] = myDCB.XonChar;
|
||
s_termios->c_cc[VSTOP] = myDCB.XoffChar;
|
||
s_termios->c_cc[VEOF] = myDCB.EofChar;
|
||
|
||
#ifdef DEBUG_VERBOSE
|
||
sprintf( message,
|
||
"tcgetattr: VTIME:%d, VMIN:%d\n", s_termios->c_cc[VTIME],
|
||
s_termios->c_cc[VMIN] );
|
||
report( message );
|
||
#endif /* DEBUG_VERBOSE */
|
||
|
||
/***** line discipline ( c_line ) ( == c_cc[33] ) *****/
|
||
|
||
DCB_to_termios( &myDCB, s_termios ); /* baudrate */
|
||
LEAVE( "tcgetattr" );
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
`TCSANOW'
|
||
Make the change immediately.
|
||
|
||
`TCSADRAIN'
|
||
Make the change after waiting until all queued output has
|
||
been written. You should usually use this option when
|
||
changing parameters that affect output.
|
||
|
||
`TCSAFLUSH'
|
||
This is like `TCSADRAIN', but also discards any queued input.
|
||
|
||
`TCSASOFT'
|
||
This is a flag bit that you can add to any of the above
|
||
alternatives. Its meaning is to inhibit alteration of the
|
||
state of the terminal hardware. It is a BSD extension; it is
|
||
only supported on BSD systems and the GNU system.
|
||
|
||
Using `TCSASOFT' is exactly the same as setting the `CIGNORE'
|
||
bit in the `c_cflag' member of the structure TERMIOS-P points
|
||
to. *Note Control Modes::, for a description of `CIGNORE'.
|
||
*/
|
||
|
||
/*----------------------------------------------------------
|
||
tcsetattr()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: GetCommState(), GetCommTimeouts(), SetCommState(),
|
||
SetCommTimeouts()
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
int tcsetattr( int fd, int when, struct termios *s_termios )
|
||
{
|
||
int vtime;
|
||
DCB dcb;
|
||
COMMTIMEOUTS timeouts;
|
||
struct termios_list *index;
|
||
|
||
ENTER( "tcsetattr" );
|
||
if ( fd <= 0 )
|
||
return 0;
|
||
index = find_port( fd );
|
||
if ( !index )
|
||
{
|
||
LEAVE( "tcsetattr" );
|
||
return -1;
|
||
}
|
||
fflush( stdout );
|
||
if ( s_termios->c_lflag & ICANON )
|
||
{
|
||
report( "tcsetattr: no canonical mode support\n" );
|
||
/* and all other c_lflags too */
|
||
return -1;
|
||
}
|
||
if ( !GetCommState( index->hComm, &dcb ) )
|
||
{
|
||
YACK();
|
||
report( "tcsetattr:GetCommState\n" );
|
||
return -1;
|
||
}
|
||
if ( !GetCommTimeouts( index->hComm, &timeouts ) )
|
||
{
|
||
YACK();
|
||
report( "tcsetattr:GetCommTimeouts\n" );
|
||
return -1;
|
||
}
|
||
|
||
/*** control flags, c_cflag **/
|
||
if ( !( s_termios->c_cflag & CIGNORE ) )
|
||
{
|
||
dcb.fParity=1;
|
||
/* CIGNORE: ignore control modes and baudrate */
|
||
/* baudrate */
|
||
if ( termios_to_DCB( s_termios, &dcb ) < 0 ) return -1;
|
||
}
|
||
else
|
||
{
|
||
}
|
||
|
||
/*** input flags, c_iflag **/
|
||
/* This is wrong. It disables Parity FIXME
|
||
if( ( s_termios->c_iflag & INPCK ) && !( s_termios->c_iflag & IGNPAR ) )
|
||
{
|
||
dcb.fParity = TRUE;
|
||
} else
|
||
{
|
||
dcb.fParity = FALSE;
|
||
}
|
||
*/
|
||
/* not in win95?
|
||
Some years later...
|
||
eww.. FIXME This is used for changing the Parity
|
||
error character
|
||
|
||
I think this code is hosed. See VEOF below
|
||
|
||
Trent
|
||
*/
|
||
|
||
if ( s_termios->c_iflag & ISTRIP ) dcb.fBinary = FALSE;
|
||
/* ISTRIP: strip to seven bits */
|
||
else dcb.fBinary = TRUE;
|
||
|
||
/* FIXME: IGNBRK: ignore break */
|
||
/* FIXME: BRKINT: interrupt on break */
|
||
if ( s_termios->c_iflag & IXON )
|
||
{
|
||
dcb.fOutX = TRUE;
|
||
}
|
||
else
|
||
{
|
||
dcb.fOutX = FALSE;
|
||
}
|
||
if ( s_termios->c_iflag & IXOFF )
|
||
{
|
||
dcb.fInX = TRUE;
|
||
}
|
||
else
|
||
{
|
||
dcb.fInX = FALSE;
|
||
}
|
||
dcb.fTXContinueOnXoff = ( s_termios->c_iflag & IXANY ) ? TRUE : FALSE;
|
||
/* FIXME: IMAXBEL: if input buffer full, send bell */
|
||
|
||
/* no DTR control in termios? */
|
||
dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
||
/* no DSR control in termios? */
|
||
dcb.fOutxDsrFlow = FALSE;
|
||
/* DONT ignore rx bytes when DSR is OFF */
|
||
dcb.fDsrSensitivity = FALSE;
|
||
dcb.XonChar = s_termios->c_cc[VSTART];
|
||
dcb.XoffChar = s_termios->c_cc[VSTOP];
|
||
dcb.XonLim = 0; /* ? */
|
||
dcb.XoffLim = 0; /* ? */
|
||
dcb.EofChar = s_termios->c_cc[VEOF];
|
||
if( dcb.EofChar != '\0' )
|
||
{
|
||
dcb.fBinary = 0;
|
||
}
|
||
else
|
||
{
|
||
dcb.fBinary = 1;
|
||
}
|
||
if ( EV_BREAK|EV_CTS|EV_DSR|EV_ERR|EV_RING | ( EV_RLSD & EV_RXFLAG ) )
|
||
dcb.EvtChar = '\n';
|
||
else
|
||
dcb.EvtChar = '\0';
|
||
|
||
if ( !SetCommState( index->hComm, &dcb ) )
|
||
{
|
||
report( "SetCommState error\n" );
|
||
YACK();
|
||
return -1;
|
||
}
|
||
|
||
#ifdef DEBUG_VERBOSE
|
||
{
|
||
char message[32];
|
||
sprintf( message, "VTIME:%d, VMIN:%d\n", s_termios->c_cc[VTIME],
|
||
s_termios->c_cc[VMIN] );
|
||
report( message );
|
||
}
|
||
#endif /* DEBUG_VERBOSE */
|
||
vtime = s_termios->c_cc[VTIME] * 100;
|
||
timeouts.ReadTotalTimeoutConstant = vtime;
|
||
timeouts.ReadIntervalTimeout = 0;
|
||
timeouts.ReadTotalTimeoutMultiplier = 0;
|
||
|
||
timeouts.WriteTotalTimeoutConstant = vtime;
|
||
timeouts.WriteTotalTimeoutMultiplier = 0;
|
||
/* max between bytes */
|
||
if ( s_termios->c_cc[VMIN] > 0 && vtime > 0 )
|
||
{
|
||
/* read blocks forever on VMIN chars */
|
||
} else if ( s_termios->c_cc[VMIN] == 0 && vtime == 0 )
|
||
{
|
||
/* read returns immediately */
|
||
timeouts.ReadIntervalTimeout = MAXDWORD;
|
||
timeouts.ReadTotalTimeoutConstant = 0;
|
||
timeouts.ReadTotalTimeoutMultiplier = 0;
|
||
}
|
||
#ifdef DEBUG_VERBOSE
|
||
{
|
||
char message[64];
|
||
sprintf( message, "ReadIntervalTimeout=%ld\n",
|
||
timeouts.ReadIntervalTimeout );
|
||
report( message );
|
||
sprintf( message, "c_cc[VTIME] = %d, c_cc[VMIN] = %d\n",
|
||
s_termios->c_cc[VTIME], s_termios->c_cc[VMIN] );
|
||
report( message );
|
||
sprintf( message, "ReadTotalTimeoutConstant: %ld\n",
|
||
timeouts.ReadTotalTimeoutConstant );
|
||
report( message );
|
||
sprintf( message, "ReadIntervalTimeout : %ld\n",
|
||
timeouts.ReadIntervalTimeout );
|
||
report( message );
|
||
sprintf( message, "ReadTotalTimeoutMultiplier: %ld\n",
|
||
timeouts.ReadTotalTimeoutMultiplier );
|
||
report( message );
|
||
}
|
||
#endif /* DEBUG_VERBOSE */
|
||
if ( !SetCommTimeouts( index->hComm, &timeouts ) )
|
||
{
|
||
YACK();
|
||
report( "SetCommTimeouts\n" );
|
||
return -1;
|
||
}
|
||
memcpy( index->ttyset, s_termios, sizeof( struct termios ) );
|
||
LEAVE( "tcsetattr" );
|
||
return 0;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
tcsendbreak()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments:
|
||
break for duration*0.25 seconds or
|
||
0.25 seconds if duration = 0.
|
||
----------------------------------------------------------*/
|
||
|
||
int tcsendbreak( int fd, int duration )
|
||
{
|
||
struct termios_list *index;
|
||
COMSTAT Stat;
|
||
|
||
ENTER( "tcsendbreak" );
|
||
|
||
index = find_port( fd );
|
||
|
||
if ( !index )
|
||
{
|
||
LEAVE( "tcdrain" );
|
||
return -1;
|
||
}
|
||
|
||
if ( duration <= 0 ) duration = 1;
|
||
|
||
if( !SetCommBreak( index->hComm ) )
|
||
ClearErrors( index, &Stat );
|
||
/* 0.25 seconds == 250000 usec */
|
||
usleep( duration * 250000 );
|
||
if( !ClearCommBreak( index->hComm ) )
|
||
ClearErrors( index, &Stat );
|
||
LEAVE( "tcsendbreak" );
|
||
return 1;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
tcdrain()
|
||
|
||
accept: file descriptor
|
||
perform: wait for ouput to be written.
|
||
return: 0 on success, -1 otherwise
|
||
exceptions: None
|
||
win32api: FlushFileBuffers
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int tcdrain ( int fd )
|
||
{
|
||
struct termios_list *index;
|
||
char message[80];
|
||
int old_flag;
|
||
|
||
ENTER( "tcdrain" );
|
||
index = find_port( fd );
|
||
|
||
if ( !index )
|
||
{
|
||
LEAVE( "tcdrain" );
|
||
return -1;
|
||
}
|
||
old_flag = index->event_flag;
|
||
/*
|
||
index->event_flag &= ~EV_TXEMPTY;
|
||
SetCommMask( index->hComm, index->event_flag );
|
||
index->tx_happened = 1;
|
||
*/
|
||
if ( !FlushFileBuffers( index->hComm ) )
|
||
{
|
||
/* FIXME Need to figure out what the various errors are in
|
||
windows. YACK() should report them and we can
|
||
handle them as we find them
|
||
|
||
|
||
Something funky is happening on NT. GetLastError =
|
||
0.
|
||
*/
|
||
sprintf( message, "FlushFileBuffers() %i\n",
|
||
(int) GetLastError() );
|
||
report( message );
|
||
if( GetLastError() == 0 )
|
||
{
|
||
set_errno( 0 );
|
||
return(0);
|
||
}
|
||
set_errno( EAGAIN );
|
||
YACK();
|
||
LEAVE( "tcdrain" );
|
||
return -1;
|
||
}
|
||
/*
|
||
sprintf( message, "FlushFileBuffers() %i\n",
|
||
(int) GetLastError() );
|
||
report( message );
|
||
*/
|
||
LEAVE( "tcdrain success" );
|
||
index->event_flag |= EV_TXEMPTY;
|
||
SetCommMask( index->hComm, index->event_flag );
|
||
index->event_flag = old_flag;
|
||
/*
|
||
index->tx_happened = 1;
|
||
*/
|
||
return 0;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
tcflush()
|
||
|
||
accept: file descriptor, queue_selector
|
||
perform: discard data not transmitted or read
|
||
TCIFLUSH: flush data not read
|
||
TCOFLUSH: flush data not transmitted
|
||
TCIOFLUSH: flush both
|
||
return: 0 on success, -1 on error
|
||
exceptions: none
|
||
win32api: PurgeComm
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
|
||
int tcflush( int fd, int queue_selector )
|
||
{
|
||
struct termios_list *index;
|
||
int old_flag;
|
||
|
||
index = find_port( fd );
|
||
if( !index)
|
||
{
|
||
LEAVE( "tclflush" );
|
||
return(-1);
|
||
}
|
||
|
||
old_flag = index->event_flag;
|
||
/*
|
||
index->event_flag &= ~EV_TXEMPTY;
|
||
SetCommMask( index->hComm, index->event_flag );
|
||
index->tx_happened = 1;
|
||
*/
|
||
ENTER( "tcflush" );
|
||
if ( !index )
|
||
{
|
||
LEAVE( "tcflush" );
|
||
return -1;
|
||
}
|
||
|
||
index->tx_happened = 1;
|
||
switch( queue_selector )
|
||
{
|
||
case TCIFLUSH:
|
||
if ( !PurgeComm( index->hComm, PURGE_RXABORT ) )
|
||
{
|
||
goto fail;
|
||
}
|
||
break;
|
||
case TCOFLUSH:
|
||
if ( !PurgeComm( index->hComm, PURGE_TXABORT ) )
|
||
{
|
||
goto fail;
|
||
}
|
||
break;
|
||
case TCIOFLUSH:
|
||
if ( !PurgeComm( index->hComm, PURGE_TXABORT ) )
|
||
{
|
||
goto fail;
|
||
}
|
||
if ( !PurgeComm( index->hComm, PURGE_RXABORT ) )
|
||
{
|
||
goto fail;
|
||
}
|
||
break;
|
||
default:
|
||
/*
|
||
set_errno( ENOTSUP );
|
||
*/
|
||
report( "tcflush: Unknown queue_selector\n" );
|
||
LEAVE( "tcflush" );
|
||
return -1;
|
||
}
|
||
index->event_flag |= EV_TXEMPTY;
|
||
SetCommMask( index->hComm, index->event_flag );
|
||
index->event_flag = old_flag;
|
||
index->tx_happened = 1;
|
||
LEAVE( "tcflush" );
|
||
return( 0 );
|
||
|
||
/* FIXME Need to figure out what the various errors are in
|
||
windows. YACK() should report them and we can
|
||
handle them as we find them
|
||
*/
|
||
|
||
fail:
|
||
LEAVE( "tcflush" );
|
||
set_errno( EAGAIN );
|
||
YACK();
|
||
return -1;
|
||
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
tcflow()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments: FIXME
|
||
----------------------------------------------------------*/
|
||
|
||
int tcflow( int fd, int action )
|
||
{
|
||
ENTER( "tcflow" );
|
||
switch ( action )
|
||
{
|
||
/* Suspend transmission of output */
|
||
case TCOOFF: break;
|
||
/* Restart transmission of output */
|
||
case TCOON: break;
|
||
/* Transmit a STOP character */
|
||
case TCIOFF: break;
|
||
/* Transmit a START character */
|
||
case TCION: break;
|
||
default: return -1;
|
||
}
|
||
LEAVE( "tcflow" );
|
||
return 1;
|
||
}
|
||
/*----------------------------------------------------------
|
||
fstat()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api:
|
||
comments: this is just to keep the eventLoop happy.
|
||
----------------------------------------------------------*/
|
||
|
||
#if 0
|
||
int fstat( int fd, ... )
|
||
{
|
||
return( 0 );
|
||
}
|
||
#endif
|
||
|
||
/*----------------------------------------------------------
|
||
ioctl()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: GetCommError(), GetCommModemStatus, EscapeCommFunction()
|
||
comments: FIXME
|
||
the DCB struct is:
|
||
|
||
typedef struct _DCB
|
||
{
|
||
unsigned long DCBlength, BaudRate, fBinary:1, fParity:1;
|
||
unsigned long fOutxCtsFlow:1, fOutxDsrFlow:1, fDtrControl:2;
|
||
unsigned long fDsrSensitivity:1, fTXContinueOnXoff:1;
|
||
unsigned long fOutX:1, fInX:1, fErrorChar:1, fNull:1;
|
||
unsigned long fRtsControl:2, fAbortOnError:1, fDummy2:17;
|
||
WORD wReserved, XonLim, XoffLim;
|
||
BYTE ByteSize, Parity, StopBits;
|
||
char XonChar, XoffChar, ErrorChar, EofChar, EvtChar;
|
||
WORD wReserved1;
|
||
} DCB;
|
||
|
||
----------------------------------------------------------*/
|
||
|
||
int win32_serial_ioctl( int fd, int request, ... )
|
||
{
|
||
unsigned long dwStatus = 0;
|
||
va_list ap;
|
||
int *arg, ret, result, old_flag;
|
||
char message[80];
|
||
|
||
#ifdef TIOCGSERIAL
|
||
DCB *dcb;
|
||
struct serial_struct *sstruct;
|
||
#endif /* TIOCGSERIAL */
|
||
COMSTAT Stat;
|
||
|
||
struct termios_list *index;
|
||
struct async_struct *astruct;
|
||
struct serial_multiport_struct *mstruct;
|
||
#ifdef TIOCGICOUNT
|
||
struct serial_icounter_struct *sistruct;
|
||
#endif /* TIOCGICOUNT */
|
||
|
||
ENTER( "ioctl" );
|
||
if ( fd <= 0 )
|
||
return 0;
|
||
index = find_port( fd );
|
||
if ( !index )
|
||
{
|
||
LEAVE( "ioctl" );
|
||
return -1;
|
||
}
|
||
|
||
va_start( ap, request );
|
||
|
||
ret = ClearErrors( index, &Stat );
|
||
if (ret == 0)
|
||
{
|
||
set_errno( EBADFD );
|
||
YACK();
|
||
report( "ClearError Failed! ernno EBADFD" );
|
||
arg = va_arg( ap, int * );
|
||
va_end( ap );
|
||
return -1;
|
||
}
|
||
switch( request )
|
||
{
|
||
case TCSBRK:
|
||
arg = va_arg( ap, int * );
|
||
va_end( ap );
|
||
return -ENOIOCTLCMD;
|
||
case TCSBRKP:
|
||
arg = va_arg( ap, int * );
|
||
va_end( ap );
|
||
return -ENOIOCTLCMD;
|
||
case TIOCGSOFTCAR:
|
||
arg = va_arg( ap, int * );
|
||
va_end( ap );
|
||
return -ENOIOCTLCMD;
|
||
case TIOCSSOFTCAR:
|
||
arg = va_arg( ap, int * );
|
||
va_end( ap );
|
||
return -ENOIOCTLCMD;
|
||
|
||
case TIOCCBRK:
|
||
case TIOCSBRK:
|
||
arg = va_arg( ap, int * );
|
||
if ( EscapeCommFunction( index->hComm,
|
||
( request == TIOCSBRK ) ? SETBREAK :
|
||
CLRBREAK ) )
|
||
report( "EscapeCommFunction: True\n" );
|
||
else
|
||
report( "EscapeCommFunction: False\n" );
|
||
break;
|
||
|
||
case TIOCMGET:
|
||
arg = va_arg( ap, int * );
|
||
/* DORITOS */
|
||
if ( !GetCommModemStatus( index->hComm, &dwStatus ) )
|
||
report_error( "GetCommMOdemStatus failed!\n" );
|
||
if ( dwStatus & MS_RLSD_ON ) *arg |= TIOCM_CAR;
|
||
else *arg &= ~TIOCM_CAR;
|
||
if ( dwStatus & MS_RING_ON ) *arg |= TIOCM_RNG;
|
||
else *arg &= ~TIOCM_RNG;
|
||
if ( dwStatus & MS_DSR_ON ) *arg |= TIOCM_DSR;
|
||
else *arg &= ~TIOCM_DSR;
|
||
if ( dwStatus & MS_CTS_ON ) *arg |= TIOCM_CTS;
|
||
else *arg &= ~TIOCM_CTS;
|
||
/* I'm not seeing a way to read the MSR directly
|
||
we store the state using TIOCM_*
|
||
|
||
Trent
|
||
*/
|
||
if ( index->MSR & TIOCM_DTR )
|
||
*arg |= TIOCM_DTR;
|
||
else *arg &= ~TIOCM_DTR;
|
||
if ( index->MSR & TIOCM_RTS )
|
||
*arg |= TIOCM_RTS;
|
||
else *arg &= ~TIOCM_RTS;
|
||
|
||
/*
|
||
|
||
TIOCM_LE
|
||
TIOCM_ST
|
||
TIOCM_SR
|
||
*/
|
||
va_end( ap );
|
||
return( 0 );
|
||
case TIOCMBIS:
|
||
arg = va_arg( ap, int * );
|
||
if ( *arg & TIOCM_DTR )
|
||
{
|
||
index->MSR |= TIOCM_DTR;
|
||
|
||
if (EscapeCommFunction( index->hComm, SETDTR))
|
||
report( "EscapeCommFunction: True\n" );
|
||
else
|
||
report( "EscapeCommFunction: False\n" );
|
||
}
|
||
|
||
if ( *arg & TIOCM_RTS )
|
||
{
|
||
index->MSR |= TIOCM_RTS;
|
||
|
||
if(EscapeCommFunction( index->hComm, SETRTS))
|
||
report( "EscapeCommFunction: True\n" );
|
||
else
|
||
report( "EscapeCommFunction: False\n" );
|
||
}
|
||
break;
|
||
|
||
case TIOCMBIC:
|
||
arg = va_arg( ap, int * );
|
||
if ( *arg & TIOCM_DTR )
|
||
{
|
||
index->MSR &= ~TIOCM_DTR;
|
||
if ( EscapeCommFunction( index->hComm, CLRDTR))
|
||
report( "EscapeCommFunction: True\n" );
|
||
else
|
||
report( "EscapeCommFunction: False\n" );
|
||
}
|
||
|
||
if ( *arg & TIOCM_RTS )
|
||
{
|
||
index->MSR &= ~TIOCM_RTS;
|
||
if( EscapeCommFunction( index->hComm, CLRRTS))
|
||
report( "EscapeCommFunction: True\n" );
|
||
else
|
||
report( "EscapeCommFunction: False\n" );
|
||
}
|
||
break;
|
||
|
||
case TIOCMSET:
|
||
arg = va_arg( ap, int * );
|
||
if (( *arg & TIOCM_DTR) == (index->MSR & TIOCM_DTR) )
|
||
{
|
||
report( "DTR is unchanged\n" );
|
||
}
|
||
sprintf(message, "DTR %i %i\n", *arg&TIOCM_DTR, index->MSR & TIOCM_DTR );
|
||
report( message );
|
||
if ( *arg & TIOCM_DTR )
|
||
{
|
||
index->MSR |= TIOCM_DTR;
|
||
}
|
||
else
|
||
{
|
||
index->MSR &= ~TIOCM_DTR;
|
||
}
|
||
if ( EscapeCommFunction( index->hComm,
|
||
( *arg & TIOCM_DTR ) ? SETDTR :
|
||
CLRDTR ) )
|
||
report( "EscapeCommFunction: True\n" );
|
||
else
|
||
report( "EscapeCommFunction: False\n" );
|
||
if ( (*arg & TIOCM_RTS) == ( index->MSR & TIOCM_RTS) )
|
||
{
|
||
report( "RTS is unchanged\n" );
|
||
}
|
||
sprintf( message, "RTS %i %i\n", *arg&TIOCM_RTS, index->MSR & TIOCM_RTS );
|
||
report( message );
|
||
if ( *arg & TIOCM_RTS )
|
||
{
|
||
index->MSR |= TIOCM_RTS;
|
||
result &= SETRTS;
|
||
}
|
||
else
|
||
{
|
||
index->MSR &= ~TIOCM_RTS;
|
||
result &= CLRRTS;
|
||
}
|
||
if( EscapeCommFunction( index->hComm,
|
||
( *arg & TIOCM_RTS ) ? SETRTS : CLRRTS ) )
|
||
report( "EscapeCommFunction: True\n" );
|
||
else
|
||
report( "EscapeCommFunction: False\n" );
|
||
break;
|
||
|
||
#ifdef TIOCGSERIAL
|
||
case TIOCGSERIAL:
|
||
report( "TIOCGSERIAL\n" );
|
||
|
||
dcb = malloc( sizeof( DCB ) );
|
||
if( !dcb )
|
||
{
|
||
va_end( ap );
|
||
return -1;
|
||
}
|
||
memset( dcb, 0, sizeof( DCB ) );
|
||
GetCommState( index->hComm, dcb );
|
||
|
||
sstruct = va_arg( ap, struct serial_struct * );
|
||
if ( DCB_to_serial_struct( dcb, sstruct ) < 0 )
|
||
{
|
||
va_end( ap );
|
||
return -1;
|
||
}
|
||
index->sstruct = sstruct;
|
||
|
||
report( "TIOCGSERIAL\n" );
|
||
free(dcb);
|
||
break;
|
||
|
||
#endif /* TIOCGSERIAL */
|
||
#ifdef TIOCSSERIAL
|
||
case TIOCSSERIAL:
|
||
report( "TIOCSSERIAL\n" );
|
||
|
||
dcb = malloc( sizeof( DCB ) );
|
||
if( !dcb )
|
||
{
|
||
va_end( ap );
|
||
return -1;
|
||
}
|
||
memset( dcb, 0, sizeof( DCB ) );
|
||
GetCommState( index->hComm, dcb );
|
||
|
||
index->sstruct = va_arg( ap, struct serial_struct * );
|
||
if ( serial_struct_to_DCB( index->sstruct, dcb ) < 0 )
|
||
{
|
||
va_end( ap );
|
||
return -1;
|
||
}
|
||
|
||
report( "TIOCSSERIAL\n" );
|
||
free(dcb);
|
||
break;
|
||
|
||
#endif /* TIOCSSERIAL */
|
||
case TIOCSERCONFIG:
|
||
case TIOCSERGETLSR:
|
||
arg = va_arg( ap, int * );
|
||
/*
|
||
do {
|
||
wait = WaitForSingleObject( index->sol.hEvent, 5000 );
|
||
} while ( wait == WAIT_TIMEOUT );
|
||
*/
|
||
ret = ClearErrors( index, &Stat );
|
||
if ( ret == 0 )
|
||
{
|
||
/* FIXME ? */
|
||
set_errno( EBADFD );
|
||
YACK();
|
||
report( "TIOCSERGETLSR EBADFD" );
|
||
va_end( ap );
|
||
return -1;
|
||
}
|
||
if ( (int ) Stat.cbOutQue == 0 )
|
||
{
|
||
/* output is empty */
|
||
if( index->tx_happened == 1 )
|
||
{
|
||
old_flag = index->event_flag;
|
||
index->event_flag &= ~EV_TXEMPTY;
|
||
SetCommMask( index->hComm,
|
||
index->event_flag );
|
||
index->event_flag = old_flag;
|
||
*arg = 1;
|
||
index->tx_happened = 0;
|
||
report( "ioctl: ouput empty\n" );
|
||
}
|
||
else
|
||
{
|
||
*arg = 0;
|
||
}
|
||
ret = 0;
|
||
}
|
||
else
|
||
{
|
||
/* still data out there */
|
||
*arg = 0;
|
||
ret = 0;
|
||
}
|
||
va_end( ap );
|
||
return(0);
|
||
break;
|
||
case TIOCSERGSTRUCT:
|
||
astruct = va_arg( ap, struct async_struct * );
|
||
va_end( ap );
|
||
return -ENOIOCTLCMD;
|
||
case TIOCSERGETMULTI:
|
||
mstruct = va_arg( ap, struct serial_multiport_struct * );
|
||
va_end( ap );
|
||
return -ENOIOCTLCMD;
|
||
case TIOCSERSETMULTI:
|
||
mstruct = va_arg( ap, struct serial_multiport_struct * );
|
||
va_end( ap );
|
||
return -ENOIOCTLCMD;
|
||
case TIOCMIWAIT:
|
||
arg = va_arg( ap, int * );
|
||
va_end( ap );
|
||
return -ENOIOCTLCMD;
|
||
/*
|
||
On linux this fills a struct with all the line info
|
||
(data available, bytes sent, ...
|
||
*/
|
||
#ifdef TIOCGICOUNT
|
||
case TIOCGICOUNT:
|
||
sistruct= va_arg( ap, struct serial_icounter_struct * );
|
||
ret = ClearErrors( index, &Stat );
|
||
if ( ret == 0 )
|
||
{
|
||
/* FIXME ? */
|
||
report( "TIOCGICOUNT failed\n" );
|
||
set_errno( EBADFD );
|
||
va_end( ap );
|
||
return -1;
|
||
}
|
||
if( sistruct->frame != index->sis->frame )
|
||
{
|
||
sistruct->frame = index->sis->frame;
|
||
/*
|
||
printf( "---------------frame = %i\n", sistruct->frame++ );
|
||
*/
|
||
}
|
||
if( sistruct->overrun != index->sis->overrun )
|
||
{
|
||
/*
|
||
printf( "---------------overrun\n" );
|
||
*/
|
||
sistruct->overrun = index->sis->overrun;
|
||
/* ErrCode &= ~CE_OVERRUN; */
|
||
}
|
||
if( sistruct->parity != index->sis->parity )
|
||
{
|
||
/*
|
||
printf( "---------------parity\n" );
|
||
*/
|
||
sistruct->parity = index->sis->parity;
|
||
}
|
||
if( sistruct->brk != index->sis->brk )
|
||
{
|
||
/*
|
||
printf( "---------------brk\n" );
|
||
*/
|
||
sistruct->brk = index->sis->brk;
|
||
}
|
||
va_end( ap );
|
||
return 0;
|
||
/* abolete ioctls */
|
||
#endif /* TIOCGICOUNT */
|
||
case TIOCSERGWILD:
|
||
case TIOCSERSWILD:
|
||
report( "TIOCSER[GS]WILD absolete\n" );
|
||
va_end( ap );
|
||
return 0;
|
||
/* number of bytes available for reading */
|
||
case FIONREAD:
|
||
arg = va_arg( ap, int * );
|
||
ret = ClearErrors( index, &Stat );
|
||
if ( ret == 0 )
|
||
{
|
||
/* FIXME ? */
|
||
report( "FIONREAD failed\n" );
|
||
set_errno( EBADFD );
|
||
va_end( ap );
|
||
return -1;
|
||
}
|
||
*arg = ( int ) Stat.cbInQue;
|
||
#ifdef DEBUG_VERBOSE
|
||
sprintf( message, "FIONREAD: %i bytes available\n",
|
||
(int) Stat.cbInQue );
|
||
report( message );
|
||
if( *arg )
|
||
{
|
||
sprintf( message, "FIONREAD: %i\n", *arg );
|
||
report( message );
|
||
}
|
||
#endif /* DEBUG_VERBOSE */
|
||
ret = 0;
|
||
break;
|
||
|
||
/* pending bytes to be sent */
|
||
case TIOCOUTQ:
|
||
arg = va_arg( ap, int * );
|
||
va_end( ap );
|
||
return -ENOIOCTLCMD;
|
||
default:
|
||
sprintf( message,
|
||
"FIXME: ioctl: unknown request: %#x\n",
|
||
request );
|
||
report( message );
|
||
va_end( ap );
|
||
return -ENOIOCTLCMD;
|
||
}
|
||
va_end( ap );
|
||
LEAVE( "ioctl" );
|
||
return 0;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
fcntl()
|
||
|
||
accept:
|
||
perform:
|
||
return:
|
||
exceptions:
|
||
win32api: None
|
||
comments: FIXME
|
||
----------------------------------------------------------*/
|
||
|
||
int fcntl( int fd, int command, ... )
|
||
{
|
||
int arg, ret = 0;
|
||
va_list ap;
|
||
struct termios_list *index;
|
||
char message[80];
|
||
|
||
ENTER( "fcntl" );
|
||
if ( fd <= 0 )
|
||
return 0;
|
||
index = find_port( fd );
|
||
if ( !index )
|
||
{
|
||
LEAVE( "fcntl" );
|
||
return -1;
|
||
}
|
||
|
||
va_start( ap, command );
|
||
|
||
arg = va_arg( ap, int );
|
||
switch ( command )
|
||
{
|
||
case F_SETOWN: /* set ownership of fd */
|
||
break;
|
||
case F_SETFL: /* set operating flags */
|
||
#ifdef DEBUG
|
||
sprintf( message, "F_SETFL fd=%d flags=%d\n", fd, arg );
|
||
report( message );
|
||
#endif
|
||
index->open_flags = arg;
|
||
break;
|
||
case F_GETFL: /* get operating flags */
|
||
ret = index->open_flags;
|
||
break;
|
||
default:
|
||
sprintf( message, "unknown fcntl command %#x\n", command );
|
||
report( message );
|
||
break;
|
||
}
|
||
|
||
va_end( ap );
|
||
LEAVE( "fcntl" );
|
||
return ret;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
termios_interrupt_event_loop()
|
||
|
||
accept:
|
||
perform:
|
||
return: let Serial_select break out so the thread can die
|
||
exceptions:
|
||
win32api:
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
void termios_interrupt_event_loop( int fd, int flag )
|
||
{
|
||
struct termios_list * index = find_port( fd );
|
||
if ( !index )
|
||
{
|
||
LEAVE( "termios_interrupt_event_loop" );
|
||
return;
|
||
}
|
||
/*
|
||
index->event_flag = 0;
|
||
TRENT SetCommMask( index->hComm, index->event_flag );
|
||
usleep(2000);
|
||
tcdrain( index->fd );
|
||
SetEvent( index->sol.hEvent );
|
||
*/
|
||
index->interrupt = flag;
|
||
return;
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
Serial_select()
|
||
|
||
accept:
|
||
perform:
|
||
return: number of fd's changed on success or -1 on error.
|
||
exceptions:
|
||
win32api: SetCommMask(), GetCommEvent(), WaitSingleObject()
|
||
comments:
|
||
----------------------------------------------------------*/
|
||
#ifndef __LCC__
|
||
int win32_serial_select( int n, fd_set *readfds, fd_set *writefds,
|
||
fd_set *exceptfds, struct timeval *timeout )
|
||
{
|
||
|
||
unsigned long dwCommEvent, wait = WAIT_TIMEOUT;
|
||
int fd = n-1;
|
||
struct termios_list *index;
|
||
char message[80];
|
||
COMSTAT Stat;
|
||
int ret;
|
||
|
||
ENTER( "serial_select" );
|
||
if ( fd <= 0 )
|
||
{
|
||
/* Baby did a bad baad thing */
|
||
goto fail;
|
||
}
|
||
|
||
index = find_port( fd );
|
||
|
||
if ( !index )
|
||
goto fail;
|
||
|
||
#define DATA_AVAILABLE 1
|
||
|
||
//nativeSetEventFlag( fd, SerialPortEvent.DATA_AVAILABLE, enable );
|
||
if (readfds) {
|
||
int eventflags[12];
|
||
memset(eventflags, 0, sizeof(eventflags));
|
||
|
||
eventflags[DATA_AVAILABLE] = 1;
|
||
termios_setflags( fd, eventflags );
|
||
}
|
||
|
||
if ( !index || !index->event_flag )
|
||
{
|
||
/* still setting up the port? hold off for a Sec so
|
||
things can fire up
|
||
|
||
this does happen. loops ~twice on a 350 Mzh with
|
||
usleep(1000000)
|
||
*/
|
||
usleep(10000);
|
||
LEAVE( "serial_uselect" );
|
||
return(0);
|
||
}
|
||
|
||
ResetEvent( index->wol.hEvent );
|
||
ResetEvent( index->sol.hEvent );
|
||
ResetEvent( index->rol.hEvent );
|
||
ret = ClearErrors( index, &Stat );
|
||
#if 1
|
||
if ( ret == 0 ) {
|
||
goto fail;
|
||
}
|
||
/* look only after read */
|
||
if (readfds && !writefds && !exceptfds) {
|
||
int timeout_usec = timeout ? timeout->tv_sec*1000000 + timeout->tv_usec : INT_MAX;
|
||
|
||
while (timeout_usec > 0) {
|
||
sprintf( message, "wait for data in read buffer%d\n", (int)Stat.cbInQue );
|
||
report( message );
|
||
|
||
if (Stat.cbInQue != 0) {
|
||
goto end;
|
||
}
|
||
|
||
usleep(10000);
|
||
/* FIXME: not very accurate wrt process time */
|
||
timeout_usec -= 10000;
|
||
|
||
report( "sleep...\n" );
|
||
|
||
ret = ClearErrors( index, &Stat );
|
||
if ( ret == 0 ) {
|
||
goto fail;
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
while ( wait == WAIT_TIMEOUT && index->sol.hEvent )
|
||
{
|
||
if( index->interrupt == 1 )
|
||
{
|
||
goto fail;
|
||
}
|
||
SetCommMask( index->hComm, index->event_flag );
|
||
ClearErrors( index, &Stat );
|
||
if ( !WaitCommEvent( index->hComm, &dwCommEvent,
|
||
&index->sol ) )
|
||
{
|
||
/* WaitCommEvent failed probably overlapped though */
|
||
if ( GetLastError() != ERROR_IO_PENDING )
|
||
{
|
||
ClearErrors( index, &Stat );
|
||
goto fail;
|
||
}
|
||
/* thought so... */
|
||
}
|
||
/* could use the select timeout here but it should not
|
||
be needed
|
||
*/
|
||
ClearErrors( index, &Stat );
|
||
wait = WaitForSingleObject( index->sol.hEvent, 100 );
|
||
switch ( wait )
|
||
{
|
||
case WAIT_OBJECT_0:
|
||
goto end;
|
||
case WAIT_TIMEOUT:
|
||
goto timeout;
|
||
case WAIT_ABANDONED:
|
||
default:
|
||
goto fail;
|
||
|
||
}
|
||
}
|
||
end:
|
||
/* You may want to chop this out for lower latency */
|
||
usleep(1000);
|
||
LEAVE( "serial_select" );
|
||
return( 1 );
|
||
timeout:
|
||
LEAVE( "serial_select" );
|
||
return( 0 );
|
||
fail:
|
||
YACK();
|
||
sprintf( message, "< select called error %i\n", n );
|
||
report( message );
|
||
errno = EBADFD;
|
||
LEAVE( "serial_select" );
|
||
return( 1 );
|
||
}
|
||
#ifdef asdf
|
||
int win32_serial_select( int n, fd_set *readfds, fd_set *writefds,
|
||
fd_set *exceptfds, struct timeval *timeout )
|
||
{
|
||
|
||
unsigned long nBytes, dwCommEvent, wait = WAIT_TIMEOUT;
|
||
int fd = n-1;
|
||
struct termios_list *index;
|
||
char message[80];
|
||
|
||
ENTER( "serial_select" );
|
||
if ( fd <= 0 )
|
||
{
|
||
usleep(1000);
|
||
return 1;
|
||
}
|
||
index = find_port( fd );
|
||
if ( !index )
|
||
{
|
||
LEAVE( "serial_select" );
|
||
return -1;
|
||
}
|
||
if( index->interrupt == 1 )
|
||
{
|
||
goto end;
|
||
}
|
||
while(!index->event_flag )
|
||
{
|
||
usleep(1000);
|
||
return -1;
|
||
}
|
||
|
||
while ( wait == WAIT_TIMEOUT && index->sol.hEvent )
|
||
{
|
||
if( index->interrupt == 1 )
|
||
{
|
||
goto end;
|
||
}
|
||
if( !index->sol.hEvent )
|
||
{
|
||
return 1;
|
||
}
|
||
if ( !WaitCommEvent( index->hComm, &dwCommEvent,
|
||
&index->sol ) )
|
||
{
|
||
/* WaitCommEvent failed */
|
||
if( index->interrupt == 1 )
|
||
{
|
||
goto end;
|
||
}
|
||
if ( GetLastError() != ERROR_IO_PENDING )
|
||
{
|
||
sprintf( message, "WaitCommEvent filename = %s\n", index->filename);
|
||
report( message );
|
||
return(1);
|
||
/*
|
||
goto fail;
|
||
*/
|
||
}
|
||
return(1);
|
||
}
|
||
if( index->interrupt == 1 )
|
||
{
|
||
goto end;
|
||
}
|
||
wait = WaitForSingleObject( index->sol.hEvent, 1000 );
|
||
switch ( wait )
|
||
{
|
||
case WAIT_OBJECT_0:
|
||
if( index->interrupt == 1 )
|
||
{
|
||
goto end;
|
||
}
|
||
if( !index->sol.hEvent ) return(1);
|
||
if (!GetOverlappedResult( index->hComm,
|
||
&index->sol, &nBytes, TRUE ))
|
||
{
|
||
goto end;
|
||
}
|
||
else if( index->tx_happened == 1 )
|
||
{
|
||
goto end;
|
||
}
|
||
else
|
||
goto end;
|
||
break;
|
||
case WAIT_TIMEOUT:
|
||
default:
|
||
return(1); /* WaitFor error */
|
||
|
||
}
|
||
}
|
||
end:
|
||
/*
|
||
usleep(1000);
|
||
*/
|
||
LEAVE( "serial_select" );
|
||
return( 1 );
|
||
#ifdef asdf
|
||
/* FIXME this needs to be cleaned up... */
|
||
fail:
|
||
sprintf( message, "< select called error %i\n", n );
|
||
YACK();
|
||
report( message );
|
||
set_errno( EBADFD );
|
||
LEAVE( "serial_select" );
|
||
return( 1 );
|
||
#endif /* asdf */
|
||
|
||
}
|
||
#endif /* asdf */
|
||
#endif /* __LCC__ */
|
||
|
||
/*----------------------------------------------------------
|
||
termiosSetParityError()
|
||
|
||
accept: fd The device opened
|
||
perform: Get the Parity Error Char
|
||
return: the Parity Error Char
|
||
exceptions: none
|
||
win32api: GetCommState()
|
||
comments: No idea how to do this in Unix (handle in read?)
|
||
----------------------------------------------------------*/
|
||
|
||
int termiosGetParityErrorChar( int fd )
|
||
{
|
||
struct termios_list *index;
|
||
DCB dcb;
|
||
|
||
ENTER( "termiosGetParityErrorChar" );
|
||
index = find_port( fd );
|
||
if( !index )
|
||
{
|
||
LEAVE( "termiosGetParityErrorChar" );
|
||
return(-1);
|
||
}
|
||
GetCommState( index->hComm, &dcb );
|
||
LEAVE( "termiosGetParityErrorChar" );
|
||
return( dcb.ErrorChar );
|
||
}
|
||
|
||
/*----------------------------------------------------------
|
||
termiosSetParityError()
|
||
|
||
accept: fd The device opened, value the new Parity Error Char
|
||
perform: Set the Parity Error Char
|
||
return: void
|
||
exceptions: none
|
||
win32api: GetCommState(), SetCommState()
|
||
comments: No idea how to do this in Unix (handle in read?)
|
||
----------------------------------------------------------*/
|
||
|
||
void termiosSetParityError( int fd, char value )
|
||
{
|
||
DCB dcb;
|
||
struct termios_list *index;
|
||
|
||
ENTER( "termiosGetParityErrorChar" );
|
||
index = find_port( fd );
|
||
if ( !index )
|
||
{
|
||
LEAVE( "termiosSetParityError" );
|
||
return;
|
||
}
|
||
GetCommState( index->hComm, &dcb );
|
||
dcb.ErrorChar = value;
|
||
SetCommState( index->hComm, &dcb );
|
||
LEAVE( "termiosGetParityErrorChar" );
|
||
}
|
||
/*----------------------- END OF LIBRARY -----------------*/
|
||
|
||
#endif /* WIN32 */
|