diff options
Diffstat (limited to 'SrcUnix/EmTransportSerialUnix.cpp')
-rw-r--r-- | SrcUnix/EmTransportSerialUnix.cpp | 1211 |
1 files changed, 1211 insertions, 0 deletions
diff --git a/SrcUnix/EmTransportSerialUnix.cpp b/SrcUnix/EmTransportSerialUnix.cpp new file mode 100644 index 0000000..4ecaf4a --- /dev/null +++ b/SrcUnix/EmTransportSerialUnix.cpp @@ -0,0 +1,1211 @@ +/* -*- mode: C++; tab-width: 4 -*- */ +/* ===================================================================== *\ + Copyright (c) 2000-2001 Palm, Inc. or its subsidiaries. + All rights reserved. + + This file is part of the Palm OS Emulator. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +\* ===================================================================== */ + +#include "EmCommon.h" +#include "EmTransportSerialUnix.h" + +#include "Logging.h" // LogSerial +#include "Platform.h" // Platform::AllocateMemory + +#include <errno.h> // errno +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> // open(), close() +#include <termios.h> // struct termios + + +#define PRINTF if (!LogSerial ()) ; else LogAppendMsg + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostConstruct + * + * DESCRIPTION: Construct platform-specific objects/data. + * + * PARAMETERS: None + * + * RETURNED: The platform-specific serial object. + * + ***********************************************************************/ + +void EmTransportSerial::HostConstruct (void) +{ + fHost = new EmHostTransportSerial; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostDestruct + * + * DESCRIPTION: Destroy platform-specific objects/data. + * + * PARAMETERS: hostData - The platform-specific serial object. + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmTransportSerial::HostDestruct (void) +{ + delete fHost; + fHost = NULL; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostOpen + * + * DESCRIPTION: Open the serial port in a platform-specific fashion. + * + * PARAMETERS: None + * + * RETURNED: 0 if no error. + * + ***********************************************************************/ + +ErrCode EmTransportSerial::HostOpen (void) +{ + ErrCode err = fHost->OpenCommPort (fConfig); + + if (!err) + err = fHost->CreateCommThreads (fConfig); + + if (err) + this->HostClose (); + + return err; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostClose + * + * DESCRIPTION: Close the serial port in a platform-specific fashion. + * + * PARAMETERS: None + * + * RETURNED: 0 if no error. + * + ***********************************************************************/ + +ErrCode EmTransportSerial::HostClose (void) +{ + ErrCode err; + + err = fHost->DestroyCommThreads (); + err = fHost->CloseCommPort (); + + return err; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostRead + * + * DESCRIPTION: Read bytes from the port in a platform-specific fashion. + * + * PARAMETERS: len - maximum number of bytes to read. + * data - buffer to receive the bytes. + * + * RETURNED: 0 if no error. The number of bytes actually read is + * returned in len if there was no error. + * + ***********************************************************************/ + +ErrCode EmTransportSerial::HostRead (long& len, void* data) +{ + fHost->GetIncomingData (data, len); + + return errNone; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostWrite + * + * DESCRIPTION: Write bytes to the port in a platform-specific fashion. + * + * PARAMETERS: len - number of bytes in the buffer. + * data - buffer containing the bytes. + * + * RETURNED: 0 if no error. The number of bytes actually written is + * returned in len if there was no error. + * + ***********************************************************************/ + +ErrCode EmTransportSerial::HostWrite (long& len, const void* data) +{ + fHost->PutOutgoingData (data, len); + + return errNone; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostBytesInBuffer + * + * DESCRIPTION: Returns the number of bytes that can be read with the + * Read method. Note that bytes may be received in + * between the time BytesInBuffer is called and the time + * Read is called, so calling the latter with the result + * of the former is not guaranteed to fetch all received + * and buffered bytes. + * + * PARAMETERS: None + * + * RETURNED: Number of bytes that can be read. + * + ***********************************************************************/ + +long EmTransportSerial::HostBytesInBuffer (long /*minBytes*/) +{ + return fHost->IncomingDataSize (); +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostSetConfig + * + * DESCRIPTION: Configure the serial port in a platform-specific + * fashion. The port is assumed to be open. + * + * PARAMETERS: config - configuration information. + * + * RETURNED: 0 if no error. + * + ***********************************************************************/ + +ErrCode EmTransportSerial::HostSetConfig (const ConfigSerial& config) +{ + PRINTF ("EmTransportSerial::HostSetConfig: Setting settings."); + + ErrCode err = errNone; + + struct termios io; + + // Get the current settings. + + if (tcgetattr (fHost->fCommHandle, &io) == -1) // macro for ioctl (..., TCGETA, ...) call + { + err = errno; + return err; + } + + // One article on the net ("Serial Programming Guide for POSIX Compliant + // Operating Systems", <http://www.easysw.com/~mike/serial/serial.html>) + // recommends to *always* set these. + + io.c_cflag |= (CREAD | CLOCAL); + + // An execllent article on serial programming under UNIX ("Linux Serial Port + // Programming Mini-Howto") says to turn off these for "raw" (as opposed to + // "canonical") mode. + + io.c_lflag &= ~(ICANON | ECHO | ISIG); + + // The UNIX Programming FAQ (<www://www.faqs.org/faqs/unix-faq/programmer/faq/>) + // recommends just setting all the c_iflags and c_oflags to zero. + + io.c_iflag = io.c_oflag = 0; + + // Set the baud + + int hostBaud = fHost->GetBaud (config.fBaud); + cfsetospeed (&io, hostBaud); + cfsetispeed (&io, hostBaud); + + // Set the parity + + if (config.fParity == EmTransportSerial::kNoParity) + { + io.c_cflag &= ~PARENB; + } + else + { + io.c_cflag |= PARENB; + + if (config.fParity == EmTransportSerial::kOddParity) + { + io.c_cflag |= PARODD; + } + else + { + io.c_cflag &= ~PARODD; + } + } + + // Set the data bits + + io.c_cflag &= ~CSIZE; + io.c_cflag |= fHost->GetDataBits (config.fDataBits); + + // Set the stop bits + + if (config.fStopBits == 2) + { + io.c_cflag |= CSTOPB; + } + else + { + io.c_cflag &= ~CSTOPB; + } + + + // Set the hardware handshaking + // + // Note that I used to have the stuff in the #if 0. + // However, on the Windows side, Olivier Naudan argues: + // + // But I don't agree with: dcb.fRtsControl = config.fHwrHandshake ? + // RTS_CONTROL_HANDSHAKE : RTS_CONTROL_ENABLE; + // As hardware overrun can't be emulated, because of Poser timing that + // can't be accurate, I believe that fRtsControl should always be set to + // RTS_CONTROL_HANDSHAKE. So bytes will always arrive succesfully to host, + // and only software overrun could happen if PalmOS does not control the + // emulated RTS. + + /* + Additional commentary + + From Alexandre Duret-Lutz <aduret@enst.fr>: + + I have a Unix application which connect to a serial device, and + configure the serial line with the following parameter. + + ... + int fd = open ("/dev/ttyS0", O_RDWR); + ... + tty.c_cflag = CS8 | B9600 | CLOCAL | CREAD; // No CRTSCTS. + ... + tcsetattr (fd, TCSANOW, &tty); + ... + + I also have the same application adapted to the Palm, which + connect to the same device, and initialize the line with + + ... + UInt32 serFlags = srmSettingsFlagStopBits1 | srmSettingsFlagBitsPerChar8; + UInt16 serFlagsLen = sizeof serFlags; + ... + SrmOpen (serPortCradlePort, 9600, &port); + ... + SrmControl (port, srmCtlSetFlags, &serFlags, &serFlagsLen); + .... + + Both applications work fine. But while the latter work on the + Palm it doesn't work in Pose. (The data seems to be sent but the + serial device never answer and my appl eventually timeout on + SrmReceive.) + + Since 3.0a8, Pose is turning CRTSCTS on for all serial + connections, I beleive this is killing me. Indeed if I change + this behavior back to how it was in 3.0a7 my appl runs all fine. + + --- SrcUnix/EmTransportSerialUnix.cpp.old Wed Apr 11 14:29:45 2001 + +++ SrcUnix/EmTransportSerialUnix.cpp Wed Apr 11 14:30:00 2001 + @@ -283,7 +283,7 @@ + // and only software overrun could happen if PalmOS does not control the + // emulated RTS. + + -#if 0 + +#if 1 + if (config.fHwrHandshake) + { + io.c_cflag |= CRTSCTS; + + + I guess this might be related to Dave Sours' recent postings, + as his appl broke when he switched from 3.0a7 and 3.1. + -- + Alexandre Duret-Lutz + + To which Olivier replies: + + Keith, + + Apparently, the change in Unix UART emulation is not exactly equivallent + to the Windows one. The change I proposed activates RTS handshaking + only, not CTS handshaking, whereas the change in Unix version activate + both (io.c_cflag |= CRTSCTS) + + If opposite device does not handle flow control, or if the serial cable + is not completely wired, the CTS handshaking MUST be deactivated. + Otherwise, the host (Windows or Unix) will never send bytes to the other + device, because CTS will always be deasserted. + + On the other hand, RTS handshaking can be activated safely on the host. + This signal is taken into account by the other device, or simply ignored + if it does not handle RTS handshaking. + + So on the Unix version, the flag must also be set to RTS-only, if + possible. Otherwise, just revert the change. + + One point is not clear to me. The developper says: + + "The data seems to be sent but the serial device never answer and my + appl eventually timeout on SrmReceive." + + In fact, since the other device does not handle flow control, the data + must not be sent due to CTS deasserted. As a result, the SrmReceive + never get an answer. + + To check this, you may ask this developper to use a hardware serial spy + (a box with blinking LEDs indicating signal states) and check if the TD + (Transmit Data) led blink. It shouldn't. If it does, my guess is wrong. + Another option is to use a software serial spy. On Windows NT, I use + "PortMon". It shows low-level calls to NT serial driver. + + BTW I proposed to always activate RTS handshaking on the host because in + the past some other tasks running may slow Poser up to the point of an + overrun. With this change, it does not occur any more if the other + device handle flow control, and I think it is safe. + I can't imagine a device that does not handle flow control, but that + still rely on a particular state of CTS/RTS. + + Moreover, software overrun can still occur in the emulated UART if + PalmOS does not deassert RTS. Hardware overrun can't be emulated because + of intermediate buffering on the host and hazardous timing. + + To help developpers understand the change, I propose the following text: + + "dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; + + Whatever state of emulated UART is, we always activate RTS flow control + on the host. This would prevent host buffer overrun, in case the host is + busy and in case the opposite device handle RTS handshaking. This is + safe because if the opposite device does not handle flow control, it + will just ignore that signal. + + However, we activate CTS handshaking only if IGNORE_CTS register is not + set. If the opposite device does not handle CTS and if we rely on CTS + state, data will never be sent." + + To conclude, try using CTS-only flag on Unix version. If the CTS-only + flag does not exist, just revert the change. Also check the Macintosh + version. Ask the developper to investigate to check if data are really + sent or not. They shouldn't. + + Hope this helps :-) + + All of that is the long-winded explanation of why CRTSCTS is deasserted + when hardware handshaking is off. + */ + +#if 1 + if (config.fHwrHandshake) + { + io.c_cflag |= CRTSCTS; + } + else + { + io.c_cflag &= ~CRTSCTS; + } +#else + io.c_cflag |= CRTSCTS; +#endif + + // Write out the changed settings. + if (tcsetattr (fHost->fCommHandle, TCSANOW, &io) == -1) // macro for ioctl (TCSETS, &io); + { + err = errno; + return err; + } + + return err; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostSetRTS + * + * DESCRIPTION: . + * + * PARAMETERS: . + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmTransportSerial::HostSetRTS (RTSControl /*state*/) +{ +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostSetDTR + * + * DESCRIPTION: . + * + * PARAMETERS: . + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmTransportSerial::HostSetDTR (Bool /*state*/) +{ +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostSetBreak + * + * DESCRIPTION: . + * + * PARAMETERS: . + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmTransportSerial::HostSetBreak (Bool /*state*/) +{ +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostGetCTS + * + * DESCRIPTION: . + * + * PARAMETERS: . + * + * RETURNED: Nothing + * + ***********************************************************************/ + +Bool EmTransportSerial::HostGetCTS (void) +{ + return false; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostGetDSR + * + * DESCRIPTION: . + * + * PARAMETERS: . + * + * RETURNED: Nothing + * + ***********************************************************************/ + +Bool EmTransportSerial::HostGetDSR (void) +{ + return false; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostGetPortNameList + * + * DESCRIPTION: Return the list of serial ports on this computer. Used + * to prepare a menu of serial port choices. + * + * PARAMETERS: nameList - port names are added to this list. + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmTransportSerial::HostGetPortNameList (PortNameList& results) +{ + results.clear (); + +#ifdef __QNXNTO__ + results.push_back ("/dev/ser1"); + results.push_back ("/dev/ser2"); +#else + results.push_back ("/dev/ttyS0"); + results.push_back ("/dev/ttyS1"); + results.push_back ("/dev/ttyS2"); + results.push_back ("/dev/ttyS3"); +#endif +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSerial::HostGetSerialBaudList + * + * DESCRIPTION: Return the list of baud rates support by this computer. + * Used to prepare a menu of baud rate choices. + * + * PARAMETERS: baudList - baud rates are added to this list. + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmTransportSerial::HostGetSerialBaudList (BaudList& results) +{ + long maxBaud = 115200; // ::PrvGetMaxBaudRate ()? How to + // determine that on Unix?. + + switch (maxBaud) + { + case 115200: results.push_back (115200); + case 57600: results.push_back (57600); + case 38400: results.push_back (38400); + case 19200: results.push_back (19200); + case 9600: results.push_back (9600); + } +} + + +#pragma mark - + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial c'tor + * + * DESCRIPTION: Constructor. Initialize our data members. + * + * PARAMETERS: None + * + * RETURNED: Nothing + * + ***********************************************************************/ + +EmHostTransportSerial::EmHostTransportSerial (void) : + fReadThread (NULL), + fWriteThread (NULL), + fCommHandle (0), + fCommSignalPipeA (0), + fCommSignalPipeB (0), + fTimeToQuit (false), + fDataMutex (), + fDataCondition (&fDataMutex), + fReadMutex (), + fReadBuffer (), + fWriteMutex (), + fWriteBuffer () +{ +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial d'tor + * + * DESCRIPTION: Destructor. Delete our data members. + * + * PARAMETERS: None + * + * RETURNED: Nothing + * + ***********************************************************************/ + +EmHostTransportSerial::~EmHostTransportSerial (void) +{ + EmAssert (fReadThread == NULL); + EmAssert (fWriteThread == NULL); + EmAssert (fCommHandle == 0); + EmAssert (fCommSignalPipeA == 0); + EmAssert (fCommSignalPipeB == 0); +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::OpenCommPort + * + * DESCRIPTION: Open the serial port. + * + * PARAMETERS: config - data block describing which port to use. + * + * RETURNED: 0 if no error. + * + ***********************************************************************/ + +ErrCode EmHostTransportSerial::OpenCommPort (const EmTransportSerial::ConfigSerial& config) +{ + EmTransportSerial::PortName portName = config.fPort; + + PRINTF ("EmTransportSerial::HostOpen: attempting to open port \"%s\"", + portName.c_str()); + + if (!portName.empty ()) + { + PRINTF ("EmTransportSerial::HostOpen: Opening serial port..."); + + // An execllent article on serial programming under UNIX ("Linux Serial Port + // Programming Mini-Howto") says to set the following flags in the open call. + // The O_NDELAY is so that you can open the serial port without having DCD + // asserted. I'm not sure what the O_NOCTTY is for. + + fCommHandle = open(portName.c_str (), O_RDWR | O_NOCTTY | O_NDELAY); + + if (fCommHandle <= 0) + { + fCommHandle = 0; + + return errno; + } + } + else + { + PRINTF ("EmTransportSerial::HostOpen: No port selected in the Properties dialog box..."); + return -1; // !!! better error number + } + + return errNone; +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::CreateCommThreads + * + * DESCRIPTION: Create the threads that asynchronously read from and + * write to the serial port. + * + * PARAMETERS: config - data block describing which port to use. + * + * RETURNED: 0 if no error. + * + ***********************************************************************/ + +ErrCode EmHostTransportSerial::CreateCommThreads (const EmTransportSerial::ConfigSerial& /*config*/) +{ + if (fCommHandle) + { + PRINTF ("EmTransportSerial::HostOpen: Creating serial port handler threads..."); + + // Create the pipe used to communicate with CommRead. + + int filedes[] = { 0, 0 }; + if (pipe (filedes) == 0) + { + fCommSignalPipeA = filedes[0]; // for reading + fCommSignalPipeB = filedes[1]; // for writing + } + + // Create the threads and start them up. + + fTimeToQuit = false; + fReadThread = omni_thread::create (CommRead, this); + fWriteThread = omni_thread::create (CommWrite, this); + } + + return errNone; +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::DestroyCommThreads + * + * DESCRIPTION: Shutdown and destroy the comm threads. + * + * PARAMETERS: None. + * + * RETURNED: 0 if no error. + * + ***********************************************************************/ + +ErrCode EmHostTransportSerial::DestroyCommThreads (void) +{ + // If never created, nothing to destroy. + + if (!fCommSignalPipeA) + return errNone; + + // Signal the threads to quit. + + fDataMutex.lock (); + + fTimeToQuit = true; + + int dummy = 0; + write (fCommSignalPipeB, &dummy, sizeof (dummy)); // Signals CommRead. + + fDataCondition.broadcast (); // Signals CommWrite. + fDataMutex.unlock (); + + // Wait for the threads to quit. + + if (fReadThread) + { + fReadThread->join (NULL); + fWriteThread->join (NULL); + } + + // Thread objects delete themselves, so set our references to NULL. + + fReadThread = NULL; + fWriteThread = NULL; + + // Close the signal pipe. + + close (fCommSignalPipeA); + close (fCommSignalPipeB); + + fCommSignalPipeA = fCommSignalPipeB = 0; + + return errNone; +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::CloseCommPort + * + * DESCRIPTION: Close the comm port. + * + * PARAMETERS: None. + * + * RETURNED: 0 if no error. + * + ***********************************************************************/ + +ErrCode EmHostTransportSerial::CloseCommPort (void) +{ + (void) close (fCommHandle); + + fCommHandle = 0; + + return errNone; +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::PutIncomingData + * + * DESCRIPTION: Thread-safe method for adding data to the queue that + * holds data read from the serial port. + * + * PARAMETERS: data - pointer to the read data. + * len - number of bytes pointed to by "data". + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmHostTransportSerial::PutIncomingData (const void* data, long& len) +{ + if (len == 0) + return; + + omni_mutex_lock lock (fReadMutex); + + char* begin = (char*) data; + char* end = begin + len; + while (begin < end) + fReadBuffer.push_back (*begin++); +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::GetIncomingData + * + * DESCRIPTION: Thread-safe method for getting data from the queue + * holding data read from the serial port. + * + * PARAMETERS: data - pointer to buffer to receive data. + * len - on input, number of bytes available in "data". + * On exit, number of bytes written to "data". + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmHostTransportSerial::GetIncomingData (void* data, long& len) +{ + omni_mutex_lock lock (fReadMutex); + + if (len > (long) fReadBuffer.size ()) + len = (long) fReadBuffer.size (); + + char* p = (char*) data; + deque<char>::iterator begin = fReadBuffer.begin (); + deque<char>::iterator end = begin + len; + + copy (begin, end, p); + fReadBuffer.erase (begin, end); +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::IncomingDataSize + * + * DESCRIPTION: Thread-safe method returning the number of bytes in the + * read queue. + * + * PARAMETERS: None. + * + * RETURNED: Number of bytes in the read queue. + * + ***********************************************************************/ + +long EmHostTransportSerial::IncomingDataSize (void) +{ + omni_mutex_lock lock (fReadMutex); + + return fReadBuffer.size (); +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::PutOutgoingData + * + * DESCRIPTION: Thread-safe method for adding data to the queue that + * holds data to be written to the serial port. + * + * PARAMETERS: data - pointer to the read data. + * len - number of bytes pointed to by "data". + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmHostTransportSerial::PutOutgoingData (const void* data, long& len) +{ + if (len == 0) + return; + + omni_mutex_lock lock (fWriteMutex); + + char* begin = (char*) data; + char* end = begin + len; + while (begin < end) + fWriteBuffer.push_back (*begin++); + + // Wake up CommWrite. + + fDataMutex.lock (); + fDataCondition.broadcast (); + fDataMutex.unlock (); +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::GetOutgoingData + * + * DESCRIPTION: Thread-safe method for getting data from the queue + * holding data to be written to the serial port. + * + * PARAMETERS: data - pointer to buffer to receive data. + * len - on input, number of bytes available in "data". + * On exit, number of bytes written to "data". + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmHostTransportSerial::GetOutgoingData (void* data, long& len) +{ + omni_mutex_lock lock (fWriteMutex); + + if (len > (long) fWriteBuffer.size ()) + len = (long) fWriteBuffer.size (); + + char* p = (char*) data; + deque<char>::iterator begin = fWriteBuffer.begin (); + deque<char>::iterator end = begin + len; + + copy (begin, end, p); + fWriteBuffer.erase (begin, end); +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::OutgoingDataSize + * + * DESCRIPTION: Thread-safe method returning the number of bytes in the + * write queue. + * + * PARAMETERS: None. + * + * RETURNED: Number of bytes in the read queue. + * + ***********************************************************************/ + +long EmHostTransportSerial::OutgoingDataSize (void) +{ + omni_mutex_lock lock (fWriteMutex); + + return fWriteBuffer.size (); +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::CommRead + * + * DESCRIPTION: This function sits in its own thread, waiting for data + * to show up in the serial port. If data arrives, this + * function plucks it out and stores it in a thread-safe + * queue. It quits when it detects that the comm handle + * has been deleted. + * + * PARAMETERS: data - pointer to owning EmHostTransportSerial. + * + * RETURNED: Thread status. + * + ***********************************************************************/ + +void* EmHostTransportSerial::CommRead (void* data) +{ + EmHostTransportSerial* This = (EmHostTransportSerial*) data; + + PRINTF ("CommRead starting."); + + while (!This->fTimeToQuit) + { + int status; + int fd1 = This->fCommHandle; + int fd2 = This->fCommSignalPipeA; + int maxfd = max (fd1, fd2); + fd_set read_fds; + + FD_ZERO (&read_fds); + FD_SET (fd1, &read_fds); + FD_SET (fd2, &read_fds); + + status = select (maxfd + 1, &read_fds, NULL, NULL, NULL); + + if (This->fTimeToQuit) + break; + + if (status > 0) // data available + { + if (FD_ISSET (fd1, &read_fds)) + { + char buf[1024]; + int len = 1024; + len = read (fd1, buf, len); + + if (len == 0) + break; // port closed + + // Log the data. + if (LogSerialData ()) + LogAppendData (buf, len, "EmHostTransportSerial::CommRead: Received data:"); + else + PRINTF ("EmHostTransportSerial::CommRead: Received %ld serial bytes.", len); + + // Add the data to the EmHostTransportSerial object's buffer. + long n = (long) len; + This->PutIncomingData (buf, n); + } + } + } + + PRINTF ("CommRead exitting."); + + return NULL; +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::CommWrite + * + * DESCRIPTION: This function sits in its own thread, waiting for data + * to show up in the write queue. If data arrives, this + * function plucks it out and sends it out to the port. It + * quits when it detects that the comm handle has been + * deleted. + * + * PARAMETERS: data - pointer to owning EmHostTransportSerial. + * + * RETURNED: Thread status. + * + ***********************************************************************/ + +void* EmHostTransportSerial::CommWrite (void* data) +{ + EmHostTransportSerial* This = (EmHostTransportSerial*) data; + + PRINTF ("CommWrite starting."); + + omni_mutex_lock lock (This->fDataMutex); + + while (!This->fTimeToQuit) + { + This->fDataCondition.wait (); + + // It's the EmHostTransportSerial object telling us to quit. + + if (This->fTimeToQuit) + break; + + // Get the data to write. + + long len = This->OutgoingDataSize (); + + // If there really wasn't any, go back to sleep. + + if (len == 0) + continue; + + // Get the data. + + void* buf = Platform::AllocateMemory (len); + This->GetOutgoingData (buf, len); + + // Log the data. + + if (LogSerialData ()) + LogAppendData (buf, len, "EmHostTransportSerial::CommWrite: Transmitted data:"); + else + PRINTF ("EmHostTransportSerial::CommWrite: Transmitted %ld serial bytes.", len); + + // Write the data. + + ::write (This->fCommHandle, buf, len); + + // Dispose of the data. + + Platform::DisposeMemory (buf); + } + + PRINTF ("CommWrite exitting."); + + return NULL; +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::GetBaud + * + * DESCRIPTION: Map a baud rate into the Mac OS constant that represents + * that rate in a SerSettings call. + * + * PARAMETERS: baud - raw baud rate. + * + * RETURNED: Unix constant that represents that rate. + * + ***********************************************************************/ + +int EmHostTransportSerial::GetBaud (EmTransportSerial::Baud baud) +{ + switch (baud) + { +#if defined (B150) + case 150: PRINTF (" Baud = 150"); return B150; +#endif + +#if defined (B300) + case 300: PRINTF (" Baud = 300"); return B300; +#endif + +#if defined (B600) + case 600: PRINTF (" Baud = 600"); return B600; +#endif + +#if defined (B1200) + case 1200: PRINTF (" Baud = 1200"); return B1200; +#endif + +#if defined (B1800) + case 1800: PRINTF (" Baud = 1800"); return B1800; +#endif + +#if defined (B2400) + case 2400: PRINTF (" Baud = 2400"); return B2400; +#endif + +#if defined (B4800) + case 4800: PRINTF (" Baud = 4800"); return B4800; +#endif + +#if defined (B9600) + case 9600: PRINTF (" Baud = 9600"); return B9600; +#endif + +#if defined (B19200) + case 19200: PRINTF (" Baud = 19200"); return B19200; +#endif + +#if defined (B38400) + case 38400: PRINTF (" Baud = 38400"); return B38400; +#endif + +#if defined (B57600) + case 57600: PRINTF (" Baud = 57600"); return B57600; +#endif + +#if defined (B115200) + case 115200: PRINTF (" Baud = 115200"); return B115200; +#endif + +#if defined (B230400) + case 230400: PRINTF (" Baud = 230400"); return B230400; +#endif + } + + PRINTF (" Unknown Baud value: %ld.", (long) baud); + + // Not necessarily invalid. The UART often has invalid baud values while + // it's being initialized. It's also equally valid for a Palm application + // to attempt to set the baud to something the underlying OS doesn't support. + + // EmAssert (false); + + return baud; +} + + +/*********************************************************************** + * + * FUNCTION: EmHostTransportSerial::GetDataBits + * + * DESCRIPTION: Map a dataBits value into the Mac OS constant that + * represents that value in a SerSettings call. + * + * PARAMETERS: dataBits - raw data bits value. + * + * RETURNED: Unix constant that represents that dataBits value. + * + ***********************************************************************/ + +int EmHostTransportSerial::GetDataBits (EmTransportSerial::DataBits dataBits) +{ + switch (dataBits) + { + case 5: PRINTF (" dataBits = 5"); return CS5; + case 6: PRINTF (" dataBits = 6"); return CS6; + case 7: PRINTF (" dataBits = 7"); return CS7; + case 8: PRINTF (" dataBits = 8"); return CS8; + } + + PRINTF (" Unknown DataBits value."); + EmAssert (false); + return 0; +} + |