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.

781 lines
19 KiB

  1. /*--------------------------------------------------------------
  2. *
  3. * FILE: SK_COMM.C
  4. *
  5. * PURPOSE: The file contains the Functions responsible for
  6. * managing the COMM ports
  7. *
  8. * CREATION: June 1994
  9. *
  10. * COPYRIGHT: Black Diamond Software (C) 1994
  11. *
  12. * AUTHOR: Ronald Moak
  13. *
  14. * NOTES:
  15. *
  16. * This file, and all others associated with it contains trade secrets
  17. * and information that is proprietary to Black Diamond Software.
  18. * It may not be copied copied or distributed to any person or firm
  19. * without the express written permission of Black Diamond Software.
  20. * This permission is available only in the form of a Software Source
  21. * License Agreement.
  22. *
  23. * $Header: %Z% %F% %H% %T% %I%
  24. *
  25. *--- Includes ---------------------------------------------------------*/
  26. //#define WINVER 0x0300
  27. // added to be compatible with new windows.h (12/91) and wintric.h
  28. #define USECOMM
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <process.h>
  32. #include "windows.h"
  33. //#include "winstric.h" // added for win 3.1 compatibility 1/92
  34. #include "gide.h" // Serial Keys Function Proto
  35. #include "initgide.h" // Serial Keys Function Proto
  36. #include "w95trace.h"
  37. #include "sk_defs.h"
  38. #include "sk_comm.h"
  39. #include "drivers.h"
  40. #include "sk_ex.h"
  41. #define COMMTERMINATE 0xFFFFFFFF // this 'character' indicates a request to terminate
  42. // Local Function ProtoTypes --------------------------------
  43. static BOOL OpenComm();
  44. static void __cdecl ProcessComm(VOID *notUsed);
  45. static int ReadComm();
  46. // Local Variables ---------------------------------------------------
  47. static DCB s_dcbCommNew; // New DCB for comm port
  48. static DCB s_dcbCommOld; // Origional DCB for comm port
  49. static OVERLAPPED s_oRead; // Overlapped structure for reading.
  50. static HANDLE s_hFileComm;
  51. static HANDLE s_hThreadComm = NULL;
  52. static HDESK s_hdeskUser = NULL;
  53. static DWORD s_NullTimer;
  54. static int s_NullCount=0;
  55. static HANDLE s_ahEvents[2] = {NULL, NULL};
  56. #define iEventComm 0
  57. #define iEventExit 1
  58. /*---------------------------------------------------------------
  59. *
  60. * Global Functions -
  61. *
  62. *---------------------------------------------------------------*/
  63. /*---------------------------------------------------------------
  64. *
  65. * FUNCTION void InitComm()
  66. *
  67. * TYPE Global
  68. *
  69. * PURPOSE
  70. *
  71. * INPUTS None
  72. *
  73. * RETURNS None
  74. *
  75. *---------------------------------------------------------------*/
  76. BOOL InitComm()
  77. {
  78. BOOL fOk = TRUE;
  79. DBPRINTF(TEXT("InitComm()\r\n"));
  80. // Create Event for Overlap File Read
  81. s_ahEvents[iEventComm] = CreateEvent(NULL, TRUE, FALSE, NULL);
  82. fOk = (NULL != s_ahEvents[iEventComm]);
  83. if (fOk)
  84. {
  85. s_ahEvents[iEventExit] = CreateEvent(NULL, TRUE, FALSE, NULL);
  86. fOk = (NULL != s_ahEvents[iEventExit]);
  87. }
  88. if (!fOk)
  89. {
  90. TerminateComm();
  91. }
  92. return(fOk);
  93. }
  94. /*---------------------------------------------------------------
  95. *
  96. * FUNCTION void TerminateComm()
  97. *
  98. * TYPE Global
  99. *
  100. * PURPOSE The function is called for the final shutdown of
  101. * the comm port.
  102. *
  103. * INPUTS None
  104. *
  105. * RETURNS TRUE - Start Successful
  106. * FALSE - Start Failed
  107. *
  108. *---------------------------------------------------------------*/
  109. void TerminateComm()
  110. {
  111. BOOL fOk;
  112. int i;
  113. DBPRINTF(TEXT("TerminateComm()\r\n"));
  114. StopComm();
  115. for (i = 0; i < ARRAY_SIZE(s_ahEvents); ++i)
  116. {
  117. if (NULL != s_ahEvents[i])
  118. {
  119. fOk = CloseHandle(s_ahEvents[i]);
  120. DBPRINTF_IF(fOk, TEXT("Unable to Close Event\r\n"));
  121. s_ahEvents[i] = NULL;
  122. }
  123. }
  124. return;
  125. }
  126. /*---------------------------------------------------------------
  127. *
  128. * FUNCTION BOOL StartComm()
  129. *
  130. * TYPE Global
  131. *
  132. * PURPOSE The function is call to start the thread to
  133. * read and process data coming from the comm port.
  134. * It will create a thread and an event. This function
  135. * assumes that the comm port is already opened.
  136. *
  137. * INPUTS None
  138. *
  139. * RETURNS TRUE - Start Successful
  140. * FALSE - Start Failed
  141. *
  142. *---------------------------------------------------------------*/
  143. BOOL StartComm()
  144. {
  145. BOOL fOk = TRUE;
  146. DWORD Id;
  147. DBPRINTF(TEXT("StartComm()\r\n"));
  148. // ----------------------------------------------------------
  149. // Note: Comm Threads are started and stopped whenever
  150. // the com port is changed. The User logs in or out
  151. // or the comm configuration is changed.
  152. // ----------------------------------------------------------
  153. if (NULL == s_hFileComm && // no port currently in use
  154. (skNewKey.dwFlags & SERKF_AVAILABLE) &&
  155. (skNewKey.dwFlags & SERKF_SERIALKEYSON))
  156. {
  157. if (NULL != s_hThreadComm)
  158. {
  159. // This is an unexpected situation. We have the comm thread
  160. // running with no open comm port. The thread must be hung.
  161. // Let's close the open handle and forget about it.
  162. DBPRINTF(TEXT("StartComm() unexpected (NULL != s_hThreadComm)\r\n"));
  163. WaitForSingleObject(s_hThreadComm, 5 * 1000);
  164. if (NULL != s_hThreadComm)
  165. {
  166. DBPRINTF(TEXT("StartComm() s_hThreadComm abandoned\r\n"));
  167. CloseHandle(s_hThreadComm);
  168. s_hThreadComm = NULL;
  169. }
  170. }
  171. // skNewKey is used by OpenComm. We're setting skCurKey to default
  172. // values in case OpenComm fails.
  173. skCurKey.iBaudRate = 300; // No - Reset To Default Values
  174. skCurKey.iPortState= 0;
  175. skCurKey.dwFlags = 0;
  176. lstrcpy(skCurKey.lpszActivePort, TEXT("COM1"));
  177. lstrcpy(skCurKey.lpszPort, TEXT("COM1"));
  178. if (!OpenComm()) // Did Comm Open Ok?
  179. {
  180. skNewKey.iBaudRate = 300; // No - Reset To Default Values
  181. skNewKey.iPortState= 0;
  182. skNewKey.dwFlags = 0;
  183. lstrcpy(skNewKey.lpszActivePort, TEXT("COM1"));
  184. lstrcpy(skNewKey.lpszPort, TEXT("COM1"));
  185. fOk = FALSE;
  186. }
  187. else
  188. {
  189. // ensure we start with clean events
  190. ResetEvent(s_ahEvents[iEventComm]);
  191. ResetEvent(s_ahEvents[iEventExit]);
  192. memset(&s_oRead, 0, sizeof(OVERLAPPED)); // Init Struct
  193. s_oRead.hEvent = s_ahEvents[iEventComm]; // Store Event
  194. // Create thread to handle Processing Comm Port
  195. s_hThreadComm = (HANDLE)CreateThread( // Start Service Thread
  196. 0, 0,
  197. (LPTHREAD_START_ROUTINE) ProcessComm,
  198. 0, 0,&Id); // argument to thread
  199. if (NULL == s_hThreadComm)// Is Thread Handle Valid?
  200. {
  201. // Close out the Comm Port
  202. SetCommState(s_hFileComm, &s_dcbCommOld); // Restore Comm State
  203. CloseHandle(s_hFileComm);
  204. s_hFileComm = NULL;
  205. skCurKey.iPortState = 0;
  206. fOk = FALSE;
  207. }
  208. else
  209. {
  210. // Comm Thread Successfully Started Set The Current Values
  211. skCurKey.iBaudRate = skNewKey.iBaudRate;
  212. skCurKey.iPortState = 2;
  213. skCurKey.dwFlags = SERKF_SERIALKEYSON
  214. | SERKF_AVAILABLE
  215. | SERKF_ACTIVE;
  216. lstrcpy(skCurKey.lpszActivePort, skNewKey.lpszActivePort);
  217. lstrcpy(skCurKey.lpszPort, skNewKey.lpszActivePort);
  218. DBPRINTF(TEXT("---- Comm Started\r\n"));
  219. }
  220. }
  221. }
  222. return(fOk);
  223. }
  224. /*---------------------------------------------------------------
  225. *
  226. * FUNCTION void SuspendComm()
  227. *
  228. * TYPE Global
  229. *
  230. * PURPOSE The function is called to Pause the thread
  231. * reading and processing data coming from the comm port.
  232. *
  233. * INPUTS None
  234. *
  235. * RETURNS None
  236. *
  237. *---------------------------------------------------------------*/
  238. void SuspendComm()
  239. {
  240. DBPRINTF(TEXT("SuspendComm()\r\n"));
  241. if (NULL != s_hThreadComm)
  242. {
  243. SuspendThread(s_hThreadComm);
  244. }
  245. }
  246. /*---------------------------------------------------------------
  247. *
  248. * FUNCTION void ResumeComm()
  249. *
  250. * TYPE Global
  251. *
  252. * PURPOSE The function is called to resume the Paused thread.
  253. *
  254. * INPUTS None
  255. *
  256. * RETURNS None
  257. *
  258. *---------------------------------------------------------------*/
  259. void ResumeComm()
  260. {
  261. if (s_hThreadComm != NULL)
  262. ResumeThread(s_hThreadComm);
  263. }
  264. /*---------------------------------------------------------------
  265. *
  266. * FUNCTION void StopComm()
  267. *
  268. * TYPE Global
  269. *
  270. * PURPOSE The function is called to stop the thread
  271. * reading and processing data coming from the comm port.
  272. *
  273. * INPUTS None
  274. *
  275. * RETURNS TRUE - Start Successful
  276. * FALSE - Start Failed
  277. *
  278. *---------------------------------------------------------------*/
  279. void StopComm()
  280. {
  281. DBPRINTF(TEXT("StopComm()\r\n"));
  282. if (NULL != s_hFileComm)
  283. {
  284. skCurKey.dwFlags = SERKF_AVAILABLE;
  285. SetEvent(s_ahEvents[iEventExit]);
  286. if (NULL != s_hThreadComm)
  287. {
  288. DWORD dwRet;
  289. BOOL fOk;
  290. dwRet = WaitForSingleObject(s_hThreadComm, 5 * 1000);
  291. DBPRINTF_IF(WAIT_OBJECT_0 == dwRet, TEXT("StopComm() Comm Thread may be hung.\r\n"));
  292. CloseHandle(s_hThreadComm);
  293. s_hThreadComm = NULL;
  294. SetCommState(s_hFileComm, &s_dcbCommOld); // Restore Comm State
  295. fOk = CloseHandle(s_hFileComm); // Close the Comm Port
  296. DBPRINTF_IF(fOk, TEXT("Unable to Close Comm File\r\n"));
  297. s_hFileComm = NULL;
  298. skCurKey.iPortState = 0;
  299. }
  300. }
  301. }
  302. /*---------------------------------------------------------------
  303. *
  304. * FUNCTION void SetCommBaud(int Baud)
  305. *
  306. * TYPE Global
  307. *
  308. * PURPOSE
  309. *
  310. *
  311. * INPUTS None
  312. *
  313. * RETURNS TRUE - Start Successful
  314. * FALSE - Start Failed
  315. *
  316. *---------------------------------------------------------------*/
  317. void SetCommBaud(int Baud)
  318. {
  319. DBPRINTF(TEXT("SetCommBaud(%d)\r\n"), Baud);
  320. switch (Baud) // Check for Valid Baud Rates
  321. {
  322. case 300:
  323. case 600:
  324. case 1200:
  325. case 2400:
  326. case 4800:
  327. case 9600:
  328. case 19200:
  329. case 110:
  330. case 14400:
  331. case 38400:
  332. case 56000:
  333. case 57600:
  334. case 115200:
  335. break; // Baud Ok
  336. default:
  337. return; // Baud Invalid
  338. }
  339. skNewKey.iBaudRate = Baud; // Save Baud
  340. if (NULL != s_hFileComm) // Is Comm Port Open?
  341. {
  342. s_dcbCommNew.BaudRate = skNewKey.iBaudRate; // Set new DCB Params
  343. if (SetCommState(s_hFileComm, &s_dcbCommNew)) // State Change Ok?
  344. {
  345. skCurKey.iBaudRate = skNewKey.iBaudRate; // Save New Baud Rate
  346. } else
  347. {
  348. DBPRINTF(TEXT("SetCommState(%d) FAILED!\r\n"), Baud);
  349. // failed to set baud rate; try to revert it
  350. s_dcbCommNew.BaudRate = skCurKey.iBaudRate; // reset DCB Params
  351. if (!SetCommState(s_hFileComm, &s_dcbCommNew))
  352. DBPRINTF(TEXT("SetCommState(%d) FAILED!\r\n"), skCurKey.iBaudRate);
  353. }
  354. }
  355. }
  356. /*---------------------------------------------------------------
  357. *
  358. * Local Functions
  359. *
  360. /*---------------------------------------------------------------
  361. /*---------------------------------------------------------------
  362. *
  363. * FUNCTION void _CRTAPI1 ProcessComm()
  364. *
  365. * TYPE Local
  366. *
  367. * PURPOSE The function is the thread the cycles thru reading
  368. * processing data coming from the comm port.
  369. *
  370. * INPUTS None
  371. *
  372. * RETURNS None
  373. *
  374. *---------------------------------------------------------------*/
  375. static void __cdecl ProcessComm(VOID *notUsed)
  376. {
  377. int c;
  378. HWINSTA hwinstaSave;
  379. HWINSTA hwinstaUser;
  380. HDESK hdeskSave;
  381. DWORD dwThreadId;
  382. BOOL fCont;
  383. //------------------------------------------------------
  384. //
  385. // Note:
  386. // The following code set the input focus to the current
  387. // desktop. It is needed to insure that keyboard and mouse
  388. // events will be passed to the current desktop.
  389. //
  390. //------------------------------------------------------
  391. hwinstaSave = GetProcessWindowStation();
  392. dwThreadId = GetCurrentThreadId();
  393. hdeskSave = GetThreadDesktop(dwThreadId);
  394. hwinstaUser = OpenWindowStation(TEXT("WinSta0"), FALSE, MAXIMUM_ALLOWED);
  395. SetProcessWindowStation(hwinstaUser);
  396. serialKeysStartUpInit(); // Initialize the Serial Keys
  397. fCont = TRUE;
  398. while (fCont)
  399. {
  400. c = ReadComm(); // Read Char from Com Port
  401. switch (c)
  402. {
  403. case 0:
  404. // Is Character a Null
  405. // Is Null Timer > 30 Seconds
  406. if ((GetTickCount() - s_NullTimer) > 30000)
  407. {
  408. s_NullTimer = GetTickCount(); // Yes - Reset Timer
  409. s_NullCount = 1; // Reset Null Count
  410. } else {
  411. s_NullCount++; // No - Inc Null Count
  412. if (s_NullCount == 3) // Have we had 3 Null in 30 Sec.?
  413. {
  414. // the user is requesting us to reset
  415. SetCommBaud(300);
  416. // DeskSwitch should be unnessary, but if it gets out of sync,
  417. // this is where we resync
  418. s_NullCount = 0; // Reset Null Counter
  419. }
  420. }
  421. break;
  422. case COMMTERMINATE:
  423. fCont = FALSE;
  424. break;
  425. default:
  426. DeskSwitchToInput();
  427. serialKeysBegin((UCHAR)c); // Process Char
  428. break;
  429. }
  430. }
  431. SetThreadDesktop(hdeskSave);
  432. SetProcessWindowStation(hwinstaSave);
  433. CloseDesktop(s_hdeskUser);
  434. s_hdeskUser = NULL;
  435. CloseWindowStation(hwinstaUser);
  436. ExitThread(0); // Close Thread
  437. }
  438. /*---------------------------------------------------------------
  439. *
  440. * BOOL IsCommPortName()
  441. *
  442. * Determines whether a given filename is a valid COM port name.
  443. * Used by OpenComm so that it doesn't open a remote file or named
  444. * pipe instead.
  445. *
  446. *---------------------------------------------------------------*/
  447. static BOOL IsCommPortName( LPCTSTR pszFilename )
  448. {
  449. // Ensure that filename has form:
  450. // COMn[n]\0
  451. LPCTSTR pScan = pszFilename;
  452. // Must start with COMn, where COM can be any case,
  453. // and n is any 0..9 digit.
  454. if( *pScan != 'C' && *pScan != 'c' )
  455. return FALSE;
  456. pScan++;
  457. if( *pScan != 'O' && *pScan != 'o' )
  458. return FALSE;
  459. pScan++;
  460. if( *pScan != 'M' && *pScan != 'm' )
  461. return FALSE;
  462. pScan++;
  463. if( *pScan < '0' || *pScan > '9' )
  464. return FALSE;
  465. pScan++;
  466. /*
  467. // TODO: are COM54 really allowed?
  468. // Optional second digit
  469. if( *pScan >= '0' && *pScan <= '9' )
  470. pScan++;
  471. */
  472. // Manditory terminating nul
  473. if( *pScan != '\0' )
  474. return FALSE;
  475. return TRUE;
  476. }
  477. /*---------------------------------------------------------------
  478. *
  479. * FUNCTION BOOL OpenComm()
  480. *
  481. * TYPE Local
  482. *
  483. * PURPOSE This Function opens the comm port and sets the new
  484. * sets the Device Control Block.
  485. *
  486. * INPUTS None
  487. *
  488. * RETURNS TRUE - Open Ok / FALSE - Open Failed
  489. *
  490. *---------------------------------------------------------------*/
  491. static BOOL OpenComm()
  492. {
  493. BOOL fOk = FALSE;
  494. COMMTIMEOUTS ctmo;
  495. // Check that the path we're given looks like a COM port.
  496. // (Not, eg, a remote file or named pipe.)
  497. if( ! IsCommPortName( skNewKey.lpszActivePort ) )
  498. {
  499. DBPRINTF(TEXT("- Not a COMn port\r\n"));
  500. s_hFileComm = NULL;
  501. return FALSE;
  502. }
  503. // The Security flags ensure that if we are duped into opening
  504. // a named pipe, we'll do so anonymously, so that we can't be
  505. // impersonated.
  506. s_hFileComm = CreateFile(
  507. skNewKey.lpszActivePort,// FileName (Com Port)
  508. GENERIC_READ , // Access Mode
  509. 0, // Share Mode
  510. NULL, // Address of Security Descriptor
  511. OPEN_EXISTING, // How to Create
  512. FILE_ATTRIBUTE_NORMAL // File Attributes
  513. | FILE_FLAG_OVERLAPPED // Set for Async File Reads
  514. | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, // see above comment
  515. NULL); // Templet File.
  516. if (INVALID_HANDLE_VALUE == s_hFileComm) // File Ok?
  517. {
  518. DBPRINTF(TEXT("- Invalid File\r\n"));
  519. s_hFileComm = NULL;
  520. }
  521. else
  522. {
  523. BOOL fRet;
  524. COMMPROP cmmp;
  525. SetupComm(
  526. s_hFileComm,
  527. 1024, // size of input buffer
  528. 1024); // size of output buffer
  529. memset(&s_dcbCommOld, 0, sizeof(s_dcbCommOld));
  530. s_dcbCommOld.DCBlength = sizeof(s_dcbCommOld);
  531. GetCommState(s_hFileComm, &s_dcbCommOld); // Save Old DCB for restore
  532. s_dcbCommNew = s_dcbCommOld; // Copy to New
  533. // set XoffLim and XonLim based on actual buffer size
  534. fRet = GetCommProperties(s_hFileComm, &cmmp);
  535. if (fRet)
  536. {
  537. s_dcbCommNew.XoffLim = (WORD)(cmmp.dwCurrentRxQueue / 4);
  538. s_dcbCommNew.XonLim = (WORD)(cmmp.dwCurrentRxQueue / 4);
  539. }
  540. s_dcbCommNew.BaudRate = skNewKey.iBaudRate; // Set new DCB Params
  541. s_dcbCommNew.ByteSize = 8;
  542. s_dcbCommNew.Parity = NOPARITY;
  543. s_dcbCommNew.StopBits = ONESTOPBIT;
  544. s_dcbCommNew.fOutX = FALSE; // XOn/XOff used during transmission
  545. s_dcbCommNew.fInX = TRUE; // XOn/XOff used during reception
  546. s_dcbCommNew.fNull = FALSE; // tell windows not to strip nulls
  547. s_dcbCommNew.fBinary = TRUE;
  548. s_dcbCommNew.fOutxCtsFlow = FALSE;
  549. s_dcbCommNew.fOutxDsrFlow = FALSE;
  550. s_dcbCommNew.fDtrControl = DTR_CONTROL_ENABLE;
  551. s_dcbCommNew.fDsrSensitivity = FALSE;
  552. s_dcbCommNew.fErrorChar = TRUE;
  553. s_dcbCommNew.fRtsControl = RTS_CONTROL_DISABLE;
  554. s_dcbCommNew.fAbortOnError = FALSE;
  555. s_dcbCommNew.XonChar = (char)0x11;
  556. s_dcbCommNew.XoffChar = (char)0x13;
  557. s_dcbCommNew.ErrorChar = '\0';
  558. fOk = SetCommState(s_hFileComm, &s_dcbCommNew);
  559. memset(&ctmo, 0, sizeof(ctmo));
  560. SetCommTimeouts(s_hFileComm, &ctmo);
  561. }
  562. if (!fOk && NULL != s_hFileComm)
  563. {
  564. CloseHandle(s_hFileComm);
  565. s_hFileComm = NULL;
  566. }
  567. return(fOk);
  568. }
  569. /*---------------------------------------------------------------
  570. *
  571. * FUNCTION int ReadComm()
  572. *
  573. * TYPE Local
  574. *
  575. * PURPOSE This Function reads a character from the comm port.
  576. * If no character is present it wait on the HEV_COMM
  577. * Event untill a character is present
  578. *
  579. * INPUTS None
  580. *
  581. * RETURNS int - Character read (-1 = Error Read)
  582. *
  583. *---------------------------------------------------------------*/
  584. static int ReadComm()
  585. {
  586. int nRet;
  587. DWORD cbRead = 0;
  588. DWORD lastError, ComError;
  589. DWORD dwRetWait;
  590. BOOL fOk;
  591. BOOL fExit;
  592. BOOL fExitLoop = FALSE; // Boolean Flag to exit loop.
  593. UCHAR uchBuff;
  594. COMSTAT ComStat;
  595. fExit = (WAIT_OBJECT_0 == WaitForSingleObject(s_ahEvents[iEventExit], 0));
  596. if (!fExit)
  597. {
  598. fOk = ReadFile(s_hFileComm, &uchBuff, 1, &cbRead, &s_oRead);
  599. if (!fOk) // Was there a Read Error?
  600. {
  601. lastError = GetLastError(); // This var can be useful for debugging
  602. switch (lastError)
  603. {
  604. // If Error = IO_PENDING, wait til
  605. // the event hadle signals success,
  606. case ERROR_IO_PENDING:
  607. dwRetWait = WaitForMultipleObjects(
  608. ARRAY_SIZE(s_ahEvents), s_ahEvents, FALSE, INFINITE);
  609. switch (dwRetWait - WAIT_OBJECT_0)
  610. {
  611. case iEventComm:
  612. // this is the expected event
  613. GetOverlappedResult(s_hFileComm, &s_oRead, &cbRead, FALSE);
  614. if (cbRead < 1) // Did we read bytes;
  615. {
  616. // There was some error, return null
  617. nRet = 0;
  618. }
  619. else
  620. {
  621. nRet = uchBuff;
  622. }
  623. break;
  624. case iEventExit:
  625. fExit = TRUE;
  626. // fall through
  627. default:
  628. // this indicates and error and we exit to prevent loop
  629. nRet = COMMTERMINATE;
  630. break;
  631. }
  632. break;
  633. default:
  634. fOk = ClearCommError(s_hFileComm, &ComError,&ComStat);
  635. if (fOk)
  636. {
  637. nRet = 0; // return a null
  638. }
  639. else
  640. {
  641. nRet = COMMTERMINATE; // terminate
  642. }
  643. break;
  644. }
  645. }
  646. else
  647. {
  648. if (cbRead < 1) // Did we read bytes;
  649. {
  650. // There was some error, return null
  651. nRet = 0;
  652. }
  653. else
  654. {
  655. nRet = uchBuff;
  656. }
  657. }
  658. }
  659. if (fExit)
  660. {
  661. ResetEvent(s_ahEvents[iEventExit]);
  662. nRet = COMMTERMINATE;
  663. }
  664. return(nRet);
  665. }