/*++ BUILD Version: 0001
 *
 *  WOW v1.0
 *
 *  Copyright (c) 1991, Microsoft Corporation
 *
 *  WUCOMM.H
 *  WOW32 16-bit User API support
 *
 *  History:
 *  Created 07-Mar-1991 by Jeff Parsons (jeffpar)
 *  Updated    Dec-1992 by Craig Jones (v-cjones)
--*/

#include "wowcomm.h"

// these limits set as doc'd in Win3.1 Prog. ref. for OpenComm()
#define NUMCOMS        9          // max avail COM's
#define NUMLPTS        3          // max available LPT's
#define NUMPORTS  NUMCOMS+NUMLPTS // max # of entries in PortTab[]

// com port indicies into PortTab[]
#define COM1           0
#define COM2           1
#define COM3           2
#define COM4           3
#define COM5           4
#define COM6           5
#define COM7           6
#define COM8           7
#define COM9           8
#define LPT1           NUMCOMS
#define LPT2           LPT1+1
#define LPT3           LPT1+2
#define AUX            COM1
#define PRN            LPT1

// DOS comm IRQ assignments
#define IRQ3   3
#define IRQ4   4
#define IRQ5   5
#define IRQ7   7

// LPT assignments a la Win3.1
#define LPTFIRST       0x80                   // 0x80 == LPT1
#define LPTLAST        LPTFIRST + NUMLPTS - 1 // 0x82 == LPT3 

// other useful deinitions & macros
#define COMMASK        0x00FF                    // strip garbage from idComDev
#define LPTMASK        0x007F                    // get 0-based LPT #
#define GETLPTID(id)   ((id & LPTMASK) + LPT1)   // 0x80 LPT to PortTab[] index
#define TABIDTOLPT(id) (id + LPTFIRST - NUMCOMS) // PortTab[] index to LPT 0x80
#define VALIDCOM(id)   (((id >= COM1)     && (id <  NUMCOMS)) ? TRUE : FALSE)
#define VALIDLPT(id)   (((id >= LPTFIRST) && (id <= LPTLAST)) ? TRUE : FALSE)

#define GETPWOWPTR(id) (VALIDCOM(id) ? PortTab[id].pWOWPort : (VALIDLPT(id) ? PortTab[GETLPTID(id)].pWOWPort : NULL))

#define RM_BIOS_DATA   0x00400000                // bios data real mode seg:0

// for Win3.1 compatibility in EscapeCommFunction() API thunk support
#define RESETDEV      7
#define GETMAXLPT     8
#define GETMAXCOM     9
#define GETBASEIRQ   10

// notifications for EnableCommNotification() support
#define CN_RECEIVE    0x0001
#define CN_TRANSMIT   0x0002
#define CN_EVENT      0x0004
#define CN_RECEIVEHI  0x0100
#define CN_TRANSMITHI 0x0200
#define CN_NOTIFYHI   0x0400

#define WOW_WM_COMMNOTIFY 0x0044

// set all the events that can be masked on NT (a sub-set of Win3.1)
#define EV_NTEVENTS (EV_BREAK | EV_CTS    | EV_DSR    | EV_ERR  | EV_TXEMPTY | \
                     EV_RLSD  | EV_RXCHAR | EV_RXFLAG | EV_RING)

// constants for how Win3.1 expects to see the MSR
#define MSR_DELTAONLY   0x0000000F // strip off MSR state bits
#define MSR_STATEONLY   0x000000F0 // strip off MSR delta bits
#define MSR_DCTS        0x01       // bit for delta CTS
#define MSR_DDSR        0x02       // bit for delta DSR
#define MSR_TERI        0x04       // bit for TERI
#define MSR_DDCD        0x08       // bit for delta DCD
#define MSR_CTS         0x10       // bit for CTS
#define MSR_DSR         0x20       // bit for DSR
#define MSR_RI          0x40       // bit for RI
#define MSR_DCD         0x80       // bit for DCD

// constants for the Event Word 
#define EV_CTSS     0x00000400 // bit for Win3.1 showing CTS state
#define EV_DSRS     0x00000800 // bit for Win3.1 showing DSR state
#define EV_RLSDS    0x00001000 // bit for Win3.1 showing RLSD state
#define EV_RingTe   0x00002000 // bit for Win3.1 showing RingTe state

#define ERR_XMIT         0x4000 // can't xmit a char Win3.1
#define INFINITE_TIMEOUT 0xFFFF // infinite timeout Win3.1

#define COMBUF 2 // max. # of bytes we'll queue for WriteComm()

#define MAXCOMNAME     4             // max length of a comm device name
#define MAXCOMNAMENULL MAXCOMNAME+1  // length of a comm device name + NULL

