//
// 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.
//
//------------------------------------------------------------------------------
// 
//      Bluetooth TDI Layer
// 
// 
// Module Name:
// 
//      tdi.cxx
// 
// Abstract:
// 
//      This file implements Bluetooth TDI Layer
// 
// 
//------------------------------------------------------------------------------
#if defined (UNDER_CE)
#include <windows.h>
#include <svsutil.hxx>

#if ! defined (UNDER_CE)
#include <stddef.h>
#include <malloc.h>
#else
#include <stdlib.h>
#endif

#include <winsock.h>
#include <ws2bth.h>
#include <bt_debug.h>
#include <bt_os.h>
#include <bt_buffer.h>
#include <bt_hcip.h>
#include <bt_ddi.h>
#include <bt_api.h>

#include <memory.h>

#include <nspapi.h>
#include <cxport.h>
#include <ndis.h>
#include <tdi.h>
#include <tdistat.h>
#include <tdice.h>
#include <tdiinfo.h>
#include <ipexport.h>
#include <tcpinfo.h>
#include <ndislink.h>

#include <bthapi.h>
#include <bt_sdp.h>
#include <sdpnode.h>
#include <sdplib.h>

extern "C" {
#include <cclib.h>
};

//#define DEBUG_CREDIT_FLOW				1
//#define DEBUG_PEER_SYNCHRONIZATION			1	// Both sides MUST be compiled with this

#if defined (DEBUG_PEER_SYNCHRONIZATION)

#define DEBUG_PEER_MAGIC	'gbdP'

struct PeerDebugData {
	unsigned int magic;

#if defined (DEBUG_CREDIT_FLOW)
	int iHaveCredits;
	int iGaveCredits;
	int iPacketsSent;
	int iPacketsRecv;
	int iCreditsSent;
	int iCreditsRecv;
#endif

	PeerDebugData () {
		memset (this, 0, sizeof(*this));
		magic = DEBUG_PEER_MAGIC;
	}
};

#define DEBUG_OVERHEAD	sizeof(PeerDebugData)

#else
#define DEBUG_OVERHEAD		0
#endif

#define CLIENTPORTSIG 0x4D534254		//"MSBT"
#define PIN_SIZE		16
#define KEY_SIZE		16

typedef void (* PBT_CONNECT_COMPLETE)(
		PVOID			Context, 
		TDI_STATUS		FinalStatus, 
		unsigned long	ByteCount);

typedef void (* PBT_DISCONNECT_COMPLETE)(
		PVOID			Context, 
		TDI_STATUS		FinalStatus, 
		unsigned long	ByteCount);

typedef void (* PBT_SEND_COMPLETE)(
		PVOID			Context,
		TDI_STATUS		TdiStatus,
		DWORD			BytesSent);

typedef void (* PBT_RECV_COMPLETE)(
		PVOID			Context,
		TDI_STATUS		TdiStatus,
		DWORD			BytesRecvd);



const WINSOCK_MAPPING BT_MAPPING = {1, 3, AF_BT, SOCK_STREAM, BTHPROTO_RFCOMM};

#define RFCOMM_TRANSPORT_NAME	L"RFCOMM"

#define TDIR_V24_CONST			0x81	// +EA -FC -RTC -RTR 0 0 -IC +DV
#define TDIR_V24_FC				0x02	//     +FC
#define TDIR_V24_RTC			0x04	//         +RTC
#define TDIR_V24_RTR			0x08	//              +RTR
#define TDIR_V24_IC				0x08	//                       +IC

#define TAI_SIZE (offsetof(TDI_ADDRESS_INFO, Address.Address[0].AddressType) + sizeof(SOCKADDR_BTH))
#define TA_SIZE (offsetof(TRANSPORT_ADDRESS, Address[0].AddressType) + sizeof(SOCKADDR_BTH))


enum CONNECT_STAGE {
	ALLOCATED,
	INITED,
	OPENING,
	OPENED,
	CLOSED
};

#define OPT_CONFIG_AUTH			0x00000001
#define OPT_CONFIG_CRYPT		0x00000002
#define OPT_CONFIG_MTU			0x00000004
#define OPT_CONFIG_MTU_MAX		0x00000008
#define OPT_CONFIG_MTU_MIN		0x00000010
#define OPT_CONFIG_XON			0x00000020
#define OPT_CONFIG_XOFF			0x00000040
#define OPT_CONFIG_MAX_SEND		0x00000080
#define OPT_CONFIG_MAX_RECV		0x00000100
#define OPT_CONFIG_PIN			0x00000200

struct OPT {
	unsigned int				fAuthenticate : 1;					// Authentication required
	unsigned int				fEncrypt : 1;						// Encryption required

	int							mtu;								// negotiated MTU

	int							imax_mtu;							// Min MTU
	int							imin_mtu;							// Max MTU

	int							xon_lim;							// xon_lim
	int							xoff_lim;							// xoff_lim

	int							imax_send;							// max_send
	int							imax_recv;							// max_recv

	int							cPinLength;							// pin length or 0
	unsigned char				caPinData[PIN_SIZE];				// PIN

	unsigned int				uiConfigMask;						// Non-default configuration mask

	OPT (void) {
		fAuthenticate = 0;
		fEncrypt = 0;

		mtu = TDIR_MTU;

		imax_mtu	= TDIR_MTUMAX;
		imin_mtu	= TDIR_MTUMIN;

		xon_lim		= TDIR_XONLIM;
		xoff_lim	= TDIR_XOFFLIM;

		imax_send	= TDIR_SENDMAX;
		imax_recv	= TDIR_SENDMAX;

		cPinLength = 0;
		memset (caPinData, 0, sizeof(caPinData));

		uiConfigMask = 0;
	}
};

struct RFCOMM_ADDRESS_OBJECT : public SVSRefObj, public OPT {
	RFCOMM_ADDRESS_OBJECT		*pNext;								// Next in list
	BD_ADDR						b;									// BD_ADDR
	unsigned char				ch;									// server channel

	unsigned int				fListenOn : 1;						// server?
	unsigned int				fAssigned : 1;						// server channel dynamically reserved

    PConnectEvent               pEventConnect;						// PConnectEvent
    PVOID                       pEventConnectContext;				// PVOID
    PDisconnectEvent            pEventDisconnect;					// PDisconnectEvent
    PVOID                       pEventDisconnectContext;			// PVOID
    PErrorEvent                 pEventError;						// PErrorEvent
    PVOID                       pEventErrorContext;					// PVOID
    PRcvEvent                   pEventReceive;						// PRcvEvent
    PVOID                       pEventReceiveContext;				// PVOID
    PRcvDGEvent                 pEventReceiveDatagram;				// PRcvDGEvent
    PVOID                       pEventReceiveDatagramContext;		// PVOID
    PRcvExpEvent                pEventReceiveExpedited;				// PRcvExpEvent
    PVOID                       pEventReceiveExpeditedContext;		// PVOID

	RFCOMM_ADDRESS_OBJECT (void) : OPT () {
		pNext = NULL;
		memset (&b, 0, sizeof(b));
		ch = 0;

		fListenOn = 0;
		fAssigned = 0;

		pEventConnect = NULL;
		pEventConnectContext = NULL;
		pEventDisconnect = NULL;
		pEventDisconnectContext = NULL;
		pEventError = NULL;
		pEventErrorContext = NULL;
		pEventReceive = NULL;
		pEventReceiveContext = NULL;
		pEventReceiveDatagram = NULL;
		pEventReceiveDatagramContext = NULL;
		pEventReceiveExpedited = NULL;
		pEventReceiveExpeditedContext = NULL;
	}

	void *operator new (size_t size);
	void operator delete (void *ptr);
};

struct RFCOMM_CONNECTION_OBJECT;

struct RFCOMM_PACKET_OBJECT {
	RFCOMM_PACKET_OBJECT		*pNext;				// Next in list
	RFCOMM_CONNECTION_OBJECT	*pConn;				// Connection
	int							cBytesSent;			// sent bytes

	void *operator new (size_t size);
	void operator delete (void *ptr);
};

struct RFCOMM_SEND_OBJECT {
	RFCOMM_SEND_OBJECT			*pNext;				// Next in list
	RFCOMM_CONNECTION_OBJECT	*pConnect;			// Connection

	NDIS_BUFFER					*pNdisBuffer;		// NDIS buffer
	int							cBytesUsed;			// Bytes used in buffer
	int							cBytesConfirmed;	// Confirmed (signal if == used)

	PBT_SEND_COMPLETE			pSendCallback;		// PBT_SEND_COMPLETE
	void						*pSendContext;		// PVOID

	RFCOMM_SEND_OBJECT (void) {
		memset (this, 0, sizeof(*this));
	}

	void *operator new (size_t size);
	void operator delete (void *ptr);
};

struct RFCOMM_RECEIVE_OBJECT {
	RFCOMM_RECEIVE_OBJECT	*pNext;					// Next in list
	BD_BUFFER				*pBuffer;				// BD_BUFFER (if pending data)

	NDIS_BUFFER				*pNdisBuffer;			// NDIS_BUFFER (if waiting)
	DWORD					dwPerms;				// Perms if waiting

	PBT_RECV_COMPLETE		pRecvEvent;				// PBT_RECV_COMPLETE
	void					*pRecvContext;			// PVOID
	int						iNdisUsed;				// Used in NDIS buffer

	void *operator new (size_t size);
	void operator delete (void *ptr);
};

struct RFCOMM_CONNECTION_OBJECT : public OPT {
	RFCOMM_CONNECTION_OBJECT	*pNext;							// Next in list
	RFCOMM_ADDRESS_OBJECT		*pAddr;							// Address object
	RFCOMM_SEND_OBJECT			*pSendList;						// List of sends
	RFCOMM_PACKET_OBJECT		*pPacketList;					// List of packets
	RFCOMM_RECEIVE_OBJECT		*pRecvList;						// List of recvs or data

	HANDLE						hConnect;						// Connection (NULL = not connected)
	SOCKADDR_BTH                BTAddr;

	CONNECT_STAGE				eStage;							// CONNECT_STAGE

	int							isent;							// sent
	int							irecvd;							// received

	unsigned int				v24in;							// v24 signals
	unsigned int				brin;							// breaks
	unsigned int				rlsin;							// line status

	unsigned int				fc_credit      : 1;				// Credit-based flow control
	unsigned int				fc_local       : 1;				// 1 = don't send (MSC)
	unsigned int				fc_remote      : 1;				// 1 = don't send (MSC)
	unsigned int				fc_aggregate   : 1;				// 1 = don't send (FCON/FCOFF)
	unsigned int                fCloseHaveRecv : 1;             // socket has closed down, but there's still more data to recv() on.
	unsigned int				fCloseHaveSend : 1;				// socket has closed down, but there's still more data to send on.
	unsigned int                fNoDisconnect  : 1;				// opening state before we know if error occured

	unsigned int				fParity        : 1;				// just store; no effect on anything

	unsigned int				fSending       : 1;				// In the process of sending

	int							cbDataIndicated;				// Bytes already indicated to TDI

	int							BaudRate;						// just store; no effect on anything
	int							ByteSize;						// just store; no effect on anything
	int							StopBits;						// just store; no effect on anything
	int							Parity;							// just store; no effect on anything

	int							iHaveCredits;
	int							iGaveCredits;

#if defined (DEBUG_CREDIT_FLOW)
	int							iPacketsSent;
	int							iPacketsRecv;
	int							iCreditsSent;
	int							iCreditsRecv;
#endif

	PVOID						pConnectionContext;				// PVOID

	PBT_CONNECT_COMPLETE		pConnectCompleteEvent;			// PBT_CONNECT_COMPLETE
	void						*pConnectCompleteContext;		// PVOID

	// SDP connection setup structures
	unsigned short              sdpCid;

	RFCOMM_CONNECTION_OBJECT (void) : OPT() {
		pNext       = NULL;
		pAddr       = NULL;
		pSendList   = NULL;
		pPacketList = NULL;
		pRecvList   = NULL;

		hConnect    = NULL;
		memset (&BTAddr, 0, sizeof(BTAddr));

		eStage		= ALLOCATED;

		isent = irecvd = 0;

		v24in = brin = rlsin = 0;

		fc_credit    = FALSE;
		fc_local     = FALSE;
		fc_remote    = FALSE;
		fc_aggregate = FALSE;

		fCloseHaveRecv = FALSE;
		fCloseHaveSend = FALSE;

		fNoDisconnect  = FALSE;

		cbDataIndicated = 0;

		iHaveCredits = 0;
		iGaveCredits = 0;

		pConnectionContext      = NULL;
		pConnectCompleteEvent   = NULL;
		pConnectCompleteContext = NULL;

		sdpCid = 0;

		fSending = FALSE;

		//	These are not accessible from the inside...
		fParity = FALSE;
		BaudRate = 115200;
		ByteSize = 8;
		StopBits = 0;
		Parity   = 0;

#if defined (DEBUG_CREDIT_FLOW)
		iPacketsSent = 0;
		iPacketsRecv = 0;
		iCreditsSent = 0;
		iCreditsRecv = 0;
#endif
	}

	void *operator new (size_t size);
	void operator delete (void *ptr);
};

struct RFCOMM_SOCKET_OBJECT : OPT {
	RFCOMM_SOCKET_OBJECT		*pNext;
	RFCOMM_ADDRESS_OBJECT		*pao;

	HANDLE						hSocketHandle;

	RFCOMM_SOCKET_OBJECT (void) : OPT() {
		pNext         = NULL;
		pao           = NULL;
		hSocketHandle = NULL;
	}

	void *operator new (size_t size);
	void operator delete (void *ptr);
};

struct RFCOMM_PN_OBJECT {
	RFCOMM_PN_OBJECT			*pNext;
	HANDLE						hConnection;
	int							imtu;

	int							fCreditFlow;		// credit-based flow control
	int							iCredits;			// credits granted

	void *operator new (size_t size);
	void operator delete (void *ptr);
};

struct RFCOMM_TDI : public SVSAllocClass, public SVSRefObj, public SVSSynch {
	FixedMemDescr				*pfmdAO;				// FMD for Address Objects
	FixedMemDescr				*pfmdCO;				// FMD for Connect Objects
	FixedMemDescr				*pfmdSO;				// FMD for Socket Objects
	FixedMemDescr				*pfmdSEND;				// FMD for sends
	FixedMemDescr				*pfmdRECV;				// FMD for recvs
	FixedMemDescr				*pfmdPL;				// FMD for data packets
	FixedMemDescr				*pfmdPN;				// FMD for parameter negotiation packets

	RFCOMM_ADDRESS_OBJECT		*paolist;				// List of addresses
	RFCOMM_CONNECTION_OBJECT	*pcolist;				// List of connections
	RFCOMM_SOCKET_OBJECT		*psolist;				// List of sockets
	RFCOMM_PN_OBJECT			*pnlist;				// List of PN associations

	HANDLE						hRFCOMM;				// RFCOMM handle
	RFCOMM_INTERFACE			rfcomm_if;				// RFCOMM interface
	unsigned int				fIsConnected : 1;		// 1 = connected
	unsigned int				fIsInitialized : 1;		// 1 = initialized

	HANDLE                      hSDP;
	SDP_INTERFACE               sdp_if;

	int							cDeviceHeader;			// pre-allocate for front
	int							cDeviceTrailer;			// pre-allocate for rear

	BT_ADDR						btAddr;					// address of local device
	HANDLE						hHCI;					// handles to HCI layer to read the address
	HCI_INTERFACE				hci_if;


	void reset (void) {
		paolist = NULL;
		pcolist = NULL;
		psolist = NULL;
		pnlist  = NULL;

		hRFCOMM = NULL;
		memset (&rfcomm_if, 0, sizeof(rfcomm_if));

		hSDP    = NULL;
		memset (&sdp_if, 0, sizeof(sdp_if));

		hHCI    = NULL;
		memset(&hci_if, 0, sizeof(hci_if));

		fIsConnected = fIsInitialized = FALSE;

		cDeviceHeader = cDeviceTrailer = 0;

		memset (&btAddr, 0, sizeof(btAddr));
	}

	RFCOMM_TDI (void) {
		pfmdAO   = NULL;
		pfmdCO   = NULL;
		pfmdSO   = NULL;
		pfmdSEND = NULL;
		pfmdRECV = NULL;
		pfmdPL   = NULL;
		pfmdPN   = NULL;

		reset ();
	}

	~RFCOMM_TDI(void) {
		if (pfmdAO)
			svsutil_ReleaseFixedEmpty (pfmdAO);

		if (pfmdCO)
			svsutil_ReleaseFixedEmpty (pfmdCO);

		if (pfmdSO)
			svsutil_ReleaseFixedEmpty (pfmdSO);

		if (pfmdSEND)
			svsutil_ReleaseFixedEmpty (pfmdSEND);

		if (pfmdRECV)
			svsutil_ReleaseFixedEmpty (pfmdRECV);

		if (pfmdPL)
			svsutil_ReleaseFixedEmpty (pfmdPL);

		if (pfmdPN)
			svsutil_ReleaseFixedEmpty (pfmdPN);
		}

	int Init (void);
};

void sdp_UnLoadWSANameSpaceProvider(void);
void sdp_LoadWSANameSpaceProvider(void);

//
//	Globals
//
static RFCOMM_TDI	*gpTDIR = NULL;

//
//	Utility functions
//
static int ExtractPN (HANDLE hConnection, int *pmtu, int *pfcreditflow, int *picreditsgranted) {
	RFCOMM_PN_OBJECT *pPNO = gpTDIR->pnlist;
	RFCOMM_PN_OBJECT *pParent = NULL;
	while (pPNO && (pPNO->hConnection != hConnection)) {
		pParent = pPNO;
		pPNO = pPNO->pNext;
	}

	if (! pPNO)
		return FALSE;

	if (pmtu)
		*pmtu             = pPNO->imtu;

	if (pfcreditflow)
		*pfcreditflow     = pPNO->fCreditFlow;

	if (picreditsgranted)
		*picreditsgranted = pPNO->iCredits;

	if (pParent)
		pParent->pNext = pPNO->pNext;
	else
		gpTDIR->pnlist = pPNO->pNext;

	delete pPNO;

	return TRUE;
}

static RFCOMM_SOCKET_OBJECT *GetSocketObject (HANDLE h) {
	RFCOMM_SOCKET_OBJECT *pSock = gpTDIR->psolist;
	while (pSock && (pSock != h))
		pSock = pSock->pNext;

	return pSock;
}

static RFCOMM_SOCKET_OBJECT *GetSocketByHandle (HANDLE h) {
	RFCOMM_SOCKET_OBJECT *pSock = gpTDIR->psolist;
	while (pSock && (pSock->hSocketHandle != h))
		pSock = pSock->pNext;

	return pSock;
}

static RFCOMM_ADDRESS_OBJECT *GetAddressObject (HANDLE h) {
	RFCOMM_ADDRESS_OBJECT *pA = gpTDIR->paolist;
	while (pA && (pA != h))
		pA = pA->pNext;

	return pA;
}

static RFCOMM_CONNECTION_OBJECT *GetConnectionObject (HANDLE h) {
	RFCOMM_CONNECTION_OBJECT *pC = gpTDIR->pcolist;
	while (pC && (pC != h))
		pC = pC->pNext;

	return pC;
}

static RFCOMM_SEND_OBJECT *GetSendObject (HANDLE h) {
	HANDLE hConnectObj = NULL;

	__try {
		hConnectObj = (HANDLE)((RFCOMM_SEND_OBJECT *)h)->pConnect;
	} __except (1) {
		IFDBG(DebugOut (DEBUG_WARN, L"RFCOMM_SEND_OBJECT faulted: returning FALSE\n"));
		return NULL;
	}

	RFCOMM_CONNECTION_OBJECT *pC = GetConnectionObject (hConnectObj);

	if (! pC)
		return NULL;

	RFCOMM_SEND_OBJECT *pSendO = pC->pSendList;

	while (pSendO && (pSendO != h))
		pSendO = pSendO->pNext;

	return pSendO;
}

static RFCOMM_PACKET_OBJECT *GetPacketObject (HANDLE h) {
	if (! h) {
		IFDBG(DebugOut (DEBUG_WARN, L"GetPacketObject failed: NULL context\n"));
		return NULL;
	}

	HANDLE hConnectObj = NULL;

	__try {
		hConnectObj = (HANDLE)((RFCOMM_PACKET_OBJECT *)h)->pConn;
	} __except (1) {
		IFDBG(DebugOut (DEBUG_WARN, L"GetPacketObject faulted: returning FALSE\n"));
		return NULL;
	}

	RFCOMM_CONNECTION_OBJECT *pC = GetConnectionObject (hConnectObj);

	if (! pC)
		return NULL;

	RFCOMM_PACKET_OBJECT *pP = pC->pPacketList;

	while (pP && (pP != h))
		pP = pP->pNext;

	return pP;
}

static RFCOMM_CONNECTION_OBJECT *GetConnectionByHandle (HANDLE h) {
	RFCOMM_CONNECTION_OBJECT *pC = gpTDIR->pcolist;
	while (pC && (pC->hConnect != h))
		pC = pC->pNext;

	return pC;
}

static RFCOMM_CONNECTION_OBJECT *GetConnectionByContext (void *pCtx) {
	RFCOMM_CONNECTION_OBJECT *pC = gpTDIR->pcolist;
	while (pC && (pC->pConnectionContext != pCtx))
		pC = pC->pNext;

	return pC;
}

static void GetConnectionState (void) {
	gpTDIR->fIsConnected = FALSE;

	__try {
		int fConnected = FALSE;
		int dwRet = 0;
		gpTDIR->hci_if.hci_ioctl (gpTDIR->hHCI, BTH_STACK_IOCTL_GET_CONNECTED, 0, NULL, sizeof(fConnected), (char *)&fConnected, &dwRet);
		if ((dwRet == sizeof(fConnected)) && fConnected)
			gpTDIR->fIsConnected = TRUE;
	} __except (1) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] GetConnectionState : exception in hci_ioctl BTH_STACK_IOCTL_GET_CONNECTED\n"));
	}
}

static int CloseAddressObject (RFCOMM_ADDRESS_OBJECT *pao) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"CloseAddressObject 0x%08x\n", pao));

	RFCOMM_ADDRESS_OBJECT *pA = gpTDIR->paolist;
	RFCOMM_ADDRESS_OBJECT *pP = NULL;
	while (pA && (pA != pao)) {
		pP = pA;
		pA = pA->pNext;
	}

	if (! pA)
		return ERROR_NOT_FOUND;

	if (pP)
		pP->pNext = pA->pNext;
	else
		gpTDIR->paolist = pA->pNext;

	unsigned char cPort = pA->ch;
	int fAssigned = pA->fAssigned;

	delete pA;

	// unbind the socket...
	RFCOMM_SOCKET_OBJECT *pso = gpTDIR->psolist;
	while (pso) {
		if (pso->pao == pao)
			pso->pao = NULL;
		pso = pso->pNext;
	}

	// disassociate connections
	RFCOMM_CONNECTION_OBJECT *pco = gpTDIR->pcolist;
	while (pco) {
		if (pco->pAddr == pA) {
			SVSUTIL_ASSERT (0);
			pco->pAddr = NULL;
		}

		pco = pco->pNext;
	}

	if (gpTDIR->fIsInitialized && fAssigned) {
		HANDLE h = gpTDIR->hRFCOMM;
		BT_LAYER_IO_CONTROL pCallback = gpTDIR->rfcomm_if.rfcomm_ioctl;
		gpTDIR->AddRef ();
		gpTDIR->Unlock ();

		__try {
			pCallback (h, BTH_STACK_IOCTL_FREE_PORT, sizeof(cPort), (char *)&cPort, 0, NULL, NULL);
		} __except (1) {
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] CloseAddressObject Exception in BTH_STACK_IOCTL_FREE_PORT\n"));
		}

		gpTDIR->Lock ();
		gpTDIR->DelRef ();
	}

	return ERROR_SUCCESS;
}

static int DisassociateConnection (RFCOMM_CONNECTION_OBJECT *pC) {
	if (pC->pAddr) {
		pC->pAddr->DelRef ();

		RFCOMM_ADDRESS_OBJECT *pAddr = pC->pAddr;
		pC->pAddr = NULL;

		if (pAddr->GetRefCount () == 1)
			CloseAddressObject (pAddr);
	}

	return ERROR_SUCCESS;
}

static void TransferOptions (RFCOMM_CONNECTION_OBJECT *pConn, OPT *pOpts) {
	if (pOpts->uiConfigMask & OPT_CONFIG_MTU)
		pConn->imax_mtu      = pOpts->imax_mtu;

	if (pOpts->uiConfigMask & OPT_CONFIG_MTU_MIN)
		pConn->imin_mtu      = pOpts->imin_mtu;

	if (pOpts->uiConfigMask & OPT_CONFIG_MAX_SEND)
		pConn->imax_send     = pOpts->imax_send;

	if (pOpts->uiConfigMask & OPT_CONFIG_MAX_RECV)
		pConn->imax_recv     = pOpts->imax_recv;

	if (pOpts->uiConfigMask & OPT_CONFIG_XOFF)
		pConn->xoff_lim      = pOpts->xoff_lim;

	if (pOpts->uiConfigMask & OPT_CONFIG_XON)
		pConn->xon_lim       = pOpts->xon_lim;

	if (pOpts->uiConfigMask & OPT_CONFIG_AUTH)
		pConn->fAuthenticate = pOpts->fAuthenticate;

	if (pOpts->uiConfigMask & OPT_CONFIG_CRYPT)
		pConn->fEncrypt      = pOpts->fEncrypt;

	if (pOpts->uiConfigMask & OPT_CONFIG_MTU)
		pConn->mtu           = pOpts->mtu;

	if (pOpts->uiConfigMask & OPT_CONFIG_PIN) {
		pConn->cPinLength    = pOpts->cPinLength;
		memcpy (pConn->caPinData, pOpts->caPinData, sizeof(pConn->caPinData));
	}

	SVSUTIL_ASSERT ((pConn->cPinLength >= 0) && (pConn->cPinLength <= PIN_SIZE));
}

static void ResetConnection (RFCOMM_CONNECTION_OBJECT *pConn, int fClearCredits) {
	pConn->fc_local = pConn->fc_remote = pConn->fc_aggregate = FALSE;
	pConn->brin = pConn->v24in = pConn->rlsin = 0;
	pConn->irecvd          = pConn->isent = 0;

	pConn->fCloseHaveRecv = FALSE;
	pConn->fCloseHaveSend = FALSE;

	pConn->cbDataIndicated = FALSE;

	pConn->fSending = FALSE;

	pConn->fNoDisconnect = FALSE;

	if (fClearCredits) {
		pConn->fc_credit       = FALSE;
		pConn->iHaveCredits    = 0;
		pConn->iGaveCredits    = 0;

#if defined (DEBUG_CREDIT_FLOW)
		pConn->iPacketsSent = 0;
		pConn->iPacketsRecv = 0;
		pConn->iCreditsSent = 0;
		pConn->iCreditsRecv = 0;
#endif
	}
}

static int DisconnectConnection (RFCOMM_CONNECTION_OBJECT *pConn, HANDLE hConn, void *pCtx) {
	IFDBG(DebugOut (DEBUG_TDI_PACKETS, L"DisconnectConnection (0x%08x, 0x%08x, 0x%08x)\n", pConn, hConn, pCtx));

	if (pConn) {
		hConn = pConn->hConnect;
		pConn->hConnect = NULL;
	}

	int iRes = ERROR_SUCCESS;

	if (gpTDIR->fIsConnected && hConn) {
		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"DisconnectConnection : physically closing 0x%08x\n", hConn));

		HANDLE h = gpTDIR->hRFCOMM;
		RFCOMM_Disconnect_In pCallback = gpTDIR->rfcomm_if.rfcomm_Disconnect_In;
		gpTDIR->AddRef ();
		gpTDIR->Unlock ();
		__try {
			iRes = pCallback (h, pCtx, hConn);
		} __except (1) {
			iRes = ERROR_INTERNAL_ERROR;
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] DisconnectConnection : exception in rfcomm_Disconnect_In\n"));
		}
		gpTDIR->Lock ();
		gpTDIR->DelRef ();
	}

	return iRes;
}

static int CloseConnectionObject (RFCOMM_CONNECTION_OBJECT *pco) {
	IFDBG(DebugOut (DEBUG_TDI_PACKETS, L"CloseConnectionObject 0x%08x\n", pco));

	RFCOMM_CONNECTION_OBJECT *pC = gpTDIR->pcolist;
	RFCOMM_CONNECTION_OBJECT *pP = NULL;

	while (pC && (pC != pco)) {
		pP = pC;
		pC = pC->pNext;
	}

	if (! pC)
		return ERROR_NOT_FOUND;

	if (pP)
		pP->pNext = pC->pNext;
	else
		gpTDIR->pcolist = pC->pNext;

	HANDLE hConn = pC->hConnect;

	SVSUTIL_ASSERT (pC->pPacketList == NULL);
	SVSUTIL_ASSERT (pC->pSendList   == NULL);
	SVSUTIL_ASSERT (pC->pRecvList   == NULL);
	SVSUTIL_ASSERT (pC->pAddr       == NULL);

	bt_addr bt = pC->BTAddr.btAddr;
	int cPinLength = pC->cPinLength;

	DisassociateConnection (pC);

	delete pC;

	DisconnectConnection (NULL, hConn, NULL);

	if (cPinLength != 0) {
		gpTDIR->AddRef ();
		gpTDIR->Unlock ();

		__try {
			BthRevokePIN (&bt);
		} __except (1) {
		}

		gpTDIR->Lock ();
		gpTDIR->DelRef ();
	}

	return ERROR_SUCCESS;
}

static int CloseSocketObject (RFCOMM_SOCKET_OBJECT *pso) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"CloseSocketObject 0x%08x\n", pso));

	RFCOMM_SOCKET_OBJECT *pS = gpTDIR->psolist;
	RFCOMM_SOCKET_OBJECT *pP = NULL;
	while (pS && (pS != pso)) {
		pP = pS;
		pS = pS->pNext;
	}

	if (! pS)
		return ERROR_NOT_FOUND;

	if (pP)
		pP->pNext = pS->pNext;
	else
		gpTDIR->psolist = pS->pNext;

	delete pS;

	return ERROR_SUCCESS;
}

