//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
// ircomm.c - Emulate a com port on top of IRDA winsock
//
#include "windows.h"
#include "memory.h"
#include "pegdser.h"
#include "winsock2.h"
#include "af_irda.h"
#include <devload.h>

#undef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))

DWORD WINAPI AcceptThread(PVOID pThreadParm);
DWORD WINAPI TxThread(PVOID pThreadParm);
DWORD WINAPI RxThread(PVOID pThreadParm);

int     IrCOMMConnect(void);
int     InitializeConnection(SOCKET);
TCHAR   *GetLastErrorText(void);
int     CreateSockets();

#ifdef DEBUG

// Debug zone definitions.
#define ZONE_STATE      DEBUGZONE(0)	// 0x0001
#define ZONE_SOCKET     DEBUGZONE(1)	// 0x0002
#define ZONE_RECV       DEBUGZONE(7)    // 0x0080
#define ZONE_CONN       DEBUGZONE(8)    // 0x0100
#define ZONE_SEND       DEBUGZONE(9)    // 0x0200
#define ZONE_INTERFACE  DEBUGZONE(10)   // 0x0400
#define ZONE_MISC       DEBUGZONE(11)   // 0x0800
#define ZONE_ALLOC      DEBUGZONE(12)   // 0x1000
#define ZONE_FUNCTION   DEBUGZONE(13)   // 0x2000
#define ZONE_WARN       DEBUGZONE(14)   // 0x4000
#define ZONE_ERROR      DEBUGZONE(15)   // 0x8000

DBGPARAM dpCurSettings = 
{
    TEXT("IrComm"), 
    {
        TEXT("State"), TEXT("Socket"),  TEXT("Unused"),  TEXT("Unused"),
        TEXT("Unused"), TEXT("Unused"), TEXT("Unused"),  TEXT("Recv"),
        TEXT("Conn"),  TEXT("Send"),    TEXT("Interface"), TEXT("Misc"),
        TEXT("Alloc"), TEXT("Function"), TEXT("Warning"), TEXT("Error") 
    },
    0xC003
};

TCHAR *StateStr[] = {
    TEXT("IRCOMM_CLOSED"),
    TEXT("IRCOMM_OPENED"),
    TEXT("IRCOMM_DISCOVERY_PENDING"),
    TEXT("IRCOMM_CONNECT_PENDING"),
    TEXT("IRCOMM_CONNECTED")
};
#endif

typedef enum 
{
    IRCOMM_CLOSED,
    IRCOMM_OPENED,
    IRCOMM_DISCOVERY_PENDING,
    IRCOMM_CONNECT_PENDING,
    IRCOMM_CONNECTED
} IRCOMM_CONN_STATES;

typedef struct 
{
    BYTE    PIDataRate_0x10;
    BYTE    PLDataRate_4;
    ULONG   PVDataRateBigEndian;
    BYTE    PIDataFmt_0x11;
    BYTE    PLDataFmt_1;
    BYTE    PVDataFmt;
    BYTE    PIFlowCtl_0x12;
    BYTE    PLFlowCtl_1;
    BYTE    PVFlowCtl;
    BYTE    PIXOnXoff_0x13;
    BYTE    PLXOnXoff_2;
    BYTE    PVXOn_0x11;
    BYTE    PVXOff_0x13;
    BYTE    PIEnqAck_0x14;
    BYTE    PLEnqAck_2;
    BYTE    PVEnq_0x05;
    BYTE    PVAck_0x06;
} IRCOMM_9WIRE_PARMS;

SOCKADDR_IRDA SockAddr = 
{
    AF_IRDA,
    0, 0, 0, 0,
    "IrDA:IrCOMM"
};

#define MAX_OPENS 8
typedef struct _IRCOMM_OPEN {
    DWORD dwEventMask;
    DWORD dwEventData;
    HANDLE hCommEvent;
    DWORD  dwWaitThds;
    DWORD dwRefCnt;
} IRCOMM_OPEN, *PIRCOMM_OPEN;

PIRCOMM_OPEN g_Opens[MAX_OPENS];

HANDLE              hTxEvent;
HANDLE              hRxEvent;

PIRCOMM_OPEN        vAccessOwner;               
DWORD               vEventMask; // OR of wait masks of all opens
CRITICAL_SECTION    IrcommCs;
CRITICAL_SECTION    IrcommCs;

IRCOMM_CONN_STATES  State;

int                 SendMaxPDU;

SOCKET              ListenSock = INVALID_SOCKET;
SOCKET              AcceptSock = INVALID_SOCKET;
SOCKET              ConnectSock = INVALID_SOCKET;
SOCKET              DataSock = INVALID_SOCKET;
BOOL                SocketsCreated = FALSE;
DWORD               g_dwListenInstance = 0;

BYTE                *pMemBase;

#define             IRCOMM_TX_RING_SIZE     4096
BYTE                *TxRing;
BYTE                *pTxRingRead;
BYTE                *pTxRingWrite;
BYTE                *pTxRingMax;

#define             IRCOMM_TX_BUF_SIZE      2048
BYTE                *TxBuf;

#define             IRCOMM_RX_RING_SIZE     4096
BYTE                *RxRing;
BYTE                *pRxRingRead;
BYTE                *pRxRingWrite;
BYTE                *pRxRingMax;

COMMTIMEOUTS        CommTimeouts;

// WaitEvent thread priority
#define 			DEFAULT_THREAD_PRIORITY 148
#define 			REGISTRY_PRIORITY_VALUE TEXT("Priority256")
DWORD				g_dwHighThreadPrio;


BOOL __stdcall
DllEntry (HANDLE  hinstDLL,
          DWORD   Op,
          LPVOID  lpvReserved)
{
    switch (Op)
    {
      case DLL_PROCESS_ATTACH:
        DEBUGREGISTER(hinstDLL);
		DisableThreadLibraryCalls ((HMODULE)hinstDLL);
        break;

      case DLL_PROCESS_DETACH:
        break;
        
      case DLL_THREAD_DETACH:
        break;
        
      case DLL_THREAD_ATTACH:
        break;
        
        default :
        break;
    }
    
    return(TRUE);
}


//
// Allocate and initialize a new IRCOMM_OPEN
//
DWORD
NewOpen(
    DWORD dwAccess
    )
{
    DWORD i;
    PIRCOMM_OPEN pOpen;

    DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:+NewOpen\n")));
    EnterCriticalSection(&IrcommCs);
    for (i = 0; i < MAX_OPENS; i++) {
        if (NULL == g_Opens[i]) {
            pOpen = LocalAlloc(LPTR, sizeof(IRCOMM_OPEN));
            if (NULL == pOpen) {
                i = MAX_OPENS;
                DEBUGMSG(ZONE_WARN, (TEXT("IRCOMM:NewOpen LocalAlloc failed\n")));
                break;
            }
            pOpen->hCommEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
            pOpen->dwRefCnt = 1;
            pOpen->dwWaitThds = 0;
            if (NULL == pOpen->hCommEvent) {
                LocalFree(pOpen);
                i = MAX_OPENS;
                DEBUGMSG(ZONE_WARN, (TEXT("IRCOMM:NewOpen CreateEvent failed\n")));
                break;
            }
            g_Opens[i] = pOpen;
            if (dwAccess & (GENERIC_READ|GENERIC_WRITE)) {
                vAccessOwner = pOpen;
            }
            break;
        }
    }
    LeaveCriticalSection(&IrcommCs);
    DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:-NewOpen %d\n"), i));
    return i+1;
}   // NewOpen


BOOL
DerefOpen(
    DWORD dwOpen
    )
{
    PIRCOMM_OPEN pOpen;
    BOOL bDelete = FALSE;
    DWORD dwIndex = dwOpen-1;

    DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:+DerefOpen %d\n"), dwOpen));
    EnterCriticalSection(&IrcommCs);
    pOpen = g_Opens[dwIndex];
    pOpen->dwRefCnt--;
    if (0 == pOpen->dwRefCnt) {
        g_Opens[dwIndex] = NULL;
        if (pOpen == vAccessOwner) {
            vAccessOwner = NULL;
        }
        bDelete = TRUE;
    }

    if (bDelete) {
        DEBUGMSG(ZONE_WARN, (TEXT("IRCOMM:DerefOpen delete %d\n"), dwOpen));
        
        dwIndex = pOpen->dwWaitThds;
        while (dwIndex) {
            SetEvent(pOpen->hCommEvent);
            dwIndex--;
        }

        LeaveCriticalSection(&IrcommCs);

        CloseHandle(pOpen->hCommEvent);
        LocalFree(pOpen);
    } else {
        LeaveCriticalSection(&IrcommCs);
    }

    DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:-DerefOpen %d\n"), dwOpen));
    return bDelete;
}   // DerefOpen


//
// Verify an open handle and reference it.
//
PIRCOMM_OPEN
IsValidOpen(
    DWORD dwOpen
    )
{
    PIRCOMM_OPEN pRet;
    DWORD dwIndex = dwOpen-1;

    DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:+IsValidOpen %d\n"), dwOpen));

    if (dwIndex >= MAX_OPENS) {
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:-IsValidOpen %d too large\n"), dwOpen));
        return NULL;
    }

    EnterCriticalSection(&IrcommCs);
    if (pRet = g_Opens[dwIndex]) {
        g_Opens[dwIndex]->dwRefCnt++;
    }
    LeaveCriticalSection(&IrcommCs);
    DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:-IsValidOpen 0x%x\n"), pRet));
    return pRet;
}   // IsValidOpen

