//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//------------------------------------------------------------------------------
// 
//      Bluetooth SDP Layer
// 
// 
// Module Name:
// 
//      sdpbase.cxx
// 
// Abstract:
// 
//      This file implements SDP layer.
// 
// 
//------------------------------------------------------------------------------


#include "common.h"
#include <svsutil.hxx>
#include <bt_debug.h>
#include <intsafe.h>


#define CALL_SDP_SERVICE_SEARCH             0x0001
#define CALL_SDP_ATTRIBUTE_SEARCH           0x0002
#define CALL_SDP_SERVICE_ATTRIBUTE_SEARCH   0x0004
#define CALL_SDP_ACCEPT                     0x0008
#define CALL_SDP_CONNECT                    0x0010
#define CALL_SDP_DISCONNECT                 0x0020
#define CALL_SDP_SERVER_WORKER              0x0040

#define CALL_SDP_SEARCH                    (CALL_SDP_SERVICE_SEARCH | CALL_SDP_ATTRIBUTE_SEARCH | CALL_SDP_SERVICE_ATTRIBUTE_SEARCH)
#define CALL_SDP_ACCEPT_CONNECT            (CALL_SDP_ACCEPT | CALL_SDP_CONNECT)
#define CALL_SDP_ACCEPT_SEARCH             (CALL_SDP_SEARCH | CALL_SDP_ACCEPT)
#define CALL_SDP_SEARCH_WORKER             (CALL_SDP_SEARCH | CALL_SDP_SERVER_WORKER)

#define    NONE             0x00
#define CONNECTED           0x01
#define CONFIG_REQ_DONE     0x02
#define CONFIG_IND_DONE     0x04
#define UP                  0x07
#define LINK_ERROR          0x80

#define DEFAULT_IN_MTU_SIZE 0x1000
#define MAX_MTU_SIZE        0xFFFF

#define SDP_SCALE           10

enum SDP_STAGE {
    JustCreated             = 0,
    Initializing,
    Connected,
    Disconnected,
    ShuttingDown,
    Error
};

#if 0
// Currently no registry settings are available for SDP.
#define SDP_DEFAULT_LINK_TIMEOUT_MS     10000

static const WCHAR g_SdpBaseRegKey[] = L"software\\Microsoft\\bluetooth\\l2cap";
static const WCHAR g_SdpLinkIdle[]   = L"SdpLinkIdle";
#endif

struct SDP_CONTEXT : public SVSAllocClass, public SVSRefObj {
    SDP_CONTEXT             *pNext;
    SDP_CALLBACKS           c;
    SDP_EVENT_INDICATION    ei;

    void *pUserContext;

    SDP_CONTEXT(void)  {
        memset(&ei, 0, sizeof(ei));
        memset(&c, 0, sizeof(c));
        pNext = NULL;
    }
};

struct Call;
struct CallQueue { 
    struct CallQueue  *pNext;
    Call              *pCall;
};

struct Link : public SVSAllocClass, public SVSRefObj {
    Link            *pNext;

    BD_ADDR         b;
    unsigned short  psm;
    unsigned short  cid;

    unsigned int    fStage;

    unsigned int    fIncoming : 1;
    unsigned int    fCallCanceled : 1;

    unsigned short  inMTU;
    unsigned short  outMTU;

    CallQueue       *pCallQueue;
};

struct Call : public SVSAllocClass {
    Call            *pNext;
    Link            *pLink;

    unsigned int    fWhat            : 8;

    SDP_CONTEXT     *pOwner;
    void            *pCallContext;
    SdpConnection   *pSdpConnection;

    SVSHandle       hCallContext;    // Unique handle for async call lookup
};

static int sdp_ConfigInd(void *pUserContext, unsigned char id, unsigned short cid, unsigned short usOutMTU, unsigned short usInFlushTO, struct btFLOWSPEC *pInFlow, int cOptNum, struct btCONFIGEXTENSION **ppExtendedOptions);
static int sdp_ConnectInd(void *pUserContext, BD_ADDR *pba, unsigned short cid, unsigned char id, unsigned short psm);
static int sdp_DataUpInd(void *pUserContext, unsigned short cid, BD_BUFFER *pBuffer);
static int sdp_DisconnectInd(void *pUserContext, unsigned short cid, int iErr);
static int sdp_lStackEvent(void *pUserContext, int iEvent, void *pEventContext);

static int sdp_lCallAborted(void *pCallContext, int iError);
static int sdp_ConfigReq_Out(void *pCallContext, unsigned short usResult, unsigned short usInMTU, unsigned short usOutFlushTO, struct btFLOWSPEC *pOutFlow, int cOptNum, struct btCONFIGEXTENSION **ppExtendedOptions);
static int sdp_ConfigResponse_Out(void *pCallContext, unsigned short result);
static int sdp_ConnectReq_Out(void *pCallContext, unsigned short cid, unsigned short result, unsigned short status);
static int sdp_ConnectResponse_Out(void *pCallContext, unsigned short result);
static int sdp_DataDown_Out(void *pCallContext, unsigned short result);
static int sdp_Disconnect_Out(void *pCallContext, unsigned short result);

static int sdp_InquiryComplete(void *pUserContext, void *pCallContext, unsigned char status, unsigned char num_responses);
static int sdp_InquiryResult(void *pUserContext, void *pCallContext, BD_ADDR *pba, unsigned char page_scan_repetition_mode_list, unsigned char page_scan_period_mode, unsigned char page_scan_mode, unsigned int class_of_device, unsigned short clock_offset);
static int sdp_RemoteNameDone(void *pUserContext, void *pCallContext, unsigned char status, BD_ADDR *pba, unsigned char utf_name[248]);
static int sdp_hStackEvent(void *pUserContext, int iEvent, void *pEventContext);

static int sdp_hCallAborted(void *pCallContext, int iError);
static int sdp_LocalNameDone(void *pCallContext, unsigned char status);
static int sdp_Inquiry_Out(void *pCallContext, unsigned char status);
static int sdp_BDADDR_Out(void *pCallContext, unsigned char status, BD_ADDR *pba);
static int sdp_RemoteName_Out(void *pCallContext, unsigned char status);
static int sdp_WriteScan_Out(void *pCallContext, unsigned char status);

static int SdpDisconnect(Link *pLink, Call *pCall);
static void DeleteCall (Call *pCall);

static int MapStatusToErrorCode(int status);

class SDP : public SVSSynch, public SVSRefObj {
public:
    Link            *pLinks;
    Call            *pCalls;
    SDP_CONTEXT     *pContexts;
    
    HANDLE          hL2CAP;
    L2CAP_INTERFACE l2cap_if;

    FixedMemDescr   *pfmdLinks;
    FixedMemDescr   *pfmdCalls;
    FixedMemDescr   *pfmdSdpConnections;
    FixedMemDescr   *pfmdCallQueue;
    FixedMemDescr   *pfmdBuffers;

//    DWORD           dwSdpIdle;

    SDP_STAGE       eStage;
    BD_ADDR         b;

    int             cDataHeaders;
    int             cDataTrailers;

    void ReInit(void)  {
        pLinks       = NULL;
        pCalls       = NULL;
        pContexts    = NULL;

        eStage       = JustCreated;

        hL2CAP       = NULL;
        memset(&l2cap_if, 0, sizeof(l2cap_if));

        pfmdLinks          = NULL;
        pfmdCalls          = NULL;
        pfmdSdpConnections = NULL;
        pfmdCallQueue      = NULL;
        pfmdBuffers        = NULL;

        memset (&b, 0, sizeof(b));
        cDataHeaders = cDataTrailers = 0;
    }

    SDP(void) {
        ReInit();
    }

    int IsStackRunning (void) {
        return (eStage == Connected) || (eStage == Disconnected);
    }
};

SDP           *gpSDP           = NULL;
SdpDatabase   *pSdpDB          = NULL;
unsigned char *gpSdpReadBuffer = NULL;



//******************************************************************
// Link/call/notification helper functions.
//******************************************************************

static SDP_CONTEXT * VerifyContext(SDP_CONTEXT *pContext) {
    SVSUTIL_ASSERT(gpSDP->IsLocked());

    SDP_CONTEXT *pTrav = gpSDP->pContexts;
    while (pTrav &&(pTrav != pContext))
        pTrav = pTrav->pNext;

    return pTrav;
}

static SDP *CreateNewSDP(void) {
    return new SDP;
}

static SdpDatabase *CreateNewSdpDatabase(void) {
    return new SdpDatabase;
}

static Call *VerifyCall(Call *pCall, unsigned char fWhat=0) {
    Call *p = gpSDP->pCalls;
    while (p && (p != pCall))
        p = p->pNext;

    // Make sure call type is appropriate for given context.
    if (p && fWhat && ! (p->fWhat & fWhat))  {
        SVSUTIL_ASSERT(0);
        return NULL;
    }

    return p;
}

static Link *VerifyLink(Link *pLink) {
    Link *p = gpSDP->pLinks;
    while (p && (p != pLink))
        p = p->pNext;

    return p;
}

static Call *AllocCall(int fWhat, Link *pLink, SDP_CONTEXT *pOwner, void *pCallContext) {
    Call *pCall = (Call *)svsutil_GetFixed(gpSDP->pfmdCalls);
    if (!pCall)
        return NULL;

    memset(pCall, 0, sizeof(*pCall));

    pCall->hCallContext = btutil_AllocHandle((LPVOID)pCall);
    if (SVSUTIL_HANDLE_INVALID == pCall->hCallContext) {
        svsutil_FreeFixed(pCall, gpSDP->pfmdCalls);
        return NULL;
    }

    pCall->pNext = gpSDP->pCalls;
    gpSDP->pCalls = pCall;

    pCall->pLink         = pLink;
    pCall->fWhat         = fWhat;
    pCall->pOwner        = pOwner;
    pCall->pCallContext  = pCallContext;

    return pCall;
}

static Link* AllocLink(void)  {
    Link *pLink = (Link *)svsutil_GetFixed(gpSDP->pfmdLinks);
    if (!pLink)
        return NULL;

    memset(pLink, 0, sizeof(*pLink));

    pLink->AddRef();
    pLink->psm = PSM_SDP;
    return pLink;
}

static SdpConnection* AllocSdpConnection(Call *pCall)  {
    SdpConnection *pSdpConn = (SdpConnection*) svsutil_GetFixed(gpSDP->pfmdSdpConnections);
    if (!pSdpConn)
        return NULL;

    memset(pSdpConn ,0,sizeof(*pSdpConn));
    pSdpConn->pCallContext = (void *) pCall;
    if (pCall)  {
        pCall->pSdpConnection = pSdpConn;
        pCall->pSdpConnection->outMtu = pCall->pLink->outMTU;
        pCall->pSdpConnection->inMtu  = pCall->pLink->inMTU;
    }
    return pSdpConn;
}

static CallQueue *AddItemToCallQueue(Link *pLink, Call *pCall)  {
    CallQueue *pNew  = (CallQueue *) svsutil_GetFixed(gpSDP->pfmdCallQueue);
    if (!pNew)
        return NULL;

    pNew->pNext = NULL;
    pNew->pCall = pCall;

    if (!pLink->pCallQueue)
        pLink->pCallQueue = pNew;
    else {
        CallQueue *pTrav = pLink->pCallQueue;
        while (pTrav->pNext)
            pTrav = pTrav->pNext;

        pTrav->pNext = pNew;
    }

    return pNew;
}

// need a connection struct for local requests, alloc once when first needed.
static SdpConnection *gpSpdConnectionLocal = NULL;

static SdpConnection* GetLocalSDPConn() {
    if (gpSpdConnectionLocal) {
        memset((void*)gpSpdConnectionLocal,0,sizeof(*gpSpdConnectionLocal));
        return gpSpdConnectionLocal;
    }

    gpSpdConnectionLocal = AllocSdpConnection(NULL);
    return gpSpdConnectionLocal;
}

static void RemoveCallFromSendQueue(Link *pLink, Call *pCall)  {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"RemoveCallFromSendQueue (0x%08x)\r\n",pLink));

    SVSUTIL_ASSERT(gpSDP->IsLocked());
    SVSUTIL_ASSERT(VerifyLink(pLink));

    CallQueue *pTrav   = pLink->pCallQueue;
    CallQueue *pFollow = NULL;
    
    while (pTrav && (pTrav->pCall != pCall)) { 
        pFollow = pTrav;
        pTrav =   pTrav->pNext;
    }
    if (!pTrav)
        return;

    if (pFollow)
        pFollow->pNext = pTrav->pNext;
    else  {
        SVSUTIL_ASSERT(pLink->pCallQueue->pCall == pCall);
        pLink->pCallQueue = pTrav->pNext;
    }

    svsutil_FreeFixed(pTrav, gpSDP->pfmdCallQueue);
}

void NotifyConnect(SDP_CONTEXT *pOwner, void *pContext, unsigned long status, unsigned short cid)  {
    SVSUTIL_ASSERT (VerifyContext(pOwner));

    SDP_Connect_Out pCallback = pOwner->c.sdp_Connect_Out;
    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"About to enter sdp_Connect_Out, pContext=0x%08x,status=0x%08x, cid=0x%04x\r\n",pContext,status,cid));

    pOwner->AddRef();
    gpSDP->Unlock();
    __try {
        pCallback(pContext,status,cid);
    }
    __except (1) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_Connect_Out excepted, exception code = 0x%08x\r\n",GetExceptionCode()));
    }
    gpSDP->Lock();
    pOwner->DelRef();
    
    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Finished with sdp_Connect_Out\r\n"));
}

