SerialPort.st
author Claus Gittinger <cg@exept.de>
Thu, 02 Feb 2006 17:22:51 +0100
changeset 1593 d6d284094d2d
child 1595 eb26b0dfa8f6
permissions -rw-r--r--
*** empty log message ***

"
 COPYRIGHT (c) 2006 by eXept Software AG
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

"{ Package: 'stx:libbasic2' }"

NonPositionableExternalStream subclass:#SerialPort
	instanceVariableNames:'portName baudRate stopBitsType parityType dataBits
		inFlowCtrlType outFlowCtrlType xOnChar xOffChar'
	classVariableNames:'DefaultPortName'
	poolDictionaries:''
	category:'Streams-External'
!

!SerialPort primitiveDefinitions!
%{

#include "stxOSDefs.h"

#ifdef WIN32

# undef __BEGIN_INTERRUPTABLE__
# undef __END_INTERRUPTABLE__
# define __BEGIN_INTERRUPTABLE__ ..
# define __END_INTERRUPTABLE__ ..

# define WRAP_STDIO

# define PORT_FROM_FD(fd)     (_get_osfhandle(fd))
# define closePort(port)      closeHandle(port)
# define SERIALPORT           HANDLE

#else /* not WIN32 */

# define PORT_FROM_FD(fd)     (fd)
# define closePort(port)      close(port)
# define SERIALPORT           int

#endif /* WIN32 */

# define PORT_FROM_FILE(f)             (PORT_FROM_FD(fileno(f)))
# define PORT_FROM_FILE_OBJECT(f)      (PORT_FROM_FILE(__FILEVal(f)))

#include <stdio.h>
#include <errno.h>

/*
 * on some systems errno is a macro ... check for it here
 */
#ifndef errno
 extern errno;
#endif

#ifdef USE_H_ERRNO
# ifndef h_errno
 extern h_errno;
# endif
#endif

#ifdef DEBUG
# define DBGPRINTF(x)    { if (__debugging__) printf x; }
# define DBGFPRINTF(x)   { if (__debugging__) fprintf x; }
#else
# define DBGPRINTF(x)    /* as nothing */
# define DBGFPRINTF(x)   /* as nothing */
#endif

#ifndef TRUE
# define TRUE   1
#endif
#ifndef FALSE
# define FALSE  0
#endif
#ifndef WIN32
typedef int BOOL;
#endif


%}
! !

!SerialPort primitiveVariables!
%{
static int __debugging__ = 0;
%}
! !

!SerialPort class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2006 by eXept Software AG
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    This class provides access to serial ports
"
!

examples
"
    example (get help info from an nntp server):
									[exBegin]
	... to be added ...
									[exEnd]
"
! !

!SerialPort class methodsFor:'defaults'!

defaultPortName
    DefaultPortName notNil ifTrue:[^ DefaultPortName ].

    OperatingSystem isMSDOSlike ifTrue:[
	^ 'COM1'
    ].
    ^ '/dev/cua0'
! !

!SerialPort class methodsFor:'instance creation'!

portName:portName
   baudRate:baudRateOrNil stopBitsType:stopBitsTypeOrNil
   parityType:parityTypeOrNil dataBits:dataBitsOrNil
   inFlowCtrlType:inFlowCtrlTypeOrNil outFlowCtrlType:outFlowCtrlTypeOrNil
   xOnChar:xOnCharOrNil xOffChar:xOffCharOrNil

    "arguments are self describing; nil values mean: leave setting as is"

    ^ (self new
	setPortName:portName
	baudRate:baudRateOrNil stopBitsType:stopBitsTypeOrNil
	parityType:parityTypeOrNil dataBits:dataBitsOrNil
	inFlowCtrlType:inFlowCtrlTypeOrNil outFlowCtrlType:outFlowCtrlTypeOrNil
	xOnChar:xOnCharOrNil xOffChar:xOffCharOrNil) open
!

new
    "create a serial port"

    ^ super new buffered:false
! !

