📄 tapi.cpp
字号:
//
// 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 + -