/* ** Command & Conquer Red Alert(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program 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 General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see <http://www.gnu.org/licenses/>. */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : Command & Conquer/ WW Library * * * * File Name : WINCOMM.H * * * * Programmer : Steve Tall * * * * Start Date : 1/10/96 * * * * Last Update : January 10th 1996 [ST] * * * *---------------------------------------------------------------------------------------------* * Overview: * * * * Functions for WinModemClass & WinNullModemClass * * * * These classes was created to replace the greenleaf comms functions used in C&C DOS with * * WIN32 API calls. * * * *---------------------------------------------------------------------------------------------* * * * Functions: * * * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ //#include "function.h" #include "wincomm.h" #include "timer.h" #include "keyboard.h" #include "misc.h" #include <io.h> #include <sys\types.h> #include <sys\stat.h> #include <fcntl.h> /* ** Define this to log modem activity to disk. */ //#define LOG_MODEM /* ** Object represents a serial port */ WinModemClass *SerialPort = NULL; /*********************************************************************************************** * WMC::WinModemClass -- WinModemClass constructor * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:14PM ST : Created * *=============================================================================================*/ WinModemClass::WinModemClass(void) { /* ** Allocate memory for our internal circular serial input buffer */ SerialBuffer = new unsigned char [SIZE_OF_WINDOWS_SERIAL_BUFFER]; /* ** Initialise the serial buffer pointers */ SerialBufferReadPtr = 0; SerialBufferWritePtr = 0; /* ** Clear the waiting flags */ WaitingForSerialCharRead = FALSE; WaitingForSerialCharWrite = FALSE; /* ** No default abort or echo function */ AbortFunction = NULL; EchoFunction = NULL; /* ** Clear the running error count */ FramingErrors=0; IOErrors=0; BufferOverruns=0; InBufferOverflows=0; ParityErrors=0; OutBufferOverflows=0; /* ** We havnt opened a port yet so... */ PortHandle = 0; } /*********************************************************************************************** * WMC::~WinModemClass -- destructor for WinModemClass * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:15PM ST : Created * *=============================================================================================*/ WinModemClass::~WinModemClass(void) { /* ** Close the port */ if (PortHandle){ Serial_Port_Close(); } /* ** Free the serial buffer */ if (SerialBuffer){ delete [] SerialBuffer; } } /*********************************************************************************************** * Get_Registry_Sub_Key -- search a registry key for a sub-key * * * * * * * * INPUT: handle of key to search * * text to search for * * true if old key should be closed when new key opened * * * * OUTPUT: handle to the key we found or 0 * * * * WARNINGS: None * * * * HISTORY: * * 1/12/96 2:11PM ST : Created * *=============================================================================================*/ HKEY Get_Registry_Sub_Key (HKEY base_key, char *search_key, BOOL close) { char class_string[1024]; DWORD string_size = 1024; DWORD num_sub_keys; DWORD longest_sub_key_name; DWORD longest_class_string; DWORD num_value_entries; DWORD longest_value_name_length; DWORD longest_value_data_length; DWORD security_descriptor_length; FILETIME last_write_time; HKEY result_key; DWORD sub_key_buffer_size; char *sub_key_buffer; char *sub_key_class; if (RegQueryInfoKey (base_key, &class_string[0], &string_size, NULL, &num_sub_keys, &longest_sub_key_name, &longest_class_string, &num_value_entries, &longest_value_name_length, &longest_value_data_length, &security_descriptor_length, &last_write_time) != ERROR_SUCCESS) return (0); sub_key_buffer_size = longest_sub_key_name+16; sub_key_buffer = new char [sub_key_buffer_size]; sub_key_class = new char [longest_class_string+1]; for (int key_num=0 ; key_num<num_sub_keys ; key_num++){ *sub_key_buffer = 0; longest_sub_key_name = sub_key_buffer_size; RegEnumKeyEx(base_key, key_num, sub_key_buffer, &longest_sub_key_name, NULL, sub_key_class, &longest_class_string, &last_write_time); if (!stricmp(search_key, sub_key_buffer)){ if (RegOpenKeyEx( base_key, sub_key_buffer, NULL, KEY_READ, &result_key) == ERROR_SUCCESS){ if (close){ RegCloseKey(base_key); } delete [] sub_key_buffer; delete [] sub_key_class; return (result_key); } } } if (close){ RegCloseKey(base_key); } delete [] sub_key_buffer; delete [] sub_key_class; return (0); } /*********************************************************************************************** * Get_Modem_Name_From_Registry -- retrieve the name of the installed modem from the registry * * * * * * * * INPUT: buffer to put the name in * * length of buffer * * * * OUTPUT: TRUE if modem was found in the registry * * * * WARNINGS: None * * * * HISTORY: * * 1/12/96 2:13PM ST : Created * *=============================================================================================*/ BOOL Get_Modem_Name_From_Registry(char *buffer, int buffer_len) { HKEY key; char modem_name[256]; DWORD modem_name_size = 256; key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "Enum", FALSE); if (!key) return (FALSE); key = Get_Registry_Sub_Key (key, "Root", TRUE); if (!key) return (FALSE); key = Get_Registry_Sub_Key (key, "Modem", TRUE); if (!key) return (FALSE); key = Get_Registry_Sub_Key (key, "0000", TRUE); if (!key) return (FALSE); if (RegQueryValueEx(key, "FriendlyName", NULL, NULL, (unsigned char*)&modem_name[0], &modem_name_size) != ERROR_SUCCESS){ RegCloseKey(key); return (FALSE); } RegCloseKey(key); memcpy (buffer, modem_name, min(buffer_len, modem_name_size)); return (TRUE); } #ifdef cuts /*********************************************************************************************** * WMC::Serial_Port_Open -- opens a com port for asyncronous read/write and gets a handle to it* * * * * * * * INPUT: Com port - 0=com1, 1=com2 etc. * * baud rate - bits per second * * parity - true or false * * word length - 5 to 8 bits * * stop bits - 0=1 stop bit, 1=1.5 & 2=2 * * * * OUTPUT: Handle to port * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:17PM ST : Created * *=============================================================================================*/ //VOID FAR PASCAL lineCallbackFunc(DWORD hDevice, DWORD dwMsg, // DWORD dwCallbackInstance, DWORD dwParam1, DWORD dwParam2, // DWORD dwParam3) VOID FAR PASCAL lineCallbackFunc(DWORD ,DWORD ,DWORD ,DWORD ,DWORD ,DWORD); extern HINSTANCE ProgramInstance; HANDLE WinModemClass::Serial_Port_Open (int com, int baud, int parity, int wordlen, int stopbits) { HANDLE com_handle; //temporary storage for the port handle DCB device_control; //device control block COMMTIMEOUTS timeouts; //timeout values char modem_name[256]; //name of modem char device_name[266]={"\\\\.\\"}; //device name to open DWORD config_size = 2048; char config[2048]; COMMCONFIG *modem_config = (COMMCONFIG*)&config[0]; MODEMDEVCAPS *modem_caps; int temp; BOOL found_modem; /* ** Map for com port values to device names */ static char com_ids[8][5]={ "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8" }; HLINEAPP app_line_handle; DWORD num_devices; lineInitialise (&app_line_handle, ProgramInstance, &lineCallbackFunc, NULL, &num_devices); found_modem = TRUE; if (!Get_Modem_Name_From_Registry(modem_name, 256)){ strcpy(modem_name, com_ids[com]); found_modem = FALSE; } strcat(device_name, modem_name); PortHandle = 0; /* ** Open the com port for asyncronous reads/writes */ com_handle = CreateFile (device_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (com_handle == INVALID_HANDLE_VALUE) return (com_handle); PortHandle = com_handle; /* ** Set the size of the windows communication buffers */ SetupComm(com_handle, 2048, 2048); /* ** Create an event object for asyncronous reads */ ReadOverlap.Internal = 0; ReadOverlap.InternalHigh = 0; ReadOverlap.Offset = 0; ReadOverlap.OffsetHigh = 0; ReadOverlap.hEvent = CreateEvent (NULL, TRUE, TRUE, NULL); /* ** Create an event object for asyncronous writes */ WriteOverlap.Internal = 0; WriteOverlap.InternalHigh = 0; WriteOverlap.Offset = 0; WriteOverlap.OffsetHigh = 0; WriteOverlap.hEvent = CreateEvent (NULL, TRUE, TRUE, NULL); if (!found_modem){ /* ** Get the current state of the com port as a basis for our device control block */ GetCommState (com_handle , &device_control); /* ** Communications settings */ device_control.BaudRate = baud; device_control.fParity = parity; device_control.ByteSize = wordlen; device_control.StopBits = stopbits-1; /* ** Misc settings for flow control etc. */ device_control.fBinary = TRUE; // Binary mode data transfer device_control.fOutxCtsFlow = TRUE; // CTS flow control device_control.fRtsControl = RTS_CONTROL_HANDSHAKE; // RTS flow control device_control.fErrorChar = FALSE; // Dont put an error char into our input stream device_control.fOutxDsrFlow = FALSE; // No DSR flow control device_control.fDtrControl = DTR_CONTROL_ENABLE; // Enable control of DTR line device_control.fOutX = FALSE; // No XON/XOF flow control device_control.fInX = FALSE; // No XON/XOF flow control device_control.fAbortOnError = FALSE; // Device continues to send after an error /* ** Pass the device settings to windows */ if (SetCommState (com_handle , &device_control) != TRUE){ Serial_Port_Close(); return (INVALID_HANDLE_VALUE); } }else{ /* ** If we are talking to a modem device then turn off compression and error correction */ GetCommConfig(PortHandle ,modem_config, &config_size); if (modem_config->dwProviderSubType == PST_MODEM){ temp = modem_config->dwProviderOffset; temp += (int)modem_config; modem_caps = (MODEMDEVCAPS*)temp; modem_caps->dwModemOptions &= ~( MDM_COMPRESSION | MDM_ERROR_CONTROL | MDM_FLOWCONTROL_HARD); SetCommConfig(PortHandle, modem_config ,(unsigned)config_size); } } /* ** Set the device timeouts */ timeouts.ReadIntervalTimeout = 10000; //10 seconds between incoming packets timeouts.ReadTotalTimeoutMultiplier = 0; //disable total timeouts timeouts.ReadTotalTimeoutConstant = 0; //disable total timeouts timeouts.WriteTotalTimeoutMultiplier= 0; //disable total timeouts timeouts.WriteTotalTimeoutConstant = 0; //disable total timeouts if (SetCommTimeouts(com_handle, &timeouts) !=TRUE){ Serial_Port_Close(); return (INVALID_HANDLE_VALUE); } return (com_handle); } #endif /*********************************************************************************************** * WMC::Serial_Port_Open -- opens a com port for asyncronous read/write and gets a handle to it* * * * * * * * INPUT: Com port - 0=com1, 1=com2 etc. * * baud rate - bits per second * * parity - true or false * * word length - 5 to 8 bits * * stop bits - 0=1 stop bit, 1=1.5 & 2=2 * * flow control- 0 = none, 1 = hardware * * * * OUTPUT: Handle to port * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:17PM ST : Created * *=============================================================================================*/ HANDLE WinModemClass::Serial_Port_Open (char *device_name, int baud, int parity, int wordlen, int stopbits, int flowcontrol) { HANDLE com_handle; //temporary storage for the port handle DCB device_control; //device control block COMMTIMEOUTS timeouts; //timeout values #if (0) /* ** Map for com port values to device names */ static char com_ids[8][5]={ "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8" }; #endif //(0) int errorval; char errortxt[128]; char devname[266]={"\\\\.\\"}; //device name to open strcat (devname, device_name); PortHandle = 0; /* ** Open the com port for asyncronous reads/writes */ //com_handle = CreateFile (&com_ids[com][0], GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); com_handle = CreateFile (devname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (com_handle == INVALID_HANDLE_VALUE) return (com_handle); PortHandle = com_handle; /* ** Set the size of the windows communication buffers */ SetupComm(com_handle, SIZE_OF_WINDOWS_SERIAL_BUFFER, SIZE_OF_WINDOWS_SERIAL_BUFFER); /* ** Reset any read or write operation and purge the buffers */ PurgeComm (PortHandle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); /* ** Create an event object for asyncronous reads */ ReadOverlap.Internal = 0; ReadOverlap.InternalHigh = 0; ReadOverlap.Offset = 0; ReadOverlap.OffsetHigh = 0; ReadOverlap.hEvent = CreateEvent (NULL, TRUE, TRUE, NULL); /* ** Create an event object for asyncronous writes */ WriteOverlap.Internal = 0; WriteOverlap.InternalHigh = 0; WriteOverlap.Offset = 0; WriteOverlap.OffsetHigh = 0; WriteOverlap.hEvent = CreateEvent (NULL, TRUE, TRUE, NULL); /* ** Get the current state of the com port as a basis for our device control block */ if (GetCommState (com_handle , &device_control)){ /* ** Communications settings */ device_control.BaudRate = baud; device_control.fParity = parity; device_control.ByteSize = (char)wordlen; device_control.StopBits = (char)(stopbits-1); /* ** Misc settings for flow control etc. */ device_control.fBinary = TRUE; // Binary mode data transfer device_control.fOutxCtsFlow = TRUE; // CTS flow control device_control.fRtsControl = RTS_CONTROL_HANDSHAKE; // RTS flow control device_control.fErrorChar = FALSE; // Dont put an error char into our input stream device_control.fOutxDsrFlow = FALSE; // No DSR flow control device_control.fDtrControl = DTR_CONTROL_ENABLE; // Enable control of DTR line device_control.fOutX = FALSE; // No XON/XOF flow control device_control.fInX = FALSE; // No XON/XOF flow control device_control.fAbortOnError = FALSE; // Device continues to send after an error /* ** Disable hardware flow control if required */ if (!flowcontrol){ device_control.fOutxCtsFlow = FALSE; // CTS flow control device_control.fRtsControl = RTS_CONTROL_DISABLE; // RTS flow control } /* ** Pass the device settings to windows */ if ( !SetCommState (com_handle , &device_control)){ errorval = GetLastError(); sprintf (errortxt, "RA95 -- SetCommState returned error code %d.\n", errorval); OutputDebugString (errortxt); //Serial_Port_Close(); //return (INVALID_HANDLE_VALUE); } }else{ errorval = GetLastError(); sprintf (errortxt, "RA95 -- GetCommState returned error code %d.\n", errorval); OutputDebugString (errortxt); } /* ** Set the device timeouts */ timeouts.ReadIntervalTimeout = 1000; //1 second between incoming bytes will time-out the read timeouts.ReadTotalTimeoutMultiplier = 0; //disable per byte timeouts timeouts.ReadTotalTimeoutConstant = 3000; //Read operations time out after 3 secs if no data received timeouts.WriteTotalTimeoutMultiplier= 500; //Allow 1/2 ms between each char write timeouts.WriteTotalTimeoutConstant = 1000; //Write operations time out after 1 sec + 1/2 sec per char if data wasnt sent if ( !SetCommTimeouts(com_handle, &timeouts) ){ errorval = GetLastError(); sprintf (errortxt, "RA95 -- SetCommTimeouts returned error code %d.\n", errorval); OutputDebugString (errortxt); //Serial_Port_Close(); //return (INVALID_HANDLE_VALUE); } return (com_handle); } /*********************************************************************************************** * WMC::Set_Modem_Dial_Type -- sets dial type to WC_TOUCH_TONE or WC_PULSE * * * * * * * * INPUT: WC_TOUCH_TONE or WC_PULSE * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:22PM ST : Created * *=============================================================================================*/ void WinModemClass::Set_Modem_Dial_Type(WinCommDialMethodType method) { DialingMethod = method; } /*********************************************************************************************** * WMC::Get_Modem_Status -- gets the status of the modem control lines * * * * * * * * INPUT: Nothing * * * * OUTPUT: Modem status. Any of the following bits CTS_SET, DSR_SET, RI_SET or CD_SET * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:24PM ST : Created * *=============================================================================================*/ unsigned WinModemClass::Get_Modem_Status(void) { DWORD modem_stat = 0; unsigned long return_stat = 0; /* ** Get the modem status */ GetCommModemStatus(PortHandle, &modem_stat); /* ** Translate the windows status flags to greenleaf flags */ if (MS_CTS_ON & modem_stat) return_stat |= CTS_SET; if (MS_DSR_ON & modem_stat) return_stat |= DSR_SET; if (MS_RING_ON & modem_stat) return_stat |= RI_SET; if (MS_RLSD_ON & modem_stat) return_stat |= CD_SET; return (return_stat); } /*********************************************************************************************** * WMC::Set_Serial_DTR -- set the state of the modems DTR control line * * * * * * * * INPUT: state - true or false * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:25PM ST : Created * *=============================================================================================*/ void WinModemClass::Set_Serial_DTR(BOOL state) { if (state){ EscapeCommFunction(PortHandle, SETDTR); }else{ EscapeCommFunction(PortHandle, CLRDTR); } } /*********************************************************************************************** * WMC::Serial_Port_Close -- close the port and free the port handle * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:26PM ST : Created * *=============================================================================================*/ void WinModemClass::Serial_Port_Close (void) { if (PortHandle){ CloseHandle(PortHandle); PortHandle = 0; } } /*********************************************************************************************** * WMC::Read_Serial_Chars -- copys chars from the windows serial buffer to the class buffer * * * * * * * * INPUT: Nothing * * * * OUTPUT: TRUE if any chars read * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:26PM ST : Created * *=============================================================================================*/ void Smart_Printf( char *format, ... ); BOOL WinModemClass::Read_Serial_Chars (void) { DWORD bytes_read; //amount of data read this time BOOL read_result; //result of ReadFile BOOL overlap_result; //result of GetOverlappedResult DWORD total_bytes_read=0; //total amount of data read int bytes_to_read; int i; /* ** Are we were still waiting for the last read operation to finish? */ if (WaitingForSerialCharRead){ /* ** Check the result of the last read operation */ bytes_read = 0; overlap_result = GetOverlappedResult(PortHandle, &ReadOverlap, &bytes_read, FALSE); /* ** If we got a good result from GetOverlappedResult and data was read then move it ** to our circular buffer */ if (overlap_result){ WaitingForSerialCharRead = FALSE; //Flag that we are no longer waiting for a read if (bytes_read){ for (i=0 ; i<bytes_read ; i++){ *(SerialBuffer + SerialBufferWritePtr++) = TempSerialBuffer[i]; SerialBufferWritePtr &= SIZE_OF_WINDOWS_SERIAL_BUFFER - 1; } total_bytes_read += bytes_read; } }else{ /* ** No chars were read since last time so just return */ if (GetLastError() == ERROR_IO_INCOMPLETE){ return (FALSE); } } } /* ** ** There is no outstanding read to wait for so try a new read ** */ /* ** Clear the event object */ ResetEvent(ReadOverlap.hEvent); /* ** ** Clear any communications errors and get the number of bytes in the in buffer ** */ DWORD error; COMSTAT status; bytes_to_read = 1; if (ClearCommError(PortHandle, &error, &status)){ InQueue = status.cbInQue; OutQueue = status.cbOutQue; if (error){ if (CE_FRAME & error) FramingErrors++; if (CE_IOE & error) IOErrors++; if (CE_OVERRUN & error) BufferOverruns++; if (CE_RXOVER & error) InBufferOverflows++; if (CE_RXPARITY & error)ParityErrors++; if (CE_TXFULL & error) OutBufferOverflows++; bytes_to_read = 0; }else{ bytes_to_read = min(status.cbInQue, SIZE_OF_WINDOWS_SERIAL_BUFFER); } } if (!bytes_to_read) bytes_to_read++; /* ** ** Start reading bytes ** */ do{ /* ** Try a read operation */ bytes_read = 0; read_result = ReadFile(PortHandle ,TempSerialBuffer ,bytes_to_read ,&bytes_read, &ReadOverlap); if (!read_result){ /* ** Read failed */ if (GetLastError() == ERROR_IO_PENDING){ /* ** But it threaded in the background OK so flag that we must wait for it to finish */ WaitingForSerialCharRead = TRUE; } }else{ /* ** Read was successful - copy to our circular buffer and try reading again */ if (bytes_read){ for (i=0 ; i<bytes_read ; i++){ *(SerialBuffer + SerialBufferWritePtr++) = TempSerialBuffer[i]; SerialBufferWritePtr &= SIZE_OF_WINDOWS_SERIAL_BUFFER - 1; } total_bytes_read += bytes_read; } bytes_to_read = 1; } } while (read_result == TRUE); return ((BOOL)total_bytes_read); } /*********************************************************************************************** * WMC::Read_From_Serial_Port -- retrieves chars from the internal class circular buffer which * * is filled from the windows serial buffer * * * * * * INPUT: buffer to copy to * * size of buffer * * * * OUTPUT: number of chars copied * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:27PM ST : Created * *=============================================================================================*/ int WinModemClass::Read_From_Serial_Port (unsigned char *dest_ptr, int buffer_len) { int bytes_read; int bytes_to_copy = 0; unsigned char *original_dest = dest_ptr; /* ** Get any outstanding data from the windows serial buffer into our class' circular buffer */ bytes_read = Read_Serial_Chars(); if (bytes_read){ /* ** Calculate how many bytes should be copied to the user buffer */ bytes_to_copy = SerialBufferWritePtr - SerialBufferReadPtr; if (bytes_to_copy <0 ) bytes_to_copy += SIZE_OF_WINDOWS_SERIAL_BUFFER; if (bytes_to_copy>buffer_len) bytes_to_copy = buffer_len; /* ** Loop to copy the data from the internal class buffer to the users buffer */ for (int i=0 ; i<bytes_to_copy ; i++){ /* ** Call the users echo function if required */ if (EchoFunction && *(SerialBuffer + SerialBufferReadPtr) !=13 ){ EchoFunction(*(SerialBuffer + SerialBufferReadPtr)); } *dest_ptr++ = *(SerialBuffer + SerialBufferReadPtr++); SerialBufferReadPtr &= SIZE_OF_WINDOWS_SERIAL_BUFFER-1; } } #ifdef LOG_MODEM if (bytes_read){ char *outstr = new char[bytes_read+1]; memcpy (outstr, original_dest, bytes_read); outstr[bytes_read] = 0; OutputDebugString (outstr); int handle; handle = open("COMMLOG.TXT",O_WRONLY | O_CREAT | O_APPEND | O_TEXT); if (handle != -1){ write(handle, original_dest, bytes_read); close(handle); } } #endif //LOG_MODEM return(bytes_to_copy); } /*********************************************************************************************** * WMC::Wait_For_Serial_Write -- waits for output buffer to empty * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:29PM ST : Created * *=============================================================================================*/ void WinModemClass::Wait_For_Serial_Write() { DWORD bytes_written; BOOL overlap_result; BOOL wait_send; CountDownTimerClass timer; if (WaitingForSerialCharWrite){ timer.Set(60*5); /* ** Wait until the overlapped port write is finished */ do{ wait_send = FALSE; overlap_result = GetOverlappedResult(PortHandle, &WriteOverlap, &bytes_written, FALSE); if (!overlap_result){ if (GetLastError() == ERROR_IO_INCOMPLETE){ wait_send = TRUE; } } }while(wait_send && timer.Time()); WaitingForSerialCharWrite = FALSE; } ResetEvent(WriteOverlap.hEvent); } /*********************************************************************************************** * WMC::Write_To_Serial_Port -- writes data to the serial port * * * * * * * * INPUT: ptr to data * * bytes to write * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:29PM ST : Created * *=============================================================================================*/ void WinModemClass::Write_To_Serial_Port (unsigned char *buffer, int length) { DWORD bytes_written; BOOL write_result; #ifdef LOG_MODEM if (length){ char *outstr = new char[length+1]; memcpy (outstr, buffer, length); outstr[length] = 0; OutputDebugString (outstr); int handle; handle = open("COMMLOG.TXT",O_WRONLY | O_CREAT | O_APPEND | O_TEXT); if (handle != -1){ write(handle, buffer, length); close(handle); } } #endif //LOG_MODEM /* ** Wait for the end of the last write operation */ Wait_For_Serial_Write(); /* ** Write the data to the port */ write_result = WriteFile (PortHandle, buffer, length, &bytes_written, &WriteOverlap); if (!write_result){ if (GetLastError() == ERROR_IO_PENDING){ WaitingForSerialCharWrite = TRUE; } } } /*********************************************************************************************** * WMC::Get_Modem_Result -- gets the result code from the modem after issuing an 'AT' command * * * * * * * * INPUT: delay for result time-out * * ptr to buffer to receive modem result * * length of buffer * * * * OUTPUT: un-elapsed delay * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:30PM ST : Created * *=============================================================================================*/ extern void CCDebugString (char*); int WinModemClass::Get_Modem_Result(int delay, char *buffer, int buffer_len) { CountDownTimerClass timer; int dest_ptr; char *cr_ptr; char *lf_ptr; int copy_bytes; //OutputDebugString ("Wincomm - In Get_Modem_Result\n"); char abuffer [128]; sprintf (abuffer, "Wincomm - delay = %d, buffer = %p, buffer_len = %d.\n", delay, buffer, buffer_len); //OutputDebugString (abuffer); //OutputDebugString ("Wincomm - About to clear input buffer.\n"); memset(buffer, 0 ,buffer_len); dest_ptr = 0; /* ** Loop to parse data from the modem and discard any echoed 'AT' commands or spurious LF ** and CR characters. ** */ //OutputDebugString ("Wincomm - Entering do loop.\n"); do{ cr_ptr = NULL; //Set the result pointer to NULL //OutputDebugString ("Wincomm - About to set timer.\n"); timer.Set(delay/16); //Set and start the timer /* ** Keep reading from the serial port until... ** 1. we time out ** 2. the user abort by pressing ESC ** 3. the supplied buffer fills up ** 4. there is an app switch ** or 5. we get a CR character from the modem */ //OutputDebugString ("Wincomm - About to enter inner do loop.\n"); do{ if (AbortFunction){// && //_Kbd->Check()) { //OutputDebugString ("Wincomm - About to call abort function.\n"); int abort = AbortFunction(); sprintf (abuffer ,"Wincomm - About function returned %d.\n", abort); //OutputDebugString (abuffer); if (abort != COMMSUCCESS) return (abort); } /* ** If we had lost focus then abort */ if (AllSurfaces.SurfacesRestored){ //OutputDebugString ("Wincomm - Aborting due to loss of focus.\n"); return (0); } //OutputDebugString ("Wincomm - About to call Read_From_Serial_Port.\n"); dest_ptr += Read_From_Serial_Port((unsigned char*)(buffer + dest_ptr), (int)buffer_len-dest_ptr); sprintf (abuffer, "Wincomm - End of inner do loop. Time is %d.\n", timer.Time()); //OutputDebugString (abuffer); } while (timer.Time() && dest_ptr < buffer_len && !strchr (buffer, 13) ); //OutputDebugString ("Wincomm - Exited inner do loop.\n"); /* ** We need to discard this result if it is just an echo of the 'AT' command we sent */ cr_ptr = strstr(buffer,"AT"); if (cr_ptr){ if (*buffer == 'A' && *(buffer+1) == 'T' && strchr(buffer,13)){ //OutputDebugString ("Wincomm - Discarding command echo.\n"); cr_ptr = strchr(buffer,13); } } /* ** If it wasnt an AT echo then strip off any leading CR/LF characters */ if (!cr_ptr && (*buffer==13 || *buffer==10)){ cr_ptr = strchr(buffer,13); lf_ptr = strchr(buffer,10); if (!cr_ptr || (lf_ptr && lf_ptr < cr_ptr)){ //OutputDebugString ("Wincomm - Stripping CR/LF.\n"); cr_ptr = strchr(buffer,10); } } /* ** Copy the good stuff at the end of the buffer over the 'AT' or CR/LF chars */ if (cr_ptr){ //OutputDebugString ("Wincomm - Copying over start of buffer.\n"); while(*cr_ptr == 13 || *cr_ptr == 10){ cr_ptr++; } if (cr_ptr != buffer){ copy_bytes = (int)cr_ptr - (int)buffer; memcpy(buffer, cr_ptr, buffer_len - copy_bytes); dest_ptr -= copy_bytes; } } //OutputDebugString ("Wincomm - End of outer do loop.\n"); }while(cr_ptr); /* ** Terminate the string at the first CR character as this is what Greenleaf does */ if (strchr(buffer, 13)){ //OutputDebugString ("Truncating result string.\n"); *(strchr(buffer, 13)) = 0; } //sprintf (abuffer, "Wincomm - returning remaining delay of %d.\n", timer.Time()); //OutputDebugString (abuffer); return (timer.Time()); } /*********************************************************************************************** * WMC::Dial_Modem -- issue an 'ATD' command to the modem * * * * * * * * INPUT: string - number to dial * * * * OUTPUT: Nothing * * * * WARNINGS: Use Set_Modem_Dial_Type to choose pulse or tone dialling * * * * HISTORY: * * 1/10/96 2:32PM ST : Created * *=============================================================================================*/ void WinModemClass::Dial_Modem(char *dial_number) { char dial_string[80]; /* ** Create the dial command to send to the modem */ strcpy (dial_string, "ATD"); if (DialingMethod == WC_TOUCH_TONE){ strcat(dial_string, "T"); }else{ strcat(dial_string, "P"); } /* ** Stick a carriage return on the end */ strcat (dial_string, dial_number); strcat (dial_string, "\r"); /* ** Write the dial command to the serial port and wait for the write to complete */ Write_To_Serial_Port ((unsigned char*)dial_string, strlen(dial_string)); Wait_For_Serial_Write(); } /*********************************************************************************************** * WMC::Send_Command_To_Modem -- send an 'AT' command to the modem and await a response * * * * * * * * INPUT: command string * * terminator byte (usually "\r") * * ptr to buffer to receive modem result code * * length of buffer * * timeout delay for waiting for result * * number of times to retry the command * * * * OUTPUT: result code * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:33PM ST : Created * *=============================================================================================*/ int WinModemClass::Send_Command_To_Modem(char *command, char terminator, char *buffer, int buflen, int delay, int retries) { int times; unsigned char tmp_string[80]; char tmp_buff[80]; char term_string[2]; int time; /* ** Build the terminator string */ term_string[0] = terminator; term_string[1] = 0; /* ** Create the command from the supplied command and terminator */ strcpy((char*)tmp_string, command); strcat((char*)tmp_string, term_string); /* ** Flush out any pending characters from the port */ unsigned char nothing_buff[80]; Read_From_Serial_Port(nothing_buff,80); Sleep (100); Read_From_Serial_Port(nothing_buff,80); for (times = 0 ; times<retries; times++){ /* ** Write the command to the serial port */ //Smart_Printf("%s",tmp_string); Write_To_Serial_Port (tmp_string, strlen((char*)tmp_string)); Wait_For_Serial_Write(); //Delay(120); /* ** Wait for the result of the command from the modem */ memset(tmp_buff, 0, 80); time = Get_Modem_Result(delay, tmp_buff, 80); //Smart_Printf("%s%s",tmp_buff,"\r"); /* ** If it is a pretty standard result then just return */ if (!strcmp(tmp_buff,"0")) return (MODEM_CMD_0); if (strstr(tmp_buff,"OK")) return (MODEM_CMD_OK); if (strstr(tmp_buff,"ERROR")) return (MODEM_CMD_ERROR); /* ** If the result was a 3 digit number then copy it to the users buffer and return OK */ if (strlen(tmp_buff)==3){ if ( (tmp_buff[0] >= '0' && tmp_buff[0] <='9') && (tmp_buff[1] >= '0' && tmp_buff[1] <='9') && (tmp_buff[2] >= '0' && tmp_buff[2] <='9')) { strncpy(buffer, tmp_buff, MIN(buflen, 80)); return (MODEM_CMD_OK); } } /* ** It was a non-standard(ish) result so copy it to the users buffer */ strncpy(buffer, tmp_buff, MIN(buflen, 80)); /* ** Spurious write for no apparent reason. Well it was there in the DOS version so... */ Sleep (100); Write_To_Serial_Port((unsigned char*)"\r",1); Wait_For_Serial_Write(); Sleep (100); } return (ASTIMEOUT); } /*********************************************************************************************** * WMC::Set_Echo_Function -- set up the echo function pointer * * * * * * * * INPUT: ptr to echo function * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:35PM ST : Created * *=============================================================================================*/ void WinModemClass::Set_Echo_Function( void ( *func )( char c) ) { EchoFunction = func; } /*********************************************************************************************** * WMC::Set_Abort_Function -- set up the abort function pointer * * * * * * * * INPUT: ptr to abort function * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 1/10/96 2:35PM ST : Created * *=============================================================================================*/ void WinModemClass::Set_Abort_Function(int (*func)(void)) { AbortFunction = func; } /*********************************************************************************************** * WMC::Get_Port_Handle -- returns a handle to the communications port * * * * * * * * INPUT: Nothing * * * * OUTPUT: Com port handle * * * * WARNINGS: None * * * * HISTORY: * * 5/23/96 1:25PM ST : Created * *=============================================================================================*/ HANDLE WinModemClass::Get_Port_Handle(void) { return (PortHandle); }