void NotifyDisconnect(SDP_CONTEXT *pOwner, void *pContext, unsigned long status) {
    SVSUTIL_ASSERT (VerifyContext(pOwner));

    SDP_Disonnect_Out pCallback = pOwner->c.sdp_Disconnect_Out;

    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"About to enter sdp_Disconnect_Out, pContext=0x%08x, status=0x%08x\r\n",pContext,status));

    pOwner->AddRef();
    gpSDP->Unlock();
    __try {
        pCallback(pContext,status);
    }
    __except (1) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_Disconnect_Out excepted, exception code = 0x%08x\r\n",GetExceptionCode()));
    }
    gpSDP->Lock();
    pOwner->DelRef();

    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Finished with sdp_Disconnect_Out\r\n"));
}

void NotifyServiceSearch(SDP_CONTEXT *pOwner, void *pContext, unsigned long status, unsigned short cReturnedHandles, unsigned char *pClientBuf)  {
    SVSUTIL_ASSERT (VerifyContext(pOwner));
    SDP_ServiceSearch_Out pCallback = pOwner->c.sdp_ServiceSearch_Out;
    unsigned long *pHandles = NULL;

    if (status == ERROR_SUCCESS)
        pHandles = (unsigned long *) pClientBuf;

    pOwner->AddRef();
    gpSDP->Unlock();
    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"About to enter sdp_ServiceSearch_Out, pContext=0x%08x\r\n",pContext));
    __try {
        pCallback(pContext,status,cReturnedHandles,pHandles);
    }
    __except (1) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceSearch_Out excepted, exception code = 0x%08x\r\n",GetExceptionCode()));
    }
    gpSDP->Lock();
    pOwner->DelRef();
    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Finished with sdp_ServiceSearch_Out\r\n"));
}

void NotifyAttributeSearch(SDP_CONTEXT *pOwner, void *pContext, unsigned long status, unsigned char *pClientBuf, unsigned long ulClientBuf)  {
    SVSUTIL_ASSERT (VerifyContext(pOwner));
    SDP_AttributeSearch_Out pCallback = pOwner->c.sdp_AttributeSearch_Out;

    if (status != ERROR_SUCCESS) {
        pClientBuf  = NULL;
        ulClientBuf = 0;
    }

    pOwner->AddRef();
    gpSDP->Unlock();
    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"About to enter sdp_AttributeSearch_Out, pContext=0x%08x\r\n",pContext));
    __try {
        pCallback(pContext,status,pClientBuf,ulClientBuf);
    }
    __except (1) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_AttributeSearch_Out excepted, exception code = 0x%08x\r\n",GetExceptionCode()));
    }
    gpSDP->Lock();
    pOwner->DelRef();
    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Finished with sdp_AttributeSearch_Out\r\n"));
}

void NotifyServiceAttributeSearch(SDP_CONTEXT *pOwner, void *pContext, unsigned long status, unsigned char *pClientBuf, unsigned long ulClientBuf)  {
    SVSUTIL_ASSERT (VerifyContext(pOwner));
    SDP_ServiceAttributeSearch_Out pCallback = pOwner->c.sdp_ServiceAttributeSearch_Out;

    if (status != ERROR_SUCCESS) {
        pClientBuf  = NULL;
        ulClientBuf = 0;
    }

    pOwner->AddRef();
    gpSDP->Unlock();
    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"About to enter sdp_ServiceAttributeSearch_Out, pContext=0x%08x\r\n",pContext));
    __try {
        pCallback(pContext,status,pClientBuf,ulClientBuf);
    }
    __except (1) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceAttributeSearch_Out excepted, exception code = 0x%08x\r\n",GetExceptionCode()));
    }
    gpSDP->Lock();
    pOwner->DelRef();
    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Finished with sdp_ServiceAttributeSearch_Out\r\n"));
}

void NotifySdpClientOfCompletion(Call *pCall, unsigned long status)   {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"NotifySdpClientOfCompletion(0x%08x,0x%08x)\r\n",pCall,status));

    SVSUTIL_ASSERT(gpSDP->IsLocked());
    SVSUTIL_ASSERT(VerifyCall(pCall));
    void           *pContext = pCall->pCallContext;
    SDP_CONTEXT    *pOwner   = pCall->pOwner;

    unsigned char *pClientBuf       = (pCall->pSdpConnection  && status == ERROR_SUCCESS) ? pCall->pSdpConnection->pClientBuf : NULL;
    unsigned long  cClientBuf       = (pCall->pSdpConnection  && status == ERROR_SUCCESS) ? pCall->pSdpConnection->cClientBuf : 0;
    unsigned short cReturnedHandles = (pCall->pSdpConnection  && status == ERROR_SUCCESS) ? pCall->pSdpConnection->u.Client.ServiceSearch.totalRecordCount : 0;
    unsigned short cid              = (pCall->pLink           && status == ERROR_SUCCESS) ? pCall->pLink->cid : 0;
    unsigned int   fWhat            = pCall->fWhat;

#if defined (DEBUG) || defined (_DEBUG)
    if ((status == ERROR_SUCCESS) && (fWhat == CALL_SDP_CONNECT))
        SVSUTIL_ASSERT( VerifyLink(pCall->pLink));

    SVSUTIL_ASSERT(!pCall->pSdpConnection || (pCall->pSdpConnection != gpSpdConnectionLocal && !pCall->pSdpConnection->fClientNotified));
    if (pCall->pSdpConnection)
        pCall->pSdpConnection->fClientNotified = TRUE;
#endif

    DeleteCall(pCall);

    switch (fWhat) {
        case CALL_SDP_ACCEPT:
        case CALL_SDP_SERVER_WORKER:
            break;  // no-op, call was generated internally.

        case CALL_SDP_CONNECT:
            NotifyConnect(pOwner,pContext,status,cid);
        break;

        case CALL_SDP_DISCONNECT:
            NotifyDisconnect(pOwner,pContext,status);
        break;
        
        case CALL_SDP_SERVICE_SEARCH:
            NotifyServiceSearch(pOwner,pContext,status,cReturnedHandles,pClientBuf);
        break;

        case CALL_SDP_ATTRIBUTE_SEARCH:
            NotifyAttributeSearch(pOwner,pContext,status,pClientBuf,cClientBuf);
        break;

        case CALL_SDP_SERVICE_ATTRIBUTE_SEARCH:
            NotifyServiceAttributeSearch(pOwner,pContext,status,pClientBuf,cClientBuf);
        break;
        
        default:
            SVSUTIL_ASSERT(0);
    }
}

static void ResetSdpConnection(SdpConnection *pSdpConn)  {
    if (pSdpConn == gpSpdConnectionLocal || pSdpConn->IsClient())  {
        pSdpConn->CompleteClientRequest(0,ERROR_SUCCESS);
    }
    else {
        pSdpConn->CleanUpServerSearchResults();
    }

    pSdpConn->ConnInformation = 0;
    pSdpConn->pClientBuf      = NULL;
    pSdpConn->cClientBuf      = 0;

#if defined (DEBUG) || defined (_DEBUG)
    // If this ASSERT is hit it means we didn't notify client of completion, 
    // meaning that there's a hanging call and that we'll end up leaking the memory.
    if ((pSdpConn != gpSpdConnectionLocal) && pSdpConn->IsClient() && (!pSdpConn->fClientNotified))
        SVSUTIL_ASSERT(0);

    pSdpConn->fClientNotified = FALSE;
#endif
}

static void DeleteSdpConnection(SdpConnection *pSdpConn)  {
    ResetSdpConnection(pSdpConn);
    svsutil_FreeFixed(pSdpConn, gpSDP->pfmdSdpConnections);
}

void RemoveAllPendingCallsFromQueue(Link *pLink, unsigned long status);

static void RemoveAllCallsByLinkAndNotify(Link* pLink, Call* pCallIgnore, int iError)
{
    SVSUTIL_ASSERT(gpSDP->IsLocked());
    
    Call *pTrav = gpSDP->pCalls;
    while (pTrav)  {
        // Remove calls from the list unless it is pCallIgnore (if specified) and
        // it is pointing to the specified link.        
        if ((!pCallIgnore || (pTrav != pCallIgnore)) && (pTrav->pLink == pLink))  {
            NotifySdpClientOfCompletion(pTrav, iError);
            // Need to start at the beginning of the list since we left the lock
            // and may have deleted a call from the list.
            pTrav = gpSDP->pCalls;
            continue;
        }
        pTrav = pTrav->pNext;
    }
}

// This call just frees up link and removes it from list, assumes upper layer has already disconnected.
static void DeleteLink(Link *pLink) {
    SVSUTIL_ASSERT(gpSDP->IsLocked());
    RemoveAllPendingCallsFromQueue(pLink,ERROR_CONNECTION_UNAVAIL);

    // Possible another thread has deleted link in interim.
    if (! VerifyLink(pLink))
        return;

    if (pLink == gpSDP->pLinks)
        gpSDP->pLinks = pLink->pNext;
    else {
        Link *pParent = gpSDP->pLinks;
        while (pParent && (pParent->pNext != pLink))
            pParent = pParent->pNext;

        if (pParent)
            pParent->pNext = pLink->pNext;
    }

    svsutil_FreeFixed(pLink, gpSDP->pfmdLinks);

    RemoveAllCallsByLinkAndNotify(pLink, NULL, ERROR_CONNECTION_UNAVAIL);   
}

static void DeleteCall (Call *pCall) {
    SVSUTIL_ASSERT(gpSDP->IsLocked());
    SVSUTIL_ASSERT(VerifyCall(pCall));
    IFDBG(DebugOut (DEBUG_SDP_TRACE,L"DeleteCall (0x%08x)\r\n",pCall));

    unsigned int fWhat  = pCall->fWhat;
    Link         *pLink = pCall->pLink;

    if (pCall->fWhat & CALL_SDP_SEARCH_WORKER && VerifyLink(pCall->pLink))
        RemoveCallFromSendQueue(pLink,pCall);

    if (pCall == gpSDP->pCalls)
        gpSDP->pCalls = pCall->pNext;
    else {
        Call *pParent = gpSDP->pCalls;
        while (pParent && (pParent->pNext != pCall))
            pParent = pParent->pNext;

        if (pParent)
            pParent->pNext = pCall->pNext;
    }

    if (pCall->pSdpConnection)
        DeleteSdpConnection(pCall->pSdpConnection);

    if (pCall->hCallContext != SVSUTIL_HANDLE_INVALID) {
        btutil_CloseHandle (pCall->hCallContext);
        pCall->hCallContext = SVSUTIL_HANDLE_INVALID;
    }

    svsutil_FreeFixed(pCall, gpSDP->pfmdCalls);
}

void RemoveAllPendingCallsFromQueue(Link *pLink, unsigned long status=0)  {
    while (VerifyLink(pLink) && pLink->pCallQueue) {
        SVSUTIL_ASSERT(pLink->pCallQueue->pCall->fWhat & CALL_SDP_SEARCH_WORKER);
        NotifySdpClientOfCompletion(pLink->pCallQueue->pCall,status);
    }
}

static Link *FindLink (unsigned short cid) {
    Link *p = gpSDP->pLinks;
    while (p && (p->cid != cid))
        p = p->pNext;
    return p;
}

static Link *FindLink(BD_ADDR *pba, BOOL fClient)  {
    Link *pTrav = gpSDP->pLinks;
    while (pTrav)   {
        if (pTrav->b == *pba)  {
            if ( (fClient && !pTrav->fIncoming) || (!fClient && pTrav->fIncoming))
                return pTrav;
        }
        pTrav = pTrav->pNext;
    }
    return pTrav;
}

static Call *FindCall(Link *pLink, unsigned long fWhat=0xFFFFFFFF)  {
    Call *pTrav;

    for (pTrav = gpSDP->pCalls; pTrav; pTrav = pTrav->pNext)  {
        if ((pTrav->pLink == pLink) && (pTrav->fWhat & fWhat))
            return pTrav;
    }
    return NULL;
}

#if defined (DEBUG)    || defined (_DEBUG)
static Call *FindAcceptConnectCall(Link *pLink)  {
    Call *pTrav;

    for (pTrav = gpSDP->pCalls; pTrav; pTrav = pTrav->pNext)  {
        if ((pTrav->pLink == pLink) && (pTrav->fWhat & CALL_SDP_ACCEPT_CONNECT))
            return pTrav;
    }
    return NULL;
}
#endif // DEBUG


static void CleanupSDPState(void) {
    if (gpSDP->pfmdSdpConnections)
        svsutil_ReleaseFixedNonEmpty(gpSDP->pfmdSdpConnections);
    if (gpSDP->pfmdLinks)
        svsutil_ReleaseFixedNonEmpty(gpSDP->pfmdLinks);
    if (gpSDP->pfmdCalls)
        svsutil_ReleaseFixedNonEmpty(gpSDP->pfmdCalls);
    if (gpSDP->pfmdCallQueue)
        svsutil_ReleaseFixedNonEmpty(gpSDP->pfmdCallQueue);
    if (gpSDP->pfmdBuffers)
        svsutil_ReleaseFixedNonEmpty(gpSDP->pfmdBuffers);

    gpSDP->ReInit();
}


static void GetConnectionState (void) {
    SVSUTIL_ASSERT(gpSDP->eStage == Connected || gpSDP->eStage == Disconnected);

    __try {
        int fConnected = FALSE;
        int dwRet = 0;
        gpSDP->l2cap_if.l2ca_ioctl (gpSDP->hL2CAP, BTH_STACK_IOCTL_GET_CONNECTED, 0, NULL, sizeof(fConnected), (char *)&fConnected, &dwRet);
        if ((dwRet == sizeof(fConnected)) && fConnected)
            gpSDP->eStage = Connected;
    } __except (1) {
        IFDBG(DebugOut (DEBUG_ERROR, L"[SDP]: exception in hci_ioctl BTH_STACK_IOCTL_GET_CONNECTED\n"));
    }
}