!SerialPort class methodsFor:'debugging'!

debug:aBoolean
    "turn on/off internal debugprints.
     This method is for ST/X debugging only and
     may  be removed in later versions"

%{  /* NOCONTEXT */

    __debugging__ = (aBoolean == true);
%}
    "
     SerialPort debug:true
     SerialPort debug:false
    "
! !

!SerialPort methodsFor:'accessing'!

setPortName:portNameArg
	baudRate:baudRateOrNil stopBitsType:stopBitsTypeOrNil
	parityType:parityTypeOrNil dataBits:dataBitsOrNil
	inFlowCtrlType:inFlowCtrlTypeOrNil outFlowCtrlType:outFlowCtrlTypeOrNil
	xOnChar:xOnCharOrNil xOffChar:xOffCharOrNil

    portName := portNameArg.
    baudRate := baudRateOrNil.
    stopBitsType := stopBitsTypeOrNil.
    parityType := parityTypeOrNil.
    dataBits := dataBitsOrNil.
    inFlowCtrlType := inFlowCtrlTypeOrNil.
    outFlowCtrlType := outFlowCtrlTypeOrNil.
    xOnChar := xOnCharOrNil.
    xOffChar := xOffCharOrNil.
! !

!SerialPort protectedMethodsFor:'low level'!

closeFile
    "low level close"

%{
    OBJ t;

    t = __INST(filePointer);
    if (t != nil) {
	FILE *fp;
	SERIALPORT port;

	__INST(filePointer) = nil;
	fp = __FILEVal(t);
	port = PORT_FROM_FILE(fp);

# ifdef xxDO_WRAP_CALLS
	{ int ret;

	  do {
	    __threadErrno = 0;
	    ret = STX_C_CALL1("fclose", fclose, fp);
	  } while ((ret < 0) && (__threadErrno == EINTR));

#  ifdef WIN32
	  do {
	    __threadErrno = 0;
	    ret = STX_WSA_CALL1("CloseHandle", CloseHandle, port);
	  } while ((ret < 0) && (__threadErrno == EINTR));
	  CloseHandle(port);
#  endif
	}
# else /* !DO_WRAP_CALLS */

	fclose(fp);
#  ifdef WIN32
	CloseHandle(port);
#  endif

# endif /* !DO_WRAP_CALLS */
    }
%}
! !

!SerialPort methodsFor:'low level'!

baudRate:newRate
%{
    OBJ fp;

    fp = __INST(filePointer);
    if ((fp != nil) && __isSmallInteger(newRate)) {
	SERIALPORT port;
	int ret;

	port = PORT_FROM_FILE_OBJECT(fp);
#ifdef WIN32
	{
	    DCB dcb;

	    ZeroMemory(&dcb, sizeof(dcb));
	    dcb.DCBlength = sizeof(dcb);
	    GetCommState(port, &dcb);

	    dcb.BaudRate = __intVal(newRate);

	    if (! SetCommState(port, &dcb)) {
		RETURN(false);
	    }
	    RETURN(true);
	}
# else /* ! WIN32 */
	/* add code for unix ioctl here ... */
# endif /* WIN32 */
    }
%}.
    self primitiveFailed.
! !

!SerialPort methodsFor:'printing & storing'!

printOn:aStream
    aStream nextPutAll:'SerialPort(baudRate='.
    baudRate printOn:aStream.
    aStream nextPutAll:')'.
! !

!SerialPort methodsFor:'queries'!

getName
    "return the name; here, we return the devices name"

    ^ portName
! !

!SerialPort methodsFor:'opening'!

open
    |errorNumber|

    filePointer notNil ifTrue:[
	^ self errorAlreadyOpen
    ].
