summaryrefslogtreecommitdiff
path: root/liblo/ser-to-osc/Serial.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'liblo/ser-to-osc/Serial.cpp')
-rw-r--r--liblo/ser-to-osc/Serial.cpp1428
1 files changed, 1428 insertions, 0 deletions
diff --git a/liblo/ser-to-osc/Serial.cpp b/liblo/ser-to-osc/Serial.cpp
new file mode 100644
index 0000000..d0c0c11
--- /dev/null
+++ b/liblo/ser-to-osc/Serial.cpp
@@ -0,0 +1,1428 @@
+// Serial.cpp - Implementation of the CSerial class
+//
+// Copyright (C) 1999-2003 Ramon de Klein (Ramon.de.Klein@ict.nl)
+//
+// 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.
+//
+// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+//////////////////////////////////////////////////////////////////////
+// Include the standard header files
+
+#define STRICT
+#include <crtdbg.h>
+#include <tchar.h>
+#include <windows.h>
+
+
+//////////////////////////////////////////////////////////////////////
+// Include module headerfile
+
+#include "Serial.h"
+
+
+//////////////////////////////////////////////////////////////////////
+// Disable warning C4127: conditional expression is constant, which
+// is generated when using the _RPTF and _ASSERTE macros.
+
+#pragma warning(disable: 4127)
+
+
+//////////////////////////////////////////////////////////////////////
+// Enable debug memory manager
+
+#ifdef _DEBUG
+
+#ifdef THIS_FILE
+#undef THIS_FILE
+#endif
+
+static const char THIS_FILE[] = __FILE__;
+#define new DEBUG_NEW
+
+#endif
+
+
+//////////////////////////////////////////////////////////////////////
+// Helper methods
+
+inline void CSerial::CheckRequirements (LPOVERLAPPED lpOverlapped, DWORD dwTimeout) const
+{
+#ifdef SERIAL_NO_OVERLAPPED
+
+ // Check if an overlapped structure has been specified
+ if (lpOverlapped || (dwTimeout != INFINITE))
+ {
+ // Quit application
+ ::MessageBox(0,_T("Overlapped I/O and time-outs are not supported, when overlapped I/O is disabled."),_T("Serial library"), MB_ICONERROR | MB_TASKMODAL);
+ ::DebugBreak();
+ ::ExitProcess(0xFFFFFFF);
+ }
+
+#endif
+
+#ifdef SERIAL_NO_CANCELIO
+
+ // Check if 0 or INFINITE time-out has been specified, because
+ // the communication I/O cannot be cancelled.
+ if ((dwTimeout != 0) && (dwTimeout != INFINITE))
+ {
+ // Quit application
+ ::MessageBox(0,_T("Timeouts are not supported, when SERIAL_NO_CANCELIO is defined"),_T("Serial library"), MB_ICONERROR | MB_TASKMODAL);
+ ::DebugBreak();
+ ::ExitProcess(0xFFFFFFF);
+ }
+
+#endif // SERIAL_NO_CANCELIO
+
+ // Avoid warnings
+ (void) dwTimeout;
+ (void) lpOverlapped;
+}
+
+inline BOOL CSerial::CancelCommIo (void)
+{
+#ifdef SERIAL_NO_CANCELIO
+ // CancelIo shouldn't have been called at this point
+ ::DebugBreak();
+ return FALSE;
+#else
+
+ // Cancel the I/O request
+ return ::CancelIo(m_hFile);
+
+#endif // SERIAL_NO_CANCELIO
+}
+
+
+//////////////////////////////////////////////////////////////////////
+// Code
+
+CSerial::CSerial ()
+ : m_lLastError(ERROR_SUCCESS)
+ , m_hFile(0)
+ , m_eEvent(EEventNone)
+ , m_dwEventMask(0)
+#ifndef SERIAL_NO_OVERLAPPED
+ , m_hevtOverlapped(0)
+#endif
+{
+}
+
+CSerial::~CSerial ()
+{
+ // If the device is already closed,
+ // then we don't need to do anything.
+ if (m_hFile)
+ {
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::~CSerial - Serial port not closed\n");
+
+ // Close implicitly
+ Close();
+ }
+}
+
+CSerial::EPort CSerial::CheckPort (LPCTSTR lpszDevice)
+{
+ // Try to open the device
+ HANDLE hFile = ::CreateFile(lpszDevice,
+ GENERIC_READ|GENERIC_WRITE,
+ 0,
+ 0,
+ OPEN_EXISTING,
+ 0,
+ 0);
+
+ // Check if we could open the device
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ // Display error
+ switch (::GetLastError())
+ {
+ case ERROR_FILE_NOT_FOUND:
+ // The specified COM-port does not exist
+ return EPortNotAvailable;
+
+ case ERROR_ACCESS_DENIED:
+ // The specified COM-port is in use
+ return EPortInUse;
+
+ default:
+ // Something else is wrong
+ return EPortUnknownError;
+ }
+ }
+
+ // Close handle
+ ::CloseHandle(hFile);
+
+ // Port is available
+ return EPortAvailable;
+}
+
+LONG CSerial::Open (LPCTSTR lpszDevice, DWORD dwInQueue, DWORD dwOutQueue, bool fOverlapped)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the port isn't already opened
+ if (m_hFile)
+ {
+ m_lLastError = ERROR_ALREADY_INITIALIZED;
+ _RPTF0(_CRT_WARN,"CSerial::Open - Port already opened\n");
+ return m_lLastError;
+ }
+
+ // Open the device
+ m_hFile = ::CreateFile(lpszDevice,
+ GENERIC_READ|GENERIC_WRITE,
+ 0,
+ 0,
+ OPEN_EXISTING,
+ fOverlapped?FILE_FLAG_OVERLAPPED:0,
+ 0);
+ if (m_hFile == INVALID_HANDLE_VALUE)
+ {
+ // Reset file handle
+ m_hFile = 0;
+
+ // Display error
+ m_lLastError = ::GetLastError();
+ _RPTF0(_CRT_WARN, "CSerial::Open - Unable to open port\n");
+ return m_lLastError;
+ }
+
+#ifndef SERIAL_NO_OVERLAPPED
+ // We cannot have an event handle yet
+ _ASSERTE(m_hevtOverlapped == 0);
+
+ // Create the event handle for internal overlapped operations (manual reset)
+ if (fOverlapped)
+ {
+ m_hevtOverlapped = ::CreateEvent(0,true,false,0);
+ if (m_hevtOverlapped == 0)
+ {
+ // Obtain the error information
+ m_lLastError = ::GetLastError();
+ _RPTF0(_CRT_WARN,"CSerial::Open - Unable to create event\n");
+
+ // Close the port
+ ::CloseHandle(m_hFile);
+ m_hFile = 0;
+
+ // Return the error
+ return m_lLastError;
+ }
+ }
+#else
+
+ // Overlapped flag shouldn't be specified
+ _ASSERTE(!fOverlapped);
+
+#endif
+
+ // Setup the COM-port
+ if (dwInQueue || dwOutQueue)
+ {
+ // Make sure the queue-sizes are reasonable sized. Win9X systems crash
+ // if the input queue-size is zero. Both queues need to be at least
+ // 16 bytes large.
+ _ASSERTE(dwInQueue >= 16);
+ _ASSERTE(dwOutQueue >= 16);
+
+ if (!::SetupComm(m_hFile,dwInQueue,dwOutQueue))
+ {
+ // Display a warning
+ long lLastError = ::GetLastError();
+ _RPTF0(_CRT_WARN,"CSerial::Open - Unable to setup the COM-port\n");
+
+ // Close the port
+ Close();
+
+ // Save last error from SetupComm
+ m_lLastError = lLastError;
+ return m_lLastError;
+ }
+ }
+
+ // Setup the default communication mask
+ SetMask();
+
+ // Non-blocking reads is default
+ SetupReadTimeouts(EReadTimeoutNonblocking);
+
+ // Setup the device for default settings
+ COMMCONFIG commConfig = {0};
+ DWORD dwSize = sizeof(commConfig);
+ commConfig.dwSize = dwSize;
+ if (::GetDefaultCommConfig(lpszDevice,&commConfig,&dwSize))
+ {
+ // Set the default communication configuration
+ if (!::SetCommConfig(m_hFile,&commConfig,dwSize))
+ {
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::Open - Unable to set default communication configuration.\n");
+ }
+ }
+ else
+ {
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::Open - Unable to obtain default communication configuration.\n");
+ }
+
+ // Return successful
+ return m_lLastError;
+}
+
+LONG CSerial::Close (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // If the device is already closed,
+ // then we don't need to do anything.
+ if (m_hFile == 0)
+ {
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::Close - Method called when device is not open\n");
+ return m_lLastError;
+ }
+
+#ifndef SERIAL_NO_OVERLAPPED
+ // Free event handle
+ if (m_hevtOverlapped)
+ {
+ ::CloseHandle(m_hevtOverlapped);
+ m_hevtOverlapped = 0;
+ }
+#endif
+
+ // Close COM port
+ ::CloseHandle(m_hFile);
+ m_hFile = 0;
+
+ // Return successful
+ return m_lLastError;
+}
+
+LONG CSerial::Setup (EBaudrate eBaudrate, EDataBits eDataBits, EParity eParity, EStopBits eStopBits)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::Setup - Device is not opened\n");
+ return m_lLastError;
+ }
+
+ // Obtain the DCB structure for the device
+ CDCB dcb;
+ if (!::GetCommState(m_hFile,&dcb))
+ {
+ // Obtain the error code
+ m_lLastError = :: GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::Setup - Unable to obtain DCB information\n");
+ return m_lLastError;
+ }
+
+ // Set the new data
+ dcb.BaudRate = DWORD(eBaudrate);
+ dcb.ByteSize = BYTE(eDataBits);
+ dcb.Parity = BYTE(eParity);
+ dcb.StopBits = BYTE(eStopBits);
+
+ // Determine if parity is used
+ dcb.fParity = (eParity != EParNone);
+
+ // Set the new DCB structure
+ if (!::SetCommState(m_hFile,&dcb))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::Setup - Unable to set DCB information\n");
+ return m_lLastError;
+ }
+
+ // Return successful
+ return m_lLastError;
+}
+
+LONG CSerial::SetEventChar (BYTE bEventChar, bool fAdjustMask)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::SetEventChar - Device is not opened\n");
+ return m_lLastError;
+ }
+
+ // Obtain the DCB structure for the device
+ CDCB dcb;
+ if (!::GetCommState(m_hFile,&dcb))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::SetEventChar - Unable to obtain DCB information\n");
+ return m_lLastError;
+ }
+
+ // Set the new event character
+ dcb.EvtChar = char(bEventChar);
+
+ // Adjust the event mask, to make sure the event will be received
+ if (fAdjustMask)
+ {
+ // Enable 'receive event character' event. Note that this
+ // will generate an EEventNone if there is an asynchronous
+ // WaitCommEvent pending.
+ SetMask(GetEventMask() | EEventRcvEv);
+ }
+
+ // Set the new DCB structure
+ if (!::SetCommState(m_hFile,&dcb))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::SetEventChar - Unable to set DCB information\n");
+ return m_lLastError;
+ }
+
+ // Return successful
+ return m_lLastError;
+}
+
+LONG CSerial::SetMask (DWORD dwEventMask)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::SetMask - Device is not opened\n");
+ return m_lLastError;
+ }
+
+ // Set the new mask. Note that this will generate an EEventNone
+ // if there is an asynchronous WaitCommEvent pending.
+ if (!::SetCommMask(m_hFile,dwEventMask))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::SetMask - Unable to set event mask\n");
+ return m_lLastError;
+ }
+
+ // Save event mask and return successful
+ m_dwEventMask = dwEventMask;
+ return m_lLastError;
+}
+
+LONG CSerial::WaitEvent (LPOVERLAPPED lpOverlapped, DWORD dwTimeout)
+{
+ // Check if time-outs are supported
+ CheckRequirements(lpOverlapped,dwTimeout);
+
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::WaitEvent - Device is not opened\n");
+ return m_lLastError;
+ }
+
+#ifndef SERIAL_NO_OVERLAPPED
+
+ // Check if an overlapped structure has been specified
+ if (!m_hevtOverlapped && (lpOverlapped || (dwTimeout != INFINITE)))
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_FUNCTION;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::WaitEvent - Overlapped I/O is disabled, specified parameters are illegal.\n");
+ return m_lLastError;
+ }
+
+ // Wait for the event to happen
+ OVERLAPPED ovInternal;
+ if (!lpOverlapped && m_hevtOverlapped)
+ {
+ // Setup our own overlapped structure
+ memset(&ovInternal,0,sizeof(ovInternal));
+ ovInternal.hEvent = m_hevtOverlapped;
+
+ // Use our internal overlapped structure
+ lpOverlapped = &ovInternal;
+ }
+
+ // Make sure the overlapped structure isn't busy
+ _ASSERTE(!m_hevtOverlapped || HasOverlappedIoCompleted(lpOverlapped));
+
+ // Wait for the COM event
+ if (!::WaitCommEvent(m_hFile,LPDWORD(&m_eEvent),lpOverlapped))
+ {
+ // Set the internal error code
+ long lLastError = ::GetLastError();
+
+ // Overlapped operation in progress is not an actual error
+ if (lLastError != ERROR_IO_PENDING)
+ {
+ // Save the error
+ m_lLastError = lLastError;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::WaitEvent - Unable to wait for COM event\n");
+ return m_lLastError;
+ }
+
+ // We need to block if the client didn't specify an overlapped structure
+ if (lpOverlapped == &ovInternal)
+ {
+ // Wait for the overlapped operation to complete
+ switch (::WaitForSingleObject(lpOverlapped->hEvent,dwTimeout))
+ {
+ case WAIT_OBJECT_0:
+ // The overlapped operation has completed
+ break;
+
+ case WAIT_TIMEOUT:
+ // Cancel the I/O operation
+ CancelCommIo();
+
+ // The operation timed out. Set the internal error code and quit
+ m_lLastError = ERROR_TIMEOUT;
+ return m_lLastError;
+
+ default:
+ // Set the internal error code
+ m_lLastError = ::GetLastError();
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::WaitEvent - Unable to wait until COM event has arrived\n");
+ return m_lLastError;
+ }
+ }
+ }
+ else
+ {
+ // The operation completed immediatly. Just to be sure
+ // we'll set the overlapped structure's event handle.
+ if (lpOverlapped)
+ ::SetEvent(lpOverlapped->hEvent);
+ }
+#else
+
+ // Wait for the COM event
+ if (!::WaitCommEvent(m_hFile,LPDWORD(&m_eEvent),0))
+ {
+ // Set the internal error code
+ m_lLastError = ::GetLastError();
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::WaitEvent - Unable to wait for COM event\n");
+ return m_lLastError;
+ }
+
+#endif
+
+ // Return successfully
+ return m_lLastError;
+}
+
+
+LONG CSerial::SetupHandshaking (EHandshake eHandshake)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::SetupHandshaking - Device is not opened\n");
+ return m_lLastError;
+ }
+
+ // Obtain the DCB structure for the device
+ CDCB dcb;
+ if (!::GetCommState(m_hFile,&dcb))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::SetupHandshaking - Unable to obtain DCB information\n");
+ return m_lLastError;
+ }
+
+ // Set the handshaking flags
+ switch (eHandshake)
+ {
+ case EHandshakeOff:
+ dcb.fOutxCtsFlow = false; // Disable CTS monitoring
+ dcb.fOutxDsrFlow = false; // Disable DSR monitoring
+ dcb.fDtrControl = DTR_CONTROL_DISABLE; // Disable DTR monitoring
+ dcb.fOutX = false; // Disable XON/XOFF for transmission
+ dcb.fInX = false; // Disable XON/XOFF for receiving
+ dcb.fRtsControl = RTS_CONTROL_DISABLE; // Disable RTS (Ready To Send)
+ break;
+
+ case EHandshakeHardware:
+ dcb.fOutxCtsFlow = true; // Enable CTS monitoring
+ dcb.fOutxDsrFlow = true; // Enable DSR monitoring
+ dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; // Enable DTR handshaking
+ dcb.fOutX = false; // Disable XON/XOFF for transmission
+ dcb.fInX = false; // Disable XON/XOFF for receiving
+ dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; // Enable RTS handshaking
+ break;
+
+ case EHandshakeSoftware:
+ dcb.fOutxCtsFlow = false; // Disable CTS (Clear To Send)
+ dcb.fOutxDsrFlow = false; // Disable DSR (Data Set Ready)
+ dcb.fDtrControl = DTR_CONTROL_DISABLE; // Disable DTR (Data Terminal Ready)
+ dcb.fOutX = true; // Enable XON/XOFF for transmission
+ dcb.fInX = true; // Enable XON/XOFF for receiving
+ dcb.fRtsControl = RTS_CONTROL_DISABLE; // Disable RTS (Ready To Send)
+ break;
+
+ default:
+ // This shouldn't be possible
+ _ASSERTE(false);
+ m_lLastError = E_INVALIDARG;
+ return m_lLastError;
+ }
+
+ // Set the new DCB structure
+ if (!::SetCommState(m_hFile,&dcb))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::SetupHandshaking - Unable to set DCB information\n");
+ return m_lLastError;
+ }
+
+ // Return successful
+ return m_lLastError;
+}
+
+LONG CSerial::SetupReadTimeouts (EReadTimeout eReadTimeout)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::SetupReadTimeouts - Device is not opened\n");
+ return m_lLastError;
+ }
+
+ // Determine the time-outs
+ COMMTIMEOUTS cto;
+ if (!::GetCommTimeouts(m_hFile,&cto))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::SetupReadTimeouts - Unable to obtain timeout information\n");
+ return m_lLastError;
+ }
+
+ // Set the new timeouts
+ switch (eReadTimeout)
+ {
+ case EReadTimeoutBlocking:
+ cto.ReadIntervalTimeout = 0;
+ cto.ReadTotalTimeoutConstant = 0;
+ cto.ReadTotalTimeoutMultiplier = 0;
+ break;
+ case EReadTimeoutNonblocking:
+ cto.ReadIntervalTimeout = MAXDWORD;
+ cto.ReadTotalTimeoutConstant = 0;
+ cto.ReadTotalTimeoutMultiplier = 0;
+ break;
+ default:
+ // This shouldn't be possible
+ _ASSERTE(false);
+ m_lLastError = E_INVALIDARG;
+ return m_lLastError;
+ }
+
+ // Set the new DCB structure
+ if (!::SetCommTimeouts(m_hFile,&cto))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::SetupReadTimeouts - Unable to set timeout information\n");
+ return m_lLastError;
+ }
+
+ // Return successful
+ return m_lLastError;
+}
+
+CSerial::EBaudrate CSerial::GetBaudrate (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::GetBaudrate - Device is not opened\n");
+ return EBaudUnknown;
+ }
+
+ // Obtain the DCB structure for the device
+ CDCB dcb;
+ if (!::GetCommState(m_hFile,&dcb))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::GetBaudrate - Unable to obtain DCB information\n");
+ return EBaudUnknown;
+ }
+
+ // Return the appropriate baudrate
+ return EBaudrate(dcb.BaudRate);
+}
+
+CSerial::EDataBits CSerial::GetDataBits (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::GetDataBits - Device is not opened\n");
+ return EDataUnknown;
+ }
+
+ // Obtain the DCB structure for the device
+ CDCB dcb;
+ if (!::GetCommState(m_hFile,&dcb))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::GetDataBits - Unable to obtain DCB information\n");
+ return EDataUnknown;
+ }
+
+ // Return the appropriate bytesize
+ return EDataBits(dcb.ByteSize);
+}
+
+CSerial::EParity CSerial::GetParity (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::GetParity - Device is not opened\n");
+ return EParUnknown;
+ }
+
+ // Obtain the DCB structure for the device
+ CDCB dcb;
+ if (!::GetCommState(m_hFile,&dcb))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::GetParity - Unable to obtain DCB information\n");
+ return EParUnknown;
+ }
+
+ // Check if parity is used
+ if (!dcb.fParity)
+ {
+ // No parity
+ return EParNone;
+ }
+
+ // Return the appropriate parity setting
+ return EParity(dcb.Parity);
+}
+
+CSerial::EStopBits CSerial::GetStopBits (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::GetStopBits - Device is not opened\n");
+ return EStopUnknown;
+ }
+
+ // Obtain the DCB structure for the device
+ CDCB dcb;
+ if (!::GetCommState(m_hFile,&dcb))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::GetStopBits - Unable to obtain DCB information\n");
+ return EStopUnknown;
+ }
+
+ // Return the appropriate stopbits
+ return EStopBits(dcb.StopBits);
+}
+
+DWORD CSerial::GetEventMask (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::GetEventMask - Device is not opened\n");
+ return 0;
+ }
+
+ // Return the event mask
+ return m_dwEventMask;
+}
+
+BYTE CSerial::GetEventChar (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::GetEventChar - Device is not opened\n");
+ return 0;
+ }
+
+ // Obtain the DCB structure for the device
+ CDCB dcb;
+ if (!::GetCommState(m_hFile,&dcb))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::GetEventChar - Unable to obtain DCB information\n");
+ return 0;
+ }
+
+ // Set the new event character
+ return BYTE(dcb.EvtChar);
+}
+
+CSerial::EHandshake CSerial::GetHandshaking (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::GetHandshaking - Device is not opened\n");
+ return EHandshakeUnknown;
+ }
+
+ // Obtain the DCB structure for the device
+ CDCB dcb;
+ if (!::GetCommState(m_hFile,&dcb))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::GetHandshaking - Unable to obtain DCB information\n");
+ return EHandshakeUnknown;
+ }
+
+ // Check if hardware handshaking is being used
+ if ((dcb.fDtrControl == DTR_CONTROL_HANDSHAKE) && (dcb.fRtsControl == RTS_CONTROL_HANDSHAKE))
+ return EHandshakeHardware;
+
+ // Check if software handshaking is being used
+ if (dcb.fOutX && dcb.fInX)
+ return EHandshakeSoftware;
+
+ // No handshaking is being used
+ return EHandshakeOff;
+}
+
+LONG CSerial::Write (const void* pData, size_t iLen, DWORD* pdwWritten, LPOVERLAPPED lpOverlapped, DWORD dwTimeout)
+{
+ // Check if time-outs are supported
+ CheckRequirements(lpOverlapped,dwTimeout);
+
+ // Overlapped operation should specify the pdwWritten variable
+ _ASSERTE(!lpOverlapped || pdwWritten);
+
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Use our own variable for read count
+ DWORD dwWritten;
+ if (pdwWritten == 0)
+ {
+ pdwWritten = &dwWritten;
+ }
+
+ // Reset the number of bytes written
+ *pdwWritten = 0;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::Write - Device is not opened\n");
+ return m_lLastError;
+ }
+
+#ifndef SERIAL_NO_OVERLAPPED
+
+ // Check if an overlapped structure has been specified
+ if (!m_hevtOverlapped && (lpOverlapped || (dwTimeout != INFINITE)))
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_FUNCTION;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::Write - Overlapped I/O is disabled, specified parameters are illegal.\n");
+ return m_lLastError;
+ }
+
+ // Wait for the event to happen
+ OVERLAPPED ovInternal;
+ if (!lpOverlapped && m_hevtOverlapped)
+ {
+ // Setup our own overlapped structure
+ memset(&ovInternal,0,sizeof(ovInternal));
+ ovInternal.hEvent = m_hevtOverlapped;
+
+ // Use our internal overlapped structure
+ lpOverlapped = &ovInternal;
+ }
+
+ // Make sure the overlapped structure isn't busy
+ _ASSERTE(!m_hevtOverlapped || HasOverlappedIoCompleted(lpOverlapped));
+
+ // Write the data
+ if (!::WriteFile(m_hFile,pData,iLen,pdwWritten,lpOverlapped))
+ {
+ // Set the internal error code
+ long lLastError = ::GetLastError();
+
+ // Overlapped operation in progress is not an actual error
+ if (lLastError != ERROR_IO_PENDING)
+ {
+ // Save the error
+ m_lLastError = lLastError;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::Write - Unable to write the data\n");
+ return m_lLastError;
+ }
+
+ // We need to block if the client didn't specify an overlapped structure
+ if (lpOverlapped == &ovInternal)
+ {
+ // Wait for the overlapped operation to complete
+ switch (::WaitForSingleObject(lpOverlapped->hEvent,dwTimeout))
+ {
+ case WAIT_OBJECT_0:
+ // The overlapped operation has completed
+ if (!::GetOverlappedResult(m_hFile,lpOverlapped,pdwWritten,FALSE))
+ {
+ // Set the internal error code
+ m_lLastError = ::GetLastError();
+
+ _RPTF0(_CRT_WARN,"CSerial::Write - Overlapped completed without result\n");
+ return m_lLastError;
+ }
+ break;
+
+ case WAIT_TIMEOUT:
+ // Cancel the I/O operation
+ CancelCommIo();
+
+ // The operation timed out. Set the internal error code and quit
+ m_lLastError = ERROR_TIMEOUT;
+ return m_lLastError;
+
+ default:
+ // Set the internal error code
+ m_lLastError = ::GetLastError();
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::Write - Unable to wait until data has been sent\n");
+ return m_lLastError;
+ }
+ }
+ }
+ else
+ {
+ // The operation completed immediatly. Just to be sure
+ // we'll set the overlapped structure's event handle.
+ if (lpOverlapped)
+ ::SetEvent(lpOverlapped->hEvent);
+ }
+
+#else
+
+ // Write the data
+ if (!::WriteFile(m_hFile,pData,iLen,pdwWritten,0))
+ {
+ // Set the internal error code
+ m_lLastError = ::GetLastError();
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::Write - Unable to write the data\n");
+ return m_lLastError;
+ }
+
+#endif
+
+ // Return successfully
+ return m_lLastError;
+}
+
+LONG CSerial::Write (LPCSTR pString, DWORD* pdwWritten, LPOVERLAPPED lpOverlapped, DWORD dwTimeout)
+{
+ // Check if time-outs are supported
+ CheckRequirements(lpOverlapped,dwTimeout);
+
+ // Determine the length of the string
+ return Write(pString,strlen(pString),pdwWritten,lpOverlapped,dwTimeout);
+}
+
+LONG CSerial::Read (void* pData, size_t iLen, DWORD* pdwRead, LPOVERLAPPED lpOverlapped, DWORD dwTimeout)
+{
+ // Check if time-outs are supported
+ CheckRequirements(lpOverlapped,dwTimeout);
+
+ // Overlapped operation should specify the pdwRead variable
+ _ASSERTE(!lpOverlapped || pdwRead);
+
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Use our own variable for read count
+ DWORD dwRead;
+ if (pdwRead == 0)
+ {
+ pdwRead = &dwRead;
+ }
+
+ // Reset the number of bytes read
+ *pdwRead = 0;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::Read - Device is not opened\n");
+ return m_lLastError;
+ }
+
+#ifdef _DEBUG
+ // The debug version fills the entire data structure with
+ // 0xDC bytes, to catch buffer errors as soon as possible.
+ memset(pData,0xDC,iLen);
+#endif
+
+#ifndef SERIAL_NO_OVERLAPPED
+
+ // Check if an overlapped structure has been specified
+ if (!m_hevtOverlapped && (lpOverlapped || (dwTimeout != INFINITE)))
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_FUNCTION;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::Read - Overlapped I/O is disabled, specified parameters are illegal.\n");
+ return m_lLastError;
+ }
+
+ // Wait for the event to happen
+ OVERLAPPED ovInternal;
+ if (lpOverlapped == 0)
+ {
+ // Setup our own overlapped structure
+ memset(&ovInternal,0,sizeof(ovInternal));
+ ovInternal.hEvent = m_hevtOverlapped;
+
+ // Use our internal overlapped structure
+ lpOverlapped = &ovInternal;
+ }
+
+ // Make sure the overlapped structure isn't busy
+ _ASSERTE(!m_hevtOverlapped || HasOverlappedIoCompleted(lpOverlapped));
+
+ // Read the data
+ if (!::ReadFile(m_hFile,pData,iLen,pdwRead,lpOverlapped))
+ {
+ // Set the internal error code
+ long lLastError = ::GetLastError();
+
+ // Overlapped operation in progress is not an actual error
+ if (lLastError != ERROR_IO_PENDING)
+ {
+ // Save the error
+ m_lLastError = lLastError;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::Read - Unable to read the data\n");
+ return m_lLastError;
+ }
+
+ // We need to block if the client didn't specify an overlapped structure
+ if (lpOverlapped == &ovInternal)
+ {
+ // Wait for the overlapped operation to complete
+ switch (::WaitForSingleObject(lpOverlapped->hEvent,dwTimeout))
+ {
+ case WAIT_OBJECT_0:
+ // The overlapped operation has completed
+ if (!::GetOverlappedResult(m_hFile,lpOverlapped,pdwRead,FALSE))
+ {
+ // Set the internal error code
+ m_lLastError = ::GetLastError();
+
+ _RPTF0(_CRT_WARN,"CSerial::Read - Overlapped completed without result\n");
+ return m_lLastError;
+ }
+ break;
+
+ case WAIT_TIMEOUT:
+ // Cancel the I/O operation
+ CancelCommIo();
+
+ // The operation timed out. Set the internal error code and quit
+ m_lLastError = ERROR_TIMEOUT;
+ return m_lLastError;
+
+ default:
+ // Set the internal error code
+ m_lLastError = ::GetLastError();
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::Read - Unable to wait until data has been read\n");
+ return m_lLastError;
+ }
+ }
+ }
+ else
+ {
+ // The operation completed immediatly. Just to be sure
+ // we'll set the overlapped structure's event handle.
+ if (lpOverlapped)
+ ::SetEvent(lpOverlapped->hEvent);
+ }
+
+#else
+
+ // Read the data
+ if (!::ReadFile(m_hFile,pData,iLen,pdwRead,0))
+ {
+ // Set the internal error code
+ m_lLastError = ::GetLastError();
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::Read - Unable to read the data\n");
+ return m_lLastError;
+ }
+
+#endif
+
+ // Return successfully
+ return m_lLastError;
+}
+
+LONG CSerial::Purge()
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::Purge - Device is not opened\n");
+ return m_lLastError;
+ }
+
+ if (!::PurgeComm(m_hFile, PURGE_TXCLEAR | PURGE_RXCLEAR))
+ {
+ // Set the internal error code
+ m_lLastError = ::GetLastError();
+ _RPTF0(_CRT_WARN,"CSerial::Purge - Overlapped completed without result\n");
+ }
+
+ // Return successfully
+ return m_lLastError;
+}
+
+LONG CSerial::Break (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::Break - Device is not opened\n");
+ return m_lLastError;
+ }
+
+ // Set the RS-232 port in break mode for a little while
+ ::SetCommBreak(m_hFile);
+ ::Sleep(100);
+ ::ClearCommBreak(m_hFile);
+
+ // Return successfully
+ return m_lLastError;
+}
+
+CSerial::EEvent CSerial::GetEventType (void)
+{
+#ifdef _DEBUG
+ // Check if the event is within the mask
+ if ((m_eEvent & m_dwEventMask) == 0)
+ _RPTF2(_CRT_WARN,"CSerial::GetEventType - Event %08Xh not within mask %08Xh.\n", m_eEvent, m_dwEventMask);
+#endif
+
+ // Obtain the event (mask unwanted events out)
+ EEvent eEvent = EEvent(m_eEvent & m_dwEventMask);
+
+ // Reset internal event type
+ m_eEvent = EEventNone;
+
+ // Return the current cause
+ return eEvent;
+}
+
+CSerial::EError CSerial::GetError (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Check if the device is open
+ if (m_hFile == 0)
+ {
+ // Set the internal error code
+ m_lLastError = ERROR_INVALID_HANDLE;
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::GetError - Device is not opened\n");
+ return EErrorUnknown;
+ }
+
+ // Obtain COM status
+ DWORD dwErrors = 0;
+ if (!::ClearCommError(m_hFile,&dwErrors,0))
+ {
+ // Set the internal error code
+ m_lLastError = ::GetLastError();
+
+ // Issue an error and quit
+ _RPTF0(_CRT_WARN,"CSerial::GetError - Unable to obtain COM status\n");
+ return EErrorUnknown;
+ }
+
+ // Return the error
+ return EError(dwErrors);
+}
+
+bool CSerial::GetCTS (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Obtain the modem status
+ DWORD dwModemStat = 0;
+ if (!::GetCommModemStatus(m_hFile,&dwModemStat))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::GetCTS - Unable to obtain the modem status\n");
+ return false;
+ }
+
+ // Determine if CTS is on
+ return (dwModemStat & MS_CTS_ON) != 0;
+}
+
+bool CSerial::GetDSR (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Obtain the modem status
+ DWORD dwModemStat = 0;
+ if (!::GetCommModemStatus(m_hFile,&dwModemStat))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::GetDSR - Unable to obtain the modem status\n");
+ return false;
+ }
+
+ // Determine if DSR is on
+ return (dwModemStat & MS_DSR_ON) != 0;
+}
+
+bool CSerial::GetRing (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Obtain the modem status
+ DWORD dwModemStat = 0;
+ if (!::GetCommModemStatus(m_hFile,&dwModemStat))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::GetRing - Unable to obtain the modem status");
+ return false;
+ }
+
+ // Determine if Ring is on
+ return (dwModemStat & MS_RING_ON) != 0;
+}
+
+bool CSerial::GetRLSD (void)
+{
+ // Reset error state
+ m_lLastError = ERROR_SUCCESS;
+
+ // Obtain the modem status
+ DWORD dwModemStat = 0;
+ if (!::GetCommModemStatus(m_hFile,&dwModemStat))
+ {
+ // Obtain the error code
+ m_lLastError = ::GetLastError();
+
+ // Display a warning
+ _RPTF0(_CRT_WARN,"CSerial::GetRLSD - Unable to obtain the modem status");
+ return false;
+ }
+
+ // Determine if RLSD is on
+ return (dwModemStat & MS_RLSD_ON) != 0;
+}