⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tapi.cpp

📁 Windows CE操作系统中适用的蓝牙驱动程序
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//

#include <windows.h>
#include <tapi.h>
#include <list.hxx>

#include "btagpub.h"
#include "btagnetwork.h"

#include "svsutil.hxx"


#define ZONE_NETWORK        DEBUGZONE(5)
#define ZONE_WARN           DEBUGZONE(14)
#define ZONE_ERROR          DEBUGZONE(15)


#define DEFAULT_TAPI_CELL_LINE          L"Cellular Line"
#define TAPI_API_LOW_VERSION    0x00020000
#define TAPI_API_HIGH_VERSION   0x00020000
#define TAPI_COMMAND_TIMEOUT    8000
#define MIN_RING_INTERVAL       3500

typedef enum _NETWORK_STATE {
    NETWORK_STATE_DISCONNECTED = 0x00,  // No calls
    NETWORK_STATE_CONNECTED = 0x01,     // Either outgoing or incoming call has just connected
    NETWORK_STATE_RINGING = 0x02,       // Incoming call is in ringing state
    NETWORK_STATE_ONHOLD = 0x03,        // No active calls but one on hold
    NETWORK_STATE_INCALL = 0x04         // Active call
} NETWORK_STATE;

typedef struct _TapiCall {
    ULONG ulCallId;                     // ID for call
    USHORT usCallType;                  // Type of call
    HANDLE hWaitEvent;                  // For synchronous calls, event to unblock the wait
    DWORD dwWaitResponse;               // Response to return for synchronous calls
} TapiCall;

typedef ce::list<TapiCall, ce::free_list<5> >    TapiCallList;

class TapiData : public SVSRefObj {
public:
    DWORD           dwState;            // State of TAPI Network Module (see NETWORK_STATE structure)
    HLINEAPP        hLineApp;           // Handle to TAPI Line App
    HLINE           hLine;              // Handle to TAPI Line
    HCALL           hCall;              // Handle to Active TAPI Call
    HCALL           hHoldCall;          // Handle to TAPI Call on hold
    HCALL           hOfferingCall;      // Handle to TAPI Call in offering state
    LPLINECALLINFO  pCallInfo;          // Pointer to TAPI Call Info struct
    DWORD           cbCallInfo;         // Size of buffer (in bytes) pointed to by pCallInfo
    DWORD           dwAPIVersion;       // Version of TAPI API
    HANDLE          hTapiEvent;         // Event that gets signalled when a TAPI message is present
    DWORD           dwWaitTimeout;      // Timeout (in milliseconds) for blocking TAPI commands
    DWORD           dwLastRingTime;     // Last time a RING was sent to headset
    HANDLE          hThread;            // Handle to TAPI event thread

    TapiCallList    CallList;           // List of TAPI calls
        
    BOOL            fShutdown : 1;      // Set when TAPI Network Module is starting to shutdown
    BOOL            fCallIdNotify : 1;  // Set if caller id has been notified to AG
    
    CRITICAL_SECTION csLock;            // Critical Section for Network Module

    TapiData()
    {
        dwState = NETWORK_STATE_DISCONNECTED;

        hLineApp = NULL;
        hLine = NULL;
        hCall = NULL;
        hHoldCall = NULL;
        hOfferingCall = NULL;
        pCallInfo = NULL;
        cbCallInfo = NULL;        
        dwAPIVersion = NULL;
        hTapiEvent = NULL;        
        hThread = NULL;

        dwWaitTimeout = 0;
        dwLastRingTime = 0;

        fShutdown = FALSE;
        fCallIdNotify = FALSE;

        InitializeCriticalSection(&csLock);
    }

    ~TapiData()
    {
        DeleteCriticalSection(&csLock);
    }
};

static TapiData g_Data;
LONG g_Inited = 0;


void BthAGNetworkDeinit(void);


