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.

649 lines
16 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1996-1998 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: comport.c
  6. * Content: Routines for COM port I/O
  7. *@@BEGIN_MSINTERNAL
  8. * History:
  9. * Date By Reason
  10. * ==== == ======
  11. * 4/10/96 kipo created it
  12. * 4/12/96 kipo use GlobalAllocPtr to create memory
  13. * 4/15/96 kipo added msinternal
  14. * 5/22/96 kipo added support for RTSDTR flow control
  15. * 6/10/96 kipo added modem support
  16. * 6/22/96 kipo added support for EnumConnectionData(); added methods
  17. * to NewComPort().
  18. * 7/13/96 kipo added GetComPortAddress()
  19. * 8/15/96 kipo added CRC
  20. * 8/16/96 kipo loop on WriteFile to send large buffers
  21. * 8/19/96 kipo update thread interface
  22. * 1/06/97 kipo updated for objects
  23. * 2/18/97 kipo allow multiple instances of service provider
  24. * 4/08/97 kipo added support for separate modem and serial baud rates
  25. * 5/23/97 kipo added support return status codes
  26. * 11/24/97 kipo better error messages
  27. * 1/30/98 kipo added hTerminateThreadEvent to fix bugs #15220 & #15228
  28. *@@END_MSINTERNAL
  29. ***************************************************************************/
  30. #include <windows.h>
  31. #include <windowsx.h>
  32. #include "comport.h"
  33. #include "dpf.h"
  34. #include "macros.h"
  35. // constants
  36. #define READTIMEOUT 5000 // ms to wait before read times out
  37. #define WRITETIMEOUT 5000 // ms to wait before write times out
  38. #define WRITETOTALTIMEOUT 5000 // total ms to wait before write times out
  39. #define IOBUFFERSIZE 4096 // size of read/write buffers in bytes
  40. // prototypes
  41. static HRESULT SetupComPort(LPDPCOMPORT globals, HANDLE hCom);
  42. static HRESULT ShutdownComPort(LPDPCOMPORT globals);
  43. static DWORD ReadComPort(LPDPCOMPORT globals, LPVOID lpvBuffer, DWORD nMaxLength);
  44. static DWORD WriteComPort(LPDPCOMPORT globals, LPVOID lpvBuffer, DWORD dwBytesToWrite, BOOLEAN bQueueOnReenter);
  45. static HRESULT GetComPortBaudRate(LPDPCOMPORT globals, LPDWORD lpdwBaudRate);
  46. static HANDLE GetComPortHandle(LPDPCOMPORT globals);
  47. static DWORD WINAPI IOThread(LPVOID lpvParam1);
  48. /*
  49. * NewComPort
  50. *
  51. * Creates a com port object of the given size. The readRoutine is called whenever
  52. * a byte is received in the input thread.
  53. */
  54. HRESULT NewComPort(DWORD dwObjectSize,
  55. LPDIRECTPLAYSP lpDPlay, LPREADROUTINE lpReadRoutine,
  56. LPDPCOMPORT *lplpObject)
  57. {
  58. LPDPCOMPORT globals;
  59. DWORD dwError;
  60. // allocate space for base object and our globals
  61. globals =(LPDPCOMPORT) SP_MemAlloc(dwObjectSize);
  62. if (globals == NULL)
  63. {
  64. dwError = GetLastError();
  65. return (HRESULT_FROM_WIN32(dwError));
  66. }
  67. // store read routine pointer and IDirectPlaySP pointer
  68. globals->lpReadRoutine = lpReadRoutine;
  69. globals->lpDPlay = lpDPlay;
  70. // fill in base methods
  71. globals->Dispose = NULL;
  72. globals->Connect = NULL;
  73. globals->Disconnect = NULL;
  74. globals->Setup = SetupComPort;
  75. globals->Shutdown = ShutdownComPort;
  76. globals->Read = ReadComPort;
  77. globals->Write = WriteComPort;
  78. globals->GetBaudRate = GetComPortBaudRate;
  79. globals->GetHandle = GetComPortHandle;
  80. globals->GetAddress = NULL;
  81. globals->GetAddressChoices = NULL;
  82. // return base object
  83. *lplpObject = globals;
  84. return (DP_OK);
  85. }
  86. /*
  87. * SetupComPort
  88. *
  89. * Sets up the COM port for overlapped I/O with a read thread.
  90. */
  91. static HRESULT SetupComPort(LPDPCOMPORT globals, HANDLE hCom)
  92. {
  93. COMMTIMEOUTS timoutInfo;
  94. DWORD dwError;
  95. // store com port handle
  96. globals->hCom = hCom;
  97. // wake up read thread when a byte arrives
  98. SetCommMask(globals->hCom, EV_RXCHAR);
  99. // setup read/write buffer for I/O
  100. SetupComm(globals->hCom, IOBUFFERSIZE, IOBUFFERSIZE);
  101. // set time outs
  102. timoutInfo.ReadIntervalTimeout = MAXDWORD;
  103. timoutInfo.ReadTotalTimeoutMultiplier = 0;
  104. timoutInfo.ReadTotalTimeoutConstant = 0;
  105. timoutInfo.WriteTotalTimeoutMultiplier = 0;
  106. timoutInfo.WriteTotalTimeoutConstant = WRITETOTALTIMEOUT;
  107. if (!SetCommTimeouts(globals->hCom, &timoutInfo))
  108. goto Failure;
  109. // create I/O event used for overlapped read
  110. ZeroMemory(&globals->readOverlapped, sizeof(OVERLAPPED));
  111. globals->readOverlapped.hEvent = CreateEvent( NULL, // no security
  112. TRUE, // explicit reset req
  113. FALSE, // initial event reset
  114. NULL ); // no name
  115. if (globals->readOverlapped.hEvent == NULL)
  116. goto Failure;
  117. // create I/O event used for overlapped write
  118. ZeroMemory(&globals->writeOverlapped, sizeof(OVERLAPPED));
  119. globals->writeOverlapped.hEvent = CreateEvent( NULL, // no security
  120. TRUE, // explicit reset req
  121. FALSE, // initial event reset
  122. NULL ); // no name
  123. if (globals->writeOverlapped.hEvent == NULL)
  124. goto Failure;
  125. // create event used to signal I/O thread to exit
  126. globals->hTerminateThreadEvent = CreateEvent( NULL, // no security
  127. TRUE, // explicit reset req
  128. FALSE, // initial event reset
  129. NULL ); // no name
  130. if (globals->hTerminateThreadEvent == NULL)
  131. goto Failure;
  132. // Init vars for pending queue
  133. InitializeCriticalSection(&globals->csWriting);
  134. InitBilink(&globals->PendingSends);
  135. globals->bWriting=FALSE;
  136. // create read thread
  137. globals->hIOThread = CreateThread(
  138. NULL, // default security
  139. 0, // default stack size
  140. IOThread, // pointer to thread routine
  141. globals, // argument for thread
  142. 0, // start it right away
  143. &globals->IOThreadID);
  144. if (globals->hIOThread == NULL)
  145. goto Failure;
  146. // adjust thread priority to be higher than normal or the serial port will
  147. // back up and the game will slow down or lose messages.
  148. SetThreadPriority(globals->hIOThread, THREAD_PRIORITY_ABOVE_NORMAL);
  149. ResumeThread(globals->hIOThread);
  150. // assert DTR
  151. EscapeCommFunction(globals->hCom, SETDTR);
  152. return (DP_OK);
  153. Failure:
  154. dwError = GetLastError();
  155. ShutdownComPort(globals);
  156. return (HRESULT_FROM_WIN32(dwError));
  157. }
  158. /*
  159. * ShutdownComPort
  160. *
  161. * Stop's all I/O on COM port and releases allocated resources.
  162. */
  163. static HRESULT ShutdownComPort(LPDPCOMPORT globals)
  164. {
  165. if (globals->hIOThread)
  166. {
  167. // the thread will wake up if we disable event notifications using
  168. // SetCommMask. Need to set the hTerminateThread event before doing
  169. // this so the thread will know to exit
  170. SetEvent(globals->hTerminateThreadEvent);
  171. SetCommMask(globals->hCom, 0);
  172. WaitForSingleObject(globals->hIOThread, INFINITE);
  173. CloseHandle (globals->hIOThread);
  174. globals->hIOThread = NULL;
  175. // purge any outstanding reads/writes
  176. EscapeCommFunction(globals->hCom, CLRDTR);
  177. PurgeComm(globals->hCom, PURGE_TXABORT | PURGE_RXABORT |
  178. PURGE_TXCLEAR | PURGE_RXCLEAR );
  179. }
  180. if (globals->hTerminateThreadEvent)
  181. {
  182. CloseHandle(globals->hTerminateThreadEvent);
  183. globals->hTerminateThreadEvent = NULL;
  184. }
  185. if (globals->readOverlapped.hEvent)
  186. {
  187. CloseHandle(globals->readOverlapped.hEvent);
  188. globals->readOverlapped.hEvent = NULL;
  189. }
  190. if (globals->writeOverlapped.hEvent)
  191. {
  192. CloseHandle(globals->writeOverlapped.hEvent);
  193. globals->writeOverlapped.hEvent = NULL;
  194. }
  195. // the com port is shut down
  196. globals->hCom = NULL;
  197. // Free resources for pending queue
  198. DeleteCriticalSection(&globals->csWriting);
  199. return (DP_OK);
  200. }
  201. /*
  202. * ReadComPort
  203. *
  204. * Read bytes from COM port. Will block until all bytes have been read.
  205. */
  206. static DWORD ReadComPort(LPDPCOMPORT globals, LPVOID lpvBuffer, DWORD nMaxLength)
  207. {
  208. COMSTAT ComStat;
  209. DWORD dwErrorFlags, dwLength, dwError;
  210. ClearCommError(globals->hCom, &dwErrorFlags, &ComStat);
  211. dwLength = min(nMaxLength, ComStat.cbInQue);
  212. if (dwLength == 0)
  213. return (0);
  214. if (ReadFile(globals->hCom, lpvBuffer, dwLength, &dwLength, &globals->readOverlapped))
  215. return (dwLength);
  216. // deal with error
  217. dwError = GetLastError();
  218. if (dwError != ERROR_IO_PENDING)
  219. {
  220. DPF(0, "Error reading from com port: 0x%8X", dwError);
  221. return (0);
  222. }
  223. // wait for this transmission to complete
  224. if (WaitForSingleObject(globals->readOverlapped.hEvent, READTIMEOUT) != WAIT_OBJECT_0)
  225. {
  226. DPF(0, "Timed out reading com port after waiting %d ms", READTIMEOUT);
  227. return (0);
  228. }
  229. GetOverlappedResult(globals->hCom, &globals->readOverlapped, &dwLength, FALSE);
  230. globals->readOverlapped.Offset += dwLength;
  231. return (dwLength);
  232. }
  233. /*
  234. * WriteComPort
  235. *
  236. * Write bytes to COM port. Will block until all bytes have been written.
  237. */
  238. static DWORD WriteComPort(LPDPCOMPORT globals, LPVOID lpvBuffer, DWORD dwBytesToWrite, BOOLEAN bQueueOnReenter)
  239. {
  240. DWORD dwLength;
  241. DWORD dwBytesWritten;
  242. LPBYTE lpData;
  243. DWORD dwError;
  244. EnterCriticalSection(&globals->csWriting);
  245. if(!globals->bWriting || !bQueueOnReenter){
  246. globals->bWriting=TRUE;
  247. LeaveCriticalSection(&globals->csWriting);
  248. lpData = lpvBuffer;
  249. dwBytesWritten = 0;
  250. while (dwBytesWritten < dwBytesToWrite)
  251. {
  252. dwLength = dwBytesToWrite - dwBytesWritten;
  253. if (WriteFile(globals->hCom, lpData, dwLength, &dwLength, &globals->writeOverlapped))
  254. {
  255. dwBytesWritten += dwLength;
  256. globals->bWriting = FALSE;
  257. return (dwBytesWritten);
  258. }
  259. dwError = GetLastError();
  260. if (dwError != ERROR_IO_PENDING)
  261. {
  262. DPF(0, "Error writing to com port: 0x%8X", dwError);
  263. globals->bWriting = FALSE;
  264. return (dwBytesWritten);
  265. }
  266. // wait for this transmission to complete
  267. if (WaitForSingleObject(globals->writeOverlapped.hEvent, WRITETIMEOUT) != WAIT_OBJECT_0)
  268. {
  269. DPF(0, "Timed out writing to com port after waiting %d ms", WRITETIMEOUT);
  270. globals->bWriting = FALSE;
  271. return (dwBytesWritten);
  272. }
  273. if (GetOverlappedResult(globals->hCom, &globals->writeOverlapped, &dwLength, TRUE) == 0)
  274. {
  275. dwError = GetLastError();
  276. DPF(0, "Error writing to com port: 0x%8X", dwError);
  277. /*
  278. // a-josbor: this probably should return, but I'm unwilling to make the change so close to ship...
  279. globals->bWriting = FALSE;
  280. return (dwBytesWritten);
  281. */
  282. }
  283. globals->writeOverlapped.Offset += dwLength;
  284. lpData += dwLength;
  285. dwBytesWritten += dwLength;
  286. }
  287. if(bQueueOnReenter){ // don't drain queue recurrsively.
  288. // Drain any pending sends.
  289. EnterCriticalSection(&globals->csWriting);
  290. while(!EMPTY_BILINK(&globals->PendingSends)){
  291. LPPENDING_SEND lpPendingSend;
  292. lpPendingSend=CONTAINING_RECORD(globals->PendingSends.next,PENDING_SEND,Bilink);
  293. Delete(&lpPendingSend->Bilink);
  294. LeaveCriticalSection(&globals->csWriting);
  295. WriteComPort(globals,lpPendingSend->Data,lpPendingSend->dwBytesToWrite,FALSE);
  296. SP_MemFree(lpPendingSend);
  297. EnterCriticalSection(&globals->csWriting);
  298. }
  299. globals->bWriting=FALSE;
  300. LeaveCriticalSection(&globals->csWriting);
  301. }
  302. } else {
  303. LPPENDING_SEND lpPendingSend;
  304. // we are in the middle of writing, so copy this to the pending queue and it will get
  305. // sent after the current write.
  306. lpPendingSend = (LPPENDING_SEND) SP_MemAlloc(dwBytesToWrite+sizeof(PENDING_SEND));
  307. if(lpPendingSend){
  308. memcpy(lpPendingSend->Data,lpvBuffer,dwBytesToWrite);
  309. lpPendingSend->dwBytesToWrite=dwBytesToWrite;
  310. InsertBefore(&lpPendingSend->Bilink, &globals->PendingSends);
  311. }
  312. LeaveCriticalSection(&globals->csWriting);
  313. dwBytesWritten=dwBytesToWrite;
  314. }
  315. return (dwBytesWritten);
  316. }
  317. /*
  318. * GetComPortBaudRate
  319. *
  320. * Get baud rate of com port.
  321. */
  322. static HRESULT GetComPortBaudRate(LPDPCOMPORT globals, LPDWORD lpdwBaudRate)
  323. {
  324. DCB dcb;
  325. DWORD dwError;
  326. ZeroMemory(&dcb, sizeof(DCB));
  327. dcb.DCBlength = sizeof(DCB);
  328. if (!GetCommState(globals->hCom, &dcb))
  329. goto Failure;
  330. *lpdwBaudRate = dcb.BaudRate;
  331. return (DP_OK);
  332. Failure:
  333. dwError = GetLastError();
  334. return (HRESULT_FROM_WIN32(dwError));
  335. }
  336. /*
  337. * GetComPortHandle
  338. *
  339. * Get handle of com port.
  340. */
  341. static HANDLE GetComPortHandle(LPDPCOMPORT globals)
  342. {
  343. return (globals->hCom);
  344. }
  345. /*
  346. * IOThread
  347. *
  348. * Thread to wait for events from COM port. Will call the read routine if an byte
  349. * is received.
  350. */
  351. DWORD WINAPI IOThread(LPVOID lpvParam1)
  352. {
  353. LPDPCOMPORT globals = (LPDPCOMPORT) lpvParam1;
  354. DWORD dwTransfer, dwEvtMask;
  355. OVERLAPPED os;
  356. HANDLE events[3];
  357. DWORD dwResult;
  358. // create I/O event used for overlapped read
  359. ZeroMemory(&os, sizeof(OVERLAPPED));
  360. os.hEvent = CreateEvent(NULL, // no security
  361. TRUE, // explicit reset req
  362. FALSE, // initial event reset
  363. NULL ); // no name
  364. if (os.hEvent == NULL)
  365. goto CreateEventFailed;
  366. if (!SetCommMask(globals->hCom, EV_RXCHAR))
  367. goto SetComMaskFailed;
  368. // events to use when waiting for overlapped I/O to complete
  369. events[0] = globals->hTerminateThreadEvent;
  370. events[1] = os.hEvent;
  371. events[2] = (HANDLE) -1; // work around Win95 bugs in WaitForMultipleObjects
  372. // spin until this event is signaled during Close.
  373. while (WaitForSingleObject(globals->hTerminateThreadEvent, 0) == WAIT_TIMEOUT)
  374. {
  375. dwEvtMask = 0;
  376. // wait for COM port event
  377. if (!WaitCommEvent(globals->hCom, &dwEvtMask, &os))
  378. {
  379. if (GetLastError() == ERROR_IO_PENDING)
  380. {
  381. // wait for overlapped I/O to complete or the terminating event
  382. // to be set. This lets us terminate this thread even if the I/O
  383. // never completes, which fixes a bug on NT 4.0
  384. dwResult = WaitForMultipleObjects(2, events, FALSE, INFINITE);
  385. // terminating event was set
  386. if (dwResult == WAIT_OBJECT_0)
  387. {
  388. break; // exit the thread
  389. }
  390. // I/O completed
  391. else if (dwResult == (WAIT_OBJECT_0 + 1))
  392. {
  393. GetOverlappedResult(globals->hCom, &os, &dwTransfer, TRUE);
  394. os.Offset += dwTransfer;
  395. }
  396. }
  397. }
  398. // was a read event
  399. if (dwEvtMask & EV_RXCHAR)
  400. {
  401. if (globals->lpReadRoutine)
  402. globals->lpReadRoutine(globals->lpDPlay); // call read routine
  403. }
  404. }
  405. SetComMaskFailed:
  406. CloseHandle(os.hEvent);
  407. CreateEventFailed:
  408. ExitThread(0);
  409. return (0);
  410. }
  411. /*
  412. Name : "CRC-32"
  413. Width : 32
  414. Poly : 04C11DB7
  415. Init : FFFFFFFF
  416. RefIn : True
  417. RefOut : True
  418. XorOut : FFFFFFFF
  419. Check : CBF43926
  420. This is supposedly what Ethernet uses
  421. */
  422. #if 0
  423. #define WIDTH 32
  424. #define POLY 0x04C11DB7
  425. #define INITVALUE 0xFFFFFFFF
  426. #define REFIN TRUE
  427. #define XOROUT 0xFFFFFFFF
  428. #define CHECK 0xCBF43926
  429. #define WIDMASK 0xFFFFFFFF // value is (2^WIDTH)-1
  430. #endif
  431. /*
  432. Name : "CRC-16"
  433. Width : 16
  434. Poly : 8005
  435. Init : 0000
  436. RefIn : True
  437. RefOut : True
  438. XorOut : 0000
  439. Check : BB3D
  440. */
  441. #if 1
  442. #define WIDTH 16
  443. #define POLY 0x8005
  444. #define INITVALUE 0
  445. #define REFIN TRUE
  446. #define XOROUT 0
  447. #define CHECK 0xBB3D
  448. #define WIDMASK 0x0000FFFF // value is (2^WIDTH)-1
  449. #endif
  450. #define BITMASK(X) (1L << (X))
  451. DWORD crc_normal(LPBYTE blk_adr, DWORD blk_len, DWORD crctable[])
  452. {
  453. DWORD crc = INITVALUE;
  454. while (blk_len--)
  455. crc = crctable[((crc>>24) ^ *blk_adr++) & 0xFFL] ^ (crc << 8);
  456. return (crc ^ XOROUT);
  457. }
  458. DWORD crc_reflected(LPBYTE blk_adr, DWORD blk_len, DWORD crctable[])
  459. {
  460. DWORD crc = INITVALUE;
  461. while (blk_len--)
  462. crc = crctable[(crc ^ *blk_adr++) & 0xFFL] ^ (crc >> 8);
  463. return (crc ^ XOROUT);
  464. }
  465. DWORD reflect(DWORD v, int b)
  466. /* Returns the value v with the bottom b [0,32] bits reflected. */
  467. /* Example: reflect(0x3e23L,3) == 0x3e26 */
  468. {
  469. int i;
  470. DWORD t = v;
  471. for (i = 0; i < b; i++)
  472. {
  473. if (t & 1L)
  474. v |= BITMASK((b-1)-i);
  475. else
  476. v &= ~BITMASK((b-1)-i);
  477. t >>= 1;
  478. }
  479. return v;
  480. }
  481. DWORD cm_tab (int index)
  482. {
  483. int i;
  484. DWORD r;
  485. DWORD topbit = (DWORD) BITMASK(WIDTH-1);
  486. DWORD inbyte = (DWORD) index;
  487. if (REFIN)
  488. inbyte = reflect(inbyte, 8);
  489. r = inbyte << (WIDTH-8);
  490. for (i = 0; i < 8; i++)
  491. {
  492. if (r & topbit)
  493. r = (r << 1) ^ POLY;
  494. else
  495. r <<= 1;
  496. }
  497. if (REFIN)
  498. r = reflect(r, WIDTH);
  499. return (r & WIDMASK);
  500. }
  501. void generate_table(DWORD dwTable[])
  502. {
  503. int i;
  504. for (i = 0; i < 256; i++)
  505. {
  506. dwTable[i] = cm_tab(i);
  507. }
  508. }
  509. // todo - make this a static table
  510. DWORD gCRCTable[256];
  511. BOOL gTableCreated = FALSE;
  512. DWORD GenerateCRC(LPVOID pBuffer, DWORD dwBufferSize)
  513. {
  514. if (!gTableCreated)
  515. {
  516. generate_table(gCRCTable);
  517. gTableCreated = TRUE;
  518. }
  519. return (crc_reflected(pBuffer, dwBufferSize, gCRCTable));
  520. }