static void DispatchStackEvent (int iEvent) {
    SDP_CONTEXT *pContext = gpSDP->pContexts;
    while (pContext && gpSDP->IsStackRunning()) {
        BT_LAYER_STACK_EVENT_IND pCallback = pContext->ei.sdp_StackEvent;
        if (pCallback) {
            void *pUserContext = pContext->pUserContext;

            IFDBG(DebugOut (DEBUG_SDP_CALLBACK, L"Going into StackEvent notification\n"));
            pContext->AddRef ();
            gpSDP->Unlock ();

            __try {
                pCallback (pUserContext, iEvent, NULL);
            } __except (1) {
                IFDBG(DebugOut (DEBUG_ERROR, L"[SDP] SDP_connect_transport: exception in SDP_StackEvent!\n"));
            }

            gpSDP->Lock ();
            pContext->DelRef ();
            IFDBG(DebugOut (DEBUG_SDP_CALLBACK, L"SDP: Came back StackEvent notification\n"));
        }
        pContext = pContext->pNext;
    }
}

static DWORD WINAPI StackDisconnect(LPVOID lpVoid) {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"SDP: StackDisconnect()\r\n"));
    sdp_CloseDriverInstance();
    return ERROR_SUCCESS;
}

static DWORD WINAPI StackDown (LPVOID pArg) {
    IFDBG(DebugOut (DEBUG_SDP_TRACE, L"SDP: Disconnect stack\n"));

    for ( ; ; ) {
        if (! gpSDP) {
            IFDBG(DebugOut (DEBUG_ERROR, L"SDP: StackDown : ERROR_SERVICE_DOES_NOT_EXIST\n"));
            return ERROR_SERVICE_DOES_NOT_EXIST;
        }
        gpSDP->Lock ();

        if (gpSDP->eStage != Connected) {
            IFDBG(DebugOut (DEBUG_ERROR, L"SDP: StackDown : ERROR_SERVICE_NOT_ACTIVE\n"));
            gpSDP->Unlock ();
            return ERROR_SERVICE_NOT_ACTIVE;
        }

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

        IFDBG(DebugOut (DEBUG_SDP_TRACE, L"SDP: Waiting for ref count in StackDown\n"));
        gpSDP->Unlock ();
        Sleep (100);
    }
    SVSUTIL_ASSERT(gpSDP->IsLocked());  // break out of above for loop with lock held.

    gpSDP->AddRef ();
    gpSDP->eStage = Disconnected;

    while (gpSDP->pCalls && gpSDP->IsStackRunning())
        NotifySdpClientOfCompletion (gpSDP->pCalls, ERROR_SHUTDOWN_IN_PROGRESS);

    while (gpSDP->pLinks && gpSDP->IsStackRunning()) {
        SVSUTIL_ASSERT(gpSDP->pLinks->pCallQueue == NULL);
        SdpDisconnect(gpSDP->pLinks,NULL);
    }

    DispatchStackEvent (BTH_STACK_DOWN);
    gpSDP->DelRef ();

    IFDBG(DebugOut (DEBUG_SDP_TRACE, L"SDP: -StackDown\n"));
    gpSDP->Unlock ();

    return ERROR_SUCCESS;
}

static DWORD WINAPI StackUp (LPVOID pArg) {
    IFDBG(DebugOut (DEBUG_SDP_TRACE, L"Connect stack\n"));

    for ( ; ; ) {
        if (! gpSDP) {
            IFDBG(DebugOut (DEBUG_ERROR, L"StackUp : ERROR_SERVICE_DOES_NOT_EXIST\n"));
            return ERROR_SERVICE_DOES_NOT_EXIST;
        }
        gpSDP->Lock ();

        if (gpSDP->eStage != Disconnected) {
            IFDBG(DebugOut (DEBUG_ERROR, L"StackUp : ERROR_SERVICE_NOT_ACTIVE\n"));
            gpSDP->Unlock ();
            return ERROR_SERVICE_NOT_ACTIVE;
        }
        if (gpSDP->GetRefCount () == 1)
            break;

        IFDBG(DebugOut (DEBUG_SDP_TRACE, L"Waiting for ref count in StackUp\n"));
        gpSDP->Unlock ();
        Sleep (100);
    }

    GetConnectionState ();

    if (gpSDP->eStage == Connected) {
        gpSDP->AddRef ();
        DispatchStackEvent (BTH_STACK_UP);
        gpSDP->DelRef ();
    }

    IFDBG(DebugOut (DEBUG_SDP_TRACE, L"-StackUp\n"));
    gpSDP->Unlock ();

    return ERROR_SUCCESS;
}

static DWORD WINAPI StackReset (LPVOID pArg) {
    IFDBG(DebugOut (DEBUG_SDP_TRACE, L"SDP: Reset stack\n"));
    StackDown (NULL);
    StackUp (NULL);
    return NULL;
}

static int SendData(Call *pCall, unsigned char *pData, int cData)  {
    SVSUTIL_ASSERT(gpSDP->eStage == Connected);
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"SendData(0x%08x,0x%08x,%d)\r\n",pCall,pData,cData));
    IFDBG(DumpBuff(DEBUG_SDP_PACKETS,pData,cData));

    int       iRet   = ERROR_INTERNAL_ERROR;
    BD_BUFFER *pBuf;

    SVSUTIL_ASSERT(pCall->hCallContext != SVSUTIL_HANDLE_INVALID);

    L2CA_DataDown_In pCallBack = gpSDP->l2cap_if.l2ca_DataDown_In;
    HANDLE hL2CAP = gpSDP->hL2CAP;
    SVSHandle hContext = pCall->hCallContext;
    unsigned short usCID = pCall->pLink->cid;

    unsigned int cFrameSize;
    if (! SUCCEEDED(CeUIntAdd3(cData, gpSDP->cDataHeaders, gpSDP->cDataTrailers, &cFrameSize)))
        return ERROR_ARITHMETIC_OVERFLOW;

    if (NULL == (pBuf = BufferAlloc(cFrameSize)))
        return ERROR_OUTOFMEMORY;
    pBuf->cStart = gpSDP->cDataHeaders;

    memcpy(pBuf->pBuffer + pBuf->cStart,pData,cData);
    pBuf->cEnd = pBuf->cStart + cData;
    SVSUTIL_ASSERT(pBuf->cEnd + gpSDP->cDataTrailers == pBuf->cSize);
    SVSUTIL_ASSERT(cData == pBuf->cSize - gpSDP->cDataHeaders - gpSDP->cDataTrailers);
    SVSUTIL_ASSERT(cData <= pCall->pLink->outMTU);
    SVSUTIL_ASSERT(!pCall->pLink->fCallCanceled);

    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Going into l2ca_DataDown_In\r\n"));
    gpSDP->AddRef();
    gpSDP->Unlock();
    __try {
        iRet = pCallBack(hL2CAP,(LPVOID)hContext,usCID,pBuf);
    } __except(1)     {
        IFDBG(DebugOut(DEBUG_ERROR,L"Exception in l2cap_DataDown_In\r\n"));
    }

    gpSDP->Lock();
    gpSDP->DelRef();
    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Came from l2ca_DataDown_In\r\n"));
    return iRet;
}


static int ProcessNextCall(Link *pLink)  {
    int iErr = ERROR_SUCCESS;

    if (!pLink->pCallQueue)  {
        IFDBG(DebugOut(DEBUG_SDP_PACKETS,L"ProcessNextCall: No pending calls in wait queue for pLink->cid=0x%04x\r\n",pLink->cid));
        return ERROR_SUCCESS;
    }

    while (VerifyLink(pLink) && pLink->pCallQueue)  {
        Call *pCall = pLink->pCallQueue->pCall;

        IFDBG(DebugOut(DEBUG_SDP_PACKETS,L"ProcessNextCall: About to process call 0x%08x\r\n",pLink->pCallQueue->pCall));
        if (ERROR_SUCCESS != (iErr = pCall->pSdpConnection->SendInitialRequestToServer())) {
            if (VerifyCall(pCall))
                NotifySdpClientOfCompletion(pCall,iErr);
            continue;                
        }
        return ERROR_SUCCESS;
    }
    return iErr;
}


void CreateCallForServer(Link *pLink) {
    Call *pNewCall = AllocCall(CALL_SDP_SERVER_WORKER,pLink,NULL,NULL);
    SdpConnection *pSdpConn = pNewCall ? AllocSdpConnection(pNewCall) : NULL;

    if (!pNewCall || !pSdpConn || !AddItemToCallQueue(pLink,pNewCall)) {
        if (pNewCall)
            DeleteCall(pNewCall);

        if (VerifyLink(pLink))
            SdpDisconnect(pLink,NULL);
    }
}


//******************************************************************
// L2Cap Interface Layer.
//******************************************************************