// for 16-bit to 32-bit comm support
typedef struct _WOWPORT {
    UINT       idComDev;       // idComDev returned to app as handle of port
    HANDLE     h32;            // NT file handle used instead of idComDev
    HANDLE     hREvent;        // structure for overlapped reads
    CRITICAL_SECTION csWrite;  // critsect controls following 4 variables.
    PUCHAR     pchWriteHead;   // oldest byte not yet written to port.
    PUCHAR     pchWriteTail;   // first byte available in buffer.
    WORD       cbWriteFree;    // number of bytes available in write buffer.
    WORD       cbWritePending; // number of bytes now in WriteFile()
    PUCHAR     pchWriteBuf;    // write buffer
    WORD       cbWriteBuf;     // size of the write buffer.  One byte unused.
    HANDLE     hWriteThread;   // thread handle for COM writer.
    HANDLE     hWriteEvent;    // signalled by app thread when empty buffer
                               // made non-empty to wake up writer thread.
    OVERLAPPED olWrite;        // Overlapped structure used for writes.
    BOOL       fWriteDone;     // Indicates app thread completed first write.
    DWORD      cbWritten;      // Valid when fWriteDone == TRUE.
    DWORD      dwThreadID;     // app's thread id for crashed/hung app support
    DWORD      dwErrCode;      // most recent error for this idComDev 
    COMSTAT    cs;             // struct for error handling
    BOOL       fChEvt;         // TRUE if app set fChEvt in DCB struct
  // 16-bit DCB for LPT support only
    PDCB16     pdcb16;         // save DCB for LPT ports
  // for UngetCommChar() support
    BOOL       fUnGet;         // flag specifying an ungot char is pending
    UCHAR      cUnGet;         // ungot char in "buffer" only if fUnGet is set
  // for SetCommEventMask()/EnableCommNotification() support
    HANDLE     hMiThread;      // thread handle for Modem interrupt support
    BOOL       fClose;         // flag to close auxiliary threads
  // for SetCommEventMask() support only
    DWORD      dwComDEB16;     // DWORD obtained by call to GlobalDosAlloc()
    PCOMDEB16  lpComDEB16;     // flat address to above
  // for XonLim & XoffLim checking in SetCommState
    DWORD      cbInQ;          // Actual size of in Queue set in WU32OpenComm
} WOWPORT, *PWOWPORT;

// Table of above structs, one entry needed for each comm port
typedef struct _PORTTAB {
    CHAR      szPort[MAXCOMNAMENULL]; // port name
    PWOWPORT  pWOWPort;               // pointer to Comm Mapping struct
} PORTTAB, *PPORTTAB;

//
// Macro to calculate the size of chunk to write from the write
// to the filesystem.
//
// This is either the entire pending part of the
// buffer, or, if the buffer wraps, it is the portion
// between the head and the end of the buffer.
//
// In order to keep COMSTAT.cbOutQue moving at a reasonable
// pace, we restrict ourselves to writing at most 1024 bytes
// at a time.  This is because ProComm for Windows uses the
// cbOutQue value in displaying its progress, so if we allow
// larger writes it will only update every 5-10k (assuming
// ProComm's default 16k write buffer),
//

#define CALC_COMM_WRITE_SIZE(pwp)                            \
                min(1024,                                      \
                    (pwp->pchWriteHead < pwp->pchWriteTail)    \
                     ? pwp->pchWriteTail - pwp->pchWriteHead   \
                     : (pwp->pchWriteBuf + pwp->cbWriteBuf) -  \
                        pwp->pchWriteHead                      \
                   );


// Win3.1 timesout Tx after approx. 65000 msec (65 sec)
#define WRITE_TIMEOUT 65000

// bitfields of the 16-bit COMSTAT.status
#define W31CS_fCtsHold       0x01
#define W31CS_fDsrHold       0x02
#define W31CS_fRlsdHold      0x04
#define W31CS_fXoffHold      0x08
#define W31CS_fSentHold      0x10
#define W31CS_fEof           0x20
#define W31CS_fTxim          0x40

// Win3.1 Baud Rate constants
#define W31CBR_110       0xFF10
#define W31CBR_300       0xFF11
#define W31CBR_600       0xFF12
#define W31CBR_1200      0xFF13
#define W31CBR_2400      0xFF14
#define W31CBR_4800      0xFF15
#define W31CBR_9600      0xFF16
#define W31CBR_14400     0xFF17
#define W31CBR_19200     0xFF18
#define W31CBR_reserved1 0xFF19
#define W31CBR_reserved2 0xFF1A
#define W31CBR_38400     0xFF1B
#define W31CBR_reserved3 0xFF1C
#define W31CBR_reserved4 0xFF1D
#define W31CBR_reserved5 0xFF1E
#define W31CBR_56000     0xFF1F