//
// Wake any threads waiting on a new event
//
void
NewCommEvent(
    DWORD dwEventData
    )
{
    DWORD i, j;

    DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:+NewCommEvent 0x%x\n"), dwEventData));
    EnterCriticalSection(&IrcommCs);
    if (dwEventData & vEventMask) { // Is anyone interested in this EV_*
        for (i = 0; i < MAX_OPENS; i++) {
            if (g_Opens[i]) {
                if (g_Opens[i]->dwEventMask & dwEventData) {
                    g_Opens[i]->dwEventData |= g_Opens[i]->dwEventMask & dwEventData;
                    j = g_Opens[i]->dwWaitThds;
                    while (j) {
                        SetEvent(g_Opens[i]->hCommEvent);
                        j--;
                    }
                }
            }
        }
    }
    LeaveCriticalSection(&IrcommCs);
    DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:-NewCommEvent\n")));
}   // NewCommEvent


void
ComputeEventMaskOR(void)
{
    int i;

    EnterCriticalSection(&IrcommCs);
    //
    // Recompute the OR of all wait masks
    //
    vEventMask = 0;
    for (i = 0; i < MAX_OPENS; i++) {
        if (g_Opens[i]) {
            vEventMask |= g_Opens[i]->dwEventMask;
        }
    }
    LeaveCriticalSection(&IrcommCs);
}

__inline
void
NewState(IRCOMM_CONN_STATES NewState)
{
    DEBUGMSG(ZONE_STATE, (TEXT("IRCOMM:NewState %s\r\n"), StateStr[NewState]));

    State = NewState;
}


//
// If the app is monitoring incoming calls (like PPP server) then don't transition to CLOSED.
// The next WaitCommEvent or ReadFile will cause IRCOMM to create a new set of sockets.
//
void
Shutdown(
    BOOL bClose
    )
{
    EnterCriticalSection(&IrcommCs);

    if (State != IRCOMM_CLOSED)
    {
        NewState(bClose ? IRCOMM_CLOSED : IRCOMM_OPENED);

        NewCommEvent(EV_RLSD);

        if (SocketsCreated) {
            // Indicate to any existing accept threads that their ListenSock is no longer valid.
            ++g_dwListenInstance;
	
            DEBUGMSG (ZONE_WARN, (TEXT("IRCOMM:Shutdown: Closing socket %d (ListenSock)\r\n"), ListenSock));
            closesocket(ListenSock);
            DEBUGMSG (ZONE_WARN, (TEXT("IRCOMM:Shutdown: Closing socket %d (ConnectSock)\r\n"), ConnectSock));
            closesocket(ConnectSock);            
            DEBUGMSG (ZONE_WARN, (TEXT("IRCOMM:Shutdown: Closing socket %d (DataSock)\r\n"), DataSock));
            closesocket(DataSock);

		    ListenSock = INVALID_SOCKET;
            ConnectSock = INVALID_SOCKET;
            DataSock = INVALID_SOCKET;            
        }
        SocketsCreated = FALSE;
    
        // make Rx/TxThread exit

        SetEvent(hRxEvent);
        SetEvent(hTxEvent);

        if (bClose) {
            CloseHandle(hTxEvent);
            CloseHandle(hRxEvent);
            VirtualFree(pMemBase, 0, MEM_RELEASE);
        } else {
            CreateSockets();
        }
    } else {
		DEBUGMSG (1, (TEXT("IRCOMM:Shutdown called and State == IRCOMM_CLOSED\r\n")));
		DEBUGMSG (1, (TEXT("IRCOMM:ListenSock=%d\r\n"), ListenSock));
		DEBUGMSG (1, (TEXT("IRCOMM:ConnectSock=%d\r\n"), ConnectSock));
		DEBUGMSG (1, (TEXT("IRCOMM:DataSock=%d\r\n"), DataSock));
	}

    LeaveCriticalSection(&IrcommCs);
}

// -----------------------------------------------------------------------------
// Function to read the thread priority from the registry.
// If it is not in the registry then a default value is returned.
// -----------------------------------------------------------------------------
static DWORD
GetRegistryThreadPriority(
	LPWSTR lpszActiveKey
	)
{
    HKEY hDevKey;
    DWORD dwValType;
    DWORD dwValLen;
    DWORD dwPrio;

    dwPrio = DEFAULT_THREAD_PRIORITY;

    hDevKey = OpenDeviceKey(lpszActiveKey);
    
    if (hDevKey) {
        dwValLen = sizeof(DWORD);
        RegQueryValueEx(
        	hDevKey,
            REGISTRY_PRIORITY_VALUE,
            NULL,
            &dwValType,
            (PUCHAR)&dwPrio,
            &dwValLen);
        RegCloseKey(hDevKey);
    }

    return dwPrio;
}

//  @func PVOID | ttt_Init | Device initialization routine
//  @parm DWORD | dwInfo | info passed to RegisterDevice
//  @rdesc  Returns a DWORD which will be passed to Open & Deinit or NULL if
//          unable to initialize the device.
//  @remark Routine exported by a device driver.  "ttt" is the string passed
//          in as lpszType in RegisterDevice

DWORD
COM_Init(DWORD Index)
{
    DWORD i;
    WORD    WsaVer = WINSOCK_VERSION;
    WSADATA WsaData;

    DEBUGMSG(ZONE_INTERFACE | ZONE_FUNCTION | ZONE_CONN, (TEXT("IRCOMM:+COM_Init(%d)\r\n"), Index));

    i = WSAStartup(WsaVer, &WsaData);
    if (i) {
        DEBUGMSG(1, (L"IRCOMM:-COM_Init:WSAStartup failed %d!!\n", i));
        return 0;
    }

    // Retrieve higher thread priority from registry
    g_dwHighThreadPrio = GetRegistryThreadPriority((LPWSTR)Index);

    InitializeCriticalSection(&IrcommCs);
    for (i = 0; i < MAX_OPENS; i++) {
        g_Opens[i] = NULL;
    }

    SocketsCreated = FALSE;
    DataSock = INVALID_SOCKET;
    vAccessOwner = NULL;

    NewState(IRCOMM_CLOSED);
    DEBUGMSG (ZONE_INTERFACE, (TEXT("IRCOMM:-COM_Init: return 1\r\n")));
    return 1;
}

//  @func PVOID | ttt_Deinit | Device deinitialization routine
//  @parm DWORD | dwData | value returned from ttt_Init call
//  @rdesc  Returns TRUE for success, FALSE for failure.
//  @remark Routine exported by a device driver.  "ttt" is the string
//          passed in as lpszType in RegisterDevice

BOOL
COM_Deinit(DWORD dwData)
{
    DEBUGMSG(ZONE_FUNCTION | ZONE_CONN, (TEXT("IRCOMM:COM_Deinit(0x%X)\r\n"), dwData));

    DeleteCriticalSection(&IrcommCs);
    WSACleanup();
    return TRUE;
}

