/**************************************************************************** * * $Archive: S:/STURGEON/SRC/Q931/VCS/hcall.cpv $ * * INTEL Corporation Prorietary Information * * This listing is supplied under the terms of a license agreement * with INTEL Corporation and may not be copied nor disclosed except * in accordance with the terms of that agreement. * * Copyright (c) 1993-1996 Intel Corporation. * * $Revision: 2.7.1.0 $ * $Date: 20 Jun 1997 14:12:08 $ * $Author: MANDREWS $ * * Deliverable: * * Abstract: * * * Notes: * ***************************************************************************/ #pragma warning ( disable : 4100 4115 4201 4214 4514 4702 4710 ) #include #include #include #include #include #include #include "q931asn.h" #include "isrg.h" #include "common.h" #include "q931.h" #include "hcall.h" #include "utils.h" #include "tstable.h" #include "provider.h" #ifdef UNICODE_TRACE // We include this header to fix problems with macro expansion when Unicode is turned on. #include "unifix.h" #endif static BOOL bCallListCreated = FALSE; // Pointer to our global table. Note that this table replaces the previous // linked-list implementation. TSTable* gpCallObjectTable = NULL; // Our call back function for enumerating the table when we want to tear down // all existing calls DWORD Q931HangUpAllCalls(P_CALL_OBJECT pCallObject, LPVOID context); // Our call back function for determining if a timer has expired DWORD Q931CheckForTimeout(P_CALL_OBJECT pCallObject, LPVOID context); // Our call back function for determining if a timer has expired DWORD Q931CallObjectFind(P_CALL_OBJECT pCallObject, LPVOID context); static struct { WORD wCRV; // Call Reference Value (0..7FFF). CRITICAL_SECTION Lock; } CRVSource; static struct { BOOL bBusy; DWORD dwTimerCount; DWORD dwTicks301; DWORD dwTicks303; UINT_PTR uTimerId; CRITICAL_SECTION Lock; } Q931GlobalTimer = {0}; typedef struct { BOOL bFound; WORD wCRV; PCC_ADDR pPeerAddr; HQ931CALL hQ931Call; } Q931CALLOBJKEY, *PQ931CALLOBJKEY; //==================================================================================== // // PRIVATE FUNCTIONS // //==================================================================================== //==================================================================================== //==================================================================================== CS_STATUS Q931CRVNew( WORD *pwCRV) { EnterCriticalSection(&(CRVSource.Lock)); CRVSource.wCRV = (WORD)((CRVSource.wCRV + 1) & 0x7fff); if (CRVSource.wCRV == 0) { CRVSource.wCRV = 1; } *pwCRV = CRVSource.wCRV; LeaveCriticalSection(&(CRVSource.Lock)); return CS_OK; } //==================================================================================== // // PUBLIC FUNCTIONS // //==================================================================================== //==================================================================================== //==================================================================================== CS_STATUS CallListCreate() { if (bCallListCreated == TRUE) { ASSERT(FALSE); return CS_DUPLICATE_INITIALIZE; } // list creation is not protected against multiple threads because it is only // called when a process is started, not when a thread is started. // Create the call object table. We will initially create 30 entries. The // TSTable can automatically grow, so no worrying about running out of entries. gpCallObjectTable = new TSTable (30); if (gpCallObjectTable == NULL || gpCallObjectTable->IsInitialized() == FALSE) { return CS_NO_MEMORY; } __try { CRVSource.wCRV = (WORD) (time(NULL) & 0x7fff); InitializeCriticalSectionAndSpinCount(&(CRVSource.Lock),H323_SPIN_COUNT); Q931GlobalTimer.dwTicks301 = Q931_TICKS_301; Q931GlobalTimer.dwTicks303 = Q931_TICKS_303; InitializeCriticalSectionAndSpinCount(&(Q931GlobalTimer.Lock),H323_SPIN_COUNT); } __except ((GetExceptionCode() == STATUS_NO_MEMORY) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { // failure return CS_NO_MEMORY; } bCallListCreated = TRUE; return CS_OK; } //==================================================================================== // this routine assumes all of the events and sockets belonging to each object // are already destroyed. It just makes sure memory is cleaned up. //==================================================================================== CS_STATUS CallListDestroy() { if (bCallListCreated == FALSE) { ASSERT(FALSE); return CS_INTERNAL_ERROR; } // For all entries, hang up the calls gpCallObjectTable->EnumerateEntries(Q931HangUpAllCalls, NULL); // Get rid of the call object table delete gpCallObjectTable; gpCallObjectTable = NULL; DeleteCriticalSection(&(Q931GlobalTimer.Lock)); DeleteCriticalSection(&(CRVSource.Lock)); bCallListCreated = FALSE; return CS_OK; } //==================================================================================== //==================================================================================== void CallObjectFree(P_CALL_OBJECT pCallObject) { if (pCallObject->NonStandardData.sData.pOctetString != NULL) { Free(pCallObject->NonStandardData.sData.pOctetString); pCallObject->NonStandardData.sData.pOctetString = NULL; } if (pCallObject->VendorInfoPresent) { if (pCallObject->VendorInfo.pProductNumber != NULL) { Free(pCallObject->VendorInfo.pProductNumber); } if (pCallObject->VendorInfo.pVersionNumber != NULL) { Free(pCallObject->VendorInfo.pVersionNumber); } } Q931FreeAliasNames(pCallObject->pCallerAliasList); pCallObject->pCallerAliasList = NULL; Q931FreeAliasNames(pCallObject->pCalleeAliasList); pCallObject->pCalleeAliasList = NULL; Q931FreeAliasNames(pCallObject->pExtraAliasList); pCallObject->pExtraAliasList = NULL; Q931FreeAliasItem(pCallObject->pExtensionAliasItem); pCallObject->pExtensionAliasItem = NULL; Free(pCallObject); } //==================================================================================== //==================================================================================== CS_STATUS CallObjectCreate( PHQ931CALL phQ931Call, DWORD dwListenToken, DWORD dwUserToken, Q931_CALLBACK ConnectCallback, BOOL fIsCaller, CC_ADDR *pLocalAddr, // Local address on which channel is connected CC_ADDR *pPeerConnectAddr, // Address to which channel is connected CC_ADDR *pPeerCallAddr, // Address of opposite call end-point. CC_ADDR *pSourceAddr, // Address of this call end-point. CC_CONFERENCEID *pConferenceID, WORD wGoal, WORD wCallType, BOOL bCallerIsMC, char *pszDisplay, char *pszCalledPartyNumber, PCC_ALIASNAMES pCallerAliasList, PCC_ALIASNAMES pCalleeAliasList, PCC_ALIASNAMES pExtraAliasList, PCC_ALIASITEM pExtensionAliasItem, PCC_ENDPOINTTYPE pEndpointType, PCC_NONSTANDARDDATA pNonStandardData, // questionable! DWORD dwBandwidth, WORD wCRV) { P_CALL_OBJECT pCallObject = NULL; CS_STATUS status = CS_OK; CS_STATUS CopyStatus = CS_OK; DWORD dwIndex = 0; int rc = 0; // make sure the call list has been created. if (bCallListCreated == FALSE) { ASSERT(FALSE); return CS_INTERNAL_ERROR; } // validate all parameters for bogus values. if ((phQ931Call == NULL) || (ConnectCallback == NULL)) { ASSERT(FALSE); return CS_BAD_PARAM; } // set phQ931Call now, in case we encounter an error later. *phQ931Call = 0; pCallObject = (P_CALL_OBJECT)Malloc(sizeof(CALL_OBJECT)); if (pCallObject == NULL) { return CS_NO_MEMORY; } memset(pCallObject, 0, sizeof(CALL_OBJECT)); // create and init an oss world struct for each call object. This is // necessary to work in MT environments. rc = Q931_InitWorld(&pCallObject->World); if (rc != ASN1_SUCCESS) { Q931DBG((DBGERROR, "Q931_InitCoder() returned: %d ", rc)); return CS_SUBSYSTEM_FAILURE; } pCallObject->LocalAddr.bMulticast = FALSE; pCallObject->PeerConnectAddr.bMulticast = FALSE; pCallObject->PeerCallAddr.bMulticast = FALSE; pCallObject->SourceAddr.bMulticast = FALSE; if (wCRV == 0) { if (Q931CRVNew(&pCallObject->wCRV) != CS_OK) { CallObjectFree(pCallObject); return CS_INTERNAL_ERROR; } } else { pCallObject->wCRV = wCRV; } pCallObject->szDisplay[0] = '\0'; if (pszDisplay) { strcpy(pCallObject->szDisplay, pszDisplay); } pCallObject->szCalledPartyNumber[0] = '\0'; if (pszCalledPartyNumber) { strcpy(pCallObject->szCalledPartyNumber, pszCalledPartyNumber); } pCallObject->dwListenToken = dwListenToken; pCallObject->dwUserToken = dwUserToken; pCallObject->Callback = ConnectCallback; pCallObject->bCallState = CALLSTATE_NULL; pCallObject->fIsCaller = fIsCaller; if (pLocalAddr) { pCallObject->LocalAddr = *pLocalAddr; } if (pPeerConnectAddr) { pCallObject->PeerConnectAddr = *pPeerConnectAddr; } if (pPeerCallAddr) { pCallObject->PeerCallAddr = *pPeerCallAddr; pCallObject->PeerCallAddrPresent = TRUE; } else { pCallObject->PeerCallAddrPresent = FALSE; } if (pSourceAddr) { pCallObject->SourceAddr = *pSourceAddr; pCallObject->SourceAddrPresent = TRUE; } else { pCallObject->SourceAddrPresent = FALSE; } if (pConferenceID == NULL) { memset(&(pCallObject->ConferenceID), 0, sizeof(CC_CONFERENCEID)); } else { int length = min(sizeof(pConferenceID->buffer), sizeof(pCallObject->ConferenceID.buffer)); memcpy(pCallObject->ConferenceID.buffer, pConferenceID->buffer, length); } pCallObject->wGoal = wGoal; pCallObject->bCallerIsMC = bCallerIsMC; pCallObject->wCallType = wCallType; if (pNonStandardData != NULL) { pCallObject->NonStandardData = *pNonStandardData; if (pNonStandardData->sData.wOctetStringLength > 0) { pCallObject->NonStandardData.sData.pOctetString = (BYTE *) Malloc(pNonStandardData->sData.wOctetStringLength); if (pCallObject->NonStandardData.sData.pOctetString == NULL) { CallObjectFree(pCallObject); return CS_NO_MEMORY; } memcpy(pCallObject->NonStandardData.sData.pOctetString, pNonStandardData->sData.pOctetString, pNonStandardData->sData.wOctetStringLength); } pCallObject->NonStandardDataPresent = TRUE; } else { pCallObject->NonStandardDataPresent = FALSE; } CopyStatus = Q931CopyAliasNames(&(pCallObject->pCallerAliasList), pCallerAliasList); if (CopyStatus != CS_OK) { CallObjectFree(pCallObject); return CopyStatus; } CopyStatus = Q931CopyAliasNames(&(pCallObject->pCalleeAliasList), pCalleeAliasList); if (CopyStatus != CS_OK) { CallObjectFree(pCallObject); return CopyStatus; } CopyStatus = Q931CopyAliasNames(&(pCallObject->pExtraAliasList), pExtraAliasList); if (CopyStatus != CS_OK) { CallObjectFree(pCallObject); return CopyStatus; } CopyStatus = Q931CopyAliasItem(&(pCallObject->pExtensionAliasItem), pExtensionAliasItem); if (CopyStatus != CS_OK) { CallObjectFree(pCallObject); return CopyStatus; } pCallObject->bResolved = FALSE; pCallObject->VendorInfoPresent = FALSE; pCallObject->bIsTerminal = TRUE; pCallObject->bIsGateway = FALSE; if (pEndpointType != NULL) { PCC_VENDORINFO pVendorInfo = pEndpointType->pVendorInfo; if (pVendorInfo != NULL) { pCallObject->VendorInfoPresent = TRUE; pCallObject->VendorInfo = *(pVendorInfo); if (pVendorInfo->pProductNumber && pVendorInfo->pProductNumber->pOctetString && pVendorInfo->pProductNumber->wOctetStringLength) { memcpy(pCallObject->bufVendorProduct, pVendorInfo->pProductNumber->pOctetString, pVendorInfo->pProductNumber->wOctetStringLength); pCallObject->VendorInfo.pProductNumber = (CC_OCTETSTRING*) Malloc(sizeof(CC_OCTETSTRING)); if (pCallObject->VendorInfo.pProductNumber == NULL) { CallObjectFree(pCallObject); return CS_NO_MEMORY; } pCallObject->VendorInfo.pProductNumber->pOctetString = pCallObject->bufVendorProduct; pCallObject->VendorInfo.pProductNumber->wOctetStringLength = pVendorInfo->pProductNumber->wOctetStringLength; } else { pCallObject->VendorInfo.pProductNumber = NULL; } if (pVendorInfo->pVersionNumber && pVendorInfo->pVersionNumber->pOctetString && pVendorInfo->pVersionNumber->wOctetStringLength) { memcpy(pCallObject->bufVendorVersion, pVendorInfo->pVersionNumber->pOctetString, pVendorInfo->pVersionNumber->wOctetStringLength); pCallObject->VendorInfo.pVersionNumber = (CC_OCTETSTRING*) Malloc(sizeof(CC_OCTETSTRING)); if (pCallObject->VendorInfo.pVersionNumber == NULL) { CallObjectFree(pCallObject); return CS_NO_MEMORY; } pCallObject->VendorInfo.pVersionNumber->pOctetString = pCallObject->bufVendorVersion; pCallObject->VendorInfo.pVersionNumber->wOctetStringLength = pVendorInfo->pVersionNumber->wOctetStringLength; } else { pCallObject->VendorInfo.pVersionNumber = NULL; } } pCallObject->bIsTerminal = pEndpointType->bIsTerminal; pCallObject->bIsGateway = pEndpointType->bIsGateway; pCallObject->dwBandwidth = dwBandwidth; } Q931MakePhysicalID(&pCallObject->dwPhysicalId); // Insert the object into the table...if that doesn't work, blow away the object. if (gpCallObjectTable->CreateAndLock(pCallObject, &dwIndex) == FALSE) { CallObjectFree(pCallObject); return CS_INTERNAL_ERROR; } // Save the index as the handle (this makes it easier to find the object later). *phQ931Call = pCallObject->hQ931Call = (HQ931CALL) dwIndex; Q931DBG((DBGTRACE, "CallObjectCreate() -returned-> 0x%08x", dwIndex)); // Unlock the entry gpCallObjectTable->Unlock(dwIndex); return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS CallObjectDestroy( P_CALL_OBJECT pCallObject) { if (pCallObject == NULL) { ASSERT(FALSE); return CS_BAD_PARAM; } Q931DBG((DBGTRACE, "CallObjectDestroy(0x%08lx)", pCallObject->hQ931Call)); Q931_TermWorld(&pCallObject->World); // Since the caller must already have a lock on the object, remove the entry from // the table. We won't let the table delete the object as we want to clean up. if (gpCallObjectTable->Delete((DWORD) pCallObject->hQ931Call) == FALSE) { return CS_OK; } Q931StopTimer(pCallObject, Q931_TIMER_301); Q931StopTimer(pCallObject, Q931_TIMER_303); // Unlock the object gpCallObjectTable->Unlock((DWORD) pCallObject->hQ931Call); // Free up the call object CallObjectFree(pCallObject); return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS CallObjectLock( HQ931CALL hQ931Call, PP_CALL_OBJECT ppCallObject) { if (ppCallObject == NULL) { ASSERT(FALSE); return CS_BAD_PARAM; } // Attempt to lock the entry. If that fails, we'll return CS_BAD_PARAM under // the assumption that the entry is invalid. *ppCallObject = gpCallObjectTable->Lock((DWORD) hQ931Call); return (*ppCallObject == NULL ? CS_BAD_PARAM : CS_OK); } //==================================================================================== //==================================================================================== CS_STATUS CallObjectUnlock( P_CALL_OBJECT pCallObject) { if (pCallObject == NULL) { ASSERT(FALSE); return CS_BAD_PARAM; } // Unlock the entry if (gpCallObjectTable->Unlock((DWORD) pCallObject->hQ931Call) == FALSE) { Q931DBG((DBGERROR, "gpCallObjectTable->Unlock(0x%08lx) FAILED!!!!", pCallObject->hQ931Call)); return CS_BAD_PARAM; } return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS CallObjectValidate( HQ931CALL hQ931Call) { if (gpCallObjectTable->Validate((DWORD) hQ931Call) == TRUE) { return CS_OK; } return CS_BAD_PARAM; } //==================================================================================== //==================================================================================== BOOL CallObjectFind( HQ931CALL *phQ931Call, WORD wCRV, PCC_ADDR pPeerAddr) { Q931CALLOBJKEY Q931CallObjKey; Q931CallObjKey.wCRV = wCRV; Q931CallObjKey.pPeerAddr = pPeerAddr; Q931CallObjKey.bFound = FALSE; gpCallObjectTable->EnumerateEntries(Q931CallObjectFind, (LPVOID) &Q931CallObjKey); if(Q931CallObjKey.bFound == TRUE) { *phQ931Call = Q931CallObjKey.hQ931Call; return TRUE; } return FALSE; } //==================================================================================== //==================================================================================== CS_STATUS CallObjectMarkForDelete(HQ931CALL hQ931Call) { // User must have the object already locked to call this. // Mark the object as deleted but don't let the table delete the object's // memory. return (gpCallObjectTable->Delete((DWORD) hQ931Call) == FALSE ? CS_BAD_PARAM : CS_OK); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Timer Routines... //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //==================================================================================== // This routine will be called every 1000 ms if any call object // has caused the Q931GlobalTimer to be created. //==================================================================================== VOID CALLBACK Q931TimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) { DWORD dwTickCount = GetTickCount(); EnterCriticalSection(&(Q931GlobalTimer.Lock)); if (Q931GlobalTimer.bBusy) { LeaveCriticalSection(&(Q931GlobalTimer.Lock)); return; } Q931GlobalTimer.bBusy = TRUE; // Check all of the entries for timeout gpCallObjectTable->EnumerateEntries(Q931CheckForTimeout, (LPVOID) &dwTickCount); Q931GlobalTimer.bBusy = FALSE; LeaveCriticalSection(&(Q931GlobalTimer.Lock)); } //==================================================================================== //==================================================================================== HRESULT Q931StartTimer(P_CALL_OBJECT pCallObject, DWORD wTimerId) { if (pCallObject == NULL) { return CS_BAD_PARAM; } switch (wTimerId) { case Q931_TIMER_301: if (pCallObject->dwTimerAlarm301) { // timer is already set for this call object... return CS_INTERNAL_ERROR; } EnterCriticalSection(&(Q931GlobalTimer.Lock)); pCallObject->dwTimerAlarm301 = GetTickCount() + Q931GlobalTimer.dwTicks301; LeaveCriticalSection(&(Q931GlobalTimer.Lock)); break; case Q931_TIMER_303: if (pCallObject->dwTimerAlarm303) { // timer is already set for this call object... return CS_INTERNAL_ERROR; } EnterCriticalSection(&(Q931GlobalTimer.Lock)); pCallObject->dwTimerAlarm303 = GetTickCount() + Q931GlobalTimer.dwTicks303; LeaveCriticalSection(&(Q931GlobalTimer.Lock)); break; default: return CS_BAD_PARAM; break; } EnterCriticalSection(&(Q931GlobalTimer.Lock)); if (!Q931GlobalTimer.dwTimerCount) { Q931GlobalTimer.uTimerId = SetTimer(NULL, 0, 1000, (TIMERPROC)Q931TimerProc); } Q931GlobalTimer.dwTimerCount++; LeaveCriticalSection(&(Q931GlobalTimer.Lock)); return CS_OK; } //==================================================================================== //==================================================================================== HRESULT Q931StopTimer(P_CALL_OBJECT pCallObject, DWORD wTimerId) { if (pCallObject == NULL) { return CS_BAD_PARAM; } switch (wTimerId) { case Q931_TIMER_301: if (!pCallObject->dwTimerAlarm301) { return CS_OK; } pCallObject->dwTimerAlarm301 = 0; break; case Q931_TIMER_303: if (!pCallObject->dwTimerAlarm303) { return CS_OK; } pCallObject->dwTimerAlarm303 = 0; break; default: return CS_BAD_PARAM; break; } EnterCriticalSection(&(Q931GlobalTimer.Lock)); if (Q931GlobalTimer.dwTimerCount > 0) { Q931GlobalTimer.dwTimerCount--; if (!Q931GlobalTimer.dwTimerCount) { KillTimer(NULL, Q931GlobalTimer.uTimerId); Q931GlobalTimer.uTimerId = 0; } } LeaveCriticalSection(&(Q931GlobalTimer.Lock)); return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS Q931SetAlertingTimeout(DWORD dwDuration) { EnterCriticalSection(&(Q931GlobalTimer.Lock)); if (dwDuration) { Q931GlobalTimer.dwTicks303 = dwDuration; } else { Q931GlobalTimer.dwTicks303 = Q931_TICKS_303; } LeaveCriticalSection(&(Q931GlobalTimer.Lock)); return CS_OK; } //==================================================================================== //==================================================================================== DWORD Q931HangUpAllCalls(P_CALL_OBJECT pCallObject, LPVOID context) { HQ931CALL hQ931Call = pCallObject->hQ931Call; // Try to hangup the call object. Q931Hangup(hQ931Call, CC_REJECT_NORMAL_CALL_CLEARING); // Try to lock the object. If that succeeds, then we want to force the object to // be deleted. We should never have to do this as the hang up is supposed to // take care of that for us. if (gpCallObjectTable->Lock(hQ931Call) != NULL) { CallObjectDestroy(pCallObject); } return CALLBACK_DELETE_ENTRY; } //==================================================================================== //==================================================================================== DWORD Q931CallObjectFind(P_CALL_OBJECT pCallObject, LPVOID context) { PQ931CALLOBJKEY pQ931CallObjKey = (PQ931CALLOBJKEY) context; PCC_ADDR pPeerAddr = pQ931CallObjKey->pPeerAddr; WORD wCRV = pQ931CallObjKey->wCRV; if (!pCallObject->bResolved) { return(CALLBACK_CONTINUE); } if ((pCallObject->wCRV & (~0x8000)) == (wCRV & (~0x8000))) { if (!pPeerAddr) { pQ931CallObjKey->bFound = TRUE; pQ931CallObjKey->hQ931Call = pCallObject->hQ931Call; return(CALLBACK_ABORT); } else if ((pPeerAddr->nAddrType == CC_IP_BINARY) && (pPeerAddr->Addr.IP_Binary.dwAddr == INADDR_LOOPBACK)) { pQ931CallObjKey->bFound = FALSE; pQ931CallObjKey->hQ931Call = NULL; return(CALLBACK_CONTINUE); // allow loopback calls } else if ((pCallObject->LocalAddr.nAddrType == CC_IP_BINARY) && (pPeerAddr->nAddrType == CC_IP_BINARY) && (pCallObject->LocalAddr.Addr.IP_Binary.dwAddr == pPeerAddr->Addr.IP_Binary.dwAddr)) { pQ931CallObjKey->bFound = FALSE; pQ931CallObjKey->hQ931Call = NULL; return(CALLBACK_CONTINUE); // allow loopback calls } else if ((pCallObject->PeerConnectAddr.nAddrType == CC_IP_BINARY) && (pPeerAddr->nAddrType == CC_IP_BINARY) && (pCallObject->PeerConnectAddr.Addr.IP_Binary.dwAddr == pPeerAddr->Addr.IP_Binary.dwAddr)) { pQ931CallObjKey->bFound = TRUE; pQ931CallObjKey->hQ931Call = pCallObject->hQ931Call; return(CALLBACK_ABORT); } } return(CALLBACK_CONTINUE); } //==================================================================================== //==================================================================================== DWORD Q931CheckForTimeout(P_CALL_OBJECT pCallObject, LPVOID context) { DWORD dwTickCount = *((LPDWORD) context); // Determine if a timer has expired for the entry if (pCallObject->dwTimerAlarm301 && (pCallObject->dwTimerAlarm301 <= dwTickCount)) { Q931StopTimer(pCallObject, Q931_TIMER_301); Q931StopTimer(pCallObject, Q931_TIMER_303); if (pCallObject->dwTimerAlarm303 && (pCallObject->dwTimerAlarm303 < pCallObject->dwTimerAlarm301) && (pCallObject->dwTimerAlarm303 <= dwTickCount)) { CallBackT303(pCallObject); } else { CallBackT301(pCallObject); } } else if (pCallObject->dwTimerAlarm303 && (pCallObject->dwTimerAlarm303 <= dwTickCount)) { Q931StopTimer(pCallObject, Q931_TIMER_301); Q931StopTimer(pCallObject, Q931_TIMER_303); CallBackT303(pCallObject); } return CALLBACK_CONTINUE; }