static int sdp_DataUpInd(void *pUserContext, unsigned short cid, BD_BUFFER *pBuffer) {
    int           iSize;
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_DataUpInd(0x%08x,0x%04x,0x%08x)\r\n",pUserContext,cid,pBuffer));

    if (! gpSDP) {
        if (! pBuffer->fMustCopy) 
            pBuffer->pFree(pBuffer);
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    gpSDP->Lock();
    if (gpSDP->eStage != Connected) {
        if (! pBuffer->fMustCopy) 
            pBuffer->pFree(pBuffer);

        IFDBG(DebugOut(DEBUG_WARN,L"sdp_DataUpInd:: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    iSize = BufferTotal(pBuffer);
    BufferGetChunk(pBuffer,iSize,gpSdpReadBuffer);
    IFDBG(DumpBuff(DEBUG_SDP_PACKETS, gpSdpReadBuffer, iSize));

    if (! pBuffer->fMustCopy)
        pBuffer->pFree(pBuffer);

    Link *pLink = FindLink(cid);
    if (! pLink) {
        IFDBG(DebugOut(DEBUG_WARN,L"sdp_DataUpInd:: cid 0x%04x not found\r\n",cid));
        gpSDP->Unlock();
        return ERROR_NOT_FOUND;
    }

    Call *pCall = NULL;
    if (!pLink->fCallCanceled && pLink->pCallQueue)
        pCall = pLink->pCallQueue->pCall;

    if (! pCall) {
        // This will occur if the call has been canceled by the client.
        SVSUTIL_ASSERT(!pLink->fIncoming);
        IFDBG(DebugOut(DEBUG_SDP_PACKETS,L"sdp_DataUpInd:: pCall 0x%08x not found\r\n",pCall));

        // Send off next request in call queue if there is one.
        pLink->fCallCanceled = 0;
        ProcessNextCall(pLink);

        gpSDP->Unlock();
        return ERROR_NOT_FOUND;
    }
    SVSUTIL_ASSERT(pCall->fWhat & CALL_SDP_SEARCH_WORKER);
    
    SdpConnection *pSdpConn = pCall->pSdpConnection;
    unsigned long status    = pSdpConn->ReadWorkItemWorker(gpSdpReadBuffer,iSize);

    if (!pLink->fIncoming && status != STATUS_PENDING) {
        NotifySdpClientOfCompletion(pCall,MapStatusToErrorCode(status));
        if (VerifyLink(pLink))
            ProcessNextCall(pLink);
    }
    else if (pLink->fIncoming && status != ERROR_SUCCESS) {
        ResetSdpConnection(pCall->pSdpConnection);
    }

    gpSDP->Unlock();
    return ERROR_SUCCESS;
}

static int sdp_DisconnectInd(void *pUserContext, unsigned short cid, int iErr) {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_DisconnectInd(0x%08x,0x%04x)\r\n",pUserContext,cid));

    if (! gpSDP)
        return ERROR_SERVICE_NOT_ACTIVE;

    gpSDP->Lock();
    if (gpSDP->eStage != Connected) {
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    Link *pLink = FindLink(cid);
    if (!pLink)  {
        IFDBG(DebugOut(DEBUG_SDP_PACKETS,L"sdp_DisconnectInd: cid=0x%04x doesn't have corresponding SDP connection\r\n",cid));
        gpSDP->Unlock();
        return ERROR_NOT_FOUND;
    }

    // The call associated with creating the link should've been deleted already.
    DeleteLink(pLink);

    gpSDP->Unlock();
    return ERROR_SUCCESS;
}

static int sdp_lStackEvent(void *pUserContext, int iEvent, void *pEventContext) {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_lStackEvent(0x%08x,%d,0x%08x)\r\n",pUserContext,iEvent,pEventContext));

    switch (iEvent) {
    case BTH_STACK_NONE:
    case BTH_STACK_HOST_BUFFER:
        IFDBG(DebugOut (DEBUG_ERROR, L"Unexpected event - none or host buffer. Doing nothing\n"));
        break;

    case BTH_STACK_RESET:
        IFDBG(DebugOut (DEBUG_SDP_INIT, L"SDP : Stack reset\n"));
        btutil_ScheduleEvent (StackReset, NULL);
        break;

    case BTH_STACK_DOWN:
        IFDBG(DebugOut (DEBUG_SDP_INIT, L"SDP : Stack down\n"));
        btutil_ScheduleEvent (StackDown, NULL);
        break;

    case BTH_STACK_UP:
        IFDBG(DebugOut (DEBUG_SDP_INIT, L"SDP : Stack up\n"));
        btutil_ScheduleEvent (StackUp, NULL);
        break;

    case BTH_STACK_DISCONNECT:
        IFDBG(DebugOut (DEBUG_SDP_INIT, L"SDP : Stack disconnect\n"));
        btutil_ScheduleEvent (StackDisconnect, NULL);
        break;

    case BTH_STACK_FLOW_ON:
        IFDBG(DebugOut (DEBUG_SDP_INIT, L"SDP: Somebody turned flow on. Not supported. Disconnecting\n"));
        btutil_ScheduleEvent (StackDown, NULL);
        break;

    case BTH_STACK_FLOW_OFF:
        IFDBG(DebugOut (DEBUG_SDP_INIT, L"SDP : HCI Flow back off. Turning stack back on.\n"));
        btutil_ScheduleEvent (StackUp, NULL);
        break;

    default:
        IFDBG(DebugOut (DEBUG_ERROR, L"SDP: unknown stack event. Disconnecting out of paranoia.\n"));
        btutil_ScheduleEvent (StackDown, NULL);
    }

    return ERROR_SUCCESS;
}

static int sdp_lCallAborted(void *pCallContext, int iError) {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_lCallAborted(0x%08x,%d)\r\n",pCallContext,iError));

    if (! gpSDP)
        return ERROR_SERVICE_NOT_ACTIVE;

    gpSDP->Lock();

    if (!gpSDP->IsStackRunning()) {
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    Call *pCall = (Call*) btutil_TranslateHandle((SVSHandle)pCallContext);
    if (! pCall) {
        gpSDP->Unlock();
        return ERROR_NOT_FOUND;
    }

    SVSUTIL_ASSERT(VerifyCall(pCall));

    if (pCall->fWhat & CALL_SDP_SEARCH)  {
        SVSUTIL_ASSERT(VerifyLink (pCall->pLink));
        Link *pLink = pCall->pLink;

        // If the call is currently pending then when we get a response it'll be
        // to this call (and item that'll be on head of queue at response time).
        if (pLink->pCallQueue && pLink->pCallQueue->pCall==pCall)  {
            IFDBG(DebugOut(DEBUG_SDP_PACKETS,L"Call (0x%08x) aborted is outstanding on link (0x%08x), further sends will pend it's response\r\n",pCall,pLink));
            pLink->fCallCanceled = 1;
        }
    }

    // During connection it's possible mutliple calls are pointing to same link.  Remove all of them.
    if (pCall->fWhat == CALL_SDP_CONNECT)  {
        RemoveAllCallsByLinkAndNotify(pCall->pLink, pCall, iError);
    }

    if (VerifyCall(pCall)) {
        if ((pCall->fWhat & CALL_SDP_ACCEPT_CONNECT) && VerifyLink(pCall->pLink))
            SdpDisconnect(pCall->pLink,NULL);

        if (VerifyCall(pCall))
            NotifySdpClientOfCompletion(pCall,iError);
    }
    
    gpSDP->Unlock();
    return ERROR_SUCCESS;
}

static int sdp_ConfigInd(void *pUserContext, unsigned char id, unsigned short cid, unsigned short usOutMTU, unsigned short usInFlushTO, struct btFLOWSPEC *pInFlow, int cOptNum, struct btCONFIGEXTENSION **ppExtendedOptions) {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_ConfigInd(0x%08x,%c,%d,%d,%d)\r\n",pUserContext,id,cid,usOutMTU,usInFlushTO));
    if (! gpSDP)
        return ERROR_SERVICE_NOT_ACTIVE;

    gpSDP->Lock();

    if (gpSDP->eStage != Connected) {
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    Link *pLink = FindLink(cid);
    Call *pCall = pLink ? FindCall(pLink) : NULL;
    if (! pCall)  {
        IFDBG(DebugOut(DEBUG_SDP_PACKETS,L"sdp_ConfigInd: can't find link 0x%04x, returns ERROR_NOT_FOUND\r\n",cid));
        gpSDP->Unlock();
        return ERROR_NOT_FOUND;
    }

    SVSUTIL_ASSERT(pLink == pCall->pLink);
    SVSUTIL_ASSERT(pCall->fWhat & CALL_SDP_ACCEPT_CONNECT);
    SVSUTIL_ASSERT(pCall->hCallContext != SVSUTIL_HANDLE_INVALID);

    int fAccept = FALSE;

    if ((usInFlushTO == 0xffff) && (!pInFlow || (pInFlow->service_type == 0x01)) && btutil_VerifyExtendedL2CAPOptions (cOptNum, ppExtendedOptions)) {
        pCall->pLink->fStage |= CONFIG_IND_DONE;
        pCall->pLink->outMTU = usOutMTU ? usOutMTU : L2CAP_MTU;
        fAccept = TRUE;

        if (pLink->fStage == UP) {
            IFDBG(DebugOut(DEBUG_SDP_PACKETS,L"sdp_ConfigInd:: connection complete for pCall=0x%08x\r\n",pCall));
        }
    }

    HANDLE hL2CAP = gpSDP->hL2CAP;
    SVSHandle hContext = pCall->hCallContext;
    L2CA_ConfigResponse_In pCallback = gpSDP->l2cap_if.l2ca_ConfigResponse_In;

    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Going into l2ca_ConfigResponse_In\r\n"));
    gpSDP->AddRef();
    gpSDP->Unlock();

    __try {
        pCallback(hL2CAP, (LPVOID)hContext, id, cid, fAccept ? 0 : 2, 0, 0xffff, NULL, 0, NULL);
    } __except (1) {
        IFDBG(DebugOut(DEBUG_ERROR,L"Exception in l2ca_ConfigResponse_In\r\n"));
    }

    gpSDP->Lock();
    gpSDP->DelRef();

    if (gpSDP->eStage == Connected && VerifyLink (pLink) && (pLink->fStage == UP)) {
        // There maybe multiple calls waiting for connection with this link.  Run through all of them.
        // We delete the call in NotifySdpClientOfCompletion for client case.
        if (NULL != (pCall = FindCall(pLink,CALL_SDP_ACCEPT_CONNECT))) {
            SVSUTIL_ASSERT((pCall->pLink == pLink) && VerifyCall(pCall));
            if (pLink->fIncoming)  {
                CreateCallForServer(pLink);
                DeleteCall(pCall);
            }
            else {
                NotifySdpClientOfCompletion(pCall,ERROR_SUCCESS);
            }
        }
    }
    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Came from l2ca_ConfigResponse_In\r\n"));
    gpSDP->Unlock();
    return ERROR_SUCCESS;
}

static int sdp_ConfigReq_Out(void *pCallContext, unsigned short usResult, unsigned short usInMTU, unsigned short usOutFlushTO, struct btFLOWSPEC *pOutFlow, int cOptNum, struct btCONFIGEXTENSION **ppExtendedOptions) {
    IFDBG(DebugOut (DEBUG_SDP_TRACE,L"sdp_ConfigReq_Out(0x%08x,%d,%d,%d)\r\n",pCallContext,usResult,usInMTU,usOutFlushTO));
    if (! gpSDP)
        return ERROR_SERVICE_NOT_ACTIVE;

    gpSDP->Lock();

    if (gpSDP->eStage != Connected) {
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    Call *pCall = (Call*) btutil_TranslateHandle((SVSHandle)pCallContext);
    if (! pCall)  {
        gpSDP->Unlock();
        return ERROR_NOT_FOUND;
    }

    SVSUTIL_ASSERT(VerifyCall(pCall,CALL_SDP_ACCEPT_CONNECT));

    Link *pLink = pCall->pLink;    
    SVSUTIL_ASSERT(VerifyLink(pLink));

    if (usResult == 0) {
        SVSUTIL_ASSERT(! (pLink->fStage & CONFIG_REQ_DONE));
        SVSUTIL_ASSERT(pLink->fStage & CONNECTED);
        SVSUTIL_ASSERT(pLink->cid);
        SVSUTIL_ASSERT(pLink->psm);

        pLink->fStage |= CONFIG_REQ_DONE;

        if (pLink->fStage == UP) {
            IFDBG(DebugOut(DEBUG_SDP_PACKETS,L"sdp_ConfigInd:: connection complete for pCall=0x%08x\r\n",pCall));

            // There maybe multiple calls waiting for connection with this link.  Run through all of them.
            while (NULL != (pCall = FindCall(pLink,CALL_SDP_ACCEPT_CONNECT))) {
                SVSUTIL_ASSERT(pCall->pLink == pLink && VerifyCall(pCall));
                if (pLink->fIncoming) {
                    CreateCallForServer(pLink);
                    DeleteCall(pCall);
                }
                else {
                    NotifySdpClientOfCompletion(pCall,ERROR_SUCCESS);
                }
            }
        }
        gpSDP->Unlock();
        return ERROR_SUCCESS;
    }

    gpSDP->Unlock();
    sdp_lCallAborted(pCallContext, ERROR_CONNECTION_ABORTED);

    return ERROR_SUCCESS;
}

static int sdp_ConnectInd(void *pUserContext, BD_ADDR *pba, unsigned short cid, unsigned char id, unsigned short psm) {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_ConnectInd(0x%08x,0x%04x 0x%08x,%d,%c,%d)\r\n",pUserContext,pba->NAP,pba->SAP,cid,id,psm));

    if (! gpSDP)
        return ERROR_SERVICE_NOT_ACTIVE;

    gpSDP->Lock();
    if (gpSDP->eStage != Connected) {
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    unsigned short    result;
    unsigned short    status = 0;
    unsigned short    mtu = 0;

    if (psm != PSM_SDP)  {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ConnectInd received connect for psm = %d (not SDP PSM)\r\n",psm));
        SVSUTIL_ASSERT(0);  // hitting this means that L2CAP has given us a request for a caller on a PSM that SDP did not specify.
        gpSDP->Unlock();
        return ERROR_INVALID_PARAMETER;
    }

    Call *pCall = AllocCall(CALL_SDP_ACCEPT,NULL,NULL,NULL);
    Link *pLink = pCall ? AllocLink() : NULL;

    if (pLink) {
        pCall->pLink = pLink;
        pLink->b = *pba;
        pLink->cid = cid;
        pLink->fStage = CONNECTED;
        pLink->inMTU = DEFAULT_IN_MTU_SIZE;
        pLink->outMTU = 0;
        pLink->psm = psm;
        pLink->fIncoming = TRUE;
        pLink->pNext = gpSDP->pLinks;
        gpSDP->pLinks = pLink;
        
        result = 0;
        mtu = pLink->inMTU;
    } else {
        if (pCall)
            DeleteCall(pCall);
        result = 4;
    }

    HANDLE hL2CAP = gpSDP->hL2CAP;    
    L2CA_ConnectResponse_In pCallbackConnect = gpSDP->l2cap_if.l2ca_ConnectResponse_In;
    L2CA_ConfigReq_In pCallbackConfig = gpSDP->l2cap_if.l2ca_ConfigReq_In;

    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Calling into l2ca_ConnectResponse_In\r\n"));
    gpSDP->AddRef();
    gpSDP->Unlock();

    __try {
        pCallbackConnect(hL2CAP, NULL, pba, id, cid, result, status);
    } __except (1) {
        IFDBG(DebugOut(DEBUG_ERROR,L"Exception in l2ca_ConfigReq_In\r\n"));
    }

    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Came from l2ca_ConnectResponse_In\r\n"));

    if (result == 0) {
        SVSUTIL_ASSERT(pCall->hCallContext != SVSUTIL_HANDLE_INVALID);
        SVSHandle hContext = pCall->hCallContext;
        
        int iRes = ERROR_INTERNAL_ERROR;
        IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Calling into l2ca_ConfigReq_In\r\n"));
        
        __try {
            iRes = pCallbackConfig(hL2CAP, (LPVOID)hContext, cid, mtu, 0xffff, NULL, 0, NULL);
        } __except (1) {
            IFDBG(DebugOut(DEBUG_ERROR,L"Exception in l2ca_ConfigReq_In\r\n"));
        }
        IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Came from l2ca_ConfigReq_In\r\n"));
        
        if (iRes != ERROR_SUCCESS)  {
            sdp_lCallAborted((LPVOID)hContext, iRes);
        }
    }
    gpSDP->Lock();
    gpSDP->DelRef();
    gpSDP->Unlock();
    
    return ERROR_SUCCESS;
}

static int sdp_ConnectReq_Out(void *pCallContext, unsigned short cid, unsigned short result, unsigned short status) {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_ConnectReq_Out(0x%08x,%d,%d,%d)\r\n",cid,result,status));

    if (result)
        return sdp_lCallAborted(pCallContext,ERROR_CONNECTION_UNAVAIL);

    if (! gpSDP)
        return ERROR_SERVICE_NOT_ACTIVE;

    gpSDP->Lock();

    if (gpSDP->eStage != Connected) {
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    Call *pCall = (Call*) btutil_TranslateHandle((SVSHandle)pCallContext);
    if (! pCall) {
        gpSDP->Unlock();
        return ERROR_NOT_FOUND;
    }

    SVSUTIL_ASSERT(VerifyCall(pCall, CALL_SDP_ACCEPT_CONNECT));
    SVSUTIL_ASSERT(VerifyLink(pCall->pLink));

    Link *pLink = pCall->pLink;

    SVSUTIL_ASSERT(pLink->fStage == NONE);
    SVSUTIL_ASSERT(! pLink->cid);
    SVSUTIL_ASSERT(pLink->psm);

    pLink->fStage = CONNECTED;
    pLink->cid = cid;

    unsigned short mtu = pLink->inMTU;

    HANDLE hL2CAP = gpSDP->hL2CAP;
    L2CA_ConfigReq_In pCallback = gpSDP->l2cap_if.l2ca_ConfigReq_In;

    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Calling into l2ca_ConfigReq_In\r\n"));
    gpSDP->AddRef();
    gpSDP->Unlock();

    int iRes = ERROR_INTERNAL_ERROR;
    __try {
        iRes = pCallback(hL2CAP, pCallContext, cid, mtu, 0xffff, NULL, 0, NULL);
    } __except (1) {
        IFDBG(DebugOut(DEBUG_ERROR,L"Exception in l2ca_ConfigReq_In\r\n"));
    }

    if (iRes != ERROR_SUCCESS)
        sdp_lCallAborted(pCallContext, iRes);

    gpSDP->Lock();
    gpSDP->DelRef();

    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Came from l2ca_ConfigReq_In\r\n"));
    gpSDP->Unlock();
    return ERROR_SUCCESS;
}

static int sdp_DataDown_Out(void *pCallContext, unsigned short result) {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_DataDown_Out(0x%08x,%d)\r\n",pCallContext,result));
    if (! gpSDP)
        return ERROR_SERVICE_NOT_ACTIVE;

    gpSDP->Lock();

    if (gpSDP->eStage != Connected) {
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    Call *pCall = (Call*) btutil_TranslateHandle((SVSHandle)pCallContext);
    if (! pCall) {
        gpSDP->Unlock();
        return ERROR_NOT_FOUND;
    }
    
    SVSUTIL_ASSERT(VerifyCall(pCall,CALL_SDP_SEARCH_WORKER));
    SVSUTIL_ASSERT(VerifyLink(pCall->pLink));

    gpSDP->Unlock();
    return ERROR_SUCCESS;
}

static int sdp_Disconnect_Out(void *pCallContext, unsigned short result) {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_Disconnect_Out(0x%08x,%d)\r\n",pCallContext,result));
    if (! gpSDP)
        return ERROR_SERVICE_NOT_ACTIVE;

    gpSDP->Lock();

    Call *pCall = pCallContext ? (Call*) btutil_TranslateHandle((SVSHandle)pCallContext) : NULL;
    if (pCall) {
        SVSUTIL_ASSERT(VerifyCall(pCall,CALL_SDP_DISCONNECT));
        NotifySdpClientOfCompletion(pCall,result);
    }

    gpSDP->Unlock();
    return ERROR_SUCCESS;
}

// These are just stubs - they do nothing
static int sdp_ConfigResponse_Out(void *pCallContext, unsigned short result) {
    return ERROR_SUCCESS;
}

static int sdp_ConnectResponse_Out(void *pCallContext, unsigned short result) {
    return ERROR_SUCCESS;
}


//**************************************************************
//    Device Driver init and deinit functions
//**************************************************************
int sdp_InitializeOnce(void)  {
    IFDBG(DebugOut(DEBUG_SDP_INIT | DEBUG_SDP_TRACE,L"sdp_InitializeOnce()\r\n"));
    SVSUTIL_ASSERT(! gpSDP && !gpSdpReadBuffer);

    if (gpSDP)  {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_InitializeOnce:: ERROR_ALREADY_EXISTS\r\n"));
        return ERROR_ALREADY_EXISTS;
    }    

    gpSDP = CreateNewSDP();
    if (gpSDP)  {
        gpSdpReadBuffer = (unsigned char*) g_funcAlloc(MAX_MTU_SIZE,g_pvAllocData);
        if (gpSdpReadBuffer) {
            IFDBG(DebugOut(DEBUG_SDP_INIT, L"sdp_InitializeOnce:: ERROR_SUCCESS\n"));
            return ERROR_SUCCESS;
        }
        delete gpSDP;
        gpSDP = NULL;
    }

    IFDBG(DebugOut(DEBUG_ERROR, L"sdp_InitializeOnce:: ERROR_OUTOFMEMORY\n"));
    return ERROR_OUTOFMEMORY;
}


int sdp_CreateDriverInstance(void)  {
    int iErr;
    IFDBG(DebugOut(DEBUG_SDP_INIT | DEBUG_SDP_TRACE,L"sdp_CreateDriverInstance()\r\n"));

    if (! gpSDP) {
        IFDBG(DebugOut(DEBUG_ERROR, L"sdp_CreateDriverInstance:: ERROR_SERVICE_DOES_NOT_EXIST\n"));
        return ERROR_SERVICE_DOES_NOT_EXIST;
    }

    gpSDP->Lock();
    if (gpSDP->eStage != JustCreated) {
        IFDBG(DebugOut(DEBUG_ERROR, L"sdp_CreateDriverInstance:: ERROR_SERVICE_ALREADY_RUNNING\n"));
        gpSDP->Unlock();
        return ERROR_SERVICE_ALREADY_RUNNING;
    }
    SVSUTIL_ASSERT(!gpSDP->pfmdLinks && !gpSDP->pfmdSdpConnections && 
                   !gpSDP->pfmdCallQueue && !gpSDP->pfmdBuffers);

    gpSDP->eStage = Initializing;

    // Setup gpSDP.
    gpSDP->pfmdLinks          = svsutil_AllocFixedMemDescr(sizeof(Link), SDP_SCALE);
    gpSDP->pfmdCalls          = svsutil_AllocFixedMemDescr(sizeof(Call), SDP_SCALE);
    gpSDP->pfmdSdpConnections = svsutil_AllocFixedMemDescr(sizeof(SdpConnection), SDP_SCALE);
    gpSDP->pfmdCallQueue      = svsutil_AllocFixedMemDescr(sizeof(CallQueue), SDP_SCALE);
    gpSDP->pfmdBuffers        = svsutil_AllocFixedMemDescr(sizeof(BD_BUFFER), SDP_SCALE);

    if (!pSdpDB) {
        pSdpDB = CreateNewSdpDatabase();
        if (pSdpDB && (STATUS_SUCCESS != pSdpDB->Init())) {
            delete pSdpDB;
            pSdpDB = NULL;
        }
    }

    if (! (gpSDP->pfmdLinks     && gpSDP->pfmdCalls   && gpSDP->pfmdSdpConnections &&
           gpSDP->pfmdCallQueue && gpSDP->pfmdBuffers && gpSDP->pfmdBuffers &&
           pSdpDB))   {

        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_CreateDriverInstance: ERROR_OUTOFMEMORY\r\n"));
        CleanupSDPState();
        gpSDP->Unlock();
        return ERROR_OUTOFMEMORY;
    }

    // Configure L2CAP 
    L2CAP_EVENT_INDICATION lei;
    memset(&lei, 0, sizeof(lei));

    lei.l2ca_ConfigInd     = sdp_ConfigInd;
    lei.l2ca_ConnectInd    = sdp_ConnectInd;
    lei.l2ca_DataUpInd     = sdp_DataUpInd;
    lei.l2ca_DisconnectInd = sdp_DisconnectInd;
    lei.l2ca_StackEvent    = sdp_lStackEvent;

    L2CAP_CALLBACKS lc;
    memset(&lc, 0, sizeof(lc));

    lc.l2ca_ConfigReq_Out        = sdp_ConfigReq_Out;
    lc.l2ca_ConfigResponse_Out   = sdp_ConfigResponse_Out;
    lc.l2ca_ConnectReq_Out       = sdp_ConnectReq_Out;
    lc.l2ca_ConnectResponse_Out  = sdp_ConnectResponse_Out;
    lc.l2ca_DataDown_Out         = sdp_DataDown_Out;
    lc.l2ca_Disconnect_Out       = sdp_Disconnect_Out;
    lc.l2ca_CallAborted          = sdp_lCallAborted;
    
    if (ERROR_SUCCESS != (iErr = L2CAP_EstablishDeviceContext (gpSDP, PSM_SDP, &lei, &lc, &gpSDP->l2cap_if,&gpSDP->cDataHeaders,&gpSDP->cDataTrailers,&gpSDP->hL2CAP))) {
        IFDBG((DEBUG_ERROR,L"sdp_CreateDriverInstance could not plug into L2CAP, exiting\r\n"));
        CleanupSDPState();
        gpSDP->Unlock();        
        return iErr;
    }    

    if (! (gpSDP->l2cap_if.l2ca_ConnectReq_In      && gpSDP->l2cap_if.l2ca_ConfigReq_In   &&
           gpSDP->l2cap_if.l2ca_ConfigResponse_In  && gpSDP->l2cap_if.l2ca_Disconnect_In  &&
           gpSDP->l2cap_if.l2ca_ConnectResponse_In && gpSDP->l2cap_if.l2ca_AbortCall      &&
           gpSDP->l2cap_if.l2ca_DataDown_In))  {

        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_CreateDriverInstance fails because not all functions required by SDP are exposed by the L2CAP layer\r\n"));

        L2CAP_CloseDeviceContext (gpSDP->hL2CAP);
        CleanupSDPState();
        gpSDP->Unlock();
        return iErr;
    }

#if 0  // Currently this reg param is unused
    HKEY hk;
    if (ERROR_SUCCESS == RegOpenKeyEx (HKEY_BASE, g_SdpBaseRegKey, 0, KEY_READ, &hk)) {
        DWORD dwSize = sizeof(gpSDP->dwSdpIdle);
        DWORD dwType = REG_DWORD;

        RegQueryValueEx(hk, g_SdpLinkIdle, NULL, &dwType, (LPBYTE)&gpSDP->dwSdpIdle, &dwSize);
        if ((dwSize != sizeof(gpSDP->dwSdpIdle)) || (dwType != REG_DWORD))
            gpSDP->dwSdpIdle = SDP_DEFAULT_LINK_TIMEOUT_MS;

        IFDBG(DebugOut(DEBUG_SDP_INIT,L"SdpLinkIdle time = %d ms\r\n",gpSDP->dwSdpIdle));
        RegCloseKey(hk);
    }
#endif

    gpSDP->eStage = Disconnected;
    GetConnectionState();

    IFDBG(DebugOut(DEBUG_SDP_INIT,L"sdp_CreateDriverInstance returns ERROR_SUCCESS\r\n"));
    gpSDP->Unlock();
    return ERROR_SUCCESS;
}


int sdp_CloseDriverInstance (void) {
    IFDBG(DebugOut(DEBUG_SDP_INIT | DEBUG_SDP_TRACE,L"sdp_CloseDriverInstance()\r\n"));
    
    if (! gpSDP)  {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_CloseDriverInstance: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        return ERROR_SERVICE_NOT_ACTIVE;
    }    

    gpSDP->Lock();
    if ((gpSDP->eStage == JustCreated) || (gpSDP->eStage == ShuttingDown)) {
        IFDBG(DebugOut(DEBUG_ERROR, L"SDP Close Driver Instance:: ERROR_SERVICE_NOT_ACTIVE\n"));
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    gpSDP->eStage = ShuttingDown;

    while (gpSDP->pCalls) {
        NotifySdpClientOfCompletion(gpSDP->pCalls,ERROR_CANCELLED);
    }

    while (gpSDP->pLinks) {
        SVSUTIL_ASSERT(gpSDP->pLinks->pCallQueue == NULL);
        SdpDisconnect(gpSDP->pLinks,NULL);
    }

    if (gpSDP->hL2CAP)  {
        while (gpSDP->GetRefCount() > 1) {
            IFDBG(DebugOut (DEBUG_SDP_TRACE, L"Waiting for ref count in sdp_CloseDriverInstance\n"));
            gpSDP->Unlock();
            Sleep(200);
            gpSDP->Lock();
        }
        L2CAP_CloseDeviceContext(gpSDP->hL2CAP);
    }

    while (gpSDP->pContexts)  {
        SDP_CONTEXT *pThis = gpSDP->pContexts;
        gpSDP->pContexts = pThis->pNext;

        if (pThis->ei.sdp_StackEvent) {
            BT_LAYER_STACK_EVENT_IND pCallback = pThis->ei.sdp_StackEvent;
            void *pUserContext = pThis->pUserContext;

            IFDBG(DebugOut(DEBUG_SDP_TRACE, L"Going into StackEvent notification(stack down)\r\n"));
            pThis->AddRef();
            gpSDP->Unlock();
            __try {
                pCallback(pUserContext, BTH_STACK_DOWN, NULL);
            } __except(1) {
                IFDBG(DebugOut(DEBUG_ERROR, L"Exception in higher layer code\r\n"));
            }
            gpSDP->Lock();
            pThis->DelRef();
            IFDBG(DebugOut(DEBUG_SDP_TRACE, L"Came back StackEvent notification\r\n"));
        }

        while (pThis->GetRefCount() > 1) {
            IFDBG(DebugOut (DEBUG_SDP_TRACE, L"Waiting for ref count in sdp_CloseDriverInstance\n"));
            gpSDP->Unlock();
            Sleep(100);
            gpSDP->Lock();
        }
        delete pThis;
    }

    SVSUTIL_ASSERT(gpSDP->eStage == ShuttingDown);
    CleanupSDPState();

    gpSDP->Unlock();
    return ERROR_SUCCESS;
}

int sdp_UninitializeOnce(void) {
    IFDBG(DebugOut(DEBUG_SDP_INIT | DEBUG_SDP_TRACE,L"sdp_UninitializeOnce()\r\n"));
    SVSUTIL_ASSERT(gpSDP);

    if (! gpSDP) {
        IFDBG(DebugOut(DEBUG_ERROR, L"SDP uninit:: ERROR_SERVICE_DOES_NOT_EXIST\n"));
        return ERROR_SERVICE_DOES_NOT_EXIST;
    }
    gpSDP->Lock();

    if (gpSDP->eStage != JustCreated) {
        IFDBG(DebugOut(DEBUG_ERROR, L"SDP uninit:: ERROR_DEVICE_IN_USE\n"));
        gpSDP->Unlock();
        return ERROR_DEVICE_IN_USE;
    }

    if (pSdpDB) {
        delete pSdpDB;
        pSdpDB = NULL;
    }

    if (gpSdpReadBuffer) {
        g_funcFree(gpSdpReadBuffer,g_pvAllocData);
        gpSdpReadBuffer    = NULL;
    }

    if (gpSpdConnectionLocal) {
        DeleteSdpConnection(gpSpdConnectionLocal);
        gpSpdConnectionLocal = NULL;
    }

    SDP *pSDP = gpSDP;
    gpSDP = NULL;
    pSDP->Unlock();
    delete pSDP;

    IFDBG(DebugOut(DEBUG_SDP_INIT, L"SDP uninit:: ERROR_SUCCESS\n");)    
    return ERROR_SUCCESS;
}

static int SdpCreateConnection(Call *pCall, BD_ADDR *pba) {
    SVSUTIL_ASSERT(gpSDP->IsLocked());
    SVSUTIL_ASSERT(!pCall->pLink);
    SVSUTIL_ASSERT(pCall->hCallContext != SVSUTIL_HANDLE_INVALID);    

    Link *pLink = AllocLink();
    if (!pLink) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_Connect:: ERROR_OUTOFMEMORY\r\n"));
        return ERROR_OUTOFMEMORY;
    }
    
    pLink->pNext = gpSDP->pLinks;
    gpSDP->pLinks = pLink;
    pCall->pLink = pLink;

    pLink->cid = 0;
    pLink->b = *pba;
    pLink->fStage = NONE;
    pLink->inMTU = DEFAULT_IN_MTU_SIZE;
    pLink->fIncoming = FALSE;

    HANDLE hL2CAP = gpSDP->hL2CAP;
    SVSHandle hContext = pCall->hCallContext;
    L2CA_ConnectReq_In pCallbackConnect = gpSDP->l2cap_if.l2ca_ConnectReq_In;
    int iRes = ERROR_INTERNAL_ERROR;

    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Calling into l2ca_ConnectReq_In\r\n"));
    gpSDP->AddRef();
    gpSDP->Unlock();
    __try {
        iRes = pCallbackConnect(hL2CAP, (LPVOID)hContext, PSM_SDP, pba);
    } __except(1) {
        IFDBG(DebugOut(DEBUG_ERROR,L"Exception in l2ca_ConnectReq_In!!!\r\n"));
    }
    gpSDP->Lock();
    gpSDP->DelRef();
    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"came from l2ca_ConnectReq_In\r\n"));

    return iRes;
}

//******************************************************************
// Replaced functions from NT SDP layer class.
//******************************************************************
unsigned long
SdpConnection::SendInitialRequestToServer(void)  {
    // Client data has request to send, rawParametersLength doesn't count PDU or continuation state info.
    return Write(u.Client.pRequest,u.Client.rawParametersLength + sizeof(SdpPDU) + sizeof(UCHAR));
}

// Called by functions in btsdp.cpp, this will send request if there's nothing busy or queue it otherwise.
unsigned long SdpConnection::SendOrQueueRequest()  {
    Call      *pCall   = (Call *) pCallContext;
    Link      *pLink   = pCall->pLink;
    int       iErr     = STATUS_PENDING;
    CallQueue *pCallQueue;

    SVSUTIL_ASSERT(gpSDP->IsLocked());
    SVSUTIL_ASSERT(VerifyCall(pCall,CALL_SDP_SEARCH));
    SVSUTIL_ASSERT(VerifyLink(pLink));    

    if (! (pCallQueue = AddItemToCallQueue(pLink,pCall)))  {
        return ERROR_OUTOFMEMORY;
    }

    if (pCallQueue == pLink->pCallQueue && !pLink->fCallCanceled)
        iErr = SendInitialRequestToServer();

    return iErr;
}


NTSTATUS 
SdpConnection::Write(
    void *pBuffer,
    unsigned long length
    )
{
    SVSUTIL_ASSERT(gpSDP->IsLocked());
    
    Call *pCall = (Call *) pCallContext;
    SVSUTIL_ASSERT(VerifyCall(pCall,CALL_SDP_SEARCH_WORKER));
    SVSUTIL_ASSERT(VerifyLink(pCall->pLink));

#if defined (DEBUG) || defined (_DEBUG)
    // call we're about to service better be first on send queue.
    if (pCall->fWhat & CALL_SDP_SEARCH)
        SVSUTIL_ASSERT(pCall->pLink->pCallQueue->pCall == pCall);
#endif

    return SendData(pCall,(unsigned char*)pBuffer,length);
}

BOOL SdpConnection::IsClient() {
    return ! ((Call*) pCallContext)->pLink->fIncoming;
}

BOOL SdpConnection::IsServer() {
    return ((Call*) pCallContext)->pLink->fIncoming;
}

// Code that relies on CSemaphore locking mechanism is from NT port, and is always wrapped
// in the SDP CritSec.  Put ASSERTS in to be sure
BOOLEAN CSemaphore::ReleaseSem(KPRIORITY Increment,LONG Adjustment, BOOLEAN bWait)  { 
    SVSUTIL_ASSERT(gpSDP && gpSDP->IsLocked());
    return TRUE; 
}

void CSemaphore::AcquireSem()  { 
    SVSUTIL_ASSERT(gpSDP && gpSDP->IsLocked());
}

// Required to map status codes returned by NT SDP layer
static int MapStatusToErrorCode(int status)  {
    switch (status) {
        case ERROR_SUCCESS:
            return ERROR_SUCCESS;

        case ERROR_INVALID_PARAMETER:
        case STATUS_UNSUCCESSFUL:
        case STATUS_INVALID_PARAMETER:
        case STATUS_DEVICE_PROTOCOL_ERROR:
        case STATUS_INVALID_NETWORK_RESPONSE:
            return ERROR_INVALID_PARAMETER;

        case ERROR_NOT_FOUND:
        case STATUS_NOT_FOUND:
            return ERROR_NOT_FOUND;

        case ERROR_OUTOFMEMORY:
        case STATUS_INSUFFICIENT_RESOURCES:
            return ERROR_OUTOFMEMORY;

        default:
            SVSUTIL_ASSERT(0);
    }
    return status;
}


//******************************************************************
// SDP Interface
//******************************************************************

// AddRecord and RemoveRecord run totally locally, no need
// to have them be Asyncronous.  Since they run locally we only need to make
// sure SDP stack is running since we don't use lower L2CAP layers in these calls.
static int sdp_AddRecord(HANDLE hDeviceContext, UCHAR *pStream, unsigned long streamSize, HANDLE *pRecordHandle) {
    int iRet;
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_AddRecord(0x%08x,0x%08x,%d)\r\n",hDeviceContext,pStream,streamSize));
                             
    if (! gpSDP) {
        IFDBG(DebugOut(DEBUG_WARN,L"sdp_AddRecord returns ERROR_SERVICE_NOT_ACTIVE\r\n"));
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    gpSDP->Lock();
    if (!gpSDP->IsStackRunning()) {
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }
    
    if (!VerifyContext((SDP_CONTEXT*) hDeviceContext) || !pStream || streamSize == 0 || !pRecordHandle)
        iRet = ERROR_INVALID_PARAMETER;
    else {
#if defined (UNDER_CE)
        // if *pRecordHandle is non NULL, then treat it as an update and not add record.
        iRet = *pRecordHandle ? pSdpDB->UpdateRecord(pStream,streamSize,*pRecordHandle) : pSdpDB->AddRecord(pStream,streamSize,pRecordHandle);
        if (iRet != ERROR_SUCCESS)
               *pRecordHandle = 0;
#else  // WINCE_EMULATION
        iRet = pSdpDB->AddRecord(pStream,streamSize,pRecordHandle);
#endif // UNDER_CE

    }
    gpSDP->Unlock();

#if defined (UNDER_CE)
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdpAddRecord returns 0x%08x, *pRecordHandle=0x%08x\r\n",iRet,*pRecordHandle));
#endif
    return MapStatusToErrorCode(iRet);
}

static int sdp_RemoveRecord(HANDLE hDeviceContext, HANDLE hRecordHandle)  {
    int iRet;
    unsigned long iRemovedRecord;
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_RemoveRecord(0x%08x,0x%08x)\r\n",hDeviceContext,hRecordHandle));

    if (! gpSDP) {
        IFDBG(DebugOut(DEBUG_WARN,L"sdp_RemoveRecord ERROR_SERVICE_NOT_ACTIVE\r\n"));
        return ERROR_SERVICE_NOT_ACTIVE;
    }
    
    gpSDP->Lock();
    if (!gpSDP->IsStackRunning()) {
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    if (!VerifyContext((SDP_CONTEXT*) hDeviceContext))
        iRet = ERROR_INVALID_PARAMETER;
    else
        iRet = pSdpDB->RemoveRecord(hRecordHandle,&iRemovedRecord);
    gpSDP->Unlock();

    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdpRemoveRecord returns 0x%08x\r\n",iRet));
    return MapStatusToErrorCode(iRet);
}

static int SdpDisconnect(Link *pLink, Call *pCall)  {
    SVSUTIL_ASSERT( VerifyLink(pLink));

    HANDLE hL2CAP = gpSDP->hL2CAP;
    SVSHandle hContext = NULL;
    L2CA_Disconnect_In pCallback = gpSDP->l2cap_if.l2ca_Disconnect_In;
    unsigned short cid = pLink->cid;
    int iRes = ERROR_INTERNAL_ERROR;

    if (pCall) {
        SVSUTIL_ASSERT(pCall->hCallContext != SVSUTIL_HANDLE_INVALID);
        hContext = pCall->hCallContext;
    }

    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Entering l2ca_Disconnect_In, cid = 0x%04x\r\n",pLink->cid));
    gpSDP->AddRef();
    gpSDP->Unlock();
    __try {
        iRes = pCallback (hL2CAP, (LPVOID)hContext, cid);
    } __except (1) {
        IFDBG(DebugOut(DEBUG_ERROR,L"Exception in l2ca_Disconnect_In\r\n"));
    }
    gpSDP->Lock();
    gpSDP->DelRef();
    IFDBG(DebugOut(DEBUG_SDP_CALLBACK,L"Finished with l2ca_Disconnect_In\r\n");)    

    DeleteLink(pLink);
    return iRes;
}

static int sdp_Connect_In(HANDLE hDeviceContext, void *pCallContext, BD_ADDR *pba)  {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_Connect_In(0x%08x,0x%08x,0x%08x 0x%04)\r\n",
                              hDeviceContext,pCallContext,pba->NAP,pba->SAP));

    if (! gpSDP) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_Connect_In: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    gpSDP->Lock();
    if (gpSDP->eStage != Connected) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_Connect_In: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    SDP_CONTEXT *pContext = VerifyContext((SDP_CONTEXT*) hDeviceContext);
    if (!pContext || !pba)  {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_Connect_In: ERROR_INVALID_PARAMETER\r\n"));
        gpSDP->Unlock();
        return ERROR_INVALID_PARAMETER;
    }

    Link *pLink = FindLink(pba,TRUE);
    if (pLink)  {
        IFDBG(DebugOut(DEBUG_SDP_PACKETS,L"sdp_Connect_In: already connected\r\n"));
        pLink->AddRef();

        if (pLink->fStage == UP)  {
            NotifyConnect(pContext,pCallContext,ERROR_SUCCESS,pLink->cid);
            gpSDP->Unlock();
            return ERROR_SUCCESS;
        }
        // if link hasn't been established we alloc the call but don't try to fire up connection.
    }

    Call *pCall = AllocCall(CALL_SDP_CONNECT,pLink,pContext,pCallContext);
    if (!pCall)  {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_Connect_In:  ERROR_OUTOFMEMORY\r\n"));
        if (pLink)
            pLink->DelRef();
        gpSDP->Unlock();
        return ERROR_OUTOFMEMORY;
    }
    int iRes = ERROR_SUCCESS;

    if (!pLink)  {
        iRes = SdpCreateConnection(pCall,pba);
        if (iRes != ERROR_SUCCESS)  {
            // Since we gave up CritSec in SdpCreateConnection need to make sure call is still around.
            if (VerifyCall(pCall))
                DeleteCall(pCall);
        }
    }

    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_Connect_In: iRes = 0x%08x\r\n",iRes));
    gpSDP->Unlock();
    return iRes;
}


static int sdp_Disconnect_In(HANDLE hDeviceContext, void *pCallContext, unsigned short cid)  {
    int iRes = ERROR_SUCCESS;
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_Disconnect_In(0x%08x,0x%08x,0x%04x)\r\n",
                              hDeviceContext,pCallContext,cid));

    if (! gpSDP) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_Disconnect_In: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    gpSDP->Lock();
    if (gpSDP->eStage != Connected) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_Disconnect_In: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    SDP_CONTEXT *pOwner = VerifyContext((SDP_CONTEXT*) hDeviceContext);
    Link *pLink = pOwner ? FindLink(cid) : NULL;

    if (!pOwner || !pLink) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_Disconnect_In: ERROR_INVALID_PARAMETER\r\n"));
        gpSDP->Unlock();
        return ERROR_INVALID_PARAMETER;
    }
    SVSUTIL_ASSERT(!pLink->fIncoming);

    pLink->DelRef();
    if (pLink->GetRefCount() > 0)  {
        IFDBG(DebugOut(DEBUG_SDP_PACKETS,L"sdp_Disconnect_In: cid 0x%04x has extra references\r\n",cid));
        NotifyDisconnect(pOwner,pCallContext,ERROR_SUCCESS);
    }
    else {
        IFDBG(DebugOut(DEBUG_SDP_PACKETS,L"sdp_Disconnect_In: cid 0x%04x has last ref removed, deleting\r\n",cid));
        Call *pCall = AllocCall(CALL_SDP_DISCONNECT,NULL, pOwner, pCallContext);

        // If are OOM we still do disconnect, but won't be able to call sdp_Disconnect_Out.
        iRes = SdpDisconnect(pLink,pCall);
        if (iRes != ERROR_SUCCESS && pCall && VerifyCall(pCall))
            DeleteCall(pCall);

        if (!pCall)
            iRes = ERROR_OUTOFMEMORY;
    }

    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_Disconnect_In: 0x%08x\r\n",iRes));
    gpSDP->Unlock();
    return iRes;
}

static int sdp_ServiceSearch_In(HANDLE hDeviceContext, void *pCallContext, unsigned short cid,
                                SdpQueryUuid* pUUIDs, unsigned short cMaxHandles)  {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_ServiceSearch_In(0x%08x,0x%08x,0x%08x,%d)\r\n",
                             hDeviceContext,pCallContext,pUUIDs,cMaxHandles));

    if (! gpSDP) { 
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceSearch_In: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        return ERROR_SERVICE_NOT_ACTIVE;
    }    

    gpSDP->Lock();
    if (gpSDP->eStage != Connected) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceSearch_In: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    SDP_CONTEXT *pContext = VerifyContext((SDP_CONTEXT*) hDeviceContext);
    Link        *pLink    = (pContext && cid) ? FindLink(cid) : NULL;

    // for VerifyCall to be legit, as soon as we take down link we must invalidate the call (or use a flag to do this,
    // let mem be freed by caller function).  Also currently this allows calls to be queued even if not connected,
    // need to make sure this case is handled both on connection and if connection fails.
    if (!pContext || (cid && (!pLink || pLink->fStage != UP))) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceSearch_In: ERROR_INVALID_PARAMETER\r\n"));
        gpSDP->Unlock();
        return ERROR_INVALID_PARAMETER;
    }

    Call             *pCall    = NULL;
    SdpConnection    *pSdpConn = NULL;
    SdpServiceSearch searchInfo;
    
    if (cid)  {
        pCall = AllocCall(CALL_SDP_SERVICE_SEARCH, pLink, pContext,pCallContext);
        if (!pCall)  {
            IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceSearch_In: ERROR_OUTOFMEMORY\r\n"));
            gpSDP->Unlock();
            return ERROR_OUTOFMEMORY;
        }
        pSdpConn = AllocSdpConnection(pCall);
    }
    else {
        pSdpConn = GetLocalSDPConn();
        if (!pSdpConn) {
            IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceSearch_In: ERROR_OUTOFMEMORY\r\n"));
            gpSDP->Unlock();
            return ERROR_OUTOFMEMORY;
        }
    }

    searchInfo.uuids       = pUUIDs;
    searchInfo.cMaxHandles = cMaxHandles;

    int iRes = SdpInterface::ServiceSearch(pSdpConn,&searchInfo,(cid == 0));
    if (iRes == STATUS_PENDING) {
        SVSUTIL_ASSERT(cid);
        iRes = ERROR_SUCCESS;
    }

    if (!cid)  {
        unsigned short cReturnedHandles;
        if (iRes == ERROR_SUCCESS)
            cReturnedHandles = pSdpConn->u.Client.ServiceSearch.totalRecordCount;
        else
            cReturnedHandles = 0;

        NotifyServiceSearch(pContext,pCallContext,iRes,cReturnedHandles,pSdpConn->pClientBuf);
        ResetSdpConnection(pSdpConn);
    }
    else if (iRes != ERROR_SUCCESS) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceSearch_In: SdpInterface::ServiceSearch fails, 0x%08x\r\n",iRes));
#if defined (DEBUG) || defined (_DEBUG)
        pCall->pSdpConnection->fClientNotified = TRUE;
#endif
        DeleteCall(pCall);
    }

    gpSDP->Unlock();
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_ServiceSearch_In: 0x%08x\r\n",iRes));
    return MapStatusToErrorCode(iRes);
}

static int sdp_AttributeSearch_In(HANDLE hDeviceContext, void *pCallContext, unsigned short cid, unsigned long recordHandle, SdpAttributeRange *pAttribRange, int numAttributes)  {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_AttributeSearch_In(0x%08x,0x%08x,0x%08x,0x%08x,0x%04x,0x%08x,%d )\r\n",
                                                    hDeviceContext,pCallContext,recordHandle,cid,pAttribRange,numAttributes));

    if (! gpSDP) { 
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_AttributeSearch_In: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        return ERROR_SERVICE_NOT_ACTIVE;
    }    

    gpSDP->Lock();
    if (gpSDP->eStage != Connected) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_AttributeSearch_In: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    SDP_CONTEXT *pContext = VerifyContext((SDP_CONTEXT*) hDeviceContext);
    Link        *pLink = (pContext && cid) ? FindLink(cid) : NULL;    

    if (!pContext || (cid && (!pLink || pLink->fStage != UP))) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_AttributeSearch_In: ERROR_INVALID_PARAMETER\r\n"));
        gpSDP->Unlock();
        return ERROR_INVALID_PARAMETER;
    }

    Call          *pCall    = NULL;
    SdpConnection *pSdpConn = NULL;
    SdpAttributeSearch searchInfo;
    
    if (cid)  {
        pCall = AllocCall(CALL_SDP_ATTRIBUTE_SEARCH, pLink, pContext,pCallContext);
        if (!pCall)  {
            IFDBG(DebugOut(DEBUG_ERROR,L"sdp_AttributeSearch_In: ERROR_OUTOFMEMORY\r\n"));
            gpSDP->Unlock();
            return ERROR_OUTOFMEMORY;
        }
        pSdpConn = AllocSdpConnection(pCall);
    }
    else {
        pSdpConn = GetLocalSDPConn();
        if (!pSdpConn) {
            IFDBG(DebugOut(DEBUG_ERROR,L"sdp_AttributeSearch_In: ERROR_OUTOFMEMORY\r\n"));
            gpSDP->Unlock();
            return ERROR_OUTOFMEMORY;
        }
    }

    searchInfo.range         = pAttribRange;
    searchInfo.numAttributes = numAttributes;
    searchInfo.recordHandle  = recordHandle;

    int iRes = SdpInterface::AttributeSearch(pSdpConn,&searchInfo,(cid == 0));
    if (iRes == STATUS_PENDING) {
        SVSUTIL_ASSERT(cid);
        iRes = ERROR_SUCCESS;
    }


    if (!cid)  {
        NotifyAttributeSearch(pContext,pCallContext,iRes,pSdpConn->pClientBuf,pSdpConn->cClientBuf);
        ResetSdpConnection(pSdpConn);
    }
    else if (iRes != ERROR_SUCCESS) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceSearch_In: SdpInterface::ServiceSearch fails, 0x%08x\r\n",iRes));
