954 lines
28 KiB
C++
954 lines
28 KiB
C++
|
#include "precomp.h"
|
|||
|
DEBUG_FILEZONE(ZONE_T120_T123PSTN);
|
|||
|
|
|||
|
/* Comport.cpp
|
|||
|
*
|
|||
|
* Copyright (c) 1993-1995 by DataBeam Corporation, Lexington, KY
|
|||
|
*
|
|||
|
* Abstract:
|
|||
|
* This is the implementation file for the ComPort class. This class
|
|||
|
* controls a specific Windows Comm port. The main purpose of this class
|
|||
|
* is to put the Windows specific comm calls in one class.
|
|||
|
*
|
|||
|
* Private Instance Variables:
|
|||
|
* m_hCommLink - Handle returned by Windows when you open
|
|||
|
* a com port
|
|||
|
* Tx_Buffer_Size - Output buffer size, Win32 buffer
|
|||
|
* Byte_Count - Represents total number of bytes transmitted
|
|||
|
* over the comm port
|
|||
|
* Last_Byte_Count - We have a timer that expires every X
|
|||
|
* seconds. It reports the total number of
|
|||
|
* bytes transmitted if Last_Byte_Count is not
|
|||
|
* equal to the Byte_Count. This reduces the
|
|||
|
* number of prints that occur
|
|||
|
* m_cbReadBufferSize - Buffer size of the ComPort's internal
|
|||
|
* buffer size.
|
|||
|
* m_pbReadBuffer - Address of our own internal buffer.
|
|||
|
* m_nReadBufferOffset - Keeps track of the number of bytes read
|
|||
|
* by the user via DataIndication calls.
|
|||
|
* m_cbRead - Number of bytes read via the last Windows
|
|||
|
* ReadFile() call.
|
|||
|
* m_hevtPendingWrite - Event object used with Windows WriteFile()
|
|||
|
* call.
|
|||
|
* m_hevtPendingRead - Event object used with Windows ReadFile()
|
|||
|
* call.
|
|||
|
* Write_Event_Object - Pointer to EventObject structure used with
|
|||
|
* the WriteFile() call.
|
|||
|
* Read_Event_Object - Pointer to EventObject structure used with
|
|||
|
* the ReadFile() call.
|
|||
|
* RLSD_Event_Object - Pointer to EventObject structure used with
|
|||
|
* the WaitCommEvent() call.
|
|||
|
* m_WriteOverlapped - Overlapped I/O structure used with the
|
|||
|
* Write event.
|
|||
|
* m_ReadOverlapped - Overlapped I/O structure used with the
|
|||
|
* Read event.
|
|||
|
* Event_Mask - Windows mask that specifies the events
|
|||
|
* that we are interested in.
|
|||
|
* Read_Active - TRUE if a ReadFile() function is active.
|
|||
|
* Write_Active - TRUE if a WriteFile() function is active.
|
|||
|
* Higher_Layer - Pointer to higher ProtocolLayer layer
|
|||
|
* Port_Configuration - Pointer to PortConfiguration structure.
|
|||
|
* Default_Com_Timeouts - This structure holds the Com timeout values
|
|||
|
* that Win32 had set as the default values.
|
|||
|
* When we are finished with the port, we
|
|||
|
* will restore these values
|
|||
|
*
|
|||
|
* Caveats:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Authors:
|
|||
|
* James P. Galvin
|
|||
|
* James W. Lawwill
|
|||
|
*/
|
|||
|
#include "comport.h"
|
|||
|
|
|||
|
/*
|
|||
|
* ComPort::ComPort (
|
|||
|
* PTransportResources transport_resources,
|
|||
|
* IObject * owner_object,
|
|||
|
* ULONG message_base,
|
|||
|
* ULONG handle,
|
|||
|
* PPortConfiguration port_configuration,
|
|||
|
* PhysicalHandle physical_handle)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This is the constructor for the ComPort class. It initializes internal
|
|||
|
* variables from the configuration file.
|
|||
|
*/
|
|||
|
ComPort::ComPort
|
|||
|
(
|
|||
|
TransportController *owner_object,
|
|||
|
ULONG message_base,
|
|||
|
PLUGXPRT_PARAMETERS *pParams,
|
|||
|
PhysicalHandle hCommLink, // physical handle
|
|||
|
HANDLE hevtClose
|
|||
|
)
|
|||
|
:
|
|||
|
m_hCommLink(hCommLink),
|
|||
|
m_hevtClose(hevtClose),
|
|||
|
m_hevtPendingRead(NULL),
|
|||
|
m_hevtPendingWrite(NULL),
|
|||
|
m_hCommLink2(NULL), // two places can call Release, one in main thread, the other in worker thread by write event
|
|||
|
m_cRef(2),
|
|||
|
m_fClosed(FALSE)
|
|||
|
{
|
|||
|
m_pController = owner_object;
|
|||
|
m_nMsgBase = message_base;
|
|||
|
Automatic_Disconnect = FALSE;
|
|||
|
Count_Errors_On_ReadFile = 0;
|
|||
|
|
|||
|
m_hevtPendingRead = ::CreateEvent(NULL, TRUE, FALSE, NULL);
|
|||
|
m_hevtPendingWrite = ::CreateEvent(NULL, TRUE, FALSE, NULL);
|
|||
|
ASSERT(m_hevtPendingRead && m_hevtPendingWrite);
|
|||
|
|
|||
|
::ZeroMemory(&m_ReadOverlapped, sizeof(m_ReadOverlapped));
|
|||
|
m_ReadOverlapped.hEvent = m_hevtPendingRead;
|
|||
|
|
|||
|
::ZeroMemory(&m_WriteOverlapped, sizeof(m_WriteOverlapped));
|
|||
|
m_WriteOverlapped.hEvent = m_hevtPendingWrite;
|
|||
|
|
|||
|
/*
|
|||
|
** Initialize internal variables
|
|||
|
*/
|
|||
|
Byte_Count = 0;
|
|||
|
Last_Byte_Count = 0;
|
|||
|
|
|||
|
m_pbReadBuffer = NULL;
|
|||
|
Read_Active = FALSE;
|
|||
|
m_nReadBufferOffset = 0;
|
|||
|
Read_Event_Object = NULL;
|
|||
|
|
|||
|
Write_Active = FALSE;
|
|||
|
Write_Event_Object = NULL;
|
|||
|
|
|||
|
DCB dcb;
|
|||
|
::ZeroMemory(&dcb, sizeof(dcb));
|
|||
|
if (::GetCommState(m_hCommLink, &dcb)) // address of communications properties structure
|
|||
|
{
|
|||
|
Baud_Rate = dcb.BaudRate;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Baud_Rate = DEFAULT_BAUD_RATE;
|
|||
|
}
|
|||
|
|
|||
|
// default settings
|
|||
|
Call_Control_Type = DEFAULT_PSTN_CALL_CONTROL;
|
|||
|
Tx_Buffer_Size = DEFAULT_TX_BUFFER_SIZE;
|
|||
|
Rx_Buffer_Size = DEFAULT_RX_BUFFER_SIZE;
|
|||
|
m_cbReadBufferSize = DEFAULT_INTERNAL_RX_BUFFER_SIZE;
|
|||
|
|
|||
|
// get new parameters
|
|||
|
if (NULL != pParams)
|
|||
|
{
|
|||
|
if (PSTN_PARAM__CALL_CONTROL & pParams->dwFlags)
|
|||
|
{
|
|||
|
Call_Control_Type = pParams->eCallControl;
|
|||
|
}
|
|||
|
if (PSTN_PARAM__READ_FILE_BUFFER_SIZE & pParams->dwFlags)
|
|||
|
{
|
|||
|
if (1024 <= pParams->cbReadFileBufferSize)
|
|||
|
{
|
|||
|
m_cbReadBufferSize = pParams->cbReadFileBufferSize;
|
|||
|
}
|
|||
|
}
|
|||
|
if (PSTN_PARAM__PHYSICAL_LAYER_SEND_BUFFER_SIZE & pParams->dwFlags)
|
|||
|
{
|
|||
|
if (DEFAULT_TX_BUFFER_SIZE <= pParams->cbPhysicalLayerSendBufferSize)
|
|||
|
{
|
|||
|
Tx_Buffer_Size = pParams->cbPhysicalLayerSendBufferSize;
|
|||
|
}
|
|||
|
}
|
|||
|
if (PSTN_PARAM__PHSYICAL_LAYER_RECV_BUFFER_SIZE & pParams->dwFlags)
|
|||
|
{
|
|||
|
if (1024 <= pParams->cbPhysicalLayerReceiveBufferSize)
|
|||
|
{
|
|||
|
Rx_Buffer_Size = pParams->cbPhysicalLayerReceiveBufferSize;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ComPort::~ComPort (void)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This is the destructor for the Comport class. It releases all memory
|
|||
|
* that was used by the class and deletes all timers. It also closes the
|
|||
|
* com port
|
|||
|
*/
|
|||
|
typedef BOOL (WINAPI *LPFN_CANCEL_IO) (HANDLE);
|
|||
|
ComPort::~ComPort(void)
|
|||
|
{
|
|||
|
// hopefully the worker thread is able to clean up all the read and write operations
|
|||
|
delete [] m_pbReadBuffer;
|
|||
|
m_pbReadBuffer = NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
LONG ComPort::Release(void)
|
|||
|
{
|
|||
|
Close ();
|
|||
|
|
|||
|
HINSTANCE hLib = ::LoadLibrary("kernel32.dll");
|
|||
|
if (NULL != hLib)
|
|||
|
{
|
|||
|
LPFN_CANCEL_IO pfnCancelIo = (LPFN_CANCEL_IO) ::GetProcAddress(hLib, "CancelIo");
|
|||
|
if (NULL != pfnCancelIo)
|
|||
|
{
|
|||
|
(*pfnCancelIo)(m_hCommLink2);
|
|||
|
}
|
|||
|
::FreeLibrary(hLib);
|
|||
|
}
|
|||
|
|
|||
|
COMMTIMEOUTS com_timeouts, com_timeouts_save;
|
|||
|
if (::GetCommTimeouts(m_hCommLink2, &com_timeouts_save))
|
|||
|
{
|
|||
|
/*
|
|||
|
** We are setting these timeout values to 0 because we were
|
|||
|
** getting a VxD fault under Windows 95 when they were set to
|
|||
|
** their normal values.
|
|||
|
*/
|
|||
|
::ZeroMemory(&com_timeouts, sizeof(com_timeouts));
|
|||
|
::SetCommTimeouts(m_hCommLink2, &com_timeouts);
|
|||
|
|
|||
|
/*
|
|||
|
** Abort any ReadFile() or WriteFile() operations
|
|||
|
*/
|
|||
|
::PurgeComm(m_hCommLink2, PURGE_TXABORT | PURGE_RXABORT);
|
|||
|
|
|||
|
/*
|
|||
|
** Set the timeouts to their original state
|
|||
|
*/
|
|||
|
::SetCommTimeouts(m_hCommLink2, &com_timeouts_save);
|
|||
|
}
|
|||
|
|
|||
|
// decrement the reference count
|
|||
|
if (! ::InterlockedDecrement(&m_cRef))
|
|||
|
{
|
|||
|
delete this;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
return m_cRef;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ComPortError ComPort::Open (void)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function opens the comm port and configures it with the values
|
|||
|
* found in the configuration object.
|
|||
|
*/
|
|||
|
ComPortError ComPort::Open(void)
|
|||
|
{
|
|||
|
BOOL fRet;
|
|||
|
ComPortError rc;
|
|||
|
|
|||
|
TRACE_OUT(("ComPort:: TX size = %d RX size = %d Int Rx Size = %d",
|
|||
|
Tx_Buffer_Size, Rx_Buffer_Size, m_cbReadBufferSize));
|
|||
|
|
|||
|
if (NULL == m_hevtPendingRead || NULL == m_hevtPendingWrite)
|
|||
|
{
|
|||
|
ERROR_OUT(("ComPort: Error create pending read/write events"));
|
|||
|
ReportInitializationFailure(COMPORT_INITIALIZATION_FAILED);
|
|||
|
return (COMPORT_INITIALIZATION_FAILED);
|
|||
|
}
|
|||
|
|
|||
|
// allocate read buffer
|
|||
|
TRACE_OUT(("Comport: Internal Rx Buffer Size = %ld", m_cbReadBufferSize));
|
|||
|
DBG_SAVE_FILE_LINE
|
|||
|
m_pbReadBuffer = new BYTE[m_cbReadBufferSize];
|
|||
|
m_nReadBufferOffset = 0;
|
|||
|
if (m_pbReadBuffer == NULL)
|
|||
|
{
|
|||
|
ERROR_OUT(("ComPort: Error allocating memory = %d", ::GetLastError()));
|
|||
|
ReportInitializationFailure(COMPORT_INITIALIZATION_FAILED);
|
|||
|
return (COMPORT_INITIALIZATION_FAILED);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Issue a read to the com port.
|
|||
|
** We are going to continue to issue Readfile() calls
|
|||
|
** until we get into a wait state. 99.9999% of the
|
|||
|
** time, we will only issue the first ReadFile() and
|
|||
|
** it will immediately block waiting for data.
|
|||
|
*/
|
|||
|
while (1)
|
|||
|
{
|
|||
|
m_cbRead = 0;
|
|||
|
fRet = ::ReadFile(m_hCommLink, m_pbReadBuffer, m_cbReadBufferSize, &m_cbRead, &m_ReadOverlapped);
|
|||
|
if (! fRet)
|
|||
|
{
|
|||
|
DWORD dwErr = ::GetLastError();
|
|||
|
if (dwErr == ERROR_IO_PENDING)
|
|||
|
{
|
|||
|
Read_Active = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ERROR_OUT(("ComPort: Error on ReadFile = %d", dwErr));
|
|||
|
ReportInitializationFailure(COMPORT_INITIALIZATION_FAILED);
|
|||
|
return (COMPORT_INITIALIZATION_FAILED);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** If this is a synchronous read, wait for the event object to be
|
|||
|
** set before returning.
|
|||
|
*/
|
|||
|
if (Call_Control_Type == PLUGXPRT_PSTN_CALL_CONTROL_MANUAL)
|
|||
|
{
|
|||
|
::WaitForSingleObject(m_hevtPendingRead, SYNCHRONOUS_WRITE_TIMEOUT*10);
|
|||
|
fRet = GetOverlappedResult(m_hCommLink, &m_ReadOverlapped, &m_cbRead, FALSE);
|
|||
|
if (! fRet)
|
|||
|
{
|
|||
|
::PurgeComm(m_hCommLink, PURGE_RXABORT);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Create and fill in the EventObject. It is then
|
|||
|
** appended to the PSTN Event_List so that the EventManager
|
|||
|
** can wait for the event to occur.
|
|||
|
*/
|
|||
|
DBG_SAVE_FILE_LINE
|
|||
|
Read_Event_Object = new EventObject;
|
|||
|
Read_Event_Object -> event = m_hevtPendingRead;
|
|||
|
Read_Event_Object -> delete_event = FALSE;
|
|||
|
Read_Event_Object -> comport = this;
|
|||
|
Read_Event_Object -> hCommLink = m_hCommLink;
|
|||
|
Read_Event_Object -> event_type = READ_EVENT;
|
|||
|
g_pPSTNEventList->append((DWORD_PTR) Read_Event_Object);
|
|||
|
g_fEventListChanged = TRUE;
|
|||
|
|
|||
|
Write_Active = FALSE;
|
|||
|
|
|||
|
/*
|
|||
|
** Create and fill in the EventObject. It is then
|
|||
|
** appended to the PSTN Event_List so that the EventManager
|
|||
|
** can wait for the event to occur.
|
|||
|
*/
|
|||
|
DBG_SAVE_FILE_LINE
|
|||
|
Write_Event_Object = new EventObject;
|
|||
|
Write_Event_Object -> event = m_hevtPendingWrite;
|
|||
|
Write_Event_Object -> delete_event = FALSE;
|
|||
|
Write_Event_Object -> comport = this;
|
|||
|
Write_Event_Object -> hCommLink = m_hCommLink;
|
|||
|
Write_Event_Object -> event_type = WRITE_EVENT;
|
|||
|
g_pPSTNEventList->append((DWORD_PTR) Write_Event_Object);
|
|||
|
g_fEventListChanged = TRUE;
|
|||
|
|
|||
|
return (COMPORT_NO_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ComPortError ComPort::Close (void)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function makes the necessary Windows calls to close the Com
|
|||
|
* port. It first clears the DTR signal to notify the modem.
|
|||
|
*/
|
|||
|
ComPortError ComPort::Close(void)
|
|||
|
{
|
|||
|
if (! m_fClosed)
|
|||
|
{
|
|||
|
m_fClosed = TRUE;
|
|||
|
|
|||
|
/*
|
|||
|
** Reset the Activity flags.
|
|||
|
*/
|
|||
|
Write_Active = FALSE;
|
|||
|
Read_Active = FALSE;
|
|||
|
|
|||
|
// we do not close the handle here, T.120 will do it.
|
|||
|
m_hCommLink2 = m_hCommLink;
|
|||
|
m_hCommLink = INVALID_HANDLE_VALUE;
|
|||
|
|
|||
|
/*
|
|||
|
** Notify the event manager that these events need to be deleted.
|
|||
|
** It is important for the event manager to realize that when the
|
|||
|
** delete_event is set to TRUE, he can no longer access this object.
|
|||
|
*/
|
|||
|
if (Write_Event_Object != NULL)
|
|||
|
{
|
|||
|
::CloseHandle(Write_Event_Object->event);
|
|||
|
g_pPSTNEventList->remove((DWORD_PTR) Write_Event_Object);
|
|||
|
delete Write_Event_Object;
|
|||
|
}
|
|||
|
|
|||
|
if (Read_Event_Object != NULL)
|
|||
|
{
|
|||
|
Read_Event_Object -> delete_event = TRUE;
|
|||
|
::SetEvent(m_hevtPendingRead);
|
|||
|
}
|
|||
|
|
|||
|
// let the worker thread to pick up the work
|
|||
|
::Sleep(50);
|
|||
|
}
|
|||
|
|
|||
|
return COMPORT_NO_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ComPortError ComPort::Reset (void)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function clears the DTR signal on the Com port.
|
|||
|
*/
|
|||
|
ComPortError ComPort::Reset(void)
|
|||
|
{
|
|||
|
return COMPORT_NO_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ComPortError ComPort::ReleaseReset (void)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function releases the previous reset. It set the DTR signal on
|
|||
|
* the com port.
|
|||
|
*/
|
|||
|
ComPortError ComPort::ReleaseReset(void)
|
|||
|
{
|
|||
|
return COMPORT_NO_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ULONG ComPort::GetBaudRate (void)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function returns the baud rate of the port
|
|||
|
*/
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ProtocolLayerError ComPort::DataRequest (
|
|||
|
* ULONG,
|
|||
|
* LPBYTE buffer_address,
|
|||
|
* ULONG length,
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function is called to send data out the port in an asynchronous
|
|||
|
* manner. In other words, we will return from the function before all
|
|||
|
* of the bytes are actually written to the modem.
|
|||
|
*/
|
|||
|
ProtocolLayerError ComPort::DataRequest(ULONG_PTR,
|
|||
|
LPBYTE buffer_address,
|
|||
|
ULONG length,
|
|||
|
ULONG *bytes_accepted)
|
|||
|
{
|
|||
|
return WriteData(FALSE, buffer_address, length, bytes_accepted);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* ProtocolLayerError ComPort::SynchronousDataRequest (
|
|||
|
* LPBYTE buffer_address,
|
|||
|
* ULONG length,
|
|||
|
* PULong bytes_accepted)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function is called to send data out the port in a synchronous
|
|||
|
* manner. In other words, we will not return from the function until
|
|||
|
* all of the bytes are actually written to the modem or a timeout occurs.
|
|||
|
*/
|
|||
|
ProtocolLayerError ComPort::SynchronousDataRequest(
|
|||
|
LPBYTE buffer_address,
|
|||
|
ULONG length,
|
|||
|
ULONG *bytes_accepted)
|
|||
|
{
|
|||
|
return WriteData(TRUE, buffer_address, length, bytes_accepted);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ProtocolLayerError ComPort::WriteData (
|
|||
|
* BOOL synchronous,
|
|||
|
* LPBYTE buffer_address,
|
|||
|
* ULONG length,
|
|||
|
* PULong bytes_accepted)
|
|||
|
*
|
|||
|
* Functional Description
|
|||
|
* This function makes the Win32 calls to write data to the port.
|
|||
|
*
|
|||
|
* Formal Parameters
|
|||
|
* synchronous - (i) TRUE, if we should wait for the write to
|
|||
|
* complete before returning.
|
|||
|
* buffer_address - (i) Address of the data to write.
|
|||
|
* length - (i) Length of the data to write.
|
|||
|
* bytes_accepted - (i) Actually number of bytes written.
|
|||
|
*
|
|||
|
* Return Value
|
|||
|
* PROTOCOL_LAYER_ERROR - Port not open
|
|||
|
* PROTOCOL_LAYER_NO_ERROR - No error occured
|
|||
|
*
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Caveats
|
|||
|
* None
|
|||
|
*/
|
|||
|
ProtocolLayerError ComPort::WriteData
|
|||
|
(
|
|||
|
BOOL synchronous,
|
|||
|
LPBYTE buffer_address,
|
|||
|
ULONG length,
|
|||
|
PULong bytes_accepted
|
|||
|
)
|
|||
|
{
|
|||
|
COMSTAT com_status;
|
|||
|
ULONG com_error;
|
|||
|
|
|||
|
ULONG byte_count;
|
|||
|
ULONG bytes_written;
|
|||
|
BOOL fRet;
|
|||
|
|
|||
|
*bytes_accepted = 0;
|
|||
|
if (m_hCommLink == INVALID_HANDLE_VALUE)
|
|||
|
{
|
|||
|
return (PROTOCOL_LAYER_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
if (Write_Active)
|
|||
|
{
|
|||
|
return (PROTOCOL_LAYER_NO_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Determine the amount of space left in the buffer
|
|||
|
*/
|
|||
|
::ZeroMemory(&com_status, sizeof(com_status));
|
|||
|
::ClearCommError(m_hCommLink, &com_error, &com_status);
|
|||
|
|
|||
|
if (length > (Tx_Buffer_Size - com_status.cbOutQue))
|
|||
|
{
|
|||
|
byte_count = Tx_Buffer_Size - com_status.cbOutQue;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
byte_count = length;
|
|||
|
}
|
|||
|
|
|||
|
::ZeroMemory(&m_WriteOverlapped, sizeof(m_WriteOverlapped));
|
|||
|
m_WriteOverlapped.hEvent = m_hevtPendingWrite;
|
|||
|
fRet = ::WriteFile(m_hCommLink, buffer_address, byte_count, &bytes_written, &m_WriteOverlapped);
|
|||
|
|
|||
|
/*
|
|||
|
** If this is a synchronous write, wait for the event object to be
|
|||
|
** set before returning.
|
|||
|
*/
|
|||
|
if (synchronous)
|
|||
|
{
|
|||
|
::WaitForSingleObject(m_hevtPendingWrite, SYNCHRONOUS_WRITE_TIMEOUT);
|
|||
|
fRet = ::GetOverlappedResult(m_hCommLink, &m_WriteOverlapped, &bytes_written, FALSE);
|
|||
|
if (! fRet)
|
|||
|
{
|
|||
|
WARNING_OUT(("ComPort::WriteData: purge comm"));
|
|||
|
::PurgeComm(m_hCommLink, PURGE_TXABORT);
|
|||
|
}
|
|||
|
::ResetEvent(m_WriteOverlapped.hEvent);
|
|||
|
}
|
|||
|
|
|||
|
if (! fRet)
|
|||
|
{
|
|||
|
if (::GetLastError () == ERROR_IO_PENDING)
|
|||
|
{
|
|||
|
Write_Active = TRUE;
|
|||
|
*bytes_accepted = byte_count;
|
|||
|
Byte_Count += byte_count;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
TRACE_OUT(("ComPort: DataRequest: Error on WriteFile = %d", ::GetLastError()));
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (bytes_written != byte_count)
|
|||
|
{
|
|||
|
TRACE_OUT(("ComPort: DataRequest: Error on WriteFile bytes written != bytes requested"));
|
|||
|
}
|
|||
|
*bytes_accepted = byte_count;
|
|||
|
|
|||
|
/*
|
|||
|
** Increment Byte_Count
|
|||
|
*/
|
|||
|
Byte_Count += bytes_written;
|
|||
|
}
|
|||
|
|
|||
|
return (PROTOCOL_LAYER_NO_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ProtocolLayerError ComPort::RegisterHigherLayer (
|
|||
|
* ULONG,
|
|||
|
* PMemoryManager,
|
|||
|
* IProtocolLayer * higher_layer)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function is called by an object that wants to receive the data
|
|||
|
* read from the com port.
|
|||
|
*/
|
|||
|
ProtocolLayerError ComPort::RegisterHigherLayer(ULONG_PTR, PMemoryManager,
|
|||
|
IProtocolLayer *pMux)
|
|||
|
{
|
|||
|
m_pMultiplexer = pMux;
|
|||
|
return (PROTOCOL_LAYER_NO_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ProtocolLayerError ComPort::RemoveHigherLayer (
|
|||
|
* USHORT)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function is called by an object that no longer wants to receive
|
|||
|
* the data from the com port.
|
|||
|
*/
|
|||
|
ProtocolLayerError ComPort::RemoveHigherLayer(ULONG_PTR)
|
|||
|
{
|
|||
|
m_pMultiplexer = NULL;
|
|||
|
return (PROTOCOL_LAYER_NO_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ProtocolLayerError ComPort::PollReceiver (
|
|||
|
* ULONG)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function is called to take the data that we have received from
|
|||
|
* the port and pass it on up to the registered layer.
|
|||
|
*/
|
|||
|
ProtocolLayerError ComPort::PollReceiver(void)
|
|||
|
{
|
|||
|
BOOL issue_read = FALSE;
|
|||
|
ULONG bytes_accepted;
|
|||
|
BOOL fRet;
|
|||
|
|
|||
|
if (m_pMultiplexer == NULL || m_hCommLink == INVALID_HANDLE_VALUE)
|
|||
|
{
|
|||
|
return (PROTOCOL_LAYER_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** This event can occur if we have completed a read but the higher layers
|
|||
|
** have not accepted all of the data. So, before we issue another
|
|||
|
** ReadFile() we are going to send the pending data on up.
|
|||
|
*/
|
|||
|
if (! Read_Active)
|
|||
|
{
|
|||
|
if (m_cbRead)
|
|||
|
{
|
|||
|
m_pMultiplexer->DataIndication(m_pbReadBuffer, m_cbRead - m_nReadBufferOffset, &bytes_accepted);
|
|||
|
if (bytes_accepted > (m_cbRead - m_nReadBufferOffset))
|
|||
|
{
|
|||
|
ERROR_OUT(("ComPort: PollReceiver1: ERROR: Higher layer accepted too many bytes"));
|
|||
|
}
|
|||
|
|
|||
|
m_nReadBufferOffset += bytes_accepted;
|
|||
|
if (m_nReadBufferOffset == m_cbRead)
|
|||
|
{
|
|||
|
issue_read = TRUE;
|
|||
|
m_cbRead = 0;
|
|||
|
m_nReadBufferOffset = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
issue_read = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Issue a ReadFile () and process any data received.
|
|||
|
*/
|
|||
|
while (issue_read)
|
|||
|
{
|
|||
|
m_cbRead = 0;
|
|||
|
m_nReadBufferOffset = 0;
|
|||
|
::ZeroMemory(&m_ReadOverlapped, sizeof(m_ReadOverlapped));
|
|||
|
m_ReadOverlapped.hEvent = m_hevtPendingRead;
|
|||
|
fRet = ::ReadFile(m_hCommLink, m_pbReadBuffer, m_cbReadBufferSize, &m_cbRead, &m_ReadOverlapped);
|
|||
|
if (! fRet)
|
|||
|
{
|
|||
|
if (::GetLastError() == ERROR_IO_PENDING)
|
|||
|
{
|
|||
|
Read_Active = TRUE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
WARNING_OUT(("ComPort: Error on ReadFile = %d", ::GetLastError()));
|
|||
|
if (Count_Errors_On_ReadFile++ == DEFAULT_COUNT_OF_READ_ERRORS)
|
|||
|
{
|
|||
|
WARNING_OUT(("ComPort: %d Errors on ReadFile, closing the connection", Count_Errors_On_ReadFile));
|
|||
|
Close();
|
|||
|
return (PROTOCOL_LAYER_ERROR);
|
|||
|
}
|
|||
|
}
|
|||
|
issue_read = FALSE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (m_pMultiplexer != NULL)
|
|||
|
{
|
|||
|
m_pMultiplexer->DataIndication(m_pbReadBuffer, m_cbRead, &bytes_accepted);
|
|||
|
if (bytes_accepted > m_cbRead)
|
|||
|
{
|
|||
|
ERROR_OUT(("ComPort: PollReceiver: ERROR: Higher layer accepted too many bytes"));
|
|||
|
}
|
|||
|
m_nReadBufferOffset += bytes_accepted;
|
|||
|
if (m_nReadBufferOffset != m_cbRead)
|
|||
|
{
|
|||
|
issue_read = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
issue_read = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return (PROTOCOL_LAYER_NO_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ProtocolLayerError ComPort::GetParameters (
|
|||
|
* ULONG,
|
|||
|
* USHORT * max_packet_size,
|
|||
|
* USHORT * prepend,
|
|||
|
* USHORT * append)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function is called by an object to determine the maximum packet
|
|||
|
* size that this object expects. It also queries the number of bytes
|
|||
|
* it should skip on the front of a packet and append to the end of a
|
|||
|
* packet. The ComPort object is a stream device, so these parameters
|
|||
|
* don't really matter.
|
|||
|
*/
|
|||
|
ProtocolLayerError ComPort::GetParameters
|
|||
|
(
|
|||
|
USHORT * max_packet_size,
|
|||
|
USHORT * prepend,
|
|||
|
USHORT * append
|
|||
|
)
|
|||
|
{
|
|||
|
/*
|
|||
|
** max_packet_size set to 0xffff means that this object receives
|
|||
|
** data in a stream format rather than a packet format. It does
|
|||
|
** group data into packets, it handles data a byte at a time.
|
|||
|
** Therefore, when a higher layer issues a DataRequest() to this
|
|||
|
** object, it may not accept the whole data block, it may only
|
|||
|
** accept part of it.
|
|||
|
**
|
|||
|
** prepend is set to 0 because this object does not prepend any
|
|||
|
** data to the beginning of a DataRequest() packet.
|
|||
|
**
|
|||
|
** append is set to 0 because this object does not append any
|
|||
|
** data to the end of a DataRequest() packet.
|
|||
|
*/
|
|||
|
*max_packet_size = 0xffff;
|
|||
|
*prepend = 0;
|
|||
|
*append = 0;
|
|||
|
|
|||
|
return (PROTOCOL_LAYER_NO_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* void ComPort::ReportInitializationFailure (
|
|||
|
* PChar error_message)
|
|||
|
*
|
|||
|
* Functional Description
|
|||
|
* This routine simply reports an error to the user and closes the
|
|||
|
* Windows comm port. It does absolutely nothing if the Physical
|
|||
|
* API is disabled.
|
|||
|
*
|
|||
|
* Formal Parameters
|
|||
|
* error_message (i) - Pointer to error message
|
|||
|
*
|
|||
|
* Return Value
|
|||
|
* None
|
|||
|
*
|
|||
|
* Side Effects
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Caveats
|
|||
|
* None
|
|||
|
*/
|
|||
|
void ComPort::ReportInitializationFailure(ComPortError rc)
|
|||
|
{
|
|||
|
ERROR_OUT(("ComPort:: IO failure, rc=%d", rc));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* BOOL ComPort::ProcessReadEvent (void)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function is called when a READ event is actually set. This means
|
|||
|
* that the read operation has completed or an error occured.
|
|||
|
*/
|
|||
|
BOOL ComPort::ProcessReadEvent(void)
|
|||
|
{
|
|||
|
BOOL fRet;
|
|||
|
|
|||
|
if (Read_Active)
|
|||
|
{
|
|||
|
if (WAIT_OBJECT_0 == ::WaitForSingleObject(m_hevtPendingRead, 0))
|
|||
|
{
|
|||
|
fRet = GetOverlappedResult(m_hCommLink, &m_ReadOverlapped, &m_cbRead, FALSE);
|
|||
|
if (fRet && m_cbRead == 0)
|
|||
|
{
|
|||
|
fRet = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
Read_Active = FALSE;
|
|||
|
::ResetEvent(m_hevtPendingRead);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
::ResetEvent(m_hevtPendingRead);
|
|||
|
}
|
|||
|
|
|||
|
return fRet;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* BOOL ComPort::ProcessWriteEvent (void)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function is called when a WRITE event is actually set. This means
|
|||
|
* that the write operation has completed or an error occured.
|
|||
|
*/
|
|||
|
BOOL ComPort::ProcessWriteEvent(void)
|
|||
|
{
|
|||
|
ULONG bytes_written;
|
|||
|
BOOL fRet = FALSE;
|
|||
|
|
|||
|
if (Write_Active)
|
|||
|
{
|
|||
|
if (WAIT_OBJECT_0 == ::WaitForSingleObject(m_hevtPendingWrite, 0))
|
|||
|
{
|
|||
|
fRet = ::GetOverlappedResult(m_hCommLink, &m_WriteOverlapped, &bytes_written, FALSE);
|
|||
|
if (! fRet)
|
|||
|
{
|
|||
|
DWORD dwErr = ::GetLastError();
|
|||
|
if (ERROR_IO_PENDING == dwErr)
|
|||
|
{
|
|||
|
TRACE_OUT(("ProcessWriteEvent: still pending"));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
WARNING_OUT(("ProcessWriteEvent: ERROR = %d", dwErr));
|
|||
|
}
|
|||
|
}
|
|||
|
Write_Active = FALSE;
|
|||
|
::ResetEvent(m_hevtPendingWrite);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
::ResetEvent(m_hevtPendingWrite);
|
|||
|
}
|
|||
|
|
|||
|
return fRet;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ProtocolLayerError ComPort::DataIndication (
|
|||
|
* LPBYTE,
|
|||
|
* ULONG,
|
|||
|
* PULong)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function is not used. It is only here because we inherit from
|
|||
|
* ProtocolLayer.
|
|||
|
*/
|
|||
|
ProtocolLayerError ComPort::DataIndication(LPBYTE, ULONG, PULong)
|
|||
|
{
|
|||
|
return (PROTOCOL_LAYER_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ProtocolLayerError ComPort::DataRequest (
|
|||
|
* ULONG,
|
|||
|
* PMemory,
|
|||
|
* PULong)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function is not used. It is only here because we inherit from
|
|||
|
* ProtocolLayer.
|
|||
|
*/
|
|||
|
ProtocolLayerError ComPort::DataRequest(ULONG_PTR, PMemory, PULong)
|
|||
|
{
|
|||
|
return (PROTOCOL_LAYER_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ProtocolLayerError ComPort::PollTransmitter (
|
|||
|
* ULONG,
|
|||
|
* USHORT,
|
|||
|
* USHORT *,
|
|||
|
* USHORT *)
|
|||
|
*
|
|||
|
* Public
|
|||
|
*
|
|||
|
* Functional Description:
|
|||
|
* This function is not used. It is only here because we inherit from
|
|||
|
* ProtocolLayer.
|
|||
|
*/
|
|||
|
ProtocolLayerError ComPort::PollTransmitter(ULONG_PTR, USHORT, USHORT *, USHORT *)
|
|||
|
{
|
|||
|
return (PROTOCOL_LAYER_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
|