static void SignalConnectionCompleteUp (RFCOMM_CONNECTION_OBJECT *pC, int iError) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"SignalConnectionCompleteUp 0x%08x : %d\n", pC, iError));

	if (iError != TDI_SUCCESS)
		pC->eStage = CLOSED;
	else
		pC->eStage = OPENED;

	PBT_CONNECT_COMPLETE		pConnectCompleteEvent    = pC->pConnectCompleteEvent;
	void						*pConnectCompleteContext = pC->pConnectCompleteContext;

	pC->pConnectCompleteContext = NULL;
	pC->pConnectCompleteEvent   = NULL;

	SVSUTIL_ASSERT (pConnectCompleteEvent);
	gpTDIR->AddRef ();
	gpTDIR->Unlock ();

	pConnectCompleteEvent (pConnectCompleteContext, iError, 0);

	gpTDIR->Lock ();
	gpTDIR->DelRef ();
}

static int SignalConnectionLoss (RFCOMM_CONNECTION_OBJECT *pC, TDI_STATUS fHow, BOOL fImmediateCleanup) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"SignalConnectionLoss : closing connection 0x%08x to %04x%08x 0x%02x\n", pC, GET_NAP(pC->BTAddr.btAddr), GET_SAP(pC->BTAddr.btAddr), pC->BTAddr.port));
	CONNECT_STAGE eStage = pC->eStage;

	pC->eStage = CLOSED;

	while (pC->pPacketList) {
		RFCOMM_PACKET_OBJECT	*pNext = pC->pPacketList->pNext;
		delete pC->pPacketList;
		pC->pPacketList = pNext;
	}

	int iRes = TDI_SUCCESS;

	if (pC->pConnectCompleteEvent && (! pC->fNoDisconnect)) {
		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"SignalConnectionLoss : signalling connection abort to complete event\n"));
		SignalConnectionCompleteUp (pC, TDI_CONNECTION_ABORTED);
		pC = GetConnectionObject ((HANDLE)pC);
	}

	if ((! fImmediateCleanup) && (eStage == OPENED) && pC && pC->pRecvList && pC->pRecvList->pBuffer) {
		// there's still data to recv(), and we want to leave it around.
		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"SignalConnectionLoss : waiting to flush the recv list...\n"));
		pC->fCloseHaveRecv = TRUE;
	} else if (pC && ((eStage == OPENED) || fImmediateCleanup || (pC->fCloseHaveRecv && (! (pC->pRecvList && pC->pRecvList->pBuffer))))) {
		pC->fCloseHaveRecv = FALSE;

		while (pC && pC->pRecvList) {
			RFCOMM_RECEIVE_OBJECT *pThis = pC->pRecvList;
			pC->pRecvList = pC->pRecvList->pNext;

			if (pThis->pBuffer && pThis->pBuffer->pFree)
				pThis->pBuffer->pFree (pThis->pBuffer);

			NDIS_BUFFER       *pNdisBuffer = pThis->pNdisBuffer;
			PBT_RECV_COMPLETE  pCallback   = pThis->pRecvEvent;
			void			  *pContext    = pThis->pRecvContext;
			int               iReceived    = pThis->iNdisUsed;

			delete pThis;

			IFDBG(DebugOut (DEBUG_TDI_TRACE, L"SignalConnectionLoss : cleaning up ndis buffer 0x%08x context 0x%08x, received %d\n", pNdisBuffer, pContext, iReceived));

			if (pNdisBuffer) {
				gpTDIR->AddRef ();
				gpTDIR->Unlock ();

				pCallback (pContext, fHow, iReceived);

				gpTDIR->Lock ();
				gpTDIR->DelRef ();
			}

			pC = GetConnectionObject ((HANDLE)pC);
		}

		while (pC && pC->pSendList) {
			RFCOMM_SEND_OBJECT *pNext = pC->pSendList->pNext;
			PBT_SEND_COMPLETE pCallback = pC->pSendList->pSendCallback;
			void *pCallbackContext = pC->pSendList->pSendContext;
			int cBytesSent = pC->pSendList->cBytesUsed;
			delete pC->pSendList;
			pC->pSendList = pNext;

			IFDBG(DebugOut (DEBUG_TDI_TRACE, L"SignalConnectionLoss : cleaning up send list context 0x%08x, sent %d\n", pCallbackContext, cBytesSent));

			gpTDIR->AddRef ();
			gpTDIR->Unlock ();

			pCallback (pCallbackContext, TDI_CONNECTION_ABORTED, cBytesSent);

			gpTDIR->Lock ();
			gpTDIR->DelRef ();

			pC = GetConnectionObject ((HANDLE)pC);
		}

		if (pC && pC->pAddr && pC->pAddr->pEventDisconnect) {
			PDisconnectEvent pEventDisconnect = pC->pAddr->pEventDisconnect;
			void *pEventContext = pC->pAddr->pEventDisconnectContext;
			void *pConnectionContext = pC->pConnectionContext;

			IFDBG(DebugOut (DEBUG_TDI_PACKETS, L"SignalConnectionLoss : forwarding up disconnection event conn 0x%08x\n", pC));

			gpTDIR->AddRef ();
			gpTDIR->Unlock ();
			iRes = pEventDisconnect (pEventContext, pConnectionContext, 0, NULL, 0, NULL,
										TDI_DISCONNECT_WAIT);
			gpTDIR->Lock ();
			gpTDIR->DelRef ();
		} else {
			IFDBG(DebugOut (DEBUG_WARN, L"[TDI] SignalConnectionLoss : nothing reported for conn 0x%08x\n", pC));
		}
	} else {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] SignalConnectionLoss : already closed\n"));
		SVSUTIL_ASSERT ((! pC) || ((! pC->pSendList) && (! pC->pRecvList)));
	}

	return iRes;
}

int SendMSC (RFCOMM_CONNECTION_OBJECT *pConn, unsigned char v24out, unsigned char brout) {
	HANDLE h = gpTDIR->hRFCOMM;
	RFCOMM_MSC_In pCallback = gpTDIR->rfcomm_if.rfcomm_MSC_In;
	HANDLE hConnection = pConn->hConnect;

	unsigned char v24 = TDIR_V24_CONST | (pConn->fc_local ? TDIR_V24_FC : 0) | v24out;

	gpTDIR->AddRef ();
	gpTDIR->Unlock ();

	int iRes = ERROR_INTERNAL_ERROR;
	__try {
		iRes = pCallback (h, NULL, hConnection, v24, brout);
	} __except (1) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] SendMSC Exception in rfcomm_MSC_In\n"));
	}

	gpTDIR->Lock ();
	gpTDIR->DelRef ();

	return iRes;
}

int SendRLS (RFCOMM_CONNECTION_OBJECT *pConn, unsigned char rls) {
	HANDLE h = gpTDIR->hRFCOMM;
	RFCOMM_RLS_In pCallback = gpTDIR->rfcomm_if.rfcomm_RLS_In;
	HANDLE hConnection = pConn->hConnect;

	gpTDIR->AddRef ();
	gpTDIR->Unlock ();

	int iRes = ERROR_INTERNAL_ERROR;
	__try {
		iRes = pCallback (h, NULL, hConnection, rls);
	} __except (1) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] SendMSC Exception in rfcomm_MSC_In\n"));
	}

	gpTDIR->Lock ();
	gpTDIR->DelRef ();

	return iRes;
}

static DWORD WINAPI StackDisconnect (LPVOID lpUnused) {
	tdiR_CloseDriverInstance ();
	return 0;
}

static DWORD WINAPI StackXXX (LPVOID lpWhat) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+StackXXX\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] StackXXX : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	for ( ; ; ) {
		gpTDIR->Lock ();

		if (! gpTDIR->fIsInitialized) {
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] StackXXX : ERROR_SERVICE_NOT_ACTIVE\n"));
			gpTDIR->Unlock ();
			return ERROR_SERVICE_NOT_ACTIVE;
		}

		if (gpTDIR->GetRefCount() == 1)
			break;

		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"Waiting for ref count in StackXXX\n"));
		gpTDIR->Unlock ();
		Sleep (100);
	}

	if ((int)lpWhat == 1) {
		gpTDIR->fIsConnected = TRUE;
	} else if (((int)lpWhat == 2) || ((int)lpWhat == 3)) {

		while (gpTDIR->pnlist) {
			RFCOMM_PN_OBJECT *pNext = gpTDIR->pnlist->pNext;
			delete gpTDIR->pnlist;
			gpTDIR->pnlist = pNext;
		}

		gpTDIR->fIsConnected = FALSE;
		gpTDIR->AddRef ();

		for ( ; ; ) {
			RFCOMM_CONNECTION_OBJECT *pC = gpTDIR->pcolist;
			while (pC && (pC->eStage != OPENED) && (pC->eStage != OPENING))
				pC = pC->pNext;

			if (! pC)
				break;

			SignalConnectionLoss (pC, TDI_CONNECTION_ABORTED, TRUE);
		}

		gpTDIR->DelRef ();
		if ((int)lpWhat == 3)
			gpTDIR->fIsConnected = TRUE;
	}

	int iErr = ERROR_SUCCESS;
	if (gpTDIR->fIsConnected) {
		HANDLE h = gpTDIR->hHCI;
		HCI_ReadBDADDR_In pCallback = gpTDIR->hci_if.hci_ReadBDADDR_In;
		gpTDIR->AddRef ();
		gpTDIR->Unlock ();

		__try {
			iErr = pCallback (h, NULL);
		} __except (1) {
			iErr = ERROR_INTERNAL_ERROR;
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] : exception in StackXXX!\n"));
		}

		gpTDIR->Lock ();
		gpTDIR->DelRef ();
	}
	else
		gpTDIR->btAddr = SET_NAP_SAP(0, 0);

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-StackXXX ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return iErr;
}

static int QueryNdisSize (NDIS_BUFFER *pNdisB) {
	int cSize = 0;
	while (pNdisB) {
		PVOID pVA = NULL;
		unsigned int uiLength = 0;

		NdisQueryBuffer (pNdisB, &pVA, &uiLength);

		if (! pVA)
			break;

		cSize += uiLength;
		NdisGetNextBuffer(pNdisB, &pNdisB);
	}

	return cSize;
}

static int CopyNdisToFlat
(
NDIS_BUFFER		*pNdisB,
unsigned char	*pFlatBuf,
int				cCount,
int				cStartOffset
) {
	int cOffset = 0;
	int cCopied = 0;
	while (pNdisB) {
		unsigned char *pVA = NULL;
		unsigned int uiLength = 0;

		NdisQueryBuffer (pNdisB, (PVOID *)&pVA, &uiLength);

		if (! pVA)
			break;

		if (cOffset + (int)uiLength > cStartOffset) {
			SVSUTIL_ASSERT (cOffset <= cStartOffset);
			int cBytesAvailable = cOffset + uiLength - cStartOffset;
			int cOffsetInBuffer = cStartOffset - cOffset;
			int cBytesToCopy = cBytesAvailable > cCount ? cCount : cBytesAvailable;
			memcpy (pFlatBuf, pVA + cOffsetInBuffer, cBytesToCopy);

			cCount -= cBytesToCopy;
			cStartOffset += cBytesToCopy;
			cCopied += cBytesToCopy;
			pFlatBuf += cBytesToCopy;

			SVSUTIL_ASSERT (cCount >= 0);

			if (cCount == 0)
				break;
		}

		cOffset += uiLength;
		NdisGetNextBuffer(pNdisB, &pNdisB);
	}

	return cCopied;
}

static int CountPendingSends (RFCOMM_CONNECTION_OBJECT *pConn) {
	RFCOMM_SEND_OBJECT *pSendList = pConn->pSendList;
	int cTotalSize = 0;

	while (pSendList) {
		int cBytes = QueryNdisSize (pSendList->pNdisBuffer) - pSendList->cBytesUsed;
		SVSUTIL_ASSERT (cBytes >= 0);
		cTotalSize += cBytes;
		pSendList = pSendList->pNext;
	}

	return cTotalSize;
}

static int TransferPendingSends (RFCOMM_CONNECTION_OBJECT *pConn, unsigned char *pBuff, int cBuff) {
	RFCOMM_SEND_OBJECT *pSendList = pConn->pSendList;
	int cTotalSent = 0;

	while (pSendList && (cBuff > 0)) {
		int cBytes = QueryNdisSize (pSendList->pNdisBuffer) - pSendList->cBytesUsed;
		SVSUTIL_ASSERT (cBytes >= 0);
		if (cBytes > cBuff)
			cBytes = cBuff;

		if (cBytes > 0) {
			int cTransf = CopyNdisToFlat (pSendList->pNdisBuffer, pBuff, cBytes, pSendList->cBytesUsed);
			SVSUTIL_ASSERT (cTransf == cBytes);

			cBuff -= cBytes;
			pBuff += cBytes;
			pSendList->cBytesUsed += cBytes;
		}

		cTotalSent += cBytes;
		pSendList = pSendList->pNext;
	}

	SVSUTIL_ASSERT (cBuff == 0);
	return cTotalSent;
}

static int GetQuota (RFCOMM_CONNECTION_OBJECT *pC) {
	if (pC->fc_credit && (pC->imax_recv < (pC->mtu * 2 * PORTEMU_CREDITS_LOWEST)))
		return pC->mtu * 2 * PORTEMU_CREDITS_LOWEST;

	return pC->imax_recv;
}

static int GiveCredits (RFCOMM_CONNECTION_OBJECT *pC) {
	if ((! pC->fc_credit) || (pC->iGaveCredits > PORTEMU_CREDITS_LOW))
		return 0;

	int nCredits = (GetQuota (pC) - pC->irecvd) / pC->mtu - pC->iGaveCredits;

	return nCredits > 0 ? nCredits : 0;
}

static void SendCredits (RFCOMM_CONNECTION_OBJECT *pC) {
	SVSUTIL_ASSERT (pC->fc_credit);

	if (pC->iGaveCredits > PORTEMU_CREDITS_LOWEST)
		return;

	int nCredits = (GetQuota (pC) - pC->irecvd) / pC->mtu - pC->iGaveCredits;

	if (nCredits <= 0)
		return;

	IFDBG(DebugOut (DEBUG_TDI_PACKETS, L"[TDIR] SendCredits 0x%08x : sending %d credits\n", pC, nCredits));

	BD_BUFFER *pBuffer = BufferAlloc (gpTDIR->cDeviceHeader + gpTDIR->cDeviceTrailer + DEBUG_OVERHEAD);

	if (! pBuffer) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] SendCredits 0x%08x :  <out of memory>\n", pC));
		return;
	}

	pBuffer->cStart = gpTDIR->cDeviceHeader;
	pBuffer->cEnd   = pBuffer->cSize - gpTDIR->cDeviceTrailer;

	SVSUTIL_ASSERT (BufferTotal (pBuffer) == DEBUG_OVERHEAD);

	pC->iGaveCredits += nCredits;

#if defined (DEBUG_CREDIT_FLOW)
	pC->iCreditsSent += nCredits;
#endif

#if defined (DEBUG_PEER_SYNCHRONIZATION)
	PeerDebugData pd;

#if defined (DEBUG_CREDIT_FLOW)
	pd.iGaveCredits = pC->iGaveCredits;
	pd.iHaveCredits = pC->iHaveCredits;
	pd.iPacketsRecv = pC->iPacketsRecv;
	pd.iPacketsSent = pC->iPacketsSent;
	pd.iCreditsRecv = pC->iCreditsRecv;
	pd.iCreditsSent = pC->iCreditsSent;
#endif

	SVSUTIL_ASSERT (sizeof(pd) == DEBUG_OVERHEAD);
	memcpy (pBuffer->pBuffer + pBuffer->cEnd - sizeof(pd), &pd, sizeof(pd));
#endif

	HANDLE h = gpTDIR->hRFCOMM;
	HANDLE hConn = pC->hConnect;
	RFCOMM_DataDown_In pCallback = gpTDIR->rfcomm_if.rfcomm_DataDown_In;

	gpTDIR->Unlock ();
	__try {
		pCallback (h, NULL, hConn, pBuffer, nCredits);
	} __except (1) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] SendCredits : exception in rfcomm_DataDown_In\n"));
	}

	gpTDIR->Lock ();
}

static TDI_STATUS SendData (RFCOMM_CONNECTION_OBJECT *pConn) {
	if (pConn != GetConnectionObject ((HANDLE)pConn)) {
		IFDBG(DebugOut (DEBUG_WARN, L"[TDIR] SendData : Connection 0x%08x not found\n", pConn));
		return TDI_INVALID_CONNECTION;
	}

	if (pConn->fSending)		// Guard against packets coming out of order
		return TDI_PENDING;

	for ( ; ; ) {
		if (! gpTDIR->fIsConnected) {
			IFDBG(DebugOut (DEBUG_WARN, L"[TDIR] SendData : disconnected\n"));
			return TDI_INVALID_STATE;
		}

		if (pConn->eStage != OPENED) {
			IFDBG(DebugOut (DEBUG_WARN, L"[TDIR] SendData : Connection 0x%08x not OPEN\n", pConn));
			return TDI_CONNECTION_RESET;
		}

		if ((pConn->isent >= pConn->imax_send) ||
			(pConn->fc_credit && (pConn->iHaveCredits <= 0)) ||
			((! pConn->fc_credit) && (pConn->fc_remote || pConn->fc_aggregate)))
			break;

		int cSend = CountPendingSends (pConn);
		SVSUTIL_ASSERT (cSend >= 0);

		if (cSend == 0) {
			if (pConn->fCloseHaveSend) {
				SignalConnectionLoss (pConn, TDI_GRACEFUL_DISC, TRUE);
				if (gpTDIR->fIsInitialized && (pConn == GetConnectionObject ((HANDLE)pConn)))
					DisconnectConnection (pConn, NULL, NULL);
			}

			break;
		}

		if (cSend > pConn->mtu)
			cSend = pConn->mtu;

		if (cSend > pConn->imax_send - pConn->isent)
			cSend = pConn->imax_send - pConn->isent;

		RFCOMM_PACKET_OBJECT *pPacket = new RFCOMM_PACKET_OBJECT;
		if (! pPacket) {
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] SendData <out of memory>\n"));
			break;
		}

		BD_BUFFER *pBuffer = BufferAlloc (cSend + gpTDIR->cDeviceHeader + gpTDIR->cDeviceTrailer + DEBUG_OVERHEAD);

		if (! pBuffer) {
			delete pPacket;
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] SendData <out of memory>\n"));
			break;
		}

		pPacket->pConn = pConn;
		pPacket->cBytesSent = cSend;
		pPacket->pNext = pConn->pPacketList;
		pConn->pPacketList = pPacket;

		pBuffer->cEnd = pBuffer->cSize - gpTDIR->cDeviceTrailer;
		pBuffer->cStart = gpTDIR->cDeviceHeader;

		SVSUTIL_ASSERT (BufferTotal (pBuffer) == (int)(cSend + DEBUG_OVERHEAD));

		int cCopiedData = TransferPendingSends (pConn, pBuffer->pBuffer + pBuffer->cStart, cSend);
		SVSUTIL_ASSERT (cCopiedData == cSend);

		pConn->isent += cSend;

		int iCredits = GiveCredits (pConn);

		if (pConn->fc_credit) {
			--pConn->iHaveCredits;
			pConn->iGaveCredits += iCredits;

#if defined (DEBUG_CREDIT_FLOW)
			pConn->iCreditsSent += iCredits;
			pConn->iPacketsSent += 1;

			if (pConn->iGaveCredits != (pConn->iCreditsSent - pConn->iPacketsRecv)) {
				SVSUTIL_ASSERT (0);
				RETAILMSG(1, (L"Bluetooth TDI Credit mismatch: GaveCredits = %d Total SentCredits = %d Total PacketsRecv = %d\n", pConn->iGaveCredits, pConn->iCreditsSent, pConn->iPacketsRecv));
			}

			if (pConn->iHaveCredits != (pConn->iCreditsRecv - pConn->iPacketsSent)) {
				RETAILMSG(1, (L"Bluetooth TDI Credit mismatch: HaveCredits = %d Total CreditsRecv = %d Total PacketsSent = %d\n", pConn->iHaveCredits, pConn->iCreditsRecv, pConn->iPacketsSent));
				SVSUTIL_ASSERT (0);
			}
#endif
		}

#if defined (DEBUG_PEER_SYNCHRONIZATION)
		PeerDebugData pd;

#if defined (DEBUG_CREDIT_FLOW)
		pd.iGaveCredits = pConn->iGaveCredits;
		pd.iHaveCredits = pConn->iHaveCredits;
		pd.iPacketsRecv = pConn->iPacketsRecv;
		pd.iPacketsSent = pConn->iPacketsSent;
		pd.iCreditsRecv = pConn->iCreditsRecv;
		pd.iCreditsSent = pConn->iCreditsSent;
#endif

		SVSUTIL_ASSERT (sizeof(pd) == DEBUG_OVERHEAD);
		memcpy (pBuffer->pBuffer + pBuffer->cEnd - sizeof(pd), &pd, sizeof(pd));
#endif

		HANDLE h = gpTDIR->hRFCOMM;
		HANDLE hConnect = pConn->hConnect;
		RFCOMM_DataDown_In pCallback = gpTDIR->rfcomm_if.rfcomm_DataDown_In;

		IFDBG(DebugOut (DEBUG_TDI_PACKETS, L"SendData 0x%08x : sending %d bytes and %d credits\n", pConn, BufferTotal(pBuffer), iCredits));

		pConn->fSending = TRUE;

		gpTDIR->AddRef ();
		gpTDIR->Unlock ();
		__try {
			int iRes = pCallback (h, pPacket, hConnect, pBuffer, iCredits);
#if defined (DEBUG) || defined (_DEBUG) || defined (RETAILLOG)
			if (iRes != ERROR_SUCCESS)
				IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] SendData return %d\n", iRes));
#endif
		} __except (1) {
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] SendData Exception in rfcomm_DataDown_In!\n"));
		}
		gpTDIR->Lock ();
		gpTDIR->DelRef ();

		if (pConn != GetConnectionObject ((HANDLE)pConn))
			return TDI_INVALID_CONNECTION;

		pConn->fSending = FALSE;
	}

	return TDI_PENDING;
}

static int RegisterTransferCompletion (RFCOMM_CONNECTION_OBJECT *pConn, int cBytes) {
	RFCOMM_SEND_OBJECT *pS = pConn->pSendList;
	pConn->isent -= cBytes;
	SVSUTIL_ASSERT (pConn->isent >= 0);

	while (pS && (cBytes > 0)) {
		int cUnconfirmed = pS->cBytesUsed - pS->cBytesConfirmed;
		int cConfirm = cUnconfirmed;
		if (cBytes < cConfirm)
			cConfirm = cBytes;
		pS->cBytesConfirmed += cConfirm;
		cBytes -= cConfirm;
		pS = pS->pNext;
	}

	SVSUTIL_ASSERT (cBytes == 0);

	for ( ; ; ) {
		if (! (gpTDIR->fIsConnected && (pConn == GetConnectionObject ((HANDLE)pConn))))
			return ERROR_OPERATION_ABORTED;

		if ((! pConn->pSendList) || (pConn->pSendList->cBytesConfirmed != QueryNdisSize (pConn->pSendList->pNdisBuffer)))
			break;
		
		RFCOMM_SEND_OBJECT *pNext = pConn->pSendList->pNext;
		PBT_SEND_COMPLETE pCallback = pConn->pSendList->pSendCallback;
		void *pCallbackContext = pConn->pSendList->pSendContext;
		int cBytesSent = pConn->pSendList->cBytesUsed;
		delete pConn->pSendList;
		pConn->pSendList = pNext;

		gpTDIR->AddRef ();
		gpTDIR->Unlock ();

		pCallback (pCallbackContext, TDI_SUCCESS, cBytesSent);

		gpTDIR->Lock ();
		gpTDIR->DelRef ();
	}

	SendData (pConn);

	return ERROR_SUCCESS;
}

static int AcceptIncomingCall (RFCOMM_ADDRESS_OBJECT *pAddr, HANDLE hConnection) {
	BD_ADDR			b;
	unsigned char	c;
	int				fLocal;
	int             fAcceptConnection = FALSE;

	int             imtu = TDIR_MTU;
	int				fcreditflow = FALSE;
	int				icredits = 0;
	
	ExtractPN (hConnection, &imtu, &fcreditflow, &icredits);

	if (ERROR_SUCCESS != gpTDIR->rfcomm_if.rfcomm_GetChannelAddress (gpTDIR->hRFCOMM, hConnection, &b, &c, &fLocal)) {
		IFDBG(DebugOut (DEBUG_WARN, L"[TDI] AcceptIncomingCall : Cannot resolve address from 0x%08x\n", hConnection));
		return FALSE;
	} else
		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"Connection from %04x%08x 0x%02x accepted\n", b.NAP, b.SAP, c));

	BYTE				AddrBuf[sizeof(TRANSPORT_ADDRESS) + sizeof(SOCKADDR_BTH)];
	PTRANSPORT_ADDRESS	pTA = (PTRANSPORT_ADDRESS)AddrBuf;

	pTA->TAAddressCount           = 1;
	pTA->Address[0].AddressLength = sizeof(SOCKADDR_BTH) - 2;

	SOCKADDR_BTH *pSockAddr = (SOCKADDR_BTH *) &(pTA->Address[0].AddressType);
	BT_ADDR bt = SET_NAP_SAP(b.NAP, b.SAP);

	memset (pSockAddr, 0x00, sizeof(SOCKADDR_BTH));
	memcpy (&pSockAddr->btAddr, &bt, sizeof(bt));

	pSockAddr->addressFamily			= AF_BT;
	pSockAddr->port		                = c;

	void *pConnectionContext = NULL;
	ConnectEventInfo ci;
	PConnectEvent pEventConnect = pAddr->pEventConnect;

	gpTDIR->AddRef ();
	gpTDIR->Unlock ();

	TDI_STATUS TdiStatus = pEventConnect (pAddr->pEventConnectContext, TA_SIZE, 
					pTA, 0x00, NULL, 0x00, NULL, &pConnectionContext, &ci);

	gpTDIR->Lock ();
	gpTDIR->DelRef ();

	if (gpTDIR->fIsConnected && (TdiStatus == TDI_MORE_PROCESSING)) {
		RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionByContext (pConnectionContext);
		if (pConn) {
			SVSUTIL_ASSERT (pConn->pAddr);	// This has to be an associated connection!
			SVSUTIL_ASSERT (! pConn->pPacketList);
			SVSUTIL_ASSERT (! pConn->pSendList);
			SVSUTIL_ASSERT (! pConn->pRecvList);

			SVSUTIL_ASSERT ((pConn->eStage == INITED) || (pConn->eStage == OPENING));

			ResetConnection (pConn, TRUE);		// Note - this resets the flow/credits
			TransferOptions (pConn, pConn->pAddr);	// Note - this overwrites MTU

			pConn->hConnect      = hConnection;

			memset (&pConn->BTAddr, 0, sizeof(pConn->BTAddr));
			pConn->BTAddr.addressFamily = AF_BTH;
			pConn->BTAddr.port   = c;
			pConn->BTAddr.btAddr = SET_NAP_SAP(b.NAP, b.SAP);

			pConn->mtu = imtu;

			if (fcreditflow) {
				pConn->fc_credit    = TRUE;
				pConn->iGaveCredits = RFCOMM_PN_CREDIT_MAX;
				pConn->iHaveCredits = icredits;
#if defined (DEBUG_CREDIT_FLOW)
				pConn->iCreditsRecv = icredits;
				pConn->iCreditsSent = RFCOMM_PN_CREDIT_MAX;
#endif
			} else {
				pConn->fc_credit    = FALSE;
				pConn->iGaveCredits = 0;
				pConn->iHaveCredits = 0;
#if defined (DEBUG_CREDIT_FLOW)
				pConn->iCreditsRecv = 0;
				pConn->iCreditsSent = 0;
#endif
			}

			IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+AcceptIncomingCall 0x%08x : negotiated parms mtu = %d, credit fc = %s, gave credits = %d, have credits = %d\n", pConn, pConn->mtu, pConn->fc_credit ? L"on" : L"off", pConn->iGaveCredits, pConn->iHaveCredits));

			pConn->eStage = OPENED;

			gpTDIR->AddRef ();
			gpTDIR->Unlock ();

			ci.cei_rtn(ci.cei_context, TDI_SUCCESS, 0);

			gpTDIR->Lock ();
			gpTDIR->DelRef ();

			return TRUE;
		} else
			ci.cei_rtn(ci.cei_context, TDI_INVALID_CONNECTION, 0);
	} else {
		if (TdiStatus == TDI_MORE_PROCESSING)
			ci.cei_rtn(ci.cei_context, TDI_CANCELLED, 0);

		IFDBG(DebugOut (DEBUG_WARN, L"[TDI] AcceptIncomingCall : AFD doesn't want us :-(\n"));
	}

	return FALSE;
}

DWORD WINAPI AuthenticateOutgoingConnection (LPVOID arg) {
	HANDLE hConnection = (HANDLE)arg;

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+AuthenticateOutgoingConnection h = 0x%04x\n", hConnection));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] AuthenticateOutgoingConnection : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] AuthenticateOutgoingConnection : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionByHandle (hConnection);

	if (pConn) {
		SVSUTIL_ASSERT (pConn->pAddr);	// This must be an associated connection!
		SVSUTIL_ASSERT (! pConn->pPacketList);
		SVSUTIL_ASSERT (! pConn->pSendList);
		SVSUTIL_ASSERT (! pConn->pRecvList);
		SVSUTIL_ASSERT (pConn->hConnect == hConnection);
		SVSUTIL_ASSERT (pConn->fAuthenticate || pConn->fEncrypt);

		SVSUTIL_ASSERT (pConn->eStage == OPENING);

		int TdiStatus = TDI_SUCCESS;
		int fAuthenticate = pConn->fAuthenticate;
		int fEncrypt = pConn->fEncrypt;
		BT_ADDR bt = pConn->BTAddr.btAddr;

		gpTDIR->Unlock ();

		if (fAuthenticate && (BthAuthenticate (&bt) != ERROR_SUCCESS))
			TdiStatus = TDI_CONN_REFUSED;

		if (fEncrypt && (TdiStatus == TDI_SUCCESS) && (BthSetEncryption (&bt, TRUE) != ERROR_SUCCESS))
			TdiStatus = TDI_CONN_REFUSED;

		if (! gpTDIR)
			return ERROR_INTERNAL_ERROR;

		gpTDIR->Lock ();
		if (TdiStatus == TDI_SUCCESS)
			ResetConnection (pConn, FALSE);		// Leave credits be - they were set in PN process
		else
			TdiStatus = TDI_CONN_REFUSED;

		SignalConnectionCompleteUp (pConn, TdiStatus);

		if (gpTDIR->fIsConnected && (pConn == GetConnectionByHandle (hConnection)) && (pConn->eStage == OPENED))
			SendMSC (pConn, TDIR_V24_RTC|TDIR_V24_RTR, 0xff);
	} else
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] AuthenticateOutgoingConnection : no connection for handle 0x%08x\n", hConnection));

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-AuthenticateOutgoingConnection ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}