#if defined (DEBUG) || defined (_DEBUG)
        pCall->pSdpConnection->fClientNotified = TRUE;
#endif
        DeleteCall(pCall);
    }

    gpSDP->Unlock();
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_ServiceSearch_In: 0x%08x\r\n",iRes));
    return MapStatusToErrorCode(iRes);
}

static int sdp_ServiceAttributeSearch_In(HANDLE hDeviceContext, void *pCallContext, unsigned short cid,
                                         SdpQueryUuid* pUUIDs, SdpAttributeRange *pAttribRange, int numAttributes)  {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_ServiceAttributeSearch_In(0x%08x,0x%08x,0x%04x,0x%08x,0x%08x,%08x)\r\n",
                                                    hDeviceContext,pCallContext,cid,pUUIDs,pAttribRange,numAttributes));

    if (! gpSDP) { 
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceAttributeSearch_In: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        return ERROR_SERVICE_NOT_ACTIVE;
    }    

    gpSDP->Lock();
    if (gpSDP->eStage != Connected) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceAttributeSearch_In: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    SDP_CONTEXT *pContext = VerifyContext((SDP_CONTEXT*) hDeviceContext);
    Link        *pLink    = (pContext && cid) ? FindLink(cid) : NULL;    

    if (!pContext || (cid && (!pLink || pLink->fStage != UP))) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceAttributeSearch_In: ERROR_INVALID_PARAMETER\r\n"));
        gpSDP->Unlock();
        return ERROR_INVALID_PARAMETER;
    }

    Call          *pCall    = NULL;
    SdpConnection *pSdpConn = NULL;
    SdpServiceAttributeSearch searchInfo;
    
    if (cid)  {
        pCall = AllocCall(CALL_SDP_SERVICE_ATTRIBUTE_SEARCH, pLink, pContext,pCallContext);
        if (!pCall)  {
            IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceAttributeSearch_In: ERROR_OUTOFMEMORY\r\n"));
            gpSDP->Unlock();
            return ERROR_OUTOFMEMORY;
        }
        pSdpConn = AllocSdpConnection(pCall);
    }
    else {
        pSdpConn = GetLocalSDPConn();
        if (!pSdpConn) {
            IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceAttributeSearch_In: ERROR_OUTOFMEMORY\r\n"));
            gpSDP->Unlock();
            return ERROR_OUTOFMEMORY;
        }
    }

    searchInfo.uuids         = pUUIDs;
    searchInfo.range         = pAttribRange;
    searchInfo.numAttributes = numAttributes;
    
    int iRes = SdpInterface::ServiceAndAttributeSearch(pSdpConn,&searchInfo,(cid == 0));
    if (iRes == STATUS_PENDING) {
        SVSUTIL_ASSERT(cid);
        iRes = ERROR_SUCCESS;
    }

    if (!cid)  {
        NotifyServiceAttributeSearch(pContext,pCallContext,iRes,pSdpConn->pClientBuf,pSdpConn->cClientBuf);
        ResetSdpConnection(pSdpConn);
    }
    else if (iRes != ERROR_SUCCESS) {
        IFDBG(DebugOut(DEBUG_ERROR,L"sdp_ServiceSearch_In: SdpInterface::ServiceSearch fails, 0x%08x\r\n",iRes));
#if defined (DEBUG) || defined (_DEBUG)
        pCall->pSdpConnection->fClientNotified = TRUE;
#endif
        DeleteCall(pCall);
    }

    gpSDP->Unlock();
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_ServiceAttributeSearch_In: 0x%08x\r\n",iRes));
    return MapStatusToErrorCode(iRes);
}

