Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2390 lines
70 KiB

/*
* Detection routines for modems.
*
* Microsoft Confidential
* Copyright (c) Microsoft Corporation 1993-1994
* All rights reserved
*
*/
#include "proj.h"
#include <stdarg.h>
#define CR '\r'
#define LF '\n'
#define RESPONSE_RCV_DELAY 5000 // A long time (5 secs) because
// once we have acquired the modem
// we can afford the wait.
#define MAX_QUERY_RESPONSE_LEN 100
#define MAX_SHORT_RESPONSE_LEN 30 // echo of ATE0Q0V1<cr> and
// <cr><lf>ERROR<cr><lf> by a
// little margin
#define ATI0_LEN 30 // amount of the ATI0 query that
// we will save
#define ATI0 0 // we will use this result completely
#define ATI4 4 // we will use this result completely,
// if it matches the Hayes format
// (check for 'a' at beginning)
// Return values for the FindModem function
//
#define RESPONSE_USER_CANCEL (-4) // user requested cancel
#define RESPONSE_UNRECOG (-3) // got some chars, but didn't
// understand them
#define RESPONSE_NONE (-2) // didn't get any chars
#define RESPONSE_FAILURE (-1) // internal error or port error
#define RESPONSE_OK 0 // matched with index of <cr><lf>OK<cr><lf>
#define RESPONSE_ERROR 1 // matched with index of <cr><lf>ERROR<cr><lf>
#ifdef WIN32
typedef HANDLE HPORT; // variable type used in FindModem
#else
typedef int HPORT; // variable type used in FindModem
#endif
#define IN_QUEUE_SIZE 8192
#define OUT_QUEUE_SIZE 256
#define RCV_DELAY 2000
#define CHAR_DELAY 100
#define CBR_HACK_115200 0xff00 // This is how we set 115,200 on
// Win 3.1 because of a stupid bug.
#pragma data_seg(DATASEG_READONLY)
TCHAR const FAR c_szPortPrefix[] = TEXT("\\\\.\\%s"); // "\\.\" in ASCII
char const FAR c_szModemIdPrefix[] = "UNIMODEM";
char const FAR c_szNoEcho[] = "ATE0Q0V1\r";
char const FAR c_szReset[] = "ATZ\r";
char const FAR c_szATPrefix[] = "AT";
char const FAR c_szATSuffix[] = "\r";
char const FAR c_szBlindOnCheck[] = "X3";
char const FAR c_szBlindOnCheckAlternate[] = "X0";
char const FAR c_szBlindOffCheck[] = "X4";
// WARNING! If you change these, you will have to change ALL of your
// CompatIDs!!!
char const FAR *c_aszQueries[] = { "ATI0\r", "ATI1\r", "ATI2\r", "ATI3\r",
"ATI4\r", "ATI5\r", "ATI6\r", "ATI7\r",
"ATI8\r", "ATI9\r", "ATI10\r", "AT%V\r" };
// these are mostly for #'s. If a numeric is adjoining one of these, it
// will not be treated as special.
// Warning: Change any of these and you have to redo all of the CRCs!!!!
// Case insensitive compares
char const FAR *c_aszIncludes[] = { "300",
"1200",
"2400", "2,400",
"9600", "96", "9.6", "9,600",
"12000", "120", "12.0", "12,000",
"14400", "144", "14.4", "14,400",
"16800", "168", "16.8", "16,800",
"19200", "192", "19.2", "19,200",
"21600", "216", "21.6", "21,600",
"24000", "240", "24.0", "24,000",
"26400", "264", "26.4", "26,400",
"28800", "288", "28.8", "28,800",
"31200", "312", "31.2", "31,200",
"33600", "336", "33.6", "33,600",
"36000", "360", "36.0", "36,000",
"38400", "384", "38.4", "38,400",
"9624", "32bis", "42bis", "V32",
"V.32", "V.FC", "FAST", "FAX",
"DATA", "VOICE", "" };
// Matches will be case-insensitive
char const FAR *c_aszExcludes[] = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
"" };
// case sensitive matching
char const FAR *c_aszBails[] = { "CONNECT", "RING", "NO CARRIER",
"NO DIALTONE", "BUSY", "NO ANSWER", "=" };
// start after CBR_9600
UINT const FAR c_auiUpperBaudRates[] = { CBR_19200, CBR_38400, CBR_56000,
CBR_HACK_115200 };
char const FAR *c_aszResponses[] = { "\r\nOK\r\n", "\r\nERROR\r\n" };
// Some MultiTech's send 0<cr> in response to AT%V (they go
// into numeric mode)
char const FAR *c_aszNumericResponses[] = { "0\r", "4\r" };
char const FAR c_szHex[] = "0123456789abcdef";
struct DCE {
char pszStr[4];
DWORD dwDce;
DWORD dwAlternateDce;
} DCE_Table[] = {
"384", 38400, 300, // Some PDI's will report 38400, and this won't work for them. Screw 'em.
"360", 36000, 300,
"336", 33600, 300,
"312", 31200, 300,
"288", 28800, 2400,
"264", 26400, 2400,
"240", 24000, 2400,
"216", 21600, 2400,
"192", 19200, 1200,
"168", 16800, 1200,
"14", 14400, 1200,
"120", 12000, 1200,
"9", 9600, 300,
"2", 2400, 300,
"1", 1200, 300,
"3", 300, 0
};
#pragma data_seg()
// BUGBUG - WARNING: Not for DBCS usage - is not a real bugbug since modems aren't DBCS.
//#define isupper(ch) (((ch) >= 'A' && (ch) <= 'Z') ? TRUE : FALSE)
//#define islower(ch) (((ch) >= 'a' && (ch) <= 'z') ? TRUE : FALSE)
//#define isalpha(ch) ((toupper(ch) >= 'A' && toupper(ch) <= 'Z') ? TRUE : FALSE)
#define toupper(ch) (islower(ch) ? (ch) - 'a' + 'A' : (ch))
#define ishex(ch) ((toupper(ch) >= 'A' && toupper(ch) <= 'F') ? TRUE : FALSE)
#define isnum(num) ((num >= '0' && num <= '9') ? TRUE : FALSE)
#define MAX_TEST_TRIES 4
#define MAX_LOG_PRINTF_LEN 256
void _cdecl LogPrintf(HANDLE hLog, UINT uResourceFmt, ...);
DWORD NEAR PASCAL FindModem(PDETECTCALLBACK pdc, HPORT hPort);
#ifdef DEBUG
void HexDump( TCHAR *, LPBYTE lpBuf, DWORD cbLen);
#define HEXDUMP(_a, _b, _c) HexDump(_a, _b, _c)
#else // !DEBUG
#define HEXDUMP(_a, _b, _c) ((void) 0)
#endif
DWORD
PRIVATE
IdentifyModem(
IN PDETECTCALLBACK pdc,
IN HPORT hPort,
OUT LPTSTR pszModemName,
IN HANDLE hLog,
OUT LPSTR lpszATI0Result);
BOOL
PRIVATE
TestBaudRate(
IN HPORT hPort,
IN UINT uiBaudRate,
IN DWORD dwRcvDelay,
IN PDETECTCALLBACK pdc,
OUT BOOL FAR *lpfCancel);
DWORD
NEAR PASCAL
SetPortBaudRate(
HPORT hPort,
UINT BaudRate);
int
NEAR PASCAL
ReadResponse(
HPORT hPort,
LPBYTE lpvBuf,
UINT uRead,
BOOL fMulti,
DWORD dwRcvDelay,
PDETECTCALLBACK pdc);
UINT
NEAR PASCAL
ReadPort(
HPORT hPort,
LPBYTE lpvBuf,
UINT uRead,
DWORD dwRcvDelay,
int FAR *lpiError,
PDETECTCALLBACK pdc,
BOOL FAR *lpfCancel);
DWORD
PRIVATE
ConstructGenericInf(
PDETECTCALLBACK pdc,
HPORT hPort,
LPCSTR lpszATI0Result,
HANDLE hLog,
PMODEM_DETECT_SIG pmds);
DWORD NEAR PASCAL CBR_To_Decimal(UINT uiCBR);
LPSTR NEAR ConvertToPrintable(LPCSTR pszIn, LPSTR pszOut, UINT uOut);
// Does a printf to a log, using a resource string as the format.
// WARNING: Do not try to print large strings.
void _cdecl LogPrintf(HANDLE hLog, UINT uResourceFmt, ...)
{
char pFmt[MAX_LOG_PRINTF_LEN];
char pOutput[MAX_LOG_PRINTF_LEN];
UINT uCount, uWritten;
va_list vArgs;
if (hLog != INVALID_HANDLE_VALUE)
{
if (LoadStringA(g_hinst, uResourceFmt, pFmt, MAX_LOG_PRINTF_LEN))
{
va_start(vArgs, uResourceFmt);
uCount = wvsprintfA(pOutput, pFmt, vArgs);
va_end(vArgs);
WriteFile(hLog, (LPCVOID)pOutput, uCount, &uWritten, NULL);
}
}
}
int FAR PASCAL mylstrncmp(LPCSTR pchSrc, LPCSTR pchDest, int count)
{
for ( ; count && *pchSrc == *pchDest; pchSrc++, pchDest++, count--) {
if (*pchSrc == '\0')
return 0;
}
return count;
}
int FAR PASCAL mylstrncmpi(LPCSTR pchSrc, LPCSTR pchDest, int count)
{
for ( ; count && toupper(*pchSrc) == toupper(*pchDest); pchSrc++, pchDest++, count--) {
if (*pchSrc == '\0')
return 0;
}
return count;
}
#ifdef WIN32
DWORD
PRIVATE
MyWriteComm(
HANDLE hPort,
LPBYTE lpBuf,
DWORD cbLen)
{
COMMTIMEOUTS cto;
DWORD cbLenRet;
HEXDUMP (TEXT("Write"), lpBuf, cbLen);
// Set comm timeout
if (!GetCommTimeouts(hPort, &cto))
{
ZeroMemory(&cto, sizeof(cto));
};
// Allow a constant write timeout
cto.WriteTotalTimeoutMultiplier = 0;
cto.WriteTotalTimeoutConstant = 1000; // 1 second
SetCommTimeouts(hPort, &cto);
// Synchronous write
WriteFile(hPort, lpBuf, cbLen, &cbLenRet, NULL);
return cbLenRet;
}
#define MyFlushComm PurgeComm
#define MyCloseComm CloseHandle
#else // WIN32
#define MyWriteComm WriteComm
#define MyCloseComm CloseComm
#ifndef PURGE_TXCLEAR
#define PURGE_TXCLEAR 0x00000001
#endif
#ifndef PURGE_RXCLEAR
#define PURGE_RXCLEAR 0x00000002
#endif
BOOL
PRIVATE
MyFlushComm(
HANDLE hport,
DWORD dwAction)
{
if (IsFlagSet(dwAction, PURGE_TXCLEAR))
{
FlushComm((int)hport, 0);
}
if (IsFlagSet(dwAction, PURGE_RXCLEAR))
{
FlushComm((int)hport, 1);
}
return TRUE;
}
#endif // WIN32
/*----------------------------------------------------------
Purpose: Open the modem detection log.
Returns: handle to the open file
NULL if the file could not be opened
Cond: --
*/
HANDLE
PUBLIC
OpenDetectionLog()
{
TCHAR szLogPath[MAX_PATH];
UINT cch;
HANDLE hLog;
// open the log file
cch = GetWindowsDirectory(szLogPath, SIZECHARS(szLogPath));
if (0 == cch)
{
hLog = INVALID_HANDLE_VALUE;
}
else
{
if (*CharPrev(szLogPath, szLogPath + cch) != TEXT('\\'))
{
szLogPath[cch++] = (TCHAR)'\\';
}
LoadString(g_hinst, IDS_DET_LOG_NAME, &szLogPath[cch],
SIZECHARS(szLogPath) - (cch - 1));
// error return will be HFILE_ERROR, so no need to check since
// we will handle that during writes
TRACE_MSG(TF_DETECT, "Opening detection log file '%s'", (LPTSTR)szLogPath);
hLog = CreateFile(szLogPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hLog == INVALID_HANDLE_VALUE)
{
TRACE_MSG(TF_ERROR, "Modem CreateFile() failed!");
}
}
return hLog;
}
/*----------------------------------------------------------
Purpose: Closes the detection log file.
Returns: --
Cond: --
*/
void
PUBLIC
CloseDetectionLog(
IN HANDLE hLog)
{
if (INVALID_HANDLE_VALUE != hLog)
{
TRACE_MSG(TF_DETECT, "Closing detection log");
CloseHandle(hLog);
}
}
/*----------------------------------------------------------
Purpose: Set the current port we're updating in the progress
bar.
Returns: --
Cond: --
*/
void
PRIVATE
DetectSetPort(
PDETECTCALLBACK pdc,
LPCTSTR lpcszName)
{
if (pdc && pdc->pfnCallback)
{
try
{
pdc->pfnCallback(DSPM_SETPORT, (LPARAM)lpcszName, pdc->lParam);
}
except (EXCEPTION_EXECUTE_HANDLER)
{
;
}
}
}
/*----------------------------------------------------------
Purpose: Set the current msg we're updating in the Detect wizard page.
Returns: --
Cond: --
*/
void
PUBLIC
DetectSetStatus(
PDETECTCALLBACK pdc,
DWORD nStatus)
{
if (pdc && pdc->pfnCallback)
{
try
{
pdc->pfnCallback(DSPM_SETSTATUS, (LPARAM)nStatus, pdc->lParam);
}
except (EXCEPTION_EXECUTE_HANDLER)
{
;
}
}
}
/*----------------------------------------------------------
Purpose: Query's whether we are supposed to cancel the detection. Also
yields.
Returns: TRUE if we should cancel. FALSE otherwise.
Cond: --
*/
BOOL
PRIVATE
DetectQueryCancel(
PDETECTCALLBACK pdc)
{
BOOL bRet = FALSE;
if (pdc && pdc->pfnCallback)
{
try
{
bRet = pdc->pfnCallback(DSPM_QUERYCANCEL, 0, pdc->lParam);
}
except (EXCEPTION_EXECUTE_HANDLER)
{
bRet = FALSE;
}
}
return bRet;
}
/*----------------------------------------------------------
Purpose: This function queries the given port to find a legacy
modem.
If a modem is detected and we recognize it (meaning
we have the hardware ID in our INF files), or if we
successfully create a generic hardware ID and
inf file, then this function also creates the phantom
device instance of this modem.
NOTE (scotth): in Win95, this function only detected
the modem and returned the hardware ID and device
description. For NT, this function also creates the
device instance. I made this change because it is
faster.
Returns: NO_ERROR
ERROR_PORT_INACCESSIBLE
ERROR_NO_MODEM
ERROR_ACCESS_DENIED
ERROR_CANCELLED
Cond: --
*/
DWORD
PUBLIC
DetectModemOnPort(
IN HDEVINFO hdi,
IN PDETECTCALLBACK pdc,
IN HANDLE hLog,
IN LPCTSTR pszPort,
OUT PMODEM_DETECT_SIG pmds,
OUT PSP_DEVINFO_DATA pdevDataOut)
{
DWORD dwRet;
HPORT hPort;
HCURSOR hCursor;
DWORD cbLen;
char szATI0Result[ATI0_LEN];
char szASCIIPort[LINE_LEN];
TCHAR szLocalHardwareID[MAX_MODEM_ID_LEN+1];
#if defined(WIN32)
TCHAR szPrefixedPort[MAX_BUF + sizeof(c_szPortPrefix)];
#endif
DBG_ENTER(DetectModemOnPort);
ASSERT(pszPort);
ASSERT(pmds);
*szLocalHardwareID=0;
#ifdef UNICODE
// Convert the port name to ASCII
WideCharToMultiByte(CP_ACP, 0, pszPort, -1, szASCIIPort, SIZECHARS(szASCIIPort),
NULL, NULL);
#else
lstrcpyA(szASCIIPort, pszPort);
#endif // UNICODE
#ifdef SKIP_MOUSE_PORT
// Is this port used by a serial mouse?
if (0 == lstrcmpi(g_szMouseComPort, pszPort))
{
// Yes; skip it
TRACE_MSG(TF_ERROR, "Serial mouse on this port, skipping");
dwRet = ERROR_NO_MODEM;
goto Cleanup;
}
#endif
// Open the port
DetectSetPort(pdc, pszPort);
#if !defined(WIN32)
hPort = OpenComm(pszPort, IN_QUEUE_SIZE, OUT_QUEUE_SIZE);
#else
wsprintf(szPrefixedPort, c_szPortPrefix, pszPort);
hPort = CreateFile(szPrefixedPort,
GENERIC_WRITE | GENERIC_READ,
0, NULL,
OPEN_EXISTING, 0, NULL);
#endif
if (hPort == INVALID_HANDLE_VALUE) {
dwRet = GetLastError();
if (dwRet == ERROR_ACCESS_DENIED) {
TRACE_MSG(TF_ERROR, "Port is in use by another app");
LogPrintf(hLog, IDS_DET_INUSE, szASCIIPort);
}
else {
TRACE_MSG(TF_ERROR, "Couldn't open port");
LogPrintf(hLog, IDS_DET_COULDNT_OPEN, szASCIIPort);
}
}
else {
SetupComm (hPort, IN_QUEUE_SIZE, OUT_QUEUE_SIZE);
TRACE_MSG(TF_DETECT, "Opened Port");
// Check for a modem on the port
hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
dwRet = FindModem(pdc, hPort);
if (dwRet == NO_ERROR)
{
// We have a modem. No matter what, we must return
// NO_ERROR (unless the user cancels).
LogPrintf(hLog, IDS_DET_FOUND, szASCIIPort);
// Initialize the detection signature
DetectSig_Init(pmds, 0, NULL, pszPort);
// Could we identify the modem?
dwRet = IdentifyModem(pdc, hPort, szLocalHardwareID, hLog, szATI0Result);
if (NO_ERROR != dwRet)
{
if (ERROR_CANCELLED != dwRet)
{
// No; attempt to construct a generic INF
TRACE_MSG(TF_DETECT, "Couldn't identify modem due to some kind of error. Will build one.");
goto DoConstructGenericInf; // save a little code space
}
}
else
{
// Yes; we now have the hardware ID for this modem.
// Copy it into pmds, truncating it to 16characters (including
// null)
lstrcpyn(
pmds->szHardwareID,
szLocalHardwareID,
sizeof(pmds->szHardwareID)/sizeof(TCHAR)
);
SetFlag(pmds->dwMask, MDSM_HARDWAREID);
DetectSetStatus(pdc, DSS_CHECK_FOR_COMPATIBLE);
// Is there a device that is compatible with this
// hardware ID? If there is, this function will also
// create a phantom device instance with a working
// set of compatible drivers.
if (CplDiCreateCompatibleDeviceInfo(hdi,
szLocalHardwareID,
NULL,
pmds->szDeviceDesc,
SIZECHARS(pmds->szDeviceDesc),
pdevDataOut))
{
// Yes; a device instance was created!
SetFlag(pmds->dwMask, MDSM_DEVICEDESC);
}
else
{
// Doh! No matching inf for this compat id. Must create a generic one...
TRACE_MSG(TF_DETECT, "No compatible infs found. Will build one.");
DoConstructGenericInf:
dwRet = ConstructGenericInf(pdc, hPort, szATI0Result, hLog, pmds);
if (NO_ERROR == dwRet)
{
LoadString(g_hinst, IDS_GENERIC_DESC, pmds->szDeviceDesc, SIZECHARS(pmds->szDeviceDesc));
SetFlag(pmds->dwMask, MDSM_DEVICEDESC);
DetectSetStatus(pdc, DSS_CHECK_FOR_COMPATIBLE);
// Try creating a device that is compatible with
// the generic modem
if ( !CplDiCreateCompatibleDeviceInfo(hdi,
pmds->szHardwareID,
pmds->szDeviceDesc,
NULL, 0,
pdevDataOut) )
{
// This still failed. Give up.
dwRet = GetLastError();
ASSERT(NO_ERROR != dwRet);
// BUG BUG: we should do this or set this to some
// other error (and clean up the detected modem instance!)
//dwRet = ERROR_CANCELLED;
}
}
}
if (DetectQueryCancel(pdc))
{
TRACE_MSG(TF_DETECT, "User pressed cancel.");
dwRet = ERROR_CANCELLED;
}
}
// Reset
cbLen = lstrlenA(c_szReset);
if (MyWriteComm(hPort, (LPBYTE)c_szReset, cbLen) == cbLen &&
ERROR_CANCELLED != dwRet)
{
// Now read the result of the write and ignore it
if (RESPONSE_OK != ReadResponse(hPort, NULL,
MAX_SHORT_RESPONSE_LEN,
FALSE, 0, pdc))
{
TRACE_MSG(TF_ERROR, "Reset result failed");
}
}
else
{
TRACE_MSG(TF_ERROR, "Couldn't write Reset string");
}
}
else
{
if (ERROR_CANCELLED != dwRet)
{
LogPrintf(hLog, IDS_DET_NOT_FOUND, szASCIIPort);
}
}
SetCursor(hCursor);
// Flush before closing becuase if there are characters stuck in the queue,
// serial.386 will take 30 seconds to time out.
MyFlushComm(hPort, PURGE_RXCLEAR | PURGE_TXCLEAR);
EscapeCommFunction(hPort, CLRDTR);
MyCloseComm(hPort);
} // hPort < 0
#ifdef SKIP_MOUSE_PORT
Cleanup:
#endif
DBG_EXIT_DWORD(DetectModemOnPort, dwRet);
return dwRet;
}
#define HAYES_COMMAND_LEN 40
// Try sending the requested command and return whether it works or not
// Try MAX_TEST_TRIES
// Returns: TRUE on SUCCESS
// FALSE on failure (including user cancels)
BOOL
NEAR PASCAL
TestCommand(
HPORT hPort,
PDETECTCALLBACK pdc,
BOOL FAR *lpfCancel,
LPCSTR szCommand)
{
DWORD cbLen;
int iTries = MAX_TEST_TRIES;
char szFullCommand[HAYES_COMMAND_LEN + 1];
lstrcpyA(szFullCommand, c_szATPrefix);
lstrcatA(szFullCommand, szCommand);
lstrcatA(szFullCommand, c_szATSuffix);
cbLen = lstrlenA(szFullCommand);
TRACE_MSG(TF_DETECT, "Testing '%s'", (LPTSTR)szFullCommand);
*lpfCancel = FALSE;
while (iTries--)
{
// clear the read queue, there shouldn't be anything there
MyFlushComm(hPort, PURGE_RXCLEAR);
if (MyWriteComm(hPort, szFullCommand, cbLen) == cbLen)
{
switch(ReadResponse(hPort, NULL, MAX_SHORT_RESPONSE_LEN, FALSE, RESPONSE_RCV_DELAY, pdc))
{
case RESPONSE_OK:
TRACE_MSG(TF_DETECT, "Command returned OK!");
return TRUE;
case RESPONSE_ERROR:
TRACE_MSG(TF_DETECT, "Command returned ERROR!");
return FALSE;
case RESPONSE_USER_CANCEL:
*lpfCancel = TRUE;
return FALSE;
}
}
}
return FALSE;
}
// Checks various AT commands to see if they work. Updates the modem
// detection signature (*pms) as necessary.
// Returns FALSE on error or user cancel
BOOL
PRIVATE
CheckCommands(
IN HPORT hPort,
IN PDETECTCALLBACK pdc,
OUT BOOL FAR *lpfCancel,
OUT PMODEM_DETECT_SIG pmds)
{
// assume failures
pmds->szBlindOn[0] = 0;
pmds->szBlindOff[0] = 0;
//
// Blind On/Off - check blind off last so that the modem is in a state we prefer.
//
// test X3
if (TestCommand(hPort, pdc, lpfCancel, c_szBlindOnCheck))
{
ASSERT(SIZECHARS(pmds->szBlindOn) > lstrlenA(c_szBlindOnCheck)); // '>' accounts for null
lstrcpynA(pmds->szBlindOn, c_szBlindOnCheck, SIZECHARS(pmds->szBlindOn));
}
else
{
if (*lpfCancel)
{
return FALSE;
}
// X3 failed so try X0
if (TestCommand(hPort, pdc, lpfCancel, c_szBlindOnCheckAlternate))
{
ASSERT(SIZECHARS(pmds->szBlindOn) > lstrlenA(c_szBlindOnCheckAlternate)); // '>' accounts for null
lstrcpynA(pmds->szBlindOn, c_szBlindOnCheckAlternate, SIZECHARS(pmds->szBlindOn));
}
else
{
if (*lpfCancel)
{
return FALSE;
}
}
}
// test X4
if (TestCommand(hPort, pdc, lpfCancel, c_szBlindOffCheck))
{
ASSERT(SIZECHARS(pmds->szBlindOff) > lstrlenA(c_szBlindOffCheck)); // '>' accounts for null
lstrcpynA(pmds->szBlindOff, c_szBlindOffCheck, SIZECHARS(pmds->szBlindOff));
}
else
{
if (*lpfCancel)
{
return FALSE;
}
}
return TRUE;
}
// Returns the max dte for this modem in numeric form (ie. 57600, NOT CBR_56000)
// Returns 0 on error or user cancel
DWORD
PRIVATE
FindMaxDTE(
HPORT hPort,
PDETECTCALLBACK pdc,
BOOL FAR *lpfCancel)
{
UINT uiWorkingBaudRate, uiMaxBaudRate;
int iBaudRateIndex;
DCB DCB;
*lpfCancel = FALSE;
// Get current baud rate
if (GetCommState(hPort, &DCB) < 0)
{
TRACE_MSG(TF_ERROR, "GetCommState failed");
return 0;
}
uiWorkingBaudRate = uiMaxBaudRate = DCB.BaudRate;
// Should we try even faster (are we at 9600 now?)?
if (uiWorkingBaudRate == CBR_9600)
{
for (iBaudRateIndex = 0; iBaudRateIndex < ARRAYSIZE(c_auiUpperBaudRates); iBaudRateIndex++)
{
// if baud rate works then break out and use it!
if (TestBaudRate(hPort, c_auiUpperBaudRates[iBaudRateIndex], 0, pdc, lpfCancel))
{
uiMaxBaudRate = c_auiUpperBaudRates[iBaudRateIndex];
}
else
{
if (*lpfCancel)
{
return 0;
}
break;
}
}
// we now have our maximum baud rate and a good baud rate for interacting with the modem
// go back to our safe baud rate (safe w/respect to computer overruns)
if (!TestBaudRate(hPort, uiWorkingBaudRate, 0, pdc, lpfCancel))
{
if (*lpfCancel)
{
return 0;
}
TRACE_MSG(TF_ERROR, "return to safe baudrate failed!");
}
}
return CBR_To_Decimal(uiMaxBaudRate);
}
// Returns the max dce for this modem in numeric form (ie. 9600, NOT CBR_9600)
// Returns 0 on error
DWORD NEAR PASCAL FindMaxDCE(HPORT hPort, LPCSTR lpszATI0Result, DWORD dwMaxDTE)
{
int i;
DWORD dwMaxDCE = 0;
char FAR *lpszPtr = (char FAR *)lpszATI0Result;
// First we try to deduce our max dce by examining the ATI0 result
while (*lpszPtr) // skip past <cr> and <lf> and any other non numeric chars
{
if (isnum(*lpszPtr))
{
break;
}
lpszPtr++;
}
// did we hit the null-terminator?
if (*lpszPtr)
{
// nope! let's rock and roll, uh I mean, do some comparisons...
TRACE_MSG(TF_DETECT, "Analyzing ATI0 result '%s'", (LPTSTR)lpszPtr);
for (i = 0; i < ARRAYSIZE(DCE_Table); i++)
{
// do we have a match?
if (!mylstrncmp(lpszPtr, DCE_Table[i].pszStr, lstrlenA(DCE_Table[i].pszStr)))
{
// yes. is there an alternate that is more feasible?
// since DTE shouldn't be lower than DCE, let's check that...
if (DCE_Table[i].dwAlternateDce && dwMaxDTE < DCE_Table[i].dwDce)
{
dwMaxDCE = DCE_Table[i].dwAlternateDce;
TRACE_MSG(TF_DETECT, "Using alternate dce of %ld", dwMaxDCE);
}
else
{
dwMaxDCE = DCE_Table[i].dwDce;
TRACE_MSG(TF_DETECT, "A.I. dce of %ld", dwMaxDCE);
}
break;
}
}
}
// If that fails, make a guess based on the MaxDTE.
if (!dwMaxDCE)
{
TRACE_MSG(TF_DETECT, "Guessing at DCE speed");
switch (dwMaxDTE)
{
case 300:
dwMaxDCE = 300;
break;
case 1200:
dwMaxDCE = 1200;
break;
case 2400:
dwMaxDCE = 2400;
break;
case 9600:
case 19200:
case 38400:
dwMaxDCE = 9600;
break;
case 57600:
dwMaxDCE = 14400;
break;
case 115200:
dwMaxDCE = 28800;
break;
default:
ASSERT(0);
TRACE_MSG(TF_WARNING, "Hit a default in FindMaxDCE");
break;
}
}
// Check the logic of the DTE and DCE. Verify that it makes sense.
if (dwMaxDCE > dwMaxDTE)
{
dwMaxDCE = dwMaxDTE;
TRACE_MSG(TF_DETECT, "Auto-correcting Max DCE to be %ld bps", dwMaxDCE);
}
ASSERT(dwMaxDCE);
return dwMaxDCE;
}
// When we get there, the modem is open and in the ATE0Q0V1 state.
// Even if we can't talk to the modem, we have to return a generic inf.
// We are only called when a modem is seen on a port. (it may be gone now)
// Return NO_ERROR unles there was a user cancel (ERROR_CANCELLED)
DWORD
PRIVATE
ConstructGenericInf(
IN PDETECTCALLBACK pdc,
IN HPORT hPort,
IN LPCSTR lpszATI0Result,
IN HANDLE hLog,
OUT PMODEM_DETECT_SIG pmds)
{
DWORD dwMaxDTE;
DWORD dwMaxDCE;
BOOL fCancel;
ASSERT(pmds);
// our fallback
lstrcpy(pmds->szHardwareID, TEXT("MDMGEN"));
// Non-dte changing stuff first.
if (!CheckCommands(hPort, pdc, &fCancel, pmds))
{
if (fCancel)
{
return ERROR_CANCELLED;
}
else
{
TRACE_MSG(TF_ERROR, "CheckCommands failed");
return NO_ERROR;
}
}
if (!(dwMaxDTE = FindMaxDTE(hPort, pdc, &fCancel)))
{
if (fCancel)
{
return ERROR_CANCELLED;
}
else
{
TRACE_MSG(TF_ERROR, "FindMaxDTE failed");
return NO_ERROR;
}
}
TRACE_MSG(TF_DETECT, "Max DTE = %ld bps", dwMaxDTE);
LogPrintf(hLog, IDS_DET_DTE, dwMaxDTE);
if (!(dwMaxDCE = FindMaxDCE(hPort, lpszATI0Result, dwMaxDTE)))
{
TRACE_MSG(TF_ERROR, "FindMaxDCE failed");
return NO_ERROR;
}
TRACE_MSG(TF_DETECT, "Hypothesized Max DCE = %ld bps", dwMaxDCE);
SetFlag(pmds->dwFlags, MDSF_UPDATE_DEVCAPS);
pmds->dwMaxDTE = dwMaxDTE;
pmds->dwMaxDCE = dwMaxDCE;
return NO_ERROR;
}
// Switch to requested baud rate and try sending ATE0Q0V1 and return whether it works or not
// Try MAX_TEST_TRIES
// Returns: TRUE on SUCCESS
// FALSE on failure (including user cancels)
BOOL
PRIVATE
TestBaudRate(
IN HPORT hPort,
IN UINT uiBaudRate,
IN DWORD dwRcvDelay,
IN PDETECTCALLBACK pdc,
OUT BOOL FAR *lpfCancel)
{
DWORD cbLen;
int iTries = MAX_TEST_TRIES;
DBG_ENTER(TestBaudRate);
*lpfCancel = FALSE;
while (iTries--)
{
// try new baud rate
if (SetPortBaudRate(hPort, uiBaudRate) == NO_ERROR)
{
cbLen = lstrlenA(c_szNoEcho); // Send an ATE0Q0V1<cr>
// clear the read queue, there shouldn't be anything there
PurgeComm(hPort, PURGE_RXCLEAR);
if (MyWriteComm(hPort, (LPBYTE)c_szNoEcho, cbLen) == cbLen)
{
switch(ReadResponse(hPort, NULL, MAX_SHORT_RESPONSE_LEN, FALSE, dwRcvDelay, pdc))
{
case RESPONSE_OK:
DBG_EXIT(TestBaudRate);
return TRUE;
case RESPONSE_USER_CANCEL:
*lpfCancel = TRUE;
DBG_EXIT(TestBaudRate);
return FALSE;
}
}
}
}
DBG_EXIT(TestBaudRate);
return FALSE;
}
// Tries to figure out if there is a modem on the port. If there is, it
// will try to find a good speed to talk to it at (300,1200,2400,9600).
// Modem will be set to echo off, result codes on, and verbose result codes. (E0Q0V1)
DWORD
PRIVATE
FindModem(
PDETECTCALLBACK pdc,
HPORT hPort)
{
UINT uGoodBaudRate;
BOOL fCancel = FALSE;
DBG_ENTER(FindModem);
Sleep(500); // Wait, give time for modem to spew junk if any.
DetectSetStatus(pdc, DSS_LOOKING);
if (TestBaudRate(hPort, CBR_9600, 500, pdc, &fCancel))
{
uGoodBaudRate = CBR_9600;
}
else
{
if (!fCancel && TestBaudRate(hPort, CBR_2400, 500, pdc, &fCancel))
{
uGoodBaudRate = CBR_2400;
}
else
{
if (!fCancel && TestBaudRate(hPort, CBR_1200, 500, pdc, &fCancel))
{
uGoodBaudRate = CBR_1200;
}
else
{
// Hayes Accura 288 needs this much at 300bps
if (!fCancel && TestBaudRate(hPort, CBR_300, 1000, pdc, &fCancel))
{
uGoodBaudRate = CBR_300;
}
else
{
uGoodBaudRate = 0;
}
}
}
}
if (fCancel)
{
return ERROR_CANCELLED;
}
if (uGoodBaudRate)
{
DetectSetStatus(pdc, DSS_FOUND_MODEM);
DBG_EXIT(FindModem);
return NO_ERROR;
}
else
{
DetectSetStatus(pdc, DSS_FOUND_NO_MODEM);
DBG_EXIT(FindModem);
return ERROR_NO_MODEM;
}
}
DWORD NEAR PASCAL SetPortBaudRate(HPORT hPort, UINT BaudRate)
{
DCB DCB;
DBG_ENTER_UL(SetPortBaudRate, CBR_To_Decimal(BaudRate));
// Get a Device Control Block with current port values
if (GetCommState(hPort, &DCB) < 0) {
TRACE_MSG(TF_ERROR, "GetCommState failed");
DBG_EXIT(SetPortBaudRate);
return ERROR_PORT_INACCESSIBLE;
}
DCB.BaudRate = BaudRate;
DCB.ByteSize = 8;
DCB.Parity = 0;
DCB.StopBits = 0;
DCB.fBinary = 1;
DCB.fParity = 0;
// BUGBUG we should probably set flow control to off here.
if (SetCommState(hPort, &DCB) < 0) {
TRACE_MSG(TF_ERROR, "SetCommState failed");
DBG_EXIT(SetPortBaudRate);
return ERROR_PORT_INACCESSIBLE;
}
TRACE_MSG(TF_DETECT, "SetBaud rate to %lu", BaudRate);
DBG_EXIT(SetPortBaudRate);
return NO_ERROR;
}
#define MAX_RESPONSE_BURST_SIZE 8192
#define MAX_NUM_RESPONSE_READ_TRIES 30 // digicom scout needs this much + some safety
#define MAX_NUM_MULTI_TRIES 3 // Maximum number of 'q's to be sent when we aren't getting any response
// Read in response. Handle multi-pagers. Return a null-terminated string.
// Also returns response code.
// If lpvBuf == NULL
// cbRead indicates the max amount to read. Bail if more than this.
// Else
// cbRead indicates the size of lpvBuf
// This can not be a state driven (ie. char by char) read because we
// must look for responses from the end of a sequence of chars backwards.
// This is because "ATI2" on some modems will return
// "<cr><lf>OK<cr><lf><cr><lf>OK<cr><lf>" and we only want to pay attention
// to the final OK. Yee haw!
// Returns: RESPONSE_xxx
int
PRIVATE
ReadResponse(
HPORT hPort,
LPBYTE lpvBuf,
UINT cbRead,
BOOL fMulti,
DWORD dwRcvDelay,
PDETECTCALLBACK pdc)
{
int iRet = RESPONSE_UNRECOG;
LPBYTE pszBuffer;
BOOL fDoCopy = TRUE;
UINT uBufferLen, uResponseLen;
UINT uReadTries = MAX_NUM_RESPONSE_READ_TRIES;
UINT i;
UINT uOutgoingBufferCount = 0;
UINT uAllocSize = lpvBuf ? MAX_RESPONSE_BURST_SIZE : cbRead;
UINT uTotalReads = 0;
UINT uNumMultiTriesLeft = MAX_NUM_MULTI_TRIES;
int iError;
BOOL fCancel;
BOOL fHadACommError = FALSE;
ASSERT(cbRead);
// do we need to adjust cbRead?
if (lpvBuf)
{
cbRead--; // preserve room for terminator
}
// Allocate buffer
if (!(pszBuffer = (LPBYTE)LocalAlloc(LMEM_FIXED, uAllocSize)))
{
TRACE_MSG(TF_ERROR, "couldn't allocate memory.\n");
return RESPONSE_FAILURE;
}
while (uReadTries--)
{
// Read response into buffer
uBufferLen = ReadPort(hPort, pszBuffer, uAllocSize, dwRcvDelay, &iError, pdc, &fCancel);
// Did the user request a cancel?
if (fCancel)
{
iRet = RESPONSE_USER_CANCEL;
goto Exit;
}
// any errors?
if (iError)
{
// BUGBUG - Were screwed if we get an error during a multi-pager.
fHadACommError = TRUE;
#ifdef DEBUG
if (iError & CE_RXOVER) TRACE_MSG(TF_DETECT, "CE_RXOVER");
if (iError & CE_OVERRUN) TRACE_MSG(TF_DETECT, "CE_OVERRUN");
if (iError & CE_RXPARITY) TRACE_MSG(TF_DETECT, "CE_RXPARITY");
if (iError & CE_FRAME) TRACE_MSG(TF_DETECT, "CE_FRAME");
if (iError & CE_BREAK) TRACE_MSG(TF_DETECT, "CE_BREAK");
//if (iError & CE_CTSTO) TRACE_MSG(TF_DETECT, "CE_CTSTO");
//if (iError & CE_DSRTO) TRACE_MSG(TF_DETECT, "CE_DSRTO");
//if (iError & CE_RLSDTO) TRACE_MSG(TF_DETECT, "CE_RLSDTO");
if (iError & CE_TXFULL) TRACE_MSG(TF_DETECT, "CE_TXFULL");
if (iError & CE_PTO) TRACE_MSG(TF_DETECT, "CE_PTO");
if (iError & CE_IOE) TRACE_MSG(TF_DETECT, "CE_IOE");
if (iError & CE_DNS) TRACE_MSG(TF_DETECT, "CE_DNS");
if (iError & CE_OOP) TRACE_MSG(TF_DETECT, "CE_OOP");
if (iError & CE_MODE) TRACE_MSG(TF_DETECT, "CE_MODE");
#endif // DEBUG
}
// Did we not get any chars?
if (uBufferLen)
{
uNumMultiTriesLeft = MAX_NUM_MULTI_TRIES; // reset num multi tries left, since we got some data
uTotalReads += uBufferLen;
HEXDUMP(TEXT("Read"), pszBuffer, uBufferLen);
if (lpvBuf)
{
// fill outgoing buffer if there is room
for (i = 0; i < uBufferLen; i++)
{
if (uOutgoingBufferCount < cbRead)
{
lpvBuf[uOutgoingBufferCount++] = pszBuffer[i];
}
else
{
break;
}
}
// null terminate what we have so far
lpvBuf[uOutgoingBufferCount] = 0;
}
else
{
if (uTotalReads >= cbRead)
{
TRACE_MSG(TF_WARNING, "Bailing ReadResponse because we exceeded our maximum read allotment.");
goto Exit;
}
}
// try to find a matching response (crude but quick)
for (i = 0; i < ARRAYSIZE(c_aszResponses); i++)
{
// Verbose responses
uResponseLen = lstrlenA(c_aszResponses[i]);
// enough read to match this response?
if (uBufferLen >= uResponseLen)
{
if (!mylstrncmp(c_aszResponses[i], pszBuffer + uBufferLen - uResponseLen, uResponseLen))
{
iRet = i;
goto Exit;
}
}
// Numeric responses, for cases like when a MultiTech interprets AT%V to mean "go into numeric response mode"
uResponseLen = lstrlenA(c_aszNumericResponses[i]);
// enough read to match this response?
if (uBufferLen >= uResponseLen)
{
if (!mylstrncmp(c_aszNumericResponses[i], pszBuffer + uBufferLen - uResponseLen, uResponseLen))
{
DCB DCB;
TRACE_MSG(TF_WARNING, "went into numeric response mode inadvertantly. Setting back to verbose.");
// Get current baud rate
if (GetCommState(hPort, &DCB) == 0)
{
// Put modem back into Verbose response mode
if (!TestBaudRate(hPort, DCB.BaudRate, 0, pdc, &fCancel))
{
if (fCancel)
{
iRet = RESPONSE_USER_CANCEL;
goto Exit;
}
else
{
TRACE_MSG(TF_ERROR, "couldn't recover contact with the modem.");
// don't return error on failure, we have good info
}
}
}
else
{
TRACE_MSG(TF_ERROR, "GetCommState failed");
// don't return error on failure, we have good info
}
iRet = i;
goto Exit;
}
}
}
}
else
{
// have we received any chars at all (ie. from this or any previous reads)?
if (uTotalReads)
{
if (fMulti && uNumMultiTriesLeft)
{ // no match found, so assume it is a multi-pager, send a 'q'
// 'q' will catch those pagers that will think 'q' means quit.
// else, we will work with the pages that just need any ole' char.
uNumMultiTriesLeft--;
TRACE_MSG(TF_DETECT, "sending a 'q' because of a multi-pager.");
if (MyWriteComm(hPort, "q", 1) != 1)
{
TRACE_MSG(TF_ERROR, "WriteComm failed");
iRet = RESPONSE_FAILURE;
goto Exit;
}
continue;
}
else
{ // we got a response, but we didn't recognize it
ASSERT(iRet == RESPONSE_UNRECOG); // check initial setting
goto Exit;
}
}
else
{ // we didn't get any kind of response
iRet = RESPONSE_NONE;
goto Exit;
}
}
} // while
Exit:
// Free local buffer
LocalFree((HLOCAL)pszBuffer);
if (fHadACommError && RESPONSE_USER_CANCEL != iRet)
{
iRet = RESPONSE_FAILURE;
}
return iRet;
}
// WARNING - DO NOT CHANGE THIS FUNCTION!!!!!! YOU WILL HAVE TO DO A LOT OF WORK IF YOU DO!!!
// WARNING - DO NOT CHANGE THIS FUNCTION!!!!!! YOU WILL HAVE TO DO A LOT OF WORK IF YOU DO!!!
// WARNING - DO NOT CHANGE THIS FUNCTION!!!!!! YOU WILL HAVE TO DO A LOT OF WORK IF YOU DO!!!
// WARNING - DO NOT CHANGE THIS FUNCTION!!!!!! YOU WILL HAVE TO DO A LOT OF WORK IF YOU DO!!!
// WARNING - DO NOT CHANGE THIS FUNCTION!!!!!! YOU WILL HAVE TO DO A LOT OF WORK IF YOU DO!!!
// WARNING - DO NOT CHANGE THIS FUNCTION!!!!!! YOU WILL HAVE TO DO A LOT OF WORK IF YOU DO!!!
// You will have to change all of the inf files if you change the CRC results.
//
// Traverse lpszIn and copy "pure" chars to lpszOut.
// Remove any "impurities" such as:
// - "bails" - find one of these and cancel the rest of the line
// - numerics/hexadecimal on any line but ATI0 and possibly ATI4, and
// not including the "includes". Includes are only used if they
// aren't adjoining another #.
//
void NEAR CleanseResponse(int iQueryNumber, LPSTR lpszIn, LPSTR lpszOut)
{
LPSTR lpszSrc = lpszIn;
LPSTR lpszDest = lpszOut;
LPSTR FAR *lppsz;
BOOL fBail = FALSE;
BOOL fInclude = FALSE;
BOOL fExclude = FALSE;
BOOL fInBody = FALSE;
BOOL fCopyAll;
int j, iLen;
// Is this query exempt?
fCopyAll = (iQueryNumber == ATI0) ? TRUE : FALSE;
while (*lpszSrc)
{
// use any CRs or LFs we get before non-CRs/no-LFs.
if (*lpszSrc == CR || *lpszSrc == LF)
{
if (fInBody)
{
break;
}
else
{
*lpszDest++ = *lpszSrc++;
continue;
}
}
// is this the first char of the body?
if (!fInBody)
{
fInBody = TRUE; // indicate that the next CR or LF means termination.
if (iQueryNumber == ATI4 && *lpszSrc == 'a') // Hayes format capabilities string
{
fCopyAll = TRUE;
*lpszDest++ = *lpszSrc++;
continue;
}
}
if (fCopyAll) // are we jammin? (happens for ATI0 and ATI4 when first char is 'a')
{
// Only do a verbatim copy of the first word of the ATI0 response.
if (iQueryNumber == ATI0 && *lpszSrc == ' ')
{
fCopyAll = FALSE;
}
else
{
*lpszDest++ = *lpszSrc++;
}
continue;
}
// Do Bails
for (j = 0; j < ARRAYSIZE(c_aszBails); j++)
{
if (!mylstrncmp(lpszSrc, c_aszBails[j], lstrlenA(c_aszBails[j])))
{
fBail = TRUE;
break;
}
}
if (fBail) // should we bail?
{
TRACE_MSG(TF_DETECT, "early bail due to Bail '%s'", (LPTSTR)c_aszBails[j]);
break;
}
// Do Includes
lppsz = (LPSTR FAR *)c_aszIncludes;
while (**lppsz)
{
iLen = lstrlenA(*lppsz);
if (!mylstrncmpi(lpszSrc, *lppsz, iLen))
{
// check before and after to make sure they aren't numbers.
// catches 33489600394, 9600 won't be exempted from certain death in this case
if (!isnum(lpszSrc[-1]) && !isnum(lpszSrc[iLen]))
{
fInclude = TRUE;
break;
}
else
{
TRACE_MSG(TF_DETECT, "skipped an include because it was adjoined by numbers.");
}
}
lppsz++;
}
if (fInclude) // should we do the include?
{
fInclude = FALSE;
TRACE_MSG(TF_DETECT, "include ('%s' len = %d)", (LPTSTR)*lppsz, iLen);
CopyMemory(lpszDest, lpszSrc, (DWORD) iLen);
lpszSrc += iLen;
lpszDest += iLen;
continue;
}
// Do Excludes
lppsz = (LPSTR FAR *)c_aszExcludes;
while (**lppsz)
{
iLen = lstrlenA(*lppsz);
if (!mylstrncmpi(lpszSrc, *lppsz, iLen))
{
fExclude = TRUE;
break;
}
lppsz++;
}
if (fExclude) // should we do the exclude?
{
fExclude = FALSE;
TRACE_MSG(TF_DETECT, "exclude ('%s' len = %d)", (LPTSTR)*lppsz, iLen);
lpszSrc += iLen;
continue;
}
// Remove numbers
if (isnum(*lpszSrc))
{
lpszSrc++;
continue;
}
// Remove hex digits (keep only if adjoining 1 or 2 non-hex letters)
if (ishex(*lpszSrc))
{
// we know there is a char or null ahead of us...
if ((lpszSrc[1] >= 'g' && lpszSrc[1] <= 'z') ||
(lpszSrc[1] >= 'G' && lpszSrc[1] <= 'Z'))
{
*lpszDest++ = *lpszSrc++;
continue;
}
// is there a char before us?
if (lpszSrc > lpszIn)
{
if ((lpszSrc[-1] >= 'g' && lpszSrc[-1] <= 'z') ||
(lpszSrc[-1] >= 'G' && lpszSrc[-1] <= 'Z'))
{
*lpszDest++ = *lpszSrc++;
continue;
}
}
// we get here if we don't want to copy the hex digit
lpszSrc++;
continue;
}
// Remove lone letters (ex. 4M4 - reject, 4MM - accept)
if (isalpha(*lpszSrc))
{
if (!isalpha(lpszSrc[-1]) && !isalpha(lpszSrc[1]))
{
lpszSrc++;
continue;
}
}
// Remove certain punctuation: periods, commas, and spaces
// Will protect against things like "1992, 1993." -> "1992, 1993, 1994"
if (*lpszSrc == '.' || *lpszSrc == ',' || *lpszSrc == ' ')
{
lpszSrc++;
continue;
}
// whatever's left is okay to copy
*lpszDest++ = *lpszSrc++;
}
*lpszSrc = 0; // for log comparison sake
*lpszDest = 0;
}
#define MAX_RESPONSE_FAILURES 5
// When we get here, we have found a modem on the hPort. Our job is
// to interogate the modem and return a hardware ID.
// returns:
// NO_ERROR and a PnP id in pszModemName
// ERROR_PORT_INACCESSIBLE
// result of ATI0 query in lpszATI0Result
DWORD
PRIVATE
IdentifyModem(
IN PDETECTCALLBACK pdc,
IN HPORT hPort,
OUT LPTSTR pszModemName,
IN HANDLE hLog,
OUT LPSTR lpszATI0Result)
{
DWORD cbLen;
char pszReadBuf[MAX_QUERY_RESPONSE_LEN];
char pszCRCBuf[MAX_QUERY_RESPONSE_LEN];
LPSTR lpszPtr;
char pszPrintableBuf[MAX_QUERY_RESPONSE_LEN];
int iRet, i, j;
int iCurQuery;
int iResponseFailureCount;
ULONG ulCrcTable[256], ulCrc;
char szASCIIModem[MAX_MODEM_ID_LEN+1];
ASSERT(pszModemName);
ASSERT(lpszATI0Result);
*lpszATI0Result = (TCHAR)0; // null-terminate in case we fail
DBG_ENTER(IdentifyModem);
// Build CRC table
for (i = 0; i < 256; i++)
{
ulCrc = i;
for (j = 8; j > 0; j--)
{
if (ulCrc & 1)
{
ulCrc = (ulCrc >> 1) ^ 0xEDB88320L;
}
else
{
ulCrc >>= 1;
}
}
ulCrcTable[i] = ulCrc;
}
// Init ulCrc
ulCrc = 0xFFFFFFFF;
// Do each query.
for (iCurQuery = 0, iResponseFailureCount = 0;
iCurQuery < ARRAYSIZE(c_aszQueries); iCurQuery++)
{
DetectSetStatus(pdc, DSS_QUERYING_RESPONSES);
#ifndef PROFILE_MASSINSTALL
TRACE_MSG(TF_DETECT, "sending query '%s'.",
ConvertToPrintable(c_aszQueries[iCurQuery],
pszPrintableBuf,
sizeof(pszPrintableBuf)));
#endif
cbLen = lstrlenA(c_aszQueries[iCurQuery]);
if (MyWriteComm(hPort, (LPBYTE)c_aszQueries[iCurQuery], cbLen) != cbLen)
{
TRACE_MSG(TF_ERROR, "WriteComm failed");
iRet = RESPONSE_FAILURE; // spoof ReadResponse return for following switch handler
}
else
{
// Read in response. Handle multi-pagers. Return a null-terminated
// string containing all or part of the response. Return response
// code.
iRet = ReadResponse(hPort, (LPBYTE)pszReadBuf, sizeof(pszReadBuf), TRUE,
RESPONSE_RCV_DELAY, pdc);
#ifdef DEBUG
switch (iRet)
{
case RESPONSE_FAILURE:
TRACE_MSG(TF_DETECT, "ReadResponse returned RESPONSE_FAILURE");
break;
case RESPONSE_UNRECOG:
TRACE_MSG(TF_DETECT, "ReadResponse returned RESPONSE_UNRECOG");
break;
case RESPONSE_NONE:
TRACE_MSG(TF_DETECT, "ReadResponse returned RESPONSE_NONE");
break;
case RESPONSE_USER_CANCEL:
TRACE_MSG(TF_DETECT, "ReadResponse returned RESPONSE_USER_CANCEL");
break;
}
#endif // DEBUG
}
switch (iRet)
{
case RESPONSE_USER_CANCEL:
return ERROR_CANCELLED;
case RESPONSE_FAILURE:
case RESPONSE_UNRECOG:
case RESPONSE_NONE:
iResponseFailureCount++;
if (iResponseFailureCount >= MAX_RESPONSE_FAILURES)
{
TRACE_MSG(TF_ERROR, "had %d failed responses, aborting IdentifyModem()", iResponseFailureCount);
return ERROR_PORT_INACCESSIBLE;
}
else
{
DCB DCB;
BOOL fCancel;
// Get current baud rate
if (GetCommState(hPort, &DCB) < 0)
{
TRACE_MSG(TF_ERROR, "GetCommState failed");
return ERROR_PORT_INACCESSIBLE;
}
if (!TestBaudRate(hPort, DCB.BaudRate, 0, pdc, &fCancel)) // attempt to recover friendship with the modem
{
if (fCancel)
{
return ERROR_CANCELLED;
}
else
{
TRACE_MSG(TF_ERROR, "couldn't recover contact with the modem.");
return ERROR_PORT_INACCESSIBLE;
}
}
iCurQuery--; // try the same query again
}
break;
case RESPONSE_OK:
case RESPONSE_ERROR:
CleanseResponse(iCurQuery, pszReadBuf, pszCRCBuf);
if (ATI0 == iCurQuery)
{
ASSERT(ATI0_LEN <= sizeof(pszCRCBuf)); // make sure we are doing a legal copy
CopyMemory(lpszATI0Result, pszCRCBuf, ATI0_LEN);
}
lpszPtr = (LPSTR) pszCRCBuf;
while (*lpszPtr)
{
ulCrc = ((ulCrc >> 8) & 0x00FFFFFF) ^ ulCrcTable[(ulCrc ^ *lpszPtr++) & 0xFF];
}
LogPrintf(hLog, IDS_DET_OK_1,
ConvertToPrintable(c_aszQueries[iCurQuery],
pszPrintableBuf,
sizeof(pszPrintableBuf)));
LogPrintf(hLog, IDS_DET_OK_2,
ConvertToPrintable(pszReadBuf,
pszPrintableBuf,
sizeof(pszPrintableBuf)));
#ifndef PROFILE_MASSINSTALL
TRACE_MSG(TF_DETECT, "response (len=%d): %s", lstrlenA(pszReadBuf),
ConvertToPrintable(pszReadBuf,
pszPrintableBuf,
sizeof(pszPrintableBuf)));
#endif
LogPrintf(hLog, IDS_DET_OK_1,
ConvertToPrintable(c_aszQueries[iCurQuery],
pszPrintableBuf,
sizeof(pszPrintableBuf)));
LogPrintf(hLog, IDS_DET_OK_2,
ConvertToPrintable(pszCRCBuf,
pszPrintableBuf,
sizeof(pszPrintableBuf)));
#ifndef PROFILE_MASSINSTALL
TRACE_MSG(TF_DETECT, "converted form : %s",
ConvertToPrintable(pszCRCBuf,
pszPrintableBuf,
sizeof(pszPrintableBuf)));
#endif
iResponseFailureCount = 0; // reset count of failed responses to 0 for upcoming query
break;
default:
TRACE_MSG(TF_ERROR, "hit a default it shouldn't have hit.");
ASSERT(0);
return ERROR_PORT_INACCESSIBLE;
}
}
// Finish up CRC
ulCrc ^= 0xFFFFFFFF;
lstrcpyA(szASCIIModem, c_szModemIdPrefix);
j = lstrlenA(szASCIIModem);
// Convert CRC into hex text.
for (i = 0; i < 8; i++)
{
szASCIIModem[i+j] = "0123456789ABCDEF"[(ulCrc>>((7-i)<<2))&0xf];
}
szASCIIModem[i+j] = 0; // null-terminate
DBG_EXIT(IdentifyModem);
TRACE_MSG(TF_DETECT, "final CRC = 0x%8lx (ascii = %s)", ulCrc, szASCIIModem);
LogPrintf(hLog, IDS_DET_ID, szASCIIModem);
#ifdef UNICODE
MultiByteToWideChar(CP_ACP, 0, szASCIIModem, -1, pszModemName, MAX_MODEM_ID_LEN+1);
// match lstrcpyn behaviour of always null-terminating the line.
pszModemName[MAX_MODEM_ID_LEN]=0;
#else
lstrcpynA(pszModemName, szASCIIModem, MAX_MODEM_ID_LEN+1);
#endif // UNICODE
return NO_ERROR;
}
// returns buffer full o' data and an int.
// if dwRcvDelay is NULL, default RCV_DELAY will be used, else
// dwRcvDelay (miliseconds) will be used
// *lpfCancel will be true if we are exiting because of a user requested cancel.
UINT
PRIVATE
ReadPort(
HPORT hPort,
LPBYTE lpvBuf,
UINT uRead,
DWORD dwRcvDelay,
int FAR *lpiError,
PDETECTCALLBACK pdc,
BOOL FAR *lpfCancel)
{
DWORD cb, cbLenRet;
UINT uTotal = 0;
DWORD tStart;
DWORD dwDelay;
COMSTAT comstat;
COMMTIMEOUTS cto;
DWORD dwError;
DWORD cbLeft;
#ifdef DEBUG
DWORD dwZeroCount = 0;
#endif // DEBUG
ASSERT(lpvBuf);
ASSERT(uRead);
ASSERT(lpiError);
*lpiError = 0;
*lpfCancel = FALSE;
tStart = GetTickCount();
dwDelay = dwRcvDelay ? dwRcvDelay : RCV_DELAY;
// save space for terminator
uRead--;
cbLeft=uRead;
// Set comm timeout
if (!GetCommTimeouts(hPort, &cto))
{
ZeroMemory(&cto, sizeof(cto));
};
// Allow a constant write timeout
cto.ReadIntervalTimeout = 0;
cto.ReadTotalTimeoutMultiplier = 0;
cto.ReadTotalTimeoutConstant = 25;
SetCommTimeouts(hPort, &cto);
do
{
cb = 0;
while( cbLeft
&& ReadFile(hPort, lpvBuf + uTotal + cb, 1, &cbLenRet, NULL)
&& (cbLenRet))
{
ASSERT(cbLenRet==1);
cb ++;
cbLeft--;
};
#ifdef DEBUG
if (cb)
{
// TRACE_MSG(TF_DETECT, "ReadComm returned %d (zero count = %d)", cb, dwZeroCount);
dwZeroCount = 0;
}
else
{
dwZeroCount++;
}
#endif // DEBUG
{
MSG msg;
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
DispatchMessage(&msg);
};
}
if (cb == 0) // possible error?
{
//*lpiError |= GetCommError(hPort, &comstat);
dwError = 0;
ClearCommError(hPort, &dwError, &comstat);
*lpiError |= dwError;
#ifdef DEBUG
if (dwError)
{
TRACE_MSG(TF_DETECT, "ReadComm returned %d, comstat: status = %hx, in = %u, out = %u",
cb, dwError, comstat.cbInQue, comstat.cbOutQue);
};
#endif // DEBUG
}
if (cb)
{
// successful read - add to total and reset delay
uTotal += cb;
if (uTotal >= uRead)
{
ASSERT(uTotal == uRead);
break;
}
tStart = GetTickCount();
dwDelay = CHAR_DELAY;
}
else
{
if (DetectQueryCancel(pdc))
{
TRACE_MSG(TF_DETECT, "User pressed cancel.");
*lpfCancel = TRUE;
break;
}
}
// While read is successful && time since last read < delay allowed)
} while (cbLeft && (GetTickCount() - tStart) < dwDelay);
*(lpvBuf+uTotal) = 0;
#ifndef PROFILE_MASSINSTALL
TRACE_MSG(TF_DETECT, "ReadPort returning %d", uTotal);
#endif
return uTotal;
}
// Convert CBR format speeds to decimal. Returns 0 on error
DWORD NEAR PASCAL CBR_To_Decimal(UINT uiCBR)
{
DWORD dwBaudRate;
switch (uiCBR)
{
case CBR_300:
dwBaudRate = 300L;
break;
case CBR_1200:
dwBaudRate = 1200L;
break;
case CBR_2400:
dwBaudRate = 2400L;
break;
case CBR_9600:
dwBaudRate = 9600L;
break;
case CBR_19200:
dwBaudRate = 19200L;
break;
case CBR_38400:
dwBaudRate = 38400L;
break;
case CBR_56000:
dwBaudRate = 57600L;
break;
case CBR_HACK_115200:
dwBaudRate = 115200L;
break;
// case CBR_110:
// case CBR_600:
// case CBR_4800:
// case CBR_14400:
// case CBR_128000:
// case CBR_256000:
default:
TRACE_MSG(TF_ERROR, "An unsupported CBR_x value was used.");
dwBaudRate = 0;
break;
}
return dwBaudRate;
}
// Convert pszIn to a printable pszOut, not using more than cbOut bytes.
// WARNING: Not a DBCS function.
// Returns a pointer to the output buffer. Always successful.
LPSTR NEAR ConvertToPrintable(LPCSTR lpszIn, LPSTR lpszOut, UINT uOut)
{
LPSTR lpszReturn = lpszOut;
ASSERT(lpszOut);
ASSERT(lpszIn);
ASSERT(uOut);
uOut--; // save space for the null-terminator
while (*lpszIn)
{
// ascii printable chars are between 0x20 and 0x7e, inclusive
if (*lpszIn >= 0x20 && *lpszIn <= 0x7e)
{
// printable text
if (uOut)
{
uOut--;
*lpszOut++ = *lpszIn;
}
else
{
break;
}
}
else
{
// binary
if (uOut >= 4)
{
uOut -= 4;
switch (*lpszIn)
{
case CR:
*lpszOut++ = '<'; *lpszOut++ = 'c'; *lpszOut++ = 'r'; *lpszOut++ = '>';
break;
case LF:
*lpszOut++ = '<'; *lpszOut++ = 'l'; *lpszOut++ = 'f'; *lpszOut++ = '>';
break;
default:
*lpszOut++ = '<';
*lpszOut++ = c_szHex[(*lpszIn>>4) & 0xf];
*lpszOut++ = c_szHex[*lpszIn & 0xf];
*lpszOut++ = '>';
}
}
else
{
break;
}
}
lpszIn++;
}
*lpszOut = 0; // make sure we are null-terminated
return lpszReturn;
}
//-----------------------------------------------------------------------------------
// Detection Signature structure functions
//-----------------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: This function initializes a MODEM_DETECT_SIG signature
structure.
Returns: TRUE on success
Cond: --
*/
BOOL
PUBLIC
DetectSig_Init(
IN PMODEM_DETECT_SIG pmds,
IN DWORD dwFlags,
IN LPCTSTR pszHardwareID, OPTIONAL
IN LPCTSTR pszPort) OPTIONAL
{
BOOL bRet;
ASSERT(pmds);
if ( !pmds )
{
bRet = FALSE;
}
else
{
ZeroInit(pmds);
pmds->cbSize = sizeof(*pmds);
pmds->dwMask = 0;
pmds->dwFlags = dwFlags;
if (pszHardwareID)
{
SetFlag(pmds->dwMask, MDSM_HARDWAREID);
lstrcpyn(pmds->szHardwareID, pszHardwareID, SIZECHARS(pmds->szHardwareID));
}
if (pszPort)
{
SetFlag(pmds->dwMask, MDSM_PORT);
lstrcpyn(pmds->szPort, pszPort, SIZECHARS(pmds->szPort));
}
}
return bRet;
}
/*----------------------------------------------------------
Purpose: This function returns TRUE if the modem detection
signature is valid.
Returns: see above
Cond: --
*/
BOOL
PUBLIC
DetectSig_Validate(
IN PMODEM_DETECT_SIG pmds)
{
return (pmds &&
sizeof(*pmds) == pmds->cbSize &&
0 == (pmds->dwMask & ~MDSM_ALL) &&
0 == (pmds->dwFlags & ~MDSF_ALL));
}
/*----------------------------------------------------------
Purpose: If the detection signature's port field does not
match what the device's AttachedTo property says,
then update it.
Returns: TRUE if the detection signature needs to be set
FALSE if the detection signature is okay as it is
Cond: --
*/
BOOL
PRIVATE
UpdateAttachedPort(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevData,
IN OUT PMODEM_DETECT_SIG pmds)
{
BOOL bRet;
MODEM_PRIV_PROP mpp;
// Get the attached port
mpp.cbSize = sizeof(mpp);
mpp.dwMask = MPPM_PORT;
bRet = CplDiGetPrivateProperties(hdi, pdevData, &mpp);
if (bRet)
{
// Assume that the signature does not need changing
bRet = FALSE;
if (IsFlagSet(mpp.dwMask, MPPM_PORT))
{
// Do we need to update the signature?
if (IsFlagClear(pmds->dwMask, MDSM_PORT) ||
IsFlagSet(pmds->dwMask, MDSM_PORT) &&
!IsSzEqual(pmds->szPort, mpp.szPort))
{
// Yes
SetFlag(pmds->dwMask, MDSM_PORT);
lstrcpyn(pmds->szPort, mpp.szPort, SIZECHARS(pmds->szPort));
bRet = TRUE;
}
}
}
return bRet;
}
/*----------------------------------------------------------
Purpose: This function compares two modem detection signatures
and determines if they are identical.
Returns: NO_ERROR
ERROR_DUPLICATE_FOUND if the modem signatures match
other errors
Cond: --
*/
DWORD
CALLBACK
DetectSig_Compare(
IN HDEVINFO hdi,
IN PSP_DEVINFO_DATA pdevDataNew,
IN PSP_DEVINFO_DATA pdevDataExisting,
IN PVOID lParam) OPTIONAL
{
DWORD dwRet;
PDETECTSIG_PARAMS pparams = (PDETECTSIG_PARAMS)lParam;
ASSERT(hdi && INVALID_HANDLE_VALUE != hdi);
ASSERT(pdevDataNew);
ASSERT(pdevDataExisting);
try
{
MODEM_DETECT_SIG mdsNew;
MODEM_DETECT_SIG mdsExisting;
DWORD dwComparedMask = 0;
DWORD dwMatchingMask = 0;
// Assume the signatures don't match
dwRet = NO_ERROR;
mdsNew.cbSize = sizeof(mdsNew);
mdsExisting.cbSize = sizeof(mdsExisting);
// Get the detection signatures for both device instances
if (CplDiGetDetectSignature(hdi, pdevDataNew, &mdsNew) &&
CplDiGetDetectSignature(hdi, pdevDataExisting, &mdsExisting))
{
// Do the sizes look like they are valid detection signatures?
if (sizeof(MODEM_DETECT_SIG) == mdsNew.cbSize &&
sizeof(MODEM_DETECT_SIG) == mdsExisting.cbSize)
{
// Yes; make sure the detection signature is up-to-date
DWORD cFieldsCompared = 0;
DWORD cMatches = 0;
// Since the user can attach the modem to any port
// after first installing it, let's go check what the
// existing modem is attached to currently.
if (UpdateAttachedPort(hdi, pdevDataExisting, &mdsExisting))
{
CplDiSetDetectSignature(hdi, pdevDataExisting, &mdsExisting);
}
// Compare each of the valid fields
// Count the number of fields that can be compared, and
// the number of matching fields. If these two numbers
// are the same and the number of fields that can be
// compared is not 0, then we have a duplicate device.
if (IsFlagSet(mdsNew.dwMask, MDSM_HARDWAREID) &&
IsFlagSet(mdsExisting.dwMask, MDSM_HARDWAREID))
{
SetFlag(dwComparedMask, MDSM_HARDWAREID);
cFieldsCompared++;
if (IsSzEqual(mdsNew.szHardwareID, mdsExisting.szHardwareID))
{
SetFlag(dwMatchingMask, MDSM_HARDWAREID);
cMatches++;
}
}
if (IsFlagSet(mdsNew.dwMask, MDSM_PORT) &&
IsFlagSet(mdsExisting.dwMask, MDSM_PORT))
{
SetFlag(dwComparedMask, MDSM_PORT);
cFieldsCompared++;
if (IsSzEqual(mdsNew.szPort, mdsExisting.szPort))
{
SetFlag(dwMatchingMask, MDSM_PORT);
cMatches++;
}
}
// There are some minimum requirements for two devices
// to be duplicates.
//
// 1) Both must have a matching hardware ID
// 2) Both must be on the same port
//
// Anything else is frosting on the cake.
// Did the hardware ID and port match?
if ( !(2 == cFieldsCompared && 2 == cMatches) )
{
// No; don't waste anymore of our time
goto Leave;
}
if (IsFlagSet(mdsNew.dwMask, MDSM_DEVICEDESC) &&
IsFlagSet(mdsExisting.dwMask, MDSM_DEVICEDESC))
{
SetFlag(dwComparedMask, MDSM_DEVICEDESC);
cFieldsCompared++;
if (IsSzEqual(mdsNew.szDeviceDesc, mdsExisting.szDeviceDesc))
{
SetFlag(dwMatchingMask, MDSM_DEVICEDESC);
cMatches++;
}
}
// Do we have a duplicate device?
if (0 < cFieldsCompared && cFieldsCompared == cMatches)
{
// Yes
dwRet = ERROR_DUPLICATE_FOUND;
}
}
}
Leave:
// Return to the caller some information about which parts
// of the detection signature matched.
if (pparams)
{
pparams->dwComparedMask = dwComparedMask;
pparams->dwMatchingMask = dwMatchingMask;
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
dwRet = ERROR_INVALID_PARAMETER;
}
return dwRet;
}
#ifdef DEBUG
void HexDump(TCHAR *ptchHdr, LPBYTE lpBuf, DWORD cbLen)
{
TCHAR rgch[10000];
TCHAR *pc = rgch;
TCHAR *pcMore = TEXT("");
if (DisplayDebug(TF_DETECT))
{
pc += wsprintf(pc, TEXT("HEX DUMP(%s,%lu): ["), ptchHdr, cbLen);
if (cbLen>1000) {pcMore = TEXT(", ..."); cbLen=1000;}
for(;cbLen--; lpBuf++)
{
pc += wsprintf(pc, TEXT(" %02lx"), (unsigned long) *lpBuf);
if (!((cbLen+1)%20))
{
pc += wsprintf(pc, TEXT("\r\n"));
}
}
pc += wsprintf(pc, TEXT("]\r\n"));
OutputDebugString(rgch);
}
}
#endif // DEBUG