//  @func PVOID | ttt_Open      | Device open routine
//  @parm DWORD | dwData        | value returned from ttt_Init call
//  @parm DWORD | dwAccess      | requested access (combination of GENERIC_READ
//                                and GENERIC_WRITE)
//  @parm DWORD | dwShareMode   | requested share mode (combination of
//                                FILE_SHARE_READ and FILE_SHARE_WRITE)
//  @rdesc  Returns a DWORD which will be passed to Read, Write, etc or NULL if
//          unable to open device.
//  @remark Routine exported by a device driver.  "ttt" is the string passed
//          in as lpszType in RegisterDevice
DWORD
COM_Open(DWORD dwData, DWORD dwAccess, DWORD dwShareMode)
{
    DWORD dwThisOpen;
    DEBUGMSG(ZONE_INTERFACE | ZONE_FUNCTION | ZONE_CONN, (TEXT("IRCOMM:+COM_Open(0x%X, 0x%X, 0x%X)\r\n"),
              dwData, dwAccess, dwShareMode));

    EnterCriticalSection(&IrcommCs);
    if ((dwAccess & (GENERIC_READ|GENERIC_WRITE)) && vAccessOwner) {
        LeaveCriticalSection(&IrcommCs);
        SetLastError(ERROR_INVALID_ACCESS);        
        return 0;
    }

    if ((dwThisOpen = NewOpen(dwAccess)) == MAX_OPENS+1) {
        LeaveCriticalSection(&IrcommCs);
        SetLastError(ERROR_OPEN_FAILED);
        return 0;
    }
    
    if (State != IRCOMM_CLOSED)
    {
        LeaveCriticalSection(&IrcommCs);
        return dwThisOpen;
    }
    
    hTxEvent        = CreateEvent(NULL, FALSE, FALSE, NULL);
    hRxEvent        = CreateEvent(NULL, FALSE, FALSE, NULL);

    if (!hTxEvent || !hRxEvent)
    {
        DEBUGMSG(ZONE_INTERFACE|ZONE_ERROR, (TEXT("IRCOMM:-COM_Open failed to create event\r\n")));        
        if (hTxEvent) {
            CloseHandle(hTxEvent);
        }
        if (hRxEvent) {
            CloseHandle(hRxEvent);
        }
        DerefOpen(dwThisOpen);
        LeaveCriticalSection(&IrcommCs);
        SetLastError(ERROR_OPEN_FAILED);
        return 0;        
    }

    pMemBase = (BYTE *) VirtualAlloc(NULL,
                           IRCOMM_TX_RING_SIZE +
                           IRCOMM_RX_RING_SIZE +
                           IRCOMM_TX_BUF_SIZE,
                           MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    if (pMemBase == NULL)
    {
        DEBUGMSG(ZONE_INTERFACE|ZONE_ERROR, (TEXT("IRCOMM:-COM_Open failed to create event\r\n")));        
        CloseHandle(hTxEvent);
        CloseHandle(hRxEvent);
        DerefOpen(dwThisOpen);
        LeaveCriticalSection(&IrcommCs);        
        SetLastError(ERROR_OPEN_FAILED);
        return 0;
    }

    TxRing  = pMemBase;
    RxRing  = pMemBase + IRCOMM_TX_RING_SIZE;
    TxBuf   = RxRing + IRCOMM_RX_RING_SIZE;

    pTxRingRead = pTxRingWrite = TxRing;
    pTxRingMax  = TxRing + IRCOMM_TX_RING_SIZE;
    
    pRxRingRead = pRxRingWrite = RxRing;
    pRxRingMax  = RxRing + IRCOMM_RX_RING_SIZE;    

    vEventMask = 0;

    NewState(IRCOMM_OPENED);

    LeaveCriticalSection(&IrcommCs);
    
    DEBUGMSG(ZONE_INTERFACE, (TEXT("IRCOMM:-COM_Open: Success\r\n")));        
    return dwThisOpen;
}


//  @func BOOL | ttt_Close | Device close routine
//  @parm DWORD | dwOpenData | value returned from ttt_Open call
//  @rdesc  Returns TRUE for success, FALSE for failure
//  @remark Routine exported by a device driver.  "ttt" is the string passed
//          in as lpszType in RegisterDevice
BOOL
COM_Close(DWORD dwData) 
{
    BOOL bShutdown;
    PIRCOMM_OPEN pOpen;
    DWORD i;

    DEBUGMSG(ZONE_INTERFACE | ZONE_FUNCTION | ZONE_CONN, (TEXT("IRCOMM:+COM_Close(0x%X)\r\n"), dwData));

    EnterCriticalSection(&IrcommCs);

    if (NULL == (pOpen = IsValidOpen(dwData))) {
        SetLastError(ERROR_INVALID_HANDLE);
        LeaveCriticalSection(&IrcommCs);
        return FALSE;

    }

    for (i = pOpen->dwRefCnt; i; i--) {
        SetEvent(pOpen->hCommEvent);
    }
    LeaveCriticalSection(&IrcommCs);

    DerefOpen(dwData);  // once for IsValidOpen
    DerefOpen(dwData);  // once to delete

    EnterCriticalSection(&IrcommCs);
    //
    // If there are no open handles, then we need to shutdown
    //
    bShutdown = TRUE;
    for (dwData = 0; dwData < MAX_OPENS; dwData++) {
        if (g_Opens[dwData]) {
            bShutdown = FALSE;
            break;
        }
    }
    if (bShutdown) {
        Shutdown(TRUE); // TRUE => Close
    } else {
        ComputeEventMaskOR();
    }
    LeaveCriticalSection(&IrcommCs);
    
    DEBUGMSG(ZONE_INTERFACE, (TEXT("IRCOMM:-COM_Close: Success\r\n")));

    return TRUE;
}

#define BIND_RETRIES 5
#define BIND_RETRY_SLEEP 500

BOOL
CreateSockets()
{
    // max attribute size 6
    BYTE        IASSetBuff[sizeof(IAS_SET) - 3 + 6];
    int         IASSetLen  = sizeof(IASSetBuff);
    PIAS_SET    pIASSet    = (PIAS_SET) &IASSetBuff;
    HANDLE      hAcceptThread;
    int         Enable9WireMode  = 1;
    BOOL        Status = FALSE;
    int         cBindRetry;
    
    DEBUGMSG(ZONE_FUNCTION|ZONE_CONN, (TEXT("IRCOMM:+CreateSockets\r\n")));

    EnterCriticalSection(&IrcommCs);

    if (SocketsCreated)
    {
        Status = TRUE;
        goto done;
    }

    if (State == IRCOMM_CLOSED)
        goto done;
    
    if ((ConnectSock = socket(AF_IRDA, SOCK_STREAM, 0)) == INVALID_SOCKET)
    {
        DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM:CreateSockets ConnectSock=socket() failed %s\n\r"), GetLastErrorText()));
        goto done;
    }

    if ((ListenSock = socket(AF_IRDA, SOCK_STREAM, 0)) == INVALID_SOCKET)
    {
        DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM:CreateSockets ListenSock=socket() failed %s\n\r"), GetLastErrorText()));
        goto done;
    }

    if (setsockopt(ListenSock, SOL_IRLMP, IRLMP_9WIRE_MODE, 
                   (const char *) &Enable9WireMode, sizeof(int)) 
        == SOCKET_ERROR)
    {
        goto done;
    }

    cBindRetry = BIND_RETRIES;
    while (cBindRetry) {
        if (bind(ListenSock, (const struct sockaddr *) &SockAddr, sizeof(SOCKADDR_IRDA)) == SOCKET_ERROR) {
            if (GetLastError() == WSAEADDRINUSE) {
                Sleep(BIND_RETRY_SLEEP);
            } else {
                DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM:CreateSockets bind(ListenSock) failed %s\n\r"), GetLastErrorText()));
                goto done;
            }
        } else {
            break;
        }
        cBindRetry--;
    }

    ASSERT(cBindRetry);
    if (0 == cBindRetry) {
        DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM:CreateSockets bind(ListenSock) failed %s\n\r"), GetLastErrorText()));
        goto done;
    }

    if (listen(ListenSock, 1) == SOCKET_ERROR)
    {
        DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM:CreateSockets listen() failed %s\n\r"), GetLastErrorText()));
        goto done;
    }

/*
    memset ((char *) CommTimeouts, 0, sizeof(COMMTIMEOUTS));
*/
    // add IrCOMM IAS attributes
    memcpy(&pIASSet->irdaClassName[0],  "IrDA:IrCOMM", 12);
    memcpy(&pIASSet->irdaAttribName[0], "Parameters",  11);
    
    pIASSet->irdaAttribType                       = IAS_ATTRIB_OCTETSEQ;
    pIASSet->irdaAttribute.irdaAttribOctetSeq.Len = 6;

    memcpy(&pIASSet->irdaAttribute.irdaAttribOctetSeq.OctetSeq[0],
           "\000\001\006\001\001\001", 6);

    if (setsockopt(ListenSock, SOL_IRLMP, IRLMP_IAS_SET, 
                   (const char *) pIASSet, IASSetLen) == SOCKET_ERROR)
    {
        DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM:CreateSockets setsockopt(,,IRLMP_IAS_SET,,) %s\n\r"), GetLastErrorText()));

        goto done;
    }

    if ((hAcceptThread = CreateThread(NULL, 0, AcceptThread, (PVOID)g_dwListenInstance, 0, NULL))
        == 0)
    {
        DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM:CreateSockets CreateThread() failed\r\n")));
        goto done;
    }

    CloseHandle(hAcceptThread);

    SocketsCreated = TRUE;

    NewState(IRCOMM_DISCOVERY_PENDING);

    Status = TRUE;
    
done:

    if (!SocketsCreated) {
        if (ListenSock != INVALID_SOCKET) {
            closesocket(ListenSock);
            ListenSock = INVALID_SOCKET;
        }
        if (ConnectSock != INVALID_SOCKET) {
            closesocket(ConnectSock);
            ConnectSock = INVALID_SOCKET;
        }
    }
    
    LeaveCriticalSection(&IrcommCs);
    
    DEBUGMSG(ZONE_FUNCTION|ZONE_CONN, (TEXT("IRCOMM:-CreateSockets %s\r\n"), Status ? L"TRUE" : L"FALSE"));
    return Status;
}

//  @func DWORD | ttt_Read | Device read routine
//  @parm DWORD | dwOpenData | value returned from ttt_Open call
//  @parm LPVOID | pBuf | buffer to receive data
//  @parm DWORD | len | maximum length to read
//  @rdesc  Returns 0 for end of file, -1 for error, otherwise the number of
//          bytes read.  The length returned is guaranteed to be the length
//          requested unless end of file or an error condition occurs.
//  @remark Routine exported by a device driver.  "ttt" is the string passed
//          in as lpszType in RegisterDevice
DWORD
COM_Read(DWORD dwData, LPVOID pBuf, DWORD Len)
{
    DWORD   RxLen = 0;
    
    DEBUGMSG(ZONE_INTERFACE | ZONE_FUNCTION, (TEXT("IRCOMM:+COM_Read %d\r\n"), Len));

    if (!IsValidOpen(dwData)) {
        SetLastError(ERROR_INVALID_HANDLE);
        DEBUGMSG(ZONE_INTERFACE | ZONE_ERROR,(TEXT("IRCOMM:-COM_Read: Invalid handle\r\n")));
        return -1;
    }

    if (!SocketsCreated)
    {
        if (CreateSockets() == FALSE) {
            SetLastError(ERROR_GEN_FAILURE);
            DEBUGMSG(ZONE_INTERFACE | ZONE_ERROR, (TEXT("IRCOMM:-COM_Read: CreateSockets failed\r\n")));
            DerefOpen(dwData);
            return -1;
        }
    }

    EnterCriticalSection(&IrcommCs);  // protect ring
    
    if (State == IRCOMM_CLOSED) {
        SetLastError(ERROR_GEN_FAILURE);
        RxLen = -1;
        goto exit;
    }
    
    if (pRxRingRead == pRxRingWrite &&
        CommTimeouts.ReadTotalTimeoutConstant == MAXDWORD)
    {
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:COM_Read waiting...\r\n")));

        LeaveCriticalSection(&IrcommCs);
        
        WaitForSingleObject(hRxEvent, INFINITE);

        EnterCriticalSection(&IrcommCs);
    }

    if (State == IRCOMM_CLOSED) {
        SetLastError(ERROR_GEN_FAILURE);
        goto exit;
    }
    
    for (RxLen = 0; RxLen < Len && pRxRingRead != pRxRingWrite;
         RxLen++)
    {
        ((BYTE *)pBuf)[RxLen] = *pRxRingRead++;
        if (pRxRingRead == pRxRingMax)
            pRxRingRead = RxRing;
    }
        
exit:        
    DerefOpen(dwData);
    LeaveCriticalSection(&IrcommCs);
    DEBUGMSG(ZONE_INTERFACE | ZONE_FUNCTION, (TEXT("IRCOMM:-COM_Read: Returning %d\r\n"), RxLen));
    return RxLen;
}

//  @func DWORD | ttt_Write | Device write routine
//  @parm DWORD | dwOpenData | value returned from ttt_Open call
//  @parm LPCVOID | pBuf | buffer containing data
//  @parm DWORD | len | maximum length to write
//  @rdesc  Returns -1 for error, otherwise the number of bytes written.  The
//          length returned is guaranteed to be the length requested unless an
//          error condition occurs.
//  @remark Routine exported by a device driver.  "ttt" is the string passed
//          in as lpszType in RegisterDevice
DWORD
COM_Write(DWORD dwData, LPCVOID pBuf, DWORD Len)
{
    DWORD   BytesSent = 0;
    BYTE    *pLast, *pRead;

    DEBUGMSG(ZONE_INTERFACE | ZONE_FUNCTION, (TEXT("IRCOMM:+COM_Write len %d\r\n"),Len));

    if (!IsValidOpen(dwData)) {
        SetLastError(ERROR_INVALID_HANDLE);
        DEBUGMSG(ZONE_INTERFACE | ZONE_ERROR,(TEXT("IRCOMM:-COM_Write: Invalid handle\r\n")));
        return -1;
    }

    if (!SocketsCreated)
    {
        if (CreateSockets() == FALSE) {
            DEBUGMSG(ZONE_INTERFACE | ZONE_ERROR, (TEXT("IRCOMM:-COM_Write: CreateSockets failed\r\n")));
            DerefOpen(dwData);
            SetLastError(ERROR_INTERNAL_ERROR);
            return (DWORD)-1;
        }
    }
    
    if (State != IRCOMM_CONNECTED)
    {
        if (IrCOMMConnect() == -1) {
            DEBUGMSG(ZONE_INTERFACE | ZONE_ERROR, (TEXT("IRCOMM:-COM_Write: IrCOMMConnect failed\r\n")));
            DerefOpen(dwData);
            SetLastError(ERROR_INTERNAL_ERROR);
            return (DWORD)-1;
        }
    }

    EnterCriticalSection(&IrcommCs);

    if (State == IRCOMM_CLOSED) {
        BytesSent = -1;
        goto exit;
    }
    
    pRead = pTxRingRead; 
    pLast = pRead == TxRing ? pTxRingMax-1 : pRead-1;
    
    while (BytesSent < Len && pTxRingWrite != pLast)
    {
        *pTxRingWrite++ = *((BYTE *) pBuf)++;
        BytesSent++;        

        if (pTxRingWrite == pTxRingMax)
            pTxRingWrite = TxRing;

        pRead = pTxRingRead;
        pLast = pRead == TxRing ? pTxRingMax-1 : pRead-1;        
    }

    if (BytesSent < Len)
    {
        DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM:COM_Write TxRing overflow Sent %d, Total %d\n"), BytesSent, Len));
    }

    SetEvent(hTxEvent);

exit:
    DerefOpen(dwData);
    LeaveCriticalSection(&IrcommCs);

    DEBUGMSG(ZONE_INTERFACE, (TEXT("IRCOMM:-COM_Write: returning %d\r\n"), BytesSent));
    return BytesSent;
}

//  @func DWORD | ttt_Seek | Device seek routine
//  @parm DWORD | dwOpenData | value returned from ttt_Open call
//  @parm long | pos | position to seek to (relative to type)
//  @parm DWORD | type | FILE_BEGIN, FILE_CURRENT, or FILE_END
//  @rdesc  Returns current position relative to start of file, or -1 on error
//  @remark Routine exported by a device driver.  "ttt" is the string passed
//       in as lpszType in RegisterDevice

DWORD
COM_Seek (DWORD dwData, long pos, DWORD type)
{
    return((DWORD)-1);
}

//  @func void | COM_PowerUp | Device powerup routine
//  @comm   Called to restore device from suspend mode.  You cannot call any
//          routines aside from those in your dll in this call.

BOOL
COM_PowerUp(void)
{
    return 0;
}

//  @func void | ttt_PowerDown | Device powerdown routine
//  @comm   Called to suspend device.  You cannot call any routines aside from
//          those in your dll in this call.
void
COM_PowerDown(void)
{
}

BOOL
WaitEvent(PIRCOMM_OPEN pOpen, PULONG pEventMask)
{
    DWORD Priority256 = CeGetThreadPriority(GetCurrentThread());

    EnterCriticalSection(&IrcommCs);
    if (State == IRCOMM_CLOSED) {
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:WaitEvent state==IRCOMM_CLOSED\r\n")));
        *pEventMask = 0;
        LeaveCriticalSection(&IrcommCs);
        SetLastError(ERROR_GEN_FAILURE);
        return FALSE;
    }

    /* We should return immediately if mask is 0
     */
    if ((vEventMask == 0) || (pOpen->dwEventMask == 0)) 
    {
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:WaitEvent, empty event mask\r\n")));
        *pEventMask = 0;
        LeaveCriticalSection(&IrcommCs);
        return FALSE;
    }
    
    CeSetThreadPriority(GetCurrentThread(), g_dwHighThreadPrio);
   
    /* Do we need to wait?
    */
    if (pOpen->dwEventMask & pOpen->dwEventData) {
        goto we_gotit;
    }

    if (!SocketsCreated)
    {
        if (CreateSockets() == FALSE) {
            LeaveCriticalSection(&IrcommCs);
            CeSetThreadPriority(GetCurrentThread(), Priority256);
            DEBUGMSG(ZONE_INTERFACE | ZONE_FUNCTION| ZONE_ERROR, (TEXT("IRCOMM:WaitEvent: CreateSockets failed\r\n")));
            SetLastError(ERROR_GEN_FAILURE);
            return FALSE;
        }
    }

    DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:WaitEvent: Waiting for CommEvent\r\n")));
    
    pOpen->dwWaitThds++;
    WaitForSingleObject(pOpen->hCommEvent, (ULONG)0);

    LeaveCriticalSection(&IrcommCs);

    WaitForSingleObject(pOpen->hCommEvent, (ULONG)-1);

    /* Get the critical section protecting the data
     */
    EnterCriticalSection(&IrcommCs);
    pOpen->dwWaitThds--;

we_gotit:

    DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:WaitEvent Got event %x\r\n"), pOpen->dwEventData));
    

    /* Get the data.
     */

    *pEventMask = pOpen->dwEventData;

    /* Clear the events that we just handled
     */ 
    pOpen->dwEventData = 0;
    LeaveCriticalSection(&IrcommCs);
    
    CeSetThreadPriority(GetCurrentThread(), Priority256);

    return TRUE;
}

//  @func BOOL | ttt_IOControl | Device IO control routine
//  @parm DWORD | dwOpenData | value returned from ttt_Open call
//  @parm DWORD | dwCode | io control code to be performed
//  @parm PBYTE | pBufIn | input data to the device
//  @parm DWORD | dwLenIn | number of bytes being passed in
//  @parm PBYTE | pBufOut | output data from the device
//  @parm DWORD | dwLenOut |maximum number of bytes to receive from device
//  @parm PDWORD | pdwActualOut | actual number of bytes received from device
//  @rdesc  Returns TRUE for success, FALSE for failure
//  @remark Routine exported by a device driver.  "ttt" is the string passed
//      in as lpszType in RegisterDevice

BOOL
COM_IOControl(DWORD dwOpenData, DWORD dwCode, PBYTE pBufIn,
              DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
              PDWORD pdwActualOut)
{
    BOOL RetVal = TRUE; // Initialize to success
    DWORD Priority256;
    PIRCOMM_OPEN pOpen;

    
    DEBUGMSG(ZONE_INTERFACE,
        (TEXT("IRCOMM:+COM_IOControl(0x%X, %d, 0x%X, %d, 0x%X, %d, 0x%X)\r\n"),
         dwOpenData, dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut, pdwActualOut));

    
    if (NULL == (pOpen = IsValidOpen(dwOpenData))) {
        SetLastError(ERROR_INVALID_HANDLE);
        DEBUGMSG(ZONE_INTERFACE | ZONE_ERROR,(TEXT("IRCOMM:-COM_IOControl: Invalid handle\r\n")));
        return -1;
    }

    switch (dwCode)
    {
        //  @func   BOOL | IOCTL_SERIAL_SET_BREAK_ON |
        //      Device IO control routine to set the break state.
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_SET_BREAK_ON
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //  @remark Sets the transmission line in a break state until
        //      <f IOCTL_SERIAL_SET_BREAK_OFF> is called.
        //
      case IOCTL_SERIAL_SET_BREAK_ON:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_SET_BREAK_ON\r\n")));
        break;

        //  @func   BOOL | IOCTL_SERIAL_SET_BREAK_OFF |
        //      Device IO control routine to clear the break state.
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_SET_BREAK_OFF
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //  @remark Restores character transmission for the communications
        //      device and places the transmission line in a nonbreak state
        //      (called after <f IOCTL_SERIAL_SET_BREAK_ON>).
        //
      case IOCTL_SERIAL_SET_BREAK_OFF:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_SET_BREAK_OFF\r\n")));
        break;

        //  @func   BOOL | IOCTL_SERIAL_SET_DTR |
        //      Device IO control routine to set DTR high.
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_SET_DTR
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //  @xref   <f IOCTL_SERIAL_CLR_DTR>
        //
      case IOCTL_SERIAL_SET_DTR:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_SET_DTR\r\n")));
        break;
        //  @func   BOOL | IOCTL_SERIAL_CLR_DTR |
        //      Device IO control routine to set DTR low.
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_CLR_DTR
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //  @xref   <f IOCTL_SERIAL_SET_DTR>
        //
      case IOCTL_SERIAL_CLR_DTR:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_CLR_DTR\r\n")));
        break;

        //  @func   BOOL | IOCTL_SERIAL_SET_RTS |
        //      Device IO control routine to set RTS high.
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_SET_RTS
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //  @xref   <f IOCTL_SERIAL_CLR_RTS>
        //
      case IOCTL_SERIAL_SET_RTS:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_SET_RTS\r\n")));
        break;

        //  @func   BOOL | IOCTL_SERIAL_CLR_RTS |
        //      Device IO control routine to set RTS low.
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_CLR_RTS
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //  @xref   <f IOCTL_SERIAL_SET_RTS>
        //
      case IOCTL_SERIAL_CLR_RTS:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_CLR_RTS\r\n")));
        break;

        //  @func   BOOL | IOCTL_SERIAL_SET_XOFF |
        //      Device IO control routine to cause transmission
        //      to act as if an XOFF character has been received.
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_SET_XOFF
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //  @xref   <f IOCTL_SERIAL_SET_XON>
        //
      case IOCTL_SERIAL_SET_XOFF:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_SET_XOFF\r\n")));
        break;

        //  @func   BOOL | IOCTL_SERIAL_SET_XON |
        //      Device IO control routine to cause transmission
        //      to act as if an XON character has been received.
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_SET_XON
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //  @xref   <f IOCTL_SERIAL_SET_XOFF>
        //
      case IOCTL_SERIAL_SET_XON:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_SET_XON\r\n")));
        break;

        //  @func   BOOL | IOCTL_SERIAL_GET_WAIT_MASK |
        //      Device IO control routine to retrieve the value
        //      of the event mask.
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_GET_WAIT_MASK
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Points to DWORD to place event mask
        //  @parm DWORD | dwLenOut | should be sizeof(DWORD) or larger
        //  @parm PDWORD | pdwActualOut | Points to DWORD to return length
        //      of returned data (should be set to sizeof(DWORD) if no error)
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //  @xref   <f IOCTL_SERIAL_SET_WAIT_MASK>
        //          <f IOCTL_SERIAL_WAIT_ON_MASK>
        //
      case IOCTL_SERIAL_GET_WAIT_MASK:

        if ((dwLenOut < sizeof(DWORD)) || 
            (NULL == pBufOut)          ||
            (NULL == pdwActualOut)) 
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            RetVal = FALSE;
            break;
        }
        
        // Set The Wait Mask
        *(DWORD *)pBufOut = pOpen->dwEventMask;
        
        // Return the size
        *pdwActualOut = sizeof(DWORD);
        DEBUGMSG (ZONE_FUNCTION, (TEXT("IRCOMM: GET_WAIT_MASK %x\r\n"), pOpen->dwEventMask));
        break;

        //  @func   BOOL | IOCTL_SERIAL_SET_WAIT_MASK |
        //      Device IO control routine to set the value
        //      of the event mask.
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_SET_WAIT_MASK
        //  @parm PBYTE | pBufIn | Pointer to the DWORD mask value
        //  @parm DWORD | dwLenIn | should be sizeof(DWORD)
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //  @xref   <f IOCTL_SERIAL_GET_WAIT_MASK>
        //          <f IOCTL_SERIAL_WAIT_ON_MASK>
        //
      case IOCTL_SERIAL_SET_WAIT_MASK:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: SET_WAIT_MASK 0x%X\r\n"), vEventMask));
        
		if (State == IRCOMM_CLOSED)
		{
			DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM: ERROR SetCommEvent while state==IRCOMM_CLOSED\n")));
			SetLastError(ERROR_INVALID_HANDLE);
            RetVal = FALSE;
			break;
		}
        else if ((dwLenIn < sizeof(DWORD)) || 
            (NULL == pBufIn)) 
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            RetVal = FALSE;
            break;
        }

        EnterCriticalSection(&IrcommCs);
        if (!SocketsCreated) {
            if (CreateSockets() == FALSE) {
                LeaveCriticalSection(&IrcommCs);
                DEBUGMSG(ZONE_FUNCTION|ZONE_ERROR, (TEXT("IRCOMM:SetCommEvent: CreateSockets failed\r\n")));
                SetLastError(ERROR_GEN_FAILURE);
                RetVal = FALSE;
                break;
            }
        }

        // Set the event so any currently waiting will return with an error
        Priority256 = CeGetThreadPriority(GetCurrentThread());
        CeSetThreadPriority(GetCurrentThread(), g_dwHighThreadPrio);

        {
            DWORD i;
            i = pOpen->dwWaitThds;
            while (i) {
                SetEvent(pOpen->hCommEvent);
                i--;
            }
        }
        
        pOpen->dwEventMask = (*(DWORD *) pBufIn);

        ComputeEventMaskOR();

        LeaveCriticalSection(&IrcommCs);
        CeSetThreadPriority(GetCurrentThread(), Priority256);
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: New MASK 0x%X\r\n"), vEventMask));
        break;

        //  @func   BOOL | IOCTL_SERIAL_WAIT_ON_MASK |
        //      Device IO control routine to wait for a communications
        //      event that matches one in the event mask
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_WAIT_ON_MASK
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Points to DWORD to place event mask.
        //      The returned mask will show the event that terminated
        //      the wait.  If a process attempts to change the device
        //      handle's event mask by using the IOCTL_SERIAL_SET_WAIT_MASK
        //      call the driver should return immediately with (DWORD)0 as
        //      the returned event mask.
        //  @parm DWORD | dwLenOut | should be sizeof(DWORD) or larger
        //  @parm PDWORD | pdwActualOut | Points to DWORD to return length
        //      of returned data (should be set to sizeof(DWORD) if no error)
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //  @xref   <f IOCTL_SERIAL_GET_WAIT_MASK>
        //          <f IOCTL_SERIAL_SET_WAIT_MASK>
        //
      case IOCTL_SERIAL_WAIT_ON_MASK:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_WAIT_ON_MASK\r\n")));

        if ((dwLenOut < sizeof(DWORD)) || 
            (NULL == pBufOut)          ||
            (NULL == pdwActualOut))
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            RetVal = FALSE;
            break;
        }
        
        RetVal = WaitEvent(pOpen, (DWORD *)pBufOut);
        
        // Return the size
        *pdwActualOut = sizeof(DWORD);
        break;

        //  @func   BOOL | IOCTL_SERIAL_GET_COMMSTATUS |
        //      Device IO control routine to clear any pending
        //      communications errors and return the current communication
        //      status.
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_GET_COMMSTATUS
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Points to a <f SERIAL_DEV_STATUS>
        //      structure for the returned status information
        //  @parm DWORD | dwLenOut | should be sizeof(SERIAL_DEV_STATUS)
        //      or larger
        //  @parm PDWORD | pdwActualOut | Points to DWORD to return length
        //      of returned data (should be set to sizeof(SERIAL_DEV_STATUS)
        //      if no error)
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //
      case IOCTL_SERIAL_GET_COMMSTATUS:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_GET_COMMSTATUS\r\n")));

        if ((dwLenOut < sizeof(SERIAL_DEV_STATUS)) ||
            (NULL == pBufOut)                      ||
            (NULL == pdwActualOut)) 
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            RetVal = FALSE;
            break;
        }
        
        // Set The Error Mask
        ((PSERIAL_DEV_STATUS)pBufOut)->Errors = 0;

        // Clear the ComStat structure
        memset ((char *) &(((PSERIAL_DEV_STATUS)pBufOut)->ComStat), 0,
                sizeof(COMSTAT));

        // Return the size
        *pdwActualOut = sizeof(SERIAL_DEV_STATUS);
        break;

        //  @func   BOOL | IOCTL_SERIAL_GET_MODEMSTATUS |
        //      Device IO control routine to retrieve current
        //      modem control-register values
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_GET_MODEMSTATUS
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Points to a DWORD for the returned
        //      modem status information
        //  @parm DWORD | dwLenOut | should be sizeof(DWORD)
        //      or larger
        //  @parm PDWORD | pdwActualOut | Points to DWORD to return length
        //      of returned data (should be set to sizeof(DWORD)
        //      if no error)
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //
      case IOCTL_SERIAL_GET_MODEMSTATUS:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_GET_MODEMSTATUS\r\n")));

        if ((dwLenOut < sizeof(DWORD))  || 
            (NULL == pBufOut)           ||
            (NULL == pdwActualOut)) 
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            RetVal = FALSE;
            break;
        }
        
        // Set the Modem Status dword
        *(DWORD *) pBufOut = 0;
        if (State == IRCOMM_CONNECTED)
            *(DWORD *)pBufOut = MS_RLSD_ON;

        // Return the size
        *pdwActualOut = sizeof(DWORD);
        break;

        //  @func   BOOL | IOCTL_SERIAL_GET_PROPERTIES |
        //      Device IO control routine to retrieve information
        //      about the communications properties for the device.
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_GET_PROPERTIES
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Points to a <f COMMPROP> structure
        //      for the returned information.
        //  @parm DWORD | dwLenOut | should be sizeof(COMMPROP)
        //      or larger
        //  @parm PDWORD | pdwActualOut | Points to DWORD to return length
        //      of returned data (should be set to sizeof(COMMPROP)
        //      if no error)
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //
      case IOCTL_SERIAL_GET_PROPERTIES:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_GET_PROPERTIES\r\n")));

        if ((dwLenOut < sizeof(COMMPROP)) ||
            (NULL == pBufOut)             ||
            (NULL == pdwActualOut)) 
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            RetVal = FALSE;
            break;
        }
        
        // Clear the ComMProp structure
        memset ((char *) ((COMMPROP *)pBufOut), 0, sizeof(COMMPROP));

        // Return the size
        *pdwActualOut = sizeof(COMMPROP);
        break;

        //  @func   BOOL | IOCTL_SERIAL_SET_TIMEOUTS |
        //      Device IO control routine to set the time-out parameters
        //      for all read and write operations on a specified
        //      communications device
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_SET_TIMEOUTS
        //  @parm PBYTE | pBufIn | Pointer to the <f COMMTIMEOUTS> structure
        //  @parm DWORD | dwLenIn | should be sizeof(COMMTIMEOUTS)
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //  @xref   <f IOCTL_SERIAL_GET_TIMEOUTS>
        //
      case IOCTL_SERIAL_SET_TIMEOUTS :
        if ((dwLenIn < sizeof(COMMTIMEOUTS)) || (NULL == pBufIn)) 
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            RetVal = FALSE;
            break;
        }

        DEBUGMSG (ZONE_FUNCTION,
            (TEXT("IRCOMM: IOCTL_SERIAL_SET_COMMTIMEOUTS (%d,%d,%d,%d,%d)\r\n"),
            ((COMMTIMEOUTS *)pBufIn)->ReadIntervalTimeout,
            ((COMMTIMEOUTS *)pBufIn)->ReadTotalTimeoutMultiplier,
            ((COMMTIMEOUTS *)pBufIn)->ReadTotalTimeoutConstant,
            ((COMMTIMEOUTS *)pBufIn)->WriteTotalTimeoutMultiplier,
            ((COMMTIMEOUTS *)pBufIn)->WriteTotalTimeoutConstant));

        CommTimeouts.ReadIntervalTimeout =
            ((COMMTIMEOUTS *)pBufIn)->ReadIntervalTimeout;
        CommTimeouts.ReadTotalTimeoutMultiplier =
            ((COMMTIMEOUTS *)pBufIn)->ReadTotalTimeoutMultiplier;
        CommTimeouts.ReadTotalTimeoutConstant =
            ((COMMTIMEOUTS *)pBufIn)->ReadTotalTimeoutConstant;
        CommTimeouts.WriteTotalTimeoutMultiplier =
            ((COMMTIMEOUTS *)pBufIn)->WriteTotalTimeoutMultiplier;
        CommTimeouts.WriteTotalTimeoutConstant =
            ((COMMTIMEOUTS *)pBufIn)->WriteTotalTimeoutConstant;

        // Normally we would do something with the passed in parameter.
        break;

        //  @func   BOOL | IOCTL_SERIAL_GET_TIMEOUTS |
        //      Device IO control routine to set the time-out parameters
        //      for all read and write operations on a specified
        //      communications device
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_GET_TIMEOUTS
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Pointer to a <f COMMTIMEOUTS> structure
        //      for the returned data
        //  @parm DWORD | dwLenOut | should be sizeof(COMMTIMEOUTS)
        //  @parm PDWORD | pdwActualOut | Points to DWORD to return length
        //      of returned data (should be set to sizeof(COMMTIMEOUTS)
        //      if no error)
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //  @xref   <f IOCTL_SERIAL_GET_TIMEOUTS>
        //
      case IOCTL_SERIAL_GET_TIMEOUTS:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_GET_TIMEOUTS\r\n")));

        if ((dwLenOut < sizeof(COMMTIMEOUTS))   || 
            (NULL == pBufOut)                   ||
            (NULL == pdwActualOut)) 
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            RetVal = FALSE;
            break;
        }
        
        // Clear the structure
        memset ((char *) ((COMMTIMEOUTS *)pBufOut), 0, sizeof(COMMTIMEOUTS));

        memcpy((LPCOMMTIMEOUTS)pBufOut, &CommTimeouts,  sizeof(COMMTIMEOUTS));

        // Return the size
        *pdwActualOut = sizeof(COMMTIMEOUTS);
        break;

        //  @func   BOOL | IOCTL_SERIAL_PURGE |
        //      Device IO control routine to discard characters from the
        //      output or input buffer of a specified communications
        //      resource.  It can also terminate pending read or write
        //      operations on the resource
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_PURGE
        //  @parm PBYTE | pBufIn | Pointer to a DWORD containing the action
        //  @parm DWORD | dwLenIn | Should be sizeof(DWORD)
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //
      case IOCTL_SERIAL_PURGE:
        DEBUGMSG(ZONE_FUNCTION, 
            (TEXT("IRCOMM: IOCTL_SERIAL_PURGE 0x%X\r\n"), *(DWORD *) pBufIn));

        if ((dwLenIn < sizeof(DWORD)) || (NULL == pBufIn))
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            RetVal = FALSE;
            break;
        }


        // Normally we would do something with the passed in parameter.
        break;

        //  @func   BOOL | IOCTL_SERIAL_SET_QUEUE_SIZE |
        //      Device IO control routine to set the queue sizes of of a
        //      communications device
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_SET_QUEUE_SIZE
        //  @parm PBYTE | pBufIn | Pointer to a <f SERIAL_QUEUE_SIZES>
        //      structure
        //  @parm DWORD | dwLenIn | should be sizeof(<f SERIAL_QUEUE_SIZES>)
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //
      case IOCTL_SERIAL_SET_QUEUE_SIZE:
        DEBUGMSG(ZONE_FUNCTION,
            (TEXT("IRCOMM: IOCTL_SERIAL_SET_QUEUE_SIZE (%d,%d,%d,%d,%d)\r\n"),
            ((SERIAL_QUEUE_SIZES *)pBufIn)->cbInQueue,
            ((SERIAL_QUEUE_SIZES *)pBufIn)->cbOutQueue));
        if ((dwLenIn < sizeof(SERIAL_QUEUE_SIZES)) || (NULL == pBufIn)) 
        {
            SetLastError (ERROR_INVALID_PARAMETER);
            RetVal = FALSE;
            break;
        }

        // Normally we would do something with the passed in parameter.
        break;

        //  @func   BOOL | IOCTL_SERIAL_IMMEDIATE_CHAR |
        //      Device IO control routine to transmit a specified character
        //      ahead of any pending data in the output buffer of the
        //      communications device
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_IMMEDIATE_CHAR
        //  @parm PBYTE | pBufIn | Pointer to a UCHAR to send
        //  @parm DWORD | dwLenIn | should be sizeof(UCHAR)
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //
      case IOCTL_SERIAL_IMMEDIATE_CHAR:
        DEBUGMSG(ZONE_FUNCTION, 
            (TEXT("IRCOMM: IOCTL_SERIAL_IMMEDIATE_CHAR 0x%X\r\n"), (UCHAR *) pBufIn));

        if ((dwLenIn < sizeof(UCHAR)) || (NULL == pBufIn)) 
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            RetVal = FALSE;
            break;
        }


        // Normally we would do something with the passed in parameter.
        break;

        //  @func   BOOL | IOCTL_SERIAL_GET_DCB |
        //      Device IO control routine to get the device-control
        //      block from a specified communications device
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_GET_DCB
        //  @parm PBYTE | pBufIn | Ignored
        //  @parm DWORD | dwLenIn | Ignored
        //  @parm PBYTE | pBufOut | Pointer to a <f DCB> structure
        //  @parm DWORD | dwLenOut | Should be sizeof(<f DCB>)
        //  @parm PDWORD | pdwActualOut | Pointer to DWORD to return length
        //      of returned data (should be set to sizeof(<f DCB>) if
        //      no error)
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //
      case IOCTL_SERIAL_GET_DCB:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_GET_DCB\r\n")));

        if ((dwLenOut < sizeof(DCB))    || 
            (NULL == pBufOut)           ||
            (NULL == pdwActualOut)) 
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            RetVal = FALSE;
            break;
        }
        
        // Clear the structure
        memset ((char *) ((DCB *)pBufOut), 0, sizeof(DCB));

        // Return the size
        *pdwActualOut = sizeof(DCB);
        break;

        //  @func   BOOL | IOCTL_SERIAL_SET_DCB |
        //      Device IO control routine to set the device-control
        //      block on a specified communications device
        //
        //  @parm DWORD | dwOpenData | value returned from ttt_Open call
        //  @parm DWORD | dwCode | IOCTL_SERIAL_SET_DCB
        //  @parm PBYTE | pBufIn | Pointer to a <f DCB> structure
        //  @parm DWORD | dwLenIn | should be sizeof(<f DCB>)
        //  @parm PBYTE | pBufOut | Ignored
        //  @parm DWORD | dwLenOut | Ignored
        //  @parm PDWORD | pdwActualOut | Ignored
        //
        //  @rdesc  Returns TRUE for success, FALSE for failure (and
        //      sets thread error code)
        //
        //
      case IOCTL_SERIAL_SET_DCB:
        DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: IOCTL_SERIAL_SET_DCB\r\n")));
        if ((dwLenIn < sizeof(DCB)) || (NULL == pBufIn)) 
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            RetVal = FALSE;
            break;
        }

        // Normally we would do something with the passed in parameter.
        break;

      default:
        SetLastError(ERROR_INVALID_PARAMETER);
        RetVal = FALSE;
        break;
    }

    DerefOpen(dwOpenData);

    DEBUGMSG(ZONE_INTERFACE | ZONE_FUNCTION, 
        (TEXT("IRCOMM:-COM_IOControl %s (len=%d)\r\n"),
        (RetVal == TRUE) ? TEXT("Success") : TEXT("Error"),
        (NULL == pdwActualOut) ? 0 : *pdwActualOut));

    return(RetVal);
}