DWORD WINAPI AuthenticateConnectionRequest (LPVOID arg) {
	HANDLE hConnection = (HANDLE)arg;

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+AuthenticateConnectionRequest 0x%04x\n", hConnection));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] AuthenticateConnectionRequest : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] AuthenticateConnectionRequest : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	BD_ADDR			b;
	unsigned char	c;
	int				fLocal;
	int             fAcceptConnection = TRUE;

	if (ERROR_SUCCESS == gpTDIR->rfcomm_if.rfcomm_GetChannelAddress (gpTDIR->hRFCOMM, hConnection, &b, &c, &fLocal)) {
		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"Got connection request from %04x%08x 0x%02x\n", b.NAP, b.SAP, c));
		// Find address object...
		RFCOMM_ADDRESS_OBJECT *pAddr = gpTDIR->paolist;
		while (pAddr &&
			(! ((((pAddr->b.NAP == 0) && (pAddr->b.SAP == 0)) || (pAddr->b == b)) && pAddr->fListenOn && pAddr->pEventConnect && (pAddr->ch == c))))
			pAddr = pAddr->pNext;

		if (pAddr) {
			IFDBG(DebugOut (DEBUG_TDI_TRACE, L"Found address object 0x%08x\n", pAddr));
			SVSUTIL_ASSERT (pAddr->fAuthenticate || pAddr->fEncrypt);

			int fAuthenticate = pAddr->fAuthenticate;
			int fEncrypt = pAddr->fEncrypt;
			BT_ADDR bt = SET_NAP_SAP (b.NAP, b.SAP);

			int cPinLength = pAddr->cPinLength;
			unsigned char caPinData[PIN_SIZE];
			memcpy (caPinData, pAddr->caPinData, sizeof(caPinData));

			SVSUTIL_ASSERT ((cPinLength >= 0) && (cPinLength <= PIN_SIZE));

			gpTDIR->Unlock ();

			__try {
				if (cPinLength)
					BthSetPIN (&bt, cPinLength, caPinData);

				if (fAuthenticate && (BthAuthenticate (&bt) != ERROR_SUCCESS))
					fAcceptConnection = FALSE;

				if (fEncrypt && fAcceptConnection && (BthSetEncryption (&bt, TRUE) != ERROR_SUCCESS))
					fAcceptConnection = FALSE;
			} __except (1) {
				IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] exception in security code!\n"));
				fAcceptConnection = FALSE;
			}

			if (! gpTDIR)
				return ERROR_INTERNAL_ERROR;

			gpTDIR->Lock ();
			if (fAcceptConnection && gpTDIR->fIsConnected && (pAddr == GetAddressObject ((HANDLE)pAddr)))
				fAcceptConnection = AcceptIncomingCall (pAddr, hConnection);
			else
				fAcceptConnection = FALSE;
		} else {
			IFDBG(DebugOut (DEBUG_ERROR, L"Address translation failed for connection 0x%08x\n", hConnection));
			fAcceptConnection = FALSE;
		}
	} else {
		IFDBG(DebugOut (DEBUG_ERROR, L"Address translation failed for connection 0x%08x\n", hConnection));
		fAcceptConnection = FALSE;
	}

	if (gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"AuthenticateConnectionRequest : %s connection\n", fAcceptConnection ? L"Accepting" : L"Declining"));
		HANDLE h = gpTDIR->hRFCOMM;
		RFCOMM_ConnectResponse_In pCallback = gpTDIR->rfcomm_if.rfcomm_ConnectResponse_In;

		gpTDIR->AddRef ();
		gpTDIR->Unlock ();
		__try {
			pCallback (h, NULL, hConnection, fAcceptConnection);
		} __except (1) {
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] AuthenticateConnectionRequest Exception in rfcomm_ConnectResponse_In\n"));
		}
		gpTDIR->Lock ();
		gpTDIR->DelRef ();
	}

	if (gpTDIR->fIsConnected && fAcceptConnection) {
		RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionByHandle (hConnection);
		if (pConn)
			SendMSC (pConn, TDIR_V24_RTC|TDIR_V24_RTR, 0xff);
	}

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-AuthenticateConnectionRequest ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

//
//	RFCOMM interface
//
static int tdir_connect_request_ind	(void *pUserContext, HANDLE hConnection, BD_ADDR *pba, unsigned char channel) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_connect_request_ind\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_connect_request_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_connect_request_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	BD_ADDR			b;
	unsigned char	c;
	int				fLocal;
	int             fAcceptConnection = FALSE;
	int				fSkipReply = FALSE;

	int				cPinLength = 0;
	unsigned char	caPinData[PIN_SIZE];

	if (ERROR_SUCCESS == gpTDIR->rfcomm_if.rfcomm_GetChannelAddress (gpTDIR->hRFCOMM, hConnection, &b, &c, &fLocal)) {
		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"Got connection request from %04x%08x 0x%02x\n", b.NAP, b.SAP, c));
		// Find address object...
		RFCOMM_ADDRESS_OBJECT *pAddr = gpTDIR->paolist;
		while (pAddr &&
			(! ((((pAddr->b.NAP == 0) && (pAddr->b.SAP == 0)) || (pAddr->b == b)) && pAddr->fListenOn && pAddr->pEventConnect && (pAddr->ch == c))))
			pAddr = pAddr->pNext;

		if (pAddr) {
			IFDBG(DebugOut (DEBUG_TDI_TRACE, L"Found address object 0x%08x\n", pAddr));
			if (pAddr->fAuthenticate || pAddr->fEncrypt) {
				IFDBG(DebugOut (DEBUG_TDI_TRACE, L"Address object 0x%08x requires authentication\n", pAddr));
				SVSCookie cookie = btutil_ScheduleEvent (AuthenticateConnectionRequest, hConnection, 0);
				if (cookie)
					fSkipReply = TRUE;
				else {
					IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] Could not create authentication thread in tdir_connect_request_ind\n"));
				}
			} else {
				cPinLength = pAddr->cPinLength;
				memcpy (caPinData, pAddr->caPinData, sizeof(caPinData));

				SVSUTIL_ASSERT ((cPinLength >= 0) && (cPinLength <= PIN_SIZE));

				fAcceptConnection = AcceptIncomingCall (pAddr, hConnection);
			}
		} else
			IFDBG(DebugOut (DEBUG_ERROR, L"Address translation failed for connection 0x%08x\n", hConnection));
	} else
		IFDBG(DebugOut (DEBUG_ERROR, L"Address translation failed for connection 0x%08x\n", hConnection));

	if (gpTDIR->fIsConnected && (! fSkipReply)) {
		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"tdir_connect_request_ind : %s connection\n", fAcceptConnection ? L"Accepting" : L"Declining"));
		HANDLE h = gpTDIR->hRFCOMM;
		RFCOMM_ConnectResponse_In pCallback = gpTDIR->rfcomm_if.rfcomm_ConnectResponse_In;

		gpTDIR->AddRef ();
		gpTDIR->Unlock ();

		__try {
			if (fAcceptConnection && cPinLength) {
				bt_addr bt = SET_NAP_SAP (b.NAP, b.SAP);
				BthSetPIN (&bt, cPinLength, caPinData);
			}

			pCallback (h, NULL, hConnection, fAcceptConnection);
		} __except (1) {
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_connect_request_ind Exception in rfcomm_ConnectResponse_In\n"));
		}
		gpTDIR->Lock ();
		gpTDIR->DelRef ();
	}

	if (gpTDIR->fIsConnected && (! fSkipReply) && fAcceptConnection) {
		RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionByHandle (hConnection);
		if (pConn)
			SendMSC (pConn, TDIR_V24_RTC|TDIR_V24_RTR, 0xff);
	}

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_connect_request_ind ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

static int ConnectEntrySDP(RFCOMM_CONNECTION_OBJECT *pC) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"[TDIR] ConnectEntrySDP : 0x%08x\n", pC));

	int iErr = ERROR_INTERNAL_ERROR;

	BD_ADDR b;
	b.NAP = GET_NAP(pC->BTAddr.btAddr);
	b.SAP = GET_SAP(pC->BTAddr.btAddr);

	HANDLE h = gpTDIR->hSDP;
	SDP_Connect_In pCallback = gpTDIR->sdp_if.sdp_Connect_In;
	gpTDIR->AddRef ();
	gpTDIR->Unlock ();

	__try {
		iErr = pCallback (h, pC, &b);
	} __except (1) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] : exception in ConnectEntrySDP!\n"));
	}

	gpTDIR->Lock ();
	gpTDIR->DelRef ();

	if (!GetConnectionObject ((HANDLE)pC) || (!gpTDIR->fIsConnected && (iErr == ERROR_SUCCESS)))
		iErr = ERROR_OPERATION_ABORTED;

	return iErr;
}

static int ConnectEntryRFCOMM(RFCOMM_CONNECTION_OBJECT *pC) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"[TDIR] ConnectEntryRFCOMM : 0x%08x\n", pC));

	SVSUTIL_ASSERT (! pC->hConnect);

	int iErr = ERROR_INTERNAL_ERROR;

	HANDLE hConnect = NULL;
	HANDLE h = gpTDIR->hRFCOMM;
	RFCOMM_CreateChannel pCallback = gpTDIR->rfcomm_if.rfcomm_CreateChannel;

	unsigned char chPort = (unsigned char)pC->BTAddr.port;
	bt_addr bt = pC->BTAddr.btAddr;

	int cPinLength = pC->cPinLength;
	unsigned char caPinData[PIN_SIZE];
	memcpy (caPinData, pC->caPinData, sizeof(caPinData));

	SVSUTIL_ASSERT ((cPinLength >= 0) && (cPinLength <= PIN_SIZE));

	gpTDIR->AddRef ();
	gpTDIR->Unlock ();

	__try {
		if (cPinLength)
			BthSetPIN (&bt, cPinLength, caPinData);

		BD_ADDR b;

		b.NAP = GET_NAP(bt);
		b.SAP = GET_SAP(bt);

		iErr = pCallback (h, &b, chPort, &hConnect);
	} __except (1) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] : exception in ConnectEntryRFCOMM!\n"));
	}

	gpTDIR->Lock ();
	gpTDIR->DelRef ();

#if defined (DEBUG) || defined (_DEBUG)
	if (hConnect) {
		RFCOMM_CONNECTION_OBJECT *pC2 = gpTDIR->pcolist;
		while (pC2) {
			SVSUTIL_ASSERT ((pC2 == pC) || (pC2->hConnect != hConnect));
			pC2 = pC2->pNext;
		}
	}
#endif

	pC = GetConnectionObject ((HANDLE)pC);

	if (hConnect && gpTDIR->fIsConnected && pC && (iErr == ERROR_SUCCESS)) {
		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"[TDIR] ConnectEntryRFCOMM : object 0x%08x handle 0x%08x channel = %d\n", pC, hConnect, chPort));

		pC->hConnect = hConnect;

		int n1 = pC->mtu;

		RFCOMM_PNREQ_In pCallback2 = gpTDIR->rfcomm_if.rfcomm_PNREQ_In;
		gpTDIR->AddRef ();
		gpTDIR->Unlock ();

		iErr = ERROR_INTERNAL_ERROR;

		__try {
			iErr = pCallback2 (h, pC, hConnect, TDIR_PRI, n1, RFCOMM_PN_CREDIT_IN, RFCOMM_PN_CREDIT_MAX);
		} __except (1) {
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] : exception in rfcomm_CreateChannel!\n"));
		}

		gpTDIR->Lock ();
		gpTDIR->DelRef ();
		if (gpTDIR->fIsInitialized)
			pC = GetConnectionObject ((HANDLE)pC);
		else
			pC = NULL;
	}

	if (! pC)
		iErr = ERROR_OPERATION_ABORTED;

	return iErr;
}


void SDPDisconnect(unsigned short cid)  {
	HANDLE h = gpTDIR->hSDP;
	SDP_Disconnect_In  pCallback = gpTDIR->sdp_if.sdp_Disconnect_In;

	SVSUTIL_ASSERT(gpTDIR->IsLocked());

	gpTDIR->AddRef ();
	gpTDIR->Unlock ();

	__try {
		pCallback (h,NULL,cid);
	} __except (1) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] : exception in sdp_disconnect_in!\n"));
	}

	gpTDIR->Lock ();
	gpTDIR->DelRef ();
}



static int tdir_data_up_ind (void *pUserContext, HANDLE hConnection, BD_BUFFER *pba, int additional_credits) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_data_up_ind, data = %d, extra credits = %d\n", BufferTotal (pba), additional_credits));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_data_up_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_data_up_ind : ERROR_SERVICE_NOT_ACTIVE\n"));

		if ((! pba->fMustCopy) && pba->pFree)
			pba->pFree (pba);

		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

#if defined (DEBUG_PEER_SYNCHRONIZATION)
	PeerDebugData pd;
	PeerDebugData *ppd = NULL;
	if (BufferTotal (pba) >= sizeof(pd)) {
		memcpy (&pd, pba->pBuffer + pba->cEnd - sizeof(pd), sizeof(pd));
		if (pd.magic == DEBUG_PEER_MAGIC) {
			pba->cEnd -= sizeof(pd);
			ppd = &pd;
		}
	}

	if (! ppd) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_data_up_ind : mismatch of peer debug builds!\n"));
		SVSUTIL_ASSERT (0);
	}
#endif

	int fDataAccepted = FALSE;

	//	Try to satisfy outstanding RECVs first...

	int cBytes = BufferTotal (pba);
	RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionByHandle (hConnection);

	if ((cBytes > 0) && pConn && pConn->fc_credit)
		--pConn->iGaveCredits;

#if defined (DEBUG_CREDIT_FLOW)
	if (pConn && (cBytes > 0))
		++pConn->iPacketsRecv;

	if (pConn && pConn->fc_credit) {
		pConn->iCreditsRecv += additional_credits;
		// Credit Invariants

		if (pConn->iGaveCredits != (pConn->iCreditsSent - pConn->iPacketsRecv)) {
			SVSUTIL_ASSERT (0);
			RETAILMSG(1, (L"Bluetooth TDI Credit mismatch: GaveCredits = %d Total SentCredits = %d Total PacketsRecv = %d\n", pConn->iGaveCredits, pConn->iCreditsSent, pConn->iPacketsRecv));
		}

		if (pConn->iHaveCredits + additional_credits != (pConn->iCreditsRecv - pConn->iPacketsSent)) {
			RETAILMSG(1, (L"Bluetooth TDI Credit mismatch: HaveCredits = %d (incl %d addtl) Total CreditsRecv = %d Total PacketsSent = %d\n", pConn->iHaveCredits + additional_credits, additional_credits, pConn->iCreditsRecv, pConn->iPacketsSent));
			SVSUTIL_ASSERT (0);
		}
	}
#endif

#if defined (DEBUG_PEER_SYNCHRONIZATION) && defined (DEBUG_CREDIT_FLOW)
	if (ppd && pConn) {
		SVSUTIL_ASSERT (ppd->iPacketsSent == pConn->iPacketsRecv);
		SVSUTIL_ASSERT (ppd->iCreditsSent == pConn->iCreditsRecv);
	}
#endif

	while (gpTDIR->fIsConnected && (cBytes > 0) && pConn && pConn->pRecvList && pConn->pRecvList->pNdisBuffer) {
		SVSUTIL_ASSERT (! pConn->pRecvList->pBuffer);

		NDIS_BUFFER *pNdis = pConn->pRecvList->pNdisBuffer;

		DWORD dwPerms = SetProcPermissions (pConn->pRecvList->dwPerms);
		BOOL  bkm = SetKMode (TRUE);

		int cBytesToTransfer = QueryNdisSize (pNdis);
		if (cBytes < cBytesToTransfer)
			cBytesToTransfer = cBytes;

		SVSUTIL_ASSERT (pConn->pRecvList->iNdisUsed == 0);

		uint StartOffset = 0;
		CopyFlatToNdis (pNdis, pba->pBuffer + pba->cStart, cBytesToTransfer, &StartOffset);

		SetKMode (bkm);
		SetProcPermissions (dwPerms);

		pba->cStart += cBytesToTransfer;
		pConn->pRecvList->iNdisUsed = cBytesToTransfer;

		cBytes = BufferTotal (pba);

		RFCOMM_RECEIVE_OBJECT *pThis = pConn->pRecvList;
		pConn->pRecvList = pConn->pRecvList->pNext;

		PBT_RECV_COMPLETE		pRecvEvent = pThis->pRecvEvent;
		void					*pRecvContext = pThis->pRecvContext;
		int						iNdisUsed = pThis->iNdisUsed;

		delete pThis;

		IFDBG(DebugOut (DEBUG_TDI_PACKETS, L"tdir_data_up_ind 0x%08x :: satisfied read request for %d bytes\n", pConn, iNdisUsed));

		gpTDIR->AddRef ();
		gpTDIR->Unlock ();

		pRecvEvent (pRecvContext, TDI_SUCCESS, iNdisUsed);

		gpTDIR->Lock ();
		gpTDIR->DelRef ();

		if (gpTDIR->fIsInitialized)
			pConn = GetConnectionByHandle (hConnection);
		else
			pConn = NULL;
	}

	SVSUTIL_ASSERT (cBytes >= 0);

	if (gpTDIR->fIsConnected && (cBytes > 0) && pConn && pConn->pAddr &&
								((pConn->irecvd + cBytes) <= GetQuota (pConn))) {
		SVSUTIL_ASSERT ((! pConn->pRecvList) || pConn->pRecvList->pBuffer);

		RFCOMM_RECEIVE_OBJECT *pRecv = new RFCOMM_RECEIVE_OBJECT;
		if (pRecv) {
			memset (pRecv, 0, sizeof(*pRecv));
			pRecv->pBuffer = pba->fMustCopy ? BufferCopy (pba) : pba;
			if (! pConn->pRecvList)
				pConn->pRecvList = pRecv;
			else {
				RFCOMM_RECEIVE_OBJECT *pRP = pConn->pRecvList;
				while (pRP->pNext)
					pRP = pRP->pNext;
				pRP->pNext = pRecv;
			}

			fDataAccepted = TRUE;
			pConn->irecvd += cBytes;

			if (pConn->pAddr && (! pConn->cbDataIndicated)) {
			    // If we need to queue any data, we indicate it first.
				// Only indicate the first time since we are using the same data.

				pConn->cbDataIndicated = cBytes;
			    EventRcvBuffer     EventRecvCompleteInfo;    
				int                BytesTaken = 0;

				void *pEventReceiveContext = pConn->pAddr->pEventReceiveContext;
				void *pConnectionContext = pConn->pConnectionContext;
				PRcvEvent pEventReceive = pConn->pAddr->pEventReceive;

				gpTDIR->AddRef ();
				gpTDIR->Unlock ();

				TDI_STATUS TdiStatus = pEventReceive (pEventReceiveContext, pConnectionContext,
					0, cBytes, cBytes, (unsigned int *)&BytesTaken, NULL, &EventRecvCompleteInfo);

				gpTDIR->Lock ();
				gpTDIR->DelRef ();

				IFDBG(DebugOut (DEBUG_TDI_PACKETS, L"tdir_data_up_ind 0x%08x :: indicated %d bytes\n", pConn, cBytes));

				SVSUTIL_ASSERT (TdiStatus == TDI_NOT_ACCEPTED);
				SVSUTIL_ASSERT (BytesTaken == 0);

				if (gpTDIR->fIsInitialized)
					pConn = GetConnectionByHandle (hConnection);
				else
					pConn = NULL;
			} else {
				IFDBG(DebugOut (DEBUG_TDI_PACKETS, L"tdir_data_up_ind 0x%08x :: data already indicated, stored %d bytes\n", pConn, cBytes));
			}

			if (pConn && (pConn->irecvd > pConn->xon_lim) && (! pConn->fc_local) && (! pConn->fc_credit)) {
				IFDBG(DebugOut (DEBUG_TDI_PACKETS, L"Switching flow off for connection 0x%08x\n", pConn));

				pConn->fc_local = TRUE;
				SendMSC (pConn, TDIR_V24_RTC|TDIR_V24_RTR, 0xff);
			}
		} else {
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] tdir_data_up_ind :: OOM !\n"));
		}
	}
#if defined (DEBUG) || defined (_DEBUG) || defined (RETAILLOG)
	else if (cBytes != 0) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] Data came to nonexistent connection 0x%08x or overflow!\n", hConnection));
		if (gpTDIR->fIsConnected && pConn)
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] Overflow details: received so far: %d, quota %d, bytes to queue %d\n", pConn->irecvd, GetQuota (pConn), cBytes));
	}
#endif

	if (! fDataAccepted) {
		if ((! pba->fMustCopy) && pba->pFree)
			pba->pFree (pba);
	}

	if (pConn && (pConn == GetConnectionByHandle (hConnection)) && pConn->fc_credit) {
		pConn->iHaveCredits += additional_credits;
		if (pConn->iHaveCredits - additional_credits <= 0)
			SendData (pConn);

		if (gpTDIR->fIsInitialized)
			pConn = GetConnectionByHandle (hConnection);
		else
			pConn = NULL;
	}

	if (pConn && pConn->fc_credit)
		SendCredits (pConn);

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_data_up_ind ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

static int tdir_disconnect_ind (void *pUserContext, HANDLE hConnection) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_disconnect_ind\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_disconnect_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_disconnect_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	ExtractPN (hConnection, NULL, NULL, NULL);

	RFCOMM_CONNECTION_OBJECT *pC = GetConnectionByHandle (hConnection);

	if (pC) {
		pC->hConnect = NULL;
		SignalConnectionLoss (pC, TDI_GRACEFUL_DISC, FALSE);
	} else
		IFDBG(DebugOut (DEBUG_WARN, L"[TDI] tdir_disconnect_ind : connection for handle 0x%08x not found or in process of being closed!\n", hConnection));

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_disconnect_ind 0x%08x ERROR_SUCCESS\n", pC));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

static int tdir_msc_ind (void *pUserContext, HANDLE hConnection, unsigned char v24, unsigned char bs) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_msc_ind\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_msc_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_msc_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionByHandle (hConnection);
	if (pConn) {
		pConn->v24in = v24;
		pConn->brin  = bs;

		if (! pConn->fc_credit) {
			if (v24 & TDIR_V24_FC) // fc
				pConn->fc_remote = TRUE;
			else {
				pConn->fc_remote = FALSE;
				SendData (pConn);
			}
		}
	} else
		IFDBG(DebugOut (DEBUG_WARN, L"[TDI] tdir_msc_ind : Connection not found for handle 0x%08x\n", hConnection));

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_msc_ind ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

static int tdir_rls_ind (void *pUserContext, HANDLE hConnection, unsigned char rls) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_rls_ind\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_rls_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_rls_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionByHandle (hConnection);
	if (pConn) {
		pConn->rlsin = rls;
	} else
		IFDBG(DebugOut (DEBUG_WARN, L"[TDI] tdir_rls_ind : Connection not found for handle 0x%08x\n", hConnection));

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_rls_ind ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

static int tdir_fc_ind (void *pUserContext, HANDLE hConnect, unsigned char fcOn) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_fc_ind\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_fc_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_fc_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	RFCOMM_CONNECTION_OBJECT *pC = GetConnectionByHandle (hConnect);

	if (pC) {
		if (fcOn) {	// Initiate data sends...
			pC->fc_aggregate = FALSE;
			SendData (pC);
		} else
			pC->fc_aggregate = TRUE;
	} else
		IFDBG(DebugOut (DEBUG_WARN, L"[TDI] tdir_fc_ind : Connection not found!\n"));

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_fc_ind ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

static int tdir_pnreq_ind (void *pUserContext, HANDLE hConnection, unsigned char priority, unsigned short n1, int use_credit_fc, int initial_credits) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_pnreq_ind, pri = %d, mtu = %d, credit fc = %x, initial credits = %d\n", priority, n1, use_credit_fc, initial_credits));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_pnreq_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_pnreq_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	RFCOMM_CONNECTION_OBJECT *pC = GetConnectionByHandle (hConnection);
	if (pC) {
		IFDBG(DebugOut (DEBUG_WARN, L"[TDI] tdir_pnreq_ind : unusual: PNREQ for existing connection, 0x%08x handle 0x%08x\n", pC, hConnection));
		if (n1 > pC->imax_mtu)
			n1 = pC->imax_mtu;
		if (n1 < pC->imin_mtu)
			n1 = pC->imin_mtu;
		else
			pC->mtu = n1;
	} else {
		if (n1 > TDIR_MTUMAX)
			n1 = TDIR_MTUMAX;
		if (n1 < TDIR_MTUMIN)
			n1 = TDIR_MTUMIN;

		RFCOMM_PN_OBJECT *pPNO = gpTDIR->pnlist;
		while (pPNO && (pPNO->hConnection != hConnection))
			pPNO = pPNO->pNext;

		if (! pPNO) {
			pPNO = new RFCOMM_PN_OBJECT;
			if (pPNO) {
				pPNO->pNext       = gpTDIR->pnlist;
				gpTDIR->pnlist    = pPNO;
			}
		}

		if (pPNO) {
			pPNO->hConnection = hConnection;
			pPNO->imtu        = n1;
			if (use_credit_fc == RFCOMM_PN_CREDIT_IN) {
				pPNO->fCreditFlow = TRUE;
				pPNO->iCredits    = initial_credits;
			} else {
				pPNO->fCreditFlow = FALSE;
				pPNO->iCredits    = 0;
			}				
		}
	}

	HANDLE h = gpTDIR->hRFCOMM;
	RFCOMM_PNRSP_In pCallback = gpTDIR->rfcomm_if.rfcomm_PNRSP_In;

	gpTDIR->Unlock ();

	int iRes = ERROR_INTERNAL_ERROR;
	__try {
		iRes = pCallback (h, NULL, hConnection, priority, n1,
						use_credit_fc ? RFCOMM_PN_CREDIT_OUT : 0,
						use_credit_fc ? RFCOMM_PN_CREDIT_MAX : 0);
	} __except (1) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] tdir_pnreq_ind Exception in rfcomm_PNRSP_In\n"));
	}

#if defined (DEBUG) || defined (_DEBUG) || defined (RETAILLOG)
	if (iRes != ERROR_SUCCESS)
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] tdir_pnreq_ind rfcomm_PNRSP_In returns %d\n", iRes));
#endif

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_pnreq_ind ERROR_SUCCESS\n"));
	return ERROR_SUCCESS;
}

static int tdir_rpnreq_ind (void *pUserContext, HANDLE hConnection, unsigned short mask, int baud, int data, int stop, int parity, int parity_type, unsigned char flow, unsigned char xon, unsigned char xoff) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_rpnreq_ind\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_rpnreq_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_rpnreq_ind : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	RFCOMM_CONNECTION_OBJECT *pC = GetConnectionByHandle (hConnection);
	if (pC) {
		if (mask & RFCOMM_RPN_HAVE_BAUD)
			pC->BaudRate = baud;

		if (mask & RFCOMM_RPN_HAVE_DATA)
			pC->ByteSize = data;

		if (mask & RFCOMM_RPN_HAVE_STOP)
			pC->StopBits = stop;

		if (mask & RFCOMM_RPN_HAVE_PARITY)
			pC->fParity = parity;

		if (mask & RFCOMM_RPN_HAVE_PT)
			pC->Parity = parity_type;

		if (! mask) {
			mask = RFCOMM_RPN_HAVE_BAUD | RFCOMM_RPN_HAVE_DATA | RFCOMM_RPN_HAVE_STOP | 
				RFCOMM_RPN_HAVE_PARITY | RFCOMM_RPN_HAVE_PT;
			baud = pC->BaudRate;
			data = pC->ByteSize;
			stop = pC->StopBits;
			parity = pC->fParity;
			parity_type = pC->Parity;
			xoff = 0;
			xon = 0;
			flow = 0;
		}
	}

	HANDLE h = gpTDIR->hRFCOMM;
	RFCOMM_RPNRSP_In pCallback = gpTDIR->rfcomm_if.rfcomm_RPNRSP_In;

	gpTDIR->AddRef ();
	gpTDIR->Unlock ();

	__try {
		//	Accept everything...
		pCallback (h, NULL, hConnection, mask, baud, data, stop, parity, parity_type, flow, xon, xoff);
	} __except (1) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_rpnreq_ind : Exception in rfcomm_RPNRSP_In\n"));
	}

	gpTDIR->Lock ();
	gpTDIR->DelRef ();

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_rpnreq_ind ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

static int tdir_stack_event (void *pUserContext, int iEvent, void *pEventContext) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_stack_event\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_stack_event : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_stack_event : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	switch (iEvent) {
	case BTH_STACK_RESET:
		btutil_ScheduleEvent (StackXXX, (LPVOID)3);
		break;

	case BTH_STACK_DOWN:
		btutil_ScheduleEvent (StackXXX, (LPVOID)2);
		break;

	case BTH_STACK_UP:
		btutil_ScheduleEvent (StackXXX, (LPVOID)1);
		break;

	case BTH_STACK_DISCONNECT:
		btutil_ScheduleEvent (StackDisconnect, NULL);
		break;
	}

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_stack_event ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

