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
2775 lines
83 KiB
/*++
|
|
*
|
|
* 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 <ntddser.h>
|
|
|
|
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);
|
|
|
|
}
|