static int check_io_control_parms(int cInBuffer,char *pInBuffer,int cOutBuffer,char *pOutBuffer,int *pcDataReturned,char *pSpace,int cSpace) {
    --cSpace;

    __try {
        if (pcDataReturned)  {
            *pcDataReturned = 0;
            memset (pOutBuffer, 0, cOutBuffer);
        } else if (pOutBuffer || cOutBuffer)
            return FALSE;

        int i = 0;
        while (cInBuffer > 0) {
            pSpace[i = (i < cSpace) ? i + 1 : 0] = *pInBuffer++;
            --cInBuffer;
        }
    } __except(1) {
        return FALSE;
    }

    return TRUE;
}

static int sdp_ioctl (HANDLE hDeviceContext, int fSelector, int cInBuffer, char *pInBuffer, int cOutBuffer, char *pOutBuffer, int *pcDataReturned) {
    IFDBG(DebugOut (DEBUG_SDP_TRACE, L"SDP: sdp_ioctl\n"));

    char c;
    if (! check_io_control_parms (cInBuffer, pInBuffer, cOutBuffer, pOutBuffer, pcDataReturned, &c, 1)) {
        IFDBG(DebugOut (DEBUG_ERROR, L"sdp_ioctl returns ERROR_INVALID_PARAMETER (exception)\n"));
        return ERROR_INVALID_PARAMETER;
    }

    if (! gpSDP) {
        IFDBG(DebugOut (DEBUG_ERROR, L"sdp_ioctl returns ERROR_SERVICE_NOT_ACTIVE\n"));
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    gpSDP->Lock();
    if (! gpSDP->IsStackRunning()) {
        IFDBG(DebugOut (DEBUG_ERROR, L"sdp_ioctl returns ERROR_SERVICE_NOT_ACTIVE\n"));
        gpSDP->Unlock ();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    SDP_CONTEXT *pContext = VerifyContext ((SDP_CONTEXT *)hDeviceContext);
    if (! pContext) {
        IFDBG(DebugOut (DEBUG_ERROR, L"sdp_ioctl returns ERROR_INVALID_HANDLE\n"));
        gpSDP->Unlock ();
        return ERROR_INVALID_HANDLE;
    }

    int iRes = ERROR_INVALID_OPERATION;

    switch (fSelector) {
    case BTH_STACK_IOCTL_GET_CONNECTED:
        if ((cInBuffer == 0) && (cOutBuffer == 4)) {
            iRes = ERROR_SUCCESS;

            int iCount = (gpSDP->eStage == Connected);

            pOutBuffer[0] = iCount & 0xff;
            pOutBuffer[1] = (iCount >> 8) & 0xff;
            pOutBuffer[2] = (iCount >> 16) & 0xff;
            pOutBuffer[3] = (iCount >> 24) & 0xff;
            *pcDataReturned = 4;
        } else
            iRes = ERROR_INVALID_PARAMETER;
        break;
    }


    IFDBG(DebugOut (DEBUG_SDP_TRACE, L"sdp_ioctl exits with code 0x%08x\n", iRes));
    gpSDP->Unlock();
    return iRes;
}

static int sdp_abort_call(HANDLE hDev, void *pCallContext)  {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"sdp_abort_call(0x%08x, 0x%08x)\r\n",hDev,pCallContext));
        
    if (! gpSDP)
        return ERROR_SERVICE_NOT_ACTIVE;

    gpSDP->Lock();

    SDP_CONTEXT *pOwner = VerifyContext((SDP_CONTEXT *)hDev);
    if (! pOwner) { 
        gpSDP->Unlock();
        return ERROR_INVALID_PARAMETER;
    }

    Call *pTrav = gpSDP->pCalls;
    while (pTrav && ((pTrav->pOwner != pOwner) || (pTrav->pCallContext != pCallContext)))
        pTrav = pTrav->pNext;

    if (! pTrav) {
        gpSDP->Unlock();
        return ERROR_INVALID_PARAMETER;
    }

    SVSHandle hContext = pTrav->hCallContext;    

    gpSDP->Unlock();

    return sdp_lCallAborted((LPVOID)hContext,ERROR_OPERATION_ABORTED);
}