static int tdir_call_aborted (void *pCallContext, int iError) {
	IFDBG(DebugOut (DEBUG_WARN, L"[TDI] +tdir_call_aborted\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_call_aborted : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_call_aborted : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	RFCOMM_CONNECTION_OBJECT *pC = NULL;
	pC = GetConnectionObject ((HANDLE)pCallContext);
	if (! pC) {
		RFCOMM_PACKET_OBJECT *pP = GetPacketObject ((HANDLE)pCallContext);
		if (pP)
			pC = pP->pConn;
	}

	if (pC) {
		HANDLE hConn = pC->hConnect;
		pC->hConnect = NULL;

		IFDBG(DebugOut (DEBUG_WARN, L"[TDI] +tdir_call_aborted : aborting connection 0x%08x handle 0x%08x\n", pC, hConn));

		SignalConnectionLoss (pC, TDI_CONNECTION_ABORTED, TRUE);
		if (gpTDIR->fIsConnected && hConn)
			DisconnectConnection (NULL, hConn, NULL);
	}

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_call_aborted ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

static int tdir_connect_request_out	(void *pCallContext, int iError, HANDLE hConnection) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_connect_request_out\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_connect_request_out : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_connect_request_out : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionByHandle (hConnection);

	if (pConn) {
		SVSUTIL_ASSERT (pConn->pAddr);	// This must be an associated connection!
		SVSUTIL_ASSERT (! pConn->pPacketList);
		SVSUTIL_ASSERT (! pConn->pSendList);
		SVSUTIL_ASSERT (! pConn->pRecvList);
		SVSUTIL_ASSERT (pConn->hConnect == hConnection);

		SVSUTIL_ASSERT (pConn->eStage == OPENING);

		int TdiStatus = TDI_SUCCESS;
		if (iError == ERROR_SUCCESS)
			ResetConnection (pConn, FALSE);		// Leave credits be - they were set in PN process
		else
			TdiStatus = TDI_CONN_REFUSED;

		if ((TdiStatus == ERROR_SUCCESS) && (pConn->fAuthenticate || pConn->fEncrypt)) {
			IFDBG(DebugOut (DEBUG_TDI_TRACE, L"Connection object 0x%08x requires authentication\n", pConn));
			SVSCookie c = btutil_ScheduleEvent (AuthenticateOutgoingConnection, hConnection, 0);
			if (c == 0) {
				IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] Could not create authentication thread in tdir_connect_request_ind\n"));
				SignalConnectionCompleteUp (pConn, TDI_CONN_REFUSED);
			}
		} else
			SignalConnectionCompleteUp (pConn, TdiStatus);

		if (gpTDIR->fIsConnected && (pConn == GetConnectionByHandle (hConnection)) && (pConn->eStage == OPENED))
			SendMSC (pConn, TDIR_V24_RTC|TDIR_V24_RTR, 0xff);
	} else
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_connect_request_out : no connection for handle 0x%08x\n", hConnection));

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_connect_request_out 0x%08x ERROR_SUCCESS\n", pConn));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

static int tdir_disconnect_out	(void *pCallContext, int iError) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_disconnect_out\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_disconnect_out : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_disconnect_out : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionObject (pCallContext);

	if (pConn) {
		pConn->hConnect = NULL;
		SignalConnectionLoss (pConn, TDI_GRACEFUL_DISC, FALSE);
	} else
		IFDBG(DebugOut (DEBUG_WARN, L"[TDI] tdir_disconnect_out : no closing connection 0x%08x\n", pCallContext));

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_disconnect_out 0x%08x ERROR_SUCCESS\n", pConn));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

static int tdir_pnreq_out (void *pCallContext, int iError, unsigned char priority, unsigned short n1, int use_credit_fc, int initial_credits) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_pnreq_out, pri = %d, mtu = %d, credit fc = %x, initial credits = %d\n", priority, n1, use_credit_fc, initial_credits));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_pnreq_out : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_pnreq_out : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionObject ((HANDLE)pCallContext);

	if (pConn) {
		int iErr = ERROR_SUCCESS;

		HANDLE hConnect = pConn->hConnect;
		HANDLE h = gpTDIR->hRFCOMM;
		if ((n1 >= pConn->imin_mtu) && (n1 <= pConn->imax_mtu)) {
			IFDBG(DebugOut (DEBUG_TDI_TRACE, L"Accepted MTU %d for connection 0x%08x\n", n1, pConn));

			pConn->mtu = n1 ? n1 : TDIR_MTU;
			if (use_credit_fc) {
				pConn->fc_credit    = TRUE;
				pConn->iGaveCredits = RFCOMM_PN_CREDIT_MAX;
				pConn->iHaveCredits = initial_credits;
#if defined (DEBUG_CREDIT_FLOW)
				pConn->iCreditsRecv = initial_credits;
				pConn->iCreditsSent = RFCOMM_PN_CREDIT_MAX;
#endif
			} else {
				pConn->fc_credit    = FALSE;
				pConn->iGaveCredits = 0;
				pConn->iHaveCredits = 0;
#if defined (DEBUG_CREDIT_FLOW)
				pConn->iCreditsRecv = 0;
				pConn->iCreditsSent = 0;
#endif
			}

			IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_pnreq_out : negotiated parms mtu = %d, credit fc = %s, gave credits = %d, have credits = %d\n", pConn->mtu, pConn->fc_credit ? L"on" : L"off", pConn->iGaveCredits, pConn->iHaveCredits));
			
			RFCOMM_ConnectRequest_In pCallback2 = gpTDIR->rfcomm_if.rfcomm_ConnectRequest_In;

			gpTDIR->AddRef ();
			gpTDIR->Unlock ();

			iErr = ERROR_INTERNAL_ERROR;

			__try {
				iErr = pCallback2 (h, pConn, hConnect);
			} __except (1) {
				IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] : exception in rfcomm_CreateChannel!\n"));
			}

			gpTDIR->Lock ();
			gpTDIR->DelRef ();
		} else {
			IFDBG(DebugOut (DEBUG_WARN, L"[TDI] The other side has supplied unacceptable mtu %d for connection 0x%08x\n", n1, pConn));
			iErr = ERROR_PROTOCOL_UNREACHABLE;
		}

		if (iErr != ERROR_SUCCESS)
			SignalConnectionCompleteUp (pConn, TDI_CONN_REFUSED);
	} else
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_pnreq_out : no connection for handle 0x%08x\n", pCallContext));

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_pnreq_out ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

static int tdir_data_down_out (void *pCallContext, int iError) {
	if (! pCallContext) {
		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_data_down_out : ignored credit-flow grant\n"));
		return ERROR_SUCCESS;
	}

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdir_data_down_out\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_data_down_out : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdir_data_down_out : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	RFCOMM_PACKET_OBJECT *pP = GetPacketObject ((HANDLE)pCallContext);

	if (pP) {
		if (pP == pP->pConn->pPacketList)
			pP->pConn->pPacketList = pP->pConn->pPacketList->pNext;
		else {
			RFCOMM_PACKET_OBJECT *pPP = pP->pConn->pPacketList;
			while (pPP->pNext != pP)
				pPP = pPP->pNext;
			pPP->pNext = pP->pNext;
		}

		int cBytes = pP->cBytesSent;
		RFCOMM_CONNECTION_OBJECT *pC = pP->pConn;
		delete pP;

		RegisterTransferCompletion (pC, cBytes);
	}

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-tdir_data_down_out ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

//
// SDP Callback functions
//

int tdisdp_connect_out(void *pCallContext, unsigned long status, unsigned short cid) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdisdp_connect_out\n"));

	int iErr = ERROR_INTERNAL_ERROR;
	RFCOMM_CONNECTION_OBJECT      *pC = NULL;
	SDP_ServiceAttributeSearch_In pCallback;
	SdpQueryUuid                  uuid[2];
	SdpAttributeRange             attribRange;
	HANDLE                        h;

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdisdp_connect_out : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}
	gpTDIR->Lock ();

	// Past this point we use goto done; because we need to call sdp_Disconnect on error.
	if (! gpTDIR->fIsConnected) {
		iErr = ERROR_SERVICE_NOT_ACTIVE;
		goto done;
	}

	if (NULL == (pC = GetConnectionObject ((HANDLE)pCallContext))) {
		iErr = ERROR_INVALID_DATA;
		goto done;
	}

	if (status != ERROR_SUCCESS) {
		SVSUTIL_ASSERT(cid == 0);
		iErr = ERROR_CONNECTION_UNAVAIL;
		goto done;
	}
	pC->sdpCid = cid;

	// Call ServiceAttribSearch to get protocol headers.
	pCallback = gpTDIR->sdp_if.sdp_ServiceAttributeSearch_In;
	h = gpTDIR->hSDP;

	uuid[0].uuidType = SDP_ST_UUID128;
	memcpy(&uuid[0].u.uuid128,&pC->BTAddr.serviceClassId,sizeof(GUID));
	memset(&uuid[1],0,sizeof(uuid[1]));

	attribRange.minAttribute = attribRange.maxAttribute = SDP_ATTRIB_PROTOCOL_DESCRIPTOR_LIST;

	gpTDIR->AddRef ();
	gpTDIR->Unlock ();

	__try {
		iErr = pCallback (h, pC,cid, uuid,&attribRange,1);
	} __except (1) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] : exception in sdp_ServiceAttributeSearch_In!\n"));
	}

	gpTDIR->Lock ();
	gpTDIR->DelRef ();

done:
	SVSUTIL_ASSERT(gpTDIR->IsLocked());

	if (iErr != ERROR_SUCCESS) {
		IFDBG(DebugOut(DEBUG_ERROR,L"tdisdp_connect_out: fails, err = 0x%08x\r\n",iErr));

		if (status == ERROR_SUCCESS)
			SDPDisconnect(cid);

		pC = pC ? GetConnectionObject ((HANDLE)pCallContext) : NULL;
		if (pC && gpTDIR->fIsConnected)
			SignalConnectionCompleteUp (pC, TDI_CONN_REFUSED);
	}
	gpTDIR->Unlock ();
	return ERROR_SUCCESS;
}

int tdisdp_service_attribute_search_out(void *pCallContext, unsigned long status, unsigned char *pOutBuf, unsigned long cOutBuf) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdisdp_service_attribute_search_out\n"));

	int iErr = ERROR_INTERNAL_ERROR;
	RFCOMM_CONNECTION_OBJECT *pC = NULL;
	BOOL fFreeLock = FALSE;

	if (! gpTDIR) {
		iErr = ERROR_SERVICE_NOT_ACTIVE;
		goto done;
	}
	gpTDIR->Lock ();
	fFreeLock = TRUE;

	if (! gpTDIR->fIsConnected) {
		iErr = ERROR_SERVICE_NOT_ACTIVE;
		goto done;
	}

	pC = GetConnectionObject ((HANDLE)pCallContext);
	if ((! pC) || (status != ERROR_SUCCESS) || (! pOutBuf) || (! cOutBuf)) {
		iErr = ERROR_INVALID_DATA;
		goto done;
	}

	// Grep through tree here, fill in pC->BTAddr.bthChannel = 1st found item.
	if (ERROR_SUCCESS != (iErr = GetRfcommCidFromResponse(pOutBuf,cOutBuf,&pC->BTAddr.port)))
		goto done;

	IFDBG(DebugOut(DEBUG_TDI_TRACE,L"tdisdp_service_attribute_search_out has got valid response, attempting to connect to channel 0x%08x\r\n",pC->BTAddr.port));

	iErr = ConnectEntryRFCOMM(pC);
done:

#if defined (DEBUG) || defined (_DEBUG)
	if (iErr != ERROR_SUCCESS)
		IFDBG(DebugOut(DEBUG_ERROR,L"tdisdp_service_attribute_search_out: err=0x%08x\r\n",iErr));
#endif

	if (pOutBuf)
		g_funcFree(pOutBuf,g_pvAllocData);

	if (!fFreeLock)
		return iErr;

	SVSUTIL_ASSERT(gpTDIR->IsLocked());

	if (gpTDIR->fIsConnected && (pC = GetConnectionObject ((HANDLE) pCallContext)))
		SDPDisconnect(pC->sdpCid);

	if (iErr != ERROR_SUCCESS && gpTDIR->fIsConnected && (pC = GetConnectionObject ((HANDLE)pC)))
		SignalConnectionCompleteUp (pC, TDI_CONN_REFUSED);

	gpTDIR->Unlock();
	return ERROR_SUCCESS;
}

// These do nothing currently
int	tdisdp_disconnect_out(void *pCallContext, unsigned long status) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdisdp_disconnect_out\n"));
	return ERROR_SUCCESS;
}

int tdisdp_service_search_out(void *pCallContext, unsigned long status, unsigned short cReturnedHandles, unsigned long *pHandles) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdisdp_service_search_out\n"));
	SVSUTIL_ASSERT(0);
	return ERROR_SUCCESS;
}

int tdisdp_attribute_search_out(void *pCallContext, unsigned long status, unsigned char *pOutBuf, unsigned long cOutBuf) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+tdisdp_attribute_search_out\n");)	
	SVSUTIL_ASSERT(0);
	return ERROR_SUCCESS;
}

//static int tdir_RPNREQ_Out			(void *pCallContext, int iError, unsigned char mask, unsigned char b, unsigned char d, unsigned char flc, unsigned char xon, unsigned char xoff, unsigned short pm);

//HCI Callback functions
static int tdih_read_bdaddr_out(void *pCallContext, unsigned char status, BD_ADDR *pba) {
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdih_read_btaddr_out : ERROR_SERVICE_NOT_ACTIVE\n"));
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	gpTDIR->Lock ();

	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] tdih_read_btaddr_out : ERROR_SERVICE_NOT_ACTIVE\n"));
		gpTDIR->Unlock ();
		return ERROR_SERVICE_NOT_ACTIVE;
	}

	if (status) {
		int iErr = StatusToError (status, ERROR_INTERNAL_ERROR);
		IFDBG(DebugOut(DEBUG_ERROR, L"tdih_read_btaddr_out failed. (Status=%d, Err=0x%08x)", status, iErr));
		gpTDIR->Unlock ();
		return iErr;
	}

	SVSUTIL_ASSERT(pba);

	gpTDIR->btAddr = SET_NAP_SAP(pba->NAP, pba->SAP);

	gpTDIR->Unlock ();
	return ERROR_SUCCESS;
}

//
//	Initialization code
//
int RFCOMM_TDI::Init (void) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+Init\n"));

	SVSUTIL_ASSERT (! fIsConnected);
	SVSUTIL_ASSERT (! fIsInitialized);
	SVSUTIL_ASSERT (! hRFCOMM);
	SVSUTIL_ASSERT (! hSDP);

	reset ();

	if ((! pfmdAO) && (! (pfmdAO = svsutil_AllocFixedMemDescr (sizeof(RFCOMM_ADDRESS_OBJECT), 10)))) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI_RFCOMM] -Init :: ERROR_OUTOFMEMORY - 1\n"));
		return ERROR_OUTOFMEMORY;
	}

	if ((! pfmdCO) && (! (pfmdCO = svsutil_AllocFixedMemDescr (sizeof(RFCOMM_CONNECTION_OBJECT), 10)))) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI_RFCOMM] -Init :: ERROR_OUTOFMEMORY - 2\n"));
		return ERROR_OUTOFMEMORY;
	}

	if ((! pfmdSO) && (! (pfmdSO = svsutil_AllocFixedMemDescr (sizeof(RFCOMM_SOCKET_OBJECT), 10)))) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI_RFCOMM] -Init :: ERROR_OUTOFMEMORY - 3\n"));
		return ERROR_OUTOFMEMORY;
	}

	if ((! pfmdSEND) && (! (pfmdSEND = svsutil_AllocFixedMemDescr (sizeof(RFCOMM_SEND_OBJECT), 10)))) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI_RFCOMM] -Init :: ERROR_OUTOFMEMORY - 4\n"));
		return ERROR_OUTOFMEMORY;
	}

	if ((! pfmdRECV) && (! (pfmdRECV = svsutil_AllocFixedMemDescr (sizeof(RFCOMM_RECEIVE_OBJECT), 10)))) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI_RFCOMM] -Init :: ERROR_OUTOFMEMORY - 5\n"));
		return ERROR_OUTOFMEMORY;
	}

	if ((! pfmdPL) && (! (pfmdPL = svsutil_AllocFixedMemDescr (sizeof(RFCOMM_PACKET_OBJECT), 10)))) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI_RFCOMM] -Init :: ERROR_OUTOFMEMORY - 6\n"));
		return ERROR_OUTOFMEMORY;
	}

	if ((! pfmdPN) && (! (pfmdPN = svsutil_AllocFixedMemDescr (sizeof(RFCOMM_PN_OBJECT), 10)))) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI_RFCOMM] -Init :: ERROR_OUTOFMEMORY - 7\n"));
		return ERROR_OUTOFMEMORY;
	}

	RFCOMM_EVENT_INDICATION	ei;
	memset (&ei, 0, sizeof(ei));

	ei.rfcomm_ConnectRequest_Ind = tdir_connect_request_ind;
	ei.rfcomm_DataUp_Ind         = tdir_data_up_ind;
	ei.rfcomm_Disconnect_Ind     = tdir_disconnect_ind;
	ei.rfcomm_FC_Ind             = tdir_fc_ind;
	ei.rfcomm_MSC_Ind            = tdir_msc_ind;
	ei.rfcomm_PNREQ_Ind          = tdir_pnreq_ind;
	ei.rfcomm_RLS_Ind            = tdir_rls_ind;
	ei.rfcomm_RPNREQ_Ind         = tdir_rpnreq_ind;
	ei.rfcomm_StackEvent         = tdir_stack_event;

	RFCOMM_CALLBACKS c;
	memset (&c, 0, sizeof(c));

	c.rfcomm_CallAborted        = tdir_call_aborted;
	c.rfcomm_ConnectRequest_Out = tdir_connect_request_out;
	c.rfcomm_Disconnect_Out     = tdir_disconnect_out;
	c.rfcomm_DataDown_Out       = tdir_data_down_out;
	c.rfcomm_PNREQ_Out          = tdir_pnreq_out;
	c.rfcomm_RPNREQ_Out         = NULL;	//#error Implement me!

	int iErr = RFCOMM_EstablishDeviceContext (this, 0, &ei, &c, &rfcomm_if, &cDeviceHeader,
													&cDeviceTrailer, &hRFCOMM);

	if (iErr != ERROR_SUCCESS) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI_RFCOMM] -Init :: %d\n", iErr));
		return iErr;
	}

	SVSUTIL_ASSERT (hRFCOMM);

	SDP_EVENT_INDICATION sdpEI;
	sdpEI.sdp_StackEvent       = NULL;

	SDP_CALLBACKS sdpC;
	sdpC.sdp_Connect_Out                 = tdisdp_connect_out; 
	sdpC.sdp_Disconnect_Out              = tdisdp_disconnect_out;	
	sdpC.sdp_ServiceSearch_Out           = tdisdp_service_search_out;
	sdpC.sdp_AttributeSearch_Out         = tdisdp_attribute_search_out;
	sdpC.sdp_ServiceAttributeSearch_Out  = tdisdp_service_attribute_search_out;
	sdpC.sdp_CallAborted                 = tdir_call_aborted;

	iErr = SDP_EstablishDeviceContext(this, &sdpEI, &sdpC, &sdp_if,&hSDP);

	if (iErr != ERROR_SUCCESS) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI_RFCOMM] SDP -Init :: %d\n", iErr));
		RFCOMM_CloseDeviceContext(hRFCOMM);
		hRFCOMM = NULL;
		return iErr;
	}

	SVSUTIL_ASSERT(hSDP);

	HCI_EVENT_INDICATION hei;
	memset (&hei, 0, sizeof (hei));

	HCI_CALLBACKS hc;
	memset (&hc, 0, sizeof (hc));
	hc.hci_ReadBDADDR_Out = tdih_read_bdaddr_out;

	int cHciHeaders = 0;
	int cHciTrailers = 0;
	
	iErr = HCI_EstablishDeviceContext (this, BTH_CONTROL_DEVICEONLY, NULL, 0, 0, &hei, &hc, &hci_if, &cHciHeaders, &cHciTrailers, &hHCI);
	if (iErr != ERROR_SUCCESS) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI_RFCOMM] HCI -Init :: %d\n", iErr));
		
		SDP_CloseDeviceContext(hSDP);
		hSDP = NULL;

		RFCOMM_CloseDeviceContext(hRFCOMM);
		hRFCOMM = NULL;
		return iErr;
	}

	SVSUTIL_ASSERT(hHCI);

	fIsInitialized = TRUE;
	GetConnectionState ();

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-Init :: ERROR_SUCCESS\n"));

	return ERROR_SUCCESS;
}

int tdiR_InitializeOnce (void) {
	IFDBG(DebugOut (DEBUG_TDI_INIT, L"+tdiR_InitializeOnce\n"));
	ASSERT (! gpTDIR);

	if (!LoadNdisProtocolVtable())	
		return ERROR_INTERNAL_ERROR;

	gpTDIR = new RFCOMM_TDI;

	if (gpTDIR)
		sdp_LoadWSANameSpaceProvider();

	IFDBG(DebugOut (gpTDIR == NULL ? DEBUG_ERROR : DEBUG_TDI_INIT, L"-tdiR_InitializeOnce (%s)\n", gpTDIR == NULL ? L"failed" : L"success"));
	return (gpTDIR != NULL) ? ERROR_SUCCESS : ERROR_OUTOFMEMORY;
}

int tdiR_UninitializeOnce (void) {
	IFDBG(DebugOut (DEBUG_TDI_INIT, L"+tdiR_UninitializeOnce\n"));
	IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] ATTENTION! TDI interface is being uninitialized.\n"));
	IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] If the library is now freed, AFD WILL CRASH.\n"));

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"-tdiR_UninitializeOnce :: ERROR_INTERNAL_ERROR\n"));
		return ERROR_INTERNAL_ERROR;
	}

	gpTDIR->Lock ();
	RFCOMM_TDI *pTDIR = gpTDIR;
	gpTDIR = NULL;
	pTDIR->Unlock ();

	SVSUTIL_ASSERT (! pTDIR->fIsInitialized);

	sdp_UnLoadWSANameSpaceProvider();

	delete pTDIR;

	IFDBG(DebugOut (DEBUG_TDI_INIT, L"-tdiR_UninitializeOnce\n"));
	return ERROR_SUCCESS;
}

int tdiR_CreateDriverInstance (void) {
	IFDBG(DebugOut (DEBUG_TDI_INIT, L"+tdiR_CreateDriverInstance\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"-tdiR_CreateDriverInstance :: ERROR_INTERNAL_ERROR\n"));
		return ERROR_INTERNAL_ERROR;
	}

	for ( ; ; ) {
		gpTDIR->Lock ();

		if (gpTDIR->fIsInitialized) {
			IFDBG(DebugOut (DEBUG_ERROR, L"-tdiR_CreateDriverInstance :: ERROR_SERVICE_ALREADY_RUNNING\n"));
			gpTDIR->Unlock ();
			return ERROR_SERVICE_ALREADY_RUNNING;
		}

		if (gpTDIR->GetRefCount () == 1)
			break;

		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"Waiting for ref count in tdiR_CreateDriverInstance\n"));
		gpTDIR->Unlock ();

		Sleep (100);
	}

	int iRes = gpTDIR->Init ();

	IFDBG(DebugOut (iRes == ERROR_SUCCESS ? DEBUG_TDI_INIT : DEBUG_ERROR, L"-tdiR_CreateDriverInstance :: %d\n", iRes));
	gpTDIR->Unlock ();
	return iRes;
}

int tdiR_CloseDriverInstance (void) {
	IFDBG(DebugOut (DEBUG_TDI_INIT, L"+tdiR_CloseDriverInstance\n"));
	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"-tdiR_CloseDriverInstance :: ERROR_INTERNAL_ERROR\n"));
		return ERROR_INTERNAL_ERROR;
	}

	for ( ; ; ) {
		gpTDIR->Lock ();

		if (! gpTDIR->fIsInitialized) {
			IFDBG(DebugOut (DEBUG_ERROR, L"-tdiR_CloseDriverInstance :: ERROR_SERVICE_NOT_ACTIVE\n"));
			gpTDIR->Unlock ();
			return ERROR_SERVICE_NOT_ACTIVE;
		}

		if (gpTDIR->GetRefCount() == 1)
			break;

		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"Waiting for ref count in tdiR_CloseDriverInstance\n"));
		gpTDIR->Unlock ();
		Sleep (100);
	}

	gpTDIR->fIsInitialized = FALSE;
	gpTDIR->fIsConnected   = FALSE;
	gpTDIR->AddRef ();

	while (gpTDIR->pcolist) {
		RFCOMM_CONNECTION_OBJECT *pC = gpTDIR->pcolist;
		SignalConnectionLoss (pC, TDI_CONNECTION_ABORTED, TRUE);
		if (pC == gpTDIR->pcolist)
			CloseConnectionObject (pC);
	}

	while (gpTDIR->paolist)
		CloseAddressObject (gpTDIR->paolist);

	while (gpTDIR->psolist)
		CloseSocketObject (gpTDIR->psolist);

	while (gpTDIR->pnlist) {
		RFCOMM_PN_OBJECT *pNext = gpTDIR->pnlist->pNext;
		delete gpTDIR->pnlist;
		gpTDIR->pnlist = pNext;
	}

	gpTDIR->Unlock ();
	if (gpTDIR->hRFCOMM)
		RFCOMM_CloseDeviceContext (gpTDIR->hRFCOMM);
	if (gpTDIR->hSDP)
		SDP_CloseDeviceContext(gpTDIR->hSDP);
	gpTDIR->Lock ();
	gpTDIR->DelRef ();
	gpTDIR->reset ();
	IFDBG(DebugOut (DEBUG_TDI_INIT, L"-tdiR_CloseDriverInstance :: ERROR_SUCCESS\n"));
	gpTDIR->Unlock ();

	return ERROR_SUCCESS;
}

//
//	TDI interface
//
////////////////////////////////////////////////////////////////////////////////
//	BTR_OpenAddressEntry()
//		
//	Description:
//		Called after bind(). Allocates a new BT_ADDR_OBJ structure.
//
//	Arguments:
//		pTDIRequest::
//			The address of the new BT_ADDR_OBJ is returned in
//			pTDIReq->Handle.AddressHandle.
//
//		pTransportAddr::
//			Pointer to input TRANSPORT_ADDRESS struct. 
//			We assume one TA_ADDRESS entry with the Address field containing 
//			the remaining bytes of a SOCKADDR_BT struct minus the first two
//			bytes.
//
//		uiProtocol::
//			Ignored/Not used.
//
//		pvOptions::
//			Ignored/Not used.
//
//	Returns:
//		TDI_SUCCESS on success, or tdistat.h
//
//	Comments:
//		Called after winsock's bind() or implicit bind().
//
static TDI_STATUS BTR_OpenAddressEntry
(
PTDI_REQUEST		pTDIRequest,
PTRANSPORT_ADDRESS	pTransportAddr,
uint				uiProtocol,
PVOID				pvOptions
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_OpenAddressEntry\n"));

	SOCKADDR_BTH	*pBTAddr = (SOCKADDR_BTH *)&pTransportAddr->Address[0].AddressType;	
	if (((pBTAddr->port < 0) || (pBTAddr->port > 31)) && (pBTAddr->port != CLIENTPORTSIG)) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_OpenAddressEntry : Bluetooth channel %d invalid\n", pBTAddr->port));
		return TDI_BAD_ADDR;
	}

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_OpenAddressEntry : TDI_INVALID_STATE\n"));
		return TDI_INVALID_STATE;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_OpenAddressEntry : TDI_INVALID_STATE\n"));
		gpTDIR->Unlock ();
		return TDI_INVALID_STATE;
	}

	BT_ADDR bt;
	memcpy (&bt, &pBTAddr->btAddr, sizeof(bt));

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_OpenAddressObject %04x%08x 0x%02x\n", GET_NAP(bt), GET_SAP(bt), pBTAddr->port));

	unsigned char cPort = (unsigned char)pBTAddr->port;
	BD_ADDR       b = { GET_NAP(bt), GET_SAP(bt) };
	unsigned int  fAssigned = 0;

	TDI_STATUS TdiStatus = TDI_SUCCESS;

	int fListenOn = FALSE;

	if (pBTAddr->port != CLIENTPORTSIG) {
		fListenOn = TRUE;

		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"[TDI] BTR_OpenAddressEntry : [semi]wildcard address supplied for server, requesting the port %d...\n", cPort));
		//	Reserve/Allocate the port first...

		HANDLE h = gpTDIR->hRFCOMM;
		BT_LAYER_IO_CONTROL pCallback = gpTDIR->rfcomm_if.rfcomm_ioctl;
		gpTDIR->AddRef ();
		gpTDIR->Unlock ();

		int iRes = ERROR_INTERNAL_ERROR;
		TdiStatus = TDI_ADDR_INVALID;

		int iSize = 0;

		__try {
			unsigned char cPortIn = cPort;
			iRes = pCallback (h, BTH_STACK_IOCTL_RESERVE_PORT, sizeof(cPortIn), (char *)&cPortIn, sizeof(cPort), (char *)&cPort, &iSize);
		} __except (1) {
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] BTR_OpenAddressEntry Exception in BTH_STACK_IOCTL_RESERVE_PORT\n"));
		}

		gpTDIR->Lock ();
		gpTDIR->DelRef ();

		if (iRes == ERROR_SUCCESS) {
			fAssigned = 1;
			TdiStatus = TDI_SUCCESS;
		} else if (iRes == ERROR_NO_SYSTEM_RESOURCES)	// already assigned
			TdiStatus = TDI_NO_FREE_ADDR;
		else if (iRes == ERROR_ALREADY_ASSIGNED)
			TdiStatus = TDI_ADDR_IN_USE;
		else if (iRes == ERROR_OUTOFMEMORY)
			TdiStatus = TDI_NO_RESOURCES;
		
		if (iRes != ERROR_SUCCESS) {
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDIR] BTR_OpenAddressEntry returns %d\n", iRes));
			gpTDIR->Unlock ();
			return TdiStatus;
		}
	} else
		cPort = 0;

	RFCOMM_ADDRESS_OBJECT *pA = NULL;
	if (cPort != 0) {	// If it's a wildcard, just create it!
		pA = gpTDIR->paolist;
		while (pA && ((pA->b != b) || (pA->ch != cPort)))
			pA = pA->pNext;
	}

	if (pA) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_OpenAddressEntry Address duplicated!\n"));
		gpTDIR->Unlock ();
		return TDI_ADDR_IN_USE;
	}

	pA = new RFCOMM_ADDRESS_OBJECT;
	if (pA) {
		pA->pNext = gpTDIR->paolist;
		gpTDIR->paolist = pA;

		pA->b          = b;
		pA->ch         = cPort;
		pA->fAssigned  = fAssigned;
		pA->AddRef ();

		pA->fListenOn = fListenOn;

		pTDIRequest->Handle.AddressHandle = pA;
	} else
		TdiStatus = TDI_NO_RESOURCES;


	IFDBG(DebugOut (((TdiStatus == TDI_SUCCESS) || (TdiStatus == TDI_PENDING)) ? DEBUG_TDI_TRACE : DEBUG_ERROR,
																	L"-BTR_OpenAddressEntry :: %d (0x%08x)\n", TdiStatus, pTDIRequest->Handle.AddressHandle));
	gpTDIR->Unlock ();
	return TdiStatus;
}