DWORD WINAPI
AcceptThread(PVOID pThreadParm)
{
    int     sizeofInt       = sizeof(int);
    int     sizeofSockAddr  = sizeof(SOCKADDR_IRDA);
    DWORD   dwListenInstance = (DWORD)pThreadParm;
    SOCKET  LocalListenSock;
    SOCKADDR_IRDA SockAddrRemote;

    DEBUGMSG (ZONE_FUNCTION, (TEXT("+IRCOMM:AcceptThread: Start\r\n")));

    EnterCriticalSection(&IrcommCs);
    LocalListenSock = ListenSock;
    LeaveCriticalSection(&IrcommCs);

    while (1) {
        EnterCriticalSection(&IrcommCs);
        if (dwListenInstance != g_dwListenInstance) {
            //
            // If the global instance count has changed then the ListenSock that 
            // this thread was created for has been closed.  Because the handle value
            // itself may be reused, we keep track using this simple counter instead
            // of watching the handle value or its state.
            //
            DEBUGMSG (ZONE_WARN, (TEXT("IRCOMM: accept() exiting, instance %08X != %08X\r\n"),
                                  dwListenInstance, g_dwListenInstance));
            LeaveCriticalSection(&IrcommCs);
            break;
        }
        LeaveCriticalSection(&IrcommCs);

        
        if ((AcceptSock = accept(LocalListenSock,
                                 (struct sockaddr *) &SockAddrRemote, 
                                 &sizeofSockAddr)) == INVALID_SOCKET)
        {
            DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM: accept() %s\n\r"), GetLastErrorText()));

            if (GetLastError() == WSAENOTSOCK) {
                DEBUGMSG (ZONE_FUNCTION, (TEXT("-IRCOMM:AcceptThread: End Accept returned WASENOTSOCK\r\n")));
                return(0);
            } else {
                continue;
            }
        }


        if (getsockopt(AcceptSock, SOL_IRLMP, IRLMP_SEND_PDU_LEN, 
                       (char *) &SendMaxPDU, &sizeofInt) == SOCKET_ERROR)
        {
            DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM: getsockopt(,,IRLMP_SEND_PDU_LEN,,) %s\n\r"), GetLastErrorText()));
            closesocket(AcceptSock);
            continue;
        }

        memcpy(&SockAddr.irdaDeviceID[0], &SockAddrRemote.irdaDeviceID, 4);

        if (InitializeConnection(AcceptSock) != 0) {
            // Error occured?
            closesocket(AcceptSock);
            AcceptSock = INVALID_SOCKET;
        }
        break;
    }

    DEBUGMSG (ZONE_FUNCTION, (TEXT("-IRCOMM:AcceptThread: End, return 0\r\n")));
    return(0);
}