inline void TapiLock(void)
{
    EnterCriticalSection(&g_Data.csLock);    
}

inline void TapiUnlock(void)
{
    LeaveCriticalSection(&g_Data.csLock);
}

//
// Find an outstanding TAPI call
//
TapiCall* FindTapiCall(DWORD dwId)
{
    for (TapiCallList::iterator it = g_Data.CallList.begin(), itEnd = g_Data.CallList.end(); it != itEnd;) {
        if (it->ulCallId == dwId) {
            return &(*it);
        }

        ++it;
    }

    return NULL;
}


//
// Add a TAPI call to the list
//
DWORD AddTapiCall(DWORD dwId, USHORT usType, HANDLE hWaitEvent)
{
    DWORD dwRetVal = ERROR_SUCCESS;
    
    TapiCall call;
    memset(&call, 0, sizeof(call));    
    call.ulCallId = dwId;
    call.usCallType = usType;
    call.hWaitEvent = hWaitEvent;

    if (false == g_Data.CallList.push_front(call)) {
        dwRetVal = ERROR_OUTOFMEMORY;
    }

    return dwRetVal;
}


//
// Remove a TAPI call from the list
//
DWORD DeleteTapiCall(DWORD dwId)
{
    DWORD dwRetVal = ERROR_NOT_FOUND;

    for (TapiCallList::iterator it = g_Data.CallList.begin(), itEnd = g_Data.CallList.end(); it != itEnd;) {
        if (it->ulCallId == dwId) {
            g_Data.CallList.erase(it);
            dwRetVal = ERROR_SUCCESS;
            break;
        }

        ++it;
    }

    return dwRetVal;
}

//
// This function finds the TSP line for cellular.
//
DWORD FindTSPLine(WCHAR* szName, unsigned int cLines, unsigned int* puiCellLine)
{
    DWORD dwRetVal = ERROR_NOT_FOUND;
 
    for (DWORD i = 0; i < (DWORD) cLines; i++) {
        LINEEXTENSIONID lineExtensionId;
        DWORD dwVersion;
                
        if (ERROR_SUCCESS == lineNegotiateAPIVersion(g_Data.hLineApp, i, TAPI_API_LOW_VERSION, TAPI_API_HIGH_VERSION, &dwVersion, &lineExtensionId)) {
            LINEDEVCAPS LineDevCaps;
            LineDevCaps.dwTotalSize = sizeof(LineDevCaps);
            if (ERROR_SUCCESS == lineGetDevCaps(g_Data.hLineApp, i, dwVersion, 0, &LineDevCaps)) {
                LINEDEVCAPS* pLineDevCaps = (LINEDEVCAPS*) new BYTE[LineDevCaps.dwNeededSize];
                if (pLineDevCaps) {
                    pLineDevCaps->dwTotalSize = LineDevCaps.dwNeededSize;
                    if (ERROR_SUCCESS == lineGetDevCaps(g_Data.hLineApp, i, dwVersion, 0, pLineDevCaps)) {
                        if (0 == wcscmp((WCHAR*)((BYTE*)pLineDevCaps + pLineDevCaps->dwLineNameOffset), szName)) {
                            dwRetVal = ERROR_SUCCESS;
                            *puiCellLine = i;
                            g_Data.dwAPIVersion = dwVersion;
                            delete[] pLineDevCaps;
                            break;
                        }
                    }

                    delete[] pLineDevCaps;
                }
            }
        }
    }

    return dwRetVal;
}

//
// This function opens the cellular TAPI line.
//
DWORD OpenLine(unsigned int uiLine)
{
    DWORD dwRetVal = ERROR_SUCCESS;
    
    dwRetVal = lineOpen(g_Data.hLineApp, 
        uiLine, 
        &g_Data.hLine, 
        g_Data.dwAPIVersion, 
        0, 
        uiLine, 
        LINECALLPRIVILEGE_MONITOR|LINECALLPRIVILEGE_OWNER,
        LINEMEDIAMODE_INTERACTIVEVOICE,
        NULL);
    if (ERROR_SUCCESS != dwRetVal) {
        ASSERT(0);
        goto exit;
    }
    
    

exit:
    return dwRetVal;
}