////////////////////////////////////////////////////////////////////////////////
//	BTR_CloseAddressEntry()
//		
//	Description:
//		Called at socket close time after BT_DisconnectEntry() and 
//		BT_CloseConnectionEntry(). Frees the BT_ADDR_OBJ.
//		
//	Arguments:
//			pTDIRequest->Handle.AddressHandle is a handle to the
//			BT_ADDR_OBJ.   BT_OpenAddressEntry() assigns this value to be
//			a pointer to the BT_ADDR_OBJ.
//
//	Returns:
//		TDI_SUCCESS (tdistat.h) on success, or (tdistat.h)
//		TDI_ADDR_INVALID on Bad address object.
//
//	Comments:
//
//
static TDI_STATUS BTR_CloseAddressEntry
(
PTDI_REQUEST		pTDIRequest
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_CloseAddressEntry 0x%08x\n", pTDIRequest->Handle.AddressHandle));

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_CloseAddressEntry : TDI_INVALID_STATE\n"));
		return TDI_INVALID_STATE;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_CloseAddressEntry : TDI_INVALID_STATE\n"));
		gpTDIR->Unlock ();
		return TDI_INVALID_STATE;
	}

	TDI_STATUS TdiStatus = TDI_SUCCESS;

	RFCOMM_ADDRESS_OBJECT *pA = GetAddressObject (pTDIRequest->Handle.AddressHandle);
	if (pA) {
		pA->DelRef ();
		pA->fListenOn = FALSE;

		if (pA->GetRefCount () == 1)
			CloseAddressObject (pA);
	} else
		TdiStatus = TDI_NO_RESOURCES;

	IFDBG(DebugOut (((TdiStatus == TDI_SUCCESS) || (TdiStatus == TDI_PENDING)) ? DEBUG_TDI_TRACE : DEBUG_ERROR,
																	L"-BTR_CloseAddressEntry :: %d (0x%08x)\n", TdiStatus, pTDIRequest->Handle.AddressHandle));
	gpTDIR->Unlock ();
	return TdiStatus;
}

////////////////////////////////////////////////////////////////////////////////
//	BTR_OpenConnectionEntry()
//		
//	Description:
//		Called after connect() and backlog times after listen(). Allocates 
//		a new BT_CONN_OBJ.
//		
//	Arguments:
//		pTDIRequest::
//			pTDIRequest->Handle.ConnectionContext returns the address of
//			the new BT_CONN_OBJ.
//
//		pvContext::
//			To be used later in the future calls to:
//			o  pEventDisconnect()
//			o  pEventReceive()
//
//	Returns:
//		TDI_SUCCESS on success, or error in tdistat.h
//
//	Comments:
//		Called after connect()
//
static TDI_STATUS BTR_OpenConnectionEntry
(
PTDI_REQUEST		pTDIRequest,
PVOID				pvContext
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_OpenConnectionEntry\n"));

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_OpenConnectionEntry : TDI_INVALID_STATE\n"));
		return TDI_INVALID_STATE;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_OpenConnectionEntry : TDI_INVALID_STATE\n"));
		gpTDIR->Unlock ();
		return TDI_INVALID_STATE;
	}

	TDI_STATUS TdiStatus = TDI_SUCCESS;

	RFCOMM_CONNECTION_OBJECT *pC = new RFCOMM_CONNECTION_OBJECT;
	if (pC) {
		pC->pConnectionContext = pvContext;
		pC->eStage = INITED;
		pC->pNext = gpTDIR->pcolist;
		gpTDIR->pcolist = pC;
		pTDIRequest->Handle.ConnectionContext = pC;

		RFCOMM_SOCKET_OBJECT *pSock = GetSocketByHandle (pvContext);

		if (pSock)
			TransferOptions (pC, pSock);
	} else
		TdiStatus = TDI_NO_RESOURCES;

	IFDBG(DebugOut (((TdiStatus == TDI_SUCCESS) || (TdiStatus == TDI_PENDING)) ? DEBUG_TDI_TRACE : DEBUG_ERROR,
																	L"-BTR_OpenConnectionEntry :: %d (0x%08x)\n", TdiStatus, pTDIRequest->Handle.ConnectionContext));
	gpTDIR->Unlock ();
	return TdiStatus;
}

////////////////////////////////////////////////////////////////////////////////
//	BT_CloseConnectionEntry()
//		
//	Description:
//		Called at socket close time. If the connection is in a connected
//		state, this call implies an abortive close - force close the 
//		connection and free the PBT_CONN_OBJ. 
//		If the connection is not in the connected state, the DisconnectEntry()
//		been called and we simply free PBT_CONN_OBJ.
//		
//	Arguments:
//		pTDIRequest::
//			pTDIRequest->Handle.ConnectionContext contains the address to
//			the BT_CONN_OBJ structure.
//	Returns:
//		TDI_SUCCESS (tdistat.h) on success, or (tdistat.h)
//		TDI_INVALID_CONNECTION for bad connection.
//
//	Comments: This will always complete immediately, which means that the next call
//   to connect may fail because channel has not disconnected at the RFCOMM layer.
//   We might want to think about returning TDI_PENDING here and really closing connection
//   later.
//
static TDI_STATUS BTR_CloseConnectionEntry
(
PTDI_REQUEST		pTDIRequest
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_CloseConnectionEntry 0x%08x\n", pTDIRequest->Handle.ConnectionContext));

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_CloseConnectionEntry : TDI_INVALID_STATE\n"));
		return TDI_INVALID_STATE;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_CloseConnectionEntry : TDI_INVALID_STATE\n"));
		gpTDIR->Unlock ();
		return TDI_INVALID_STATE;
	}

	TDI_STATUS TdiStatus = TDI_SUCCESS;

	RFCOMM_CONNECTION_OBJECT *pC = GetConnectionObject (pTDIRequest->Handle.ConnectionContext);
	if (pC) {
		DisassociateConnection (pC);	// This precludes signal up the stack.

		if (gpTDIR->fIsInitialized && (pC == GetConnectionObject ((HANDLE)pC)))
			SignalConnectionLoss (pC, TDI_GRACEFUL_DISC, TRUE);

		if (gpTDIR->fIsInitialized && (pC == GetConnectionObject ((HANDLE)pC)))
			CloseConnectionObject (pC);
	} else
		TdiStatus = TDI_INVALID_CONNECTION;

	IFDBG(DebugOut (((TdiStatus == TDI_SUCCESS) || (TdiStatus == TDI_PENDING)) ? DEBUG_TDI_TRACE : DEBUG_ERROR,
																	L"-BTR_CloseConnectionEntry :: %d (0x%08x)\n", TdiStatus, pTDIRequest->Handle.ConnectionContext));
	gpTDIR->Unlock ();
	return TdiStatus;
}

////////////////////////////////////////////////////////////////////////////////
//	BT_AssociateAddressEntry()
//		
//	Description:
//		Called after connect() and backlog times after listen(). 
//		Assigns pAddrObj in a newly created BT_CONN_OBJ to point
//		to an BT_ADDR_OBJ and adds the BT_CONN_OBJ to pConnList
//		anchored on BT_ADDR_OBJ. 
//		SockAddrLocal is copied from the BT_ADDR_OBJ to the BT_CONN_OBJ
//		to handle the the IsConn option of BT_QueryInformation().
//
//	Arguments:
//		pTDIRequest::
//			pTDIRequest->Handle.ConnectionContext contains the 
//			CONNECTION_CONTEXT of the BT_CONN_OBJ. 
//			BT_OpenConnection() assigns this value to be a pointer to
//			the BT_CONN_OBJ.
//
//		hAddrHandle::
//			Extract the BT_ADDR_OBJ from this pointer.
//
//	Returns:
//		TDI_SUCCESS on success, or error in tdistat.h
//
//	Comments:
//		Called after connect() and backlog times after listen()
//
static TDI_STATUS BTR_AssociateAddressEntry
(
PTDI_REQUEST		pTDIRequest,
HANDLE				hAddrHandle
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_AssociateAddressEntry addr: 0x%08x conn: 0x%08x\n", hAddrHandle, pTDIRequest->Handle.ConnectionContext));

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_AssociateAddressEntry : TDI_INVALID_STATE\n"));
		return TDI_INVALID_STATE;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_AssociateAddressEntry : TDI_INVALID_STATE\n"));
		gpTDIR->Unlock ();
		return TDI_INVALID_STATE;
	}

	TDI_STATUS TdiStatus = TDI_SUCCESS;

	RFCOMM_ADDRESS_OBJECT    *pA = GetAddressObject    (hAddrHandle);
	RFCOMM_CONNECTION_OBJECT *pC = GetConnectionObject (pTDIRequest->Handle.ConnectionContext);
	if (! pA)
		TdiStatus = TDI_ADDR_INVALID;
	else if (! pC)
		TdiStatus = TDI_INVALID_CONNECTION;
	else {
		pA->AddRef ();
		pC->pAddr = pA;

		ResetConnection (pC, TRUE);
		TransferOptions (pC, pC->pAddr);
	}

	IFDBG(DebugOut (((TdiStatus == TDI_SUCCESS) || (TdiStatus == TDI_PENDING)) ? DEBUG_TDI_TRACE : DEBUG_ERROR,
																	L"-BTR_AssociateAddressEntry :: %d\n", TdiStatus));
	gpTDIR->Unlock ();
	return TdiStatus;
}

////////////////////////////////////////////////////////////////////////////////
//	BT_DisAssociateAddressEntry()
//		
//	Description:
//
//
//		
//	Arguments:
//
//	Returns:
//
//	Comments:
//
//
static TDI_STATUS BTR_DisAssociateAddressEntry
(
PTDI_REQUEST		pTDIRequest
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_DisAssociateAddressEntry 0x%08x\n", pTDIRequest->Handle.ConnectionContext));

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_DisAssociateAddressEntry : TDI_INVALID_STATE\n"));
		return TDI_INVALID_STATE;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_DisAssociateAddressEntry : TDI_INVALID_STATE\n"));
		gpTDIR->Unlock ();
		return TDI_INVALID_STATE;
	}

	TDI_STATUS TdiStatus = TDI_SUCCESS;

	RFCOMM_CONNECTION_OBJECT *pC = GetConnectionObject (pTDIRequest->Handle.ConnectionContext);
	if (pC)
		DisassociateConnection (pC);
	else
		TdiStatus = TDI_INVALID_CONNECTION;

	IFDBG(DebugOut (((TdiStatus == TDI_SUCCESS) || (TdiStatus == TDI_PENDING)) ? DEBUG_TDI_TRACE : DEBUG_ERROR,
																	L"-BTR_DisAssociateAddressEntry :: %d (0x%08x)\n", TdiStatus, pTDIRequest->Handle.ConnectionContext));
	gpTDIR->Unlock ();
	return TdiStatus;
}

////////////////////////////////////////////////////////////////////////////////
//	BT_ConnectEntry()
//		
//	Description:
//		Called after connect().   
//		Initiates an BT connection. 
//		BT should call ConnectComplete() when the connection is established.
//		
//	Arguments:
//		pTDIRequest::
//			pTDIRequest->Handle.ConnectionContext contains the 
//			CONNECTION_CONTEXT (tdi.h, PVOID) of the BT_CONN_OBJ. 
//			BT_OpenConnection() assigns this value to be a pointer to
//			the BT_CONN_OBJ.
//
//			pTDIReq->RequestNotifyObject is a pointer 
//			(void (*)(PVOID Context, TDI_STATUS FinalStatus, 
//			unsigned long ByteCount)) 
//			to a Winsock routine to call if this function returns TDI_PENDING.
//
//			pTDIReq->RequestNotifyContext is a Winsock context to return in
//			the callback.
//
//		pvTimeOut::
//			sockutil.c appears to always set this -1.   We will ignore it.
//
//		pReqAddr::
//			Pointer to input TDI_CONNECTION_INFORMATION (tdi.h) struct.
//			BT assumes RemoteAddress points to a TRANSPORT_ADDRESS (tdi.h)
//			struct. BT assumes one TA_ADDRESS entry with the Address field
//			containing the remaining bytes of a SOCKADDR_BT struct minus the
//			first two bytes.
//
//		pRetAddr::
//			Pointer to output TDI_CONNECTION_INFORMATION (tdi.h) struct.
//
//
//	Returns:
//		TDI_SUCCESS on success, or tdistat.h
//
//	Comments:
//
//
static TDI_STATUS BTR_ConnectEntry
(
PTDI_REQUEST					pTDIRequest,
PVOID							pvTimeOut,
PTDI_CONNECTION_INFORMATION		pReqAddr,
PTDI_CONNECTION_INFORMATION		pRetAddr
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_ConnectEntry 0x%08x\n", pTDIRequest->Handle.ConnectionContext));

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_ConnectEntry : TDI_INVALID_STATE\n"));
		return TDI_INVALID_STATE;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_ConnectEntry : TDI_INVALID_STATE\n"));
		gpTDIR->Unlock ();
		return TDI_INVALID_STATE;
	}

	TDI_STATUS TdiStatus = TDI_SUCCESS;

	RFCOMM_CONNECTION_OBJECT *pC = GetConnectionObject (pTDIRequest->Handle.ConnectionContext);
	if (pC) {
		SVSUTIL_ASSERT (pC->pAddr);
		SVSUTIL_ASSERT (pC->eStage == INITED);
		if (pC->pAddr->fListenOn) {
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_ConnectEntry : Cannot connect on a bound socket\n"));
			gpTDIR->Unlock ();
			return TDI_INVALID_STATE;
		}

		// Address MUST be wildcard at this point, otherwise we are using server address
		// or existing connection.
		SVSUTIL_ASSERT ((pC->pAddr->b.NAP == 0) && (pC->pAddr->b.SAP == 0) && (pC->pAddr->ch == 0)); // Wildcard

        SOCKADDR_BTH *pBTAddr = (SOCKADDR_BTH *) &(((PTRANSPORT_ADDRESS) pReqAddr->RemoteAddress)->Address[0].AddressType);

		pC->eStage = OPENING;
		pC->pConnectCompleteContext = pTDIRequest->RequestContext;
		pC->pConnectCompleteEvent   = (PBT_CONNECT_COMPLETE)pTDIRequest->RequestNotifyObject;
		pC->fNoDisconnect           = TRUE;
		memcpy (&pC->BTAddr, pBTAddr, sizeof(pC->BTAddr));

		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"Connect to %04x%08x 0x%02d\n", GET_NAP(pC->BTAddr.btAddr), GET_SAP(pC->BTAddr.btAddr), pBTAddr->port));

		int iErr;
		if (pBTAddr->port)
			iErr = ConnectEntryRFCOMM(pC);
		else
			iErr = ConnectEntrySDP(pC);

		if (iErr != ERROR_SUCCESS) {
			switch (iErr) {
			case ERROR_OPERATION_ABORTED:
				TdiStatus = TDI_REQ_ABORTED;
				break;
			case ERROR_INTERNAL_ERROR:
				TdiStatus = TDI_NOT_ACCEPTED;
				break;
			case ERROR_ALREADY_EXISTS:
				TdiStatus = TDI_LINK_BUSY;
				break;
			default:
				SVSUTIL_ASSERT (0);	// invent the case above for this error!
				TdiStatus = TDI_CONNECTION_ABORTED;
			};
		} else {
			//Refcount=1 in constructor, =2 in OpenAddressEntry, =3 in AssociateAddressEntry
			//If it is three, AssociateAddressEntry has not been called more than once.
			SVSUTIL_ASSERT(3 == pC->pAddr->GetRefCount());
			// In high-contention environments the connection might complete before we got here.
			// We need to signal connection then. However, before we returned "pending" we don't
			// know if we can call that event. Instead of signalling with pConnectComplete event,
			// we now have separate flag.
			//pC->pConnectCompleteEvent   = (PBT_CONNECT_COMPLETE)pTDIRequest->RequestNotifyObject;

			pC->fNoDisconnect = FALSE;
			pC->pAddr->ch = (unsigned char)pBTAddr->port;
			TdiStatus = TDI_PENDING;
		}
	} else
		TdiStatus = TDI_INVALID_CONNECTION;

	IFDBG(DebugOut (((TdiStatus == TDI_SUCCESS) || (TdiStatus == TDI_PENDING)) ? DEBUG_TDI_TRACE : DEBUG_ERROR,
																	L"-BTR_ConnectEntry :: %d (0x%08x)\n", TdiStatus, pTDIRequest->Handle.ConnectionContext));
	gpTDIR->Unlock ();
	return TdiStatus;
}


////////////////////////////////////////////////////////////////////////////////
//	BT_DisconnectEntry()
//		
//	Description:
//		Called at socket close time. 
//		We will call BTDisconnect() to complete the disconnect. 
//		It appears that only graceful closes use this mechanism. 
//		For abortive closes, Winsock calls only BT_CloseConnectionEntry().
//
//	Arguments:
//		pTdiRequest::
//			pTDIRequest->Handle.ConnectionContext contains the 
//			CONNECTION_CONTEXT (tdi.h, PVOID) of the BT_CONN_OBJ. 
//			BT_OpenConnection() assigns this value to be a pointer to
//			the BT_CONN_OBJ.
//
//		pvTimeOut::
//			Ignored.
//
//		usFlags::
//			Ignored.
//
//		pDiscInfo::
//			Ignored.
//
//		pRetInfo::
//			Ignored.
//
//
//
//	Returns:
//		TDI_SUCCESS, TDI_INVALID_CONNECTION, TDI_INVALID_STATE
//
//	Comments:
//
//	OmarM says signaling unless TDI_PENDING is unnecessary:
//	PBT_DISCONNECT_COMPLETE pDisconnectCompleteCallback = NULL;
//	void					*pDisconnectCompleteContext = NULL;
//		pDisconnectCompleteCallback = (PBT_DISCONNECT_COMPLETE)pTDIRequest->RequestNotifyObject;
//		pDisconnectCompleteContext = pTDIRequest->RequestContext;	
//	if (pDisconnectCompleteCallback) {
//		pDisconnectCompleteCallback (pDisconnectCompleteContext, TdiStatus, 0);
//		TdiStatus = TDI_SUCCESS;
//	}
//
static TDI_STATUS BTR_DisconnectEntry
(
PTDI_REQUEST					pTDIRequest,
PVOID							pvTimeOut,
ushort							usFlags,
PTDI_CONNECTION_INFORMATION		pDiscInfo,
PTDI_CONNECTION_INFORMATION		pRetInfo
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_DisconnectEntry 0x%08x\n", pTDIRequest->Handle.ConnectionContext));

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_DisconnectEntry : TDI_INVALID_STATE\n"));
		return TDI_INVALID_STATE;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsConnected) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_DisconnectEntry : TDI_INVALID_STATE\n"));
		gpTDIR->Unlock ();
		return TDI_SUCCESS;
	}

	TDI_STATUS TdiStatus = TDI_SUCCESS;

	RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionObject (pTDIRequest->Handle.ConnectionContext);
	if (pConn && (pConn->eStage == OPENED)) {
		if ((usFlags & TDI_DISCONNECT_ABORT) || (pConn->pSendList == NULL)) {
			if (ERROR_SUCCESS != DisconnectConnection (NULL, pConn->hConnect, pConn)) {
				if (gpTDIR->fIsConnected && (pConn == GetConnectionObject (pTDIRequest->Handle.ConnectionContext)))
					SignalConnectionLoss (pConn, TDI_GRACEFUL_DISC, TRUE);
			}
		} else
			pConn->fCloseHaveSend = TRUE;
	} else
        TdiStatus = TDI_INVALID_STATE;

	IFDBG(DebugOut (((TdiStatus == TDI_SUCCESS) || (TdiStatus == TDI_PENDING)) ? DEBUG_TDI_TRACE : DEBUG_ERROR,
																	L"-BTR_DisconnectEntry :: %d (0x%08x)\n", TdiStatus, pTDIRequest->Handle.ConnectionContext));
	gpTDIR->Unlock ();

	return TdiStatus;
}


////////////////////////////////////////////////////////////////////////////////
//	BTR_ListenEntry()
//		
//	Description:
//
//
//		
//	Arguments:
//
//	Returns:
//
//	Comments: NEVER CALLED
//
//
static TDI_STATUS BTR_ListenEntry
(
PTDI_REQUEST					pTDIRequest,
ushort							usFlags,
PTDI_CONNECTION_INFORMATION		pAcceptableInfo,
PTDI_CONNECTION_INFORMATION		pConnectedInfo
) {
	SVSUTIL_ASSERT (0);
	return TDI_SUCCESS;
}

////////////////////////////////////////////////////////////////////////////////
//	BTR_AcceptEntry()
//		
//	Description:
//
//
//		
//	Arguments:
//
//	Returns:
//
//	Comments: NEVER CALLED
//
//
static TDI_STATUS BTR_AcceptEntry
(
PTDI_REQUEST					pTDIRequest,
PTDI_CONNECTION_INFORMATION		pAcceptInfo,
PTDI_CONNECTION_INFORMATION		pConnectedInfo
) {
	SVSUTIL_ASSERT (0);
	return TDI_SUCCESS;
}


////////////////////////////////////////////////////////////////////////////////
//	BTR_ReceiveEntry()
//		
//	Description:
//		Called to recv data driectly into a user buffer or to signal
//		a flow off condition.
//		
//	Arguments:
//		pTDIRequest::
//			pTDIReq->Handle.ConnectionContext contains the CONNECTION_CONTEXT 
//			of the BT_CONN_OBJ.   BT_OpenConnectionEntry() assigns this 
//			value to be a pointer to the BT_CONN_OBJ.
//	
//			pTDIReq->RequestNotifyObject is a pointer 
//			(void (*)(PVOID Context, TDI_STATUS TdiStatus, DWORD BytesSent))
//			to a Winsock routine to call if this function returns TDI_PENDING.
//			TDIReq->RequestNotifyContext is a Winsock context to return in
//			the callback.
//
//	Returns:
//		TDI_SUCCESS on success, or tdistat.h
//
//	Comments:
//
static TDI_STATUS BTR_ReceiveEntry
(
PTDI_REQUEST		pTDIRequest,
ushort				*pusFlags,
uint				*puiRecvLen, 
PNDIS_BUFFER		pNdisBuffer
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_ReceiveEntry 0x%08x\n", pTDIRequest->Handle.ConnectionContext));

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_ReceiveEntry : WSANOTINITIALISED\n"));
		return TDI_INVALID_STATE;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_ReceiveEntry : WSANOTINITIALISED\n"));
		gpTDIR->Unlock ();
		return TDI_INVALID_STATE;
	}

	TDI_STATUS		TdiStatus = TDI_SUCCESS;
	*puiRecvLen = 0;

	RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionObject (pTDIRequest->Handle.ConnectionContext);
	if (pConn && ((pConn->eStage == OPENED) || pConn->fCloseHaveRecv)) {
		SVSUTIL_ASSERT (pConn->pAddr);

		if (pConn->pRecvList && pConn->pRecvList->pBuffer) {
			PVOID	VirtualAddress;
			UINT	BufferLength;

			//	Have data, will travel.

			int cBufferSize = QueryNdisSize (pNdisBuffer);
			int cNdisCurrentOffset = 0;
			for ( ; ; ) {
				int cDataSize = BufferTotal (pConn->pRecvList->pBuffer);
				NdisQueryBuffer(pNdisBuffer, &VirtualAddress, &BufferLength);
				if ((int)BufferLength - cNdisCurrentOffset < cDataSize)
					cDataSize = (int)BufferLength - cNdisCurrentOffset;

				BufferGetChunk (pConn->pRecvList->pBuffer, cDataSize,
						((unsigned char *)VirtualAddress) + cNdisCurrentOffset);
				cNdisCurrentOffset += cDataSize;
				*puiRecvLen += cDataSize;

				SVSUTIL_ASSERT (cNdisCurrentOffset <= (int)BufferLength);
				if (cNdisCurrentOffset == (int)BufferLength) {
					NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer);
					cNdisCurrentOffset = 0;
				}

				if (BufferTotal (pConn->pRecvList->pBuffer) == 0) {
					if (pConn->pRecvList->pBuffer->pFree)
						pConn->pRecvList->pBuffer->pFree (pConn->pRecvList->pBuffer);
					RFCOMM_RECEIVE_OBJECT *pNext = pConn->pRecvList->pNext;
					delete pConn->pRecvList;
					pConn->pRecvList = pNext;
				}

				if ((! pConn->pRecvList) || (! pNdisBuffer))
					break;
			}

			IFDBG(DebugOut (DEBUG_TDI_TRACE, L"BTR_ReceiveEntry :: read %d bytes\n", *puiRecvLen));

			pConn->cbDataIndicated -= (int)*puiRecvLen;
			if (pConn->cbDataIndicated < 0)
				pConn->cbDataIndicated = 0;

			//	Now, we need to indicate the data left...
			int cDataLeft = 0;
			RFCOMM_RECEIVE_OBJECT *pRecvList = pConn->pRecvList;
			while (pRecvList) {
				SVSUTIL_ASSERT (pRecvList->pBuffer);
				cDataLeft += BufferTotal (pRecvList->pBuffer);
				pRecvList = pRecvList->pNext;
			}

			SVSUTIL_ASSERT (cDataLeft >= 0);

			pConn->irecvd = cDataLeft;

			if (cDataLeft && (pConn->cbDataIndicated == 0)) {
				pConn->cbDataIndicated = cDataLeft;
			    EventRcvBuffer     EventRecvCompleteInfo;    
				int                BytesTaken = 0;

				void *pEventReceiveContext = pConn->pAddr->pEventReceiveContext;
				void *pConnectionContext = pConn->pConnectionContext;
				PRcvEvent pEventReceive = pConn->pAddr->pEventReceive;

				gpTDIR->AddRef ();
				gpTDIR->Unlock ();

				TDI_STATUS TdiStatus2 = pEventReceive (pEventReceiveContext, pConnectionContext,
					0, cDataLeft, cDataLeft, (unsigned int *)&BytesTaken, NULL, &EventRecvCompleteInfo);

				gpTDIR->Lock ();
				gpTDIR->DelRef ();

				SVSUTIL_ASSERT (TdiStatus2 == TDI_NOT_ACCEPTED);
				SVSUTIL_ASSERT (BytesTaken == 0);
				IFDBG(DebugOut (DEBUG_TDI_TRACE, L"BTR_ReceiveEntry :: still have %d bytes, indicated\n", cDataLeft));
			} else if (! cDataLeft) {
				SVSUTIL_ASSERT (pConn->cbDataIndicated == 0);
				IFDBG(DebugOut (DEBUG_TDI_TRACE, L"BTR_ReceiveEntry :: all read, nothing left\n"));

					// No more data to be read and socket has been closed.

				if (pConn->fCloseHaveRecv)
					SignalConnectionLoss(pConn, TDI_GRACEFUL_DISC, TRUE);
			}

			if (pConn == GetConnectionObject (pTDIRequest->Handle.ConnectionContext)) {
				if ((pConn->irecvd < pConn->xoff_lim) && pConn->fc_local && (! pConn->fc_credit)) {
					IFDBG(DebugOut (DEBUG_TDI_TRACE, L"Switching flow back on for connection 0x%08x\n", pConn));

					pConn->fc_local = FALSE;
					SendMSC (pConn, TDIR_V24_RTC|TDIR_V24_RTR, 0xff);
				} else if (pConn->fc_credit)
					SendCredits (pConn);
			}
		} else if (pConn->fCloseHaveRecv) {
			// No more data to be read and socket has been closed.
			SignalConnectionLoss(pConn, TDI_GRACEFUL_DISC, TRUE);
		} else {
			RFCOMM_RECEIVE_OBJECT *pRecv = new RFCOMM_RECEIVE_OBJECT;
			IFDBG(DebugOut (DEBUG_TDI_TRACE, L"BTR_ReceiveEntry queueing request...\n"));
			if (pRecv) {
				memset (pRecv, 0, sizeof(*pRecv));
				pRecv->pNdisBuffer  = pNdisBuffer;
				pRecv->dwPerms      = GetCurrentPermissions ();
				pRecv->pRecvEvent   = (PBT_RECV_COMPLETE)pTDIRequest->RequestNotifyObject;
				pRecv->pRecvContext = pTDIRequest->RequestContext;

				if (pConn->pRecvList) {
					RFCOMM_RECEIVE_OBJECT *pROP = pConn->pRecvList;

					while (pROP->pNext)
						pROP = pROP->pNext;

					pROP->pNext = pRecv;
				} else
					pConn->pRecvList = pRecv;
				TdiStatus = TDI_PENDING;
			} else {
				IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_ReceiveEntry No memory!\n"));
				TdiStatus = TDI_NO_RESOURCES;
			}
		}

	} else
        TdiStatus = TDI_INVALID_STATE;

	IFDBG(DebugOut (((TdiStatus == TDI_SUCCESS) || (TdiStatus == TDI_PENDING)) ? DEBUG_TDI_TRACE : DEBUG_ERROR,
																	L"-BTR_ReceiveEntry :: %d (0x%08x)\n", TdiStatus, pTDIRequest->Handle.ConnectionContext));
	gpTDIR->Unlock ();
	return TdiStatus;
}


