/*
* serchan.cxx
*
* Asynchronous serial I/O channel class implementation.
*
* Portable Windows Library
*
* Copyright (c) 1993-1998 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* $Log: serchan.cxx,v $
* Revision 1.32 2005/11/30 12:47:42 csoutheren
* Removed tabs, reformatted some code, and changed tags for Doxygen
*
* Revision 1.31 2005/03/10 03:27:30 dereksmithies
* Fix an address typo.
*
* Revision 1.30 2005/01/03 02:52:52 csoutheren
* Fixed problem with default speed of serial ports
* Fixed problem with using obsolete lock directory for serial ports
*
* Revision 1.29 2004/07/11 07:56:36 csoutheren
* Applied jumbo VxWorks patch, thanks to Eize Slange
*
* Revision 1.28 2004/02/22 04:06:47 ykiryanov
* ifdef'd all functions because BeOS don't support it
*
* Revision 1.27 2002/11/02 00:32:21 robertj
* Further fixes to VxWorks (Tornado) port, thanks Andreas Sikkema.
*
* Revision 1.26 2002/10/17 13:44:27 robertj
* Port to RTEMS, thanks Vladimir Nesic.
*
* Revision 1.25 2002/10/10 04:43:44 robertj
* VxWorks port, thanks Martijn Roest
*
* Revision 1.24 2002/03/27 06:42:16 robertj
* Implemented the DTR etc functions and ttya/ttyb strings for sunos,
* thanks tommi.korhonen@insta.fi & Raimo Ruokonen <rruokonen@koti.soon.fi>
*
* Revision 1.23 2001/09/10 03:03:36 robertj
* Major change to fix problem with error codes being corrupted in a
* PChannel when have simultaneous reads and writes in threads.
*
* Revision 1.22 2001/08/11 15:38:43 rogerh
* Add Mac OS Carbon changes from John Woods <jfw@jfwhome.funhouse.com>
*
* Revision 1.21 2001/01/04 17:57:41 rogerh
* Fix a cut and past error in my previous commit
*
* Revision 1.20 2001/01/04 10:28:07 rogerh
* FreeBSD does not set the Baud Rate with c_cflags. Add the 'BSD' way
*
* Revision 1.19 2001/01/03 10:56:01 rogerh
* CBAUD is not defined on FreeBSD.
*
* Revision 1.18 2000/12/29 07:36:18 craigs
* Finally got working correctly!
*
* Revision 1.17 2000/11/14 14:56:24 rogerh
* Fix #define parameters (fd should be just f)
*
* Revision 1.16 2000/11/14 14:52:32 rogerh
* Fix SET/GET typo error
*
* Revision 1.15 2000/11/12 23:30:41 craigs
* Fixed problems with serial port configuration
*
* Revision 1.14 2000/06/21 01:01:22 robertj
* AIX port, thanks Wolfgang Platzer (wolfgang.platzer@infonova.at).
*
* Revision 1.13 2000/04/09 18:19:23 rogerh
* Add my changes for NetBSD support.
*
* Revision 1.12 2000/04/06 12:11:32 rogerh
* MacOS X support submitted by Kevin Packard
*
* Revision 1.11 2000/03/08 12:17:09 rogerh
* Add OpenBSD support
*
* Revision 1.10 1998/12/21 06:08:08 robertj
* Fixed warning on solaris x86 GNU system.
*
* Revision 1.9 1998/11/30 21:51:54 robertj
* New directory structure.
*
* Revision 1.8 1998/11/24 09:39:14 robertj
* FreeBSD port.
*
* Revision 1.7 1998/09/24 04:12:17 robertj
* Added open software license.
*
*/
#pragma implementation "serchan.h"
#pragma implementation "modem.h"
#include <ptlib.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/ioctl.h>
#if defined(P_LINUX)
#define TCSETATTR(f,t) tcsetattr(f,TCSANOW,t)
#define TCGETATTR(f,t) tcgetattr(f,t)
#elif defined(P_FREEBSD) || defined(P_OPENBSD) || defined (P_NETBSD) || defined(P_MACOSX) || defined(P_MACOS) || defined(P_RTEMS)
#include <sys/ttycom.h>
#define TCGETA TIOCGETA
#define TCSETAW TIOCSETAW
#elif defined(P_SUN4)
#include <sys/termio.h>
extern "C" int ioctl(int, int, void *);
#elif defined (P_AIX)
#include <sys/termio.h>
#endif
#ifndef TCSETATTR
#define TCSETATTR(f,t) ::ioctl(f,TCSETAW,t)
#endif
#ifndef TCGETATTR
#define TCGETATTR(f,t) ::ioctl(f,TCGETA,t)
#endif
//#define BINARY_LOCK 1
//#define LOCK_PREFIX "/var/spool/uucp/LCK.."
#define LOCK_PREFIX "/var/lock/LCK.."
#define DEV_PREFIX "/dev/"
#define PORTLISTENV "PWLIB_SERIALPORTS"
#define DEV_PREFIX "/dev/"
#include "../common/serial.cxx"
////////////////////////////////////////////////////////////////
//
// PSerialChannel
//
void PSerialChannel::Construct()
{
// set control modes: 9600, N, 8, 1, local line
baudRate = 9600;
dataBits = 8;
parityBits = NoParity;
stopBits = 1;
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
#else
// set input mode: ignore breaks, ignore parity errors, do not strip chars,
// no CR/NL conversion, no case conversion, no XON/XOFF control,
// no start/stop
Termio.c_iflag = IGNBRK | IGNPAR;
Termio.c_cflag = CS8 | CSTOPB | CREAD | CLOCAL;
#if defined(P_FREEBSD) || defined(P_OPENBSD) || defined (P_NETBSD) || defined(P_MACOSX) || defined(P_MACOS)
Termio.c_ispeed = Termio.c_ospeed = B9600;
#else
Termio.c_cflag |= B9600;
#endif
// set output mode: no post process output,
Termio.c_oflag = 0;
// set line discipline
Termio.c_lflag = 0;
#endif // P_VXWORKS
}
BOOL PSerialChannel::Close()
{
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
return FALSE;
#else
if (os_handle >= 0) {
// delete the lockfile
PFile::Remove(PString(LOCK_PREFIX) + channelName);
// restore the original terminal settings
TCSETATTR(os_handle, &oldTermio);
}
return PChannel::Close();
#endif // P_VXWORKS
}
BOOL PSerialChannel::Open(const PString & port,
DWORD speed,
BYTE data,
Parity parity,
BYTE stop,
FlowControl inputFlow,
FlowControl outputFlow)
{
// if the port is already open, close it
if (IsOpen())
Close();
// // check prefix of name
// if (port.Left(PORT_PREFIX_LEN) != PORT_PREFIX) {
// lastError = BadParameter;
// return FALSE;
// }
// // check suffix
// int portnum = (port.Right(port.GetLength()-PORT_PREFIX_LEN)).AsInteger();
// if ((portnum < PORT_START) || (portnum >= (PORT_START + PORT_COUNT))) {
// lastError = BadParameter;
// return FALSE;
// }
// save the port name
channelName = port;
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
return FALSE;
#else
// construct lock filename
PString lockfilename = PString(LOCK_PREFIX) + port;
// if the file exists, probe the process to see if it is still running
if (PFile::Exists(lockfilename)) {
PFile lockfile(lockfilename, PFile::ReadOnly);
int lock_pid;
#ifdef BINARY_LOCK
lockfile.Read(&lock_pid, sizeof(lock_pid));
#else
char lock_pid_str[20];
lockfile.Read(lock_pid_str, 20);
lock_pid = atoi(lock_pid_str);
#endif
// if kill returns 0, then the port is in use
if (kill(lock_pid, 0) == 0)
return SetErrorValues(DeviceInUse, EBUSY);
// remove the lock file
lockfile.Remove();
}
// create new lockfile with our PID
PFile lockfile(lockfilename, PFile::WriteOnly, PFile::Create);
int pid = getpid();
#ifdef BINARY_LOCK
lockfile.Write(&pid, sizeof(pid));
#else
lockfile << pid;
#endif
lockfile.Close();
// attempt to open the device
PString device_name = PString(DEV_PREFIX) + port;
if ((os_handle = ::open((const char *)device_name, O_RDWR|O_NONBLOCK|O_NOCTTY)) < 0) {
ConvertOSError(os_handle);
Close();
return FALSE;
}
// save the channel name
channelName = port;
// save the current port setup
TCGETATTR(os_handle, &oldTermio);
// set the default paramaters
TCSETATTR(os_handle, &Termio);
// now set the mode that was passed in
if (!SetSpeed(speed) ||
!SetDataBits(data) ||
!SetParity(parity) ||
!SetStopBits(stop) ||
!SetInputFlowControl(inputFlow) ||
!SetOutputFlowControl(outputFlow)) {
errno = EINVAL;
ConvertOSError(-1);
return FALSE;
}
::fcntl(os_handle, F_SETFD, 1);
#endif // P_VXWORKS
return TRUE;
}
BOOL PSerialChannel::SetSpeed(DWORD newBaudRate)
{
if (newBaudRate == baudRate)
return TRUE;
if (os_handle < 0)
return TRUE;
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
return FALSE;
#else
int baud;
switch(newBaudRate) {
#ifdef B50
case 50:
baud = B50;
break;
#endif
#ifdef B75
case 75:
baud = B75;
break;
#endif
#ifdef B110
case 110:
baud = B110;
break;
#endif
#ifdef B134
case 134:
baud = B134;
break;
#endif
#ifdef B150
case 150:
baud = B150;
break;
#endif
#ifdef B200
case 200:
baud = B200;
break;
#endif
#ifdef B300
case 300:
baud = B300;
break;
#endif
#ifdef B600
case 600:
baud = B600;
break;
#endif
#ifdef B1200
case 1200:
baud = B1200;
break;
#endif
#ifdef B1800
case 1800:
baud = B1800;
break;
#endif
#ifdef B2400
case 2400:
baud = B2400;
break;
#endif
#ifdef B4800
case 4800:
baud = B4800;
break;
#endif
#ifdef B9600
case 9600:
case 0: // default
baud = B9600;
break;
#endif
#ifdef B19200
case 19200:
baud = B19200;
break;
#endif
#ifdef B38400
case 38400:
baud = B38400;
break;
#endif
#ifdef B57600
case 57600:
baud = B57600;
break;
#endif
#ifdef B115200
case 115200:
baud = B115200;
break;
#endif
#ifdef B230400
case 230400:
baud = B230400;
break;
#endif
default:
baud = -1;
};
if (baud == -1) {
errno = EINVAL;
ConvertOSError(-1);
return FALSE;
}
// save new baud rate
baudRate = newBaudRate;
#if defined(P_FREEBSD) || defined(P_OPENBSD) || defined (P_NETBSD) || defined(P_MACOSX) || defined(P_MACOS)
// The BSD way
Termio.c_ispeed = baud;
Termio.c_ospeed = baud;
#else
// The Linux way
Termio.c_cflag &= ~CBAUD;
Termio.c_cflag |= baud;
#endif
if (os_handle < 0)
return TRUE;
// initialise the port
return ConvertOSError(TCSETATTR(os_handle, &Termio));
#endif // P_VXWORKS
}
BOOL PSerialChannel::SetDataBits(BYTE data)
{
if (data == dataBits)
return TRUE;
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
return FALSE;
#else
int flags;
switch (data) {
#ifdef CS5
case 5:
flags = CS5;
break;
#endif
#ifdef CS6
case 6:
flags = CS6;
break;
#endif
#ifdef CS7
case 7:
flags = CS7;
break;
#endif
#ifdef CS8
case 8:
case 0: // default
flags = CS8;
break;
#endif
default:
flags = -1;
break;
}
if (flags == 0) {
errno = EINVAL;
ConvertOSError(-1);
return FALSE;
}
// set the new number of data bits
dataBits = data;
Termio.c_cflag &= ~CSIZE;
Termio.c_cflag |= flags;
if (os_handle < 0)
return TRUE;
return ConvertOSError(TCSETATTR(os_handle, &Termio));
#endif // P_VXWORKS
}
BOOL PSerialChannel::SetParity(Parity parity)
{
if (parity == parityBits)
return TRUE;
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
return FALSE;
#else
int flags;
switch (parity) {
case OddParity:
flags = PARODD | PARENB;
break;
case EvenParity:
flags = PARENB;
case NoParity:
case DefaultParity:
flags = IGNPAR;
break;
case MarkParity:
case SpaceParity:
default:
flags = -1;
}
if (flags < 0) {
errno = EINVAL;
ConvertOSError(-1);
return FALSE;
}
if (os_handle < 0)
return TRUE;
// set the new parity
parityBits = parity;
Termio.c_cflag &= ~(PARENB|PARODD);
Termio.c_cflag |= flags;
return ConvertOSError(TCSETATTR(os_handle, &Termio));
#endif // P_VXWORKS
}
BOOL PSerialChannel::SetStopBits(BYTE stop)
{
if (stop == stopBits)
return TRUE;
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
return FALSE;
#else
int flags;
switch (stop) {
case 2:
flags = CSTOPB;
break;
default:
case 1:
flags = 0;
break;
}
if (flags < 0) {
errno = EINVAL;
ConvertOSError(-1);
return FALSE;
}
if (os_handle < 0)
return TRUE;
// set the new number of stop bits
stopBits = stop;
Termio.c_cflag &= ~CSTOPB;
Termio.c_cflag |= flags;
return ConvertOSError(TCSETATTR(os_handle, &Termio));
#endif // P_VXWORKS
}
DWORD PSerialChannel::GetSpeed() const
{
return baudRate;
}
BYTE PSerialChannel::GetStopBits() const
{
return stopBits;
}
BYTE PSerialChannel::GetDataBits() const
{
return dataBits;
}
PSerialChannel::Parity PSerialChannel::GetParity() const
{
return parityBits;
}
BOOL PSerialChannel::SetInputFlowControl(FlowControl)
{
return TRUE;
}
PSerialChannel::FlowControl PSerialChannel::GetInputFlowControl() const
{
return NoFlowControl;
}
BOOL PSerialChannel::SetOutputFlowControl(FlowControl)
{
return TRUE;
}
PSerialChannel::FlowControl PSerialChannel::GetOutputFlowControl() const
{
return NoFlowControl;
}
void PSerialChannel::SetDTR(BOOL mode)
{
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
#else
int flags = 0;
ioctl(os_handle,TIOCMGET,&flags); // get the bits
flags &= ~TIOCM_DTR;
if ( mode == TRUE )
flags |= TIOCM_DTR;
ioctl(os_handle,TIOCMSET,&flags); // set back
/*
ALTERNATE IMPLEMENTATION?
Uses "Local Mode" bits?
if ( mode TRUE )
ioctl(os_handle, TIOCSDTR, 0);
else
ioctl(os_handle, TIOCCDTR, 0);
*/
#endif // P_VXWORKS
}
void PSerialChannel::SetRTS(BOOL mode)
{
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
#else
int flags = 0;
ioctl(os_handle,TIOCMGET,&flags); // get the bits
flags &= ~TIOCM_RTS;
if ( mode == TRUE )
flags |= TIOCM_RTS;
ioctl(os_handle,TIOCMSET,&flags); // set back
#endif // P_VXWORKS
}
void PSerialChannel::SetBreak(BOOL mode)
{
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
#else
if (mode)
ioctl(os_handle, TIOCSBRK, 0);
else
ioctl(os_handle, TIOCCBRK, 0);
#endif // P_VXWORKS
}
BOOL PSerialChannel::GetCTS()
{
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
return FALSE;
#else
int flags = 0;
ioctl(os_handle,TIOCMGET,&flags); // get the bits
return (flags&TIOCM_CTS)?TRUE:FALSE;
#endif // P_VXWORKS
}
BOOL PSerialChannel::GetDSR()
{
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
return FALSE;
#else
int flags = 0;
ioctl(os_handle,TIOCMGET,&flags); // get the bits
return (flags&TIOCM_DSR)?TRUE:FALSE;
#endif // P_VXWORKS
}
BOOL PSerialChannel::GetDCD()
{
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
return FALSE;
#else
int flags = 0;
ioctl(os_handle,TIOCMGET,&flags); // get the bits
return (flags&TIOCM_CD)?TRUE:FALSE;
#endif // P_VXWORKS
}
BOOL PSerialChannel::GetRing()
{
#if defined(P_VXWORKS) || defined (__BEOS__)
PAssertAlways(PUnimplementedFunction);
return FALSE;
#else
int flags = 0;
ioctl(os_handle,TIOCMGET,&flags); // get the bits
return (flags&TIOCM_RNG)?TRUE:FALSE;
#endif // P_VXWORKS
}
PStringList PSerialChannel::GetPortNames()
{
PStringList ports;
char * env = getenv(PORTLISTENV);
if (env != NULL) {
PString str(env);
PStringArray tokens = str.Tokenise(" ,\t", FALSE);
PINDEX i;
for (i = 0; i < tokens.GetSize(); i++)
ports.AppendString(tokens[i]);
} else {
#if defined(__sun) && defined (__sparc)
ports.AppendString(PString("ttya"));
ports.AppendString(PString("ttyb"));
#else
ports.AppendString(PString("ttyS0"));
ports.AppendString(PString("ttyS1"));
ports.AppendString(PString("ttyS2"));
ports.AppendString(PString("ttyS3"));
#endif
}
return ports;
}
// End of File ///////////////////////////////////////////////////////////////
syntax highlighted by Code2HTML, v. 0.9.1