// these are defined in Win3.1 windows.h but aren't supported in comm.drv
#define W31CBR_128000    0xFF23
#define W31CBR_256000    0xFF27

// special way to say 115200
#define W31CBR_115200    0xFEFF

// constants for conversions from Win3.1 baud specifications to 32-bit baud
#define W31_DLATCH_110      1047
#define W31_DLATCH_300       384
#define W31_DLATCH_600       192 
#define W31_DLATCH_1200       96
#define W31_DLATCH_2400       48
#define W31_DLATCH_4800       24
#define W31_DLATCH_9600       12
#define W31_DLATCH_14400       8
#define W31_DLATCH_19200       6
#define W31_DLATCH_38400       3
#define W31_DLATCH_56000       2
#define W31_DLATCH_115200      1

// Win3.1 flags for DCB structure
#define W31DCB_fBinary       0x0001
#define W31DCB_fRtsDisable   0x0002
#define W31DCB_fParity       0x0004
#define W31DCB_fOutxCtsFlow  0x0008
#define W31DCB_fOutxDsrFlow  0x0010
#define W31DCB_fDummy       (0x0020 | 0x0040)
#define W31DCB_fDtrDisable   0x0080
#define W31DCB_fOutX         0x0100
#define W31DCB_fInX          0x0200
#define W31DCB_fPeChar       0x0400
#define W31DCB_fNull         0x0800
#define W31DCB_fChEvt        0x1000
#define W31DCB_fDtrFlow      0x2000
#define W31DCB_fRtsFlow      0x4000
#define W31DCB_fDummy2       0x8000



//+++ DEBUG SUPPORT

#ifdef DEBUG

#define COMMDEBUG(lpszformat) LOGDEBUG(1, lpszformat)

// for watching the modem events
#define DEBUGWATCHMODEMEVENTS(dwE, dwM, dwS, pcE16, pcM16) {    \
    if(dwS) {                                                   \
        if((dwE != (DWORD)pcE16) || (dwM != (DWORD)pcM16)) {    \
            dwE = (DWORD)pcE16;                                 \
            dwM = (DWORD)pcM16;                                 \
            COMMDEBUG(("\nEvt:0x%4X  MSR:0x%2X\n", dwE, dwM));  \
        }                                                       \
        else {                                                  \
         COMMDEBUG(("."));                                      \
        }                                                       \
    }                                                           \
}

// prototype for real-time debug output
void CommIODebug(ULONG fhCommIO, HANDLE hCommIO, LPSZ lpsz, ULONG cb, LPSZ lpszFile);


#else  // endif DEBUG

#define COMMDEBUG(lpszFormat) 
#define DEBUGWATCHMODEMEVENTS(dwE, dwM, dwS, pcE16, pcM16) 
#define CommIODebug(fhCommIO, hCommIO, lpsz, cb, lpszFile)

#endif // endif !DEBUG

//--- DEBUG SUPPORT




// API support function prototypes
ULONG FASTCALL   WU32BuildCommDCB(PVDMFRAME pFrame);
ULONG FASTCALL   WU32ClearCommBreak(PVDMFRAME pFrame);
ULONG FASTCALL   WU32CloseComm(PVDMFRAME pFrame);
ULONG FASTCALL   WU32EnableCommNotification(PVDMFRAME pFrame);
ULONG FASTCALL   WU32EscapeCommFunction(PVDMFRAME pFrame);
ULONG FASTCALL   WU32FlushComm(PVDMFRAME pFrame);
ULONG FASTCALL   WU32GetCommError(PVDMFRAME pFrame);
ULONG FASTCALL   WU32GetCommEventMask(PVDMFRAME pFrame);
ULONG FASTCALL   WU32GetCommState(PVDMFRAME pFrame);
ULONG FASTCALL   WU32OpenComm(PVDMFRAME pFrame);
ULONG FASTCALL   WU32ReadComm(PVDMFRAME pFrame);
ULONG FASTCALL   WU32SetCommBreak(PVDMFRAME pFrame);
ULONG FASTCALL   WU32SetCommEventMask(PVDMFRAME pFrame);
ULONG FASTCALL   WU32SetCommState(PVDMFRAME pFrame);
ULONG FASTCALL   WU32TransmitCommChar(PVDMFRAME pFrame);
ULONG FASTCALL   WU32UngetCommChar(PVDMFRAME pFrame);
ULONG FASTCALL   WU32WriteComm(PVDMFRAME pFrame);

// prototypes for functions exported to the VDM
BYTE    GetCommShadowMSR(WORD idComDev);
HANDLE  GetCommHandle(WORD idComDev);

// prototype for crashed/hung app cleanup support
VOID FreeCommSupportResources(DWORD dwThreadID);