////////////////////////////////////////////////////////////////////////////////
//	BTR_SendEntry()
//		
//	Description:
//		Called to send data.
//		
//	Arguments:
//		pTDIRequest::
//			pTDIReq->Handle.ConnectionContext contains the CONNECTION_CONTEXT 
//			of the BT_CONN_OBJ.   BT_OpenConnectionEntry() assigns this 
//			value to be a pointer to the BT_CONN_OBJ.
//	
//			pTDIReq->RequestNotifyObject is a pointer 
//			(void (*)(PVOID Context, TDI_STATUS TdiStatus, DWORD BytesSent))
//			to a Winsock routine to call if this function returns TDI_PENDING.
//			TDIReq->RequestNotifyContext is a Winsock context to return in
//			the callback.
//
//		usFlags::
//			One of the:
//			TDI_SEND_EXPEDITED
//			TDI_SEND_PARTIAL
//			TDI_SEND_NO_RESPONSE_EXPECTED
//			TDI_SEND_NON_BLOCKING
//
//		uiSendLen::
//
//		pNdisBuff::
//			Pointer to input NDIS_BUFFER
//
//	Returns:
//		TDI_SUCCESS on success, or tdistat.h
//
//	Comments:
//
//
static TDI_STATUS BTR_SendEntry
(
PTDI_REQUEST		pTDIRequest,
ushort				usFlags,
uint				uiSendLen,
PNDIS_BUFFER		pNdisBuff
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_SendEntry 0x%08x\n", pTDIRequest->Handle.ConnectionContext));

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_SendEntry : WSANOTINITIALISED\n"));
		return TDI_INVALID_STATE;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_SendEntry : WSANOTINITIALISED\n"));
		gpTDIR->Unlock ();
		return TDI_INVALID_STATE;
	}

	TDI_STATUS		TdiStatus = TDI_SUCCESS;

	RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionObject (pTDIRequest->Handle.ConnectionContext);
	if (pConn && (pConn->eStage == OPENED)) {
		if (! pConn->fCloseHaveSend) {
			RFCOMM_SEND_OBJECT *pS = new RFCOMM_SEND_OBJECT;
			if (pS) {
				SVSUTIL_ASSERT (pS->pNext == NULL);
				SVSUTIL_ASSERT (pS->cBytesUsed == 0);

				pS->pConnect = pConn;
				pS->pNdisBuffer = pNdisBuff;
				pS->pSendCallback = (PBT_SEND_COMPLETE)pTDIRequest->RequestNotifyObject;
				pS->pSendContext = pTDIRequest->RequestContext;

				if (pConn->pSendList) {
					RFCOMM_SEND_OBJECT *pP = pConn->pSendList;
					while (pP->pNext)
						pP = pP->pNext;

					pP->pNext = pS;
				} else
					pConn->pSendList = pS;

				SendData (pConn);
				TdiStatus = TDI_PENDING;
			} else
				TdiStatus = TDI_NO_RESOURCES;
		} else
			TdiStatus = TDI_INVALID_STATE;
	} else
        TdiStatus = TDI_INVALID_STATE;

	IFDBG(DebugOut (((TdiStatus == TDI_SUCCESS) || (TdiStatus == TDI_PENDING)) ? DEBUG_TDI_TRACE : DEBUG_ERROR,
																	L"-BTR_SendEntry :: %d (0x%08x)\n", TdiStatus, pTDIRequest->Handle.ConnectionContext));
	gpTDIR->Unlock ();
	return TdiStatus;
}


////////////////////////////////////////////////////////////////////////////////
//	BTR_SendDatagramEntry()
//		
//	Description:
//
//
//		
//	Arguments:
//
//	Returns:
//
//	Comments: NEVER CALLED
//
//
static TDI_STATUS BTR_SendDatagramEntry
(
PTDI_REQUEST					pTDIRequest,
PTDI_CONNECTION_INFORMATION		pConnInfo,
uint							uiSendLen,
ULONG							*puiBytesSent,
PNDIS_BUFFER					pNdisBuff
) {
	SVSUTIL_ASSERT (0);
	return TDI_SUCCESS;
}


////////////////////////////////////////////////////////////////////////////////
//	BTR_ReceiveDatagramEntry()
//		
//	Description:
//
//
//		
//	Arguments:
//
//	Returns:
//
//	Comments: NEVER CALLED
//
//
static TDI_STATUS BTR_ReceiveDatagramEntry
(
PTDI_REQUEST					pTDIRequest,
PTDI_CONNECTION_INFORMATION		pConnInfo,
PTDI_CONNECTION_INFORMATION		pRetInfo,
uint							uiRecvLen,
uint							*puiBytesRecvd,
PNDIS_BUFFER					pNdisBuff
) {
	SVSUTIL_ASSERT (0);
	return TDI_SUCCESS;
}

////////////////////////////////////////////////////////////////////////////////
//	BTR_SetEventEntry()
//		
//	Description:
//		Informs the stack of each Winsock entry point (event handler).
//		Called after bind() for each event handler except
//		TDI_EVENT_CONNECT. Called for TDI_EVENT_CONNECT after listen().
//		Each Address Object can have unique event handlers. 
//		
//	Arguments:
//		pvHandle::
//			Pointer to the BT_ADDR_OBJ struct that will be associated with
//			this event handler. 
//
//		iType::
//
//		pvHandler::
//
//		pvContext::
//
//	Returns:
//		TDI_SUCCESS  on success, or tdistat.h
//
//	Comments:
//
//
static TDI_STATUS BTR_SetEventEntry
(
PVOID				pvHandle,
int					iType,
PVOID				pvHandler,
PVOID				pvContext
) {
	TDI_STATUS      TdiStatus = TDI_SUCCESS;

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"BTR_SetEventEntry 0x%08x :: for [%s] ptr 0x%08x, ctx 0x%08x\n", pvHandle,
		(iType == TDI_EVENT_CONNECT)			?	L"EventConnect" :
		(iType == TDI_EVENT_DISCONNECT)			?	L"EventDisconnect" :
		(iType == TDI_EVENT_ERROR)				?	L"EventError" :
		(iType == TDI_EVENT_RECEIVE)			?	L"EventReceive" :
		(iType == TDI_EVENT_RECEIVE_DATAGRAM)	?	L"EventRxDatagram" :
		(iType == TDI_EVENT_RECEIVE_EXPEDITED)	?	L"EventRxEpd" :
		L"Unsupported!", pvHandler, pvContext));

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_SetEventEntry : WSANOTINITIALISED\n"));
		return TDI_INVALID_STATE;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_SetEventEntry : WSANOTINITIALISED\n"));
		gpTDIR->Unlock ();
		return TDI_INVALID_STATE;
	}

    RFCOMM_ADDRESS_OBJECT *pAddrObj = GetAddressObject (pvHandle);

    if (pAddrObj) {
		switch (iType)
		{
		  case TDI_EVENT_CONNECT:		
			pAddrObj->pEventConnect                 = (PConnectEvent)pvHandler;
			pAddrObj->pEventConnectContext          = pvContext;
			break;

		  case TDI_EVENT_DISCONNECT:
			pAddrObj->pEventDisconnect              = (PDisconnectEvent)pvHandler;
			pAddrObj->pEventDisconnectContext       = pvContext;
			break;

		  case TDI_EVENT_ERROR:
			pAddrObj->pEventError                   = (PErrorEvent)pvHandler;
			pAddrObj->pEventDisconnectContext       = pvContext;
			break;

		  case TDI_EVENT_RECEIVE:
			pAddrObj->pEventReceive                 = (PRcvEvent)pvHandler;
			pAddrObj->pEventReceiveContext          = pvContext;
			break;

		  case TDI_EVENT_RECEIVE_DATAGRAM:
			pAddrObj->pEventReceiveDatagram         = (PRcvDGEvent)pvHandler;
			pAddrObj->pEventReceiveDatagramContext  = pvContext;
			break;

		  case TDI_EVENT_RECEIVE_EXPEDITED:
			pAddrObj->pEventReceiveExpedited        = (PRcvExpEvent)pvHandler;
			pAddrObj->pEventReceiveExpeditedContext = pvContext;
			break;
        
		  default:
			TdiStatus = TDI_BAD_EVENT_TYPE;
			break;
		}
	} else
        TdiStatus = TDI_ADDR_INVALID;

    
	IFDBG(DebugOut (TdiStatus == TDI_SUCCESS ? DEBUG_TDI_TRACE : DEBUG_ERROR, L"-BTR_SetEventEntry :: %d\n", TdiStatus));
	gpTDIR->Unlock ();

    return TdiStatus;
}


////////////////////////////////////////////////////////////////////////////////
//	BTR_QueryInformationEntry()
//		
//	Description:
//		pTDIRequest::
//			If (IsConn), pTDIReq->Handle.ConnectionContext contains
//			the CONNECTION_CONTEXT of the BT_CONN_OBJ
//			BT_OpenConnection() assigns this value to be a pointer to
//			the BT_CONN_OBJ.
//			If (~IsConn), pTDIReq->Handle.AddressHandle is a handle to
//			the BT_ADDR_OBJ. BT_OpenAddress() assigns this value to be
//			a pointer to the BT_ADDR_OBJ.
//
//		uiQueryType::
//			TDI_QUERY_BROADCAST_ADDRESS
//			TDI_QUERY_PROVIDER_INFO
//			TDI_QUERY_ADDRESS_INFO
//			TDI_QUERY_CONNECTION_INFO
//			TDI_QUERY_PROVIDER_STATISTICS
//
//		pNdisBuff::
//			Pointer to output NDIS_BUFFER.
//
//		puiBytesReturned::
//			On entry, the data size of the NDIS buffer chain. On return, 
//			sizeof() information struct copied into NDIS_BUFFER.
//
//		uiIsConn::
//			Indicates a query for connection information rather than Address
//			Object information (TDI_QUERY_ADDRESS_INFO only).
//		
//	Arguments:
//
//	Returns:
//		TDI_SUCCESS (tdistat.h) on success, or (tdistat.h)
//
//	Comments:
//
//
static TDI_STATUS BTR_QueryInformationEntry
(
PTDI_REQUEST		pTDIRequest,
uint				uiQueryType,
PNDIS_BUFFER		pNdisBuff,
uint *				puiBytesReturned,
uint				uiIsConn
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_QueryInformationEntry 0x%08x\n", uiQueryType));

	////////////////////////////////////////////////////////////////////////////
	//	This is large enough for TDI_QUERY_ADDRESS_INFO because
    //	of the inclusion of TDI_PROVIDER_STATISTICS.
	//
	union 
	{
		TDI_CONNECTION_INFO		ConnInfo;
		TDI_ADDRESS_INFO		AddrInfo;
		TDI_PROVIDER_INFO		ProviderInfo;
		TDI_PROVIDER_STATISTICS	ProviderStats;
	} InfoBuf;

	TDI_STATUS  TdiStatus = TDI_SUCCESS;
    int         InfoSize  = 0;
    int         Offset;
	
	switch (uiQueryType)
	{
		case TDI_QUERY_PROVIDER_INFO:
            InfoSize = sizeof(TDI_PROVIDER_INFO);
            InfoBuf.ProviderInfo.Version 				= 0x0100;
            InfoBuf.ProviderInfo.MaxSendSize			= 2048;
            InfoBuf.ProviderInfo.MaxConnectionUserData	= 0;
            InfoBuf.ProviderInfo.MaxDatagramSize		= 0;    
            InfoBuf.ProviderInfo.ServiceFlags			= 0;
            InfoBuf.ProviderInfo.MinimumLookaheadData	= 0;
            InfoBuf.ProviderInfo.MaximumLookaheadData	= 0;
            InfoBuf.ProviderInfo.NumberOfResources		= 0;
            InfoBuf.ProviderInfo.StartTime.LowPart		= 0;
            InfoBuf.ProviderInfo.StartTime.HighPart	    = 0;
            break;
    
        case TDI_QUERY_ADDRESS_INFO: {
            InfoSize = TAI_SIZE;
    
			if (! gpTDIR) {
				IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_QueryInformationEntry : TDI_INVALID_STATE\n"));
				TdiStatus = TDI_INVALID_STATE;
				break;
			}

			gpTDIR->Lock ();
			if (! gpTDIR->fIsInitialized) {
				IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_QueryInformationEntry : WSANOTINITIALISED\n"));
				gpTDIR->Unlock ();
				TdiStatus = TDI_INVALID_STATE;
				break;
			}

			InfoBuf.AddrInfo.ActivityCount          = 1;	// No one knows!
			InfoBuf.AddrInfo.Address.TAAddressCount = 1;	// # addr follow
			InfoBuf.AddrInfo.Address.Address[0].AddressLength = sizeof(SOCKADDR_BTH) - 2;

			SOCKADDR_BTH *pAddr = (SOCKADDR_BTH *)&InfoBuf.AddrInfo.Address.Address[0].AddressType;
			memset (pAddr, 0, sizeof(*pAddr));
			pAddr->addressFamily = AF_BT;
                
            if (uiIsConn) {
				////////////////////////////////////////////////////////////////
				//	Extract the local address from the Connection
				//
				RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionObject (pTDIRequest->Handle.ConnectionContext);
				if (pConn && pConn->pAddr) {
					pAddr->port = pConn->pAddr->ch;
					if (pConn->pAddr->b.NAP == 0 && pConn->pAddr->b.SAP == 0)
						memcpy (&pAddr->btAddr, &gpTDIR->btAddr, sizeof(pAddr->btAddr));
					else {
						BT_ADDR bt = SET_NAP_SAP(pConn->pAddr->b.NAP, pConn->pAddr->b.SAP);
						memcpy (&pAddr->btAddr, &bt, sizeof(pAddr->btAddr));
					}
				} else
                    TdiStatus = TDI_INVALID_CONNECTION;
            } else {
				////////////////////////////////////////////////////////////////
				//	Extract the local address from the Address Object
				//
				RFCOMM_ADDRESS_OBJECT *pAO = GetAddressObject (pTDIRequest->Handle.AddressHandle);
				if (pAO) {
					pAddr->port = pAO->ch;
					if (pAO->b.NAP == 0 && pAO->b.SAP == 0)
						memcpy (&pAddr->btAddr, &gpTDIR->btAddr, sizeof(pAddr->btAddr));
					else {
						BT_ADDR bt = SET_NAP_SAP(pAO->b.NAP, pAO->b.SAP);
						memcpy (&pAddr->btAddr, &bt, sizeof(pAddr->btAddr));
					}
				} else
                    TdiStatus = TDI_ADDR_INVALID;
            }
    
			gpTDIR->Unlock ();
            break;
		}
    
		case TDI_QUERY_BROADCAST_ADDRESS:		
		case TDI_QUERY_PROVIDER_STATISTICS:
		case TDI_QUERY_DATAGRAM_INFO:
		case TDI_QUERY_DATA_LINK_ADDRESS:
		case TDI_QUERY_NETWORK_ADDRESS:
			IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_QueryInformationEntry : query 0x%08x not supported\n", uiQueryType));
			break;
			
		default:
            TdiStatus = TDI_INVALID_QUERY;
    }

    if ((TdiStatus == TDI_SUCCESS) && (*puiBytesReturned < (ULONG) InfoSize))
        TdiStatus = TDI_BUFFER_OVERFLOW;

	if (TdiStatus == TDI_SUCCESS) {
	    Offset = 0;
		CopyFlatToNdis(pNdisBuff, (uchar *) &InfoBuf, InfoSize, (unsigned int *)&Offset);
	    *puiBytesReturned = InfoSize;
	}

	IFDBG(DebugOut (TdiStatus == TDI_SUCCESS ? DEBUG_TDI_TRACE : DEBUG_ERROR, L"-BTR_QueryInformationEntry : %d\n", TdiStatus);   ) 
    return TdiStatus;
	
}

////////////////////////////////////////////////////////////////////////////////
//	BTR_SetInformationEntry()
//		
//	Description:
//
//
//		
//	Arguments:
//
//	Returns:
//
//	Comments: NEVER CALLED
//
//
static TDI_STATUS BTR_SetInformationEntry
(
PTDI_REQUEST		pTDIRequest,
uint				uiSetType, 
PNDIS_BUFFER		pNdisBuff,
uint				uiBufLen,
uint				uiIsConn
) {
	SVSUTIL_ASSERT (0);
	return TDI_SUCCESS;
}


////////////////////////////////////////////////////////////////////////////////
//	BTR_ActionEntry()
//		
//	Description:
//
//
//		
//	Arguments:
//
//	Returns:
//
//	Comments: NEVER CALLED
//
//
static TDI_STATUS BTR_ActionEntry
(
PTDI_REQUEST		pTDIRequest,
uint				uiActionType, 
PNDIS_BUFFER		pNdisBuff,
uint				uiBuffLen
) {
	SVSUTIL_ASSERT (0);
	return TDI_SUCCESS;
}


////////////////////////////////////////////////////////////////////////////////
//	BTR_ActionEntry()
//		
//	Description:
//
//
//		
//	Arguments:
//
//	Returns:
//
//	Comments: NEVER CALLED
//
//
static TDI_STATUS BTR_QueryInformationExEntry
(
PTDI_REQUEST        pTDIRequest, 
struct TDIObjectID	*psObjId, 
PNDIS_BUFFER        pNdisBuff,
uint *              puiBufLen,
void *              pvContext
) {
	SVSUTIL_ASSERT (0);
	return TDI_INVALID_REQUEST;
}

////////////////////////////////////////////////////////////////////////////////
//	BTR_SetInformationExEntry()
//		
//	Description:
//		Set info associated with ADDRESS_OBJECTS or CONNECTIONS.
//
//	Arguments:
//		pTDIRequest::
//			pTDIReq->Handle.ConnectionContext contains the CONNECTION_CONTEXT
//			of the BT_CONN_OBJ.   BT_OpenConnection() assigns this 
//			value to be a pointer to the BT_CONN_OBJ.
//			
//		pObjID::
//			Pointer to input TDIObjectID struct. IrLMP ignores the 
//			TCP_SOCKET_WINDOW (tcpinfo.h, ipexport.h) option that occurs on a 
//			connect()
//
//		pvBuf::
//			Pointer to input buffer.
//
//		uiBuffLen::
//			Input buffer len.
//		
//
//	Returns:
//		TDI_SUCCESS on success, or tdistat.h
//
//	Comments:
//		Ported from IR TDI.
//		Doesn't realy do anything on BTh
//
static TDI_STATUS BTR_SetInformationExEntry
(
PTDI_REQUEST		pTDIRequest, 
struct TDIObjectID	*pObjId,
void				*pvBuf,
uint                uiBuffLen
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+/-BTR_SetInformationExEntry\n"));

	if (pObjId->toi_entity.tei_entity != CO_TL_ENTITY &&
        pObjId->toi_entity.tei_entity != CL_TL_ENTITY)
        return TDI_INVALID_REQUEST;

	if (pObjId->toi_entity.tei_instance != 0)
        return TDI_INVALID_REQUEST;

	switch(pObjId->toi_class) {
		case INFO_CLASS_PROTOCOL:
		{
			switch(pObjId->toi_type) {
				case INFO_TYPE_ADDRESS_OBJECT:
					//switch(pObjId->toi_id) {
					//	default:
							return TDI_INVALID_PARAMETER;
					//		break;
					//}
					//break;


				case INFO_TYPE_CONNECTION:
					switch(pObjId->toi_id) {
						case TCP_SOCKET_WINDOW:
							return TDI_SUCCESS;
							break;

						default:
							return TDI_INVALID_PARAMETER;
							break;
					}
					break;
            
          
				case INFO_TYPE_PROVIDER:
					default:
						return TDI_INVALID_PARAMETER;
						break;
			}
			break;
		}
        
		case INFO_CLASS_GENERIC:
			return TDI_INVALID_PARAMETER;
			break;
        
		case INFO_CLASS_IMPLEMENTATION:
		default:
			return TDI_INVALID_REQUEST;
			break;
    }

    return TDI_SUCCESS;
}

////////////////////////////////////////////////////////////////////////////////
//	BTR_GetSockaddrType()	
//		
//	Description:
//		Called after bind(), this function returns address information.
//
//	Arguments:
//		pSockAddr::
//			Pointer to winsock's SOCKADDR structure.
//			
//		dwSockAddrLen::
//			Length of the input SOCKADDR structure.
//
//		pSockAddrInfo::
//			Pointer to output SOCKADDR_INFO structure.
//
//	Returns:
//		NO_ERROR on success, or winsock.h error.
//
//	Comments:
//		Called after winsock's bind().
//
static TDI_STATUS BTR_GetSockaddrType
(
PSOCKADDR Sockaddr,
DWORD SockaddrLength,
PSOCKADDR_INFO SockaddrInfo
) {
	SOCKADDR_BTH *pAddr = (SOCKADDR_BTH *)Sockaddr;

	if (SockaddrLength < sizeof(SOCKADDR_BTH))
        return WSAEFAULT;

    if (pAddr->addressFamily != AF_BT)
        return WSAEAFNOSUPPORT;
	BT_ADDR bt;
	memcpy (&bt, &pAddr->btAddr, sizeof(pAddr->btAddr));

    if (GET_NAP(bt) == 0x00 &&	GET_SAP(bt) == 0x00) {
        SockaddrInfo->AddressInfo  = SockaddrAddressInfoWildcard;
        SockaddrInfo->EndpointInfo = SockaddrEndpointInfoWildcard;
    } else {
        SockaddrInfo->AddressInfo  = SockaddrAddressInfoNormal;
        SockaddrInfo->EndpointInfo = SockaddrEndpointInfoNormal;
    }
    
    return NO_ERROR;
}

////////////////////////////////////////////////////////////////////////////////
//	BTR_GetWildcardSockaddr()
//		
//	Description:
//		Called after connect() without previous bind(). 
//		Returns a SOCKADDR_BT suitable for an implicit bind().
//		
//	Arguments:
//		pvHelperDllSocketContext::
//
//		pSockAddr::
//
//		piSockaddrLen::
//
//
//	Returns:
//		NO_ERROR on success, or winsock.h error.
//
//	Comments:
//		Called after connect() without previous bind()
//
//
static TDI_STATUS BTR_GetWildcardSockaddr
(
PVOID HelperDllSocketContext,
PSOCKADDR Sockaddr,
PINT SockaddrLength
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_GetSockaddrType\n"));

	if (*SockaddrLength < sizeof(SOCKADDR_BTH)) {
		IFDBG(DebugOut (DEBUG_WARN, L"-BTR_GetSockaddrType WSAEFAULT\n"));

        return WSAEFAULT;
    }

    *SockaddrLength = sizeof(SOCKADDR_BTH);

    memset(Sockaddr, sizeof(SOCKADDR_BTH), 0);
    
	((SOCKADDR_BTH *) Sockaddr)->addressFamily = AF_BT;
	((SOCKADDR_BTH *) Sockaddr)->port = CLIENTPORTSIG;

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_GetSockaddrType NO_ERROR\n"));
    return NO_ERROR;
}

