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.

2775 lines
83 KiB

  1. /*++
  2. *
  3. * WOW v1.0
  4. *
  5. * Copyright (c) 1991, Microsoft Corporation
  6. *
  7. * WUCOMM.C
  8. * WOW32 16-bit User API support
  9. *
  10. * History:
  11. * Created 07-Mar-1991 by Jeff Parsons (jeffpar)
  12. * made real Dec-1992 by Craig Jones (v-cjones)
  13. * made work Apr-1993 by Craig Jones (v-cjones)
  14. * made fast Jun-1993 by Craig Jones (v-cjones)
  15. --*/
  16. #include "precomp.h"
  17. #pragma hdrstop
  18. #include <ntddser.h>
  19. MODNAME(wucomm.c);
  20. /* Define the table for mapping Win3.1 idComDev's to 32-bit comm HFILE's. */
  21. /* This table is indexed by the 16-bit idComDev that we return to the app */
  22. /* which is assigned based on the device name (see wucomm.h). You can */
  23. /* use GETPWOWPTR(idComDev) to get the ptr to the corresponding WOWPort */
  24. /* struct from PortTab[]. */
  25. /* This table must contain NUMPORTS (def'd in wucomm.h) entries */
  26. PORTTAB PortTab[] = { {"COM1", NULL},
  27. {"COM2", NULL},
  28. {"COM3", NULL},
  29. {"COM4", NULL},
  30. {"COM5", NULL},
  31. {"COM6", NULL},
  32. {"COM7", NULL},
  33. {"COM8", NULL},
  34. {"COM9", NULL},
  35. {"LPT1", NULL},
  36. {"LPT2", NULL},
  37. {"LPT3", NULL}
  38. };
  39. /* function prototypes for local support functions */
  40. DWORD Baud16toBaud32(UINT BaudRate);
  41. WORD Baud32toBaud16(DWORD BaudRate);
  42. void DCB16toDCB32(PWOWPORT pWOWPort, LPDCB lpdcb32, PDCB16 pdcb16);
  43. void DCB32toDCB16(PDCB16 pdcb16, LPDCB lpdcb32, UINT idComDev, BOOL fChEvt);
  44. BOOL DeletePortTabEntry(PWOWPORT pWOWPort);
  45. ULONG WOWCommWriterThread(LPVOID pWOWPortStruct);
  46. USHORT EnqueueCommWrite(PWOWPORT pwp, PUCHAR pch, USHORT cb);
  47. UINT GetModePortTabIndex(PSZ pszModeStr);
  48. BOOL GetPortName(LPSTR pszMode, LPSTR pszPort);
  49. UINT GetStrPortTabIndex(PSZ szPort);
  50. BOOL InitDCB32(LPDCB pdcb32, LPSTR pszModeStr);
  51. VOID InitDEB16(PCOMDEB16 pComDEB16, UINT iTab, WORD QInSize, WORD QOutSize);
  52. PSZ StripPortName(PSZ psz);
  53. PSZ GetPortStringToken(PSZ pszSrc, PSZ pszToken);
  54. BOOL MSRWait(PWOWPORT pwp);
  55. BOOL IsQLinkGold(WORD wTDB);
  56. /* prototypes for Modem interrupt emulation thread support */
  57. VOID WOWModemIntThread(PWOWPORT pWOWPortStruct);
  58. BOOL WOWStartModemIntThread(PWOWPORT pWOWPort);
  59. DWORD WOWGetCommError(PWOWPORT pWOWPort);
  60. // Win3.1 returns:
  61. // 0 on success OR LPT.
  62. // -1 on ANY error.
  63. ULONG FASTCALL WU32BuildCommDCB(PVDMFRAME pFrame)
  64. {
  65. ULONG ul = (ULONG)-1;
  66. UINT len, iTab;
  67. PSZ psz1;
  68. PDCB16 pdcb16;
  69. DCB dcb32;
  70. register PBUILDCOMMDCB16 parg16;
  71. GETARGPTR(pFrame, sizeof(BUILDCOMMDCB16), parg16);
  72. GETPSZPTR(parg16->f1, psz1);
  73. // if valid device name...
  74. if((INT)(iTab = GetModePortTabIndex(psz1)) >= 0) {
  75. // Initialize a Win3.1 compatible 32-bit DCB
  76. if(InitDCB32(&dcb32, psz1)) {
  77. GETMISCPTR(parg16->f2, pdcb16);
  78. if(pdcb16) {
  79. // copy the psz1 fields to the 16-bit struct
  80. iTab = (VALIDCOM(iTab) ? iTab : TABIDTOLPT(iTab));
  81. DCB32toDCB16(pdcb16, &dcb32, iTab, FALSE);
  82. // set timeouts for COMx ports only
  83. if(VALIDCOM(iTab)) {
  84. // 'P' is the only "retry" option supported in Win3.1
  85. len = strlen(psz1) - 1;
  86. while(psz1[len] != ' ') { // delete trailing spaces
  87. len--;
  88. }
  89. if((psz1[len] == 'P') || (psz1[len] == 'p')) {
  90. pdcb16->RlsTimeout = INFINITE_TIMEOUT;
  91. pdcb16->CtsTimeout = INFINITE_TIMEOUT;
  92. pdcb16->DsrTimeout = INFINITE_TIMEOUT;
  93. }
  94. }
  95. FLUSHVDMPTR(parg16->f2, sizeof(DCB16), pdcb16);
  96. FREEMISCPTR(pdcb16);
  97. ul = 0; // Win3.1 returns 0 if success
  98. }
  99. }
  100. FREEPSZPTR(psz1);
  101. }
  102. #ifdef DEBUG
  103. if(!(ul==0)) {
  104. LOGDEBUG(0,("WOW::WU32BuildCommDCB: failed\n"));
  105. }
  106. #endif
  107. FREEARGPTR(parg16);
  108. RETURN(ul);
  109. }
  110. // Win3.1 returns:
  111. // Error word on success OR LPTx.
  112. // 0x8000 on bad idComDev.
  113. ULONG FASTCALL WU32ClearCommBreak(PVDMFRAME pFrame)
  114. {
  115. ULONG ul = 0x00008000;
  116. UINT idComDev;
  117. PWOWPORT pWOWPort;
  118. register PCLEARCOMMBREAK16 parg16;
  119. GETARGPTR(pFrame, sizeof(CLEARCOMMBREAK16), parg16);
  120. idComDev = UINT32(parg16->f1);
  121. if (pWOWPort = GETPWOWPTR(idComDev)) {
  122. if (VALIDCOM(idComDev)) {
  123. if(!ClearCommBreak(pWOWPort->h32)) {
  124. WOWGetCommError(pWOWPort);
  125. }
  126. }
  127. ul = pWOWPort->dwErrCode;
  128. }
  129. #ifdef DEBUG
  130. if(!(ul!=0x00008000)) {
  131. LOGDEBUG(0,("WOW::WU32ClearCommBreak: failed\n"));
  132. }
  133. #endif
  134. FREEARGPTR(parg16);
  135. RETURN(ul);
  136. }
  137. // Win3.1 returns:
  138. // 0 if success OR if LPTx.
  139. // -1 for bad idComDev OR port not open.
  140. // -2 for Timeout error.
  141. // We pass back (as a 2nd parameter) the DWORD obtained from the call to
  142. // GlobalDosAlloc() in IOpenComm() in user.exe. (WOWModemIntThread() support)
  143. ULONG FASTCALL WU32CloseComm(PVDMFRAME pFrame)
  144. {
  145. ULONG ul = (ULONG)-1;
  146. UINT idComDev;
  147. PDWORD16 lpdwDEB16;
  148. PWOWPORT pWOWPort = NULL;
  149. register PCLOSECOMM16 parg16;
  150. GETARGPTR(pFrame, sizeof(CLOSECOMM16), parg16);
  151. idComDev = UINT32(parg16->f1);
  152. if (pWOWPort = GETPWOWPTR(idComDev)) {
  153. // pass back the 16:16 ptr for the WOWModemIntThread() support
  154. GETMISCPTR(parg16->f2, lpdwDEB16);
  155. if (lpdwDEB16) {
  156. *lpdwDEB16 = pWOWPort->dwComDEB16;
  157. FLUSHVDMPTR(parg16->f2, sizeof(DWORD), lpdwDEB16);
  158. FREEMISCPTR(lpdwDEB16);
  159. }
  160. // clean up the PortTab[] entry
  161. if (DeletePortTabEntry(pWOWPort)) {
  162. ul = (ULONG)-2; // return Win3.1 timeOut error
  163. }
  164. else {
  165. ul = 0;
  166. }
  167. }
  168. else {
  169. LOGDEBUG (0, ("WOW::WU32CloseComm: Not a valid COM or LPT\n"));
  170. }
  171. #ifdef DEBUG
  172. if(!(ul==0)) {
  173. LOGDEBUG(0,("WOW::WU32CloseComm: failed\n"));
  174. }
  175. #endif
  176. FREEARGPTR(parg16);
  177. RETURN(ul);
  178. }
  179. // Win3.1 returns:
  180. // TRUE on success.
  181. // FALSE if error OR if EnableCommNotification() not supported.
  182. // User16 validation layer returns 0 for bad hwnd.
  183. ULONG FASTCALL WU32EnableCommNotification(PVDMFRAME pFrame)
  184. {
  185. ULONG ul = (ULONG)FALSE;
  186. UINT idComDev;
  187. WORD cbQue;
  188. BOOL fOK = TRUE;
  189. PWOWPORT pWOWPort;
  190. PCOMDEB16 lpComDEB16;
  191. register PENABLECOMMNOTIFICATION16 parg16;
  192. GETARGPTR(pFrame, sizeof(ENABLECOMMNOTIFICATION16), parg16);
  193. idComDev = UINT32(parg16->f1);
  194. if ((VALIDCOM(idComDev)) && (pWOWPort = PortTab[idComDev].pWOWPort)) {
  195. lpComDEB16 = pWOWPort->lpComDEB16;
  196. // if they are trying to disable notifcation (HWND == NULL)
  197. if(WORD32(parg16->f2) == 0) {
  198. lpComDEB16->NotifyHandle = 0;
  199. lpComDEB16->NotifyFlags = CN_TRANSMITHI;
  200. lpComDEB16->RecvTrigger = (WORD)-1;
  201. lpComDEB16->SendTrigger = 0;
  202. ul = (ULONG)TRUE;
  203. }
  204. // Validate non-null hwnd's since hwnd validation is disabled in
  205. // user16 validation layer
  206. else if(!IsWindow(HWND32(parg16->f2))) {
  207. ul = (ULONG)FALSE;
  208. }
  209. // else set up the notification mechanisms
  210. else {
  211. // if the Modem interrupt thread hasn't started yet -- go start it
  212. if(pWOWPort->hMiThread == NULL) {
  213. if(!WOWStartModemIntThread(pWOWPort)) {
  214. fOK = FALSE;
  215. }
  216. }
  217. // update the DEB to reflect notification
  218. if(fOK) {
  219. lpComDEB16->NotifyHandle = WORD32(parg16->f2);
  220. lpComDEB16->NotifyFlags = CN_TRANSMITHI | CN_NOTIFYHI;
  221. // set trigger values the same way Win3.1 does
  222. cbQue = WORD32(parg16->f3);
  223. if((cbQue < lpComDEB16->QInSize) || ((SHORT)cbQue == -1)) {
  224. lpComDEB16->RecvTrigger = cbQue;
  225. }
  226. else {
  227. lpComDEB16->RecvTrigger = lpComDEB16->QInSize - 10;
  228. }
  229. cbQue = WORD32(parg16->f4);
  230. if((cbQue < lpComDEB16->QOutSize) || ((SHORT)cbQue == -1)) {
  231. lpComDEB16->SendTrigger = cbQue;
  232. }
  233. else {
  234. lpComDEB16->SendTrigger = lpComDEB16->QOutSize - 10;
  235. }
  236. ul = (ULONG)TRUE;
  237. }
  238. }
  239. }
  240. // else there is no notification for LPT in Win3.1
  241. else {
  242. ul = (ULONG)FALSE;
  243. }
  244. #ifdef DEBUG
  245. if(!(ul==1)) {
  246. LOGDEBUG(0,("WOW::WU32EnableCommNotification: failed\n"));
  247. }
  248. #endif
  249. FREEARGPTR(parg16);
  250. RETURN(ul);
  251. }
  252. // Win3.1 returns:
  253. // The value from the specified function.
  254. // The error word for: the line & signal state functions,
  255. // function not implemented, OR LPTx where function != (RESETDEV||GETMAXLPT).
  256. // 0x8000 for bad idComDev.
  257. ULONG FASTCALL WU32EscapeCommFunction(PVDMFRAME pFrame)
  258. {
  259. ULONG ul = 0x00008000;
  260. UINT idComDev;
  261. UINT nFunction;
  262. WORD IRQ;
  263. VPVOID vpBiosData;
  264. PWORD16 pwBiosData;
  265. PWOWPORT pWOWPort;
  266. register PESCAPECOMMFUNCTION16 parg16;
  267. GETARGPTR(pFrame, sizeof(ESCAPECOMMFUNCTION16), parg16);
  268. // this construct is set up this way because Win3.1 will allow GETMAXCOM
  269. // & GETMAXLPT to succeed as long as the idComDev is in the valid range.
  270. // (ie: the app doesn't have to call OpenComm() first to set up the PortTab)
  271. // for RESETDEV we tell them that we reset the printer. (we're such liars!)
  272. nFunction = WORD32(parg16->f2);
  273. idComDev = UINT32(parg16->f1);
  274. if (VALIDCOM(idComDev)) {
  275. if (nFunction == GETMAXCOM) {
  276. ul = NUMCOMS-1;
  277. } else if (nFunction == GETBASEIRQ || nFunction == GETBASEIRQ+1) {
  278. ul = 0xFFFFFFFF;
  279. if (idComDev < COM5) {
  280. vpBiosData = (VPVOID) (RM_BIOS_DATA + (idComDev * sizeof(WORD)));
  281. if (pwBiosData = (PWORD16)GetRModeVDMPointer(vpBiosData)) {
  282. if (idComDev == COM1 || idComDev == COM3) {
  283. IRQ = IRQ4;
  284. } else {
  285. IRQ = IRQ3;
  286. }
  287. ul = MAKELONG((WORD)(*pwBiosData), IRQ);
  288. FREEVDMPTR(pwBiosData);
  289. }
  290. }
  291. } else {
  292. // for the other functions they must have called OpenComm()
  293. if (pWOWPort = PortTab[idComDev].pWOWPort) {
  294. switch(nFunction) {
  295. // line & signal state functions
  296. case SETXOFF:
  297. case SETXON:
  298. case SETRTS:
  299. case CLRRTS:
  300. case SETDTR:
  301. case CLRDTR:
  302. if(!EscapeCommFunction(pWOWPort->h32, nFunction)) {
  303. WOWGetCommError(pWOWPort);
  304. }
  305. ul = pWOWPort->dwErrCode;
  306. break;
  307. // 0:
  308. case 0:
  309. ul = 0; // like WFW
  310. break;
  311. // any other value...
  312. default:
  313. // non-zero is error: use dwErrcode if there is one
  314. if(pWOWPort->dwErrCode)
  315. ul = pWOWPort->dwErrCode;
  316. // else use what WFW seems inclined to return
  317. else
  318. ul = CE_OVERRUN | CE_RXPARITY;
  319. break;
  320. }
  321. }
  322. }
  323. } else if (VALIDLPT(idComDev)) {
  324. if(nFunction == RESETDEV) {
  325. ul = 0; // no error (ie. "just tell them we did it" - TonyE)
  326. }
  327. else if(nFunction == GETMAXLPT) {
  328. ul = LPTLAST;
  329. }
  330. else if (pWOWPort = PortTab[GETLPTID(idComDev)].pWOWPort) {
  331. ul = pWOWPort->dwErrCode;
  332. }
  333. else {
  334. ul = 0;
  335. }
  336. }
  337. FREEARGPTR(parg16);
  338. RETURN(ul);
  339. }
  340. // Win3.1 returns:
  341. // 0 on success.
  342. // 0x8000 if bad idComDev.
  343. // Error word on error or LPTx.
  344. ULONG FASTCALL WU32FlushComm(PVDMFRAME pFrame)
  345. {
  346. ULONG ul = 0x00008000;
  347. UINT idComDev;
  348. DWORD dwAction;
  349. PWOWPORT pWOWPort;
  350. register PFLUSHCOMM16 parg16;
  351. GETARGPTR(pFrame, sizeof(FLUSHCOMM16), parg16);
  352. idComDev = UINT32(parg16->f1);
  353. if (pWOWPort = GETPWOWPTR(idComDev)) {
  354. // is a COMx?
  355. if (VALIDCOM(idComDev)) {
  356. // if flush transmit buffer specified
  357. dwAction = PURGE_RXCLEAR;
  358. if(parg16->f2 == 0) {
  359. dwAction = PURGE_TXCLEAR | PURGE_TXABORT;
  360. //
  361. // Flush the local writers buffer
  362. //
  363. EnterCriticalSection(&pWOWPort->csWrite);
  364. pWOWPort->pchWriteHead =
  365. pWOWPort->pchWriteTail = pWOWPort->pchWriteBuf;
  366. pWOWPort->cbWriteFree = pWOWPort->cbWriteBuf - 1;
  367. pWOWPort->cbWritePending = 0;
  368. LeaveCriticalSection(&pWOWPort->csWrite);
  369. }
  370. if(PurgeComm(pWOWPort->h32, dwAction)) {
  371. if(dwAction == PURGE_RXCLEAR) {
  372. pWOWPort->fUnGet = FALSE;
  373. }
  374. ul = 0; // Win3.1 returns 0 on success
  375. }
  376. else {
  377. WOWGetCommError(pWOWPort);
  378. ul = pWOWPort->dwErrCode;
  379. }
  380. }
  381. // else just return current error code for LPTx
  382. else {
  383. ul = pWOWPort->dwErrCode;
  384. }
  385. }
  386. #ifdef DEBUG
  387. if(!(ul==0)) {
  388. LOGDEBUG(0,("WOW::WU32FlushComm: failed\n"));
  389. }
  390. #endif
  391. FREEARGPTR(parg16);
  392. RETURN(ul);
  393. }
  394. // Win3.1 returns:
  395. // 0x8000 for bad idComDev.
  396. // The error word for all other cases.
  397. ULONG FASTCALL WU32GetCommError(PVDMFRAME pFrame)
  398. {
  399. ULONG ul = 0x00008000;
  400. UINT idComDev;
  401. PWOWPORT pWOWPort;
  402. PCOMSTAT16 pcs16;
  403. register PGETCOMMERROR16 parg16;
  404. GETARGPTR(pFrame, sizeof(GETCOMMERROR16), parg16);
  405. GETMISCPTR(parg16->f2, pcs16);
  406. idComDev = UINT32(parg16->f1);
  407. if (pWOWPort = GETPWOWPTR (idComDev)) {
  408. if (VALIDCOM(idComDev) && pcs16) {
  409. WOWGetCommError(pWOWPort);
  410. // Always update the COMSTAT status byte, DynComm depends on it.
  411. pcs16->status = 0;
  412. if(pWOWPort->cs.fCtsHold) pcs16->status |= W31CS_fCtsHold;
  413. if(pWOWPort->cs.fDsrHold) pcs16->status |= W31CS_fDsrHold;
  414. // Note: RlsdHold is zero'd out on Win3.1
  415. if(pWOWPort->cs.fRlsdHold) pcs16->status |= W31CS_fRlsdHold;
  416. if(pWOWPort->cs.fXoffHold) pcs16->status |= W31CS_fXoffHold;
  417. if(pWOWPort->cs.fXoffSent) pcs16->status |= W31CS_fSentHold;
  418. if(pWOWPort->cs.fEof) pcs16->status |= W31CS_fEof;
  419. if(pWOWPort->cs.fTxim) pcs16->status |= W31CS_fTxim;
  420. pcs16->cbInQue = (WORD)pWOWPort->cs.cbInQue;
  421. pcs16->cbOutQue = (WORD)pWOWPort->cs.cbOutQue;
  422. // account for the UnGot char (if any)
  423. if(pWOWPort->fUnGet) {
  424. pcs16->cbInQue++;
  425. }
  426. }
  427. // if an LPT OR pcs16 == NULL, Win3.1 returns the error code
  428. else {
  429. // for LPT's Win3.1 just zero's the COMSTAT & returns the error code
  430. if(VALIDLPT(idComDev)) {
  431. if(pcs16) {
  432. RtlZeroMemory((PVOID)pcs16, sizeof(COMSTAT16));
  433. }
  434. }
  435. }
  436. ul = (ULONG)pWOWPort->dwErrCode;
  437. // clear the error now that the app has got it (but maintain queues)
  438. pWOWPort->dwErrCode = 0;
  439. pWOWPort->lpComDEB16->ComErr = 0;
  440. RtlZeroMemory((PVOID)&(pWOWPort->cs), sizeof(COMSTAT));
  441. if(pcs16) {
  442. pWOWPort->cs.cbInQue = pcs16->cbInQue;
  443. pWOWPort->cs.cbOutQue = pcs16->cbOutQue;
  444. }
  445. }
  446. FLUSHVDMPTR(parg16->f2, sizeof(COMSTAT16), pcs16);
  447. FREEMISCPTR(pcs16);
  448. FREEARGPTR(parg16);
  449. RETURN(ul);
  450. }
  451. // Win3.1 returns:
  452. // EvtWord on success.
  453. // 0 for bad idComDev OR LPTx.
  454. ULONG FASTCALL WU32GetCommEventMask(PVDMFRAME pFrame)
  455. {
  456. ULONG ul=0;
  457. DWORD dwEvtMask;
  458. UINT idComDev;
  459. PWOWPORT pWOWPort;
  460. PCOMDEB16 pDEB16;
  461. register PGETCOMMEVENTMASK16 parg16;
  462. GETARGPTR(pFrame, sizeof(GETCOMMEVENTMASK16), parg16);
  463. idComDev = UINT32(parg16->f1);
  464. if (VALIDCOM(idComDev)) {
  465. if(pWOWPort = PortTab[idComDev].pWOWPort) {
  466. if(pDEB16 = pWOWPort->lpComDEB16) {
  467. // in Win3.1 the app gets current event word (NOT the EvtMask!!)
  468. ul = (ULONG)pDEB16->EvtWord;
  469. // clear event word like Win3.1 does
  470. dwEvtMask = (DWORD)WORD32(parg16->f2);
  471. pDEB16->EvtWord = LOWORD((~dwEvtMask) & (DWORD)ul);
  472. }
  473. }
  474. }
  475. FREEARGPTR(parg16);
  476. RETURN(ul);
  477. }
  478. // Win3.1 returns:
  479. // 0 for success.
  480. // -1 for bad idComDev.
  481. // IE_NOPEN for not opened.
  482. ULONG FASTCALL WU32GetCommState(PVDMFRAME pFrame)
  483. {
  484. ULONG ul = (ULONG)-1;
  485. UINT idComDev;
  486. DCB dcb32;
  487. PWOWPORT pWOWPort;
  488. PDCB16 pdcb16;
  489. register PGETCOMMSTATE16 parg16;
  490. GETARGPTR(pFrame, sizeof(GETCOMMSTATE16), parg16);
  491. idComDev = UINT32(parg16->f1);
  492. if (pWOWPort = GETPWOWPTR(idComDev)) {
  493. GETMISCPTR(parg16->f2, pdcb16);
  494. if(pdcb16) {
  495. if(VALIDCOM(idComDev)) {
  496. if(GetCommState(pWOWPort->h32, &dcb32)) {
  497. DCB32toDCB16(pdcb16, &dcb32, idComDev, pWOWPort->fChEvt);
  498. ul = 0; // Win3.1 returns 0 if success
  499. }
  500. }
  501. // else get DCB for LPT's
  502. else {
  503. RtlCopyMemory((PVOID)pdcb16,
  504. (PVOID)pWOWPort->pdcb16,
  505. sizeof(DCB16));
  506. ul = 0; // Win3.1 returns 0 if success
  507. }
  508. FLUSHVDMPTR(parg16->f2, sizeof(DCB16), pdcb16);
  509. FREEMISCPTR(pdcb16);
  510. }
  511. }
  512. // else if they got a handle that looks good but they didn't open the port
  513. else if(VALIDCOM(idComDev) || VALIDLPT(idComDev)) {
  514. ul = (ULONG)IE_NOPEN;
  515. }
  516. #ifdef DEBUG
  517. if(!(ul==0)) {
  518. LOGDEBUG(0,("WOW::WU32GetCommState: failed\n"));
  519. }
  520. #endif
  521. FREEARGPTR(parg16);
  522. RETURN(ul);
  523. }
  524. // Win3.1 returns:
  525. // An idComDev on success.
  526. // IE_BADID for bad port name.
  527. // IE_OPEN if port already open.
  528. // IE_HARDWARE if hardware in use (ie. by mouse) OR port doesn't exist.
  529. // IE_MEMORY if both cbInQueue & cbOutQueue == 0 OR can't allocate a queue.
  530. // IE_NOPEN if can't open port.
  531. // IE_DEFAULT if initialization fails for various reasons.
  532. // We pass an additional (4th) parameter from IOpenComm() for SetCommEventMask()
  533. // support. It's a DWORD that is obtained by a call to GlobalDosAlloc().
  534. ULONG FASTCALL WU32OpenComm(PVDMFRAME pFrame)
  535. {
  536. INT ret;
  537. UINT iTab, idComDev;
  538. CHAR COMbuf[] = "COMx:9600,E,7,1"; // Win3.1 default
  539. CHAR szPort[MAXCOMNAMENULL];
  540. DWORD dwDEBAddr;
  541. DWORD cbInQ = 0;
  542. DWORD cbOutQ;
  543. HANDLE h32 = 0;
  544. HANDLE hREvent = 0;
  545. DCB dcb32;
  546. PSZ psz1;
  547. PDCB16 pdcb16 = NULL;
  548. PWOWPORT pWOWPort;
  549. PCOMDEB16 lpComDEB16;
  550. COMMTIMEOUTS ct;
  551. PUCHAR pchWriteBuf = NULL;
  552. UINT cbWriteBuf = 0;
  553. HANDLE hWriteEvent = 0;
  554. DWORD dwWriteThreadId;
  555. BOOL fIsLPTPort;
  556. register POPENCOMM16 parg16;
  557. GETARGPTR(pFrame, sizeof(OPENCOMM16), parg16);
  558. GETPSZPTR(parg16->f1, psz1);
  559. // see if valid com device name...
  560. if((iTab = GetModePortTabIndex(psz1)) == (UINT)IE_BADID) {
  561. ret = IE_BADID;
  562. goto ErrorExit0;
  563. }
  564. // check if named port is already in use
  565. if(PortTab[iTab].pWOWPort != NULL) {
  566. ret = IE_OPEN;
  567. goto ErrorExit0;
  568. }
  569. if ( VALIDCOM(iTab) ) {
  570. idComDev = iTab;
  571. fIsLPTPort = FALSE;
  572. } else {
  573. idComDev = TABIDTOLPT(iTab);
  574. fIsLPTPort = TRUE;
  575. }
  576. // get port name: app may pass in a full mode string in Win3.1
  577. GetPortName(psz1, szPort);
  578. // try to open the port
  579. if((h32 = CreateFile(szPort,
  580. GENERIC_READ | GENERIC_WRITE,
  581. 0,
  582. NULL,
  583. OPEN_EXISTING,
  584. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
  585. NULL)) == INVALID_HANDLE_VALUE) {
  586. if(GetLastError() == ERROR_FILE_NOT_FOUND) {
  587. ret = IE_HARDWARE;
  588. }
  589. else {
  590. LOGDEBUG (LOG_ERROR,("WOW::WU32OpenComm CreateFile failed, lasterror=0x%x\n",GetLastError()));
  591. ret = IE_NOPEN;
  592. }
  593. goto ErrorExit0;
  594. }
  595. // ignore LPT's for this check like Win3.1 does
  596. if( !fIsLPTPort ) {
  597. // common method apps use to see if a COM port is already open
  598. if((WORD32(parg16->f2) == 0) &&
  599. (WORD32(parg16->f3) == 0)) {
  600. ret = IE_MEMORY;
  601. goto ErrorExit1;
  602. }
  603. // set up the I/O queues
  604. cbInQ = (DWORD)WORD32(parg16->f2);
  605. cbOutQ = (DWORD)WORD32(parg16->f3);
  606. //
  607. // Allocate write buffer to emulate Win3.1's transmit queue.
  608. // We allocate one extra byte because the last byte of the
  609. // buffer is never filled. If it were, then the head and
  610. // tail pointers would be equal, which we use to indicate
  611. // an *empty* buffer.
  612. //
  613. cbWriteBuf = cbOutQ + 1;
  614. if (!(pchWriteBuf = malloc_w(cbWriteBuf))) {
  615. ret = IE_MEMORY;
  616. goto ErrorExit1;
  617. }
  618. //
  619. // IO buffers must be a multiple of 2 for SetupComm().
  620. // Note that SetupComm may ignore the write buffer size
  621. // entirely, but TonyE says that we should still pass
  622. // down the size requested, since in any case writes
  623. // will complete only when the bits are irretrievably
  624. // sent, I.E. in the UART or other hardware, out of
  625. // the control of the device driver.
  626. //
  627. cbInQ = (cbInQ + 1) & ~1;
  628. cbOutQ = (cbOutQ + 1) & ~1;
  629. if(!SetupComm(h32, cbInQ, cbOutQ)) {
  630. ret = IE_MEMORY;
  631. goto ErrorExit2;
  632. }
  633. //
  634. // Create an event used by the app thread to wake up
  635. // the writer thread when the write buffer is
  636. // empty and the app writes something. The event
  637. // is auto-reset, meaning it is reset when the
  638. // writer wakes up. The event is initially not
  639. // signaled, it will be signaled when the first
  640. // write occurs.
  641. //
  642. if (!(hWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL))) {
  643. ret = IE_MEMORY;
  644. goto ErrorExit2;
  645. }
  646. //
  647. // create an event for ReadComm()'s overlapped structure
  648. //
  649. if(!(hREvent = CreateEvent(NULL, TRUE, FALSE, NULL))) {
  650. ret = IE_NOPEN;
  651. goto ErrorExit3;
  652. }
  653. // set the timeout values
  654. ct.ReadIntervalTimeout = (DWORD)INFINITE; // == MAXDWORD
  655. ct.ReadTotalTimeoutMultiplier=0;
  656. ct.ReadTotalTimeoutConstant=0;
  657. ct.WriteTotalTimeoutMultiplier=0;
  658. ct.WriteTotalTimeoutConstant=WRITE_TIMEOUT;
  659. if(!SetCommTimeouts(h32, &ct)) {
  660. ret = IE_DEFAULT;
  661. goto ErrorExit3;
  662. }
  663. // make sure the DCB is Win3.1 compatible
  664. // NOTE: app can pass in a full mode string in Win3.1
  665. if((strlen(psz1) < 4) || !InitDCB32(&dcb32, psz1)) {
  666. if(!InitDCB32(&dcb32, COMbuf)) {
  667. ret = IE_DEFAULT;
  668. goto ErrorExit3;
  669. }
  670. }
  671. // set current DCB to Win3.1 compatibility
  672. if(!SetCommState(h32, &dcb32)) {
  673. ret = IE_DEFAULT;
  674. goto ErrorExit3;
  675. }
  676. // purge the I/O buffers just to be sure
  677. PurgeComm(h32, PURGE_TXCLEAR);
  678. PurgeComm(h32, PURGE_RXCLEAR);
  679. }
  680. // we need to set up a default DCB for LPT's
  681. else {
  682. if((pdcb16 = malloc_w(sizeof(DCB16))) == NULL) {
  683. ret = IE_DEFAULT;
  684. goto ErrorExit1;
  685. }
  686. // initialize everything to 0
  687. RtlZeroMemory((PVOID)pdcb16, sizeof(DCB16));
  688. // save the idComDev only in the DCB
  689. pdcb16->Id = LOBYTE(LOWORD(idComDev));
  690. }
  691. // allocate the WOWPort structure for this port
  692. if((pWOWPort = malloc_w(sizeof(WOWPORT))) == NULL) {
  693. ret = IE_DEFAULT;
  694. goto ErrorExit3;
  695. }
  696. // get seg:sel dword returned by GlobalDosAlloc for the DEB struct
  697. // we'll treat the 16:16 pDEB as real mode on 32-bit side due to
  698. // some MIPS issues: v-simonf
  699. if (!(dwDEBAddr = DWORD32(parg16->f4))) {
  700. ret = IE_MEMORY;
  701. goto ErrorExit4;
  702. }
  703. // Isolate the segment value
  704. dwDEBAddr &= 0xFFFF0000;
  705. // save flat pointer to DEB for use in Modem interrupt thread
  706. lpComDEB16 = (PCOMDEB16) GetRModeVDMPointer(dwDEBAddr);
  707. // init the DEB
  708. InitDEB16(lpComDEB16, iTab, WORD32(parg16->f2), WORD32(parg16->f3));
  709. // init the support struct
  710. RtlZeroMemory((PVOID)pWOWPort, sizeof(WOWPORT));
  711. pWOWPort->h32 = h32;
  712. pWOWPort->idComDev = idComDev;
  713. pWOWPort->dwComDEB16 = DWORD32(parg16->f4);
  714. pWOWPort->lpComDEB16 = lpComDEB16;
  715. pWOWPort->dwThreadID = CURRENTPTD()->dwThreadID;
  716. pWOWPort->hREvent = hREvent;
  717. pWOWPort->cbWriteBuf = (WORD)cbWriteBuf;
  718. pWOWPort->cbWriteFree = cbWriteBuf - 1; // never use byte before head.
  719. pWOWPort->pchWriteBuf = pchWriteBuf;
  720. pWOWPort->pchWriteHead = pchWriteBuf;
  721. pWOWPort->pchWriteTail = pchWriteBuf;
  722. pWOWPort->hWriteEvent = hWriteEvent;
  723. pWOWPort->cbWritePending = 0;
  724. InitializeCriticalSection(&pWOWPort->csWrite);
  725. pWOWPort->pdcb16 = pdcb16;
  726. pWOWPort->cbInQ = cbInQ;
  727. // hack for QuickLink Gold 1.3 -- See bug #398011
  728. // save QL stack sel in hiword, ComDEB16 seg in the loword
  729. if(IsQLinkGold(pFrame->wTDB)) {
  730. pWOWPort->QLStackSeg = (DWORD32(parg16->f1) & 0xFFFF0000) |
  731. (pWOWPort->dwComDEB16 & 0x0000FFFF);
  732. }
  733. // else pWOWPort->QLStackSeg implicitly set to 0 by RtlZeroMemory above.
  734. if (!(pWOWPort->olWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) {
  735. LOGDEBUG(0, ("%s", "WU32OpenComm unable to create overlapped write event, failing.\n"));
  736. ret = IE_MEMORY;
  737. goto ErrorExit4;
  738. }
  739. PortTab[iTab].pWOWPort = pWOWPort;
  740. //
  741. // Create the writer thread and pass it pWOWPort as its
  742. // parameter.
  743. //
  744. if (!fIsLPTPort) {
  745. pWOWPort->hWriteThread = CreateThread(
  746. NULL, // lpsa
  747. 0, // stack size (default)
  748. WOWCommWriterThread, // start address
  749. pWOWPort, // lpvThreadParm
  750. 0, // fdwCreate
  751. &dwWriteThreadId
  752. );
  753. if (!pWOWPort->hWriteThread) {
  754. ret = IE_MEMORY;
  755. goto ErrorExit5;
  756. }
  757. }
  758. ret = idComDev; // return the idComDev
  759. goto CleanExit;
  760. // this is the error code path
  761. ErrorExit5:
  762. CloseHandle(pWOWPort->olWrite.hEvent);
  763. ErrorExit4:
  764. free_w(pWOWPort);
  765. ErrorExit3:
  766. if (hREvent) { CloseHandle(hREvent); }
  767. if (hWriteEvent) { CloseHandle(hWriteEvent); }
  768. if (fIsLPTPort) { free_w(pdcb16); }
  769. ErrorExit2:
  770. if(pchWriteBuf) { free_w(pchWriteBuf); }
  771. ErrorExit1:
  772. CloseHandle(h32);
  773. ErrorExit0:
  774. LOGDEBUG (0, ("WOW::WU32OpenComm failed\n"));
  775. CleanExit:
  776. FREEVDMPTR(psz1);
  777. FREEARGPTR(parg16);
  778. RETURN((ULONG)ret); // return error
  779. }
  780. //
  781. // WriteComm()
  782. //
  783. // Win3.1 returns:
  784. // # bytes written on success (*= -1 on error).
  785. // 0 for bad idComDev OR if app specifies to write 0 bytes.
  786. // -1 if port hasn't been opened,
  787. //
  788. ULONG FASTCALL WU32WriteComm(PVDMFRAME pFrame)
  789. {
  790. register PWRITECOMM16 parg16;
  791. LONG i = -1;
  792. PSZ psz2;
  793. PWOWPORT pwp;
  794. UINT idComDev;
  795. PWOWPORT pWOWPort;
  796. DWORD cbWritten;
  797. GETARGPTR(pFrame, sizeof(WRITECOMM16), parg16);
  798. GETPSZPTR(parg16->f2, psz2);
  799. idComDev = UINT32(parg16->f1);
  800. // this will be true only if the (valid) port has been opened
  801. if (pWOWPort = GETPWOWPTR(idComDev)) {
  802. if(VALIDCOM(idComDev)) {
  803. if ((pwp = GETPWOWPTR(UINT32(parg16->f1))) && psz2) {
  804. // if the app is interested in timeouts...
  805. if(pwp->lpComDEB16->MSRMask) {
  806. // ...see if RLSD, CTS, & DSR timeout before going high
  807. if(MSRWait(pwp)) {
  808. FREEPSZPTR(psz2);
  809. FREEARGPTR(parg16);
  810. return(0); // this is what Win3.1 does for Timeouts
  811. }
  812. }
  813. i = EnqueueCommWrite(pwp, psz2, parg16->f3);
  814. if (i != parg16->f3) {
  815. i = -i;
  816. pwp->dwErrCode |= CE_TXFULL;
  817. }
  818. }
  819. }
  820. // else LPT's go this way...
  821. else {
  822. //
  823. // This call to WriteFile could block, but I don't think
  824. // that's a problem. - DaveHart
  825. //
  826. if ((pwp = GETPWOWPTR(UINT32(parg16->f1))) && psz2) {
  827. if (!WriteFile(pwp->h32, psz2, parg16->f3, &cbWritten, &pwp->olWrite)) {
  828. if (ERROR_IO_PENDING == GetLastError() ) {
  829. //
  830. // Wait for the write to complete or for us to
  831. // be alerted that the port is closing.
  832. //
  833. if (GetOverlappedResult(pwp->h32,
  834. &pwp->olWrite,
  835. &cbWritten,
  836. TRUE
  837. )) {
  838. i = cbWritten;
  839. goto WriteSuccess;
  840. }
  841. }
  842. LOGDEBUG(0, ("WU32WriteComm: WriteFile to id %u fails (error %u)\n",
  843. pwp->idComDev, GetLastError()));
  844. if (cbWritten) {
  845. i = cbWritten;
  846. i = -i;
  847. }
  848. }
  849. else {
  850. i = cbWritten;
  851. }
  852. }
  853. }
  854. }
  855. else if(!(VALIDCOM(idComDev) || VALIDLPT(idComDev))) {
  856. i = 0;
  857. }
  858. WriteSuccess:
  859. FREEPSZPTR(psz2);
  860. FREEARGPTR(parg16);
  861. RETURN((ULONG)i);
  862. }
  863. // Win3.1 returns:
  864. // # chars read on success.
  865. // 0 for: bad idComDev, cbRead == 0, LPTx, port not open, 0 chars read,
  866. // OR for general comm error.
  867. ULONG FASTCALL WU32ReadComm(PVDMFRAME pFrame)
  868. {
  869. ULONG ul = 0;
  870. ULONG cb;
  871. BOOL fUnGet = FALSE;
  872. UINT idComDev;
  873. PBYTE pb2;
  874. PWOWPORT pWOWPort;
  875. OVERLAPPED Rol;
  876. register PREADCOMM16 parg16;
  877. GETARGPTR(pFrame, sizeof(READCOMM16), parg16);
  878. GETMISCPTR(parg16->f2, pb2);
  879. cb = (ULONG)UINT32(parg16->f3);
  880. if((cb != 0) && pb2) {
  881. idComDev = UINT32(parg16->f1);
  882. if (VALIDCOM(idComDev) && (pWOWPort = PortTab[idComDev].pWOWPort)) {
  883. // if an UnGot char is pending
  884. if (pWOWPort->fUnGet) {
  885. fUnGet = TRUE;
  886. pWOWPort->fUnGet = FALSE;
  887. *pb2++ = pWOWPort->cUnGet;
  888. // this line commented out 8/3/95
  889. // cb--; // we now need one less char
  890. // In order to make this work correctly we should cb-- above
  891. // to reflect the ungot char, unfortunately Win3.1 & Win95
  892. // don't do that so we will maintain this bug for "ouch!"
  893. // compatibility. a-craigj 8/3/95
  894. }
  895. // TonyE claims we should do this before each read to avoid problems
  896. Rol.Internal = 0;
  897. Rol.InternalHigh = 0;
  898. Rol.Offset = 0;
  899. Rol.OffsetHigh = 0;
  900. Rol.hEvent = pWOWPort->hREvent;
  901. if (!ReadFile(pWOWPort->h32,
  902. pb2,
  903. cb,
  904. (LPDWORD)&ul,
  905. &Rol)) {
  906. if (ERROR_IO_PENDING == GetLastError()) {
  907. if (!GetOverlappedResult(pWOWPort->h32,
  908. &Rol,
  909. &ul,
  910. TRUE
  911. )) {
  912. LOGDEBUG(0, ("WOW::WU32ReadComm:GetOverlappedResult failed, error = 0x%x\n",
  913. GetLastError()));
  914. ul = 0;
  915. }
  916. } else {
  917. LOGDEBUG(0, ("WOW::WU32ReadComm:ReadFile failed, error = 0x%x\n",
  918. GetLastError()));
  919. ul = 0;
  920. }
  921. }
  922. if(fUnGet) {
  923. ul++; // account for ungot char
  924. pb2--; // accounts for previous pb2++ for FREEVDMPTR
  925. }
  926. FLUSHVDMPTR(parg16->f2, (USHORT)ul, pb2);
  927. }
  928. FREEVDMPTR(pb2);
  929. }
  930. FREEARGPTR(parg16);
  931. RETURN(ul);
  932. }
  933. // Win3.1 returns:
  934. // Error word on success OR LPTx.
  935. // 0x8000 on bad idComDev.
  936. ULONG FASTCALL WU32SetCommBreak(PVDMFRAME pFrame)
  937. {
  938. ULONG ul = 0x00008000;
  939. UINT idComDev;
  940. PWOWPORT pWOWPort;
  941. register PSETCOMMBREAK16 parg16;
  942. GETARGPTR(pFrame, sizeof(SETCOMMBREAK16), parg16);
  943. idComDev = UINT32(parg16->f1);
  944. if (pWOWPort = GETPWOWPTR(idComDev)) {
  945. if(VALIDCOM(idComDev)) {
  946. if(!SetCommBreak(pWOWPort->h32)) {
  947. WOWGetCommError(pWOWPort);
  948. }
  949. }
  950. ul = pWOWPort->dwErrCode; // Win3.1 returns last err
  951. }
  952. #ifdef DEBUG
  953. if(!(ul!=CE_MODE)) {
  954. LOGDEBUG(0,("WOW::WU32SetCommBreak: failed\n"));
  955. }
  956. #endif
  957. FREEARGPTR(parg16);
  958. RETURN(ul);
  959. }
  960. // Win3.1 returns:
  961. // A 16:16 ptr into the DEB struct on success.
  962. // 0 on any error OR LPT.
  963. // The 16:16 ptr that we return to the app was actually obtained in
  964. // IOpenComm() in user.exe.
  965. ULONG FASTCALL WU32SetCommEventMask(PVDMFRAME pFrame)
  966. {
  967. ULONG ul = 0;
  968. BOOL fOK = TRUE;
  969. UINT idComDev;
  970. DWORD dwDEBAddr;
  971. PWOWPORT pWOWPort;
  972. register PSETCOMMEVENTMASK16 parg16;
  973. GETARGPTR(pFrame, sizeof(SETCOMMEVENTMASK16), parg16);
  974. idComDev = UINT32(parg16->f1);
  975. if ((VALIDCOM(idComDev)) && (pWOWPort = PortTab[idComDev].pWOWPort)) {
  976. // if the Modem interrupt thread hasn't been started yet -- go start it
  977. if(pWOWPort->hMiThread == NULL) {
  978. // start our Modem interrupt thread
  979. if(!WOWStartModemIntThread(pWOWPort)) {
  980. fOK = FALSE;
  981. }
  982. }
  983. // if everything is hunky-dory...
  984. if(fOK) {
  985. // success: Win3.1 returns 16:16 protect mode ptr to
  986. // DEB->EvtWord (some apps subtract offset of EvtWord
  987. // from ptr to get start of DEB).
  988. dwDEBAddr = LOWORD(pWOWPort->dwComDEB16) << 16;
  989. ul = dwDEBAddr + FIELD_OFFSET(COMDEB16, EvtWord);
  990. // save the mask the app requested
  991. pWOWPort->lpComDEB16->EvtMask = (WORD)(parg16->f2);
  992. }
  993. }
  994. #ifdef DEBUG
  995. if(!(ul!=0)) {
  996. LOGDEBUG(0,("WOW::WU32SETCOMMEVENTMASK: failed\n"));
  997. }
  998. #endif
  999. FREEARGPTR(parg16);
  1000. RETURN(ul);
  1001. }
  1002. // Win3.1 returns:
  1003. // 0 on success OR LPTx.
  1004. // IE_BADID for bad idComDev.
  1005. // IE_NOPEN if file hasn't been opened.
  1006. // IE_BAUDRATE for bad baud rate.
  1007. // IE_BYTESIZE for bad byte size.
  1008. // IE_DEFAULT for bad parity or stop bits.
  1009. ULONG FASTCALL WU32SetCommState(PVDMFRAME pFrame)
  1010. {
  1011. ULONG ul = (ULONG)IE_BADID;
  1012. UINT idComDev;
  1013. PDCB16 pdcb16;
  1014. DCB dcb32;
  1015. PWOWPORT pWOWPort;
  1016. register PSETCOMMSTATE16 parg16;
  1017. DWORD dwMSR;
  1018. GETARGPTR(pFrame, sizeof(SETCOMMSTATE16), parg16);
  1019. GETMISCPTR(parg16->f1, pdcb16);
  1020. if(pdcb16) {
  1021. idComDev = pdcb16->Id;
  1022. if(pWOWPort = GETPWOWPTR(idComDev)) {
  1023. if(VALIDCOM(idComDev)) {
  1024. DCB16toDCB32(pWOWPort, &dcb32, pdcb16);
  1025. if(SetCommState(pWOWPort->h32, &dcb32)) {
  1026. ul = 0;
  1027. // Win 3.1 initializes the MSRShadow during SetCommState
  1028. // so we will too. InterNet in a Box Dialer depends on it.
  1029. GetCommModemStatus(pWOWPort->h32, &dwMSR);
  1030. dwMSR &= MSR_STATEONLY;
  1031. pWOWPort->lpComDEB16->MSRShadow = LOBYTE(LOWORD(dwMSR));
  1032. }
  1033. else {
  1034. ul = (ULONG)IE_DEFAULT; // we just say something's wrong
  1035. }
  1036. }
  1037. else {
  1038. RtlCopyMemory((PVOID)pWOWPort->pdcb16,
  1039. (PVOID)pdcb16,
  1040. sizeof(DCB16));
  1041. ul = 0;
  1042. }
  1043. }
  1044. // else if they got a handle that looks good but they didn't open port
  1045. else if (VALIDCOM(idComDev) || VALIDLPT(idComDev)) {
  1046. ul = (ULONG)IE_NOPEN;
  1047. }
  1048. FREEMISCPTR(pdcb16);
  1049. }
  1050. FREEARGPTR(parg16);
  1051. RETURN(ul);
  1052. }
  1053. // Win3.1 returns:
  1054. // 0 for success.
  1055. // 0x8000 for bad idComDev.
  1056. // 0x4000 if char can't be sent.
  1057. ULONG FASTCALL WU32TransmitCommChar(PVDMFRAME pFrame)
  1058. {
  1059. ULONG ul = 0x8000;
  1060. UINT idComDev;
  1061. CHAR ch;
  1062. PWOWPORT pWOWPort;
  1063. DWORD cbWritten;
  1064. register PTRANSMITCOMMCHAR16 parg16;
  1065. GETARGPTR(pFrame, sizeof(TRANSMITCOMMCHAR16), parg16);
  1066. idComDev = UINT32(parg16->f1);
  1067. if (pWOWPort = GETPWOWPTR(idComDev)) {
  1068. if(VALIDCOM(idComDev)) {
  1069. if(TransmitCommChar(pWOWPort->h32, CHAR32(parg16->f2))) {
  1070. ul = 0; // Win3.1 returns 0 on success
  1071. }
  1072. else {
  1073. ul = (ULONG)ERR_XMIT;
  1074. }
  1075. }
  1076. // else LPT's go this way...
  1077. else {
  1078. //
  1079. // This call to WriteFile could block, but I don't think
  1080. // that's a problem. - DaveHart
  1081. //
  1082. ch = CHAR32(parg16->f2);
  1083. ul = ERR_XMIT;
  1084. if (pWOWPort = GETPWOWPTR(UINT32(parg16->f1))) {
  1085. if (!WriteFile(pWOWPort->h32, &ch, 1, &cbWritten, &pWOWPort->olWrite)) {
  1086. if (ERROR_IO_PENDING == GetLastError() ) {
  1087. //
  1088. // Wait for the write to complete or for us to
  1089. // be alerted that the port is closing.
  1090. //
  1091. if (GetOverlappedResult(pWOWPort->h32,
  1092. &pWOWPort->olWrite,
  1093. &cbWritten,
  1094. TRUE
  1095. )) {
  1096. ul = 0;
  1097. goto TransmitSuccess;
  1098. }
  1099. }
  1100. LOGDEBUG(0, ("WU32TransmitCommChar: WriteFile to id %u fails (error %u)\n",
  1101. pWOWPort->idComDev, GetLastError()));
  1102. }
  1103. else {
  1104. ul = 0;
  1105. }
  1106. }
  1107. }
  1108. }
  1109. TransmitSuccess:
  1110. FREEARGPTR(parg16);
  1111. RETURN(ul);
  1112. }
  1113. // Win3.1 returns:
  1114. // 0 on success OR bad idComDev OR LPTx.
  1115. // -1 if port not open OR if ungot char already pending.
  1116. ULONG FASTCALL WU32UngetCommChar(PVDMFRAME pFrame)
  1117. {
  1118. ULONG ul = (ULONG)-1;
  1119. UINT idComDev;
  1120. PWOWPORT pWOWPort;
  1121. register PUNGETCOMMCHAR16 parg16;
  1122. GETARGPTR(pFrame, sizeof(UNGETCOMMCHAR16), parg16);
  1123. // see if port open...
  1124. idComDev = UINT32(parg16->f1);
  1125. if (VALIDCOM(idComDev)) {
  1126. if (pWOWPort = PortTab[idComDev].pWOWPort) {
  1127. // if ungot char already pending return -1
  1128. if(pWOWPort->fUnGet == FALSE) {
  1129. pWOWPort->fUnGet = TRUE;
  1130. pWOWPort->cUnGet = CHAR32(parg16->f2);
  1131. ul = 0;
  1132. }
  1133. }
  1134. }
  1135. else {
  1136. ul = 0;
  1137. }
  1138. #ifdef DEBUG
  1139. if(!(ul==0)) {
  1140. LOGDEBUG(0,("WOW::WU32UngetCommChar: failed\n"));
  1141. }
  1142. #endif
  1143. FREEARGPTR(parg16);
  1144. RETURN(ul);
  1145. }
  1146. DWORD Baud16toBaud32(UINT BaudRate)
  1147. {
  1148. UINT DLatch;
  1149. // this function is set up this way on purpose (see SetCom300 ibmsetup.asm)
  1150. // get the equivalent baud
  1151. switch(BaudRate) {
  1152. // it they specified the baud rate directly
  1153. case CBR_110:
  1154. case CBR_300:
  1155. case CBR_600:
  1156. case CBR_1200:
  1157. case CBR_2400:
  1158. case CBR_4800:
  1159. case CBR_9600:
  1160. case CBR_19200:
  1161. case CBR_14400:
  1162. case CBR_38400:
  1163. case CBR_56000: return(BaudRate);
  1164. // Win3.1 baud rate constants
  1165. case W31CBR_110: return(CBR_110);
  1166. case W31CBR_300: return(CBR_300);
  1167. case W31CBR_600: return(CBR_600);
  1168. case W31CBR_1200: return(CBR_1200);
  1169. case W31CBR_2400: return(CBR_2400);
  1170. case W31CBR_4800: return(CBR_4800);
  1171. case W31CBR_9600: return(CBR_9600);
  1172. case W31CBR_19200: return(CBR_19200);
  1173. case W31CBR_14400: return(CBR_14400);
  1174. case W31CBR_38400: return(CBR_38400);
  1175. case W31CBR_56000: return(CBR_56000);
  1176. // start special cases
  1177. // SmartCom uses this to get 115200
  1178. case W31CBR_115200: return(CBR_115200);
  1179. // Win3.1 fails these two (even though they're defined in windows.h)
  1180. // but they just might work on NT
  1181. case W31CBR_128000: return(CBR_128000);
  1182. case W31CBR_256000: return(CBR_256000);
  1183. // end special cases
  1184. // handle the blank table entries for "reserved"
  1185. case W31CBR_reserved1:
  1186. case W31CBR_reserved2:
  1187. case W31CBR_reserved3:
  1188. case W31CBR_reserved4:
  1189. case W31CBR_reserved5: return(0);
  1190. // avoid divide by zero
  1191. case 0:
  1192. case 1: return(0);
  1193. // handle obscure specifications that will work in Win3.1
  1194. default:
  1195. // get the integer divisor latch value
  1196. DLatch = CBR_115200 / BaudRate;
  1197. switch(DLatch) {
  1198. case W31_DLATCH_110: return(CBR_110);
  1199. case W31_DLATCH_300: return(CBR_300);
  1200. case W31_DLATCH_600: return(CBR_600);
  1201. case W31_DLATCH_1200: return(CBR_1200);
  1202. case W31_DLATCH_2400: return(CBR_2400);
  1203. case W31_DLATCH_4800: return(CBR_4800);
  1204. case W31_DLATCH_9600: return(CBR_9600);
  1205. case W31_DLATCH_19200: return(CBR_19200);
  1206. case W31_DLATCH_14400: return(CBR_14400);
  1207. case W31_DLATCH_38400: return(CBR_38400);
  1208. case W31_DLATCH_56000: return(CBR_56000);
  1209. case W31_DLATCH_115200: return(CBR_115200);
  1210. // Win3.1, anything else returns whatever DLatch happens to be
  1211. // since we're mapping to baud we return the specified baud
  1212. default: return(BaudRate);
  1213. }
  1214. }
  1215. }
  1216. WORD Baud32toBaud16(DWORD BaudRate)
  1217. {
  1218. if(BaudRate >= CBR_115200) {
  1219. switch(BaudRate) {
  1220. case CBR_256000: return(W31CBR_256000);
  1221. case CBR_128000: return(W31CBR_128000);
  1222. case CBR_115200:
  1223. default: return(W31CBR_115200);
  1224. }
  1225. }
  1226. else {
  1227. return(LOWORD(BaudRate));
  1228. }
  1229. }
  1230. void DCB16toDCB32(PWOWPORT pWOWPort, LPDCB lpdcb32, PDCB16 pdcb16)
  1231. {
  1232. // zero 32-bit struct -> any flags and fields not explicitly set will be 0
  1233. RtlZeroMemory((PVOID)lpdcb32, sizeof(DCB));
  1234. lpdcb32->DCBlength = sizeof(DCB);
  1235. lpdcb32->BaudRate = Baud16toBaud32(pdcb16->BaudRate);
  1236. // 16-bit bitfields may align differently with 32-bit compilers
  1237. // we use this mechanism to align them the way Win3.1 expects them
  1238. if(pdcb16->wFlags & W31DCB_fBinary) lpdcb32->fBinary = 1;
  1239. if(pdcb16->wFlags & W31DCB_fParity) lpdcb32->fParity = 1;
  1240. if(pdcb16->wFlags & W31DCB_fOutxCtsFlow) lpdcb32->fOutxCtsFlow = 1;
  1241. if(pdcb16->wFlags & W31DCB_fOutxDsrFlow) lpdcb32->fOutxDsrFlow = 1;
  1242. // set up mechanism for handling event char notification
  1243. if(pdcb16->wFlags & W31DCB_fChEvt) pWOWPort->fChEvt = TRUE;
  1244. if(pdcb16->wFlags & W31DCB_fDtrFlow) {
  1245. lpdcb32->fDtrControl = DTR_CONTROL_HANDSHAKE;
  1246. }
  1247. else if(pdcb16->wFlags & W31DCB_fDtrDisable) {
  1248. lpdcb32->fDtrControl = DTR_CONTROL_DISABLE;
  1249. }
  1250. else {
  1251. lpdcb32->fDtrControl = DTR_CONTROL_ENABLE;
  1252. }
  1253. if(pdcb16->wFlags & W31DCB_fOutX) lpdcb32->fOutX = 1;
  1254. if(pdcb16->wFlags & W31DCB_fInX) lpdcb32->fInX = 1;
  1255. if(pdcb16->wFlags & W31DCB_fPeChar) lpdcb32->fErrorChar = 1;
  1256. if(pdcb16->wFlags & W31DCB_fNull) lpdcb32->fNull = 1;
  1257. if(pdcb16->wFlags & W31DCB_fRtsFlow) {
  1258. lpdcb32->fRtsControl = RTS_CONTROL_HANDSHAKE;
  1259. }
  1260. else if(pdcb16->wFlags & W31DCB_fRtsDisable) {
  1261. lpdcb32->fRtsControl = RTS_CONTROL_DISABLE;
  1262. }
  1263. else {
  1264. lpdcb32->fRtsControl = RTS_CONTROL_ENABLE;
  1265. }
  1266. if(pdcb16->wFlags & W31DCB_fDummy2) lpdcb32->fDummy2 = 1;
  1267. // Check the passed in XonLim & XoffLim values against the cbInQ value.
  1268. // Prodigy's modem detector leaves these values uninitialized.
  1269. if ((pdcb16->XonLim >= pWOWPort->cbInQ) ||
  1270. (pdcb16->XoffLim > pWOWPort->cbInQ) ||
  1271. (pdcb16->XonLim >= pdcb16->XoffLim)) {
  1272. lpdcb32->XonLim = 0;
  1273. lpdcb32->XoffLim = (WORD)(pWOWPort->cbInQ - (pWOWPort->cbInQ >> 2));
  1274. }
  1275. else {
  1276. lpdcb32->XonLim = pdcb16->XonLim;
  1277. lpdcb32->XoffLim = pdcb16->XoffLim;
  1278. }
  1279. lpdcb32->ByteSize = pdcb16->ByteSize;
  1280. lpdcb32->Parity = pdcb16->Parity;
  1281. lpdcb32->StopBits = pdcb16->StopBits;
  1282. // Digiboard driver doesn't want to see XonChar == XoffChar even if
  1283. // xon/xoff is disabled.
  1284. if ((pdcb16->XonChar == '\0') && (lpdcb32->XoffChar == '\0')) {
  1285. lpdcb32->XonChar = pdcb16->XonChar+1;
  1286. }
  1287. else {
  1288. lpdcb32->XonChar = pdcb16->XonChar;
  1289. }
  1290. lpdcb32->XoffChar = pdcb16->XoffChar;
  1291. lpdcb32->ErrorChar = pdcb16->PeChar;
  1292. lpdcb32->EofChar = pdcb16->EofChar;
  1293. lpdcb32->EvtChar = pdcb16->EvtChar;
  1294. #ifdef FE_SB
  1295. // for MSKKBUG #3213 by v-kenich
  1296. // MYTALK for Win set NULL these two fields at transfering binary file
  1297. // If call SetCommstate as it is, SetCommState return error (Invalid parameter)
  1298. // I think this fix doesn't occur any bad thing without condition of MYTALK
  1299. // Really correcting parameter check is better. but I don't know where it is.
  1300. if (!lpdcb32->XonChar) lpdcb32->XonChar = 0x11;
  1301. if (!lpdcb32->XoffChar) lpdcb32->XoffChar = 0x13;
  1302. #endif // FE_SB
  1303. // set up for RLSD, CTS, and DSR timeout support (not supported on NT)
  1304. pWOWPort->lpComDEB16->MSRMask = 0;
  1305. pWOWPort->RLSDTimeout = pdcb16->RlsTimeout;
  1306. if(pWOWPort->RLSDTimeout != IGNORE_TIMEOUT)
  1307. pWOWPort->lpComDEB16->MSRMask |= LOBYTE(MS_RLSD_ON);
  1308. pWOWPort->CTSTimeout = pdcb16->CtsTimeout;
  1309. if(pWOWPort->CTSTimeout != IGNORE_TIMEOUT)
  1310. pWOWPort->lpComDEB16->MSRMask |= LOBYTE(MS_CTS_ON);
  1311. pWOWPort->DSRTimeout = pdcb16->DsrTimeout;
  1312. if(pWOWPort->DSRTimeout != IGNORE_TIMEOUT)
  1313. pWOWPort->lpComDEB16->MSRMask |= LOBYTE(MS_DSR_ON);
  1314. // these fields remain 0
  1315. //lpdcb32->fDsrSensitivity = 0;
  1316. //lpdcb32->fTXContinueOnXoff = 0;
  1317. //lpdcb32->fAbortOnError = 0;
  1318. //lpdcb32->wReserved = 0;
  1319. }
  1320. void DCB32toDCB16(PDCB16 pdcb16, LPDCB lpdcb32, UINT idComDev, BOOL fChEvt)
  1321. {
  1322. // zero 16-bit struct -> any flags and fields not explicitly set will be 0
  1323. RtlZeroMemory((PVOID)pdcb16, sizeof(DCB16));
  1324. // set this field no matter what
  1325. pdcb16->Id = (BYTE)idComDev;
  1326. // if a COMx (Win3.1 leaves the rest 0 for LPT's)
  1327. if(VALIDCOM(idComDev)) {
  1328. pdcb16->Id = (BYTE)idComDev;
  1329. // these are the "ComX:96,n,8,1" fields
  1330. pdcb16->BaudRate = Baud32toBaud16(lpdcb32->BaudRate);
  1331. pdcb16->ByteSize = lpdcb32->ByteSize;
  1332. pdcb16->Parity = lpdcb32->Parity;
  1333. pdcb16->StopBits = lpdcb32->StopBits;
  1334. // 16-bit bitfields may align differently with 32-bit compilers
  1335. // we use this mechanism to align them the way Win3.1 expects them
  1336. if(lpdcb32->fBinary) pdcb16->wFlags |= W31DCB_fBinary;
  1337. if(lpdcb32->fRtsControl == RTS_CONTROL_DISABLE) {
  1338. pdcb16->wFlags |= W31DCB_fRtsDisable;
  1339. }
  1340. if(lpdcb32->fParity) pdcb16->wFlags |= W31DCB_fParity;
  1341. if(lpdcb32->fOutxCtsFlow) pdcb16->wFlags |= W31DCB_fOutxCtsFlow;
  1342. if(lpdcb32->fOutxDsrFlow) pdcb16->wFlags |= W31DCB_fOutxDsrFlow;
  1343. if(lpdcb32->fDtrControl == DTR_CONTROL_DISABLE) {
  1344. pdcb16->wFlags |= W31DCB_fDtrDisable;
  1345. }
  1346. if(lpdcb32->fOutX) pdcb16->wFlags |= W31DCB_fOutX;
  1347. if(lpdcb32->fInX) pdcb16->wFlags |= W31DCB_fInX;
  1348. if(lpdcb32->fErrorChar) pdcb16->wFlags |= W31DCB_fPeChar;
  1349. if(lpdcb32->fNull) pdcb16->wFlags |= W31DCB_fNull;
  1350. if(fChEvt) pdcb16->wFlags |= W31DCB_fChEvt;
  1351. if(lpdcb32->fDtrControl == DTR_CONTROL_HANDSHAKE) {
  1352. pdcb16->wFlags |= W31DCB_fDtrFlow;
  1353. }
  1354. if(lpdcb32->fRtsControl == RTS_CONTROL_HANDSHAKE) {
  1355. pdcb16->wFlags |= W31DCB_fRtsFlow;
  1356. }
  1357. if(lpdcb32->fDummy2) pdcb16->wFlags |= W31DCB_fDummy2;
  1358. pdcb16->XonChar = lpdcb32->XonChar;
  1359. pdcb16->XoffChar = lpdcb32->XoffChar;
  1360. pdcb16->XonLim = lpdcb32->XonLim;
  1361. pdcb16->XoffLim = lpdcb32->XoffLim;
  1362. pdcb16->PeChar = lpdcb32->ErrorChar;
  1363. pdcb16->EofChar = lpdcb32->EofChar;
  1364. pdcb16->EvtChar = lpdcb32->EvtChar;
  1365. }
  1366. // these fields remain 0
  1367. //pdcb16->fDummy = 0;
  1368. //pdcb16->TxDelay = 0;
  1369. }
  1370. BOOL DeletePortTabEntry(PWOWPORT pWOWPort)
  1371. {
  1372. INT iTab;
  1373. BOOL fTimeOut;
  1374. iTab = pWOWPort->idComDev;
  1375. if(VALIDLPT(iTab)) {
  1376. iTab = GETLPTID(iTab);
  1377. }
  1378. // flush I/O buffers & attempt to wake up Modem Interrupt thread (if any)
  1379. pWOWPort->fClose = TRUE;
  1380. if(VALIDCOM(iTab)) {
  1381. PurgeComm(pWOWPort->h32, PURGE_TXCLEAR);
  1382. PurgeComm(pWOWPort->h32, PURGE_RXCLEAR);
  1383. SetCommMask(pWOWPort->h32, 0); // this should wake up the Mi thread
  1384. // wake up WOWModemIntThread & tell it to exit
  1385. // (we attempt to block (1.5 second max.) until it does)
  1386. if(pWOWPort->hMiThread) {
  1387. WaitForSingleObject(pWOWPort->hMiThread, 1500);
  1388. CloseHandle(pWOWPort->hMiThread);
  1389. // zero COMDEB
  1390. RtlZeroMemory((PVOID)pWOWPort->lpComDEB16, sizeof(COMDEB16));
  1391. }
  1392. //
  1393. // Wake up WOWCommWriterThread so it will exit, wait up to
  1394. // 5 sec for it to go away.
  1395. //
  1396. SetEvent(pWOWPort->hWriteEvent);
  1397. fTimeOut = (WaitForSingleObject(pWOWPort->hWriteThread, 5000) ==
  1398. WAIT_TIMEOUT);
  1399. #ifdef DEBUG
  1400. if (fTimeOut) {
  1401. LOGDEBUG(LOG_ALWAYS,
  1402. ("WOW DeletePortTabEntry: Comm writer thread for port %d refused\n"
  1403. " to die when asked nicely.\n", (int)pWOWPort->idComDev));
  1404. }
  1405. #endif
  1406. CloseHandle(pWOWPort->hWriteThread);
  1407. CloseHandle(pWOWPort->hWriteEvent);
  1408. free_w(pWOWPort->pchWriteBuf);
  1409. CloseHandle(pWOWPort->hREvent);
  1410. }
  1411. // else free the LPT DCB support struct
  1412. else {
  1413. free_w(pWOWPort->pdcb16);
  1414. CloseHandle(pWOWPort->olWrite.hEvent);
  1415. fTimeOut = FALSE;
  1416. }
  1417. DeleteCriticalSection(&pWOWPort->csWrite);
  1418. CloseHandle(pWOWPort->h32);
  1419. // QuickLink Gold 1.3 hack. Bug #398011
  1420. // The app calls OpenComm(), & then SetCommEventMask() to get the ptr to the
  1421. // comdeb16 struct. It saves the ptr at offset 0xf36 on its stack. The
  1422. // problem is that the app holds onto the comdeb16 ptr after it calls
  1423. // CloseComm() (when we free the comdeb16 memory) to be able to peek at a
  1424. // status byte from time to time. This works OK on Win 3.1 but not with
  1425. // our model on NT. Fortunately, the app tests to see if it has a comdeb16
  1426. // ptr before dereferencing it. Also, we're lucky because the ptr for
  1427. // lpszDevControl in its call to OpenComm() is from its stack thus allowing
  1428. // us to obtain the stack selector and zero out the comdeb16 ptr stored at
  1429. // stack ss:0xf36 when the app calls CloseComm().
  1430. if(pWOWPort->QLStackSeg) {
  1431. LPDWORD lpQLS;
  1432. VPVOID vpQLS, vpCD16;
  1433. // construct the 16:16 ptr to where the app saved the ptr to the
  1434. // COMDEB16 struct on its stack at offset 0xf36
  1435. vpQLS = pWOWPort->QLStackSeg & 0xFFFF0000;
  1436. vpQLS = vpQLS | 0x00000f36;
  1437. GETMISCPTR(vpQLS, lpQLS);
  1438. // construct realmode 16:16 ptr of the COMDEB16 struct + 0x38 (seg:0x38)
  1439. vpCD16 = pWOWPort->QLStackSeg & 0x0000FFFF;
  1440. vpCD16 = (vpCD16 << 16) | 0x00000038;
  1441. if(lpQLS) {
  1442. // sanity check to see if everything is still what & where we
  1443. // think it is
  1444. // if seg:0x38 is still stored at offset 0xf36 on the apps stack...
  1445. if(*lpQLS == (DWORD)vpCD16) {
  1446. // zero it out -- forcing app to avoid checking the status byte
  1447. *lpQLS = 0;
  1448. FLUSHVDMPTR(vpQLS, sizeof(DWORD), lpQLS);
  1449. FREEMISCPTR(lpQLS);
  1450. }
  1451. }
  1452. }
  1453. free_w(pWOWPort);
  1454. PortTab[iTab].pWOWPort = NULL;
  1455. return(fTimeOut);
  1456. }
  1457. UINT GetModePortTabIndex(PSZ pszModeStr)
  1458. {
  1459. CHAR szPort[MAXCOMNAMENULL*2];
  1460. if(pszModeStr) {
  1461. if(GetPortName(pszModeStr, szPort)) {
  1462. return(GetStrPortTabIndex(szPort));
  1463. }
  1464. }
  1465. return((UINT)IE_BADID);
  1466. }
  1467. BOOL GetPortName(LPSTR pszMode, LPSTR pszPort)
  1468. {
  1469. INT len;
  1470. CHAR szTemp[80]; // max len we'll take for DOS style MODE command
  1471. BOOL bRet = FALSE;
  1472. len = strlen(pszMode);
  1473. if((len >= 3) && (len < 80)) {
  1474. // Get the first token from the mode string.
  1475. GetPortStringToken(pszMode, szTemp);
  1476. // map "AUX" or "PRN" to "COM1" or "LPT1" if necessary
  1477. len = strlen(szTemp);
  1478. if((len >= 3) && (len <= MAXCOMNAME)) { // "AUX" <= len <= "COMx"
  1479. strcpy(pszPort, szTemp);
  1480. CharUpper(pszPort);
  1481. // filter out duplicate names for the same thing
  1482. if(!WOW32_strcmp(pszPort, "PRN")) {
  1483. strcpy(pszPort, "LPT1");
  1484. }
  1485. else if(!WOW32_strcmp(pszPort, "AUX")) {
  1486. strcpy(pszPort, "COM1");
  1487. }
  1488. bRet = TRUE;
  1489. }
  1490. }
  1491. return(bRet);
  1492. }
  1493. PSZ StripPortName(PSZ psz)
  1494. {
  1495. CHAR dummy[80]; // max len we'll take for DOS style MODE command
  1496. return(GetPortStringToken(psz, dummy));
  1497. }
  1498. //
  1499. // Copy first token to pszToken. Return pointer to next token or NULL if none.
  1500. // This code cloned from Win 3.1, COMDEV.C, field(). HGW 3.0 modem registration
  1501. // passes "COMx,,," instead of "COMx:,,," so we need to handle all seperators.
  1502. //
  1503. PSZ GetPortStringToken(PSZ pszSrc, PSZ pszToken)
  1504. {
  1505. char c;
  1506. // While not the end of the string.
  1507. while (c = *pszSrc) {
  1508. pszSrc++;
  1509. //Look for seperators.
  1510. if ((c == ' ') || (c == ':') || (c == ',')) {
  1511. *pszToken = '\0';
  1512. while (*pszSrc == ' ') {
  1513. pszSrc++;
  1514. }
  1515. if (*pszSrc) {
  1516. return(pszSrc);
  1517. }
  1518. return(NULL);
  1519. }
  1520. *pszToken++ = c;
  1521. }
  1522. *pszToken = '\0';
  1523. return(NULL);
  1524. }
  1525. UINT GetStrPortTabIndex(PSZ szPort)
  1526. {
  1527. UINT iTab;
  1528. for(iTab = COM1; iTab < NUMPORTS; iTab++) {
  1529. if(!WOW32_strcmp((LPCTSTR)PortTab[iTab].szPort, (LPCTSTR)szPort)) {
  1530. return(iTab);
  1531. }
  1532. }
  1533. return((UINT)IE_BADID);
  1534. }
  1535. BOOL InitDCB32(LPDCB pdcb32, LPSTR pszModeStr)
  1536. {
  1537. BOOL bRet = FALSE;
  1538. LPSTR pszParams;
  1539. // eliminate "COMx:" from mode string leaving ptr to parameters string
  1540. pszParams = StripPortName(pszModeStr);
  1541. // if there are params... (some apps pass "com1:" -- hence 2nd test)
  1542. if(pszParams) {
  1543. // initialize everything to 0 (especially the flags)
  1544. RtlZeroMemory((PVOID)pdcb32, sizeof(DCB));
  1545. // NOTE: 32-bit BuildCommDCB ONLY touches fields associated with psz1
  1546. if(BuildCommDCB(pszParams, pdcb32)) {
  1547. pdcb32->DCBlength = sizeof(DCB);
  1548. // fill in specific fields a la Win3.1
  1549. // NOTE: fields are 0 unless explicitly set
  1550. pdcb32->fBinary = 1;
  1551. pdcb32->fDtrControl = DTR_CONTROL_ENABLE; //same as fDTRDisable == 0
  1552. pdcb32->fRtsControl = RTS_CONTROL_ENABLE; //same as fRTSDisable == 0
  1553. pdcb32->XonLim = 10;
  1554. pdcb32->XoffLim = 10;
  1555. pdcb32->XonChar = 0x11; // Ctrl-Q
  1556. pdcb32->XoffChar = 0x13; // Ctrl-S
  1557. bRet = TRUE;
  1558. }
  1559. }
  1560. return(bRet);
  1561. }
  1562. VOID InitDEB16(PCOMDEB16 pComDEB16, UINT iTab, WORD QInSize, WORD QOutSize)
  1563. {
  1564. VPVOID vpBiosData;
  1565. PWORD16 pwBiosData;
  1566. // Win3.1 init's most the stuff to zero except as handled below
  1567. RtlZeroMemory((PVOID)pComDEB16, sizeof(COMDEB16));
  1568. // get the I/O base address for the port
  1569. vpBiosData = (VPVOID)(RM_BIOS_DATA + (iTab * sizeof(WORD)));
  1570. if(pwBiosData = (PWORD16)GetRModeVDMPointer(vpBiosData)) {
  1571. pComDEB16->Port = (WORD)*pwBiosData;
  1572. FREEVDMPTR(pwBiosData);
  1573. }
  1574. pComDEB16->RecvTrigger = (WORD)-1;
  1575. pComDEB16->QInSize = QInSize;
  1576. pComDEB16->QOutSize = QOutSize;
  1577. }
  1578. /* start thread for Modem interrupt emulation */
  1579. BOOL WOWStartModemIntThread(PWOWPORT pWOWPort)
  1580. {
  1581. BOOL ret = FALSE;
  1582. DWORD dwUnused;
  1583. HANDLE hEvent, hMiThread;
  1584. // set up temporary semaphore to sync with Modem interrupt thread
  1585. if((hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) {
  1586. goto ErrorExit0;
  1587. }
  1588. // use pWOWPort->hMiThread temporarily to help start the thread
  1589. pWOWPort->hMiThread = hEvent;
  1590. // create the MSR thread
  1591. if((hMiThread = CreateThread(NULL,
  1592. 8192,
  1593. (LPTHREAD_START_ROUTINE)WOWModemIntThread,
  1594. (PWOWPORT)pWOWPort,
  1595. 0,
  1596. (LPDWORD)&dwUnused)) == NULL) {
  1597. goto ErrorExit1;
  1598. }
  1599. // block until thread notifies us that it has started
  1600. WaitForSingleObject(hEvent, INFINITE);
  1601. pWOWPort->hMiThread = hMiThread;
  1602. CloseHandle(hEvent);
  1603. ret = TRUE;
  1604. goto FunctionExit;
  1605. // this is the error code path
  1606. ErrorExit1:
  1607. CloseHandle(hEvent);
  1608. ErrorExit0:
  1609. pWOWPort->hMiThread = NULL;
  1610. FunctionExit:
  1611. #ifdef DEBUG
  1612. if(!(ret)) {
  1613. LOGDEBUG(0,("WOW::W32StartModemIntThread failed\n"));
  1614. }
  1615. #endif
  1616. return(ret);
  1617. }
  1618. // Modem Interrupt thread for SetCommEventMask/EnableCommNotification support
  1619. // Tries to emulate the interrupt handling in ibmint.asm of Win3.1 comm.drv.
  1620. // Our "interrupts" here are the events from the NT serial comm stuff
  1621. VOID WOWModemIntThread(PWOWPORT pWOWPort)
  1622. {
  1623. BOOL fRing = FALSE;
  1624. UINT iTab;
  1625. DWORD dwRing;
  1626. DWORD dwEvts = 0;
  1627. DWORD dwEvtOld = 0;
  1628. DWORD dwEvtWord = 0;
  1629. DWORD dwMSR = 0;
  1630. DWORD dwErrCode = 0;
  1631. DWORD cbTransfer;
  1632. HANDLE h32;
  1633. PCOMDEB16 lpComDEB16;
  1634. OVERLAPPED ol;
  1635. iTab = pWOWPort->idComDev;
  1636. lpComDEB16 = pWOWPort->lpComDEB16;
  1637. h32 = pWOWPort->h32;
  1638. // set the current modem status & Event word
  1639. lpComDEB16->MSRShadow = (BYTE)0;
  1640. lpComDEB16->EvtWord = (WORD)0;
  1641. lpComDEB16->ComErr = (WORD)0;
  1642. lpComDEB16->QInCount = (WORD)0;
  1643. lpComDEB16->QOutCount = (WORD)0;
  1644. if(VALIDLPT(iTab)) {
  1645. iTab = GETLPTID(iTab);
  1646. }
  1647. ol.Internal = 0;
  1648. ol.InternalHigh = 0;
  1649. ol.Offset = 0;
  1650. ol.OffsetHigh = 0;
  1651. ol.hEvent = CreateEvent(NULL,
  1652. TRUE,
  1653. FALSE,
  1654. (LPTSTR)PortTab[iTab].szPort);
  1655. // activate modem events in the mask, we want to emulate all the interrupts
  1656. SetCommMask(h32, EV_NTEVENTS);
  1657. // initialize the shadow MSR
  1658. GetCommModemStatus(h32, &dwMSR);
  1659. dwMSR &= MSR_STATEONLY;
  1660. lpComDEB16->MSRShadow = LOBYTE(LOWORD(dwMSR));
  1661. // wake up the thread that created this thread in WOWStartModemIntThread()
  1662. SetEvent(pWOWPort->hMiThread);
  1663. while(!pWOWPort->fClose) {
  1664. // wait for an event - hopefully this will be somewhat similar to
  1665. // the TimerProc in ibmint.asm which gets called every 100ms
  1666. if(!WaitCommEvent(h32, &dwEvts, &ol)) {
  1667. if(GetLastError() == ERROR_IO_PENDING) {
  1668. // ...block here 'til event specified in WaitCommEvent() occurs
  1669. if(!GetOverlappedResult(h32, &ol, &cbTransfer, TRUE)) {
  1670. LOGDEBUG(0, ("WOW::WUCOMM: WOWModemIntThread: Wait failed\n"));
  1671. }
  1672. }
  1673. else {
  1674. LOGDEBUG(0, ("WOW::WUCOMM: WOWModemIntThread : Overlap failed\n"));
  1675. }
  1676. }
  1677. ResetEvent(ol.hEvent);
  1678. // Get current MSR state, current state of delta bits isn't accurate for us
  1679. GetCommModemStatus(h32, &dwMSR);
  1680. dwMSR &= MSR_STATEONLY; // throw away delta bits
  1681. // set the DELTA bits in the shadow MSR
  1682. if(dwEvts & EV_CTS) dwMSR |= MSR_DCTS;
  1683. if(dwEvts & EV_DSR) dwMSR |= MSR_DDSR;
  1684. if(dwEvts & EV_RLSD) dwMSR |= MSR_DDCD;
  1685. if(dwEvts & EV_RING) {
  1686. fRing = TRUE;
  1687. dwRing = EV_RING;
  1688. }
  1689. else if(fRing) {
  1690. fRing = FALSE;
  1691. dwMSR |= MSR_TERI;
  1692. dwRing = EV_RingTe;
  1693. }
  1694. else {
  1695. dwRing = 0;
  1696. }
  1697. // Form the events
  1698. dwEvtOld = (DWORD)lpComDEB16->EvtWord;
  1699. dwEvtWord = 0;
  1700. dwEvtWord = dwRing | (dwEvts & (EV_ERR | EV_BREAK | EV_RXCHAR | EV_TXEMPTY | EV_CTS | EV_DSR | EV_RLSD | EV_RXFLAG));
  1701. // we have to figure the state bits out from the MSR
  1702. if(dwMSR & MS_CTS_ON) dwEvtWord |= EV_CTSS;
  1703. if(dwMSR & MS_DSR_ON) dwEvtWord |= EV_DSRS;
  1704. if(dwMSR & MS_RLSD_ON) dwEvtWord |= EV_RLSDS;
  1705. // One of the major tasks of this routine is to update the MSRShadow
  1706. // and EvtWord in the COMDEB16 structure.
  1707. //
  1708. //apply the msr as well
  1709. lpComDEB16->MSRShadow = LOBYTE(LOWORD(dwMSR));
  1710. // apply the event mask the app specified
  1711. lpComDEB16->EvtWord |= LOWORD(dwEvtWord) & lpComDEB16->EvtMask;
  1712. // The following code simluates the COM Notifcation functionality of
  1713. // Win 3.1.
  1714. //
  1715. // Notifications:
  1716. //
  1717. // if they want receive transmit notification & it's time to notify
  1718. // if there wasn't an Rx overflow continue...
  1719. if( lpComDEB16->NotifyHandle ) {
  1720. // get current error code & queue counts
  1721. WOWGetCommError(pWOWPort);
  1722. if((dwEvtWord & ( EV_RXCHAR | EV_RXFLAG )) &&
  1723. !(pWOWPort->dwErrCode & CE_RXOVER)) {
  1724. // if they want receive notification & it's time to notify
  1725. // apps should set RecvTrigger to -1 if they don't want notification
  1726. if((((SHORT)lpComDEB16->RecvTrigger) != -1) &&
  1727. (lpComDEB16->QInCount >= lpComDEB16->RecvTrigger)) {
  1728. // if the app hasn't already been notified of this ...
  1729. if(!(lpComDEB16->NotifyFlags & CN_RECEIVEHI)) {
  1730. PostMessage(HWND32(lpComDEB16->NotifyHandle),
  1731. WOW_WM_COMMNOTIFY,
  1732. MAKEWPARAM((WORD)pWOWPort->idComDev, 0),
  1733. MAKELPARAM(CN_RECEIVE, 0));
  1734. lpComDEB16->NotifyFlags |= CN_RECEIVEHI;
  1735. }
  1736. }
  1737. else {
  1738. lpComDEB16->NotifyFlags &= ~CN_RECEIVEHI;
  1739. }
  1740. }
  1741. // if they want receive transmit notification & it's time to notify
  1742. if(lpComDEB16->QOutCount < (SHORT)lpComDEB16->SendTrigger) {
  1743. // if the app hasn't already been notified of this ...
  1744. if(!(lpComDEB16->NotifyFlags & CN_TRANSMITHI)) {
  1745. PostMessage(HWND32(lpComDEB16->NotifyHandle),
  1746. WOW_WM_COMMNOTIFY,
  1747. MAKEWPARAM((WORD)pWOWPort->idComDev, 0),
  1748. MAKELPARAM(CN_TRANSMIT, 0));
  1749. lpComDEB16->NotifyFlags |= CN_TRANSMITHI;
  1750. }
  1751. }
  1752. else {
  1753. lpComDEB16->NotifyFlags &= ~CN_TRANSMITHI;
  1754. }
  1755. // if we are notifying the app of EV_ event's
  1756. if((lpComDEB16->NotifyFlags & CN_NOTIFYHI) &&
  1757. ((DWORD)lpComDEB16->EvtWord != dwEvtOld)) {
  1758. PostMessage(HWND32(lpComDEB16->NotifyHandle),
  1759. WOW_WM_COMMNOTIFY,
  1760. MAKEWPARAM((WORD)pWOWPort->idComDev, 0),
  1761. MAKELPARAM(CN_EVENT, 0));
  1762. }
  1763. // Now that we've processed all the interrupts, do the TimerProc.
  1764. // if we are notifying the app of anything in Rx queue
  1765. // this mimics the notification in the TimerProc (see ibmint.asm)
  1766. if(((SHORT)lpComDEB16->RecvTrigger != -1) &&
  1767. (lpComDEB16->QInCount != 0) &&
  1768. (!(lpComDEB16->NotifyFlags & CN_RECEIVEHI))) {
  1769. PostMessage(HWND32(lpComDEB16->NotifyHandle),
  1770. WOW_WM_COMMNOTIFY,
  1771. MAKEWPARAM((WORD)pWOWPort->idComDev, 0),
  1772. MAKELPARAM(CN_RECEIVE, 0));
  1773. lpComDEB16->NotifyFlags |= CN_RECEIVEHI;
  1774. }
  1775. }
  1776. // we've handled all interrupts, give control back to app
  1777. Sleep(0);
  1778. } // end thread loop
  1779. CloseHandle(ol.hEvent);
  1780. ExitThread(0);
  1781. }
  1782. DWORD WOWGetCommError(PWOWPORT pwp)
  1783. {
  1784. COMSTAT cs;
  1785. DWORD dwErr;
  1786. ClearCommError(pwp->h32, &dwErr, &cs);
  1787. EnterCriticalSection(&pwp->csWrite);
  1788. //
  1789. // We do our own write buffering so we ignore
  1790. // the cbOutQue returned by ClearCommError, which
  1791. // only reflects pending writes.
  1792. //
  1793. // Number of bytes in our write queue is calculated
  1794. // using the size of the queue and the amount free
  1795. // in the queue, minus one. Minus one because
  1796. // there's one slot in the queue which is never used.
  1797. //
  1798. cs.cbOutQue = (pwp->cbWriteBuf - pwp->cbWriteFree) - 1;
  1799. LeaveCriticalSection(&pwp->csWrite);
  1800. // always update the status & preserve any error condition
  1801. pwp->cs = cs;
  1802. pwp->dwErrCode |= dwErr;
  1803. pwp->lpComDEB16->ComErr |= LOWORD(dwErr);
  1804. // always update the queue counts in the DEB
  1805. pwp->lpComDEB16->QInCount = LOWORD(cs.cbInQue);
  1806. pwp->lpComDEB16->QOutCount = LOWORD(cs.cbOutQue);
  1807. return(dwErr);
  1808. }
  1809. /* for hung/crashed app support */
  1810. VOID FreeCommSupportResources(DWORD dwThreadID)
  1811. {
  1812. UINT iTab;
  1813. PWOWPORT pWOWPort;
  1814. for(iTab = 0; iTab < NUMPORTS; iTab++) {
  1815. if(pWOWPort = PortTab[iTab].pWOWPort) {
  1816. if(pWOWPort->dwThreadID == dwThreadID) {
  1817. DeletePortTabEntry(pWOWPort);
  1818. break;
  1819. }
  1820. }
  1821. }
  1822. }
  1823. /* functions exported to the VDM */
  1824. /* NOTE: idComDev: COM1 == 0, LPT1 == 0x80 */
  1825. BYTE GetCommShadowMSR(WORD idComDev)
  1826. {
  1827. BYTE MSR=0;
  1828. DWORD dwModemStatus;
  1829. PWOWPORT pWOWPort;
  1830. if (pWOWPort = GETPWOWPTR (idComDev)) {
  1831. if(pWOWPort->hMiThread) {
  1832. MSR = (BYTE)pWOWPort->lpComDEB16->MSRShadow;
  1833. }
  1834. // get it the slow way if SetCommEventMask() hasn't been called
  1835. else if ( GetCommModemStatus(pWOWPort->h32, &dwModemStatus) ) {
  1836. MSR = (BYTE)LOBYTE(LOWORD(dwModemStatus));
  1837. }
  1838. }
  1839. return(MSR);
  1840. }
  1841. /* NOTE: idComDev: COM1 == 0, LPT1 == 0x80 */
  1842. HANDLE GetCommHandle(WORD idComDev)
  1843. {
  1844. PWOWPORT pWOWPort;
  1845. if (pWOWPort = GETPWOWPTR (idComDev)) {
  1846. return(pWOWPort->h32);
  1847. }
  1848. else {
  1849. return(NULL); // will return NULL if bad range of idComDev or if the
  1850. } // port wasn't initialized through an OpenComm() API call
  1851. }
  1852. BOOL IsQLinkGold(WORD wTDB)
  1853. {
  1854. PTDB pTDB;
  1855. pTDB = (PVOID)SEGPTR(wTDB,0);
  1856. if(WOW32_stricmp(pTDB->TDB_ModName, "QLGOLD")) {
  1857. return(FALSE);
  1858. }
  1859. return(TRUE);
  1860. }
  1861. //
  1862. // EnqueueCommWrite - stuff characters into the Comm Write queue
  1863. // assoicated with pWOWPort.
  1864. //
  1865. // Returns number of characters queued.
  1866. //
  1867. // This function takes care of entering/leaving the critsec.
  1868. //
  1869. USHORT EnqueueCommWrite(PWOWPORT pwp, PUCHAR pch, USHORT cb)
  1870. {
  1871. USHORT cbWritten = 0;
  1872. USHORT cbToCopy;
  1873. USHORT cbChunk;
  1874. BOOL fQueueEmpty;
  1875. BOOL fDelay = FALSE;
  1876. // WinFax Lite 3 calls WriteFile("AT+FCLASS=1") to set the modem to fax mode
  1877. // when it is recieving a fax. Some modems appear to be slow to repsond
  1878. // with the "OK" string (especially since we enqueue the "AT+FCLASS=1" write
  1879. // and then write it in overlapped mode) -- so, when we tell the app we sent
  1880. // it, it then follows with a "ATA" string without waiting for the modem's
  1881. // response to the previous command. This confuses several different modems
  1882. // and so they never answer. This mechanism allows us to synchronize the
  1883. // "AT+FCLASS=1" command so that it works more like Win3.1. See bug #9479
  1884. if(cb == 12) {
  1885. // Handy way to say:
  1886. // if(pch[0]=='A' && pch[1]=='T' && pch[2]=='+' && pch[3]=='F') {
  1887. if((*(DWORD *)pch) == 0x462b5441) {
  1888. // if(pch[0]=='C' && pch[1]=='L' && pch[2]=='A' && pch[3]=='S') {
  1889. if((*(DWORD *)(pch+sizeof(DWORD))) == 0x53414c43) {
  1890. // if(pch[0]=='S' && pch[1]=='=') {
  1891. if((*(WORD *)(pch+(2*sizeof(DWORD)))) == 0x3D53) {
  1892. fDelay = TRUE;
  1893. } } } }
  1894. EnterCriticalSection(&pwp->csWrite);
  1895. fQueueEmpty = (pwp->pchWriteHead == pwp->pchWriteTail);
  1896. //
  1897. // cbToCopy is the total number of bytes that we are going to enqueue
  1898. //
  1899. cbToCopy = min(cb, pwp->cbWriteFree);
  1900. //
  1901. // Any write can be accomplished in at most two chunks.
  1902. // The first writes up until the buffer wraps, while
  1903. // the second starts at the beginning of the buffer.
  1904. //
  1905. // Do the first half, which may do it all.
  1906. //
  1907. // Number of bytes for the first chunk is the smaller of
  1908. // the total number of bytes free in the write buffer and
  1909. // the number of bytes free before the end of the buffer.
  1910. //
  1911. cbChunk = min(cbToCopy,
  1912. (pwp->pchWriteBuf + pwp->cbWriteBuf) - pwp->pchWriteTail);
  1913. RtlCopyMemory(pwp->pchWriteTail, pch, cbChunk);
  1914. pwp->cbWriteFree -= cbChunk;
  1915. pwp->pchWriteTail += cbChunk;
  1916. cbWritten += cbChunk;
  1917. //
  1918. // Tail pointer may have moved to point just beyond the buffer.
  1919. //
  1920. if (pwp->pchWriteTail >= pwp->pchWriteBuf + pwp->cbWriteBuf) {
  1921. WOW32ASSERT(pwp->pchWriteTail == pwp->pchWriteBuf + pwp->cbWriteBuf);
  1922. pwp->pchWriteTail = pwp->pchWriteBuf;
  1923. }
  1924. //
  1925. // Are we done?
  1926. //
  1927. if (cbWritten < cbToCopy) {
  1928. //
  1929. // I think this case should only be taken when we've wrapped, so
  1930. // be sure.
  1931. //
  1932. WOW32ASSERT(pwp->pchWriteTail == pwp->pchWriteBuf);
  1933. //
  1934. // Nope, do the second half.
  1935. //
  1936. cbChunk = min((cbToCopy - cbWritten), pwp->cbWriteFree);
  1937. RtlCopyMemory(pwp->pchWriteTail, pch + cbWritten, cbChunk);
  1938. pwp->cbWriteFree -= cbChunk;
  1939. pwp->pchWriteTail += cbChunk;
  1940. cbWritten += cbChunk;
  1941. WOW32ASSERT(pwp->pchWriteTail < pwp->pchWriteBuf + pwp->cbWriteBuf);
  1942. }
  1943. //
  1944. // If the buffer was empty to start with and we made it
  1945. // non-empty, issue the first WriteFile and signal the
  1946. // writer thread to wake up.
  1947. //
  1948. if (fQueueEmpty && cbWritten) {
  1949. pwp->cbWritePending = CALC_COMM_WRITE_SIZE(pwp);
  1950. if (!WriteFile(pwp->h32, pwp->pchWriteHead, pwp->cbWritePending,
  1951. &pwp->cbWritten, &pwp->olWrite)) {
  1952. if (ERROR_IO_PENDING == GetLastError()) {
  1953. pwp->fWriteDone = FALSE;
  1954. } else {
  1955. pwp->fWriteDone = TRUE;
  1956. LOGDEBUG(0, ("WOW EnqueueCommWrite: WriteFile to id %u fails (error %u)\n",
  1957. pwp->idComDev, GetLastError()));
  1958. }
  1959. } else {
  1960. pwp->fWriteDone = TRUE;
  1961. }
  1962. //
  1963. // Leave the critical section before setting the event. Otherwise
  1964. // the other thread could wake up when the event is set and immediately
  1965. // block on the critical section.
  1966. //
  1967. LeaveCriticalSection(&pwp->csWrite);
  1968. // avoid setting the event twice
  1969. if(!fDelay) {
  1970. SetEvent(pwp->hWriteEvent);
  1971. }
  1972. } else {
  1973. LeaveCriticalSection(&pwp->csWrite);
  1974. }
  1975. // this gives the writer thread a chance to write out "AT+FCLASS=1" strings
  1976. if(fDelay) {
  1977. SetEvent(pwp->hWriteEvent);
  1978. Sleep(1000);
  1979. }
  1980. return cbWritten;
  1981. }
  1982. //
  1983. // WOWCommWriteThread created for COM ports only. This thread dequeues
  1984. // characters from the write buffer and writes them to the COM port.
  1985. // This thread uses pwp->hWriteEvent for two purposes:
  1986. //
  1987. // 1. The event is signalled by EnqueueCommWrite when the write
  1988. // buffer had been empty but is not now. This wakes us up
  1989. // so we can write to the port. Note that we will always
  1990. // be in the WaitForSingleObject at the top of the function
  1991. // in this case, since that's where we sleep when the buffer
  1992. // is empty.
  1993. //
  1994. // 2. DeletePortTabEntry signals the event after setting
  1995. // pwp->fClose to tell us the port is closing and we
  1996. // need to clean up and terminate this thread. This
  1997. // thread might be doing anything in this case, but
  1998. // it is careful to check pwp->fClose before sleeping
  1999. // again.
  2000. //
  2001. // 3. wu32FlushComm() signals the event and marks the queue empty
  2002. ULONG WOWCommWriterThread(LPVOID pWOWPortStruct)
  2003. {
  2004. PWOWPORT pwp = (PWOWPORT)pWOWPortStruct;
  2005. HANDLE ah[2];
  2006. //
  2007. // Copy event handles into array for WaitForMultipleObjects.
  2008. //
  2009. ah[0] = pwp->hWriteEvent;
  2010. ah[1] = pwp->olWrite.hEvent;
  2011. WaitForWriteOrder:
  2012. //
  2013. // pwp->fClose is TRUE when the port is closed.
  2014. //
  2015. while (!pwp->fClose) {
  2016. //
  2017. // First wait for something to be written to the buffer.
  2018. //
  2019. WaitForSingleObject(pwp->hWriteEvent, INFINITE);
  2020. //
  2021. // Critical section protects write buffer.
  2022. //
  2023. EnterCriticalSection(&pwp->csWrite);
  2024. //
  2025. // The buffer is empty when head == tail.
  2026. //
  2027. while (pwp->pchWriteHead != pwp->pchWriteTail) {
  2028. //
  2029. // pwp->cbWritePending will be nonzero if
  2030. // the application thread queued a write to
  2031. // an empty buffer and then issued the first
  2032. // WriteFile call.
  2033. //
  2034. if (pwp->cbWritePending) {
  2035. if (!pwp->fWriteDone) {
  2036. LeaveCriticalSection(&pwp->csWrite);
  2037. goto WaitForWriteCompletion;
  2038. } else {
  2039. goto CleanupAfterWriteComplete;
  2040. }
  2041. }
  2042. pwp->cbWritePending = CALC_COMM_WRITE_SIZE(pwp);
  2043. //
  2044. // Leave the critical section before writing. This is
  2045. // safe because the app thread doesn't change the
  2046. // head pointer. (Not true if wu32FlushComm was called)
  2047. //
  2048. LeaveCriticalSection(&pwp->csWrite);
  2049. if (!WriteFile(pwp->h32, pwp->pchWriteHead, pwp->cbWritePending,
  2050. &pwp->cbWritten, &pwp->olWrite)) {
  2051. if (ERROR_IO_PENDING == GetLastError() ) {
  2052. WaitForWriteCompletion:
  2053. //
  2054. // Wait for the write to complete or for us to
  2055. // be alerted that the port is closing.
  2056. //
  2057. while (WAIT_OBJECT_0 == WaitForMultipleObjects(2, ah, FALSE, INFINITE)) {
  2058. //
  2059. // pwp->hWriteEvent was signaled. This probably
  2060. // means that the port was closed.
  2061. //
  2062. if (pwp->fClose) {
  2063. goto PortClosed;
  2064. }
  2065. }
  2066. if (GetOverlappedResult(pwp->h32,
  2067. &pwp->olWrite,
  2068. &pwp->cbWritten,
  2069. TRUE
  2070. ) )
  2071. {
  2072. goto WriteSuccess;
  2073. }
  2074. }
  2075. LOGDEBUG(0, ("WOWCommWriterThread: WriteFile to id %u fails (error %u)\n",
  2076. pwp->idComDev, GetLastError()));
  2077. pwp->cbWritePending = 0;
  2078. goto WaitForWriteOrder;
  2079. }
  2080. WriteSuccess:
  2081. //
  2082. // Update head pointer to reflect portion written.
  2083. //
  2084. EnterCriticalSection(&pwp->csWrite);
  2085. CleanupAfterWriteComplete:
  2086. WOW32ASSERT(pwp->cbWritten == (WORD)pwp->cbWritten);
  2087. pwp->pchWriteHead += pwp->cbWritten;
  2088. pwp->cbWriteFree += (WORD)pwp->cbWritten;
  2089. pwp->cbWritePending = 0;
  2090. //
  2091. // The following is a sanity check on our buffer manipulations.
  2092. //
  2093. #ifdef DEBUG
  2094. if (pwp->pchWriteHead >= pwp->pchWriteBuf + pwp->cbWriteBuf) {
  2095. WOW32ASSERT(pwp->pchWriteHead == pwp->pchWriteBuf + pwp->cbWriteBuf);
  2096. }
  2097. #endif
  2098. if (pwp->pchWriteHead == pwp->pchWriteBuf + pwp->cbWriteBuf) {
  2099. pwp->pchWriteHead = pwp->pchWriteBuf;
  2100. }
  2101. }
  2102. //
  2103. // We have exhausted the write buffer, leave the critical section
  2104. // and loop back to the wait for the buffer to become non-empty.
  2105. //
  2106. LeaveCriticalSection(&pwp->csWrite);
  2107. }
  2108. PortClosed:
  2109. CloseHandle(pwp->olWrite.hEvent);
  2110. return 0;
  2111. }
  2112. // Checks status on RLSD, CTS, and DSR for timeout support
  2113. // see MSRWait() in win3.1 comm.drv code
  2114. BOOL MSRWait(PWOWPORT pwp)
  2115. {
  2116. DWORD dwStartTime, dwElapsedTime, dwLineStatus;
  2117. DWORD dwErr = 0;
  2118. // start the timeout clock (returns msec)
  2119. dwStartTime = GetTickCount();
  2120. // loop until either all lines are high or a timeout occurs
  2121. while(!dwErr) {
  2122. // get the current status of the lines
  2123. if ( !GetCommModemStatus(pwp->h32, &dwLineStatus) ) {
  2124. //can't rely on third party drivers not to mess with dwLineStatus on failure
  2125. dwLineStatus = 0;
  2126. }
  2127. // if all the required lines are up -- we're done
  2128. if((pwp->lpComDEB16->MSRMask & LOBYTE(dwLineStatus)) == pwp->lpComDEB16->MSRMask)
  2129. break;
  2130. // get the elapsed time
  2131. dwElapsedTime = GetTickCount() - dwStartTime;
  2132. if(pwp->RLSDTimeout != IGNORE_TIMEOUT) {
  2133. // if line is low
  2134. if(!(dwLineStatus & MS_RLSD_ON)) {
  2135. if(dwElapsedTime > UINT32(pwp->RLSDTimeout))
  2136. dwErr |= CE_RLSDTO;
  2137. }
  2138. }
  2139. if(pwp->CTSTimeout != IGNORE_TIMEOUT) {
  2140. // if line is low
  2141. if(!(dwLineStatus & MS_CTS_ON)) {
  2142. if(dwElapsedTime > UINT32(pwp->CTSTimeout))
  2143. dwErr |= CE_CTSTO;
  2144. }
  2145. }
  2146. if(pwp->DSRTimeout != IGNORE_TIMEOUT) {
  2147. // if line is low
  2148. if(!(dwLineStatus & MS_DSR_ON)) {
  2149. if(dwElapsedTime > UINT32(pwp->DSRTimeout))
  2150. dwErr |= CE_DSRTO;
  2151. }
  2152. }
  2153. }
  2154. pwp->dwErrCode |= dwErr;
  2155. pwp->lpComDEB16->ComErr |= LOWORD(dwErr);
  2156. if(dwErr)
  2157. return(TRUE);
  2158. else
  2159. return(FALSE);
  2160. }