//
// This functions gets the current call info
//
DWORD GetCallInfo(HCALL hCall)
{
    LRESULT lResult;
    DWORD dwRetVal = ERROR_SUCCESS;
    
    if ((0 == g_Data.cbCallInfo) || (!g_Data.pCallInfo)) {
        // First time, need to alloc enough memory
        const DWORD c_dwFirstSize = sizeof(LINECALLINFO) + 256;
        g_Data.pCallInfo = (LPLINECALLINFO) new BYTE[c_dwFirstSize];
        if (! g_Data.pCallInfo) {
            dwRetVal = ERROR_OUTOFMEMORY;
            DEBUGMSG(ZONE_ERROR, (L"BTAGSVC: Out of memory.\n"));
            goto exit;
        }
        g_Data.cbCallInfo = c_dwFirstSize;
    }

    g_Data.pCallInfo->dwTotalSize = g_Data.cbCallInfo;
    lResult = lineGetCallInfo(hCall, g_Data.pCallInfo);
    if (LINEERR_NOMEM == lResult) {
        g_Data.cbCallInfo = g_Data.pCallInfo->dwNeededSize;
        // Need more memory, free and realloc.
        delete[] g_Data.pCallInfo;                
        g_Data.pCallInfo = (LPLINECALLINFO) new BYTE[g_Data.cbCallInfo];
        if (! g_Data.pCallInfo) {
            g_Data.cbCallInfo = 0;
            dwRetVal = ERROR_OUTOFMEMORY;
            DEBUGMSG(ZONE_ERROR, (L"BTAGSVC: Out of memory.\n"));
            goto exit;
        }        
    }
    else if (0 != lResult) {
        dwRetVal = (DWORD) lResult;
        goto exit;
    }

exit:
    return dwRetVal;
}


//
// This function puts the active call on hold and blocks until this operation is complete.
//
DWORD BlockingTapiCall(LONG lCallId)
{
    DWORD dwRetVal = ERROR_SUCCESS;
    DWORD dwTimeout = g_Data.dwWaitTimeout;
    DWORD dwErr;
    
    HANDLE h = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (! h) {
        dwRetVal = GetLastError();
        DEBUGMSG(ZONE_ERROR, (L"BTAGSVC: BlockingTapiCall - CreateEvent Failed: %d\n", dwRetVal));
        goto exit;
    }
    
    dwRetVal = AddTapiCall(lCallId, 0, h);
    if (ERROR_SUCCESS != dwRetVal) {
        goto exit;
    }

    g_Data.AddRef();
    TapiUnlock();
    
    dwErr = WaitForSingleObject(h, dwTimeout);
    
    TapiLock();
    g_Data.DelRef();

    if (WAIT_OBJECT_0 == dwErr) {
        TapiCall* pCall = FindTapiCall(lCallId);
        if (! pCall) {
            dwRetVal = ERROR_NOT_FOUND;
            goto exit;
        }
        
        dwRetVal = pCall->dwWaitResponse;
        if (dwRetVal == ERROR_SUCCESS) {
            // Call state change should have already updated this
            ASSERT(g_Data.hCall == NULL);   
            ASSERT(g_Data.dwState == NETWORK_STATE_ONHOLD);
        }
    }
    else {
        // Timeout or unexpected failure
        dwRetVal = ERROR_TIMEOUT;
    }

exit:
    if (h) {
        CloseHandle(h);
    }
    
    return dwRetVal;
}