int
IrCOMMConnect(void)
{
    // allow discovery of 5 devices
    BYTE        DevListBuff[sizeof(DEVICELIST) + (sizeof(IRDA_DEVICE_INFO) * 4)];
    int         DevListLen = sizeof(DevListBuff);
    PDEVICELIST pDevList   = (PDEVICELIST) &DevListBuff;
    int         sizeofInt = sizeof(int);
    int         Enable9WireMode  = 1;
    int         i;


    if (setsockopt(ConnectSock, SOL_IRLMP, IRLMP_9WIRE_MODE, 
                   (const char *) &Enable9WireMode, sizeof(int)) 
        == SOCKET_ERROR)
    {
        closesocket(ConnectSock);
        ConnectSock = INVALID_SOCKET;
        return(-1);
    }

    if (State == IRCOMM_DISCOVERY_PENDING)
    {
        pDevList->numDevice = 0;

        for (i = 0; (i < 6) && (pDevList->numDevice == 0); i++)
        {
            if (getsockopt(ConnectSock, SOL_IRLMP, IRLMP_ENUMDEVICES, 
                           (char *) pDevList, &DevListLen) == SOCKET_ERROR)
            {
                DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM: getsockopt(,,IRLMP_ENUM_DEVICES,,) %s\n\r"), GetLastErrorText()));
                return(-1);
            }
        }

        if (pDevList->numDevice == 0)
        {
            DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM: No devices found\r\n")));
            return(-1);
        }

        memcpy(&SockAddr.irdaDeviceID[0], 
               &pDevList->Device[0].irdaDeviceID[0], 4);
        
        NewState(IRCOMM_CONNECT_PENDING);

#ifdef DEBUG
        for (i = 0; i < (int)pDevList->numDevice; i++) {
            DEBUGMSG(ZONE_CONN, (L"IRCOMM: Device %d = %a\n", i, pDevList->Device[i].irdaDeviceName));
        }
#endif

    }

    if (State == IRCOMM_CONNECT_PENDING)
    {
        if (connect(ConnectSock, (const struct sockaddr *) &SockAddr, 
                    sizeof(SOCKADDR_IRDA)) == SOCKET_ERROR)
        {
            DEBUGMSG(ZONE_ERROR,
                (TEXT("IRCOMM: connect() %s\n\r"), GetLastErrorText()));
            return(-1);
        }
        else
        {
            if (getsockopt(ConnectSock, SOL_IRLMP, IRLMP_SEND_PDU_LEN, 
                           (char *) &SendMaxPDU, &sizeofInt) == SOCKET_ERROR)
            {
                DEBUGMSG(ZONE_ERROR,
                    (TEXT("IRCOMM: getsockopt(,,IRLMP_SEND_PDU_LEN,,) %s\n\r"),
                    GetLastErrorText()));

                return(-1);
            }
            
            return InitializeConnection(ConnectSock);
        }
    }

    return(0);
}

