Leaked source code of windows server 2003
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.

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