static int check_SDP_EstablishDeviceContext_parms
(
SDP_CALLBACKS           *pCall,             /* IN */
SDP_INTERFACE           *pInt,              /* IN */
HANDLE                    *phDeviceContext    /* IN */
)  {
    __try {
        memset(pInt, 0, sizeof(*pInt));
        *phDeviceContext = NULL;

        SDP_CALLBACKS c;
        memcpy(&c, pCall, sizeof(c));
    } __except(1) {
        return FALSE;
    }

    return TRUE;
}

int SDP_EstablishDeviceContext
(
void                    *pUserContext,        /* IN */
SDP_EVENT_INDICATION    *pInd,              /* IN */
SDP_CALLBACKS           *pCall,             /* IN */
SDP_INTERFACE           *pInt,              /* OUT */
HANDLE                    *phDeviceContext    /* OUT */
)
{
    IFDBG(DebugOut(DEBUG_SDP_TRACE, L"SDP_EstablishDeviceContext()\r\n"));
    if ( ! check_SDP_EstablishDeviceContext_parms(pCall, pInt, phDeviceContext)) {
        IFDBG(DebugOut(DEBUG_WARN, L"check_SDP_EstablishDeviceContext_parms returns ERROR_INVALID_PARAMETER(exception)\r\n"));
        return ERROR_INVALID_PARAMETER;
    }

    if (! gpSDP)
        return ERROR_SERVICE_NOT_ACTIVE;

    gpSDP->Lock();
    if (!gpSDP->IsStackRunning()) {
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    SDP_CONTEXT *pContext = new SDP_CONTEXT;
    if (!pContext)  {
        gpSDP->Unlock();
        IFDBG(DebugOut(DEBUG_ERROR,L"SDP_EstablishDeviceContext: ERROR_OUTOFMEMORY\r\n"));
        return ERROR_OUTOFMEMORY;
    }
    
    pContext->c  = *pCall;
    pContext->ei = *pInd;
    pContext->pNext  = gpSDP->pContexts;
    pContext->pUserContext = pUserContext;
    gpSDP->pContexts = pContext;
    *phDeviceContext = pContext;

    pInt->sdp_AddRecord                 = sdp_AddRecord;
    pInt->sdp_RemoveRecord              = sdp_RemoveRecord;
    pInt->sdp_ServiceSearch_In          = sdp_ServiceSearch_In;
    pInt->sdp_Disconnect_In             = sdp_Disconnect_In;
    pInt->sdp_Connect_In                = sdp_Connect_In;
    pInt->sdp_AttributeSearch_In        = sdp_AttributeSearch_In;
    pInt->sdp_ServiceAttributeSearch_In = sdp_ServiceAttributeSearch_In;
    pInt->sdp_ioctl                     = sdp_ioctl;
    pInt->sdp_AbortCall                 = sdp_abort_call;

    IFDBG(DebugOut(DEBUG_SDP_TRACE, L"SDP_EstablishDeviceContext: ERROR_SUCCESS\r\n"));
    gpSDP->Unlock();
    return ERROR_SUCCESS;
}

int SDP_CloseDeviceContext(HANDLE hDeviceContext) {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"SDP_CloseDeviceContext(0x%08x)\r\n",hDeviceContext));
    if (! gpSDP) {
        IFDBG(DebugOut(DEBUG_WARN, L"SDP_CloseDeviceContext returns ERROR_SERVICE_NOT_ACTIVE\n"));
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    gpSDP->Lock();
    if (!gpSDP->IsStackRunning()) {
        IFDBG(DebugOut(DEBUG_WARN, L"SDP_CloseDeviceContext returns ERROR_SERVICE_NOT_ACTIVE\n"));
        gpSDP->Unlock();
        return ERROR_SERVICE_NOT_ACTIVE;
    }

    int iRes = ERROR_SUCCESS;
    while (gpSDP && gpSDP->IsStackRunning()) {
        SDP_CONTEXT *pOwner = VerifyContext((SDP_CONTEXT *) hDeviceContext);

        if (!pOwner) {
            iRes = ERROR_NOT_FOUND;
            break;
        }

        if (pOwner->GetRefCount() > 1)  {
            IFDBG(DebugOut (DEBUG_SDP_TRACE, L"Waiting for ref count in SDP_CloseDeviceContext\n"));
            gpSDP->Unlock();
            Sleep(100);
            gpSDP->Lock();
            continue;
        }

        Call *pCall = gpSDP->pCalls;
        while (pCall && (pCall->pOwner != pOwner))
            pCall = pCall->pNext;

        if (pCall) {
            if (VerifyLink(pCall->pLink))
                SdpDisconnect(pCall->pLink,NULL);
            continue;
        }

        if (gpSDP->pContexts == pOwner)
            gpSDP->pContexts = pOwner->pNext;
        else {
            SDP_CONTEXT *pParent = gpSDP->pContexts;
            while (pParent && (pParent->pNext != pOwner))
                pParent = pParent->pNext;
            pParent->pNext = pOwner->pNext;
        }
        delete pOwner;
        break;
    }

    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"SDP_CloseDeviceContext returns 0x%08x\r\n",iRes));
    gpSDP->Unlock();
    return iRes;
}

