|
|
#include "mbftpch.h"
#include "nullmdm.h"
// #undef TRACE_OUT
// #define TRACE_OUT WARNING_OUT
BYTE g_szNULLMStartString[] = "NULLMDM"; ULONG g_nConnID = 0;
DWORD __stdcall TPhysWorkerThreadProc(void *lpv);
CNullModem::CNullModem(HINSTANCE hDllInst) : m_fInitialized(FALSE), m_hDllInst(hDllInst), m_pfnCallback(NULL), m_fListening(FALSE), m_hwnd(NULL), m_nTransportID(0), m_nConnectionID(0), m_dwThreadID(0), m_hThread(NULL), m_hevtOverlapped(NULL), m_dwEventMask(0), m_fCommPortInUse(FALSE) { ::ZeroMemory(&m_Line, sizeof(m_Line)); ::ZeroMemory(&m_Overlapped, sizeof(m_Overlapped)); ::ZeroMemory(&m_DefaultTimeouts, sizeof(m_DefaultTimeouts));
m_hevtOverlapped = ::CreateEvent(NULL, TRUE, FALSE, NULL); // manual reset
ASSERT(NULL != m_hevtOverlapped); }
CNullModem::~CNullModem(void) { if (m_fInitialized) { TPhysTerminate(); }
if (NULL != m_hevtOverlapped) { ::CloseHandle(m_hevtOverlapped); } }
//////////////////////////////////////////////////////////////////////////////
// TPhysInitialize
//////////////////////////////////////////////////////////////////////////////
TPhysicalError CNullModem::TPhysInitialize ( TPhysCallback callback, UINT nTransportID ) { const char *pszNULLMClassName = "COMMWndClass"; TPhysicalError rc = TPHYS_SUCCESS;
TRACE_OUT(("TPhysInitialize"));
//
// If we have already been initialized then this is a reinit call from
// the node controller so just do nothing.
//
if (! m_fInitialized) { //
// zero out the SESSION INFO structure
//
::ZeroMemory(&m_Line, sizeof(m_Line));
//
// Store control information
//
m_pfnCallback = callback; m_nTransportID = nTransportID; m_fInitialized = TRUE; m_nConnectionID = ++g_nConnID;
//
// Create a window so we can decouple to the node controller context
// Register the main window class. Since it is invisible, leave
// the wndclass structure sparse.
//
WNDCLASS wc; ::ZeroMemory(&wc, sizeof(wc)); wc.style = 0; wc.lpfnWndProc = TPhysWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = m_hDllInst; wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = pszNULLMClassName; ::RegisterClass(&wc);
//
// Create the main window.
//
m_hwnd = ::CreateWindow( pszNULLMClassName, // See RegisterClass() call.
NULL, // It's invisible!
0, // Window style.
0, // Default horizontal position.
0, // Default vertical position.
0, // Default width.
0, // Default height.
NULL, // No parent.
NULL, // No menu.
m_hDllInst, // This instance owns this window.
NULL // Pointer not needed.
); if (NULL == m_hwnd) { ERROR_OUT(( "Failed to create wnd")); return(TPHYS_RESULT_FAIL); }
// rc = g_lpfnPTPhysicalInit(TPhysDriverCallback, NULL);
ASSERT(TPHYS_SUCCESS == rc); } return rc; }
//////////////////////////////////////////////////////////////////////////////////
// TPhysTerminate
//
// The node controller is shutting down
// Destroy our window and clean up transports.
//////////////////////////////////////////////////////////////////////////////////
TPhysicalError CNullModem::TPhysTerminate(void) { TRACE_OUT(("TPhysTerminate"));
if (! m_fInitialized) { return(TPHYS_RESULT_NOT_INITIALIZED); }
//
// Clean up the PSTN transport
//
// g_lpfnPTPhysicalCleanup();
//
// Destroy the window
//
if (NULL != m_hwnd) { ::DestroyWindow(m_hwnd); m_hwnd = NULL; }
m_pfnCallback = NULL; m_nTransportID = 0; m_fInitialized = FALSE; return(TPHYS_SUCCESS); }
//////////////////////////////////////////////////////////////////////////////
// TPhysConnectRequest
//
// The node controller wants to place a call and has determined that it
// first needs a physical modem connection. Make a call and flag an
// outgoing call so that when the call comes active we can tell the modem
// transport to begin negotiation.
//////////////////////////////////////////////////////////////////////////////
TPhysicalError CNullModem::TPhysConnectRequest(LPSTR pszComPort) { TPhysicalError rc = TPHYS_SUCCESS; HANDLE hCommLink; DCB dcb; DWORD dwWritten;
TRACE_OUT(("TPhysConnectRequest"));
if (! m_fInitialized) { ERROR_OUT(("NULL MODEM OOB not initialized")); return TPHYS_RESULT_NOT_INITIALIZED; }
//
// Select a comm port for the call.
//
if (CALL_STATE_IDLE == m_Line.eCallState || CALL_STATE_DROP == m_Line.eCallState) { //
// Also prime our local copy of the conninfo structure for use in callbacks
//
m_Line.connInfo.resultCode = 0; m_Line.connInfo.connectionID = m_nConnectionID; } else { ERROR_OUT(("No comm port is available")); return TPHYS_RESULT_COMM_PORT_BUSY; }
// lonchanc: From now on, we can bail out thru the common exit point
// because the cleanup checks for m_fCommPortInUse.
//
// Only alow one at at time
//
// lonchanc: g_COMM_Thread_Users is starting from -1
// why can we simply use a flag???
if (m_fCommPortInUse) { ERROR_OUT(("TPhysConnectRequest: Waiting for a previous null mode connection")); rc = TPHYS_RESULT_WAITING_FOR_CONNECTION; goto bail; } m_fCommPortInUse = TRUE;
//
// Open the comm port
//
hCommLink = ::CreateFile(pszComPort, GENERIC_READ | GENERIC_WRITE, 0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // overlapped I/O
NULL ); if (hCommLink == INVALID_HANDLE_VALUE) { ERROR_OUT(("TPhysConnectRequest: CreateFile failed. err=%d", ::GetLastError())); rc = TPHYS_RESULT_COMM_PORT_BUSY; goto bail; }
m_Line.hCommLink = hCommLink;
//
// remember the default timeouts
//
::GetCommTimeouts(hCommLink, &m_DefaultTimeouts);
//
// Let the other side know that we are trying to connect
//
if (! ::EscapeCommFunction(hCommLink, SETDTR)) { ERROR_OUT(("TPhysConnectRequest: Unable to Set DTR: err=%d", ::GetLastError())); }
::ZeroMemory(&m_Overlapped, sizeof(m_Overlapped)); m_Overlapped.hEvent = m_hevtOverlapped; m_dwEventMask = 0;
::ResetEvent(m_hevtOverlapped);
if (! ::WriteFile(hCommLink, g_szNULLMStartString, sizeof(g_szNULLMStartString), &dwWritten, &m_Overlapped)) { DWORD dwErr = ::GetLastError(); if (ERROR_IO_PENDING != dwErr) { ERROR_OUT(("TPhysConnectRequest: WriteFile failed. err=%d", dwErr)); ::ResetEvent(m_hevtOverlapped); rc = TPHYS_RESULT_COMM_PORT_BUSY; goto bail; } else { DWORD dwWait = ::WaitForSingleObject(m_hevtOverlapped, INFINITE); BOOL fRet = ::GetOverlappedResult(hCommLink, &m_Overlapped, &dwWritten, TRUE); ASSERT(fRet); ASSERT(dwWritten == sizeof(g_szNULLMStartString)); } } else { ASSERT(dwWritten == sizeof(g_szNULLMStartString)); }
::ResetEvent(m_hevtOverlapped);
//
// Get the default dcb
//
::ZeroMemory(&dcb, sizeof(dcb)); ::GetCommState(hCommLink, &dcb); dcb.BaudRate = 19200; // Default: BaudRate
//
// Set our state so we can get notification in the comm port
//
dcb.DCBlength = sizeof(DCB); dcb.fBinary = 1; // binary mode, no EOF check
dcb.fParity = 0; // enable parity checking
dcb.fOutxCtsFlow = 1; // CTS output flow control
dcb.fOutxDsrFlow = 0; // DSR output flow control
dcb.fDtrControl = DTR_CONTROL_ENABLE; // DTR flow control type
dcb.fDsrSensitivity = 0; // DSR sensitivity
dcb.fTXContinueOnXoff = 0; // XOFF continues Tx
dcb.fOutX = 0; // XON/XOFF out flow control
dcb.fInX = 0; // XON/XOFF in flow control
dcb.fErrorChar = 0; // enable error replacement
dcb.fNull = 0; // enable null stripping
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;// RTS flow control
dcb.XonLim = 0; // transmit XON threshold
dcb.XoffLim = 0; // transmit XOFF threshold
dcb.fErrorChar = 0; // enable error replacement
dcb.fNull = 0; // enable null stripping
dcb.fAbortOnError = 0; // abort reads/writes on error
::SetCommState(hCommLink, &dcb);
::PurgeComm(hCommLink, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
m_Line.eCallState = CALL_STATE_MAKE; m_Line.hevtCall = m_hevtOverlapped; m_Line.pstnHandle = (PHYSICAL_HANDLE) hCommLink; m_Line.fCaller = TRUE;
if (! ::SetCommMask(hCommLink, EV_RXCHAR | // Any Character received
EV_CTS | // CTS changed state
EV_DSR | // DSR changed state
EV_RLSD| // RLSD changed state
EV_RXFLAG)) // Certain character
{ ERROR_OUT(("TPhysConnectRequest: Unable to SetCommMask: err=%d", ::GetLastError())); }
::ZeroMemory(&m_Overlapped, sizeof(m_Overlapped)); m_Overlapped.hEvent = m_hevtOverlapped; m_dwEventMask = 0;
::ResetEvent(m_hevtOverlapped);
if (! ::WaitCommEvent(hCommLink, &m_dwEventMask, &m_Overlapped)) { DWORD dwErr = ::GetLastError(); if (ERROR_IO_PENDING != dwErr) { ERROR_OUT(("TPhysConnectRequest: WaitCommEvent failed, err=%d", dwErr)); m_fCommPortInUse = FALSE; } }
#if 1
WorkerThreadProc(); #else
//
// If the comm thread doesn't exist, create it now.
//
// lonchanc: I am not sure that the thread will exist, because
// the while loop inside the thread will exit if m_fCommPortInUse is false.
// If m_fCommPortInUse is true, then we should bail out already.
//
ASSERT(NULL == m_hThread);
//
// We need to create another thread that will wait on comm events.
//
m_hThread = ::CreateThread(NULL, 0, TPhysWorkerThreadProc, this, 0, &m_dwThreadID); ASSERT(NULL != m_hThread); #endif
bail:
return(rc); }
//////////////////////////////////////////////////////////////////////////////
// TPhysDisconnect
//
// The node controller wants us to bring the call down now. We must first
// ask the transports to close down their physicall connection.
//
// Note that in the case of the PSTN transport we use a NOWAIT call which
// completes syncronously. Because we will not get a follow on confirm
// we simulate one from here. If we switch to using WAIT mode then
// take the event generation code out!
//////////////////////////////////////////////////////////////////////////////
TPhysicalError CNullModem::TPhysDisconnect(void) { TRACE_OUT(("TPhysDisconnect"));
if (! m_fInitialized) { ERROR_OUT(( "Not initialised")); return(TPHYS_RESULT_NOT_INITIALIZED); }
TRACE_OUT(("Disconnect call, state %u", m_Line.eCallState));
//
// Otherwise it must be a pstn call in progress so end that. Note
// that NC may still think it is MODEM, but we still need to close
// PSTN.
//
// g_lpfnPTPhysicalDisconnectRequest(m_aLines[lineID].pstnHandle, TPHYSICAL_NO_WAIT);
::PostMessage(m_hwnd, WM_TPHYS_DISCONNECT_CONFIRM, (WPARAM) this, m_Line.pstnHandle);
//
// close the comport
//
DropCall();
return(TPHYS_SUCCESS); }
//////////////////////////////////////////////////////////////////////////////
// TPhysListen/TPhysUnlisten
//
// NULLMMAN does very little with listen/unlisten, just sets a state variable
// that is interrogated to see if we should accept incoming calls.
//
// NOTE - This is different from the Listen/Unlisten that is used to tell
// the PSTN transport about the presence of an incoming call.
//////////////////////////////////////////////////////////////////////////////
TPhysicalError CNullModem::TPhysListen(void) { TRACE_OUT(("TPhysListen"));
if (! m_fInitialized) { ERROR_OUT(("TPhysListen: not initialized")); return(TPHYS_RESULT_NOT_INITIALIZED); }
m_fListening = TRUE;
return(TPHYS_SUCCESS); }
TPhysicalError CNullModem::TPhysUnlisten(void) { TRACE_OUT(("TPhysUnlisten"));
if (! m_fInitialized) { ERROR_OUT(("TPhysUnlisten: not initialized")); return(TPHYS_RESULT_NOT_INITIALIZED); }
m_fListening = FALSE;
return(TPHYS_SUCCESS); }
//////////////////////////////////////////////////////////////////////////////
// DropCall - close the comm port
/////////////////////////////////////////////////////////////////////////////
void CNullModem::DropCall(void) { TRACE_OUT(("DropCall"));
//
// close the device handle
//
if (NULL != m_Line.hCommLink) { CALL_STATE eCallState = m_Line.eCallState; m_Line.eCallState = CALL_STATE_IDLE;
//
// If this call is connected it is not using the comm thread
//
if (eCallState != CALL_STATE_CONNECTED) { m_fCommPortInUse = FALSE; }
//
// restore comm timeouts
//
SetCommTimeouts(m_Line.hCommLink, &m_DefaultTimeouts);
TRACE_OUT(("Closing device handle %x", m_Line.hCommLink)); ::CloseHandle(m_Line.hCommLink); m_Line.hCommLink = NULL; } }
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION: PSTNCallback
//
// DESCRIPTION:
//
// PSTN callback function, called by the PSTN driver with the resuts of
// TPhysical operations
//
// See MCATTPRT.H for definitions of the parameters
//
//////////////////////////////////////////////////////////////////////////////
TPhysicalError CALLBACK TPhysDriverCallback(USHORT msg, ULONG lParam, void *userData) { TRACE_OUT(("NULLM_PSTNCallback")); TRACE_OUT(("Hit TPhysical callback, %x %lx %lx", msg, lParam, userData));
CNullModem *p = (CNullModem *) userData; BOOL fRet = ::PostMessage(p->GetHwnd(), msg, (WPARAM) p, lParam); ASSERT(fRet);
return TPHYS_SUCCESS; }
//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION:TPhysWndProc
//
// Window procedure used to decouple callback requests
//
//////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK TPhysWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CNullModem *p = (CNullModem *) wParam; if (uMsg >= WM_APP) { return p->TPhysProcessMessage(uMsg, lParam); } return ::DefWindowProc(hwnd, uMsg, wParam, lParam); }
LRESULT CNullModem::TPhysProcessMessage(UINT uMsg, LPARAM lParam) { USHORT rc; USHORT reason; ULONG status; LINE_INFO *pLine;
if (WM_TPHYS_STATUS_INDICATION == uMsg) { return 0; } ASSERT(lParam == (LPARAM) m_Line.pstnHandle);
switch (uMsg) { case WM_TPHYS_CONNECT_INDICATION: TRACE_OUT(("got a WM_TPHYS_CONNECT_INDICATION"));
m_Line.connInfo.resultCode = TPHYS_RESULT_INUSE; m_Line.eCallState = CALL_STATE_CONNECTED;
if (NULL != m_pfnCallback) { (*m_pfnCallback)(WM_TPHYS_CONNECT_CONFIRM, &m_Line.connInfo, m_nTransportID); } break;
case WM_TPHYS_CONNECT_CONFIRM: //
// The transport has connected OK! We will take the line
// back when the transport pulls DTR down
//
TRACE_OUT(("got a WM_TPHYS_CONNECT_CONFIRM")); m_Line.connInfo.resultCode = TPHYS_RESULT_SUCCESS_ALTERNATE; m_Line.eCallState = CALL_STATE_CONNECTED;
if (NULL != m_pfnCallback) { (*m_pfnCallback)(WM_TPHYS_CONNECT_CONFIRM, &m_Line.connInfo, m_nTransportID); } break;
case WM_TPHYS_DISCONNECT_INDICATION: case WM_TPHYS_DISCONNECT_CONFIRM: //
// If the disconnect is the result of a failed connect request
// then tell the NC of the failure (Otherwise it is a
// successful disconnect)
//
if (WM_TPHYS_DISCONNECT_INDICATION == uMsg) { TRACE_OUT(("WM_TPHYS_DISCONNECT_INDICATION, %ld", lParam)); } else { TRACE_OUT(("WM_TPHYS_DISCONNECT_CONFIRM, %ld", lParam)); } if ((m_Line.eCallState == CALL_STATE_MAKE) || (m_Line.eCallState == CALL_STATE_ANSWER) || (m_Line.eCallState == CALL_STATE_CONNECTED)) { TRACE_OUT(( "T120 connection attempt has failed")); if (m_Line.fCaller) { m_Line.connInfo.resultCode = TPHYS_RESULT_CONNECT_FAILED; if (NULL != m_pfnCallback) { (*m_pfnCallback)(WM_TPHYS_CONNECT_CONFIRM, &m_Line.connInfo, m_nTransportID); } } DropCall(); } else { //
// The transport has disconnected OK. We can take the line
// back as soon as the unlisten returns
//
TRACE_OUT(("T120 has disconnected - unlistening")); if (! m_Line.fCaller) { // g_lpfnPTPhysicalUnlisten(m_Line.pstnHandle);
} else { //
// We only drop the call for outgoing requests - leave
// incoming calls to drop when the line goes down
//
DropCall(); } } break; } return 0; }
//////////////////////////////////////////////////////////////////////////////
// SetConnectedPort
//
// Waits for comm port changes
//////////////////////////////////////////////////////////////////////////////
void CNullModem::SetConnectedPort(void) { DCB dcb;
TRACE_OUT(("SetConnectedPort"));
::ZeroMemory(&dcb, sizeof(dcb));
//
// Set comm mask and state
//
::SetCommMask(m_Line.hCommLink, 0); // RLSD changed state
::GetCommState(m_Line.hCommLink, &dcb); dcb.DCBlength = sizeof(DCB); dcb.fBinary = 1; // binary mode, no EOF check
dcb.fOutxDsrFlow = 0; // DSR output flow control
dcb.fDsrSensitivity = 0; // DSR sensitivity
dcb.fTXContinueOnXoff = 0; // XOFF continues Tx
dcb.fOutX = 0; // XON/XOFF out flow control
dcb.fInX = 0; // XON/XOFF in flow control
dcb.fErrorChar = 0; // enable error replacement
dcb.fNull = 0; // enable null stripping
dcb.XonLim = 0; // transmit XON threshold
dcb.XoffLim = 0; // transmit XOFF threshold
::SetCommState(m_Line.hCommLink, &dcb); }
//////////////////////////////////////////////////////////////////////////////
// WaitForConnection
//
// Waits for comm port changes
//////////////////////////////////////////////////////////////////////////////
BOOL CNullModem::WaitForConnection(void) { BOOL fRet = FALSE;
TRACE_OUT(("WaitForConnection"));
while (TRUE) { DWORD dwWait = ::WaitForSingleObject(m_hevtOverlapped, COMM_PORT_TIMEOUT); TRACE_OUT(("WaitForConnection: WaitForSingleObject returns %d", dwWait));
::ResetEvent(m_hevtOverlapped);
if (dwWait == WAIT_ABANDONED || dwWait == WAIT_TIMEOUT || dwWait == WAIT_FAILED) { DropCall(); m_fCommPortInUse = FALSE; ERROR_OUT(("WaitForConnection: Unable to WaitCommEvent: error = %d", ::GetLastError())); goto Failure; }
ASSERT(m_hevtOverlapped == m_Line.hevtCall); if (CALL_STATE_MAKE != m_Line.eCallState && CALL_STATE_ANSWER != m_Line.eCallState) { ERROR_OUT(("WaitForConnection: Got a bad event = %d", m_hevtOverlapped)); goto Failure; }
TRACE_OUT(("WaitForConnection: m_dwEventMask = %d", m_dwEventMask)); switch (m_Line.eCallState) { case CALL_STATE_MAKE: { //
// The other side was connected and cleared DTR
//
if (m_dwEventMask & (EV_RXCHAR)) { ::EscapeCommFunction(m_Line.hCommLink, CLRDTR); ::EscapeCommFunction(m_Line.hCommLink, SETDTR); SetConnectedPort(); m_Line.fCaller = FALSE; goto Success; } //
// The other side just connected
//
else if(m_dwEventMask & (EV_DSR | EV_RLSD | EV_CTS)) { //
// Change the state of this connection so we dont get here again
//
m_Line.eCallState = CALL_STATE_ANSWER;
//
// Wait sometime so the other side can transition to the wait state
//
::Sleep(2000); //
// Tell the other side we connected before
//
::EscapeCommFunction(m_Line.hCommLink, SETBREAK);
::ZeroMemory(&m_Overlapped, sizeof(m_Overlapped)); m_Overlapped.hEvent = m_Line.hevtCall; m_dwEventMask = 0;
::ResetEvent(m_Overlapped.hEvent);
if (! ::WaitCommEvent(m_Line.hCommLink, &m_dwEventMask, &m_Overlapped)) { DWORD dwErr = ::GetLastError(); if (ERROR_IO_PENDING != dwErr) { ERROR_OUT(("TPhysConnectRequest: Unable to WaitCommEvent: error = %d", dwErr)); DropCall(); goto Failure; } } } } break; case CALL_STATE_ANSWER: { ::EscapeCommFunction(m_Line.hCommLink, CLRBREAK); SetConnectedPort(); goto Success; } break; } } // while
Success:
fRet = TRUE;
Failure:
return fRet; }
//////////////////////////////////////////////////////////////////////////////
// COMMThread
//
// Waits for comm port changes
//////////////////////////////////////////////////////////////////////////////
DWORD __stdcall TPhysWorkerThreadProc(void *lParam) { return ((CNullModem *) lParam)->WorkerThreadProc(); }
DWORD CNullModem::WorkerThreadProc(void) { ULONG rc = 0; ULONG dwResult;
TRACE_OUT(("TPhysWorkerThreadProc"));
while (m_fCommPortInUse) { //
// Wait for connection to happen
//
if (WaitForConnection()) { SetBuffers(); SetTimeouts();
//
// Call T120 physicall request
//
if (m_Line.fCaller) { // rc = g_lpfnPTPhysicalConnectRequest(0, // CALL_CONTROL_MANUAL
// &m_Line.hCommLink, NULL, &m_Line.pstnHandle);
} else { m_Line.connInfo.connectionID = m_nConnectionID; m_Line.connInfo.resultCode = TPHYS_SUCCESS;
if (NULL != m_pfnCallback) { (*m_pfnCallback)(WM_TPHYS_CONNECT_INDICATION, &m_Line.connInfo, m_nTransportID); }
// rc = g_lpfnPTPhysicalListen(0, // CALL_CONTROL_MANUAL
// &m_Line.hCommLink, NULL, &m_Line.pstnHandle);
} if (rc != 0) { TRACE_OUT(( "Failed COMM connect, rc %d",rc)); m_Line.connInfo.resultCode = TPHYS_RESULT_CONNECT_FAILED;
if (NULL != m_pfnCallback) { (*m_pfnCallback)(WM_TPHYS_CONNECT_CONFIRM, &m_Line.connInfo, m_nTransportID); }
DropCall(); } else { m_Line.eCallState = CALL_STATE_CONNECTED; m_Line.connInfo.resultCode = TPHYS_SUCCESS;
//
// This comm port doesn't need the thread anymore
//
m_fCommPortInUse = FALSE; } } else { TRACE_OUT(( "Failed COMM connect, rc %d",rc)); m_Line.connInfo.resultCode = TPHYS_RESULT_CONNECT_FAILED;
if (NULL != m_pfnCallback) { (*m_pfnCallback)(WM_TPHYS_CONNECT_CONFIRM, &m_Line.connInfo, m_nTransportID); } //
// Something went wrong in the wait, get out of the loop
//
break; } }
//
// We are going down.
//
if (NULL != m_hThread) { ::CloseHandle(m_hThread); m_hThread = NULL; }
return 0; }
void CNullModem::SetBuffers(void) { BOOL fRet = ::SetupComm(m_Line.hCommLink, /* rx */ 10240, /* tx */ 1024); ASSERT(fRet); }
void CNullModem::SetTimeouts(void) { COMMTIMEOUTS com_timeouts; ::ZeroMemory(&com_timeouts, sizeof(com_timeouts)); com_timeouts.ReadIntervalTimeout = 10; com_timeouts.ReadTotalTimeoutMultiplier = 0; com_timeouts.ReadTotalTimeoutConstant = 100; com_timeouts.WriteTotalTimeoutMultiplier = 0; com_timeouts.WriteTotalTimeoutConstant = 10000; BOOL fRet = ::SetCommTimeouts(m_Line.hCommLink, &com_timeouts); ASSERT(fRet);
}
|