////////////////////////////////////////////////////////////////////////////////
//	BTR_GetSocketInformation()
//		
//	Description:
//		Called by the Winsock DLL when a level/option name combination
//		is passed to getsockopt() that the winsock DLL does not understand.
//		
//	Arguments:
//		pvHelperDllSocketContext::
//			Pointer to the BT_SOCKET_CONTEXT returned by BT_OpenSocket().
//
//		SocketHandle::
//			Handle of the socket for which we're getting information.
//
//		hTdiAddressObjectHandle::
//			TDI address object of the socket, or NULL.
//
//		iLevel::
//			level, from getsockopt().
//
//		iOptionName::
//			optname, from getsockopt().
//
//		pcOptionValue::
//			optval, from getsockopt().
//
//		piOptionLen::
//			optlen, from getsockopt().
//
//	Returns:
//		NO_ERROR (winerror.h) on success, or (winsock.h)
//
//	Comments:
//
static TDI_STATUS BTR_GetSocketInformation
(
PVOID HelperDllSocketContext,
SOCKET SocketHandle,
HANDLE TdiAddressObjectHandle,
HANDLE TdiConnectionObjectHandle,
INT Level,
INT OptionName,
PCHAR OptionValue,
PINT OptionLength
) {
	if (Level != SOL_RFCOMM)
		return WSAEINVAL;

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_GetSocketInformation : WSANOTINITIALISED\n"));
		return WSANOTINITIALISED;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_GetSocketInformation : WSANOTINITIALISED\n"));
		gpTDIR->Unlock ();
		return WSANOTINITIALISED;
	}

	//
	//	Process getsockopt data here
	//
	RFCOMM_SOCKET_OBJECT *pSocket = GetSocketObject (HelperDllSocketContext);
	if (! pSocket) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_GetSocketInformation : WSANOTINITIALISED\n"));
		gpTDIR->Unlock ();
		return WSAENOTSOCK;
	}

	RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionObject (TdiConnectionObjectHandle);
	RFCOMM_ADDRESS_OBJECT *pao = GetAddressObject (TdiAddressObjectHandle);

	int iRet = NO_ERROR;

	__try {
		switch (OptionName) {
		case SO_BTH_GET_PAGE_TO:			// no restrictions. optlen=sizeof(unsigned int), optval = &page timeout
			{
				if (*OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : invalid SO_BTH_GET_PAGE_TO : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				gpTDIR->Unlock ();

				unsigned short page = 0;
				int iRes = BthReadPageTimeout (&page);
				if (iRes == ERROR_SUCCESS)
					*(unsigned int *)OptionValue = page;
				return iRes;
			}

		case SO_BTH_GET_SCAN:			// no restrictions. optlen=sizeof(unsigned int), optval = &scan
			{
				if (*OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : invalid SO_BTH_GET_SCAN : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				gpTDIR->Unlock ();

				unsigned char mask = 0;
				int iRes = BthReadScanEnableMask (&mask);
				if (iRes == ERROR_SUCCESS)
					*(unsigned int *)OptionValue = mask;
				return iRes;
			}

		case SO_BTH_GET_AUTHN_ENABLE:	// no restrictions. optlen=sizeof(unsigned int), optval = &ae
			{
				if (*OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : invalid SO_BTH_GET_AUTHN_ENABLE : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				gpTDIR->Unlock ();

				unsigned char ae = 0;
				int iRes = BthReadAuthenticationEnable (&ae);
				if (iRes == ERROR_SUCCESS)
					*(unsigned int *)OptionValue = ae;
				return iRes;
			}

		case SO_BTH_GET_LINK_POLICY:	// connected only. optlen=sizeof(int), optval = &int
			{
				if (*OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : invalid SO_BTH_GET_LINK_POLICY : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				if (pConn && (pConn->eStage == OPENED)) {
					BT_ADDR bt = pConn->BTAddr.btAddr;
					unsigned short settings;

					IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_SetSocketInformation :: querying settings for %04x%08x\n", GET_NAP(bt), GET_SAP(bt)));
					gpTDIR->Unlock ();
					int iRes = BthReadLinkPolicySettings (&bt, &settings);
					if (iRes == ERROR_SUCCESS)
						*(unsigned int *)OptionValue = settings;

					return iRes;
				}

				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_GET_LINK_POLICY : NOT CONNECTED\n"));
				iRet = WSAENOTCONN;
				break;
			}

		case SO_BTH_GET_MODE:	// connected only. optlen=sizeof(int), optval = &int
			{
				if (*OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : invalid SO_BTH_GET_MODE : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				if (pConn && (pConn->eStage == OPENED)) {
					BT_ADDR bt = pConn->BTAddr.btAddr;
					unsigned char mode;

					IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_SetSocketInformation :: querying mode for %04x%08x\n", GET_NAP(bt), GET_SAP(bt)));
					gpTDIR->Unlock ();
					int iRes = BthGetCurrentMode (&bt, &mode);
					if (iRes == ERROR_SUCCESS)
						*(unsigned int *)OptionValue = mode;

					return iRes;
				}

				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_GET_MODE : NOT CONNECTED\n"));
				iRet = WSAENOTCONN;
				break;
			}

		case SO_BTH_GET_COD:			// no restrictions. optlen=sizeof(unsigned int), optval = &cod
			{
				if (*OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : invalid SO_BTH_GET_COD : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				gpTDIR->Unlock ();

				unsigned int cod = 0;
				int iRes = BthReadCOD (&cod);
				if (iRes == ERROR_SUCCESS)
					*(unsigned int *)OptionValue = cod;
				return iRes;
			}

		case SO_BTH_GET_LOCAL_VER:			// no restrictions. optlen=sizeof(unsigned int), optval = &BTH_LOCAL_VERSION
			{
				if (*OptionLength != sizeof(BTH_LOCAL_VERSION)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : invalid SO_BTH_GET_LOCAL_VER : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				gpTDIR->Unlock ();

				unsigned char hci_version, lmp_version;
				unsigned short hci_revision, lmp_revision, manufacturer;
				unsigned char lmp_features[8];
				int iRes = BthReadLocalVersion (&hci_version, &hci_revision, &lmp_version, &lmp_revision, &manufacturer, lmp_features);
				if (iRes == ERROR_SUCCESS) {
					BTH_LOCAL_VERSION *pv = (BTH_LOCAL_VERSION *)OptionValue;
					pv->hci_revision = hci_revision;
					pv->hci_version = hci_version;
					pv->lmp_subversion = lmp_revision;
					pv->lmp_version = lmp_version;
					pv->manufacturer = manufacturer;
					memcpy (pv->lmp_features, lmp_features, sizeof(pv->lmp_features));
				}
				return iRes;
			}

		case SO_BTH_GET_REMOTE_VER:			// no restrictions. optlen=sizeof(unsigned int), optval = &BTH_REMOTE_VERSION
			{
				if (*OptionLength != sizeof(BTH_REMOTE_VERSION)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : invalid SO_BTH_GET_REMOTE_VER : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				if (! (pConn && pConn->pAddr && (pConn->eStage == OPENED))) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : connection not open SO_BTH_GET_REMOTE_VER\n"));
					iRet = WSAEINVAL;
					break;
				}

				BT_ADDR bt = pConn->BTAddr.btAddr;
				gpTDIR->Unlock ();

				unsigned char lmp_version;
				unsigned short lmp_revision, manufacturer;
				unsigned char lmp_features[8];
				int iRes = BthReadRemoteVersion (&bt, &lmp_version, &lmp_revision, &manufacturer, lmp_features);
				if (iRes == ERROR_SUCCESS) {
					BTH_REMOTE_VERSION *pv = (BTH_REMOTE_VERSION *)OptionValue;
					pv->lmp_subversion = lmp_revision;
					pv->lmp_version = lmp_version;
					pv->manufacturer = manufacturer;
					memcpy (pv->lmp_features, lmp_features, sizeof(pv->lmp_features));
				}
				return iRes;
			}

		case SO_BTH_GET_LINK:			// bound only! optlen=sizeof(BTH_SOCKOPT_SECURITY), optval=BTH_SOCKOPT_SECURITY
			{
				if (*OptionLength != sizeof(BTH_SOCKOPT_SECURITY)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : invalid SO_BTH_GET_LINK : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				BTH_SOCKOPT_SECURITY *pbs = (BTH_SOCKOPT_SECURITY *)OptionValue;
				pbs->iLength = 16;

				BT_ADDR bt = pbs->btAddr;
				if ((bt == 0) && pConn)
					bt = pConn->BTAddr.btAddr;

				if (bt == 0) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_LINK: not bound\n"));
					iRet = WSAEADDRNOTAVAIL;
					break;
				}

				gpTDIR->Unlock ();

				return BthGetLinkKey (&bt, pbs->caData);
			}

		case SO_BTH_GET_MTU:			// optlen=sizeof(unsigned int), optval = &mtu
			if (*OptionLength != sizeof(unsigned int)) {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_MTU: bad length\n"));
				iRet = WSAEINVAL;
			} else if (pConn)
				*(unsigned int *)OptionValue = pConn->mtu;
			else if (pao)
				*(unsigned int *)OptionValue = pao->mtu;
			else {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_MTU: not bound\n"));
				iRet = WSAEADDRNOTAVAIL;
			}
			break;

		case SO_BTH_GET_MTU_MAX:		// optlen=sizeof(unsigned int), optval = &max. mtu
			if (*OptionLength != sizeof(unsigned int)) {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_MTU_MAX: bad length\n"));
				iRet = WSAEINVAL;
			} else if (pConn)
				*(unsigned int *)OptionValue = pConn->imax_mtu;
			else if (pao)
				*(unsigned int *)OptionValue = pao->imax_mtu;
			else {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_MTU_MAX: not bound\n"));
				iRet = WSAEADDRNOTAVAIL;
			}
			break;

		case SO_BTH_GET_MTU_MIN:		// optlen=sizeof(unsigned int), optval = &min. mtu
			if (*OptionLength != sizeof(unsigned int)) {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_MTU_MIN: bad length\n"));
				iRet = WSAEINVAL;
			} else if (pConn)
				*(unsigned int *)OptionValue = pConn->imin_mtu;
			else if (pao)
				*(unsigned int *)OptionValue = pao->imin_mtu;
			else {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_MTU_MIN: not bound\n"));
				iRet = WSAEADDRNOTAVAIL;
			}
			break;

		case SO_BTH_GET_XON_LIM:		// optlen=sizeof(unsigned int), optval = &xon
			if (*OptionLength != sizeof(unsigned int)) {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_XON_LIM: bad length\n"));
				iRet = WSAEINVAL;
			} else if (pConn)
				*(unsigned int *)OptionValue = pConn->xon_lim;
			else if (pao)
				*(unsigned int *)OptionValue = pao->xon_lim;
			else {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_XON_LIM: not bound\n"));
				iRet = WSAEADDRNOTAVAIL;
			}
			break;

		case SO_BTH_GET_XOFF_LIM:		// optlen=sizeof(unsigned int), optval = &xoff
			if (*OptionLength != sizeof(unsigned int)) {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_XOFF_LIM: bad length\n"));
				iRet = WSAEINVAL;
			} else if (pConn)
				*(unsigned int *)OptionValue = pConn->xoff_lim;
			else if (pao)
				*(unsigned int *)OptionValue = pao->xoff_lim;
			else {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_XOFF_LIM: not bound\n"));
				iRet = WSAEADDRNOTAVAIL;
			}
			break;

		case SO_BTH_GET_SEND_BUFFER:	// optlen=sizeof(unsigned int), optval = &max buffered size for send
			if (*OptionLength != sizeof(unsigned int)) {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_SEND_BUFFER: bad length\n"));
				iRet = WSAEINVAL;
			} else if (pConn)
				*(unsigned int *)OptionValue = pConn->imax_send;
			else if (pao)
				*(unsigned int *)OptionValue = pao->imax_send;
			else {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_SEND_BUFFER: not bound\n"));
				iRet = WSAEADDRNOTAVAIL;
			}
			break;

		case SO_BTH_GET_RECV_BUFFER:	// optlen=sizeof(unsigned int), optval = &max buffered size for recv
			if (*OptionLength != sizeof(unsigned int)) {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_RECV_BUFFER: bad length\n"));
				iRet = WSAEINVAL;
			} else if (pConn)
				*(unsigned int *)OptionValue = pConn->imax_recv;
			else if (pao)
				*(unsigned int *)OptionValue = pao->imax_recv;
			else {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_RECV_BUFFER: not bound\n"));
				iRet = WSAEADDRNOTAVAIL;
			}
			break;

		case SO_BTH_GET_V24_BR:			// connected only! optlen=2*sizeof(unsigned char), optval = &{v24 , br}
			if (*OptionLength != 2*sizeof(int)) {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_V24_BR: bad length\n"));
				iRet = WSAEINVAL;
			} else if (pConn && (pConn->eStage == OPENED)) {
				((int *)OptionValue)[0] = pConn->v24in;
				((int *)OptionValue)[1] = pConn->brin;
			} else {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_V24_BR: not connected\n"));
				iRet = WSAENOTCONN;
			}
			break;

		case SO_BTH_GET_RLS:			// connected only! optlen=sizeof(unsigned char), optval = &rls
			if (*OptionLength != sizeof(int)) {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_RLS: bad length\n"));
				iRet = WSAEINVAL;
			} else if (pConn && (pConn->eStage == OPENED)) {
				((int *)OptionValue)[0] = pConn->rlsin;
			} else {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_RLS: not connected\n"));
				iRet = WSAENOTCONN;
			}
			break;

		case SO_BTH_GET_FLOW_TYPE:		// connected only! optlen=sizeof(unsigned int), optval=&1=credit-based, 0=legacy
			if (*OptionLength != sizeof(int)) {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_RLS: bad length\n"));
				iRet = WSAEINVAL;
			} else if (pConn && (pConn->eStage == OPENED)) {
				*(int *)OptionValue = pConn->fc_credit ? 1 : 0;
			} else {
				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : SO_BTH_GET_RLS: not connected\n"));
				iRet = WSAENOTCONN;
			}
			break;

		default:
			IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : invalid socket option %d\n", OptionName));
			iRet = WSAENOPROTOOPT;
		}
	} __except (1) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_GetSocketInformation : Exception!\n"));
		iRet = WSAEFAULT;
	}

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_GetSocketInformation :: %d\n", iRet));
	gpTDIR->Unlock ();
	return iRet;
}

////////////////////////////////////////////////////////////////////////////////
//	BTR_GetWinsockMapping()
//		
//	Description:		
//		Called at stack load time. Returns a WINSOCK_MAPPING 
//		struct containing the AddressFamily, SocketType and Protocol 
//		triple socket() parms supported by this stack.
//
//		Required WINSOCK_MAPPING struct size. The function is called again 
//	    with a larger buffer if needed.
//		
//	Arguments:
//		pMapping :: 
//			structure determining the address family/socket
//			type/protocol supported by this stack driver.
//	
//		dwMappingLength :: 
//			Length of the given buffer.
//
//	Returns:
//		Size of the WINSOCK_MAPPING this driver support.
//
//	Comments:
//		Called during stack load time.
//
static DWORD BTR_GetWinsockMapping
(
PWINSOCK_MAPPING Mapping,
DWORD MappingLength
) {
    IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_GetWinsockMapping\n"));

	if (MappingLength >= sizeof(BT_MAPPING))
        memcpy(Mapping, &BT_MAPPING, sizeof(BT_MAPPING));

    IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_GetWinsockMapping\n"));
    return sizeof(BT_MAPPING);	
}

////////////////////////////////////////////////////////////////////////////////
//	BTR_Notify()
//		
//	Description:
//		Called after the socket event(s) specified in IRLMP_OpenSocket()
//		occur.
//		
//	Arguments:
//		pvHelperDllSocketContext::
//			Pointer to the BT_SOCKET_CONTEXT returned by BT_OpenSocket().
//
//		SocketHandle::
//			TDI Address Object handle if the socket is bound, otherwise NULL.
//
//		hTdiAddressObjectHandle::
//			TDI Address Object handle if the socket is bound, otherwise NULL.
//
//		hTdiConnectionObjectHandle::
//			TDI Connection Object handle if socket is connected, otherwise NULL.
//
//		dwNotifyEvent::
//			One of the:
//				WSH_NOTIFY_BIND
//				WSH_NOTIFY_LISTEN
//				WSH_NOTIFY_CONNECT
//				WSH_NOTIFY_ACCEPT
//				WSH_NOTIFY_SHUTDOWN_RECEIVE
//				WSH_NOTIFY_SHUTDOWN_SEND
//				WSH_NOTIFY_SHUTDOWN_ALL
//				WSH_NOTIFY_CLOSE
//				WSH_NOTIFY_CONNECT_ERROR
//
//	Returns:
//		NO_ERROR (winerror.h) on success, or (winsock.h)
//
//	Comments:
//
//
static TDI_STATUS BTR_Notify
(
PVOID pvHelperDllSocketContext,
SOCKET SocketHandle,
HANDLE hTdiAddressObjectHandle,
HANDLE hTdiConnectionObjectHandle,
DWORD dwNotifyEvent
) {
    IFDBG(DebugOut (DEBUG_TDI_TRACE, L"BTR_Notify [%s] [0x%x]\r\n",
        dwNotifyEvent == WSH_NOTIFY_BIND			 ? L"WSH_NOTIFY_BIND" :
        dwNotifyEvent == WSH_NOTIFY_LISTEN			 ? L"WSH_NOTIFY_LISTEN" :        
	    dwNotifyEvent == WSH_NOTIFY_CONNECT			 ? L"WSH_NOTIFY_CONNECT" :        
	    dwNotifyEvent == WSH_NOTIFY_ACCEPT			 ? L"WSH_NOTIFY_ACCEPT" :
	    dwNotifyEvent == WSH_NOTIFY_SHUTDOWN_RECEIVE ? L"WSH_NOTIFY_SHUTDOWN_RECEIVE" :
	    dwNotifyEvent == WSH_NOTIFY_SHUTDOWN_SEND    ? L"WSH_NOTIFY_SHUTDOWN_SEND" :
	    dwNotifyEvent == WSH_NOTIFY_SHUTDOWN_ALL	 ? L"WSH_NOTIFY_SHUTDOWN_ALL" :
	    dwNotifyEvent == WSH_NOTIFY_CLOSE			 ? L"WSH_NOTIFY_CLOSE" :
	    dwNotifyEvent == WSH_NOTIFY_CONNECT_ERROR	 ? L"WSH_NOTIFY_CONNECT_ERROR" :
		L"unexpected WSH_NOTIFY",
		dwNotifyEvent));

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_Notify : WSANOTINITIALISED\n"));
		return WSANOTINITIALISED;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_Notify : WSANOTINITIALISED\n"));
		gpTDIR->Unlock ();
		return WSANOTINITIALISED;
	}

	int status = NO_ERROR;

	if (dwNotifyEvent == WSH_NOTIFY_LISTEN) {
		////////////////////////////////////////////////////////////////////////
		//	When we are notified to LISTEN, AFD should have informed us on 
		//	the connection event to call back via SetEventEntry() for
		//	TDI_EVENT_CONNECT.
		//	We will now trigger L2CAP to start listening to the port that
		//	this socket bind to.
		//
		RFCOMM_ADDRESS_OBJECT *pao = GetAddressObject (hTdiAddressObjectHandle);
		if (pao) {
			if (!pao->fListenOn)
				status = WSAEINVAL;
		} else
			status = WSAESTALE;
	} else if (dwNotifyEvent == WSH_NOTIFY_BIND) {	
		RFCOMM_ADDRESS_OBJECT *pao = GetAddressObject (hTdiAddressObjectHandle);
		RFCOMM_SOCKET_OBJECT *pSocket = GetSocketObject (pvHelperDllSocketContext);
		if (pao && pSocket)
			pSocket->pao = pao;
		else
			status = WSAESTALE;
	} else if (dwNotifyEvent == WSH_NOTIFY_CLOSE) {	
		RFCOMM_SOCKET_OBJECT *pSocket = GetSocketObject (pvHelperDllSocketContext);
		if (pSocket)
			CloseSocketObject (pSocket);
		else
			status = WSAENOTSOCK;
	}

	IFDBG(DebugOut (status == NO_ERROR ? DEBUG_TDI_TRACE : DEBUG_ERROR, L"-BTR_Notify :: %d\n", status));
	gpTDIR->Unlock ();
	return status;
}

////////////////////////////////////////////////////////////////////////////////
//	BT_OpenSocket()
//		
//	Description:
//		Called after winsock's socket() call.
//		Validates the parms and allocates internal data structure for new
//		socket connection.
//
//	Arguments:
//		piAddressFamily::
//			From socket(AF_BT, ,).   
//			Must be AF_BT.
//
//		piSocketType::
//			From socket( , SOCK_SEQPACKET, ).   
//			Must be SOCK_STREAM
//
//		piProtocol::
//			From socket( , , 0)
//			Must be 0x00.
//
//		pwszTransportDeviceName::
//			Will copy the BLUETOOTH_TRANSPORT_NAME to this buffer.
//
//		pvHelperSocketContext::
//			Returns the address of a new BT_SOCKET_CONTEXT. 
//			This context will be returned in 
//			BT_Notify(), 
//			BT_OpenConnection(), 
//			BT_GetWildcardSockaddr(), 
//			BT_SetSocketInformation(),
//			BT_GetSocketInformation(). 
//
//		pdwNotificationEvents::
//			Bitmask indicating which Winsock events should result
//			in a call to BT_Notify().   
//
//	Returns:
//		NO_ERROR on success, or winsock.h error.
//
//	Comments:
//		Called after winsock's socket() call.
//	
static TDI_STATUS BTR_OpenSocket
(
PINT piAddressFamily,
PINT piSocketType,
PINT piProtocol,
PWSTR pwszTransportDeviceName,
PVOID *pvHelperDllSocketContext,
PDWORD pdwNotificationEvents
) {
	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_OpenSocket\n"));
	////////////////////////////////////////////////////////////////////////////
	//	Reject it immediately if it's not our supported protocol.
	//
	if ((*piAddressFamily != (int) BT_MAPPING.Mapping[0].AddressFamily) ||
		(*piSocketType    != (int) BT_MAPPING.Mapping[0].SocketType)	||
		(*piProtocol      != (int) BT_MAPPING.Mapping[0].Protocol))	{
		IFDBG(DebugOut (DEBUG_WARN, L"[TDI] Unsupported protocol in BTR_OpenSocket\n"));

        return WSAEINVAL;
	}

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_OpenSocket : WSANOTINITIALISED\n"));
		return WSANOTINITIALISED;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_OpenSocket : WSANOTINITIALISED\n"));
		gpTDIR->Unlock ();
		return WSANOTINITIALISED;
	}

	wcscpy(pwszTransportDeviceName, RFCOMM_TRANSPORT_NAME);

	RFCOMM_SOCKET_OBJECT *pSock = new RFCOMM_SOCKET_OBJECT;

    if (! pSock) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_OpenSocket : WSAENOBUFS\n"));
		gpTDIR->Unlock ();
		return WSAENOBUFS;
    }

	pSock->pNext = gpTDIR->psolist;
	gpTDIR->psolist = pSock;

	////////////////////////////////////////////////////////////////////////////
	//	Here are the goodies we return..
	//
	*pvHelperDllSocketContext = pSock;	
	*pdwNotificationEvents = 
		WSH_NOTIFY_BIND					| 
		WSH_NOTIFY_LISTEN				|	
        WSH_NOTIFY_CLOSE				| 
		WSH_NOTIFY_CONNECT				|
		WSH_NOTIFY_ACCEPT				|
		WSH_NOTIFY_SHUTDOWN_RECEIVE		|
		WSH_NOTIFY_SHUTDOWN_SEND		|
		WSH_NOTIFY_SHUTDOWN_ALL			|
		WSH_NOTIFY_CLOSE				|
		WSH_NOTIFY_CONNECT_ERROR;       


	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_OpenSocket :: NO_ERROR\n"));
	gpTDIR->Unlock ();
	return NO_ERROR;
}

////////////////////////////////////////////////////////////////////////////////
//	BT_SetSocketInformation()
//		
//	Description:
//		Called by the Winsock DLL when a level/option name combination
//		is passed to setsockopt() that the winsock DLL does not understand.
//		
//	Arguments:
//		NO_ERROR (winerror.h) on success, or (winsock.h)
//
//	Returns:
//		pvHelperDllSocketContext::
//			Pointer to the BT_SOCKET_CONTEXT returned by BT_OpenSocket().
//
//		SocketHandle::
//			Handle of the socket for which we're getting information.
//	
//		hTdiAddressObjectHandle::
//			TDI address object of the socket, or NULL.
//
//		hTdiConnectionObjectHandle::
//			TDI connection object of the socket, or NULL.
//
//		iLevel::
//			level, from setsockopt().			
//
//		iOptionName::
//			optname, from setsockopt().
//
//		pcOptionValue::
//			optval, from setsockopt().
//
//		iOptionLength::
//			optlen, from setsockopt().
//
//	Comments:
//
//
static TDI_STATUS BTR_SetSocketInformation
(
PVOID HelperDllSocketContext,
SOCKET SocketHandle,
HANDLE TdiAddressObjectHandle,
HANDLE TdiConnectionObjectHandle,
INT Level,
INT OptionName,
PCHAR OptionValue,
INT OptionLength
) {
	if ((Level != SOL_RFCOMM) || (OptionLength < 4))
		return WSAEINVAL;

	if (! gpTDIR) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_SetSocketInformation : WSANOTINITIALISED\n"));
		return WSANOTINITIALISED;
	}

	gpTDIR->Lock ();
	if (! gpTDIR->fIsInitialized) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_SetSocketInformation : WSANOTINITIALISED\n"));
		gpTDIR->Unlock ();
		return WSANOTINITIALISED;
	}

	//
	//	Process setsockopt data here
	//
	RFCOMM_SOCKET_OBJECT *pSocket = GetSocketObject (HelperDllSocketContext);
	if (! pSocket) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_SetSocketInformation : WSANOTINITIALISED\n"));
		gpTDIR->Unlock ();
		return WSAENOTSOCK;
	}

	if (! pSocket->hSocketHandle)
		pSocket->hSocketHandle = (HANDLE)SocketHandle;

	SVSUTIL_ASSERT (pSocket->hSocketHandle == (HANDLE)SocketHandle);

	RFCOMM_CONNECTION_OBJECT *pConn = GetConnectionObject (TdiConnectionObjectHandle);
	RFCOMM_ADDRESS_OBJECT *pao = GetAddressObject (TdiAddressObjectHandle);

	OPT *pOpts = NULL;

	if (pao)
		pOpts = pao;
	else
		pOpts = pSocket;

	int iRet = NO_ERROR;

	__try {
		switch (OptionName) {
		case SO_BTH_SET_READ_REMOTE_NAME:	// no restrictions. optlen=sizeof(BTH_REMOTE_NAME), optval = &BTH_REMOTE_NAME
			{
				if (OptionLength != sizeof(BTH_REMOTE_NAME)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_GetSocketInformation : invalid SO_BTH_GET_REMOTE_NAME : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				gpTDIR->Unlock ();

				BTH_REMOTE_NAME *prn = (BTH_REMOTE_NAME *)OptionValue;
				unsigned int uiRetSize = 0;
				return BthRemoteNameQuery (&prn->bt, 248, &uiRetSize, prn->szNameBuffer);
			}

		case SO_BTH_SET_PAGE_TO:	// no restrictions. optlen=sizeof(unsigned int), optval = &page timeout
			{
				if (OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_PAGE_TO : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				unsigned int page = *(unsigned int *)OptionValue;
				gpTDIR->Unlock ();
				return BthWritePageTimeout ((unsigned short)page);
			}

		case SO_BTH_SET_SCAN:	// no restrictions. optlen=sizeof(unsigned int), optval = &scan
			{
				if (OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_SCAN : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				unsigned int mask = *(unsigned int *)OptionValue;
				gpTDIR->Unlock ();
				return BthWriteScanEnableMask ((unsigned char)mask);
			}

		case SO_BTH_SET_AUTHN_ENABLE:	// no restrictions. optlen=sizeof(unsigned int), optval = &authn_enable
			{
				if (OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_AUTHN_ENABLE : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				unsigned int ae = *(unsigned int *)OptionValue;
				gpTDIR->Unlock ();
				return BthWriteAuthenticationEnable ((unsigned char)ae);
			}

		case SO_BTH_SET_LINK_POLICY:	// connected only. optlen=sizeof(int), optval = &int
			{
				if (OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_LINK_POLICY : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				if (pConn && (pConn->eStage == OPENED)) {
					BT_ADDR bt = pConn->BTAddr.btAddr;
					unsigned short settings = (short)*(int *)OptionValue;

					IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_SetSocketInformation :: settings for %04x%08x = 0x%04x\n", GET_NAP(bt), GET_SAP(bt), settings));
					gpTDIR->Unlock ();
					return BthWriteLinkPolicySettings (&bt, settings);
				}

				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_LINK_POLICY : NOT CONNECTED\n"));
				iRet = WSAENOTCONN;
				break;
			}

		case SO_BTH_ENTER_HOLD_MODE:	// connected only. optlen=sizeof(BTH_HOLD_MODE), optval = &BTH_HOLD_MODE
			{
				if (OptionLength != sizeof(BTH_HOLD_MODE)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_ENTER_HOLD_MODE : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				if (pConn && (pConn->eStage == OPENED)) {
					BT_ADDR bt = pConn->BTAddr.btAddr;
					BTH_HOLD_MODE *phm = (BTH_HOLD_MODE *)OptionValue;

					IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_SetSocketInformation :: entering hold mode for %04x%08x\n", GET_NAP(bt), GET_SAP(bt)));
					gpTDIR->Unlock ();
					return BthEnterHoldMode (&bt, phm->hold_mode_max, phm->hold_mode_min, &phm->interval);
				}

				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_ENTER_HOLD_MODE : NOT CONNECTED\n"));
				iRet = WSAENOTCONN;
				break;
			}

		case SO_BTH_ENTER_SNIFF_MODE:	// connected only. optlen=sizeof(BTH_SNIFF_MODE), optval = &BTH_SNIFF_MODE
			{
				if (OptionLength != sizeof(BTH_SNIFF_MODE)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_ENTER_SNIFF_MODE : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				if (pConn && (pConn->eStage == OPENED)) {
					BT_ADDR bt = pConn->BTAddr.btAddr;
					BTH_SNIFF_MODE *psm = (BTH_SNIFF_MODE *)OptionValue;

					IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_SetSocketInformation :: entering sniff mode for %04x%08x\n", GET_NAP(bt), GET_SAP(bt)));
					gpTDIR->Unlock ();
					return BthEnterSniffMode (&bt, psm->sniff_mode_max, psm->sniff_mode_min, psm->sniff_attempt, psm->sniff_timeout, &psm->interval);
				}

				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_ENTER_SNIFF_MODE : NOT CONNECTED\n"));
				iRet = WSAENOTCONN;
				break;
			}

		case SO_BTH_EXIT_SNIFF_MODE:	// connected only. optlen, optval ignored
			{
				if (pConn && (pConn->eStage == OPENED)) {
					BT_ADDR bt = pConn->BTAddr.btAddr;

					IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_SetSocketInformation :: exiting sniff mode for %04x%08x\n", GET_NAP(bt), GET_SAP(bt)));
					gpTDIR->Unlock ();
					return BthExitSniffMode (&bt);
				}

				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_EXIT_SNIFF_MODE : NOT CONNECTED\n"));
				iRet = WSAENOTCONN;
				break;
			}

		case SO_BTH_ENTER_PARK_MODE:	// connected only. optlen=sizeof(BTH_PARK_MODE), optval = &BTH_PARK_MODE
			{
				if (OptionLength != sizeof(BTH_PARK_MODE)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_ENTER_PARK_MODE : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				if (pConn && (pConn->eStage == OPENED)) {
					BT_ADDR bt = pConn->BTAddr.btAddr;
					BTH_PARK_MODE *ppm = (BTH_PARK_MODE *)OptionValue;

					IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_SetSocketInformation :: entering park mode for %04x%08x\n", GET_NAP(bt), GET_SAP(bt)));
					gpTDIR->Unlock ();
					return BthEnterParkMode (&bt, ppm->beacon_max, ppm->beacon_min, &ppm->interval);
				}

				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_ENTER_PARK_MODE : NOT CONNECTED\n"));
				iRet = WSAENOTCONN;
				break;
			}


		case SO_BTH_EXIT_PARK_MODE:	// connected only. optlen, optval ignored
			{
				if (pConn && (pConn->eStage == OPENED)) {
					BT_ADDR bt = pConn->BTAddr.btAddr;

					IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_SetSocketInformation :: exiting park mode for %04x%08x\n", GET_NAP(bt), GET_SAP(bt)));
					gpTDIR->Unlock ();
					return BthExitParkMode (&bt);
				}

				IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_EXIT_PARK_MODE : NOT CONNECTED\n"));
				iRet = WSAENOTCONN;
				break;
			}

		case SO_BTH_SET_COD:	// no restrictions. optlen=sizeof(unsigned int), optval = &cod
			{
				if (OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_COD : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				unsigned int cod = *(unsigned int *)OptionValue;
				gpTDIR->Unlock ();
				return BthWriteCOD (cod);
			}

		case SO_BTH_AUTHENTICATE:	// optlen, optval ignored
			{
				if (pConn) {
					pConn->fAuthenticate = TRUE;
					if (pConn->pAddr && (pConn->eStage == OPENED)) {
						BT_ADDR bt = pConn->BTAddr.btAddr;

						IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_SetSocketInformation :: authenticating live connection\n"));
						gpTDIR->Unlock ();

						return BthAuthenticate (&bt);
					}
					break;
				}

				pOpts->fAuthenticate = TRUE;
				pOpts->uiConfigMask |= OPT_CONFIG_AUTH;

				break;
			}

		case SO_BTH_ENCRYPT:	// optlen=sizeof(unsigned int), optval = &(unsigned int)TRUE/FALSE
			{
				if (OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_ENCRYPT : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				int flag = *(int *)OptionValue ? TRUE : FALSE;

				if (pConn) {
					pConn->fEncrypt = flag;
					if (pConn->pAddr && (pConn->eStage == OPENED)) {
						BT_ADDR bt = pConn->BTAddr.btAddr;

						IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_SetSocketInformation :: encryption %s on live connection\n", flag ? L"on" : L"off"));
						gpTDIR->Unlock ();

						return BthSetEncryption (&bt, flag);
					}
					break;
				}

				pOpts->fEncrypt = flag;
				pOpts->uiConfigMask |= OPT_CONFIG_CRYPT;

				break;
			}

		case SO_BTH_SET_PIN:	// bound only! survives socket! optlen=sizeof(BTH_SOCKOPT_SECURITY), optval=&BTH_SOCKOPT_SECURITY
			{
				if (OptionLength != sizeof(BTH_SOCKOPT_SECURITY)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_PIN : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				BTH_SOCKOPT_SECURITY *pbs = (BTH_SOCKOPT_SECURITY *)OptionValue;
				if ((pbs->iLength < 0) || (pbs->iLength > PIN_SIZE)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_PIN : length\n"));
					iRet = WSAEINVAL;
					break;
				}


				BT_ADDR bt = pbs->btAddr;
				if ((bt == 0) && pConn)
					bt = pConn->BTAddr.btAddr;

				if (bt == 0) {
					if (pConn) {
						pConn->cPinLength = pbs->iLength;
						memcpy (pConn->caPinData, pbs->caData, pbs->iLength);
						break;
					}

					pOpts->cPinLength = pbs->iLength;
					memcpy (pOpts->caPinData, pbs->caData, pbs->iLength);
					pOpts->uiConfigMask |= OPT_CONFIG_PIN;

					break;
				}

				gpTDIR->Unlock ();

				return pbs->iLength ? BthSetPIN (&bt, pbs->iLength, pbs->caData) : BthRevokePIN (&bt);
			}

		case SO_BTH_SET_LINK:	// optlen=sizeof(BTH_SOCKOPT_SECURITY), optval=&BTH_SOCKOPT_SECURITY
			{
				if (OptionLength != sizeof(BTH_SOCKOPT_SECURITY)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_LINK : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				BTH_SOCKOPT_SECURITY *pbs = (BTH_SOCKOPT_SECURITY *)OptionValue;
				if ((pbs->iLength != 0) && (pbs->iLength != KEY_SIZE)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_LINK : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				BT_ADDR bt = pbs->btAddr;
				if ((bt == 0) && pConn)
					bt = pConn->BTAddr.btAddr;

				if (bt != 0) {
					gpTDIR->Unlock ();

					return pbs->iLength ? BthSetLinkKey (&bt, pbs->caData) : BthRevokeLinkKey (&bt);
				} else {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : SO_BTH_SET_LINK : WSAEADDRNOTAVAIL\n"));
					iRet = WSAEADDRNOTAVAIL;
				}

				break;
			}

		case SO_BTH_SET_MTU:	// unconnected only! optlen=sizeof(unsigned int), optval = &mtu
			{
				if (OptionLength != sizeof(unsigned int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_MTU : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				unsigned short mtu = (unsigned short)*(unsigned int *)OptionValue;
				if ((mtu < TDIR_MTUMIN) || (mtu > TDIR_MTUMAX)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_MTU : out of bounds\n"));
					iRet = WSAEINVAL;
					break;
				}

				if (pConn) {
					if (pConn->eStage == INITED) {
						pConn->mtu = mtu;
						if (mtu > pConn->imax_mtu)
							pConn->imax_mtu = mtu;
						if (mtu < pConn->imin_mtu)
							pConn->imin_mtu = mtu;
					} else {
						IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_MTU : already connected\n"));
						iRet = WSAEISCONN;
					}

					break;
				}

				pOpts->mtu = mtu;
				if (mtu > pOpts->imax_mtu)
					pOpts->imax_mtu = mtu;
				if (mtu < pOpts->imin_mtu)
					pOpts->imin_mtu = mtu;

				pOpts->uiConfigMask |= OPT_CONFIG_MTU;
				break;
			}

		case SO_BTH_SET_MTU_MAX:	// unconnected only! optlen=sizeof(unsigned int), optval = &max. mtu
			{
				if (OptionLength != sizeof(unsigned int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_MTU_MAX : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				unsigned short mtumax = (unsigned short)*(unsigned int *)OptionValue;
				if ((mtumax < TDIR_MTUMIN) || (mtumax > TDIR_MTUMAX)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_MTU_MAX : out of bounds\n"));
					iRet = WSAEINVAL;
					break;
				}
				
				if (pConn) {
					if (pConn->eStage == INITED) {
						pConn->imax_mtu = mtumax;
						if (mtumax < pConn->mtu)
							pConn->mtu = mtumax;
						if (mtumax < pConn->imin_mtu)
							pConn->imin_mtu = mtumax;
					} else {
						IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_MTU_MAX : already connected\n"));
						iRet = WSAEISCONN;
					}

					break;
				}

				pOpts->imax_mtu = mtumax;
				if (mtumax < pOpts->mtu)
					pOpts->mtu = mtumax;
				if (mtumax < pOpts->imin_mtu)
					pOpts->imin_mtu = mtumax;

				pOpts->uiConfigMask |= OPT_CONFIG_MTU_MAX;
				break;
			}

		case SO_BTH_SET_MTU_MIN:	// unconnected only! optlen=sizeof(unsigned int), optval = &min. mtu
			{
				if (OptionLength != sizeof(unsigned int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_MTU_MIN : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				unsigned short mtumin = (unsigned short)*(unsigned int *)OptionValue;
				if ((mtumin < TDIR_MTUMIN) || (mtumin > TDIR_MTUMAX)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_MTU_MIN : out of bounds\n"));
					iRet = WSAEINVAL;
					break;
				}

				if (pConn) {
					if (pConn->eStage == INITED) {
						pConn->imax_mtu = mtumin;
						if (mtumin > pConn->mtu)
							pConn->mtu = mtumin;
						if (mtumin > pConn->imax_mtu)
							pConn->imax_mtu = mtumin;
					} else {
						IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_MTU_MIN : already connected\n"));
						iRet = WSAEISCONN;
					}

					break;
				}

				pOpts->imin_mtu = mtumin;
				if (mtumin > pOpts->mtu)
					pOpts->mtu = mtumin;
				if (mtumin > pOpts->imax_mtu)
					pOpts->imax_mtu = mtumin;

				pOpts->uiConfigMask |= OPT_CONFIG_MTU_MIN;
				break;
			}

		case SO_BTH_SET_XON_LIM:	// optlen=sizeof(unsigned int), optval = &xon limit (set flow off)
			{
				if (OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_XON_LIM : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				int xon = *(int *)OptionValue;

				if (pConn) {
					if (pConn->eStage == INITED) {
						if ((xon < 0) || (xon > pConn->imax_recv) || (xon < pConn->xoff_lim)) {
							IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_XON_LIM : out of bounds\n"));
							iRet = WSAEINVAL;
						} else
							pConn->xon_lim = xon;
					} else {
						IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_XON_LIM : already connected\n"));
						iRet = WSAEISCONN;
					}

					break;
				}

				if ((xon < 0) || (xon > pOpts->imax_recv) || (xon < pOpts->xoff_lim)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_XON_LIM : out of bounds\n"));
					iRet = WSAEINVAL;
				} else {
					pOpts->xon_lim = xon;
					pOpts->uiConfigMask |= OPT_CONFIG_XON;
				}

				break;
			}

		case SO_BTH_SET_XOFF_LIM:	// optlen=sizeof(unsigned int), optval = &xoff limit (set flow on)
			{
				if (OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_XOFF_LIM : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				int xoff = *(int *)OptionValue;
				if (pConn) {
					if (pConn->eStage == INITED) {
						if ((xoff < 0) || (xoff > pConn->imax_recv) || (xoff > pConn->xon_lim)) {
							IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_XOFF_LIM : out of bounds\n"));
							iRet = WSAEINVAL;
						} else
							pConn->xoff_lim = xoff;
					} else {
						IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_XOFF_LIM : already connected\n"));
						iRet = WSAEISCONN;
					}

					break;
				}

				if ((xoff < 0) || (xoff > pOpts->imax_recv) || (xoff > pOpts->xon_lim)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_XOFF_LIM : out of bounds\n"));
					iRet = WSAEINVAL;
				} else {
					pOpts->xoff_lim = xoff;
					pOpts->uiConfigMask |= OPT_CONFIG_XOFF;
				}
				break;
			}

		case SO_BTH_SET_SEND_BUFFER:	// optlen=sizeof(unsigned int), optval = &max buffered size for send
			{
				if (OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_SEND_BUFFER : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				int isend = *(int *)OptionValue;
				if (isend <= 0) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_SEND_BUFFER : out of bounds\n"));
					iRet = WSAEINVAL;
					break;
				}

				if (pConn) {
					if (pConn->eStage == INITED)
						pConn->imax_send = isend;
					else {
						IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_SEND_BUFFER : already connected\n"));
						iRet = WSAEISCONN;
					}

					break;
				}

				pOpts->imax_send = isend;
				pOpts->uiConfigMask |= OPT_CONFIG_MAX_SEND;

				break;
			}

		case SO_BTH_SET_RECV_BUFFER:	// optlen=sizeof(unsigned int), optval = &max buffered size for recv
			{
				if (OptionLength != sizeof(int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_RECV_BUFFER : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				int irecv = *(int *)OptionValue;
				if (irecv <= 0) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_RECV_BUFFER : out of bounds\n"));
					iRet = WSAEINVAL;
					break;
				}

				if (pConn) {
					if (pConn->eStage == INITED) {
						pConn->imax_recv = irecv;
						if ((pConn->xoff_lim > irecv) || (pConn->xon_lim > irecv)) {
							pConn->xoff_lim = irecv / 5;
							pConn->xon_lim  = (irecv / 5) * 4;
						}
					} else {
						IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SET_RECV_BUFFER : already connected\n"));
						iRet = WSAEISCONN;
					}

					break;
				}

				pOpts->imax_recv = irecv;
				if ((pOpts->xoff_lim > irecv) || (pOpts->xon_lim > irecv)) {
					pOpts->xoff_lim = irecv / 5;
					pOpts->xon_lim  = (irecv / 5) * 4;
				}

				pOpts->uiConfigMask |= OPT_CONFIG_MAX_RECV;
				break;
			}

		case SO_BTH_SEND_MSC:	// connected only! optlen=2*sizeof(unsigned int), optval = &{v24, br}
			{
				if (OptionLength != 2*sizeof(unsigned int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SEND_MSC : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				unsigned char v24 = (unsigned char)(((int *)OptionValue)[0] & (TDIR_V24_RTC | TDIR_V24_RTR | TDIR_V24_IC));
				unsigned char br  = (unsigned char)(((int *)OptionValue)[1]);

				if (pConn && (pConn->eStage == OPENED)) {
					iRet = SendMSC (pConn, v24, br);
				} else {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SEND_MSC : NOT CONNECTED\n"));
					iRet = WSAENOTCONN;
				}
				break;
			}

		case SO_BTH_SEND_RLS:	// connected only! optlen=sizeof(unsigned int), optval = &rls
			{
				if (OptionLength != sizeof(unsigned int)) {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SEND_RLS : length\n"));
					iRet = WSAEINVAL;
					break;
				}

				unsigned char rls = (*(unsigned int *)OptionValue) & 0xf;

				if (pConn && (pConn->eStage == OPENED)) {
					iRet = SendRLS (pConn, rls);
				} else {
					IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid SO_BTH_SEND_RLS : NOT CONNECTED\n"));
					iRet = WSAENOTCONN;
				}
				break;
			}

		default:
			IFDBG(DebugOut (DEBUG_WARN, L"[TDI] BTR_SetSocketInformation : invalid socket option %d\n", OptionName));
			iRet = WSAENOPROTOOPT;
		}
	} __except (1) {
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] BTR_SetSocketInformation : Exception!\n"));
		iRet = WSAEFAULT;
	}

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_SetSocketInformation :: %d\n", iRet));
	gpTDIR->Unlock ();
	return iRet;
}

////////////////////////////////////////////////////////////////////////////////
//	BT_EnumProtocols()
//		
//	Description:
//		Called at stack load time.
//		Fill up the PROTOCOL_INFO structure supported by this stack driver.
//		
//	Arguments:
//		piProtocols	::
//			Ignored/Not used.
//
//		pszTransportKeyName ::
//			Ignored/Not used.
//
//		lpvProtocolBuffer::
//			The buffer to return the PROTOCOL_INFO structure.
//
//		lpdwBufferLength::
//			Pointer to the length of the buffer pointed to be lpvProtocolBuffer.
//
//	Returns:
//
//	Comments:
//		Called during stack load time.
//
//
static TDI_STATUS BTR_EnumProtocols
(
LPINT lpiProtocols,
LPTSTR lpTransportKeyName,
LPVOID lpProtocolBuffer,
LPDWORD lpdwBufferLength
) {
	DWORD			dwBytesRequired;
	PPROTOCOL_INFO	pProtocolInfo;

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_EnumProtocols\n"));
	
	dwBytesRequired = sizeof(PROTOCOL_INFO) + sizeof(RFCOMM_TRANSPORT_NAME);

	if (dwBytesRequired > *lpdwBufferLength) {
		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"-BTR_EnumProtocols : not enough space\n"));
        *lpdwBufferLength = dwBytesRequired;
        return -1;
    }


	////////////////////////////////////////////////////////////////////////////
	//	Start filling the protocol info to be returned to the AFD.
	//
	pProtocolInfo = (PROTOCOL_INFO *)lpProtocolBuffer;	
	
    pProtocolInfo->iAddressFamily = AF_BT;
    pProtocolInfo->iMaxSockAddr   = sizeof (SOCKADDR_BTH);
    pProtocolInfo->iMinSockAddr   = sizeof (SOCKADDR_BTH);
    pProtocolInfo->iSocketType    = SOCK_STREAM;
    pProtocolInfo->iProtocol      = BTHPROTO_RFCOMM;
    pProtocolInfo->dwMessageSize  = 0;   
	pProtocolInfo->dwServiceFlags = XP_GUARANTEED_DELIVERY | XP_GUARANTEED_ORDER;


	////////////////////////////////////////////////////////////////////////////
    //	Use the end of the passed in buffer to store the name
	//
    pProtocolInfo->lpProtocol = (LPWSTR) ((LPBYTE)pProtocolInfo + sizeof(PROTOCOL_INFO));    
    wcscpy (pProtocolInfo->lpProtocol, RFCOMM_TRANSPORT_NAME);
    	
	////////////////////////////////////////////////////////////////////////////
    //	Indicate one protocol info structure returned.
	//
	*lpdwBufferLength = dwBytesRequired;

	IFDBG(DebugOut (DEBUG_TDI_TRACE, L"+BTR_EnumProtocols\n"));
    return 1;	
}

////////////////////////////////////////////////////////////////////////////////
//	TDI dispatch table returned to AFD.
//
const 
TDIDispatchTable BTR_DispatchTable =
{
    BTR_OpenAddressEntry,
    BTR_CloseAddressEntry,
    BTR_OpenConnectionEntry,
    BTR_CloseConnectionEntry,
    BTR_AssociateAddressEntry,
    BTR_DisAssociateAddressEntry,
    BTR_ConnectEntry,
    BTR_DisconnectEntry,
    BTR_ListenEntry,
	BTR_AcceptEntry,
    BTR_ReceiveEntry,
    BTR_SendEntry,
    BTR_SendDatagramEntry,
    BTR_ReceiveDatagramEntry,
    BTR_SetEventEntry,
    BTR_QueryInformationEntry,
    BTR_SetInformationEntry,
    BTR_ActionEntry,
    BTR_QueryInformationExEntry,
    BTR_SetInformationExEntry,
	BTR_GetSockaddrType,
    BTR_GetWildcardSockaddr,
    BTR_GetSocketInformation,
    BTR_GetWinsockMapping,
    BTR_Notify,
    BTR_OpenSocket,
    BTR_SetSocketInformation,
	BTR_EnumProtocols
};	

////////////////////////////////////////////////////////////////////////////////
//	Register()
//		
//	Description:
//		AFD entry point to register protocol.
//		
//	Arguments:
//		pRegister	::	Function pointer to register TDI dispatch table 
//						with AFD.
//		pAfdCS		::	Must be NULL. Not required anymore by AFD.
//
//	Returns:
//
//	Comments:
//
//
extern "C" void
Register(
	PWSRegister			pRegister, 
    CRITICAL_SECTION	*pAfdCS)
{
	DebugInitialize ();

	IFDBG(DebugOut (DEBUG_TDI_INIT, L"+Register\n"));

	////////////////////////////////////////////////////////////////////////////
	//	Initialize the TDI.
	//

#if defined (SDK_BUILD)
	int CheckTime (void);

	if (! CheckTime ())
		return;
#endif

	////////////////////////////////////////////////////////////////////////////
	//	Early initialization the BT stack..
	//		

	////////////////////////////////////////////////////////////////////////////
	//	Register the stack driver with the AFD.
	//
	if (pRegister(BLUETOOTH_TRANSPORT_NAME, 
			(TDIDispatchTable *)&BTR_DispatchTable) != TDI_SUCCESS)
	{
		IFDBG(DebugOut (DEBUG_ERROR, L"[TDI] failed to register with AFD\n"));
	} else {
		HANDLE hDevice = RegisterDevice (L"BTD", 0, L"btd.dll", 0);
		IFDBG(DebugOut (DEBUG_TDI_TRACE, L"hDevice = 0x%08x GetLastError = 0x%08x (%d)\n", hDevice, GetLastError (), GetLastError ()));
	}
}	//	Register()

//
//	Memory allocation
//
void *RFCOMM_ADDRESS_OBJECT::operator new (size_t size) {
	SVSUTIL_ASSERT (size == sizeof(RFCOMM_ADDRESS_OBJECT));

	return svsutil_GetFixed (gpTDIR->pfmdAO);
}

void RFCOMM_ADDRESS_OBJECT::operator delete (void *ptr) {
	svsutil_FreeFixed (ptr, gpTDIR->pfmdAO);
}

void *RFCOMM_CONNECTION_OBJECT::operator new (size_t size) {
	SVSUTIL_ASSERT (size == sizeof(RFCOMM_CONNECTION_OBJECT));

	return svsutil_GetFixed (gpTDIR->pfmdCO);
}

void RFCOMM_CONNECTION_OBJECT::operator delete (void *ptr) {
	svsutil_FreeFixed (ptr, gpTDIR->pfmdCO);
}

void *RFCOMM_SOCKET_OBJECT::operator new (size_t size) {
	SVSUTIL_ASSERT (size == sizeof(RFCOMM_SOCKET_OBJECT));

	return svsutil_GetFixed (gpTDIR->pfmdSO);
}

void RFCOMM_SOCKET_OBJECT::operator delete (void *ptr) {
	svsutil_FreeFixed (ptr, gpTDIR->pfmdSO);
}

void *RFCOMM_SEND_OBJECT::operator new (size_t size) {
	SVSUTIL_ASSERT (size == sizeof(RFCOMM_SEND_OBJECT));

	return svsutil_GetFixed (gpTDIR->pfmdSEND);
}

void RFCOMM_SEND_OBJECT::operator delete (void *ptr) {
	svsutil_FreeFixed (ptr, gpTDIR->pfmdSEND);
}

void *RFCOMM_RECEIVE_OBJECT::operator new (size_t size) {
	SVSUTIL_ASSERT (size == sizeof(RFCOMM_RECEIVE_OBJECT));

	return svsutil_GetFixed (gpTDIR->pfmdRECV);
}

void RFCOMM_RECEIVE_OBJECT::operator delete (void *ptr) {
	svsutil_FreeFixed (ptr, gpTDIR->pfmdRECV);
}

void *RFCOMM_PACKET_OBJECT::operator new (size_t size) {
	SVSUTIL_ASSERT (size == sizeof(RFCOMM_PACKET_OBJECT));

	return svsutil_GetFixed (gpTDIR->pfmdPL);
}

void RFCOMM_PACKET_OBJECT::operator delete (void *ptr) {
	svsutil_FreeFixed (ptr, gpTDIR->pfmdPL);
}

void *RFCOMM_PN_OBJECT::operator new (size_t size) {
	SVSUTIL_ASSERT (size == sizeof(RFCOMM_PN_OBJECT));

	return svsutil_GetFixed (gpTDIR->pfmdPN);
}

void RFCOMM_PN_OBJECT::operator delete (void *ptr) {
	svsutil_FreeFixed (ptr, gpTDIR->pfmdPN);
}

//
//	Console output
//
#if defined (BTH_CONSOLE)

int tdiR_ProcessConsoleCommand (WCHAR *pszCommand) {
	if (! gpTDIR) {
		DebugOut (DEBUG_OUTPUT, L"TDI help : service not initialized\n");
		return ERROR_SUCCESS;
	}

	gpTDIR->Lock ();
	int iRes = ERROR_SUCCESS;
	__try {
		if (wcsicmp (pszCommand, L"help") == 0) {
			DebugOut (DEBUG_OUTPUT, L"TDI Commands:\n");
			DebugOut (DEBUG_OUTPUT, L"    help        prints this text\n");
			DebugOut (DEBUG_OUTPUT, L"    global      dumps global state\n");
			DebugOut (DEBUG_OUTPUT, L"    conn        dumps currently installed connection objects\n");
			DebugOut (DEBUG_OUTPUT, L"    addr        dumps currently installed address objects\n");
			DebugOut (DEBUG_OUTPUT, L"    socket      dumps currently installed socket objects\n");
		} else if (wcsicmp (pszCommand, L"global") == 0) {
			DebugOut (DEBUG_OUTPUT, L"Global state : %s Initialized,  %s Connected\n", gpTDIR->fIsInitialized ? L"" : L"Not", gpTDIR->fIsConnected ? L"" : L"Not");
			DebugOut (DEBUG_OUTPUT, L"RFCOMM Handle   : 0x%08x\n", gpTDIR->hRFCOMM);
			DebugOut (DEBUG_OUTPUT, L"SDP Handle      : 0x%08x\n", gpTDIR->hSDP);
			DebugOut (DEBUG_OUTPUT, L"Device headers  : %d\n", gpTDIR->cDeviceHeader);
			DebugOut (DEBUG_OUTPUT, L"Device trailers : %d\n", gpTDIR->cDeviceTrailer);
			int iCount = 0;
			RFCOMM_ADDRESS_OBJECT *pAddr = gpTDIR->paolist;
			while (pAddr) {
				iCount++;
				pAddr = pAddr->pNext;
			}
			DebugOut (DEBUG_OUTPUT, L"Address objects : %d\n", iCount);
			iCount = 0;
			RFCOMM_CONNECTION_OBJECT *pConn = gpTDIR->pcolist;
			while (pConn) {
				iCount++;
				pConn = pConn->pNext;
			}
			DebugOut (DEBUG_OUTPUT, L"Connect objects : %d\n", iCount);
			iCount = 0;
			RFCOMM_SOCKET_OBJECT *pSock = gpTDIR->psolist;
			while (pSock) {
				++iCount;
				pSock = pSock->pNext;
			}
			DebugOut (DEBUG_OUTPUT, L"Socket objects  : %d\n", iCount);
			DebugOut (DEBUG_OUTPUT, L"Address descr   : 0x%08x\n", gpTDIR->pfmdAO);
			DebugOut (DEBUG_OUTPUT, L"Conn descr      : 0x%08x\n", gpTDIR->pfmdCO);
			DebugOut (DEBUG_OUTPUT, L"Socket descr    : 0x%08x\n", gpTDIR->pfmdSO);
			DebugOut (DEBUG_OUTPUT, L"Send descr      : 0x%08x\n", gpTDIR->pfmdSEND);
			DebugOut (DEBUG_OUTPUT, L"Recv descr      : 0x%08x\n", gpTDIR->pfmdRECV);
			DebugOut (DEBUG_OUTPUT, L"Packet descr    : 0x%08x\n", gpTDIR->pfmdPL);
		} else if (wcsicmp (pszCommand, L"conn") == 0) {
			RFCOMM_CONNECTION_OBJECT *pConn = gpTDIR->pcolist;
			while (pConn) {
				DebugOut (DEBUG_OUTPUT, L"Connection object :         0x%08x\n", pConn);
				DebugOut (DEBUG_OUTPUT, L"Connect ctx:                0x%08x\n", pConn->pConnectionContext);
				DebugOut (DEBUG_OUTPUT, L"Addr :                      0x%08x %04x%08x 0x%02x\n", pConn->pAddr,
					pConn->pAddr ? pConn->pAddr->b.NAP : 0, pConn->pAddr ? pConn->pAddr->b.SAP : 0,
					pConn->pAddr ? pConn->pAddr->ch : 0);
				DebugOut (DEBUG_OUTPUT, L"Stage :                     %s\n",
					(pConn->eStage == ALLOCATED ? L"ALLOCATED" : 
					(pConn->eStage == INITED ? L"INITED" : 
					(pConn->eStage == OPENING ? L"OPENING" : 
					(pConn->eStage == OPENED ? L"OPENED" : 
					(pConn->eStage == CLOSED ? L"CLOSED" : L"Error -- Undefined!\n"))))));

				DebugOut (DEBUG_OUTPUT, L"Connection handle :         0x%08x\n", pConn->hConnect);
				DebugOut (DEBUG_OUTPUT, L"MTU       :                 %d\n", pConn->mtu);
				DebugOut (DEBUG_OUTPUT, L"MaxMTU    :                 %d\n", pConn->imax_mtu);
				DebugOut (DEBUG_OUTPUT, L"MinMTU    :                 %d\n", pConn->imin_mtu);
				DebugOut (DEBUG_OUTPUT, L"xon_lim   :                 %d\n", pConn->xon_lim);
				DebugOut (DEBUG_OUTPUT, L"xoff_lim  :                 %d\n", pConn->xoff_lim);
				DebugOut (DEBUG_OUTPUT, L"imax_send :                 %d\n", pConn->imax_send);
				DebugOut (DEBUG_OUTPUT, L"imax_recv :                 %d\n", pConn->imax_recv);
				DebugOut (DEBUG_OUTPUT, L"isent :                     %d\n", pConn->isent);
				DebugOut (DEBUG_OUTPUT, L"irecvd :                    %d\n", pConn->irecvd);
				DebugOut (DEBUG_OUTPUT, L"v24 :                       0x%02x\n", pConn->v24in);
				DebugOut (DEBUG_OUTPUT, L"br :                        0x%02x\n", pConn->brin);
				DebugOut (DEBUG_OUTPUT, L"rls :                       0x%02x\n", pConn->rlsin);
				DebugOut (DEBUG_OUTPUT, L"Connect complete evt:       0x%08x\n", pConn->pConnectCompleteEvent);
				DebugOut (DEBUG_OUTPUT, L"Connect complete ctx:       0x%08x\n", pConn->pConnectCompleteContext);
				DebugOut (DEBUG_OUTPUT, L"Local FC :                  %s\n", pConn->fc_local ? L"yes" : L"no");
				DebugOut (DEBUG_OUTPUT, L"Remote FC :                 %s\n", pConn->fc_remote ? L"yes" : L"no");
				DebugOut (DEBUG_OUTPUT, L"Aggregate :                 %s\n", pConn->fc_aggregate ? L"yes" : L"no");
				DebugOut (DEBUG_OUTPUT, L"Data Indicated :            %d\n", pConn->cbDataIndicated);
				DebugOut (DEBUG_OUTPUT, L"Send List:\n");

				RFCOMM_SEND_OBJECT			*pSendList = pConn->pSendList;
				while (pSendList) {
					DebugOut (DEBUG_OUTPUT, L"    NDIS 0x%08x size %d cfm %d used %d callback %d ctx %d\n", pSendList->pNdisBuffer, QueryNdisSize (pSendList->pNdisBuffer), pSendList->cBytesConfirmed, pSendList->cBytesUsed, pSendList->pSendCallback, pSendList->pSendContext);
					pSendList = pSendList->pNext;
				}

				DebugOut (DEBUG_OUTPUT, L"Packets:\n");
				RFCOMM_PACKET_OBJECT		*pPacketList = pConn->pPacketList;
				while (pPacketList) {
					DebugOut (DEBUG_OUTPUT, L"Pkt 0x%08x bytes %d\n", pPacketList, pPacketList->cBytesSent);
					pPacketList = pPacketList->pNext;
				}

				DebugOut (DEBUG_OUTPUT, L"Recv blocks:\n");
				RFCOMM_RECEIVE_OBJECT		*pRecvList = pConn->pRecvList;
				while (pRecvList) {
					if (pRecvList->pNdisBuffer) {
						DWORD dwPerms = SetProcPermissions (pRecvList->dwPerms);
						BOOL  bkm = SetKMode (TRUE);

						int iSize = QueryNdisSize (pRecvList->pNdisBuffer);

						SetKMode (bkm);
						SetProcPermissions (pRecvList->dwPerms);

						DebugOut (DEBUG_OUTPUT, L"    RECV 0x%08x NDIS 0x%08x Size %d Used %d PERMS 0x%08x Ctx 0x%08x cb 0x%08x\n",
							pRecvList, pRecvList->pNdisBuffer, iSize, pRecvList->iNdisUsed, pRecvList->dwPerms, pRecvList->pRecvContext, pRecvList->pRecvEvent);
					} else
						DebugOut (DEBUG_OUTPUT, L"    RECV 0x%08x BUFF 0x%08x bytes %d size %d head %d tail %d\n",
							pRecvList, pRecvList->pBuffer, BufferTotal (pRecvList->pBuffer), pRecvList->pBuffer->cSize,
							pRecvList->pBuffer->cStart, pRecvList->pBuffer->cEnd);

					pRecvList = pRecvList->pNext;
				}

				pConn = pConn->pNext;
			}

		} else if (wcsicmp (pszCommand, L"addr") == 0) {
			RFCOMM_ADDRESS_OBJECT *pAddr = gpTDIR->paolist;
			while (pAddr) {
				DebugOut (DEBUG_OUTPUT, L"Address   : 0x%08x\n", pAddr);
				DebugOut (DEBUG_OUTPUT, L"BD_ADDR   : %04x%08x\n", pAddr->b.NAP, pAddr->b.SAP);
				DebugOut (DEBUG_OUTPUT, L"Channel   : 0x%02x\n", pAddr->ch);
				DebugOut (DEBUG_OUTPUT, L"Listening : %s\n", pAddr->fListenOn ? L"yes" : L"no");
				DebugOut (DEBUG_OUTPUT, L"MTU       : %d\n", pAddr->mtu);
				DebugOut (DEBUG_OUTPUT, L"MaxMTU    : %d\n", pAddr->imax_mtu);
				DebugOut (DEBUG_OUTPUT, L"MinMTU    : %d\n", pAddr->imin_mtu);
				DebugOut (DEBUG_OUTPUT, L"xon_lim   : %d\n", pAddr->xon_lim);
				DebugOut (DEBUG_OUTPUT, L"xoff_lim  : %d\n", pAddr->xoff_lim);
				DebugOut (DEBUG_OUTPUT, L"imax_send : %d\n", pAddr->imax_send);
				DebugOut (DEBUG_OUTPUT, L"imax_recv : %d\n", pAddr->imax_recv);
				DebugOut (DEBUG_OUTPUT, L"pEventConnect            : 0x%08x\n", pAddr->pEventConnect);
				DebugOut (DEBUG_OUTPUT, L"pEventConnectContext     : 0x%08x\n", pAddr->pEventConnectContext);
				DebugOut (DEBUG_OUTPUT, L"pEventDisconnect         : 0x%08x\n", pAddr->pEventDisconnect);
				DebugOut (DEBUG_OUTPUT, L"pEventDisconnectContext  : 0x%08x\n", pAddr->pEventDisconnectContext);
				DebugOut (DEBUG_OUTPUT, L"pEventError              : 0x%08x\n", pAddr->pEventError);
				DebugOut (DEBUG_OUTPUT, L"pEventErrorContext       : 0x%08x\n", pAddr->pEventErrorContext);
				DebugOut (DEBUG_OUTPUT, L"pEventReceive            : 0x%08x\n", pAddr->pEventReceive);
				DebugOut (DEBUG_OUTPUT, L"pEventReceiveContext     : 0x%08x\n", pAddr->pEventReceiveContext);
				DebugOut (DEBUG_OUTPUT, L"pEventReceiveDgm         : 0x%08x\n", pAddr->pEventReceiveDatagram);
				DebugOut (DEBUG_OUTPUT, L"pEventReceiveDgmContext  : 0x%08x\n", pAddr->pEventReceiveDatagramContext);
				DebugOut (DEBUG_OUTPUT, L"pEventReceiveExp         : 0x%08x\n", pAddr->pEventReceiveExpedited);
				DebugOut (DEBUG_OUTPUT, L"pEventReceiveExpContext  : 0x%08x\n", pAddr->pEventReceiveExpeditedContext);

				DebugOut (DEBUG_OUTPUT, L"\n\n");
				pAddr = pAddr->pNext;
			}
		} else if (wcsicmp (pszCommand, L"socket") == 0) {
			RFCOMM_SOCKET_OBJECT *pSock = gpTDIR->psolist;
			while (pSock) {
				DebugOut (DEBUG_OUTPUT, L"Socket 0x%08x Addr 0x%08x\n", pSock, pSock->pao);
				pSock = pSock->pNext;
			}
		} else
			iRes = ERROR_UNKNOWN_FEATURE;
	} __except (1) {
		DebugOut (DEBUG_ERROR, L"Exception in TDI dump!\n");
	}

	gpTDIR->Unlock ();

	return iRes;
}

#endif // BTH_CONSOLE

#else
#include <windows.h>

extern "C" void
Register
(
void *pVoid1,
void *pVoid2
){
}

int tdiR_InitializeOnce (void) {
	return TRUE;
}

int tdiR_UninitializeOnce (void) {
	return TRUE;
}

int tdiR_CreateDriverInstance (void) {
	return ERROR_SUCCESS;
}

int tdiR_CloseDriverInstance (void) {
	return ERROR_SUCCESS;
}

int tdiR_ProcessConsoleCommand (WCHAR *pszCommand) { return 0; }

#endif