int
InitializeConnection(SOCKET p_DataSock)
{
    HANDLE      hThrd;
    int         result = -1;

    DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:+InitializeConnection %d\n"), p_DataSock));

    EnterCriticalSection(&IrcommCs);
    
    if (DataSock != INVALID_SOCKET) {
        // A connection is already established, and only 1 connection is allowed at a time.
        DEBUGMSG(ZONE_FUNCTION|ZONE_WARN, (TEXT("IRCOMM:-InitializeConnection - WARNING connection already established\n")));

        LeaveCriticalSection(&IrcommCs);
        return result;
    }

    NewState(IRCOMM_CONNECTED);

    ASSERT(DataSock == INVALID_SOCKET);
    
    DataSock    = p_DataSock;
    
    pRxRingRead = pRxRingWrite = RxRing;
    pRxRingMax  = RxRing + IRCOMM_RX_RING_SIZE;    

    pTxRingRead = pTxRingWrite = TxRing;
    pTxRingMax  = TxRing + IRCOMM_TX_RING_SIZE;

    // Indicate to any existing accept threads that their ListenSock is no longer valid.
    ++g_dwListenInstance;
	
    closesocket(ListenSock);
    ListenSock = INVALID_SOCKET;
    LeaveCriticalSection(&IrcommCs);
    
    ASSERT(SendMaxPDU <= IRCOMM_TX_BUF_SIZE);
 
    if ((hThrd = CreateThread(NULL, 0, TxThread, NULL, 0, NULL)) != 0) {
        CeSetThreadPriority(hThrd, g_dwHighThreadPrio);
        CloseHandle(hThrd);
    
        if ((hThrd = CreateThread(NULL, 0, RxThread, NULL, 0, NULL)) != 0) {
            CeSetThreadPriority(hThrd, g_dwHighThreadPrio);
            CloseHandle(hThrd);
            NewCommEvent(EV_RLSD);
            result = 0;
        } else {
            // Need to terminate TxThread at this point
            NewState(IRCOMM_OPENED);
            SetEvent(hTxEvent);
        }
    }
    
	DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM: -InitializeConnection result = %d\r\n"), result));
    
    return result;
}