//
// This function is called when TAPI device state has changed
//
void TapiEventDevStateChange(LPLINEMESSAGE pMsg)
{
    if (LINEDEVSTATE_RINGING == pMsg->dwParam1) {
        // Ringing cycle
        DEBUGMSG(ZONE_NETWORK, (L"BTAGSVC: Network - RING.\n"));
        g_Data.dwState = NETWORK_STATE_RINGING;

        DWORD dwCurrTime = GetTickCount();
        if (!g_Data.dwLastRingTime || ((dwCurrTime - g_Data.dwLastRingTime) >= MIN_RING_INTERVAL)) {
            g_Data.dwLastRingTime = dwCurrTime;
            
            TapiUnlock();
            BthAGOnNetworkEvent(NETWORK_EVENT_RING, NULL, 0);
            TapiLock();
        }
    }
}


//
// This function is called when Tapi Call Info has changed
//
void TapiEventCallInfo(LPLINEMESSAGE pMsg) 
{
    if (!g_Data.fCallIdNotify && (pMsg->dwParam1 & LINECALLINFOSTATE_CALLERID)) {
        HCALL hCall = (HCALL) pMsg->hDevice;

        DEBUGMSG(ZONE_NETWORK, (L"BTAGSVC: Network - Need to indicate Caller Id.\n"));
        
        CHAR szNumber[MAX_PHONE_NUMBER];
        szNumber[0] = 0; // Null terminate

        if (ERROR_SUCCESS != GetCallInfo(hCall)) {
            DEBUGMSG(ZONE_WARN, (L"BTAGSVC: Query for caller id failed.\n"));
            return;
        }

        if (LINEMEDIAMODE_INTERACTIVEVOICE != g_Data.pCallInfo->dwMediaMode) {
            DEBUGMSG(ZONE_WARN, (L"BTAGSVC: Call is of wrong media mode: 0x%X\n", g_Data.pCallInfo->dwMediaMode));
            return;
        }                    

        if (g_Data.pCallInfo->dwCallerIDOffset > 0) {
            DWORD cbWritten = WideCharToMultiByte(CP_ACP, 0, 
                                    (WCHAR*)((BYTE*)g_Data.pCallInfo + g_Data.pCallInfo->dwCallerIDOffset), 
                                    g_Data.pCallInfo->dwCallerIDSize/2,
                                    szNumber,
                                    MAX_PHONE_NUMBER,
                                    NULL, NULL);

            g_Data.fCallIdNotify = TRUE;
                        
            TapiUnlock();
            BthAGOnNetworkEvent(NETWORK_EVENT_CALL_INFO, (LPVOID)szNumber, cbWritten);
            TapiLock();
        }
    }
}


//
// This function is called when an asychronous TAPI command completes
//
void TapiEventLineReply(LPLINEMESSAGE pMsg)
{
    TapiCall* pCall = FindTapiCall(pMsg->dwParam1);
    if (! pCall) {
        DEBUGMSG(ZONE_NETWORK, (L"BTAGSVC: Network - TapiEventLineReply: Reply for non-existent call.\n"));
        ASSERT(0);
        return;
    }

    if (pCall->hWaitEvent) {
        // We are internally making a TAPI call and waiting
        // for the response before returning to AG.
        
        DEBUGMSG(ZONE_NETWORK, (L"BTAGSVC: Network - TapiEventLineReply: Reply for synchronous call.\n"));
        
        pCall->dwWaitResponse = pMsg->dwParam2;
        SetEvent(pCall->hWaitEvent);
    }
    else {
        // AG is making async network call.

        ASSERT(pCall->ulCallId > 0);
        ASSERT(pCall->usCallType);
        
        DWORD dwResponse = pMsg->dwParam2;
        if (dwResponse != ERROR_SUCCESS) {
            TapiUnlock();

            NetworkCallFailedInfo CallFailInfo;
            memset(&CallFailInfo, 0, sizeof(CallFailInfo));

            CallFailInfo.usCallType = pCall->usCallType;
            CallFailInfo.dwStatus = dwResponse;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -