diff options
Diffstat (limited to 'liblo/ser-to-osc/Serial.cpp')
-rw-r--r-- | liblo/ser-to-osc/Serial.cpp | 1428 |
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; +} |