DWORD WINAPI
TxThread(PVOID pThreadParm)
{
    int TxLen;

    DEBUGMSG(ZONE_FUNCTION, (TEXT("+IrCOMM:TxThread: Start\r\n")));

    while (1)
    {
        // Wait for something to send
        
        if (pTxRingRead == pTxRingWrite)
        {
            NewCommEvent(EV_TXEMPTY);

            DEBUGMSG(ZONE_FUNCTION, (TEXT("IRCOMM:TxThread waiting...\r\n")));
            
            WaitForSingleObject(hTxEvent, INFINITE);
        }

        EnterCriticalSection(&IrcommCs);
        
        if (State != IRCOMM_CONNECTED)
        {
            DEBUGMSG(1, (TEXT("IRCOMM:TxThread exit1\r\n")));

            LeaveCriticalSection(&IrcommCs);
            
            DEBUGMSG (ZONE_FUNCTION|ZONE_ERROR, (TEXT("-IRCOMM:TxThread: Exit (State!=IRCOMM_CONNECTED)\r\n")));
            return 0;
        }

        for (TxLen=0; TxLen < SendMaxPDU - 1 && pTxRingRead != pTxRingWrite;
             TxLen++)
        {
            TxBuf[TxLen] = *pTxRingRead++;
            if (pTxRingRead == pTxRingMax)
            {
                pTxRingRead = TxRing;
            }
        }

        LeaveCriticalSection(&IrcommCs);
        
        if (send(DataSock, TxBuf, TxLen, 0) == SOCKET_ERROR)        
        {
            DEBUGMSG(ZONE_ERROR, (TEXT("IRCOMM: send() %s\n\r"), GetLastErrorText()));
            
            Shutdown(FALSE);    // FALSE => Don't close
            
            DEBUGMSG (ZONE_FUNCTION|ZONE_ERROR, (TEXT("-IRCOMM:TxThread: Exit Error from send(%d)\r\n"), GetLastError()));
            return 0; 
        }
    }

    DEBUGMSG (ZONE_FUNCTION, (TEXT("-IRCOMM:TxThread: End\r\n")));
    
    return 0;
}


