You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
638 lines
16 KiB
638 lines
16 KiB
/****************************************************************************
|
|
*
|
|
* $Archive: S:/STURGEON/SRC/CALLCONT/VCS/chanman.c_v $
|
|
*
|
|
* 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-1994 Intel Corporation.
|
|
*
|
|
* $Revision: 1.43 $
|
|
* $Date: 04 Mar 1997 17:35:04 $
|
|
* $Author: MANDREWS $
|
|
*
|
|
* Deliverable:
|
|
*
|
|
* Abstract:
|
|
*
|
|
*
|
|
* Notes:
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
|
|
#include "apierror.h"
|
|
#include "incommon.h"
|
|
#include "callcont.h"
|
|
#include "q931.h"
|
|
#include "ccmain.h"
|
|
#include "ccutils.h"
|
|
#include "listman.h"
|
|
#include "q931man.h"
|
|
#include "userman.h"
|
|
#include "callman.h"
|
|
#include "confman.h"
|
|
#include "chanman.h"
|
|
|
|
|
|
static BOOL bChannelInited = FALSE;
|
|
|
|
static struct {
|
|
PCHANNEL pHead;
|
|
LOCK Lock;
|
|
} ChannelTable;
|
|
|
|
static struct {
|
|
CC_HCHANNEL hChannel;
|
|
LOCK Lock;
|
|
} ChannelHandle;
|
|
|
|
|
|
|
|
HRESULT InitChannelManager()
|
|
{
|
|
ASSERT(bChannelInited == FALSE);
|
|
|
|
ChannelTable.pHead = NULL;
|
|
InitializeLock(&ChannelTable.Lock);
|
|
|
|
ChannelHandle.hChannel = CC_INVALID_HANDLE + 1;
|
|
InitializeLock(&ChannelHandle.Lock);
|
|
|
|
bChannelInited = TRUE;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT DeInitChannelManager()
|
|
{
|
|
PCHANNEL pChannel;
|
|
PCHANNEL pNextChannel;
|
|
|
|
if (bChannelInited == FALSE)
|
|
return CC_OK;
|
|
|
|
pChannel = ChannelTable.pHead;
|
|
while (pChannel != NULL) {
|
|
AcquireLock(&pChannel->Lock);
|
|
pNextChannel = pChannel->pNextInTable;
|
|
FreeChannel(pChannel);
|
|
pChannel = pNextChannel;
|
|
}
|
|
|
|
DeleteLock(&ChannelHandle.Lock);
|
|
DeleteLock(&ChannelTable.Lock);
|
|
bChannelInited = FALSE;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT _AddChannelToTable( PCHANNEL pChannel)
|
|
{
|
|
ASSERT(pChannel != NULL);
|
|
ASSERT(pChannel->hChannel != CC_INVALID_HANDLE);
|
|
ASSERT(pChannel->bInTable == FALSE);
|
|
|
|
AcquireLock(&ChannelTable.Lock);
|
|
|
|
pChannel->pNextInTable = ChannelTable.pHead;
|
|
pChannel->pPrevInTable = NULL;
|
|
if (ChannelTable.pHead != NULL)
|
|
ChannelTable.pHead->pPrevInTable = pChannel;
|
|
ChannelTable.pHead = pChannel;
|
|
|
|
pChannel->bInTable = TRUE;
|
|
|
|
RelinquishLock(&ChannelTable.Lock);
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT _RemoveChannelFromTable( PCHANNEL pChannel)
|
|
{
|
|
CC_HCHANNEL hChannel;
|
|
BOOL bTimedOut;
|
|
|
|
ASSERT(pChannel != NULL);
|
|
ASSERT(pChannel->bInTable == TRUE);
|
|
|
|
// Caller must have a lock on the channel object;
|
|
// in order to avoid deadlock, we must:
|
|
// 1. unlock the channel object,
|
|
// 2. lock the ChannelTable,
|
|
// 3. locate the channel object in the ChannelTable (note that
|
|
// after step 2, the channel object may be deleted from the
|
|
// ChannelTable by another thread),
|
|
// 4. lock the channel object (someone else may have the lock)
|
|
// 5. remove the channel object from the ChannelTable,
|
|
// 6. unlock the ChannelTable
|
|
//
|
|
// The caller can now safely unlock and destroy the channel object,
|
|
// since no other thread will be able to find the object (its been
|
|
// removed from the ChannelTable), and therefore no other thread will
|
|
// be able to lock it.
|
|
|
|
// Save the channel handle; its the only way to look up
|
|
// the channel object in the ChannelTable. Note that we
|
|
// can't use pChannel to find the channel object, since
|
|
// pChannel may be free'd up, and another channel object
|
|
// allocated at the same address
|
|
hChannel = pChannel->hChannel;
|
|
|
|
// step 1
|
|
RelinquishLock(&pChannel->Lock);
|
|
|
|
step2:
|
|
// step 2
|
|
AcquireLock(&ChannelTable.Lock);
|
|
|
|
// step 3
|
|
pChannel = ChannelTable.pHead;
|
|
while ((pChannel != NULL) && (pChannel->hChannel != hChannel))
|
|
pChannel = pChannel->pNextInTable;
|
|
|
|
if (pChannel != NULL) {
|
|
// step 4
|
|
AcquireTimedLock(&pChannel->Lock,10,&bTimedOut);
|
|
if (bTimedOut) {
|
|
RelinquishLock(&ChannelTable.Lock);
|
|
Sleep(0);
|
|
goto step2;
|
|
}
|
|
// step 5
|
|
if (pChannel->pPrevInTable == NULL)
|
|
ChannelTable.pHead = pChannel->pNextInTable;
|
|
else
|
|
pChannel->pPrevInTable->pNextInTable = pChannel->pNextInTable;
|
|
|
|
if (pChannel->pNextInTable != NULL)
|
|
pChannel->pNextInTable->pPrevInTable = pChannel->pPrevInTable;
|
|
|
|
pChannel->pPrevInTable = NULL;
|
|
pChannel->pNextInTable = NULL;
|
|
pChannel->bInTable = FALSE;
|
|
}
|
|
|
|
// step 6
|
|
RelinquishLock(&ChannelTable.Lock);
|
|
|
|
if (pChannel == NULL)
|
|
return CC_BAD_PARAM;
|
|
else
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT _MakeChannelHandle( PCC_HCHANNEL phChannel)
|
|
{
|
|
AcquireLock(&ChannelHandle.Lock);
|
|
*phChannel = ChannelHandle.hChannel++;
|
|
RelinquishLock(&ChannelHandle.Lock);
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
HRESULT AllocAndLockChannel( PCC_HCHANNEL phChannel,
|
|
PCONFERENCE pConference,
|
|
CC_HCALL hCall,
|
|
PCC_TERMCAP pTxTermCap,
|
|
PCC_TERMCAP pRxTermCap,
|
|
H245_MUX_T *pTxMuxTable,
|
|
H245_MUX_T *pRxMuxTable,
|
|
H245_ACCESS_T *pSeparateStack,
|
|
DWORD_PTR dwUserToken,
|
|
BYTE bChannelType,
|
|
BYTE bSessionID,
|
|
BYTE bAssociatedSessionID,
|
|
WORD wRemoteChannelNumber,
|
|
PCC_ADDR pLocalRTPAddr,
|
|
PCC_ADDR pLocalRTCPAddr,
|
|
PCC_ADDR pPeerRTPAddr,
|
|
PCC_ADDR pPeerRTCPAddr,
|
|
BOOL bLocallyOpened,
|
|
PPCHANNEL ppChannel)
|
|
{
|
|
HRESULT status;
|
|
|
|
ASSERT(bChannelInited == TRUE);
|
|
|
|
// all parameters should have been validated by the caller
|
|
ASSERT(phChannel != NULL);
|
|
ASSERT(pConference != NULL);
|
|
ASSERT((bChannelType == TX_CHANNEL) ||
|
|
(bChannelType == RX_CHANNEL) ||
|
|
(bChannelType == TXRX_CHANNEL) ||
|
|
(bChannelType == PROXY_CHANNEL));
|
|
ASSERT(ppChannel != NULL);
|
|
|
|
// set phChannel now, in case we encounter an error
|
|
*phChannel = CC_INVALID_HANDLE;
|
|
|
|
*ppChannel = (PCHANNEL)MemAlloc(sizeof(CHANNEL));
|
|
if (*ppChannel == NULL)
|
|
return CC_NO_MEMORY;
|
|
|
|
(*ppChannel)->bInTable = FALSE;
|
|
(*ppChannel)->bMultipointChannel = FALSE;
|
|
(*ppChannel)->hCall = hCall;
|
|
(*ppChannel)->wNumOutstandingRequests = 0;
|
|
(*ppChannel)->pTxH245TermCap = NULL;
|
|
(*ppChannel)->pRxH245TermCap = NULL;
|
|
(*ppChannel)->pTxMuxTable = NULL;
|
|
(*ppChannel)->pRxMuxTable = NULL;
|
|
(*ppChannel)->pSeparateStack = NULL;
|
|
(*ppChannel)->pCloseRequests = NULL;
|
|
(*ppChannel)->pLocalRTPAddr = NULL;
|
|
(*ppChannel)->pLocalRTCPAddr = NULL;
|
|
(*ppChannel)->pPeerRTPAddr = NULL;
|
|
(*ppChannel)->pPeerRTCPAddr = NULL;
|
|
(*ppChannel)->dwUserToken = dwUserToken;
|
|
(*ppChannel)->hConference = pConference->hConference;
|
|
(*ppChannel)->bSessionID = bSessionID;
|
|
(*ppChannel)->bAssociatedSessionID = bAssociatedSessionID;
|
|
(*ppChannel)->wLocalChannelNumber = 0;
|
|
(*ppChannel)->wRemoteChannelNumber = 0;
|
|
(*ppChannel)->bLocallyOpened = bLocallyOpened;
|
|
(*ppChannel)->pNextInTable = NULL;
|
|
(*ppChannel)->pPrevInTable = NULL;
|
|
(*ppChannel)->pNext = NULL;
|
|
(*ppChannel)->pPrev = NULL;
|
|
|
|
InitializeLock(&(*ppChannel)->Lock);
|
|
AcquireLock(&(*ppChannel)->Lock);
|
|
|
|
status = _MakeChannelHandle(&(*ppChannel)->hChannel);
|
|
if (status != CC_OK) {
|
|
FreeChannel(*ppChannel);
|
|
return status;
|
|
}
|
|
|
|
if (bLocallyOpened == TRUE)
|
|
(*ppChannel)->tsAccepted = TS_TRUE;
|
|
else
|
|
(*ppChannel)->tsAccepted = TS_UNKNOWN;
|
|
|
|
if (pTxMuxTable != NULL) {
|
|
(*ppChannel)->pTxMuxTable = (H245_MUX_T *)MemAlloc(sizeof(H245_MUX_T));
|
|
if ((*ppChannel)->pTxMuxTable == NULL) {
|
|
FreeChannel(*ppChannel);
|
|
return CC_NO_MEMORY;
|
|
}
|
|
*(*ppChannel)->pTxMuxTable = *pTxMuxTable;
|
|
}
|
|
|
|
if (pRxMuxTable != NULL) {
|
|
(*ppChannel)->pRxMuxTable = (H245_MUX_T *)MemAlloc(sizeof(H245_MUX_T));
|
|
if ((*ppChannel)->pRxMuxTable == NULL) {
|
|
FreeChannel(*ppChannel);
|
|
return CC_NO_MEMORY;
|
|
}
|
|
*(*ppChannel)->pRxMuxTable = *pRxMuxTable;
|
|
}
|
|
|
|
if (pSeparateStack != NULL) {
|
|
status = CopySeparateStack(&(*ppChannel)->pSeparateStack,
|
|
pSeparateStack);
|
|
if (status != CC_OK) {
|
|
FreeChannel(*ppChannel);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
(*ppChannel)->bChannelType = bChannelType;
|
|
(*ppChannel)->bCallbackInvoked = FALSE;
|
|
if (pTxTermCap != NULL) {
|
|
status = H245CopyCap(&(*ppChannel)->pTxH245TermCap, pTxTermCap);
|
|
if (status != H245_ERROR_OK) {
|
|
FreeChannel(*ppChannel);
|
|
return status;
|
|
}
|
|
}
|
|
if (pRxTermCap != NULL) {
|
|
status = H245CopyCap(&(*ppChannel)->pRxH245TermCap, pRxTermCap);
|
|
if (status != H245_ERROR_OK) {
|
|
FreeChannel(*ppChannel);
|
|
return status;
|
|
}
|
|
}
|
|
if (pLocalRTPAddr != NULL) {
|
|
(*ppChannel)->pLocalRTPAddr = (PCC_ADDR)MemAlloc(sizeof(CC_ADDR));
|
|
if ((*ppChannel)->pLocalRTPAddr == NULL) {
|
|
FreeChannel(*ppChannel);
|
|
return CC_NO_MEMORY;
|
|
}
|
|
*(*ppChannel)->pLocalRTPAddr = *pLocalRTPAddr;
|
|
}
|
|
if (pLocalRTCPAddr != NULL) {
|
|
(*ppChannel)->pLocalRTCPAddr = (PCC_ADDR)MemAlloc(sizeof(CC_ADDR));
|
|
if ((*ppChannel)->pLocalRTCPAddr == NULL) {
|
|
FreeChannel(*ppChannel);
|
|
return CC_NO_MEMORY;
|
|
}
|
|
*(*ppChannel)->pLocalRTCPAddr = *pLocalRTCPAddr;
|
|
}
|
|
if (pPeerRTPAddr != NULL) {
|
|
(*ppChannel)->pPeerRTPAddr = (PCC_ADDR)MemAlloc(sizeof(CC_ADDR));
|
|
if ((*ppChannel)->pPeerRTPAddr == NULL) {
|
|
FreeChannel(*ppChannel);
|
|
return CC_NO_MEMORY;
|
|
}
|
|
*(*ppChannel)->pPeerRTPAddr = *pPeerRTPAddr;
|
|
}
|
|
if (pPeerRTCPAddr != NULL) {
|
|
(*ppChannel)->pPeerRTCPAddr = (PCC_ADDR)MemAlloc(sizeof(CC_ADDR));
|
|
if ((*ppChannel)->pPeerRTCPAddr == NULL) {
|
|
FreeChannel(*ppChannel);
|
|
return CC_NO_MEMORY;
|
|
}
|
|
*(*ppChannel)->pPeerRTCPAddr = *pPeerRTCPAddr;
|
|
}
|
|
|
|
*phChannel = (*ppChannel)->hChannel;
|
|
|
|
// add the conference to the conference table
|
|
status = _AddChannelToTable(*ppChannel);
|
|
if (status != CC_OK) {
|
|
FreeChannel(*ppChannel);
|
|
return status;
|
|
}
|
|
|
|
switch (bChannelType) {
|
|
case TX_CHANNEL:
|
|
status = AllocateChannelNumber(pConference, &(*ppChannel)->wLocalChannelNumber);
|
|
if (status != CC_OK) {
|
|
FreeChannel(*ppChannel);
|
|
return status;
|
|
}
|
|
(*ppChannel)->wRemoteChannelNumber = 0;
|
|
break;
|
|
|
|
case RX_CHANNEL:
|
|
(*ppChannel)->wLocalChannelNumber = 0;
|
|
(*ppChannel)->wRemoteChannelNumber = wRemoteChannelNumber;
|
|
break;
|
|
|
|
case TXRX_CHANNEL:
|
|
status = AllocateChannelNumber(pConference, &(*ppChannel)->wLocalChannelNumber);
|
|
if (status != CC_OK) {
|
|
FreeChannel(*ppChannel);
|
|
return status;
|
|
}
|
|
if (bLocallyOpened)
|
|
(*ppChannel)->wRemoteChannelNumber = 0;
|
|
else
|
|
(*ppChannel)->wRemoteChannelNumber = wRemoteChannelNumber;
|
|
break;
|
|
|
|
case PROXY_CHANNEL:
|
|
status = AllocateChannelNumber(pConference, &(*ppChannel)->wLocalChannelNumber);
|
|
if (status != CC_OK) {
|
|
FreeChannel(*ppChannel);
|
|
return status;
|
|
}
|
|
(*ppChannel)->wRemoteChannelNumber = wRemoteChannelNumber;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT AddLocalAddrPairToChannel( PCC_ADDR pRTPAddr,
|
|
PCC_ADDR pRTCPAddr,
|
|
PCHANNEL pChannel)
|
|
{
|
|
ASSERT(pChannel != NULL);
|
|
|
|
if (pRTPAddr != NULL) {
|
|
if (pChannel->pLocalRTPAddr == NULL) {
|
|
pChannel->pLocalRTPAddr = (PCC_ADDR)MemAlloc(sizeof(CC_ADDR));
|
|
if (pChannel->pLocalRTPAddr == NULL)
|
|
return CC_NO_MEMORY;
|
|
}
|
|
*pChannel->pLocalRTPAddr = *pRTPAddr;
|
|
}
|
|
|
|
if (pRTCPAddr != NULL) {
|
|
if (pChannel->pLocalRTCPAddr == NULL) {
|
|
pChannel->pLocalRTCPAddr = (PCC_ADDR)MemAlloc(sizeof(CC_ADDR));
|
|
if (pChannel->pLocalRTCPAddr == NULL)
|
|
return CC_NO_MEMORY;
|
|
}
|
|
*pChannel->pLocalRTCPAddr = *pRTCPAddr;
|
|
}
|
|
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT AddSeparateStackToChannel( H245_ACCESS_T *pSeparateStack,
|
|
PCHANNEL pChannel)
|
|
{
|
|
ASSERT(pSeparateStack != NULL);
|
|
ASSERT(pChannel != NULL);
|
|
|
|
if (pChannel->pSeparateStack != NULL)
|
|
return CC_BAD_PARAM;
|
|
|
|
pChannel->pSeparateStack = (H245_ACCESS_T *)MemAlloc(sizeof(H245_ACCESS_T));
|
|
if (pChannel->pSeparateStack == NULL)
|
|
return CC_NO_MEMORY;
|
|
*pChannel->pSeparateStack = *pSeparateStack;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
// Caller must have a lock on the channel object
|
|
HRESULT FreeChannel( PCHANNEL pChannel)
|
|
{
|
|
HRESULT status;
|
|
CC_HCHANNEL hChannel;
|
|
PCONFERENCE pConference;
|
|
|
|
ASSERT(pChannel != NULL);
|
|
|
|
// caller must have a lock on the channel object,
|
|
// so there's no need to re-lock it
|
|
|
|
hChannel = pChannel->hChannel;
|
|
if (pChannel->hConference != CC_INVALID_HANDLE) {
|
|
UnlockChannel(pChannel);
|
|
status = LockChannelAndConference(hChannel, &pChannel, &pConference);
|
|
if (status != CC_OK)
|
|
return status;
|
|
}
|
|
|
|
if (pChannel->bInTable == TRUE)
|
|
if (_RemoveChannelFromTable(pChannel) == CC_BAD_PARAM)
|
|
// the channel object was deleted by another thread,
|
|
// so just return CC_OK
|
|
return CC_OK;
|
|
|
|
if (pChannel->hConference != CC_INVALID_HANDLE)
|
|
RemoveChannelFromConference(pChannel, pConference);
|
|
|
|
if (pChannel->pSeparateStack != NULL)
|
|
FreeSeparateStack(pChannel->pSeparateStack);
|
|
|
|
if (pChannel->pTxMuxTable != NULL)
|
|
MemFree(pChannel->pTxMuxTable);
|
|
|
|
if (pChannel->pRxMuxTable != NULL)
|
|
MemFree(pChannel->pRxMuxTable);
|
|
|
|
if (pChannel->pTxH245TermCap != NULL)
|
|
H245FreeCap(pChannel->pTxH245TermCap);
|
|
|
|
if (pChannel->pRxH245TermCap != NULL)
|
|
H245FreeCap(pChannel->pRxH245TermCap);
|
|
|
|
while (DequeueRequest(&pChannel->pCloseRequests, NULL) == CC_OK);
|
|
|
|
if (pChannel->pLocalRTPAddr != NULL)
|
|
MemFree(pChannel->pLocalRTPAddr);
|
|
|
|
if (pChannel->pLocalRTCPAddr != NULL)
|
|
MemFree(pChannel->pLocalRTCPAddr);
|
|
|
|
if (pChannel->pPeerRTPAddr != NULL)
|
|
MemFree(pChannel->pPeerRTPAddr);
|
|
|
|
if (pChannel->pPeerRTCPAddr != NULL)
|
|
MemFree(pChannel->pPeerRTCPAddr);
|
|
|
|
if (pChannel->wLocalChannelNumber != 0) {
|
|
FreeChannelNumber(pConference, pChannel->wLocalChannelNumber);
|
|
}
|
|
|
|
if (pChannel->hConference != CC_INVALID_HANDLE)
|
|
UnlockConference(pConference);
|
|
|
|
// Since the channel object has been removed from the ChannelTable,
|
|
// no other thread will be able to find the channel object and obtain
|
|
// a lock, so its safe to unlock the channel object and delete it here
|
|
RelinquishLock(&pChannel->Lock);
|
|
DeleteLock(&pChannel->Lock);
|
|
MemFree(pChannel);
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT LockChannel( CC_HCHANNEL hChannel,
|
|
PPCHANNEL ppChannel)
|
|
{
|
|
BOOL bTimedOut;
|
|
|
|
ASSERT(hChannel != CC_INVALID_HANDLE);
|
|
ASSERT(ppChannel != NULL);
|
|
|
|
step1:
|
|
AcquireLock(&ChannelTable.Lock);
|
|
|
|
*ppChannel = ChannelTable.pHead;
|
|
while ((*ppChannel != NULL) && ((*ppChannel)->hChannel != hChannel))
|
|
*ppChannel = (*ppChannel)->pNextInTable;
|
|
|
|
if (*ppChannel != NULL) {
|
|
AcquireTimedLock(&(*ppChannel)->Lock,10,&bTimedOut);
|
|
if (bTimedOut) {
|
|
RelinquishLock(&ChannelTable.Lock);
|
|
Sleep(0);
|
|
goto step1;
|
|
}
|
|
}
|
|
|
|
RelinquishLock(&ChannelTable.Lock);
|
|
|
|
if (*ppChannel == NULL)
|
|
return CC_BAD_PARAM;
|
|
else
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT LockChannelAndConference( CC_HCHANNEL hChannel,
|
|
PPCHANNEL ppChannel,
|
|
PPCONFERENCE ppConference)
|
|
{
|
|
HRESULT status;
|
|
CC_HCONFERENCE hConference;
|
|
|
|
ASSERT(hChannel != CC_INVALID_HANDLE);
|
|
ASSERT(ppChannel != NULL);
|
|
ASSERT(ppConference != NULL);
|
|
|
|
status = LockChannel(hChannel, ppChannel);
|
|
if (status != CC_OK)
|
|
return status;
|
|
|
|
if ((*ppChannel)->hConference == CC_INVALID_HANDLE) {
|
|
UnlockChannel(*ppChannel);
|
|
return CC_BAD_PARAM;
|
|
}
|
|
|
|
hConference = (*ppChannel)->hConference;
|
|
UnlockChannel(*ppChannel);
|
|
|
|
status = LockConference(hConference, ppConference);
|
|
if (status != CC_OK)
|
|
return status;
|
|
|
|
status = LockChannel(hChannel, ppChannel);
|
|
if (status != CC_OK) {
|
|
UnlockConference(*ppConference);
|
|
return status;
|
|
}
|
|
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT ValidateChannel( CC_HCHANNEL hChannel)
|
|
{
|
|
PCHANNEL pChannel;
|
|
|
|
ASSERT(hChannel != CC_INVALID_HANDLE);
|
|
|
|
AcquireLock(&ChannelTable.Lock);
|
|
|
|
pChannel = ChannelTable.pHead;
|
|
while ((pChannel != NULL) && (pChannel->hChannel != hChannel))
|
|
pChannel = pChannel->pNextInTable;
|
|
|
|
RelinquishLock(&ChannelTable.Lock);
|
|
|
|
if (pChannel == NULL)
|
|
return CC_BAD_PARAM;
|
|
else
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT UnlockChannel( PCHANNEL pChannel)
|
|
{
|
|
ASSERT(pChannel != NULL);
|
|
|
|
RelinquishLock(&pChannel->Lock);
|
|
return CC_OK;
|
|
}
|
|
|