// Analogous to NT.  In NT this function is implemented in user mode,
// stubbed out in kernel mode.
void SdpReleaseContainer(ISdpNodeContainer *container) 
{
}

typedef struct {
    GUID    Protocol;
    BOOL    Found;
    BOOL    ParamFound;
    ULONG   Param;
} PROTOCOL_WALK_CONTEXT, *PPROTOCOL_WALK_CONTEXT;

static NTSTATUS
ProtocolListWalkFunc(
    IN      PVOID   Context,
    IN      UCHAR   DataType,
    IN      ULONG   DataSize,
    IN      PUCHAR  Data,
    IN      ULONG   DataStorageSize
    )
{
    PPROTOCOL_WALK_CONTEXT pContext = (PPROTOCOL_WALK_CONTEXT) Context;
    switch (DataType)
    {
        case SDP_TYPE_UUID:
            if (!pContext->Found)
            {
                UCHAR uuid[sizeof(GUID)];
                switch (DataSize)
                {
                    case sizeof(USHORT):
                    {
                        SdpRetrieveUint16(Data, (PUSHORT)uuid);
                        *(PUSHORT)uuid = SdpByteSwapUint16(*(PUSHORT)uuid);
                        break;
                    }
                    case sizeof(ULONG):
                    {
                        SdpRetrieveUint32(Data, (PULONG)uuid);
                        *(PULONG)uuid = SdpByteSwapUint32(*(PULONG)uuid);
                        break;
                    }
                    case sizeof(GUID):
                    {
                        SdpRetrieveUuid128(Data, (LPGUID)uuid);
                        SdpByteSwapUuid128((LPGUID)uuid, (LPGUID)uuid);
                        break;
                    }
                    default:
                        ASSERT(!"Bad data size for UUID\n");
                        break;
                }
                if (RtlEqualMemory(&pContext->Protocol,
                                   uuid,
                                   DataSize))
                {
                    pContext->Found = TRUE;
                }
            }
            else
            {
                // If we've found it, this is the next protocol, and we don't care.
                // Returning failure will stop the walk.
                return STATUS_UNSUCCESSFUL;
            }
            break;
        case SDP_TYPE_INT:
        case SDP_TYPE_UINT:
            if (pContext->Found)
            {
                pContext->ParamFound = TRUE;
                switch (DataSize)
                {
                    case 1:
                        pContext->Param = *(PUCHAR)Data;
                        break;
                    case 2:
                        SdpRetrieveUint16(Data, (PUSHORT)&pContext->Param);
                        pContext->Param = SdpByteSwapUint16((USHORT)pContext->Param);
                        break;
                    case 4:
                        SdpRetrieveUint32(Data, (PULONG)&pContext->Param);
                        pContext->Param = SdpByteSwapUint32(pContext->Param);
                        break;

                    default:
                        pContext->ParamFound = FALSE;  // Bad size
                        break;
                }

                return STATUS_UNSUCCESSFUL; // stop walking
            }
            break;
        default:
            break;
    }
    return STATUS_SUCCESS;
}

int GetProtocolCidFromResponse(unsigned char *pInputBuffer, DWORD cOutBuf, const GUID *pProtocolUUID, unsigned long *pbtChannel) {
    int iErr               = ERROR_NOT_FOUND;
    PROTOCOL_WALK_CONTEXT  protContext;
    unsigned char          *pAttrib;
    unsigned long          ulAttribLen;
    unsigned char          *pCurrentSequence;
    unsigned long          ulCurrentSequenceLen;
    unsigned char          type, sizeIndex;
    unsigned long          StorageSize;

    SVSUTIL_ASSERT((int)cOutBuf >= 0);

    if (cOutBuf == 0 || (ERROR_SUCCESS != ValidateStream(pInputBuffer,cOutBuf,NULL,NULL,NULL)))
        return ERROR_NOT_FOUND;

    memcpy(&protContext.Protocol,pProtocolUUID,sizeof(GUID));    
    SdpRetrieveHeader(pInputBuffer, type, sizeIndex);
    if (type != SDP_TYPE_SEQUENCE && type != SDP_TYPE_ALTERNATIVE)
        return ERROR_NOT_FOUND;

    SdpRetrieveVariableSize(pInputBuffer,sizeIndex,&ulCurrentSequenceLen,&StorageSize);
    ulCurrentSequenceLen = StorageSize + 1;
    pCurrentSequence     = pInputBuffer;

    while (1) {
        pCurrentSequence += ulCurrentSequenceLen;
        SVSUTIL_ASSERT(pCurrentSequence - pInputBuffer <= (int)cOutBuf);
        
        if (pCurrentSequence - pInputBuffer >= (int)cOutBuf)
            break;
        
        SdpRetrieveHeader(pCurrentSequence, type, sizeIndex);
        if (type != SDP_TYPE_SEQUENCE && type != SDP_TYPE_ALTERNATIVE)
            break;

        SdpRetrieveVariableSize(pCurrentSequence+1,sizeIndex,&ulCurrentSequenceLen,&StorageSize);
        ulCurrentSequenceLen += StorageSize + 1;

        iErr = SdpFindAttributeInStream(pCurrentSequence,ulCurrentSequenceLen,SDP_ATTRIB_PROTOCOL_DESCRIPTOR_LIST,&pAttrib,&ulAttribLen);

        if (ERROR_SUCCESS != iErr || !pAttrib || !ulAttribLen)
        {
            iErr = ERROR_NOT_FOUND;
            // We only output current sequence, not entire record, for succintness.
            IFDBG(DebugOut(DEBUG_WARN,L"SDP: cannot find ProtocolDescriptor in ServAttribSearch because remote server returned invalid response.  Invalid RecordSequence follows:"));
            IFDBG(DumpBuff(DEBUG_WARN,pCurrentSequence,ulCurrentSequenceLen));
            continue;
        }

        protContext.Found      = FALSE;
        protContext.ParamFound = FALSE;
        protContext.Param      = 0;

        SdpWalkStream(pAttrib,ulAttribLen,ProtocolListWalkFunc,&protContext);

        if (protContext.ParamFound && protContext.ParamFound)
        {
            *pbtChannel = protContext.Param;
            iErr = ERROR_SUCCESS;
            break;
        }
    }

    return iErr;
}


// If a process has created SDP records and its being terminated, we receive
// a notification and will run through SDP database removing any SDP records 
// that it created.
void SdpRemoveRecordsOnProcessExit(HANDLE hDyingProc) {
    IFDBG(DebugOut(DEBUG_SDP_TRACE,L"SdpRemoveRecordsFromProcess(0x%08x)\r\n",hDyingProc));

    if (! gpSDP) {
        IFDBG(DebugOut(DEBUG_WARN,L"SdpRemoveRecordsOnProcessExit: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        return;
    }

    gpSDP->Lock();
    if (!pSdpDB) {
        gpSDP->Unlock();
        IFDBG(DebugOut(DEBUG_WARN,L"SdpRemoveRecordsOnProcessExit: ERROR_SERVICE_NOT_ACTIVE\r\n"));
        return;
    }

    pSdpDB->RemoveRecordsAssociatedWithProcess(hDyingProc);

    gpSDP->Unlock();
    return;
}



#if defined(BTH_CONSOLE)
int sdp_ProcessConsoleCommand(WCHAR *pszCommand)  {
    if (! gpSDP) 
        return ERROR_SERVICE_NOT_ACTIVE;

    int iRes = ERROR_SUCCESS;
    gpSDP->Lock();

    __try {
        if (wcsicmp(pszCommand, L"help") == 0) {
            DebugOut(DEBUG_OUTPUT,L"SDP Commands:\r\n");
            DebugOut(DEBUG_OUTPUT,L"    No commands implemented yet\r\n");
        }
        else
            iRes = ERROR_NOT_FOUND;
    } 
    __except(1) {
        DebugOut(DEBUG_OUTPUT, L"ERROR! -- Exception during command processing!\n");
    }
    gpSDP->Unlock();

    return iRes;
}
#endif // BTH_CONSOLE