DWORD WINAPI
RxThread(PVOID pThreadParm)
{
#define RXBUF_SIZE  256    
    BYTE    RecvBuf[RXBUF_SIZE]; 
    int     RecvCnt, i;
    BYTE    *pLast, *pRead;
    
    DEBUGMSG (ZONE_FUNCTION, (TEXT("+IRCOMM:RxThread: Start\r\n")));
    
    while (1)
    {
        if ((RecvCnt = recv(DataSock, RecvBuf, RXBUF_SIZE, 0))
            == SOCKET_ERROR || RecvCnt == 0)
        {
            DEBUGMSG (ZONE_FUNCTION|ZONE_ERROR, (TEXT("-IRCOMM:RxThread: recv() failed %s/%d\r\n"),
                       GetLastErrorText(), GetLastError()));

            Shutdown(FALSE);    // FALSE => Don't close
            
            return 0;
        }

        EnterCriticalSection(&IrcommCs);

        if (State == IRCOMM_CLOSED)
        {
            LeaveCriticalSection(&IrcommCs);
            DEBUGMSG (ZONE_FUNCTION|ZONE_ERROR, (TEXT("-IRCOMM:RxThread: State == IRCOMM_CLOSED\r\n")));
            return 0;
        }
        
        pRead = pRxRingRead; 
        pLast = pRead == RxRing ? pRxRingMax-1 : pRead-1;

        DEBUGMSG(ZONE_RECV|ZONE_MISC, (TEXT("IRCOMM:RxThread read %d \r\n"), RecvCnt));
        
        i = 0;
        
        while (i < RecvCnt && pRxRingWrite != pLast)
        {
            *pRxRingWrite++ = RecvBuf[i++];

            if (pRxRingWrite == pRxRingMax)
                pRxRingWrite = RxRing;

            pRead = pRxRingRead;
            pLast = pRead == TxRing ? pTxRingMax-1 : pRead-1;        
        }

        LeaveCriticalSection(&IrcommCs);

        if (i < RecvCnt)
        {
            DEBUGMSG(ZONE_WARN, (TEXT("IRCOMM: RxRing overflow Read %d, Total %d\n"), i, RecvCnt));
        }
    
        SetEvent(hRxEvent);

        if (RecvCnt)
        {
            NewCommEvent(EV_RXCHAR);
        }
        else
            break;
    }

    DEBUGMSG (ZONE_FUNCTION, (TEXT("-IRCOMM:RxThread: State == IRCOMM_CLOSED\r\n")));
    return 0;
}

#ifdef DEBUG
TCHAR *
GetLastErrorText(void)
{
    switch (GetLastError())
    {
      case WSAEINTR:
        return (TEXT("WSAEINTR"));
        break;

      case WSAEBADF:
        return(TEXT("WSAEBADF"));
        break;

      case WSAEACCES:
        return(TEXT("WSAEACCES"));
        break;

      case WSAEFAULT:
        return(TEXT("WSAEFAULT"));
        break;

      case WSAEINVAL:
        return(TEXT("WSAEINVAL"));
        break;
        
      case WSAEMFILE:
        return(TEXT("WSAEMFILE"));
        break;

      case WSAEWOULDBLOCK:
        return(TEXT("WSAEWOULDBLOCK"));
        break;

      case WSAEINPROGRESS:
        return(TEXT("WSAEINPROGRESS"));
        break;

      case WSAEALREADY:
        return(TEXT("WSAEALREADY"));
        break;

      case WSAENOTSOCK:
        return(TEXT("WSAENOTSOCK"));
        break;

      case WSAEDESTADDRREQ:
        return(TEXT("WSAEDESTADDRREQ"));
        break;

      case WSAEMSGSIZE:
        return(TEXT("WSAEMSGSIZE"));
        break;

      case WSAEPROTOTYPE:
        return(TEXT("WSAEPROTOTYPE"));
        break;

      case WSAENOPROTOOPT:
        return(TEXT("WSAENOPROTOOPT"));
        break;

      case WSAEPROTONOSUPPORT:
        return(TEXT("WSAEPROTONOSUPPORT"));
        break;

      case WSAESOCKTNOSUPPORT:
        return(TEXT("WSAESOCKTNOSUPPORT"));
        break;

      case WSAEOPNOTSUPP:
        return(TEXT("WSAEOPNOTSUPP"));
        break;

      case WSAEPFNOSUPPORT:
        return(TEXT("WSAEPFNOSUPPORT"));
        break;

      case WSAEAFNOSUPPORT:
        return(TEXT("WSAEAFNOSUPPORT"));
        break;

      case WSAEADDRINUSE:
        return(TEXT("WSAEADDRINUSE"));
        break;

      case WSAEADDRNOTAVAIL:
        return(TEXT("WSAEADDRNOTAVAIL"));
        break;

      case WSAENETDOWN:
        return(TEXT("WSAENETDOWN"));
        break;

      case WSAENETUNREACH:
        return(TEXT("WSAENETUNREACH"));
        break;

      case WSAENETRESET:
        return(TEXT("WSAENETRESET"));
        break;

      case WSAECONNABORTED:
        return(TEXT("WSAECONNABORTED"));
        break;

      case WSAECONNRESET:
        return(TEXT("WSAECONNRESET"));
        break;

      case WSAENOBUFS:
        return(TEXT("WSAENOBUFS"));
        break;

      case WSAEISCONN:
        return(TEXT("WSAEISCONN"));
        break;

      case WSAENOTCONN:
        return(TEXT("WSAENOTCONN"));
        break;

      case WSAESHUTDOWN:
        return(TEXT("WSAESHUTDOWN"));
        break;

      case WSAETOOMANYREFS:
        return(TEXT("WSAETOOMANYREFS"));
        break;

      case WSAETIMEDOUT:
        return(TEXT("WSAETIMEDOUT"));
        break;

      case WSAECONNREFUSED:
        return(TEXT("WSAECONNREFUSED"));
        break;

      case WSAELOOP:
        return(TEXT("WSAELOOP"));
        break;

      case WSAENAMETOOLONG:
        return(TEXT("WSAENAMETOOLONG"));
        break;

      case WSAEHOSTDOWN:
        return(TEXT("WSAEHOSTDOWN"));
        break;

      case WSAEHOSTUNREACH:
        return(TEXT("WSAEHOSTUNREACH"));
        break;

      case WSAENOTEMPTY:
        return(TEXT("WSAENOTEMPTY"));
        break;

      case WSAEPROCLIM:
        return(TEXT("WSAEPROCLIM"));
        break;

      case WSAEUSERS:
        return(TEXT("WSAEUSERS"));
        break;

      case WSAEDQUOT:
        return(TEXT("WSAEDQUOT"));
        break;

      case WSAESTALE:
        return(TEXT("WSAESTALE"));
        break;

      case WSAEREMOTE:
        return(TEXT("WSAEREMOTE"));
        break;

      case WSAEDISCON:
        return(TEXT("WSAEDISCON"));
        break;

      case WSASYSNOTREADY:
        return(TEXT("WSASYSNOTREADY"));
        break;

      case WSAVERNOTSUPPORTED:
        return(TEXT("WSAVERNOTSUPPORTED"));
        break;

      case WSANOTINITIALISED:
        return(TEXT("WSANOTINITIALISED"));
        break;

        /*
      case WSAHOST:
        return(TEXT("WSAHOST"));
        break;

      case WSATRY:
        return(TEXT("WSATRY"));
        break;

      case WSANO:
        return(TEXT("WSANO"));
        break;
        */

      default:
        return(TEXT("Unknown Error"));
    }
}
#endif


