/*++ * * WOW v1.0 * * Copyright (c) 1991, Microsoft Corporation * * WUCOMM.C * WOW32 16-bit User API support * * History: * Created 07-Mar-1991 by Jeff Parsons (jeffpar) * made real Dec-1992 by Craig Jones (v-cjones) * made work Apr-1993 by Craig Jones (v-cjones) * made fast Jun-1993 by Craig Jones (v-cjones) --*/ #include "precomp.h" #pragma hdrstop #include MODNAME(wucomm.c); /* Define the table for mapping Win3.1 idComDev's to 32-bit comm HFILE's. */ /* This table is indexed by the 16-bit idComDev that we return to the app */ /* which is assigned based on the device name (see wucomm.h). You can */ /* use GETPWOWPTR(idComDev) to get the ptr to the corresponding WOWPort */ /* struct from PortTab[]. */ /* This table must contain NUMPORTS (def'd in wucomm.h) entries */ PORTTAB PortTab[] = { {"COM1", NULL}, {"COM2", NULL}, {"COM3", NULL}, {"COM4", NULL}, {"COM5", NULL}, {"COM6", NULL}, {"COM7", NULL}, {"COM8", NULL}, {"COM9", NULL}, {"LPT1", NULL}, {"LPT2", NULL}, {"LPT3", NULL} }; /* function prototypes for local support functions */ DWORD Baud16toBaud32(UINT BaudRate); WORD Baud32toBaud16(DWORD BaudRate); void DCB16toDCB32(PWOWPORT pWOWPort, LPDCB lpdcb32, PDCB16 pdcb16); void DCB32toDCB16(PDCB16 pdcb16, LPDCB lpdcb32, UINT idComDev, BOOL fChEvt); BOOL DeletePortTabEntry(PWOWPORT pWOWPort); ULONG WOWCommWriterThread(LPVOID pWOWPortStruct); USHORT EnqueueCommWrite(PWOWPORT pwp, PUCHAR pch, USHORT cb); UINT GetModePortTabIndex(PSZ pszModeStr); BOOL GetPortName(LPSTR pszMode, LPSTR pszPort); UINT GetStrPortTabIndex(PSZ szPort); BOOL InitDCB32(LPDCB pdcb32, LPSTR pszModeStr); VOID InitDEB16(PCOMDEB16 pComDEB16, UINT iTab, WORD QInSize, WORD QOutSize); PSZ StripPortName(PSZ psz); PSZ GetPortStringToken(PSZ pszSrc, PSZ pszToken); BOOL MSRWait(PWOWPORT pwp); BOOL IsQLinkGold(WORD wTDB); /* prototypes for Modem interrupt emulation thread support */ VOID WOWModemIntThread(PWOWPORT pWOWPortStruct); BOOL WOWStartModemIntThread(PWOWPORT pWOWPort); DWORD WOWGetCommError(PWOWPORT pWOWPort); // Win3.1 returns: // 0 on success OR LPT. // -1 on ANY error. ULONG FASTCALL WU32BuildCommDCB(PVDMFRAME pFrame) { ULONG ul = (ULONG)-1; UINT len, iTab; PSZ psz1; PDCB16 pdcb16; DCB dcb32; register PBUILDCOMMDCB16 parg16; GETARGPTR(pFrame, sizeof(BUILDCOMMDCB16), parg16); GETPSZPTR(parg16->f1, psz1); // if valid device name... if((INT)(iTab = GetModePortTabIndex(psz1)) >= 0) { // Initialize a Win3.1 compatible 32-bit DCB if(InitDCB32(&dcb32, psz1)) { GETMISCPTR(parg16->f2, pdcb16); if(pdcb16) { // copy the psz1 fields to the 16-bit struct iTab = (VALIDCOM(iTab) ? iTab : TABIDTOLPT(iTab)); DCB32toDCB16(pdcb16, &dcb32, iTab, FALSE); // set timeouts for COMx ports only if(VALIDCOM(iTab)) { // 'P' is the only "retry" option supported in Win3.1 len = strlen(psz1) - 1; while(psz1[len] != ' ') { // delete trailing spaces len--; } if((psz1[len] == 'P') || (psz1[len] == 'p')) { pdcb16->RlsTimeout = INFINITE_TIMEOUT; pdcb16->CtsTimeout = INFINITE_TIMEOUT; pdcb16->DsrTimeout = INFINITE_TIMEOUT; } } FLUSHVDMPTR(parg16->f2, sizeof(DCB16), pdcb16); FREEMISCPTR(pdcb16); ul = 0; // Win3.1 returns 0 if success } } FREEPSZPTR(psz1); } #ifdef DEBUG if(!(ul==0)) { LOGDEBUG(0,("WOW::WU32BuildCommDCB: failed\n")); } #endif FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // Error word on success OR LPTx. // 0x8000 on bad idComDev. ULONG FASTCALL WU32ClearCommBreak(PVDMFRAME pFrame) { ULONG ul = 0x00008000; UINT idComDev; PWOWPORT pWOWPort; register PCLEARCOMMBREAK16 parg16; GETARGPTR(pFrame, sizeof(CLEARCOMMBREAK16), parg16); idComDev = UINT32(parg16->f1); if (pWOWPort = GETPWOWPTR(idComDev)) { if (VALIDCOM(idComDev)) { if(!ClearCommBreak(pWOWPort->h32)) { WOWGetCommError(pWOWPort); } } ul = pWOWPort->dwErrCode; } #ifdef DEBUG if(!(ul!=0x00008000)) { LOGDEBUG(0,("WOW::WU32ClearCommBreak: failed\n")); } #endif FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // 0 if success OR if LPTx. // -1 for bad idComDev OR port not open. // -2 for Timeout error. // We pass back (as a 2nd parameter) the DWORD obtained from the call to // GlobalDosAlloc() in IOpenComm() in user.exe. (WOWModemIntThread() support) ULONG FASTCALL WU32CloseComm(PVDMFRAME pFrame) { ULONG ul = (ULONG)-1; UINT idComDev; PDWORD16 lpdwDEB16; PWOWPORT pWOWPort = NULL; register PCLOSECOMM16 parg16; GETARGPTR(pFrame, sizeof(CLOSECOMM16), parg16); idComDev = UINT32(parg16->f1); if (pWOWPort = GETPWOWPTR(idComDev)) { // pass back the 16:16 ptr for the WOWModemIntThread() support GETMISCPTR(parg16->f2, lpdwDEB16); if (lpdwDEB16) { *lpdwDEB16 = pWOWPort->dwComDEB16; FLUSHVDMPTR(parg16->f2, sizeof(DWORD), lpdwDEB16); FREEMISCPTR(lpdwDEB16); } // clean up the PortTab[] entry if (DeletePortTabEntry(pWOWPort)) { ul = (ULONG)-2; // return Win3.1 timeOut error } else { ul = 0; } } else { LOGDEBUG (0, ("WOW::WU32CloseComm: Not a valid COM or LPT\n")); } #ifdef DEBUG if(!(ul==0)) { LOGDEBUG(0,("WOW::WU32CloseComm: failed\n")); } #endif FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // TRUE on success. // FALSE if error OR if EnableCommNotification() not supported. // User16 validation layer returns 0 for bad hwnd. ULONG FASTCALL WU32EnableCommNotification(PVDMFRAME pFrame) { ULONG ul = (ULONG)FALSE; UINT idComDev; WORD cbQue; BOOL fOK = TRUE; PWOWPORT pWOWPort; PCOMDEB16 lpComDEB16; register PENABLECOMMNOTIFICATION16 parg16; GETARGPTR(pFrame, sizeof(ENABLECOMMNOTIFICATION16), parg16); idComDev = UINT32(parg16->f1); if ((VALIDCOM(idComDev)) && (pWOWPort = PortTab[idComDev].pWOWPort)) { lpComDEB16 = pWOWPort->lpComDEB16; // if they are trying to disable notifcation (HWND == NULL) if(WORD32(parg16->f2) == 0) { lpComDEB16->NotifyHandle = 0; lpComDEB16->NotifyFlags = CN_TRANSMITHI; lpComDEB16->RecvTrigger = (WORD)-1; lpComDEB16->SendTrigger = 0; ul = (ULONG)TRUE; } // Validate non-null hwnd's since hwnd validation is disabled in // user16 validation layer else if(!IsWindow(HWND32(parg16->f2))) { ul = (ULONG)FALSE; } // else set up the notification mechanisms else { // if the Modem interrupt thread hasn't started yet -- go start it if(pWOWPort->hMiThread == NULL) { if(!WOWStartModemIntThread(pWOWPort)) { fOK = FALSE; } } // update the DEB to reflect notification if(fOK) { lpComDEB16->NotifyHandle = WORD32(parg16->f2); lpComDEB16->NotifyFlags = CN_TRANSMITHI | CN_NOTIFYHI; // set trigger values the same way Win3.1 does cbQue = WORD32(parg16->f3); if((cbQue < lpComDEB16->QInSize) || ((SHORT)cbQue == -1)) { lpComDEB16->RecvTrigger = cbQue; } else { lpComDEB16->RecvTrigger = lpComDEB16->QInSize - 10; } cbQue = WORD32(parg16->f4); if((cbQue < lpComDEB16->QOutSize) || ((SHORT)cbQue == -1)) { lpComDEB16->SendTrigger = cbQue; } else { lpComDEB16->SendTrigger = lpComDEB16->QOutSize - 10; } ul = (ULONG)TRUE; } } } // else there is no notification for LPT in Win3.1 else { ul = (ULONG)FALSE; } #ifdef DEBUG if(!(ul==1)) { LOGDEBUG(0,("WOW::WU32EnableCommNotification: failed\n")); } #endif FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // The value from the specified function. // The error word for: the line & signal state functions, // function not implemented, OR LPTx where function != (RESETDEV||GETMAXLPT). // 0x8000 for bad idComDev. ULONG FASTCALL WU32EscapeCommFunction(PVDMFRAME pFrame) { ULONG ul = 0x00008000; UINT idComDev; UINT nFunction; WORD IRQ; VPVOID vpBiosData; PWORD16 pwBiosData; PWOWPORT pWOWPort; register PESCAPECOMMFUNCTION16 parg16; GETARGPTR(pFrame, sizeof(ESCAPECOMMFUNCTION16), parg16); // this construct is set up this way because Win3.1 will allow GETMAXCOM // & GETMAXLPT to succeed as long as the idComDev is in the valid range. // (ie: the app doesn't have to call OpenComm() first to set up the PortTab) // for RESETDEV we tell them that we reset the printer. (we're such liars!) nFunction = WORD32(parg16->f2); idComDev = UINT32(parg16->f1); if (VALIDCOM(idComDev)) { if (nFunction == GETMAXCOM) { ul = NUMCOMS-1; } else if (nFunction == GETBASEIRQ || nFunction == GETBASEIRQ+1) { ul = 0xFFFFFFFF; if (idComDev < COM5) { vpBiosData = (VPVOID) (RM_BIOS_DATA + (idComDev * sizeof(WORD))); if (pwBiosData = (PWORD16)GetRModeVDMPointer(vpBiosData)) { if (idComDev == COM1 || idComDev == COM3) { IRQ = IRQ4; } else { IRQ = IRQ3; } ul = MAKELONG((WORD)(*pwBiosData), IRQ); FREEVDMPTR(pwBiosData); } } } else { // for the other functions they must have called OpenComm() if (pWOWPort = PortTab[idComDev].pWOWPort) { switch(nFunction) { // line & signal state functions case SETXOFF: case SETXON: case SETRTS: case CLRRTS: case SETDTR: case CLRDTR: if(!EscapeCommFunction(pWOWPort->h32, nFunction)) { WOWGetCommError(pWOWPort); } ul = pWOWPort->dwErrCode; break; // 0: case 0: ul = 0; // like WFW break; // any other value... default: // non-zero is error: use dwErrcode if there is one if(pWOWPort->dwErrCode) ul = pWOWPort->dwErrCode; // else use what WFW seems inclined to return else ul = CE_OVERRUN | CE_RXPARITY; break; } } } } else if (VALIDLPT(idComDev)) { if(nFunction == RESETDEV) { ul = 0; // no error (ie. "just tell them we did it" - TonyE) } else if(nFunction == GETMAXLPT) { ul = LPTLAST; } else if (pWOWPort = PortTab[GETLPTID(idComDev)].pWOWPort) { ul = pWOWPort->dwErrCode; } else { ul = 0; } } FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // 0 on success. // 0x8000 if bad idComDev. // Error word on error or LPTx. ULONG FASTCALL WU32FlushComm(PVDMFRAME pFrame) { ULONG ul = 0x00008000; UINT idComDev; DWORD dwAction; PWOWPORT pWOWPort; register PFLUSHCOMM16 parg16; GETARGPTR(pFrame, sizeof(FLUSHCOMM16), parg16); idComDev = UINT32(parg16->f1); if (pWOWPort = GETPWOWPTR(idComDev)) { // is a COMx? if (VALIDCOM(idComDev)) { // if flush transmit buffer specified dwAction = PURGE_RXCLEAR; if(parg16->f2 == 0) { dwAction = PURGE_TXCLEAR | PURGE_TXABORT; // // Flush the local writers buffer // EnterCriticalSection(&pWOWPort->csWrite); pWOWPort->pchWriteHead = pWOWPort->pchWriteTail = pWOWPort->pchWriteBuf; pWOWPort->cbWriteFree = pWOWPort->cbWriteBuf - 1; pWOWPort->cbWritePending = 0; LeaveCriticalSection(&pWOWPort->csWrite); } if(PurgeComm(pWOWPort->h32, dwAction)) { if(dwAction == PURGE_RXCLEAR) { pWOWPort->fUnGet = FALSE; } ul = 0; // Win3.1 returns 0 on success } else { WOWGetCommError(pWOWPort); ul = pWOWPort->dwErrCode; } } // else just return current error code for LPTx else { ul = pWOWPort->dwErrCode; } } #ifdef DEBUG if(!(ul==0)) { LOGDEBUG(0,("WOW::WU32FlushComm: failed\n")); } #endif FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // 0x8000 for bad idComDev. // The error word for all other cases. ULONG FASTCALL WU32GetCommError(PVDMFRAME pFrame) { ULONG ul = 0x00008000; UINT idComDev; PWOWPORT pWOWPort; PCOMSTAT16 pcs16; register PGETCOMMERROR16 parg16; GETARGPTR(pFrame, sizeof(GETCOMMERROR16), parg16); GETMISCPTR(parg16->f2, pcs16); idComDev = UINT32(parg16->f1); if (pWOWPort = GETPWOWPTR (idComDev)) { if (VALIDCOM(idComDev) && pcs16) { WOWGetCommError(pWOWPort); // Always update the COMSTAT status byte, DynComm depends on it. pcs16->status = 0; if(pWOWPort->cs.fCtsHold) pcs16->status |= W31CS_fCtsHold; if(pWOWPort->cs.fDsrHold) pcs16->status |= W31CS_fDsrHold; // Note: RlsdHold is zero'd out on Win3.1 if(pWOWPort->cs.fRlsdHold) pcs16->status |= W31CS_fRlsdHold; if(pWOWPort->cs.fXoffHold) pcs16->status |= W31CS_fXoffHold; if(pWOWPort->cs.fXoffSent) pcs16->status |= W31CS_fSentHold; if(pWOWPort->cs.fEof) pcs16->status |= W31CS_fEof; if(pWOWPort->cs.fTxim) pcs16->status |= W31CS_fTxim; pcs16->cbInQue = (WORD)pWOWPort->cs.cbInQue; pcs16->cbOutQue = (WORD)pWOWPort->cs.cbOutQue; // account for the UnGot char (if any) if(pWOWPort->fUnGet) { pcs16->cbInQue++; } } // if an LPT OR pcs16 == NULL, Win3.1 returns the error code else { // for LPT's Win3.1 just zero's the COMSTAT & returns the error code if(VALIDLPT(idComDev)) { if(pcs16) { RtlZeroMemory((PVOID)pcs16, sizeof(COMSTAT16)); } } } ul = (ULONG)pWOWPort->dwErrCode; // clear the error now that the app has got it (but maintain queues) pWOWPort->dwErrCode = 0; pWOWPort->lpComDEB16->ComErr = 0; RtlZeroMemory((PVOID)&(pWOWPort->cs), sizeof(COMSTAT)); if(pcs16) { pWOWPort->cs.cbInQue = pcs16->cbInQue; pWOWPort->cs.cbOutQue = pcs16->cbOutQue; } } FLUSHVDMPTR(parg16->f2, sizeof(COMSTAT16), pcs16); FREEMISCPTR(pcs16); FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // EvtWord on success. // 0 for bad idComDev OR LPTx. ULONG FASTCALL WU32GetCommEventMask(PVDMFRAME pFrame) { ULONG ul=0; DWORD dwEvtMask; UINT idComDev; PWOWPORT pWOWPort; PCOMDEB16 pDEB16; register PGETCOMMEVENTMASK16 parg16; GETARGPTR(pFrame, sizeof(GETCOMMEVENTMASK16), parg16); idComDev = UINT32(parg16->f1); if (VALIDCOM(idComDev)) { if(pWOWPort = PortTab[idComDev].pWOWPort) { if(pDEB16 = pWOWPort->lpComDEB16) { // in Win3.1 the app gets current event word (NOT the EvtMask!!) ul = (ULONG)pDEB16->EvtWord; // clear event word like Win3.1 does dwEvtMask = (DWORD)WORD32(parg16->f2); pDEB16->EvtWord = LOWORD((~dwEvtMask) & (DWORD)ul); } } } FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // 0 for success. // -1 for bad idComDev. // IE_NOPEN for not opened. ULONG FASTCALL WU32GetCommState(PVDMFRAME pFrame) { ULONG ul = (ULONG)-1; UINT idComDev; DCB dcb32; PWOWPORT pWOWPort; PDCB16 pdcb16; register PGETCOMMSTATE16 parg16; GETARGPTR(pFrame, sizeof(GETCOMMSTATE16), parg16); idComDev = UINT32(parg16->f1); if (pWOWPort = GETPWOWPTR(idComDev)) { GETMISCPTR(parg16->f2, pdcb16); if(pdcb16) { if(VALIDCOM(idComDev)) { if(GetCommState(pWOWPort->h32, &dcb32)) { DCB32toDCB16(pdcb16, &dcb32, idComDev, pWOWPort->fChEvt); ul = 0; // Win3.1 returns 0 if success } } // else get DCB for LPT's else { RtlCopyMemory((PVOID)pdcb16, (PVOID)pWOWPort->pdcb16, sizeof(DCB16)); ul = 0; // Win3.1 returns 0 if success } FLUSHVDMPTR(parg16->f2, sizeof(DCB16), pdcb16); FREEMISCPTR(pdcb16); } } // else if they got a handle that looks good but they didn't open the port else if(VALIDCOM(idComDev) || VALIDLPT(idComDev)) { ul = (ULONG)IE_NOPEN; } #ifdef DEBUG if(!(ul==0)) { LOGDEBUG(0,("WOW::WU32GetCommState: failed\n")); } #endif FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // An idComDev on success. // IE_BADID for bad port name. // IE_OPEN if port already open. // IE_HARDWARE if hardware in use (ie. by mouse) OR port doesn't exist. // IE_MEMORY if both cbInQueue & cbOutQueue == 0 OR can't allocate a queue. // IE_NOPEN if can't open port. // IE_DEFAULT if initialization fails for various reasons. // We pass an additional (4th) parameter from IOpenComm() for SetCommEventMask() // support. It's a DWORD that is obtained by a call to GlobalDosAlloc(). ULONG FASTCALL WU32OpenComm(PVDMFRAME pFrame) { INT ret; UINT iTab, idComDev; CHAR COMbuf[] = "COMx:9600,E,7,1"; // Win3.1 default CHAR szPort[MAXCOMNAMENULL]; DWORD dwDEBAddr; DWORD cbInQ = 0; DWORD cbOutQ; HANDLE h32 = 0; HANDLE hREvent = 0; DCB dcb32; PSZ psz1; PDCB16 pdcb16 = NULL; PWOWPORT pWOWPort; PCOMDEB16 lpComDEB16; COMMTIMEOUTS ct; PUCHAR pchWriteBuf = NULL; UINT cbWriteBuf = 0; HANDLE hWriteEvent = 0; DWORD dwWriteThreadId; BOOL fIsLPTPort; register POPENCOMM16 parg16; GETARGPTR(pFrame, sizeof(OPENCOMM16), parg16); GETPSZPTR(parg16->f1, psz1); // see if valid com device name... if((iTab = GetModePortTabIndex(psz1)) == (UINT)IE_BADID) { ret = IE_BADID; goto ErrorExit0; } // check if named port is already in use if(PortTab[iTab].pWOWPort != NULL) { ret = IE_OPEN; goto ErrorExit0; } if ( VALIDCOM(iTab) ) { idComDev = iTab; fIsLPTPort = FALSE; } else { idComDev = TABIDTOLPT(iTab); fIsLPTPort = TRUE; } // get port name: app may pass in a full mode string in Win3.1 GetPortName(psz1, szPort); // try to open the port if((h32 = CreateFile(szPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL)) == INVALID_HANDLE_VALUE) { if(GetLastError() == ERROR_FILE_NOT_FOUND) { ret = IE_HARDWARE; } else { LOGDEBUG (LOG_ERROR,("WOW::WU32OpenComm CreateFile failed, lasterror=0x%x\n",GetLastError())); ret = IE_NOPEN; } goto ErrorExit0; } // ignore LPT's for this check like Win3.1 does if( !fIsLPTPort ) { // common method apps use to see if a COM port is already open if((WORD32(parg16->f2) == 0) && (WORD32(parg16->f3) == 0)) { ret = IE_MEMORY; goto ErrorExit1; } // set up the I/O queues cbInQ = (DWORD)WORD32(parg16->f2); cbOutQ = (DWORD)WORD32(parg16->f3); // // Allocate write buffer to emulate Win3.1's transmit queue. // We allocate one extra byte because the last byte of the // buffer is never filled. If it were, then the head and // tail pointers would be equal, which we use to indicate // an *empty* buffer. // cbWriteBuf = cbOutQ + 1; if (!(pchWriteBuf = malloc_w(cbWriteBuf))) { ret = IE_MEMORY; goto ErrorExit1; } // // IO buffers must be a multiple of 2 for SetupComm(). // Note that SetupComm may ignore the write buffer size // entirely, but TonyE says that we should still pass // down the size requested, since in any case writes // will complete only when the bits are irretrievably // sent, I.E. in the UART or other hardware, out of // the control of the device driver. // cbInQ = (cbInQ + 1) & ~1; cbOutQ = (cbOutQ + 1) & ~1; if(!SetupComm(h32, cbInQ, cbOutQ)) { ret = IE_MEMORY; goto ErrorExit2; } // // Create an event used by the app thread to wake up // the writer thread when the write buffer is // empty and the app writes something. The event // is auto-reset, meaning it is reset when the // writer wakes up. The event is initially not // signaled, it will be signaled when the first // write occurs. // if (!(hWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL))) { ret = IE_MEMORY; goto ErrorExit2; } // // create an event for ReadComm()'s overlapped structure // if(!(hREvent = CreateEvent(NULL, TRUE, FALSE, NULL))) { ret = IE_NOPEN; goto ErrorExit3; } // set the timeout values ct.ReadIntervalTimeout = (DWORD)INFINITE; // == MAXDWORD ct.ReadTotalTimeoutMultiplier=0; ct.ReadTotalTimeoutConstant=0; ct.WriteTotalTimeoutMultiplier=0; ct.WriteTotalTimeoutConstant=WRITE_TIMEOUT; if(!SetCommTimeouts(h32, &ct)) { ret = IE_DEFAULT; goto ErrorExit3; } // make sure the DCB is Win3.1 compatible // NOTE: app can pass in a full mode string in Win3.1 if((strlen(psz1) < 4) || !InitDCB32(&dcb32, psz1)) { if(!InitDCB32(&dcb32, COMbuf)) { ret = IE_DEFAULT; goto ErrorExit3; } } // set current DCB to Win3.1 compatibility if(!SetCommState(h32, &dcb32)) { ret = IE_DEFAULT; goto ErrorExit3; } // purge the I/O buffers just to be sure PurgeComm(h32, PURGE_TXCLEAR); PurgeComm(h32, PURGE_RXCLEAR); } // we need to set up a default DCB for LPT's else { if((pdcb16 = malloc_w(sizeof(DCB16))) == NULL) { ret = IE_DEFAULT; goto ErrorExit1; } // initialize everything to 0 RtlZeroMemory((PVOID)pdcb16, sizeof(DCB16)); // save the idComDev only in the DCB pdcb16->Id = LOBYTE(LOWORD(idComDev)); } // allocate the WOWPort structure for this port if((pWOWPort = malloc_w(sizeof(WOWPORT))) == NULL) { ret = IE_DEFAULT; goto ErrorExit3; } // get seg:sel dword returned by GlobalDosAlloc for the DEB struct // we'll treat the 16:16 pDEB as real mode on 32-bit side due to // some MIPS issues: v-simonf if (!(dwDEBAddr = DWORD32(parg16->f4))) { ret = IE_MEMORY; goto ErrorExit4; } // Isolate the segment value dwDEBAddr &= 0xFFFF0000; // save flat pointer to DEB for use in Modem interrupt thread lpComDEB16 = (PCOMDEB16) GetRModeVDMPointer(dwDEBAddr); // init the DEB InitDEB16(lpComDEB16, iTab, WORD32(parg16->f2), WORD32(parg16->f3)); // init the support struct RtlZeroMemory((PVOID)pWOWPort, sizeof(WOWPORT)); pWOWPort->h32 = h32; pWOWPort->idComDev = idComDev; pWOWPort->dwComDEB16 = DWORD32(parg16->f4); pWOWPort->lpComDEB16 = lpComDEB16; pWOWPort->dwThreadID = CURRENTPTD()->dwThreadID; pWOWPort->hREvent = hREvent; pWOWPort->cbWriteBuf = (WORD)cbWriteBuf; pWOWPort->cbWriteFree = cbWriteBuf - 1; // never use byte before head. pWOWPort->pchWriteBuf = pchWriteBuf; pWOWPort->pchWriteHead = pchWriteBuf; pWOWPort->pchWriteTail = pchWriteBuf; pWOWPort->hWriteEvent = hWriteEvent; pWOWPort->cbWritePending = 0; InitializeCriticalSection(&pWOWPort->csWrite); pWOWPort->pdcb16 = pdcb16; pWOWPort->cbInQ = cbInQ; // hack for QuickLink Gold 1.3 -- See bug #398011 // save QL stack sel in hiword, ComDEB16 seg in the loword if(IsQLinkGold(pFrame->wTDB)) { pWOWPort->QLStackSeg = (DWORD32(parg16->f1) & 0xFFFF0000) | (pWOWPort->dwComDEB16 & 0x0000FFFF); } // else pWOWPort->QLStackSeg implicitly set to 0 by RtlZeroMemory above. if (!(pWOWPort->olWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) { LOGDEBUG(0, ("%s", "WU32OpenComm unable to create overlapped write event, failing.\n")); ret = IE_MEMORY; goto ErrorExit4; } PortTab[iTab].pWOWPort = pWOWPort; // // Create the writer thread and pass it pWOWPort as its // parameter. // if (!fIsLPTPort) { pWOWPort->hWriteThread = CreateThread( NULL, // lpsa 0, // stack size (default) WOWCommWriterThread, // start address pWOWPort, // lpvThreadParm 0, // fdwCreate &dwWriteThreadId ); if (!pWOWPort->hWriteThread) { ret = IE_MEMORY; goto ErrorExit5; } } ret = idComDev; // return the idComDev goto CleanExit; // this is the error code path ErrorExit5: CloseHandle(pWOWPort->olWrite.hEvent); ErrorExit4: free_w(pWOWPort); ErrorExit3: if (hREvent) { CloseHandle(hREvent); } if (hWriteEvent) { CloseHandle(hWriteEvent); } if (fIsLPTPort) { free_w(pdcb16); } ErrorExit2: if(pchWriteBuf) { free_w(pchWriteBuf); } ErrorExit1: CloseHandle(h32); ErrorExit0: LOGDEBUG (0, ("WOW::WU32OpenComm failed\n")); CleanExit: FREEVDMPTR(psz1); FREEARGPTR(parg16); RETURN((ULONG)ret); // return error } // // WriteComm() // // Win3.1 returns: // # bytes written on success (*= -1 on error). // 0 for bad idComDev OR if app specifies to write 0 bytes. // -1 if port hasn't been opened, // ULONG FASTCALL WU32WriteComm(PVDMFRAME pFrame) { register PWRITECOMM16 parg16; LONG i = -1; PSZ psz2; PWOWPORT pwp; UINT idComDev; PWOWPORT pWOWPort; DWORD cbWritten; GETARGPTR(pFrame, sizeof(WRITECOMM16), parg16); GETPSZPTR(parg16->f2, psz2); idComDev = UINT32(parg16->f1); // this will be true only if the (valid) port has been opened if (pWOWPort = GETPWOWPTR(idComDev)) { if(VALIDCOM(idComDev)) { if ((pwp = GETPWOWPTR(UINT32(parg16->f1))) && psz2) { // if the app is interested in timeouts... if(pwp->lpComDEB16->MSRMask) { // ...see if RLSD, CTS, & DSR timeout before going high if(MSRWait(pwp)) { FREEPSZPTR(psz2); FREEARGPTR(parg16); return(0); // this is what Win3.1 does for Timeouts } } i = EnqueueCommWrite(pwp, psz2, parg16->f3); if (i != parg16->f3) { i = -i; pwp->dwErrCode |= CE_TXFULL; } } } // else LPT's go this way... else { // // This call to WriteFile could block, but I don't think // that's a problem. - DaveHart // if ((pwp = GETPWOWPTR(UINT32(parg16->f1))) && psz2) { if (!WriteFile(pwp->h32, psz2, parg16->f3, &cbWritten, &pwp->olWrite)) { if (ERROR_IO_PENDING == GetLastError() ) { // // Wait for the write to complete or for us to // be alerted that the port is closing. // if (GetOverlappedResult(pwp->h32, &pwp->olWrite, &cbWritten, TRUE )) { i = cbWritten; goto WriteSuccess; } } LOGDEBUG(0, ("WU32WriteComm: WriteFile to id %u fails (error %u)\n", pwp->idComDev, GetLastError())); if (cbWritten) { i = cbWritten; i = -i; } } else { i = cbWritten; } } } } else if(!(VALIDCOM(idComDev) || VALIDLPT(idComDev))) { i = 0; } WriteSuccess: FREEPSZPTR(psz2); FREEARGPTR(parg16); RETURN((ULONG)i); } // Win3.1 returns: // # chars read on success. // 0 for: bad idComDev, cbRead == 0, LPTx, port not open, 0 chars read, // OR for general comm error. ULONG FASTCALL WU32ReadComm(PVDMFRAME pFrame) { ULONG ul = 0; ULONG cb; BOOL fUnGet = FALSE; UINT idComDev; PBYTE pb2; PWOWPORT pWOWPort; OVERLAPPED Rol; register PREADCOMM16 parg16; GETARGPTR(pFrame, sizeof(READCOMM16), parg16); GETMISCPTR(parg16->f2, pb2); cb = (ULONG)UINT32(parg16->f3); if((cb != 0) && pb2) { idComDev = UINT32(parg16->f1); if (VALIDCOM(idComDev) && (pWOWPort = PortTab[idComDev].pWOWPort)) { // if an UnGot char is pending if (pWOWPort->fUnGet) { fUnGet = TRUE; pWOWPort->fUnGet = FALSE; *pb2++ = pWOWPort->cUnGet; // this line commented out 8/3/95 // cb--; // we now need one less char // In order to make this work correctly we should cb-- above // to reflect the ungot char, unfortunately Win3.1 & Win95 // don't do that so we will maintain this bug for "ouch!" // compatibility. a-craigj 8/3/95 } // TonyE claims we should do this before each read to avoid problems Rol.Internal = 0; Rol.InternalHigh = 0; Rol.Offset = 0; Rol.OffsetHigh = 0; Rol.hEvent = pWOWPort->hREvent; if (!ReadFile(pWOWPort->h32, pb2, cb, (LPDWORD)&ul, &Rol)) { if (ERROR_IO_PENDING == GetLastError()) { if (!GetOverlappedResult(pWOWPort->h32, &Rol, &ul, TRUE )) { LOGDEBUG(0, ("WOW::WU32ReadComm:GetOverlappedResult failed, error = 0x%x\n", GetLastError())); ul = 0; } } else { LOGDEBUG(0, ("WOW::WU32ReadComm:ReadFile failed, error = 0x%x\n", GetLastError())); ul = 0; } } if(fUnGet) { ul++; // account for ungot char pb2--; // accounts for previous pb2++ for FREEVDMPTR } FLUSHVDMPTR(parg16->f2, (USHORT)ul, pb2); } FREEVDMPTR(pb2); } FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // Error word on success OR LPTx. // 0x8000 on bad idComDev. ULONG FASTCALL WU32SetCommBreak(PVDMFRAME pFrame) { ULONG ul = 0x00008000; UINT idComDev; PWOWPORT pWOWPort; register PSETCOMMBREAK16 parg16; GETARGPTR(pFrame, sizeof(SETCOMMBREAK16), parg16); idComDev = UINT32(parg16->f1); if (pWOWPort = GETPWOWPTR(idComDev)) { if(VALIDCOM(idComDev)) { if(!SetCommBreak(pWOWPort->h32)) { WOWGetCommError(pWOWPort); } } ul = pWOWPort->dwErrCode; // Win3.1 returns last err } #ifdef DEBUG if(!(ul!=CE_MODE)) { LOGDEBUG(0,("WOW::WU32SetCommBreak: failed\n")); } #endif FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // A 16:16 ptr into the DEB struct on success. // 0 on any error OR LPT. // The 16:16 ptr that we return to the app was actually obtained in // IOpenComm() in user.exe. ULONG FASTCALL WU32SetCommEventMask(PVDMFRAME pFrame) { ULONG ul = 0; BOOL fOK = TRUE; UINT idComDev; DWORD dwDEBAddr; PWOWPORT pWOWPort; register PSETCOMMEVENTMASK16 parg16; GETARGPTR(pFrame, sizeof(SETCOMMEVENTMASK16), parg16); idComDev = UINT32(parg16->f1); if ((VALIDCOM(idComDev)) && (pWOWPort = PortTab[idComDev].pWOWPort)) { // if the Modem interrupt thread hasn't been started yet -- go start it if(pWOWPort->hMiThread == NULL) { // start our Modem interrupt thread if(!WOWStartModemIntThread(pWOWPort)) { fOK = FALSE; } } // if everything is hunky-dory... if(fOK) { // success: Win3.1 returns 16:16 protect mode ptr to // DEB->EvtWord (some apps subtract offset of EvtWord // from ptr to get start of DEB). dwDEBAddr = LOWORD(pWOWPort->dwComDEB16) << 16; ul = dwDEBAddr + FIELD_OFFSET(COMDEB16, EvtWord); // save the mask the app requested pWOWPort->lpComDEB16->EvtMask = (WORD)(parg16->f2); } } #ifdef DEBUG if(!(ul!=0)) { LOGDEBUG(0,("WOW::WU32SETCOMMEVENTMASK: failed\n")); } #endif FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // 0 on success OR LPTx. // IE_BADID for bad idComDev. // IE_NOPEN if file hasn't been opened. // IE_BAUDRATE for bad baud rate. // IE_BYTESIZE for bad byte size. // IE_DEFAULT for bad parity or stop bits. ULONG FASTCALL WU32SetCommState(PVDMFRAME pFrame) { ULONG ul = (ULONG)IE_BADID; UINT idComDev; PDCB16 pdcb16; DCB dcb32; PWOWPORT pWOWPort; register PSETCOMMSTATE16 parg16; DWORD dwMSR; GETARGPTR(pFrame, sizeof(SETCOMMSTATE16), parg16); GETMISCPTR(parg16->f1, pdcb16); if(pdcb16) { idComDev = pdcb16->Id; if(pWOWPort = GETPWOWPTR(idComDev)) { if(VALIDCOM(idComDev)) { DCB16toDCB32(pWOWPort, &dcb32, pdcb16); if(SetCommState(pWOWPort->h32, &dcb32)) { ul = 0; // Win 3.1 initializes the MSRShadow during SetCommState // so we will too. InterNet in a Box Dialer depends on it. GetCommModemStatus(pWOWPort->h32, &dwMSR); dwMSR &= MSR_STATEONLY; pWOWPort->lpComDEB16->MSRShadow = LOBYTE(LOWORD(dwMSR)); } else { ul = (ULONG)IE_DEFAULT; // we just say something's wrong } } else { RtlCopyMemory((PVOID)pWOWPort->pdcb16, (PVOID)pdcb16, sizeof(DCB16)); ul = 0; } } // else if they got a handle that looks good but they didn't open port else if (VALIDCOM(idComDev) || VALIDLPT(idComDev)) { ul = (ULONG)IE_NOPEN; } FREEMISCPTR(pdcb16); } FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // 0 for success. // 0x8000 for bad idComDev. // 0x4000 if char can't be sent. ULONG FASTCALL WU32TransmitCommChar(PVDMFRAME pFrame) { ULONG ul = 0x8000; UINT idComDev; CHAR ch; PWOWPORT pWOWPort; DWORD cbWritten; register PTRANSMITCOMMCHAR16 parg16; GETARGPTR(pFrame, sizeof(TRANSMITCOMMCHAR16), parg16); idComDev = UINT32(parg16->f1); if (pWOWPort = GETPWOWPTR(idComDev)) { if(VALIDCOM(idComDev)) { if(TransmitCommChar(pWOWPort->h32, CHAR32(parg16->f2))) { ul = 0; // Win3.1 returns 0 on success } else { ul = (ULONG)ERR_XMIT; } } // else LPT's go this way... else { // // This call to WriteFile could block, but I don't think // that's a problem. - DaveHart // ch = CHAR32(parg16->f2); ul = ERR_XMIT; if (pWOWPort = GETPWOWPTR(UINT32(parg16->f1))) { if (!WriteFile(pWOWPort->h32, &ch, 1, &cbWritten, &pWOWPort->olWrite)) { if (ERROR_IO_PENDING == GetLastError() ) { // // Wait for the write to complete or for us to // be alerted that the port is closing. // if (GetOverlappedResult(pWOWPort->h32, &pWOWPort->olWrite, &cbWritten, TRUE )) { ul = 0; goto TransmitSuccess; } } LOGDEBUG(0, ("WU32TransmitCommChar: WriteFile to id %u fails (error %u)\n", pWOWPort->idComDev, GetLastError())); } else { ul = 0; } } } } TransmitSuccess: FREEARGPTR(parg16); RETURN(ul); } // Win3.1 returns: // 0 on success OR bad idComDev OR LPTx. // -1 if port not open OR if ungot char already pending. ULONG FASTCALL WU32UngetCommChar(PVDMFRAME pFrame) { ULONG ul = (ULONG)-1; UINT idComDev; PWOWPORT pWOWPort; register PUNGETCOMMCHAR16 parg16; GETARGPTR(pFrame, sizeof(UNGETCOMMCHAR16), parg16); // see if port open... idComDev = UINT32(parg16->f1); if (VALIDCOM(idComDev)) { if (pWOWPort = PortTab[idComDev].pWOWPort) { // if ungot char already pending return -1 if(pWOWPort->fUnGet == FALSE) { pWOWPort->fUnGet = TRUE; pWOWPort->cUnGet = CHAR32(parg16->f2); ul = 0; } } } else { ul = 0; } #ifdef DEBUG if(!(ul==0)) { LOGDEBUG(0,("WOW::WU32UngetCommChar: failed\n")); } #endif FREEARGPTR(parg16); RETURN(ul); } DWORD Baud16toBaud32(UINT BaudRate) { UINT DLatch; // this function is set up this way on purpose (see SetCom300 ibmsetup.asm) // get the equivalent baud switch(BaudRate) { // it they specified the baud rate directly case CBR_110: case CBR_300: case CBR_600: case CBR_1200: case CBR_2400: case CBR_4800: case CBR_9600: case CBR_19200: case CBR_14400: case CBR_38400: case CBR_56000: return(BaudRate); // Win3.1 baud rate constants case W31CBR_110: return(CBR_110); case W31CBR_300: return(CBR_300); case W31CBR_600: return(CBR_600); case W31CBR_1200: return(CBR_1200); case W31CBR_2400: return(CBR_2400); case W31CBR_4800: return(CBR_4800); case W31CBR_9600: return(CBR_9600); case W31CBR_19200: return(CBR_19200); case W31CBR_14400: return(CBR_14400); case W31CBR_38400: return(CBR_38400); case W31CBR_56000: return(CBR_56000); // start special cases // SmartCom uses this to get 115200 case W31CBR_115200: return(CBR_115200); // Win3.1 fails these two (even though they're defined in windows.h) // but they just might work on NT case W31CBR_128000: return(CBR_128000); case W31CBR_256000: return(CBR_256000); // end special cases // handle the blank table entries for "reserved" case W31CBR_reserved1: case W31CBR_reserved2: case W31CBR_reserved3: case W31CBR_reserved4: case W31CBR_reserved5: return(0); // avoid divide by zero case 0: case 1: return(0); // handle obscure specifications that will work in Win3.1 default: // get the integer divisor latch value DLatch = CBR_115200 / BaudRate; switch(DLatch) { case W31_DLATCH_110: return(CBR_110); case W31_DLATCH_300: return(CBR_300); case W31_DLATCH_600: return(CBR_600); case W31_DLATCH_1200: return(CBR_1200); case W31_DLATCH_2400: return(CBR_2400); case W31_DLATCH_4800: return(CBR_4800); case W31_DLATCH_9600: return(CBR_9600); case W31_DLATCH_19200: return(CBR_19200); case W31_DLATCH_14400: return(CBR_14400); case W31_DLATCH_38400: return(CBR_38400); case W31_DLATCH_56000: return(CBR_56000); case W31_DLATCH_115200: return(CBR_115200); // Win3.1, anything else returns whatever DLatch happens to be // since we're mapping to baud we return the specified baud default: return(BaudRate); } } } WORD Baud32toBaud16(DWORD BaudRate) { if(BaudRate >= CBR_115200) { switch(BaudRate) { case CBR_256000: return(W31CBR_256000); case CBR_128000: return(W31CBR_128000); case CBR_115200: default: return(W31CBR_115200); } } else { return(LOWORD(BaudRate)); } } void DCB16toDCB32(PWOWPORT pWOWPort, LPDCB lpdcb32, PDCB16 pdcb16) { // zero 32-bit struct -> any flags and fields not explicitly set will be 0 RtlZeroMemory((PVOID)lpdcb32, sizeof(DCB)); lpdcb32->DCBlength = sizeof(DCB); lpdcb32->BaudRate = Baud16toBaud32(pdcb16->BaudRate); // 16-bit bitfields may align differently with 32-bit compilers // we use this mechanism to align them the way Win3.1 expects them if(pdcb16->wFlags & W31DCB_fBinary) lpdcb32->fBinary = 1; if(pdcb16->wFlags & W31DCB_fParity) lpdcb32->fParity = 1; if(pdcb16->wFlags & W31DCB_fOutxCtsFlow) lpdcb32->fOutxCtsFlow = 1; if(pdcb16->wFlags & W31DCB_fOutxDsrFlow) lpdcb32->fOutxDsrFlow = 1; // set up mechanism for handling event char notification if(pdcb16->wFlags & W31DCB_fChEvt) pWOWPort->fChEvt = TRUE; if(pdcb16->wFlags & W31DCB_fDtrFlow) { lpdcb32->fDtrControl = DTR_CONTROL_HANDSHAKE; } else if(pdcb16->wFlags & W31DCB_fDtrDisable) { lpdcb32->fDtrControl = DTR_CONTROL_DISABLE; } else { lpdcb32->fDtrControl = DTR_CONTROL_ENABLE; } if(pdcb16->wFlags & W31DCB_fOutX) lpdcb32->fOutX = 1; if(pdcb16->wFlags & W31DCB_fInX) lpdcb32->fInX = 1; if(pdcb16->wFlags & W31DCB_fPeChar) lpdcb32->fErrorChar = 1; if(pdcb16->wFlags & W31DCB_fNull) lpdcb32->fNull = 1; if(pdcb16->wFlags & W31DCB_fRtsFlow) { lpdcb32->fRtsControl = RTS_CONTROL_HANDSHAKE; } else if(pdcb16->wFlags & W31DCB_fRtsDisable) { lpdcb32->fRtsControl = RTS_CONTROL_DISABLE; } else { lpdcb32->fRtsControl = RTS_CONTROL_ENABLE; } if(pdcb16->wFlags & W31DCB_fDummy2) lpdcb32->fDummy2 = 1; // Check the passed in XonLim & XoffLim values against the cbInQ value. // Prodigy's modem detector leaves these values uninitialized. if ((pdcb16->XonLim >= pWOWPort->cbInQ) || (pdcb16->XoffLim > pWOWPort->cbInQ) || (pdcb16->XonLim >= pdcb16->XoffLim)) { lpdcb32->XonLim = 0; lpdcb32->XoffLim = (WORD)(pWOWPort->cbInQ - (pWOWPort->cbInQ >> 2)); } else { lpdcb32->XonLim = pdcb16->XonLim; lpdcb32->XoffLim = pdcb16->XoffLim; } lpdcb32->ByteSize = pdcb16->ByteSize; lpdcb32->Parity = pdcb16->Parity; lpdcb32->StopBits = pdcb16->StopBits; // Digiboard driver doesn't want to see XonChar == XoffChar even if // xon/xoff is disabled. if ((pdcb16->XonChar == '\0') && (lpdcb32->XoffChar == '\0')) { lpdcb32->XonChar = pdcb16->XonChar+1; } else { lpdcb32->XonChar = pdcb16->XonChar; } lpdcb32->XoffChar = pdcb16->XoffChar; lpdcb32->ErrorChar = pdcb16->PeChar; lpdcb32->EofChar = pdcb16->EofChar; lpdcb32->EvtChar = pdcb16->EvtChar; #ifdef FE_SB // for MSKKBUG #3213 by v-kenich // MYTALK for Win set NULL these two fields at transfering binary file // If call SetCommstate as it is, SetCommState return error (Invalid parameter) // I think this fix doesn't occur any bad thing without condition of MYTALK // Really correcting parameter check is better. but I don't know where it is. if (!lpdcb32->XonChar) lpdcb32->XonChar = 0x11; if (!lpdcb32->XoffChar) lpdcb32->XoffChar = 0x13; #endif // FE_SB // set up for RLSD, CTS, and DSR timeout support (not supported on NT) pWOWPort->lpComDEB16->MSRMask = 0; pWOWPort->RLSDTimeout = pdcb16->RlsTimeout; if(pWOWPort->RLSDTimeout != IGNORE_TIMEOUT) pWOWPort->lpComDEB16->MSRMask |= LOBYTE(MS_RLSD_ON); pWOWPort->CTSTimeout = pdcb16->CtsTimeout; if(pWOWPort->CTSTimeout != IGNORE_TIMEOUT) pWOWPort->lpComDEB16->MSRMask |= LOBYTE(MS_CTS_ON); pWOWPort->DSRTimeout = pdcb16->DsrTimeout; if(pWOWPort->DSRTimeout != IGNORE_TIMEOUT) pWOWPort->lpComDEB16->MSRMask |= LOBYTE(MS_DSR_ON); // these fields remain 0 //lpdcb32->fDsrSensitivity = 0; //lpdcb32->fTXContinueOnXoff = 0; //lpdcb32->fAbortOnError = 0; //lpdcb32->wReserved = 0; } void DCB32toDCB16(PDCB16 pdcb16, LPDCB lpdcb32, UINT idComDev, BOOL fChEvt) { // zero 16-bit struct -> any flags and fields not explicitly set will be 0 RtlZeroMemory((PVOID)pdcb16, sizeof(DCB16)); // set this field no matter what pdcb16->Id = (BYTE)idComDev; // if a COMx (Win3.1 leaves the rest 0 for LPT's) if(VALIDCOM(idComDev)) { pdcb16->Id = (BYTE)idComDev; // these are the "ComX:96,n,8,1" fields pdcb16->BaudRate = Baud32toBaud16(lpdcb32->BaudRate); pdcb16->ByteSize = lpdcb32->ByteSize; pdcb16->Parity = lpdcb32->Parity; pdcb16->StopBits = lpdcb32->StopBits; // 16-bit bitfields may align differently with 32-bit compilers // we use this mechanism to align them the way Win3.1 expects them if(lpdcb32->fBinary) pdcb16->wFlags |= W31DCB_fBinary; if(lpdcb32->fRtsControl == RTS_CONTROL_DISABLE) { pdcb16->wFlags |= W31DCB_fRtsDisable; } if(lpdcb32->fParity) pdcb16->wFlags |= W31DCB_fParity; if(lpdcb32->fOutxCtsFlow) pdcb16->wFlags |= W31DCB_fOutxCtsFlow; if(lpdcb32->fOutxDsrFlow) pdcb16->wFlags |= W31DCB_fOutxDsrFlow; if(lpdcb32->fDtrControl == DTR_CONTROL_DISABLE) { pdcb16->wFlags |= W31DCB_fDtrDisable; } if(lpdcb32->fOutX) pdcb16->wFlags |= W31DCB_fOutX; if(lpdcb32->fInX) pdcb16->wFlags |= W31DCB_fInX; if(lpdcb32->fErrorChar) pdcb16->wFlags |= W31DCB_fPeChar; if(lpdcb32->fNull) pdcb16->wFlags |= W31DCB_fNull; if(fChEvt) pdcb16->wFlags |= W31DCB_fChEvt; if(lpdcb32->fDtrControl == DTR_CONTROL_HANDSHAKE) { pdcb16->wFlags |= W31DCB_fDtrFlow; } if(lpdcb32->fRtsControl == RTS_CONTROL_HANDSHAKE) { pdcb16->wFlags |= W31DCB_fRtsFlow; } if(lpdcb32->fDummy2) pdcb16->wFlags |= W31DCB_fDummy2; pdcb16->XonChar = lpdcb32->XonChar; pdcb16->XoffChar = lpdcb32->XoffChar; pdcb16->XonLim = lpdcb32->XonLim; pdcb16->XoffLim = lpdcb32->XoffLim; pdcb16->PeChar = lpdcb32->ErrorChar; pdcb16->EofChar = lpdcb32->EofChar; pdcb16->EvtChar = lpdcb32->EvtChar; } // these fields remain 0 //pdcb16->fDummy = 0; //pdcb16->TxDelay = 0; } BOOL DeletePortTabEntry(PWOWPORT pWOWPort) { INT iTab; BOOL fTimeOut; iTab = pWOWPort->idComDev; if(VALIDLPT(iTab)) { iTab = GETLPTID(iTab); } // flush I/O buffers & attempt to wake up Modem Interrupt thread (if any) pWOWPort->fClose = TRUE; if(VALIDCOM(iTab)) { PurgeComm(pWOWPort->h32, PURGE_TXCLEAR); PurgeComm(pWOWPort->h32, PURGE_RXCLEAR); SetCommMask(pWOWPort->h32, 0); // this should wake up the Mi thread // wake up WOWModemIntThread & tell it to exit // (we attempt to block (1.5 second max.) until it does) if(pWOWPort->hMiThread) { WaitForSingleObject(pWOWPort->hMiThread, 1500); CloseHandle(pWOWPort->hMiThread); // zero COMDEB RtlZeroMemory((PVOID)pWOWPort->lpComDEB16, sizeof(COMDEB16)); } // // Wake up WOWCommWriterThread so it will exit, wait up to // 5 sec for it to go away. // SetEvent(pWOWPort->hWriteEvent); fTimeOut = (WaitForSingleObject(pWOWPort->hWriteThread, 5000) == WAIT_TIMEOUT); #ifdef DEBUG if (fTimeOut) { LOGDEBUG(LOG_ALWAYS, ("WOW DeletePortTabEntry: Comm writer thread for port %d refused\n" " to die when asked nicely.\n", (int)pWOWPort->idComDev)); } #endif CloseHandle(pWOWPort->hWriteThread); CloseHandle(pWOWPort->hWriteEvent); free_w(pWOWPort->pchWriteBuf); CloseHandle(pWOWPort->hREvent); } // else free the LPT DCB support struct else { free_w(pWOWPort->pdcb16); CloseHandle(pWOWPort->olWrite.hEvent); fTimeOut = FALSE; } DeleteCriticalSection(&pWOWPort->csWrite); CloseHandle(pWOWPort->h32); // QuickLink Gold 1.3 hack. Bug #398011 // The app calls OpenComm(), & then SetCommEventMask() to get the ptr to the // comdeb16 struct. It saves the ptr at offset 0xf36 on its stack. The // problem is that the app holds onto the comdeb16 ptr after it calls // CloseComm() (when we free the comdeb16 memory) to be able to peek at a // status byte from time to time. This works OK on Win 3.1 but not with // our model on NT. Fortunately, the app tests to see if it has a comdeb16 // ptr before dereferencing it. Also, we're lucky because the ptr for // lpszDevControl in its call to OpenComm() is from its stack thus allowing // us to obtain the stack selector and zero out the comdeb16 ptr stored at // stack ss:0xf36 when the app calls CloseComm(). if(pWOWPort->QLStackSeg) { LPDWORD lpQLS; VPVOID vpQLS, vpCD16; // construct the 16:16 ptr to where the app saved the ptr to the // COMDEB16 struct on its stack at offset 0xf36 vpQLS = pWOWPort->QLStackSeg & 0xFFFF0000; vpQLS = vpQLS | 0x00000f36; GETMISCPTR(vpQLS, lpQLS); // construct realmode 16:16 ptr of the COMDEB16 struct + 0x38 (seg:0x38) vpCD16 = pWOWPort->QLStackSeg & 0x0000FFFF; vpCD16 = (vpCD16 << 16) | 0x00000038; if(lpQLS) { // sanity check to see if everything is still what & where we // think it is // if seg:0x38 is still stored at offset 0xf36 on the apps stack... if(*lpQLS == (DWORD)vpCD16) { // zero it out -- forcing app to avoid checking the status byte *lpQLS = 0; FLUSHVDMPTR(vpQLS, sizeof(DWORD), lpQLS); FREEMISCPTR(lpQLS); } } } free_w(pWOWPort); PortTab[iTab].pWOWPort = NULL; return(fTimeOut); } UINT GetModePortTabIndex(PSZ pszModeStr) { CHAR szPort[MAXCOMNAMENULL*2]; if(pszModeStr) { if(GetPortName(pszModeStr, szPort)) { return(GetStrPortTabIndex(szPort)); } } return((UINT)IE_BADID); } BOOL GetPortName(LPSTR pszMode, LPSTR pszPort) { INT len; CHAR szTemp[80]; // max len we'll take for DOS style MODE command BOOL bRet = FALSE; len = strlen(pszMode); if((len >= 3) && (len < 80)) { // Get the first token from the mode string. GetPortStringToken(pszMode, szTemp); // map "AUX" or "PRN" to "COM1" or "LPT1" if necessary len = strlen(szTemp); if((len >= 3) && (len <= MAXCOMNAME)) { // "AUX" <= len <= "COMx" strcpy(pszPort, szTemp); CharUpper(pszPort); // filter out duplicate names for the same thing if(!WOW32_strcmp(pszPort, "PRN")) { strcpy(pszPort, "LPT1"); } else if(!WOW32_strcmp(pszPort, "AUX")) { strcpy(pszPort, "COM1"); } bRet = TRUE; } } return(bRet); } PSZ StripPortName(PSZ psz) { CHAR dummy[80]; // max len we'll take for DOS style MODE command return(GetPortStringToken(psz, dummy)); } // // Copy first token to pszToken. Return pointer to next token or NULL if none. // This code cloned from Win 3.1, COMDEV.C, field(). HGW 3.0 modem registration // passes "COMx,,," instead of "COMx:,,," so we need to handle all seperators. // PSZ GetPortStringToken(PSZ pszSrc, PSZ pszToken) { char c; // While not the end of the string. while (c = *pszSrc) { pszSrc++; //Look for seperators. if ((c == ' ') || (c == ':') || (c == ',')) { *pszToken = '\0'; while (*pszSrc == ' ') { pszSrc++; } if (*pszSrc) { return(pszSrc); } return(NULL); } *pszToken++ = c; } *pszToken = '\0'; return(NULL); } UINT GetStrPortTabIndex(PSZ szPort) { UINT iTab; for(iTab = COM1; iTab < NUMPORTS; iTab++) { if(!WOW32_strcmp((LPCTSTR)PortTab[iTab].szPort, (LPCTSTR)szPort)) { return(iTab); } } return((UINT)IE_BADID); } BOOL InitDCB32(LPDCB pdcb32, LPSTR pszModeStr) { BOOL bRet = FALSE; LPSTR pszParams; // eliminate "COMx:" from mode string leaving ptr to parameters string pszParams = StripPortName(pszModeStr); // if there are params... (some apps pass "com1:" -- hence 2nd test) if(pszParams) { // initialize everything to 0 (especially the flags) RtlZeroMemory((PVOID)pdcb32, sizeof(DCB)); // NOTE: 32-bit BuildCommDCB ONLY touches fields associated with psz1 if(BuildCommDCB(pszParams, pdcb32)) { pdcb32->DCBlength = sizeof(DCB); // fill in specific fields a la Win3.1 // NOTE: fields are 0 unless explicitly set pdcb32->fBinary = 1; pdcb32->fDtrControl = DTR_CONTROL_ENABLE; //same as fDTRDisable == 0 pdcb32->fRtsControl = RTS_CONTROL_ENABLE; //same as fRTSDisable == 0 pdcb32->XonLim = 10; pdcb32->XoffLim = 10; pdcb32->XonChar = 0x11; // Ctrl-Q pdcb32->XoffChar = 0x13; // Ctrl-S bRet = TRUE; } } return(bRet); } VOID InitDEB16(PCOMDEB16 pComDEB16, UINT iTab, WORD QInSize, WORD QOutSize) { VPVOID vpBiosData; PWORD16 pwBiosData; // Win3.1 init's most the stuff to zero except as handled below RtlZeroMemory((PVOID)pComDEB16, sizeof(COMDEB16)); // get the I/O base address for the port vpBiosData = (VPVOID)(RM_BIOS_DATA + (iTab * sizeof(WORD))); if(pwBiosData = (PWORD16)GetRModeVDMPointer(vpBiosData)) { pComDEB16->Port = (WORD)*pwBiosData; FREEVDMPTR(pwBiosData); } pComDEB16->RecvTrigger = (WORD)-1; pComDEB16->QInSize = QInSize; pComDEB16->QOutSize = QOutSize; } /* start thread for Modem interrupt emulation */ BOOL WOWStartModemIntThread(PWOWPORT pWOWPort) { BOOL ret = FALSE; DWORD dwUnused; HANDLE hEvent, hMiThread; // set up temporary semaphore to sync with Modem interrupt thread if((hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) { goto ErrorExit0; } // use pWOWPort->hMiThread temporarily to help start the thread pWOWPort->hMiThread = hEvent; // create the MSR thread if((hMiThread = CreateThread(NULL, 8192, (LPTHREAD_START_ROUTINE)WOWModemIntThread, (PWOWPORT)pWOWPort, 0, (LPDWORD)&dwUnused)) == NULL) { goto ErrorExit1; } // block until thread notifies us that it has started WaitForSingleObject(hEvent, INFINITE); pWOWPort->hMiThread = hMiThread; CloseHandle(hEvent); ret = TRUE; goto FunctionExit; // this is the error code path ErrorExit1: CloseHandle(hEvent); ErrorExit0: pWOWPort->hMiThread = NULL; FunctionExit: #ifdef DEBUG if(!(ret)) { LOGDEBUG(0,("WOW::W32StartModemIntThread failed\n")); } #endif return(ret); } // Modem Interrupt thread for SetCommEventMask/EnableCommNotification support // Tries to emulate the interrupt handling in ibmint.asm of Win3.1 comm.drv. // Our "interrupts" here are the events from the NT serial comm stuff VOID WOWModemIntThread(PWOWPORT pWOWPort) { BOOL fRing = FALSE; UINT iTab; DWORD dwRing; DWORD dwEvts = 0; DWORD dwEvtOld = 0; DWORD dwEvtWord = 0; DWORD dwMSR = 0; DWORD dwErrCode = 0; DWORD cbTransfer; HANDLE h32; PCOMDEB16 lpComDEB16; OVERLAPPED ol; iTab = pWOWPort->idComDev; lpComDEB16 = pWOWPort->lpComDEB16; h32 = pWOWPort->h32; // set the current modem status & Event word lpComDEB16->MSRShadow = (BYTE)0; lpComDEB16->EvtWord = (WORD)0; lpComDEB16->ComErr = (WORD)0; lpComDEB16->QInCount = (WORD)0; lpComDEB16->QOutCount = (WORD)0; if(VALIDLPT(iTab)) { iTab = GETLPTID(iTab); } ol.Internal = 0; ol.InternalHigh = 0; ol.Offset = 0; ol.OffsetHigh = 0; ol.hEvent = CreateEvent(NULL, TRUE, FALSE, (LPTSTR)PortTab[iTab].szPort); // activate modem events in the mask, we want to emulate all the interrupts SetCommMask(h32, EV_NTEVENTS); // initialize the shadow MSR GetCommModemStatus(h32, &dwMSR); dwMSR &= MSR_STATEONLY; lpComDEB16->MSRShadow = LOBYTE(LOWORD(dwMSR)); // wake up the thread that created this thread in WOWStartModemIntThread() SetEvent(pWOWPort->hMiThread); while(!pWOWPort->fClose) { // wait for an event - hopefully this will be somewhat similar to // the TimerProc in ibmint.asm which gets called every 100ms if(!WaitCommEvent(h32, &dwEvts, &ol)) { if(GetLastError() == ERROR_IO_PENDING) { // ...block here 'til event specified in WaitCommEvent() occurs if(!GetOverlappedResult(h32, &ol, &cbTransfer, TRUE)) { LOGDEBUG(0, ("WOW::WUCOMM: WOWModemIntThread: Wait failed\n")); } } else { LOGDEBUG(0, ("WOW::WUCOMM: WOWModemIntThread : Overlap failed\n")); } } ResetEvent(ol.hEvent); // Get current MSR state, current state of delta bits isn't accurate for us GetCommModemStatus(h32, &dwMSR); dwMSR &= MSR_STATEONLY; // throw away delta bits // set the DELTA bits in the shadow MSR if(dwEvts & EV_CTS) dwMSR |= MSR_DCTS; if(dwEvts & EV_DSR) dwMSR |= MSR_DDSR; if(dwEvts & EV_RLSD) dwMSR |= MSR_DDCD; if(dwEvts & EV_RING) { fRing = TRUE; dwRing = EV_RING; } else if(fRing) { fRing = FALSE; dwMSR |= MSR_TERI; dwRing = EV_RingTe; } else { dwRing = 0; } // Form the events dwEvtOld = (DWORD)lpComDEB16->EvtWord; dwEvtWord = 0; dwEvtWord = dwRing | (dwEvts & (EV_ERR | EV_BREAK | EV_RXCHAR | EV_TXEMPTY | EV_CTS | EV_DSR | EV_RLSD | EV_RXFLAG)); // we have to figure the state bits out from the MSR if(dwMSR & MS_CTS_ON) dwEvtWord |= EV_CTSS; if(dwMSR & MS_DSR_ON) dwEvtWord |= EV_DSRS; if(dwMSR & MS_RLSD_ON) dwEvtWord |= EV_RLSDS; // One of the major tasks of this routine is to update the MSRShadow // and EvtWord in the COMDEB16 structure. // //apply the msr as well lpComDEB16->MSRShadow = LOBYTE(LOWORD(dwMSR)); // apply the event mask the app specified lpComDEB16->EvtWord |= LOWORD(dwEvtWord) & lpComDEB16->EvtMask; // The following code simluates the COM Notifcation functionality of // Win 3.1. // // Notifications: // // if they want receive transmit notification & it's time to notify // if there wasn't an Rx overflow continue... if( lpComDEB16->NotifyHandle ) { // get current error code & queue counts WOWGetCommError(pWOWPort); if((dwEvtWord & ( EV_RXCHAR | EV_RXFLAG )) && !(pWOWPort->dwErrCode & CE_RXOVER)) { // if they want receive notification & it's time to notify // apps should set RecvTrigger to -1 if they don't want notification if((((SHORT)lpComDEB16->RecvTrigger) != -1) && (lpComDEB16->QInCount >= lpComDEB16->RecvTrigger)) { // if the app hasn't already been notified of this ... if(!(lpComDEB16->NotifyFlags & CN_RECEIVEHI)) { PostMessage(HWND32(lpComDEB16->NotifyHandle), WOW_WM_COMMNOTIFY, MAKEWPARAM((WORD)pWOWPort->idComDev, 0), MAKELPARAM(CN_RECEIVE, 0)); lpComDEB16->NotifyFlags |= CN_RECEIVEHI; } } else { lpComDEB16->NotifyFlags &= ~CN_RECEIVEHI; } } // if they want receive transmit notification & it's time to notify if(lpComDEB16->QOutCount < (SHORT)lpComDEB16->SendTrigger) { // if the app hasn't already been notified of this ... if(!(lpComDEB16->NotifyFlags & CN_TRANSMITHI)) { PostMessage(HWND32(lpComDEB16->NotifyHandle), WOW_WM_COMMNOTIFY, MAKEWPARAM((WORD)pWOWPort->idComDev, 0), MAKELPARAM(CN_TRANSMIT, 0)); lpComDEB16->NotifyFlags |= CN_TRANSMITHI; } } else { lpComDEB16->NotifyFlags &= ~CN_TRANSMITHI; } // if we are notifying the app of EV_ event's if((lpComDEB16->NotifyFlags & CN_NOTIFYHI) && ((DWORD)lpComDEB16->EvtWord != dwEvtOld)) { PostMessage(HWND32(lpComDEB16->NotifyHandle), WOW_WM_COMMNOTIFY, MAKEWPARAM((WORD)pWOWPort->idComDev, 0), MAKELPARAM(CN_EVENT, 0)); } // Now that we've processed all the interrupts, do the TimerProc. // if we are notifying the app of anything in Rx queue // this mimics the notification in the TimerProc (see ibmint.asm) if(((SHORT)lpComDEB16->RecvTrigger != -1) && (lpComDEB16->QInCount != 0) && (!(lpComDEB16->NotifyFlags & CN_RECEIVEHI))) { PostMessage(HWND32(lpComDEB16->NotifyHandle), WOW_WM_COMMNOTIFY, MAKEWPARAM((WORD)pWOWPort->idComDev, 0), MAKELPARAM(CN_RECEIVE, 0)); lpComDEB16->NotifyFlags |= CN_RECEIVEHI; } } // we've handled all interrupts, give control back to app Sleep(0); } // end thread loop CloseHandle(ol.hEvent); ExitThread(0); } DWORD WOWGetCommError(PWOWPORT pwp) { COMSTAT cs; DWORD dwErr; ClearCommError(pwp->h32, &dwErr, &cs); EnterCriticalSection(&pwp->csWrite); // // We do our own write buffering so we ignore // the cbOutQue returned by ClearCommError, which // only reflects pending writes. // // Number of bytes in our write queue is calculated // using the size of the queue and the amount free // in the queue, minus one. Minus one because // there's one slot in the queue which is never used. // cs.cbOutQue = (pwp->cbWriteBuf - pwp->cbWriteFree) - 1; LeaveCriticalSection(&pwp->csWrite); // always update the status & preserve any error condition pwp->cs = cs; pwp->dwErrCode |= dwErr; pwp->lpComDEB16->ComErr |= LOWORD(dwErr); // always update the queue counts in the DEB pwp->lpComDEB16->QInCount = LOWORD(cs.cbInQue); pwp->lpComDEB16->QOutCount = LOWORD(cs.cbOutQue); return(dwErr); } /* for hung/crashed app support */ VOID FreeCommSupportResources(DWORD dwThreadID) { UINT iTab; PWOWPORT pWOWPort; for(iTab = 0; iTab < NUMPORTS; iTab++) { if(pWOWPort = PortTab[iTab].pWOWPort) { if(pWOWPort->dwThreadID == dwThreadID) { DeletePortTabEntry(pWOWPort); break; } } } } /* functions exported to the VDM */ /* NOTE: idComDev: COM1 == 0, LPT1 == 0x80 */ BYTE GetCommShadowMSR(WORD idComDev) { BYTE MSR=0; DWORD dwModemStatus; PWOWPORT pWOWPort; if (pWOWPort = GETPWOWPTR (idComDev)) { if(pWOWPort->hMiThread) { MSR = (BYTE)pWOWPort->lpComDEB16->MSRShadow; } // get it the slow way if SetCommEventMask() hasn't been called else if ( GetCommModemStatus(pWOWPort->h32, &dwModemStatus) ) { MSR = (BYTE)LOBYTE(LOWORD(dwModemStatus)); } } return(MSR); } /* NOTE: idComDev: COM1 == 0, LPT1 == 0x80 */ HANDLE GetCommHandle(WORD idComDev) { PWOWPORT pWOWPort; if (pWOWPort = GETPWOWPTR (idComDev)) { return(pWOWPort->h32); } else { return(NULL); // will return NULL if bad range of idComDev or if the } // port wasn't initialized through an OpenComm() API call } BOOL IsQLinkGold(WORD wTDB) { PTDB pTDB; pTDB = (PVOID)SEGPTR(wTDB,0); if(WOW32_stricmp(pTDB->TDB_ModName, "QLGOLD")) { return(FALSE); } return(TRUE); } // // EnqueueCommWrite - stuff characters into the Comm Write queue // assoicated with pWOWPort. // // Returns number of characters queued. // // This function takes care of entering/leaving the critsec. // USHORT EnqueueCommWrite(PWOWPORT pwp, PUCHAR pch, USHORT cb) { USHORT cbWritten = 0; USHORT cbToCopy; USHORT cbChunk; BOOL fQueueEmpty; BOOL fDelay = FALSE; // WinFax Lite 3 calls WriteFile("AT+FCLASS=1") to set the modem to fax mode // when it is recieving a fax. Some modems appear to be slow to repsond // with the "OK" string (especially since we enqueue the "AT+FCLASS=1" write // and then write it in overlapped mode) -- so, when we tell the app we sent // it, it then follows with a "ATA" string without waiting for the modem's // response to the previous command. This confuses several different modems // and so they never answer. This mechanism allows us to synchronize the // "AT+FCLASS=1" command so that it works more like Win3.1. See bug #9479 if(cb == 12) { // Handy way to say: // if(pch[0]=='A' && pch[1]=='T' && pch[2]=='+' && pch[3]=='F') { if((*(DWORD *)pch) == 0x462b5441) { // if(pch[0]=='C' && pch[1]=='L' && pch[2]=='A' && pch[3]=='S') { if((*(DWORD *)(pch+sizeof(DWORD))) == 0x53414c43) { // if(pch[0]=='S' && pch[1]=='=') { if((*(WORD *)(pch+(2*sizeof(DWORD)))) == 0x3D53) { fDelay = TRUE; } } } } EnterCriticalSection(&pwp->csWrite); fQueueEmpty = (pwp->pchWriteHead == pwp->pchWriteTail); // // cbToCopy is the total number of bytes that we are going to enqueue // cbToCopy = min(cb, pwp->cbWriteFree); // // Any write can be accomplished in at most two chunks. // The first writes up until the buffer wraps, while // the second starts at the beginning of the buffer. // // Do the first half, which may do it all. // // Number of bytes for the first chunk is the smaller of // the total number of bytes free in the write buffer and // the number of bytes free before the end of the buffer. // cbChunk = min(cbToCopy, (pwp->pchWriteBuf + pwp->cbWriteBuf) - pwp->pchWriteTail); RtlCopyMemory(pwp->pchWriteTail, pch, cbChunk); pwp->cbWriteFree -= cbChunk; pwp->pchWriteTail += cbChunk; cbWritten += cbChunk; // // Tail pointer may have moved to point just beyond the buffer. // if (pwp->pchWriteTail >= pwp->pchWriteBuf + pwp->cbWriteBuf) { WOW32ASSERT(pwp->pchWriteTail == pwp->pchWriteBuf + pwp->cbWriteBuf); pwp->pchWriteTail = pwp->pchWriteBuf; } // // Are we done? // if (cbWritten < cbToCopy) { // // I think this case should only be taken when we've wrapped, so // be sure. // WOW32ASSERT(pwp->pchWriteTail == pwp->pchWriteBuf); // // Nope, do the second half. // cbChunk = min((cbToCopy - cbWritten), pwp->cbWriteFree); RtlCopyMemory(pwp->pchWriteTail, pch + cbWritten, cbChunk); pwp->cbWriteFree -= cbChunk; pwp->pchWriteTail += cbChunk; cbWritten += cbChunk; WOW32ASSERT(pwp->pchWriteTail < pwp->pchWriteBuf + pwp->cbWriteBuf); } // // If the buffer was empty to start with and we made it // non-empty, issue the first WriteFile and signal the // writer thread to wake up. // if (fQueueEmpty && cbWritten) { pwp->cbWritePending = CALC_COMM_WRITE_SIZE(pwp); if (!WriteFile(pwp->h32, pwp->pchWriteHead, pwp->cbWritePending, &pwp->cbWritten, &pwp->olWrite)) { if (ERROR_IO_PENDING == GetLastError()) { pwp->fWriteDone = FALSE; } else { pwp->fWriteDone = TRUE; LOGDEBUG(0, ("WOW EnqueueCommWrite: WriteFile to id %u fails (error %u)\n", pwp->idComDev, GetLastError())); } } else { pwp->fWriteDone = TRUE; } // // Leave the critical section before setting the event. Otherwise // the other thread could wake up when the event is set and immediately // block on the critical section. // LeaveCriticalSection(&pwp->csWrite); // avoid setting the event twice if(!fDelay) { SetEvent(pwp->hWriteEvent); } } else { LeaveCriticalSection(&pwp->csWrite); } // this gives the writer thread a chance to write out "AT+FCLASS=1" strings if(fDelay) { SetEvent(pwp->hWriteEvent); Sleep(1000); } return cbWritten; } // // WOWCommWriteThread created for COM ports only. This thread dequeues // characters from the write buffer and writes them to the COM port. // This thread uses pwp->hWriteEvent for two purposes: // // 1. The event is signalled by EnqueueCommWrite when the write // buffer had been empty but is not now. This wakes us up // so we can write to the port. Note that we will always // be in the WaitForSingleObject at the top of the function // in this case, since that's where we sleep when the buffer // is empty. // // 2. DeletePortTabEntry signals the event after setting // pwp->fClose to tell us the port is closing and we // need to clean up and terminate this thread. This // thread might be doing anything in this case, but // it is careful to check pwp->fClose before sleeping // again. // // 3. wu32FlushComm() signals the event and marks the queue empty ULONG WOWCommWriterThread(LPVOID pWOWPortStruct) { PWOWPORT pwp = (PWOWPORT)pWOWPortStruct; HANDLE ah[2]; // // Copy event handles into array for WaitForMultipleObjects. // ah[0] = pwp->hWriteEvent; ah[1] = pwp->olWrite.hEvent; WaitForWriteOrder: // // pwp->fClose is TRUE when the port is closed. // while (!pwp->fClose) { // // First wait for something to be written to the buffer. // WaitForSingleObject(pwp->hWriteEvent, INFINITE); // // Critical section protects write buffer. // EnterCriticalSection(&pwp->csWrite); // // The buffer is empty when head == tail. // while (pwp->pchWriteHead != pwp->pchWriteTail) { // // pwp->cbWritePending will be nonzero if // the application thread queued a write to // an empty buffer and then issued the first // WriteFile call. // if (pwp->cbWritePending) { if (!pwp->fWriteDone) { LeaveCriticalSection(&pwp->csWrite); goto WaitForWriteCompletion; } else { goto CleanupAfterWriteComplete; } } pwp->cbWritePending = CALC_COMM_WRITE_SIZE(pwp); // // Leave the critical section before writing. This is // safe because the app thread doesn't change the // head pointer. (Not true if wu32FlushComm was called) // LeaveCriticalSection(&pwp->csWrite); if (!WriteFile(pwp->h32, pwp->pchWriteHead, pwp->cbWritePending, &pwp->cbWritten, &pwp->olWrite)) { if (ERROR_IO_PENDING == GetLastError() ) { WaitForWriteCompletion: // // Wait for the write to complete or for us to // be alerted that the port is closing. // while (WAIT_OBJECT_0 == WaitForMultipleObjects(2, ah, FALSE, INFINITE)) { // // pwp->hWriteEvent was signaled. This probably // means that the port was closed. // if (pwp->fClose) { goto PortClosed; } } if (GetOverlappedResult(pwp->h32, &pwp->olWrite, &pwp->cbWritten, TRUE ) ) { goto WriteSuccess; } } LOGDEBUG(0, ("WOWCommWriterThread: WriteFile to id %u fails (error %u)\n", pwp->idComDev, GetLastError())); pwp->cbWritePending = 0; goto WaitForWriteOrder; } WriteSuccess: // // Update head pointer to reflect portion written. // EnterCriticalSection(&pwp->csWrite); CleanupAfterWriteComplete: WOW32ASSERT(pwp->cbWritten == (WORD)pwp->cbWritten); pwp->pchWriteHead += pwp->cbWritten; pwp->cbWriteFree += (WORD)pwp->cbWritten; pwp->cbWritePending = 0; // // The following is a sanity check on our buffer manipulations. // #ifdef DEBUG if (pwp->pchWriteHead >= pwp->pchWriteBuf + pwp->cbWriteBuf) { WOW32ASSERT(pwp->pchWriteHead == pwp->pchWriteBuf + pwp->cbWriteBuf); } #endif if (pwp->pchWriteHead == pwp->pchWriteBuf + pwp->cbWriteBuf) { pwp->pchWriteHead = pwp->pchWriteBuf; } } // // We have exhausted the write buffer, leave the critical section // and loop back to the wait for the buffer to become non-empty. // LeaveCriticalSection(&pwp->csWrite); } PortClosed: CloseHandle(pwp->olWrite.hEvent); return 0; } // Checks status on RLSD, CTS, and DSR for timeout support // see MSRWait() in win3.1 comm.drv code BOOL MSRWait(PWOWPORT pwp) { DWORD dwStartTime, dwElapsedTime, dwLineStatus; DWORD dwErr = 0; // start the timeout clock (returns msec) dwStartTime = GetTickCount(); // loop until either all lines are high or a timeout occurs while(!dwErr) { // get the current status of the lines if ( !GetCommModemStatus(pwp->h32, &dwLineStatus) ) { //can't rely on third party drivers not to mess with dwLineStatus on failure dwLineStatus = 0; } // if all the required lines are up -- we're done if((pwp->lpComDEB16->MSRMask & LOBYTE(dwLineStatus)) == pwp->lpComDEB16->MSRMask) break; // get the elapsed time dwElapsedTime = GetTickCount() - dwStartTime; if(pwp->RLSDTimeout != IGNORE_TIMEOUT) { // if line is low if(!(dwLineStatus & MS_RLSD_ON)) { if(dwElapsedTime > UINT32(pwp->RLSDTimeout)) dwErr |= CE_RLSDTO; } } if(pwp->CTSTimeout != IGNORE_TIMEOUT) { // if line is low if(!(dwLineStatus & MS_CTS_ON)) { if(dwElapsedTime > UINT32(pwp->CTSTimeout)) dwErr |= CE_CTSTO; } } if(pwp->DSRTimeout != IGNORE_TIMEOUT) { // if line is low if(!(dwLineStatus & MS_DSR_ON)) { if(dwElapsedTime > UINT32(pwp->DSRTimeout)) dwErr |= CE_DSRTO; } } } pwp->dwErrCode |= dwErr; pwp->lpComDEB16->ComErr |= LOWORD(dwErr); if(dwErr) return(TRUE); else return(FALSE); }