Windows2003-3790/base/mvdm/wow32/wucomm.c
2020-09-30 16:53:55 +02:00

2776 lines
83 KiB
C

/*++
*
* 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);
}