/***************************************************************************
rlserial.cpp - description
-------------------
begin : Sat Dec 21 2002
copyright : (C) 2002 by Rainer Lehrig
email : lehrig@t-online.de
RMOS implementation:
Copyright : (C) 2004 Zertrox GbR
Written by : Alexander Feller
Email : feller@zertrox.de
***************************************************************************/
/***************************************************************************
* *
* 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 *
* *
***************************************************************************/
#include "rlserial.h"
#ifdef unix
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#endif
#ifdef __VMS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <starlet.h>
#include <descrip.h>
#include <lib$routines.h>
#include <ssdef.h>
#include <iodef.h>
#endif
#ifdef _WIN32
#include <windows.h>
#include <stdio.h>
#endif
#ifdef RM3
#include <stdio.h>
#include <stdlib.h>
#include <rmcomp.h>
#include <rmapi.h>
#include <rio.h>
#include <drvspec.h>
#include <rm3cav.h>
//#include <clean.h>
#include <rcinc.h>
#define RTS_TIME_WAIT 0x008 /* Verzoerungszeit fuer RTS - Signal */
#endif
#include "rlthread.h"
/*
static void sighandler(int sig)
{
if(sig == SIGINT)
{
closeDevice();
closeDatabase();
}
}
*/
rlSerial::rlSerial()
{
ttysavefd = -1;
ttystate = RESET;
fd = -1;
trace = 0;
}
rlSerial::~rlSerial()
{
closeDevice();
}
void rlSerial::setTrace(int on)
{
if(on == 1) trace = 1;
else trace = 0;
}
int rlSerial::openDevice(const char *devicename, int speed, int block, int rtscts, int bits, int stopbits, int parity)
{
#ifdef unix
struct termios buf;
if(fd != -1) return -1;
fd = open(devicename, O_RDWR | O_NOCTTY | O_NDELAY);
if(fd < 0) { return -1; }
//signal(SIGINT, sighandler);
if(tcgetattr(fd, &save_termios) < 0) { return -1; }
buf = save_termios;
buf.c_cflag = speed | CLOCAL | CREAD;
if(rtscts == 1) buf.c_cflag |= CRTSCTS;
if(bits == 7) buf.c_cflag |= CS7;
else buf.c_cflag |= CS8;
if(stopbits == 2) buf.c_cflag |= CSTOPB;
if(parity == rlSerial::ODD) buf.c_cflag |= (PARENB | PARODD);
if(parity == rlSerial::EVEN) buf.c_cflag |= PARENB;
buf.c_lflag = IEXTEN; //ICANON;
buf.c_oflag = OPOST;
buf.c_cc[VMIN] = 1;
buf.c_cc[VTIME] = 0;
buf.c_line = 0;
buf.c_iflag = IGNBRK | IGNPAR | IXANY;
if(tcsetattr(fd, TCSAFLUSH, &buf) < 0) { return -1; }
//if(tcsetattr(fd, TCSANOW, &buf) < 0) { return -1; }
ttystate = RAW;
ttysavefd = fd;
if(block == 1) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
tcflush(fd,TCIOFLUSH);
#endif
#ifdef __VMS
// Please set com parameters at DCL level
struct dsc$descriptor_s dsc;
int status;
dsc.dsc$w_length = strlen(devicename);
dsc.dsc$a_pointer = devicename;
dsc.dsc$b_class = DSC$K_CLASS_S;
dsc.dsc$b_dtype = DSC$K_DTYPE_T;
status = SYS$ASSIGN(&dsc,&vms_channel,0,0);
if(status != SS$_NORMAL) return -1;
#endif
#ifdef _WIN32
DWORD ccsize;
COMMCONFIG cc;
int baudrate,ret;
hdl = CreateFile(
(LPCWSTR)devicename, // pointer to name of the file
GENERIC_READ | GENERIC_WRITE, // access (read-write) mode
0, // share mode
0, // pointer to security attributes
OPEN_EXISTING, // how to create
0, // not overlapped I/O
0 // handle to file with attributes to copy
);
if(hdl == INVALID_HANDLE_VALUE)
{
printf("CreateFile(%s) failed\n",devicename);
return -1;
}
baudrate = CBR_9600;
if(speed == B50 ) baudrate = 50;
if(speed == B75 ) baudrate = 75;
if(speed == B110 ) baudrate = CBR_110;
if(speed == B134 ) baudrate = 134;
if(speed == B150 ) baudrate = 150;
if(speed == B200 ) baudrate = 200;
if(speed == B300 ) baudrate = CBR_300;
if(speed == B600 ) baudrate = CBR_600;
if(speed == B1200 ) baudrate = CBR_1200;
if(speed == B1800 ) baudrate = 1800;
if(speed == B2400 ) baudrate = CBR_2400;
if(speed == B4800 ) baudrate = CBR_4800;
if(speed == B9600 ) baudrate = CBR_9600;
if(speed == B19200 ) baudrate = CBR_19200;
if(speed == B38400 ) baudrate = CBR_38400;
if(speed == B57600 ) baudrate = CBR_57600;
if(speed == B115200 ) baudrate = CBR_115200;
if(speed == B230400 ) baudrate = 230400;
if(speed == B460800 ) baudrate = 460800;
if(speed == B500000 ) baudrate = 500000;
if(speed == B576000 ) baudrate = 576000;
if(speed == B921600 ) baudrate = 921600;
if(speed == B1000000) baudrate = 1000000;
if(speed == B1152000) baudrate = 1152000;
if(speed == B1500000) baudrate = 1500000;
if(speed == B2000000) baudrate = 2000000;
if(speed == B2500000) baudrate = 2500000;
if(speed == B3000000) baudrate = 3000000;
if(speed == B3500000) baudrate = 3500000;
if(speed == B4000000) baudrate = 4000000;
GetCommConfig(hdl,&cc,&ccsize);
//cc.dwSize = sizeof(cc); // size of structure
//cc.wVersion = 1; // version of structure
//cc.wReserved = 0; // reserved
// DCB dcb; // device-control block
cc.dcb.DCBlength = sizeof(DCB); // sizeof(DCB)
cc.dcb.BaudRate = baudrate; // current baud rate
cc.dcb.fBinary = 1; // binary mode, no EOF check
cc.dcb.fParity = 1; // enable parity checking
cc.dcb.fOutxCtsFlow = 0; // CTS output flow control
if(rtscts == 1) cc.dcb.fOutxCtsFlow = 1;
cc.dcb.fOutxDsrFlow = 0; // DSR output flow control
cc.dcb.fDtrControl = DTR_CONTROL_DISABLE; // DTR flow control type
cc.dcb.fDsrSensitivity = 0; // DSR sensitivity
cc.dcb.fTXContinueOnXoff = 1; // XOFF continues Tx
//cc.dcb.fOutX = 0; // XON/XOFF out flow control
//cc.dcb.fInX = 0; // XON/XOFF in flow control
//cc.dcb.fErrorChar = 0; // enable error replacement
cc.dcb.fNull = 0; // enable null stripping
cc.dcb.fRtsControl = RTS_CONTROL_DISABLE;
if(rtscts == 1) cc.dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; // RTS flow control
cc.dcb.fAbortOnError = 0; // abort reads/writes on error
//cc.dcb.fDummy2 = 0; // reserved
//cc.dcb.wReserved = 0; // not currently used
//cc.dcb.XonLim = 0; // transmit XON threshold
//cc.dcb.XoffLim = 0; // transmit XOFF threshold
cc.dcb.ByteSize = bits; // number of bits/byte, 4-8
cc.dcb.Parity = 0; // 0-4=no,odd,even,mark,space
if(parity == rlSerial::ODD) cc.dcb.Parity = 1;
if(parity == rlSerial::EVEN) cc.dcb.Parity = 2;
cc.dcb.StopBits = ONESTOPBIT; // 0,1,2 = 1, 1.5, 2
if(stopbits==2) cc.dcb.StopBits = TWOSTOPBITS;
//cc.dcb.XonChar = 0; // Tx and Rx XON character
//cc.dcb.XoffChar = 0; // Tx and Rx XOFF character
//cc.dcb.ErrorChar = 0; // error replacement character
//cc.dcb.EofChar = 0; // end of input character
//cc.dcb.EvtChar = 0; // received event character
//cc.dcb.wReserved1 = 0; // reserved; do not use
cc.dwProviderSubType = PST_RS232; // type of provider-specific data
//cc.dwProviderOffset = 0; // offset of provider-specific data
//cc.dwProviderSize = 0; // size of provider-specific data
//cc.wcProviderData[0] = 0; // provider-specific data
ret = SetCommConfig(hdl,&cc,sizeof(cc));
if(ret == 0)
{
printf("SetCommConfig ret=%d devicename=%s LastError=%d\n",ret,devicename,GetLastError());
return -1;
}
#endif
#ifdef RM3
RmEntryStruct CatEntry; /* Struktur der Deiviceinformationen */
int iStatus; /* Rckgabewert */
RmIOStatusStruct DrvSts; /* Struktur der Rckgabewerte fr RmIO - Funktion */
RmBytParmStruct PBlock; /* Parameterstruktur fr RmIO - Funktion */
static UCD_BYT_PORT Ucd_byt_drv; /* Struktur zum Setzen der UCD - Werte */
ushort uTimeBd; /* Timing - Wert der �ertragungsgeschwindigkeit */
uint uMode; /* Portsteuerungsparameter */
unsigned char cByte; /* Byte - Parameter */
/* Timing = 748800 / Baudrate; */
/**************************************************/
char byt_com[32];
/* COM1=0x3F8 COM2=0x2F8 - Port Adresse */
if (strcmp(devicename,"COM1") == 0)
{
strcpy(byt_com,"BYT_COM1");
com = 0x3f8;
}
else if(strcmp(devicename,"COM2") == 0)
{
strcpy(byt_com,"BYT_COM2");
com = 0x2f8;
}
else
{
printf("Error: devicename=%s unknown\n",devicename);
return -1;
}
//printf("Open COM port - inside\n");
/*
* Device und Unit - Id auslesen
*/
if( RmGetEntry( RM_WAIT, byt_com, &CatEntry ) != RM_OK ) /* RM_CONTINUE */
{
printf( "Error: %s device not found\n", byt_com);
return -1;
}
device = (int) ((ushort) CatEntry.ide);
unit = (int) CatEntry.id;
/*
* Ger� reservieren
*/
if( RmIO( BYT_RESERVE, (unsigned)(device), (unsigned)(unit), 0u, 0u, &DrvSts, &PBlock ) < 0 )
{
printf( "Error: Unable to reserve %s device\n", byt_com);
return -1;
}
/*
* Baudrate ausrechnen
*/
baudrate = 9600;
if(speed == B50 ) baudrate = 50;
if(speed == B75 ) baudrate = 75;
if(speed == B110 ) baudrate = 110;
if(speed == B134 ) baudrate = 134;
if(speed == B150 ) baudrate = 150;
if(speed == B200 ) baudrate = 200;
if(speed == B300 ) baudrate = 300;
if(speed == B600 ) baudrate = 600;
if(speed == B1200 ) baudrate = 1200;
if(speed == B1800 ) baudrate = 1800;
if(speed == B2400 ) baudrate = 2400;
if(speed == B4800 ) baudrate = 4800;
if(speed == B9600 ) baudrate = 9600;
if(speed == B19200 ) baudrate = 19200;
if(speed == B38400 ) baudrate = 38400;
if(speed == B57600 ) baudrate = 57600;
if(speed == B115200 ) baudrate = 115200;
if(speed == B230400 ) baudrate = 230400;
if(speed == B460800 ) baudrate = 460800;
if(speed == B500000 ) baudrate = 500000;
if(speed == B576000 ) baudrate = 576000;
if(speed == B921600 ) baudrate = 921600;
if(speed == B1000000) baudrate = 1000000;
if(speed == B1152000) baudrate = 1152000;
if(speed == B1500000) baudrate = 1500000;
if(speed == B2000000) baudrate = 2000000;
if(speed == B2500000) baudrate = 2500000;
if(speed == B3000000) baudrate = 3000000;
if(speed == B3500000) baudrate = 3500000;
if(speed == B4000000) baudrate = 4000000;
uTimeBd = 748800 / baudrate;
/*
* Portsteuerungsparameter setzen
*/
uMode = 0x1000 | DATA_8 | STOP_1 | NOPARITY;
/*
* UCD des seriellen Ports auslesen
*/
PBlock.string = 0;
PBlock.strlen = 0;
PBlock.buffer = (char *)&Ucd_byt_drv;
PBlock.timlen = sizeof(UCD_BYT_PORT);
PBlock.status = 0;
iStatus = RmIO( BYT_CREATE_NEW, (unsigned)(device), (unsigned)(unit), 0u, 0u, &DrvSts, &PBlock );
/*
* Modus �dern
*/
Ucd_byt_drv.mobyte[5] |= (ushort) (uMode & 0xFFu);
/*
* Timeout setzen
*/
Ucd_byt_drv.header.timout = timeout;
/*
* Werte zuweisen
*/
PBlock.string = (char*) &Ucd_byt_drv;
PBlock.strlen = sizeof(UCD_BYT_PORT);
PBlock.buffer = 0;
PBlock.timlen = 0;
PBlock.status = 0;
iStatus = RmIO( BYT_CREATE_NEW, (unsigned)(device), (unsigned)(unit), 0u, 0u, &DrvSts, &PBlock );
/*
* Register 0 und 1 zum Schreiben freigeben
*/
cByte = inbyte( com + 0x03 );
outbyte( com + 0x03, (unsigned char)(cByte | 0x80) );
/*
* Baudrate setzen
*/
outbyte( com + 0x00, (ushort) LOW (uTimeBd) );
outbyte( com + 0x01, (ushort) HIGH (uTimeBd) );
/*
* Register 0 und 1 sperren
*/
outbyte( com + 0x03, cByte );
if( iStatus ) printf( "BYT_CREATE_NEW (set ucb): Error status = %X\n", iStatus );
#endif
return 0;
}
int rlSerial::readChar()
{
#ifdef unix
int ret;
unsigned char buf[2];
if(fd == -1) return -1;
ret = read(fd,buf,1);
if(ret == 1) return buf[0];
if(ret == 0) return -2;
return -1;
#endif
#ifdef __VMS
unsigned char buf[2];
if(readBlock(buf, 1) < 0) return -1;
return buf[0];
#endif
#ifdef _WIN32
BOOL ret;
unsigned char buf[2];
unsigned long len;
ret = ReadFile(
hdl, // handle of file to read
buf, // pointer to buffer that receives data
1, // number of bytes to read
&len, // pointer to number of bytes read
NULL // pointer to structure for data
);
if(len > 0)
{
if(trace == 1) printf("readChar %d\n",(int) buf[0]);
return buf[0];
}
return -1;
#endif
#ifdef RM3
int ret;
unsigned char buf[2];
ret = readBlock(buf,1);
if(ret < 0) return ret;
return buf[0];
#endif
}
int rlSerial::writeChar(unsigned char uchar)
{
#ifdef unix
int ret;
if(fd == -1) return -1;
if(trace == 1) printf("writeChar %d\n",(int)uchar);
ret = write(fd,&uchar,1);
if(ret < 0) return -1;
//tcflush(fd, TCIOFLUSH);
return ret;
#endif
#ifdef __VMS
int status;
IOSB iosb;
status = SYS$QIOW(0,vms_channel,IO$_WRITEVBLK | IO$M_CANCTRLO | IO$M_NOFORMAT,
&iosb,0,0,&uchar,1,0,0,0,0);
if(status != SS$_NORMAL) return -1;
return 1;
#endif
#ifdef _WIN32
BOOL ret;
unsigned long len;
if(trace == 1) printf("writeChar %d\n",(int)uchar);
ret = WriteFile(
hdl, // handle to file to write to
&uchar, // pointer to data to write to file
1, // number of bytes to write
&len, // pointer to number of bytes written
NULL // pointer to structure for overlapped I/O
);
if(ret) return (int) len;
return -1;
#endif
#ifdef RM3
return writeBlock(&uchar, 1);
#endif
}
int rlSerial::readBlock(unsigned char *buf, int len)
{
#ifdef unix
int c;
for(int i=0; i<len; i++)
{
c = readChar();
if(c < 0) return c;
buf[i] = (unsigned char) c;
}
return len;
#endif
#ifdef __VMS
int status;
int timeout = 1; // second
short iosb[4];
status = SYS$QIOW(0,vms_channel,
IO$_READVBLK | IO$M_NOFILTR | IO$M_NOECHO | IO$M_TIMED,
iosb,0,0,buf,len,timeout,0,0,0);
if(status != SS$_NORMAL) return -1;
len=iosb[1];
if(iosb[2] != 0)
{
len++;
buf[len] = iosb[2];
}
return len;
#endif
#ifdef _WIN32
BOOL ret;
unsigned long retlen;
ret = ReadFile(
hdl, // handle of file to read
buf, // pointer to buffer that receives data
len, // number of bytes to read
&retlen, // pointer to number of bytes read
NULL // pointer to structure for data
);
if(retlen > 0)
{
if(trace == 1) printf("readBlock retlen=%d\n",retlen);
return (int) retlen;
}
return -1;
#endif
#ifdef RM3
RmBytParmStruct PBlock; /* Parameterstruktur fr RmIO - Funktion */
RmIOStatusStruct DrvSts; /* Struktur der Rckgabewerte fr RmIO - Funktion */
int iStatus; /* Rckgabewert */
int i; /* Schleifenz�ler */
/**************************************************/
/*
* Schreibparameter setzen
*/
PBlock.string = 0;
PBlock.strlen = 0;
PBlock.buffer = (char*)buf;
PBlock.timlen = len;
PBlock.status = 0;
/*
* Lesevorgang einleiten
*/
iStatus = RmIO( BYT_POLL_XBUF_WAIT, (unsigned)device, (unsigned)unit, 0u, 0u, &DrvSts, &PBlock );
if( iStatus ) printf( "BYT_POLL_XBUF_WAIT (set ucb): Error status = %X\n", iStatus );
if( !iStatus ) return len;
return -1;
#endif
}
int rlSerial::writeBlock(const unsigned char *buf, int len)
{
#ifdef unix
int ret;
if(fd == -1) return -1;
if(trace == 1)
{
printf("writeBlock:");
for(int i=0; i<len; i++) printf(" %d",(int) buf[i]);
printf("\n");
}
ret = write(fd,buf,len);
//tcflush(fd, TCIOFLUSH);
return ret;
#endif
#ifdef __VMS
int status;
IOSB iosb;
status = SYS$QIOW(0,vms_channel,IO$_WRITEVBLK | IO$M_CANCTRLO | IO$M_NOFORMAT,
&iosb,0,0,buf,len,0,0,0,0);
if(status != SS$_NORMAL) return -1;
return len;
#endif
#ifdef _WIN32
BOOL ret;
unsigned long retlen;
if(trace == 1)
{
printf("writeBlock:");
for(int i=0; i<len; i++) printf(" %d",(int) buf[i]);
printf("\n");
}
retlen = len;
ret = WriteFile(
hdl, // handle to file to write to
buf, // pointer to data to write to file
len, // number of bytes to write
&retlen, // pointer to number of bytes written
NULL // pointer to structure for overlapped I/O
);
if(ret) return (int) retlen;
return -1;
#endif
#ifdef RM3
RmBytParmStruct PBlock; /* Parameterstruktur fr RmIO - Funktion */
RmIOStatusStruct DrvSts; /* Struktur der Rckgabewerte fr RmIO - Funktion */
unsigned char cByte; /* Rckgabewert von ibyte - Funktion */
int iStatus; /* Rckgabewert */
int i; /* Schleifenz�ler */
/**************************************************/
/*
* Schreibparameter setzen
*/
PBlock.string = (char*)buf;
PBlock.strlen = len;
PBlock.buffer = 0;
PBlock.timlen = 0;
PBlock.status = 0;
/******************************************************
* *
* Toggle mode wird emuliert indem an dieser Stelle *
* RTS-Signal gesetzt und sp�er wieder gel�cht wird *
* *
******************************************************/
cByte = inbyte( com + 0x04u );
outbyte( com + 0x04, (unsigned char)(cByte | 0x02u) );
/*
* Schreibvorgang einleiten
*/
iStatus = RmIO( BYT_WRITE_WAIT, (unsigned)device, (unsigned)unit, 0u, 0u, &DrvSts, &PBlock );
/******************************************************************
* *
* 8ms warten.Bei der Aenderung der Uebertragungsgeschwindigkeit, *
* sollte dieser Wert angepasst werden. Hier fuer 9600 *
* *
******************************************************************/
RmPauseTask((RTS_TIME_WAIT*9600)/baudrate);
/*
* RTS-Signal l�chen
*/
outbyte( com + 0x04, (unsigned char)(cByte & 0xFDu) );
if( iStatus ) printf( "BYT_WRITE_WAIT (write block): Error status = %X\n", iStatus );
if( !iStatus ) return len;
return -1;
#endif
}
int rlSerial::readLine(unsigned char *buf, int maxlen)
{
int i,c,ret;
if(maxlen <= 1) return -1;
ret = 0;
buf[maxlen-1] = '\0';
for(i=0; i<maxlen-2; i++)
{
ret = i;
c = readChar();
if(c < 0)
{
buf[i] = '\0';
ret = c;
break;
}
buf[i] = (unsigned char) c;
if(c < ' ')
{
buf[i+1] = '\0';
break;
}
}
return ret;
}
int rlSerial::select(int timeout)
{
#ifdef unix
struct timeval timout;
fd_set wset,rset,eset;
int ret,maxfdp1;
if(timeout <= 0) return 1;
/* setup sockets to read */
maxfdp1 = fd+1;
FD_ZERO(&rset);
FD_SET (fd,&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
timout.tv_sec = timeout / 1000;
timout.tv_usec = timeout % 1000;
ret = ::select(maxfdp1,&rset,&wset,&eset,&timout);
if(ret == 0) return 0; /* timeout */
return 1;
#endif
#ifdef __VMS
return 1;
#endif
#ifdef _WIN32
COMMTIMEOUTS ctimeout;
ctimeout.ReadIntervalTimeout = timeout;
ctimeout.ReadTotalTimeoutMultiplier = 1;
ctimeout.ReadTotalTimeoutConstant = timeout;
ctimeout.WriteTotalTimeoutMultiplier = 1;
ctimeout.WriteTotalTimeoutConstant = timeout;
SetCommTimeouts(hdl, &ctimeout);
return 1;
#endif
#ifdef RM3
return 1;
#endif
}
int rlSerial::closeDevice()
{
#ifdef unix
if(fd == -1) return -1;
//if(::tcsetattr(fd,TCSAFLUSH,&save_termios) < 0) return -1;
if(::tcsetattr(fd,TCSANOW,&save_termios) < 0) return -1;
::close(fd);
ttystate = RESET;
fd = -1;
return 0;
#endif
#ifdef __VMS
sys$dassgn(vms_channel);
return 0;
#endif
#ifdef _WIN32
CloseHandle(hdl);
return 0;
#endif
#ifdef RM3
RmBytParmStruct PBlock; /* Parameterstruktur fr RmIO - Funktion */
RmIOStatusStruct DrvSts; /* Struktur der Rckgabewerte fr RmIO - Funktion */
/**************************************************/
if( RmIO( BYT_RELEASE, (unsigned)(device), (unsigned)(unit), 0u, 0u, &DrvSts, &PBlock ) < 0 )
{
printf( "Error: Unable to release device. device: %i, unit: %i\n",device, unit );
return -1;
}
return 0;
#endif
}