%{
    FILE *fp;
    SERIALPORT port;
    char *__portName;
    int __setBaudRate = 1,
	__setDataBits = 1,
	__setXOnChar = 1,
	__setXOffChar = 1,
	__setInFlowCtrl = 1,
	__setOutFlowCtrl = 1,
	__setStopBits = 1,
	__setParityType = 1;
    int __baudRate, __dataBits;
    int __xOnChar, __xOffChar;
    int __inFlowCtrl, __outFlowCtrl;
    int __stopBits, __parityType;
#ifdef WIN32
    COMMTIMEOUTS timeouts;
    DCB dcb;
#endif /* WIN32 */

#   define XONOFF    1
#   define HARDWARE  2
#   define STOP1     1
#   define STOP2     2
#   define STOP1_5   3
#   define ODD       1
#   define EVEN      2
#   define NONE      3

    if (__isString(__INST(portName))) {
	__portName = __stringVal(__INST(portName));
    } else {
	goto failure;
    }

    if (__isSmallInteger(__INST(baudRate))) {
	__baudRate = __intVal(__INST(baudRate));
    } else if (__INST(baudRate) == nil) {
	__setBaudRate = 0;
    } else {
	goto failure;
    }

    if (__isSmallInteger(__INST(dataBits))) {
	__dataBits = __intVal(__INST(dataBits));
    } else if (__INST(dataBits == nil)) {
	__setDataBits = 0;
    } else {
	goto failure;
    }

    if (__isSmallInteger(__INST(xOnChar))) {
	__xOnChar = __intVal(__INST(xOnChar));
    } else if (__isCharacter(__INST(xOnChar))) {
	__xOnChar = __intVal(_characterVal(__INST(xOnChar)));
    } else if (__INST(xOnChar) == nil) {
	__setXOnChar = 0;
    } else {
	goto failure;
    }

    if (__isSmallInteger(__INST(xOffChar))) {
	__xOffChar = __intVal(__INST(xOffChar));
    } else if (__isCharacter(__INST(xOffChar))) {
	__xOffChar = __intVal(__characterVal(__INST(xOffChar)));
    } else if (__INST(xOffChar) == nil) {
	__setXOffChar = 0;
    } else {
	goto failure;
    }

    if (__INST(inFlowCtrlType) == @symbol(xOnOff)) {
	__inFlowCtrl = XONOFF;
    } else if (__INST(inFlowCtrlType) == @symbol(hardware)) {
	__inFlowCtrl = HARDWARE;
    } else if (__INST(inFlowCtrlType) == nil) {
	__setInFlowCtrl = 0;
    } else {
	goto failure;
    }

    if (__INST(outFlowCtrlType) == @symbol(xOnOff)) {
	__outFlowCtrl = XONOFF;
    } else if (__INST(outFlowCtrlType) == @symbol(hardware)) {
	__outFlowCtrl = HARDWARE;
    } else if (__INST(outFlowCtrlType) == nil) {
	__setOutFlowCtrl = 0;
    } else {
	goto failure;
    }

    if (__INST(stopBitsType) == @symbol(stop1)) {
	__stopBits = STOP1;
    } else if (__INST(stopBitsType) == @symbol(stop2)) {
	__stopBits = STOP2;
    } else if (__INST(stopBitsType) == @symbol(stop1_5)) {
	__stopBits = STOP1_5;
    } else if (__INST(stopBitsType) == nil) {
	__setStopBits = 0;
    } else {
	goto failure;
    }

#ifdef WIN32
    port = CreateFile(__portName,
	      GENERIC_READ | GENERIC_WRITE,
	      0,             /* comm devices must be opened with exclusive access */
	      NULL,          /* no security attrs */
	      OPEN_EXISTING, /* comm devices must use OPEN_EXISTING */
	      0,             /* no overlapped I/O */
	      NULL           /* hTemplate must be NULL for comm devices */
	   );

    if (port == INVALID_HANDLE_VALUE) {
	fprintf(stderr, "Win32OS [info]: serial port open failed\n");
	errorNumber = __mkSmallInteger( __WIN32_ERR(GetLastError()) );
	goto failure;
    }

    /* Flush the driver */
    PurgeComm( port, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR );

    /* Set driver buffer sizes */
    SetupComm( port, 4096 /*SERIAL_IN_QUEUE_SIZE*/, 4096 /*SERIAL_OUT_QUEUE_SIZE*/);

    /* Reset timeout constants */
    timeouts.ReadIntervalTimeout= 0xFFFFFFFF;
    timeouts.ReadTotalTimeoutMultiplier = 0;
    timeouts.ReadTotalTimeoutConstant = 0;
    timeouts.WriteTotalTimeoutMultiplier = 0;
    timeouts.WriteTotalTimeoutConstant = 0;
    SetCommTimeouts( port, &timeouts );

    /* Set communication parameters */
    ZeroMemory(&dcb, sizeof(dcb));
    dcb.DCBlength = sizeof(dcb);
    GetCommState(port, &dcb);

    if (__setBaudRate) dcb.BaudRate = __baudRate;
    if (__setDataBits) dcb.ByteSize = __dataBits;
    if (__setXOnChar)  dcb.XonChar = __xOnChar;
    if (__setXOffChar) dcb.XoffChar = __xOffChar;

    if (__setStopBits) {
	/* set stop bits */
	switch(__stopBits) {
	    case STOP1_5: dcb.StopBits = 1; break; /* 1.5 stop bits */
	    case STOP1: dcb.StopBits = 0; break; /* 1 stop bit */
	    case STOP2: dcb.StopBits = 2; break; /* 2 stop bits */
	    default: goto errExit;
	}
    }

    if (__setParityType) {
	/* set parity */
	switch(__parityType) {
	    case NONE: dcb.Parity = NOPARITY; break;
	    case ODD: dcb.Parity = ODDPARITY; break;
	    case EVEN: dcb.Parity = EVENPARITY; break;
	    default: goto errExit;
	}
    }

    if (__setInFlowCtrl) {
	/* set control flow */
	dcb.fInX = FALSE;
	dcb.fDtrControl = FALSE;
	if (__inFlowCtrl == XONOFF) dcb.fInX = TRUE;  /* XOn/XOff handshaking */
	if (__inFlowCtrl == HARDWARE) dcb.fDtrControl = TRUE;  /* hardware handshaking */
    }
    if (__setOutFlowCtrl) {
	dcb.fOutX = FALSE;
	dcb.fOutxCtsFlow = FALSE;

	if (__outFlowCtrl == XONOFF) dcb.fOutX = TRUE;  /* XOn/XOff handshaking */
	if (__outFlowCtrl == HARDWARE) dcb.fOutxCtsFlow = TRUE;  /* hardware handshaking */
    }

    if (! SetCommState(port, &dcb)) {
	fprintf(stderr, "Win32OS [info]: serial port comm-setup failed\n");
	errorNumber = __mkSmallInteger( __WIN32_ERR(GetLastError()) );
	goto errExit;
    }
# endif /* WIN32 */

    /*
     * make it a FILE *
     */
# ifdef WIN32
    {
	int _fd = _open_osfhandle(port, 0);
	fp = fdopen(_fd, "r+");
    }
# else
    fp = fdopen(port, "r+");
# endif
    if (! fp) {
	errorNumber = __MKSMALLINT(errno);
	goto errExit;
    }

    {
	OBJ t;

	t = __MKOBJ(fp);
	__INST(filePointer) = t;
	__STORE(self, t);
    }

	RETURN( true );

errExit: ;
    CloseHandle(port);

failure: ;
#   undef XONOFF
#   undef HARDWARE
#   undef STOP1
#   undef STOP2
#   undef STOP1_5
#   undef ODD
#   undef EVEN
#   undef NONE

%}.
    "all ok?"
    filePointer notNil ifTrue:[
	Lobby register:self.
    ] ifFalse:[
	errorNumber isNil ifTrue:[
	    self error:'invalid argument(s)'.
	] ifFalse:[
	    (OperatingSystem errorHolderForNumber:errorNumber) reportError
	].
    ].
! !

!SerialPort class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libbasic2/SerialPort.st,v 1.1 2006-02-02 16:22:51 cg Exp $'
! !