Source code of Windows XP (NT5)
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.

1296 lines
35 KiB

  1. //****************************************************************************
  2. //
  3. // Microsoft NT Remote Access Service
  4. //
  5. // Copyright (C) 1992-93 Microsft Corporation. All rights reserved.
  6. //
  7. // Filename: rasstate.c
  8. //
  9. // Revision History
  10. //
  11. // Jul 1, 1992 J. Perry Hannah Created
  12. //
  13. //
  14. // Description: This file contains the state machine functions for the
  15. // RASMXS.DLL and related funcitons.
  16. //
  17. //****************************************************************************
  18. #include <nt.h> //These first five headers are used by media.h
  19. #include <ntrtl.h>
  20. #include <nturtl.h>
  21. #include <windows.h>
  22. #include <string.h>
  23. #include <malloc.h>
  24. #include <stdlib.h>
  25. #include <rasman.h>
  26. #include <raserror.h>
  27. #include <serial.h>
  28. #include <rasfile.h>
  29. #include <media.h>
  30. #include <mprlog.h>
  31. #include <rtutils.h>
  32. #include <rasmxs.h>
  33. #include <mxsint.h>
  34. #include <mxspriv.h>
  35. #include "mxswrap.h" // inf file wrapper
  36. //* Global Variables *******************************************************
  37. //
  38. extern RESPSECTION ResponseSection ; //Shared response section
  39. extern PortSetInfo_t PortSetInfo; //API typedef defined in media.h
  40. extern BOOL gbLogDeviceDialog; //Indicates logging on if TRUE
  41. extern HANDLE ghLogFile; //Handle of device log file
  42. //* BuildMacroXlationsTable ------------------------------------------------
  43. //
  44. // Function: Creates a table of macros and their expansions for use by
  45. // the RasDevAPIs. Memory is allocated for the table and the
  46. // pMacros pointer in the device control block points to it.
  47. // Since this function depends on a valid InfoTable being present
  48. // CreateInfoTable and CreateAttributes must be called before
  49. // this function is called.
  50. //
  51. // Assumptions: - Parameters in InfoTable are sorted by P_Key.
  52. // - Both parts of binary macros are present.
  53. // These assumptions imply that if somename_off is in InfoTable
  54. // somename_on is also present and is adjacent to somename_off.
  55. //
  56. // Returns: SUCCESS
  57. // ERROR_ALLOCATING_MEMORY
  58. //*
  59. DWORD
  60. BuildMacroXlationTable(DEVICE_CB *pDevice)
  61. {
  62. WORD i, j, k, cMacros;
  63. DWORD dSize;
  64. TCHAR szCoreName[MAX_PARAM_KEY_SIZE];
  65. RASMAN_DEVICEINFO *pInfo = pDevice->pInfoTable;
  66. MACROXLATIONTABLE *pMacros;
  67. // Calucate size and allocate memory
  68. cMacros = MacroCount(pInfo, ALL_MACROS);
  69. dSize = sizeof(MACROXLATIONTABLE) + sizeof(MXT_ENTRY) * (cMacros - 1);
  70. GetMem(dSize, (BYTE **) &(pDevice->pMacros));
  71. if (pDevice->pMacros == NULL)
  72. return(ERROR_ALLOCATING_MEMORY);
  73. // Copy macro names and pointers to new Macro Translation Table
  74. pMacros = pDevice->pMacros;
  75. pMacros->MXT_NumOfEntries = cMacros;
  76. for (i=0, j=0; i < pInfo->DI_NumOfParams; i++)
  77. {
  78. if (IsVariable(pInfo->DI_Params[i]))
  79. ;
  80. // copy nothing
  81. else if (IsBinaryMacro(pInfo->DI_Params[i].P_Key))
  82. {
  83. // copy Core Macro Name and pointer to Param
  84. GetCoreMacroName(pInfo->DI_Params[i].P_Key, szCoreName);
  85. strcpy(pMacros->MXT_Entry[j].E_MacroName, szCoreName);
  86. // copy Param ptr for ON macro if enabled, else copy Off Param ptr
  87. if (XOR(pInfo->DI_Params[i].P_Attributes & ATTRIB_ENABLED,
  88. BinarySuffix(pInfo->DI_Params[i].P_Key) == ON_SUFFIX))
  89. k = i + 1;
  90. else
  91. k = i;
  92. pMacros->MXT_Entry[j].E_Param = &(pInfo->DI_Params[k]);
  93. i++;
  94. j++;
  95. }
  96. else // Is Unary Macro
  97. {
  98. // copy Core Macro Name and pointer to Param
  99. strcpy(pMacros->MXT_Entry[j].E_MacroName, pInfo->DI_Params[i].P_Key);
  100. pMacros->MXT_Entry[j].E_Param = &(pInfo->DI_Params[i]);
  101. j++;
  102. }
  103. }
  104. return(SUCCESS);
  105. ///***
  106. #ifdef DEBUG //Printout Macro Translation Table
  107. for(i=0; i<cMacros; i++)
  108. DebugPrintf(("%32s %s\n", pMacros->MXT_Entry[i].E_MacroName,
  109. pMacros->MXT_Entry[i].E_Param->P_Value.String.Data));
  110. #endif // DEBUG
  111. //***/
  112. }
  113. //* DeviceStateMachine -----------------------------------------------------
  114. //
  115. // Function: This is the main state machine used by the DLL to control
  116. // asynchronous actions (writing and reading to/from devices).
  117. //
  118. // Returns: PENDING
  119. // SUCCESS
  120. // ERROR_CMD_TOO_LONG from RasDevGetCommand
  121. // Error return codes from GetLastError()
  122. //
  123. //*
  124. DWORD
  125. DeviceStateMachine(DEVICE_CB *pDevice, HANDLE hIOPort)
  126. {
  127. DWORD dRC, lpcBytesWritten;
  128. BOOL fIODone, fEndOfSection = FALSE;
  129. TCHAR szCmdSuffix[MAX_CMDTYPE_SUFFIX_LEN + 1];
  130. COMMTIMEOUTS CT;
  131. while(1)
  132. {
  133. //DebugPrintf(("DeviceStateMachine state: %d\n", pDevice->eDevNextAction));
  134. switch(pDevice->eDevNextAction)
  135. {
  136. // Send a Command to the device
  137. case SEND:
  138. // Get Command string
  139. dRC = RasDevGetCommand(pDevice->hInfFile,
  140. CmdTypeToStr(szCmdSuffix, pDevice->eCmdType),
  141. pDevice->pMacros,
  142. pDevice->szCommand,
  143. &(pDevice->dCmdLen));
  144. switch(dRC)
  145. {
  146. case SUCCESS:
  147. // Check to see if a response is expected
  148. pDevice->bResponseExpected =
  149. RasDevResponseExpected(pDevice->hInfFile, pDevice->eDeviceType);
  150. // Log the Command
  151. if (gbLogDeviceDialog)
  152. LogString(pDevice, "Command to Device:", pDevice->szCommand,
  153. pDevice->dCmdLen);
  154. // Check for null command with no response expected
  155. if (pDevice->dCmdLen == 0 && !pDevice->bResponseExpected)
  156. {
  157. // Pause between commands
  158. if (CommWait(pDevice, hIOPort, NO_RESPONSE_DELAY))
  159. return(ERROR_UNEXPECTED_RESPONSE);
  160. else if ((dRC = GetLastError()) == ERROR_IO_PENDING)
  161. {
  162. pDevice->eDevNextAction = DONE;
  163. return(PENDING);
  164. }
  165. else
  166. return(dRC);
  167. }
  168. // Send the command to the Port
  169. CT.WriteTotalTimeoutMultiplier = 0;
  170. CT.WriteTotalTimeoutConstant = TO_WRITE;
  171. SetCommTimeouts(hIOPort, &CT);
  172. fIODone = WriteFile(hIOPort, // Send Cmd string to modem
  173. pDevice->szCommand,
  174. pDevice->dCmdLen,
  175. &lpcBytesWritten,
  176. (LPOVERLAPPED)&(pDevice->Overlapped));
  177. pDevice->eDevNextAction = RECEIVE;
  178. if ( ! fIODone)
  179. {
  180. if ((dRC = GetLastError()) == ERROR_IO_PENDING)
  181. return(PENDING);
  182. else
  183. return(dRC);
  184. }
  185. return(PENDING);
  186. case ERROR_END_OF_SECTION:
  187. fEndOfSection = TRUE;
  188. pDevice->eDevNextAction = DONE;
  189. break;
  190. default:
  191. return(dRC);
  192. }
  193. break;
  194. // Recieve Response string from device
  195. case RECEIVE:
  196. dRC = ReceiveStateMachine(pDevice, hIOPort);
  197. switch(dRC)
  198. {
  199. case SUCCESS:
  200. pDevice->eDevNextAction = DONE;
  201. pDevice->eRcvState = GETECHO; //Reset Recieve State Machine
  202. break;
  203. case PENDING:
  204. return(PENDING);
  205. default:
  206. pDevice->eRcvState = GETECHO; //Reset Recieve State Machine
  207. return(dRC);
  208. }
  209. break;
  210. // A Command-Response cycle is complete
  211. case DONE:
  212. if (fEndOfSection)
  213. switch(pDevice->eCmdType) //Last cmd of this type is now done
  214. {
  215. case CT_INIT:
  216. pDevice->eCmdType = pDevice->eNextCmdType; //Reset command type
  217. RasDevResetCommand(pDevice->hInfFile); //Reset INF file ptr
  218. break;
  219. case CT_DIAL:
  220. case CT_LISTEN:
  221. if ((dRC = CheckBpsMacros(pDevice)) != SUCCESS)
  222. return(dRC);
  223. return(ResetBPS(pDevice));
  224. case CT_GENERIC:
  225. return(SUCCESS);
  226. }
  227. pDevice->eDevNextAction = SEND; //Reset state machine
  228. break;
  229. } /* Switch */
  230. } /* While */
  231. } /* DeviceStateMachine */
  232. //* ReceiveStateMachine ----------------------------------------------------
  233. //
  234. // Function: This state machine controls asynchronously reading from the
  235. // device. First the command echo is read promptly after the
  236. // command is sent. Then after a delay the the response begins
  237. // arriving. An asynchronous read with a long time out is done
  238. // for the first character. Then the rest of the string is read
  239. // (also asynchronously).
  240. //
  241. // Returns: PENDING
  242. // SUCCESS
  243. // ERROR_REPEATED_PARTIAL_RESPONSE
  244. // Error return codes from GetLastError(), RasDevCheckResponse()
  245. //
  246. //*
  247. DWORD
  248. ReceiveStateMachine(DEVICE_CB *pDevice, HANDLE hIOPort)
  249. {
  250. DWORD dRC;
  251. BOOL fKeyIsOK;
  252. TCHAR szKey[MAX_PARAM_KEY_SIZE];
  253. while(1)
  254. {
  255. //DebugPrintf(("ReceiveStateMachine state: %d\n", pDevice->eRcvState));
  256. switch (pDevice->eRcvState)
  257. {
  258. case GETECHO:
  259. // Check if an echo is expected.
  260. // 1. If there is no command there is no echo.
  261. // 2. Null modems require that if there is no response there is no echo
  262. // so we require it for all devices.
  263. // 3. If the current line of INF file is "NoEcho", there is no echo.
  264. if (pDevice->dCmdLen == 0 ||
  265. !pDevice->bResponseExpected ||
  266. !RasDevEchoExpected(pDevice->hInfFile))
  267. {
  268. pDevice->eRcvState = GETFIRSTCHAR;
  269. break;
  270. }
  271. // Clear buffer used for echo and device response, and Reset Event
  272. memset(pDevice->szResponse, '\0', sizeof(pDevice->szResponse));
  273. ResetEvent(pDevice->hNotifier); //Reset event handle
  274. ConsolePrintf(("WaitForEcho hIOPort: 0x%08lx hNotifier: 0x%08x\n",
  275. hIOPort, pDevice->hNotifier));
  276. // Get Echo
  277. if (WaitForEcho(pDevice, hIOPort, pDevice->dCmdLen))
  278. {
  279. pDevice->eRcvState = CHECKECHO;
  280. return(PENDING);
  281. }
  282. else if ((dRC = GetLastError()) == ERROR_IO_PENDING)
  283. {
  284. pDevice->eRcvState = GETNUMBYTESECHOD;
  285. return(PENDING);
  286. }
  287. else
  288. return(dRC);
  289. break;
  290. case GETNUMBYTESECHOD:
  291. if (!GetOverlappedResult(hIOPort,
  292. (LPOVERLAPPED)&pDevice->Overlapped,
  293. &pDevice->cbRead,
  294. !WAITFORCOMPLETION))
  295. return(GetLastError());
  296. pDevice->eRcvState = CHECKECHO; //Set Next state
  297. break;
  298. case CHECKECHO:
  299. // Log the Echo received
  300. DebugPrintf(("Echo:%s!\n cbEcohed:%d\n",
  301. pDevice->szResponse, pDevice->cbRead));
  302. if (gbLogDeviceDialog && !pDevice->fPartialResponse)
  303. LogString(pDevice, "Echo from Device :", pDevice->szResponse,
  304. pDevice->cbRead);
  305. // Check for echo different from command
  306. switch(pDevice->eDeviceType)
  307. {
  308. case DT_MODEM:
  309. if (pDevice->cbRead != pDevice->dCmdLen ||
  310. _strnicmp(pDevice->szCommand,
  311. pDevice->szResponse, pDevice->dCmdLen) != 0)
  312. {
  313. if (CheckForOverruns(hIOPort))
  314. return(ERROR_OVERRUN);
  315. else
  316. return(ERROR_PORT_OR_DEVICE);
  317. }
  318. break;
  319. case DT_PAD:
  320. case DT_SWITCH:
  321. if (RasDevSubStr(pDevice->szResponse,
  322. pDevice->cbRead,
  323. "NO CARRIER",
  324. strlen("NO CARRIER")))
  325. return(ERROR_NO_CARRIER);
  326. break;
  327. }
  328. pDevice->eRcvState = GETFIRSTCHAR; //Set Next state
  329. break;
  330. case GETFIRSTCHAR:
  331. // Check if a response is expected
  332. if ( ! pDevice->bResponseExpected)
  333. {
  334. if ((dRC = PutInMessage(pDevice, "", 0)) != SUCCESS)
  335. return(dRC);
  336. pDevice->cbTotal = 0; //Reset for next response
  337. return(SUCCESS);
  338. }
  339. // Save starting point for a receive following an echo
  340. if (!pDevice->fPartialResponse)
  341. {
  342. (pDevice->cbTotal) += pDevice->cbRead;
  343. pDevice->pszResponseStart = pDevice->szResponse + pDevice->cbTotal;
  344. }
  345. ResetEvent(pDevice->hNotifier); //Reset event handle
  346. if (WaitForFirstChar(pDevice, hIOPort))
  347. {
  348. pDevice->eRcvState = GETRECEIVESTR;
  349. return(PENDING);
  350. }
  351. else if ((dRC = GetLastError()) == ERROR_IO_PENDING)
  352. {
  353. pDevice->eRcvState = GETNUMBYTESFIRSTCHAR;
  354. return(PENDING);
  355. }
  356. else
  357. return(dRC);
  358. break;
  359. case GETNUMBYTESFIRSTCHAR:
  360. DebugPrintf(("After 1st char:%s! cbTotal:%d\n",
  361. pDevice->szResponse, pDevice->cbTotal));
  362. if (!GetOverlappedResult(hIOPort,
  363. (LPOVERLAPPED)&pDevice->Overlapped,
  364. &pDevice->cbRead,
  365. !WAITFORCOMPLETION))
  366. return(GetLastError());
  367. pDevice->eRcvState = GETRECEIVESTR; //Set Next state
  368. break;
  369. case GETRECEIVESTR:
  370. (pDevice->cbTotal)++; //FIRSTCAR always rcvs 1 byte
  371. ResetEvent(pDevice->hNotifier); //Reset event handle
  372. if (ReceiveString(pDevice, hIOPort))
  373. {
  374. pDevice->eRcvState = CHECKRESPONSE;
  375. return(PENDING);
  376. }
  377. else if ((dRC = GetLastError()) == ERROR_IO_PENDING)
  378. {
  379. pDevice->eRcvState = GETNUMBYTESRCVD;
  380. return(PENDING);
  381. }
  382. else
  383. return(dRC);
  384. break;
  385. case GETNUMBYTESRCVD:
  386. if (!GetOverlappedResult(hIOPort,
  387. (LPOVERLAPPED)&pDevice->Overlapped,
  388. &pDevice->cbRead,
  389. !WAITFORCOMPLETION))
  390. return(GetLastError());
  391. pDevice->eRcvState = CHECKRESPONSE; //Set Next state
  392. break;
  393. case CHECKRESPONSE:
  394. (pDevice->cbTotal) += pDevice->cbRead;
  395. // Always put response string where UI can get it
  396. if (pDevice->eDeviceType == DT_MODEM)
  397. dRC = PutInMessage(pDevice,
  398. pDevice->pszResponseStart,
  399. ModemResponseLen(pDevice));
  400. else
  401. dRC = PutInMessage(pDevice, pDevice->szResponse, pDevice->cbTotal);
  402. if (dRC != SUCCESS)
  403. return(dRC);
  404. // Check the response
  405. dRC = CheckResponse(pDevice, szKey);
  406. // Log the response received
  407. if (gbLogDeviceDialog && dRC != ERROR_PARTIAL_RESPONSE)
  408. LogString(pDevice,
  409. "Response from Device:",
  410. pDevice->pszResponseStart,
  411. ModemResponseLen(pDevice));
  412. switch(dRC)
  413. {
  414. case ERROR_UNRECOGNIZED_RESPONSE:
  415. default: // Other errors
  416. return(dRC);
  417. case ERROR_PARTIAL_RESPONSE:
  418. if (pDevice->fPartialResponse)
  419. return(ERROR_PARTIAL_RESPONSE_LOOPING);
  420. pDevice->fPartialResponse = TRUE;
  421. pDevice->eRcvState = GETFIRSTCHAR;
  422. ConsolePrintf(("Partial Response\n"));
  423. break;
  424. case SUCCESS: // Response found in INF file
  425. pDevice->cbTotal = 0; // Reset for next response
  426. fKeyIsOK = !_strnicmp(szKey, MXS_OK_KEY, strlen(MXS_OK_KEY));
  427. // Do we need to loop and get another response from device
  428. if (((_stricmp(szKey, LOOP_TXT) == 0) && (pDevice->eCmdType != CT_INIT)) ||
  429. (fKeyIsOK && pDevice->eCmdType == CT_LISTEN) )
  430. {
  431. pDevice->eRcvState = GETFIRSTCHAR;
  432. break;
  433. }
  434. // Check if device has error contol on
  435. pDevice->bErrorControlOn = _stricmp(szKey, MXS_CONNECT_EC_KEY) == 0;
  436. // Determine return code
  437. if (fKeyIsOK)
  438. if (pDevice->eCmdType == CT_DIAL)
  439. return(ERROR_PORT_OR_DEVICE);
  440. else
  441. return(SUCCESS);
  442. if (_strnicmp(szKey, MXS_CONNECT_KEY, strlen(MXS_CONNECT_KEY)) == 0)
  443. return(SUCCESS);
  444. else if (_strnicmp(szKey, MXS_ERROR_KEY, strlen(MXS_ERROR_KEY)) == 0)
  445. return(MapKeyToErrorCode(szKey));
  446. else if (CheckForOverruns(hIOPort))
  447. return(ERROR_OVERRUN);
  448. else
  449. return(ERROR_UNKNOWN_RESPONSE_KEY);
  450. }
  451. break;
  452. } /* Switch */
  453. } /* While */
  454. } /* ReceiveStateMachine */
  455. //* CheckResponse ----------------------------------------------------------
  456. //
  457. // Function: If DeviceType is Modem this function checks first for a
  458. // response in that particular modem's section of the INF
  459. // file and returns if it finds one. If there is no response
  460. // there, it checks for a response in the Modems Responses
  461. // section.
  462. //
  463. // If DeviceType is not Modem the function checks only in
  464. // the particular device's section of the INF file.
  465. //
  466. // Returns: Error return codes from RasDevCheckResponse()
  467. //
  468. //*
  469. DWORD
  470. CheckResponse(DEVICE_CB *pDev, LPTSTR szKey)
  471. {
  472. DWORD dRC, dResponseLen;
  473. if (pDev->cbTotal > sizeof(pDev->szResponse))
  474. return(ERROR_RECV_BUF_FULL);
  475. dResponseLen = ModemResponseLen(pDev);
  476. DebugPrintf(("Device Response:%s! cbResponse:%d\n",
  477. pDev->pszResponseStart, dResponseLen));
  478. dRC = RasDevCheckResponse(pDev->hInfFile,
  479. pDev->pszResponseStart,
  480. dResponseLen,
  481. pDev->pMacros,
  482. szKey);
  483. if (pDev->eDeviceType == DT_MODEM &&
  484. dRC != SUCCESS &&
  485. dRC != ERROR_PARTIAL_RESPONSE) {
  486. // **** Exclusion Begin ****
  487. WaitForSingleObject(ResponseSection.Mutex, INFINITE) ;
  488. dRC = RasDevCheckResponse(ResponseSection.Handle,
  489. pDev->pszResponseStart,
  490. dResponseLen,
  491. pDev->pMacros,
  492. szKey);
  493. // *** Exclusion End ***
  494. ReleaseMutex(ResponseSection.Mutex);
  495. }
  496. if (dRC == ERROR_UNRECOGNIZED_RESPONSE)
  497. {
  498. // Maybe there was no echo.
  499. // Try again assuming string starts at beginning of buffer.
  500. dRC = RasDevCheckResponse(pDev->hInfFile,
  501. pDev->szResponse,
  502. pDev->cbTotal,
  503. pDev->pMacros,
  504. szKey);
  505. if (pDev->eDeviceType == DT_MODEM &&
  506. dRC != SUCCESS &&
  507. dRC != ERROR_PARTIAL_RESPONSE) {
  508. // **** Exclusion Begin ****
  509. WaitForSingleObject(ResponseSection.Mutex, INFINITE) ;
  510. dRC = RasDevCheckResponse(ResponseSection.Handle,
  511. pDev->szResponse,
  512. pDev->cbTotal,
  513. pDev->pMacros,
  514. szKey);
  515. // *** Exclusion End ***
  516. ReleaseMutex(ResponseSection.Mutex);
  517. }
  518. }
  519. return(dRC);
  520. }
  521. //* ModemResponseLen -------------------------------------------------------
  522. //
  523. // Function: This function returns the length of the portion of the
  524. // response in the response buffer which follows the echo.
  525. //
  526. // Returns: Total length - (start of response - beginning of buffer)
  527. //
  528. //*
  529. DWORD
  530. ModemResponseLen(DEVICE_CB *pDev)
  531. {
  532. return(DWORD)(pDev->cbTotal - (pDev->pszResponseStart - pDev->szResponse));
  533. }
  534. //* CommWait ---------------------------------------------------------------
  535. //
  536. // Function: This function causes an asynchronous delay by reading the
  537. // com port when no characters are expected. When the ReadFile
  538. // times out the calling process is signaled via hNotifier.
  539. //
  540. // Returns: Values from Win32 api calls.
  541. //
  542. //*
  543. DWORD
  544. CommWait(DEVICE_CB *pDevice, HANDLE hIOPort, DWORD dwPause)
  545. {
  546. DWORD dwBytesRead;
  547. TCHAR Buffer[2048];
  548. COMMTIMEOUTS CT;
  549. CT.ReadIntervalTimeout = 0;
  550. CT.ReadTotalTimeoutMultiplier = 0;
  551. CT.ReadTotalTimeoutConstant = dwPause;
  552. if ( ! SetCommTimeouts(hIOPort, &CT))
  553. return(FALSE);
  554. return(ReadFile(hIOPort,
  555. Buffer,
  556. sizeof(Buffer),
  557. &dwBytesRead,
  558. (LPOVERLAPPED)&pDevice->Overlapped));
  559. }
  560. //* WaitForEcho ------------------------------------------------------------
  561. //
  562. // Function: This function reads the echo of the command sent to the
  563. // device. The echo is not used and is simply ignored.
  564. // Since the length of the echo is the length of the command
  565. // sent, cbEcho is the size of the command sent.
  566. //
  567. // ReadFile is asynchronous (because the port was opened in
  568. // overlapped mode), and completes when the buffer is full
  569. // (cbEcho bytes) or after TO_ECHO mS, whichever comes first.
  570. //
  571. // Returns: Error return codes from ReadFile(), or GetLastError().
  572. //
  573. //*
  574. BOOL
  575. WaitForEcho(DEVICE_CB *pDevice, HANDLE hIOPort, DWORD cbEcho)
  576. {
  577. COMMTIMEOUTS CT;
  578. CT.ReadIntervalTimeout = 0;
  579. CT.ReadTotalTimeoutMultiplier = 0;
  580. CT.ReadTotalTimeoutConstant = TO_ECHO; // Comm time out = TO_ECHO
  581. if ( ! SetCommTimeouts(hIOPort, &CT))
  582. return(FALSE);
  583. return(ReadFile(hIOPort,
  584. pDevice->szResponse,
  585. cbEcho,
  586. &pDevice->cbRead,
  587. (LPOVERLAPPED)&pDevice->Overlapped));
  588. }
  589. //* WaitForFirstChar -------------------------------------------------------
  590. //
  591. // Function: This function reads the first character received from the
  592. // device in response to the last command. (This follows the
  593. // echo of the command.)
  594. //
  595. // ReadFile is asynchronous (because the port was opened in
  596. // overlapped mode), and completes after one character is
  597. // received, or after CT.ReadToalTimeoutConstant, whichever
  598. // comes first.
  599. //
  600. // Returns: Error return codes from ReadFile(), or GetLastError().
  601. //
  602. //*
  603. BOOL
  604. WaitForFirstChar(DEVICE_CB *pDevice, HANDLE hIOPort)
  605. {
  606. TCHAR *pszResponse;
  607. COMMTIMEOUTS CT;
  608. CT.ReadIntervalTimeout = 0;
  609. CT.ReadTotalTimeoutMultiplier = 0;
  610. if (pDevice->fPartialResponse)
  611. CT.ReadTotalTimeoutConstant = TO_PARTIALRESPONSE;
  612. else if (pDevice->eCmdType == CT_LISTEN)
  613. CT.ReadTotalTimeoutConstant = 0; //Never timeout for LISTEN
  614. else if (pDevice->cbTotal == 0) //Implies no Echo
  615. CT.ReadTotalTimeoutConstant = TO_FIRSTCHARNOECHO; //Probably not a modem
  616. else
  617. CT.ReadTotalTimeoutConstant = TO_FIRSTCHARAFTERECHO; //Probably a modem
  618. if ( ! SetCommTimeouts(hIOPort, &CT))
  619. return(FALSE);
  620. pszResponse = pDevice->szResponse;
  621. pszResponse += pDevice->cbTotal;
  622. return(ReadFile(hIOPort,
  623. pszResponse,
  624. 1,
  625. &pDevice->cbRead,
  626. (LPOVERLAPPED)&pDevice->Overlapped));
  627. }
  628. //* ReceiveString ----------------------------------------------------------
  629. //
  630. // Function: This function reads the string received from the device in
  631. // response to the last command. The first byte of this string
  632. // has already been received by WaitForFirstChar().
  633. //
  634. // ReadFile is asynchronous (because the port was opened in
  635. // overlapped mode), and times out after a total time of
  636. // TO_RCV_CONSTANT, or if the time between characters exceeds
  637. // TO_RCV_INTERVAL.
  638. //
  639. // Returns: Error return codes from ReadFile(), or GetLastError().
  640. //
  641. //*
  642. BOOL
  643. ReceiveString(DEVICE_CB *pDevice, HANDLE hIOPort)
  644. {
  645. TCHAR *pszResponse;
  646. COMMTIMEOUTS CT;
  647. CT.ReadIntervalTimeout = TO_RCV_INTERVAL;
  648. CT.ReadTotalTimeoutMultiplier = 0;
  649. CT.ReadTotalTimeoutConstant = TO_RCV_CONSTANT;
  650. if ( ! SetCommTimeouts(hIOPort, &CT))
  651. return(FALSE);
  652. pszResponse = pDevice->szResponse;
  653. pszResponse += pDevice->cbTotal;
  654. return(ReadFile(hIOPort,
  655. pszResponse,
  656. sizeof(pDevice->szResponse) - pDevice->cbTotal,
  657. &pDevice->cbRead,
  658. (LPOVERLAPPED)&pDevice->Overlapped));
  659. }
  660. //* PutInMessage -----------------------------------------------------------
  661. //
  662. // Function: This function finds the message macro in the Macro Translations
  663. // table, and copies the second parameter, a string, into the
  664. // message macro's value field.
  665. //
  666. // Returns: SUCCESS
  667. // ERROR_MESSAGE_MACRO_NOT_FOUND
  668. // Return codes from UpdateparmString
  669. //*
  670. DWORD
  671. PutInMessage(DEVICE_CB *pDevice, LPTSTR lpszStr, DWORD dwStrLen)
  672. {
  673. WORD i;
  674. MACROXLATIONTABLE *pMacros = pDevice->pMacros;
  675. for (i=0; i<pMacros->MXT_NumOfEntries; i++)
  676. if (_stricmp(MXS_MESSAGE_KEY, pMacros->MXT_Entry[i].E_MacroName) == 0)
  677. break;
  678. if (i >= pMacros->MXT_NumOfEntries)
  679. return(ERROR_MESSAGE_MACRO_NOT_FOUND);
  680. return(UpdateParamString(pMacros->MXT_Entry[i].E_Param,
  681. lpszStr,
  682. dwStrLen));
  683. }
  684. //* PortSetStringInfo -----------------------------------------------------
  685. //
  686. // Function: Formats a RASMAN_PORTINFO struct for string data and calls
  687. // PortSetInfo.
  688. //
  689. // Returns: Return codes from PortSetInfo
  690. //*
  691. DWORD
  692. PortSetStringInfo(HANDLE hIOPort, char *pszKey, char *psStr, DWORD sStrLen)
  693. {
  694. BYTE chBuffer[sizeof(RASMAN_PORTINFO) + RAS_MAXLINEBUFLEN];
  695. RASMAN_PORTINFO *pSetInfo;
  696. pSetInfo = (RASMAN_PORTINFO *)chBuffer;
  697. pSetInfo->PI_NumOfParams = 1;
  698. strcpy(pSetInfo->PI_Params[0].P_Key, pszKey);
  699. pSetInfo->PI_Params[0].P_Type = String;
  700. pSetInfo->PI_Params[0].P_Attributes = 0;
  701. pSetInfo->PI_Params[0].P_Value.String.Data =
  702. (PCHAR)pSetInfo + sizeof(RASMAN_PORTINFO);
  703. strncpy(pSetInfo->PI_Params[0].P_Value.String.Data, psStr, sStrLen);
  704. pSetInfo->PI_Params[0].P_Value.String.Length = sStrLen;
  705. PortSetInfo(hIOPort, pSetInfo) ;
  706. return(SUCCESS);
  707. }
  708. //* ResetBPS --------------------------------------------------------------
  709. //
  710. // Function: This function calls the serial dll API, PortSetInfo, and
  711. // 1. sets the Error Control Flag in the Serial PCB,
  712. // 2. sets the Hardware Flow Conrol Flag,
  713. // 3. sets the Carrier BPS rate in the Serial PCB,
  714. // 4. if the Connect BPS macro is non-null the Port BPS is
  715. // is set to the macro value, otherwise it is unchanged.
  716. //
  717. // See the truth table in the CheckBpsMacros() function notes.
  718. //
  719. // Assumptions: ConnectBps and CarrierBps macros are filled in,
  720. // that is, RasDevCheckResponse has been called successfully.
  721. //
  722. // Returns: Error codes from GetLastError().
  723. //
  724. //*
  725. DWORD
  726. ResetBPS(DEVICE_CB *pDev)
  727. {
  728. UINT i;
  729. DWORD dwRC;
  730. TCHAR *pStrData, *pArgs[1];
  731. BYTE chBuffer[sizeof(RASMAN_PORTINFO) + (MAX_LEN_STR_FROM_NUMBER + 1)];
  732. RAS_PARAMS *pParam;
  733. RASMAN_PORTINFO *pPortInfo;
  734. RASMAN_DEVICEINFO *pInfoTable = pDev->pInfoTable;
  735. pPortInfo = (RASMAN_PORTINFO *)chBuffer;
  736. pParam = pPortInfo->PI_Params;
  737. pStrData = (PCHAR)pPortInfo + sizeof(RASMAN_PORTINFO);
  738. pPortInfo->PI_NumOfParams = 1;
  739. // Make Error Control Flag Entry
  740. strcpy(pParam->P_Key, SER_ERRORCONTROLON_KEY);
  741. pParam->P_Type = Number;
  742. pParam->P_Attributes = 0;
  743. pParam->P_Value.Number = pDev->bErrorControlOn;
  744. PortSetInfo(pDev->hPort, pPortInfo) ;
  745. // Make HwFlowControl Flag Entry
  746. strcpy(pParam->P_Key, SER_HDWFLOWCTRLON_KEY);
  747. pParam->P_Type = Number;
  748. pParam->P_Attributes = 0;
  749. i = FindTableEntry(pInfoTable, MXS_HDWFLOWCONTROL_KEY);
  750. if (i == INVALID_INDEX)
  751. return(ERROR_MACRO_NOT_FOUND);
  752. pParam->P_Value.Number =
  753. pInfoTable->DI_Params[i].P_Attributes & ATTRIB_ENABLED;
  754. PortSetInfo(pDev->hPort, pPortInfo) ;
  755. // Make Carrier BPS Entry
  756. i = FindTableEntry(pInfoTable, MXS_CARRIERBPS_KEY);
  757. if (i == INVALID_INDEX)
  758. return(ERROR_MACRO_NOT_FOUND);
  759. dwRC = PortSetStringInfo(pDev->hPort,
  760. SER_CARRIERBPS_KEY,
  761. pInfoTable->DI_Params[i].P_Value.String.Data,
  762. pInfoTable->DI_Params[i].P_Value.String.Length);
  763. if (dwRC != SUCCESS)
  764. return(dwRC);
  765. // Make Connect BPS Entry
  766. i = FindTableEntry(pInfoTable, MXS_CONNECTBPS_KEY);
  767. if (i == INVALID_INDEX)
  768. return(ERROR_MACRO_NOT_FOUND);
  769. dwRC = PortSetStringInfo(pDev->hPort,
  770. SER_CONNECTBPS_KEY,
  771. pInfoTable->DI_Params[i].P_Value.String.Data,
  772. pInfoTable->DI_Params[i].P_Value.String.Length);
  773. if (dwRC == ERROR_INVALID_PARAMETER)
  774. {
  775. pArgs[0] = pDev->szPortName;
  776. LogError(ROUTERLOG_UNSUPPORTED_BPS, 1, pArgs, NO_ERROR);
  777. return(ERROR_UNSUPPORTED_BPS);
  778. }
  779. else if (dwRC != SUCCESS)
  780. return(dwRC);
  781. return(SUCCESS);
  782. }
  783. //* CheckBpsMacros ---------------------------------------------------------
  784. //
  785. // Function: If the connectbps macro string converts to zero (no connect BPS
  786. // rate was received from the device, or it was a word, eg, FAST),
  787. // then the value for the Port BPS saved in the DCB is copied to
  788. // connectbps.
  789. //
  790. // If the carrierbps macro string converts to zero, then the value
  791. // for the carrierbps is estimated as max(2400, connectbps/4),
  792. // unless the connectbps <= 2400, in which case carrerbps is set to
  793. // the value for connectbps.
  794. //
  795. // Returns: SUCCESS
  796. // Return codes from UpdateparmString
  797. //*
  798. DWORD
  799. CheckBpsMacros(DEVICE_CB *pDev)
  800. {
  801. UINT i, j;
  802. DWORD dwRC, dwConnectBps, dwCarrierBps, dwCarrierBpsEstimate;
  803. TCHAR szConnectBps[MAX_LEN_STR_FROM_NUMBER];
  804. TCHAR szCarrierBps[MAX_LEN_STR_FROM_NUMBER];
  805. RASMAN_DEVICEINFO *pInfoTable = pDev->pInfoTable;
  806. // Find Carrier BPS rate in InfoTable
  807. j = FindTableEntry(pInfoTable, MXS_CONNECTBPS_KEY);
  808. i = FindTableEntry(pInfoTable, MXS_CARRIERBPS_KEY);
  809. if (j == INVALID_INDEX || i == INVALID_INDEX)
  810. return(ERROR_MACRO_NOT_FOUND);
  811. // Get Connect Bps rate from macro
  812. strncpy(szConnectBps,
  813. pInfoTable->DI_Params[j].P_Value.String.Data,
  814. pInfoTable->DI_Params[j].P_Value.String.Length);
  815. szConnectBps[pInfoTable->DI_Params[j].P_Value.String.Length] = '\0';
  816. dwConnectBps = atoi(szConnectBps);
  817. // If Connect BPS macro value is zero copy Port BPS to Connect BPS
  818. if (dwConnectBps == 0)
  819. {
  820. dwRC = UpdateParamString(&(pInfoTable->DI_Params[j]),
  821. pDev->szPortBps,
  822. strlen(pDev->szPortBps));
  823. if (dwRC != SUCCESS)
  824. return(dwRC);
  825. dwConnectBps = atoi(pDev->szPortBps);
  826. }
  827. // Get Carrier Bps rate from macro
  828. strncpy(szCarrierBps,
  829. pInfoTable->DI_Params[i].P_Value.String.Data,
  830. pInfoTable->DI_Params[i].P_Value.String.Length);
  831. szCarrierBps[pInfoTable->DI_Params[i].P_Value.String.Length] = '\0';
  832. dwCarrierBps = atoi(szCarrierBps);
  833. // If Carrier BPS macro value is zero estimate Carrier BPS
  834. if (dwCarrierBps == 0)
  835. {
  836. if (dwConnectBps <= MIN_LINK_SPEED) //2400 bps
  837. dwCarrierBpsEstimate = dwConnectBps;
  838. else
  839. {
  840. UINT k ;
  841. k = FindTableEntry(pInfoTable, MXS_HDWFLOWCONTROL_KEY);
  842. if (k == INVALID_INDEX)
  843. return(ERROR_MACRO_NOT_FOUND);
  844. if (pInfoTable->DI_Params[k].P_Attributes & ATTRIB_ENABLED)
  845. dwCarrierBpsEstimate = dwConnectBps/4 ;
  846. else
  847. dwCarrierBpsEstimate = dwConnectBps ;
  848. if (dwCarrierBpsEstimate < MIN_LINK_SPEED)
  849. dwCarrierBpsEstimate = MIN_LINK_SPEED;
  850. }
  851. _itoa(dwCarrierBpsEstimate, szCarrierBps, 10);
  852. dwRC = UpdateParamString(&(pInfoTable->DI_Params[i]),
  853. szCarrierBps,
  854. strlen(szCarrierBps));
  855. if (dwRC != SUCCESS)
  856. return(dwRC);
  857. }
  858. // Log BPS Macros
  859. if (gbLogDeviceDialog)
  860. {
  861. LogString(pDev,
  862. "Connect BPS:",
  863. pInfoTable->DI_Params[j].P_Value.String.Data,
  864. pInfoTable->DI_Params[j].P_Value.String.Length);
  865. LogString(pDev,
  866. "Carrier BPS:",
  867. pInfoTable->DI_Params[i].P_Value.String.Data,
  868. pInfoTable->DI_Params[i].P_Value.String.Length);
  869. }
  870. DebugPrintf(("ConnectBps: %s\n", pInfoTable->DI_Params[j].P_Value.String.Data));
  871. DebugPrintf(("CarrierBps: %s\n", pInfoTable->DI_Params[i].P_Value.String.Data));
  872. return(SUCCESS);
  873. }
  874. //* FindTableEntry ---------------------------------------------------------
  875. //
  876. // Function: Finds a key in Info Table. This function matches the length
  877. // up to the lenght of the input parameter (pszKey). If a binary
  878. // macro name is given without the suffix the first of the two
  879. // binary macros will be found. If the full binary macro name is
  880. // given the exact binary macro will be found.
  881. //
  882. // Assumptions: The input parameter, pszKey, is
  883. // 1. a full unary macro name, or
  884. // 2. a "core" binary macro name, or
  885. // 3. a full binary macro name.
  886. //
  887. // Returns: Index of DI_Params in Info Table.
  888. //
  889. //*
  890. UINT
  891. FindTableEntry(RASMAN_DEVICEINFO *pTable, TCHAR *pszKey)
  892. {
  893. WORD i;
  894. for (i=0; i<pTable->DI_NumOfParams; i++)
  895. if (_strnicmp(pszKey, pTable->DI_Params[i].P_Key, strlen(pszKey)) == 0)
  896. break;
  897. if (i >= pTable->DI_NumOfParams)
  898. return(INVALID_INDEX);
  899. return(i);
  900. }
  901. //* MapKeyToErrorCode ------------------------------------------------------
  902. //
  903. // Function: Maps the error key from the device.ini file to an error code
  904. // number to be returned to the UI.
  905. //
  906. // Returns: An error code that represents the error returned from the device.
  907. //
  908. //*
  909. DWORD
  910. MapKeyToErrorCode(TCHAR *pszKey)
  911. {
  912. int i;
  913. ERROR_ELEM ErrorTable[] = {
  914. MXS_ERROR_BUSY_KEY , ERROR_LINE_BUSY ,
  915. MXS_ERROR_NO_ANSWER_KEY , ERROR_NO_ANSWER ,
  916. MXS_ERROR_VOICE_KEY , ERROR_VOICE_ANSWER ,
  917. MXS_ERROR_NO_CARRIER_KEY , ERROR_NO_CARRIER ,
  918. MXS_ERROR_NO_DIALTONE_KEY , ERROR_NO_DIALTONE ,
  919. MXS_ERROR_DIAGNOSTICS_KEY , ERROR_X25_DIAGNOSTIC
  920. };
  921. for (i=0; i < sizeof(ErrorTable)/sizeof(ERROR_ELEM); i++)
  922. if (_stricmp(pszKey, ErrorTable[i].szKey) == 0)
  923. return(ErrorTable[i].dwErrorCode);
  924. return(ERROR_FROM_DEVICE);
  925. }