/** @file
* Host channel.
*/
/*
* Copyright (C) 2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include <iprt/alloc.h>
#include <iprt/string.h>
#include <iprt/asm.h>
#include <iprt/assert.h>
#include "HostChannel.h"
static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvInstance,
uint32_t u32Id, const void *pvEvent, uint32_t cbEvent);
static DECLCALLBACK(void) HostChannelCallbackDeleted(void *pvCallbacks, void *pvChannel);
/* A registered provider of channels. */
typedef struct VBOXHOSTCHPROVIDER
{
int32_t volatile cRefs;
RTLISTNODE nodeContext; /* Member of the list of providers in the service context. */
VBOXHOSTCHCTX *pCtx;
VBOXHOSTCHANNELINTERFACE iface;
char *pszName;
RTLISTANCHOR listChannels;
} VBOXHOSTCHPROVIDER;
/* An established channel. */
typedef struct VBOXHOSTCHINSTANCE
{
int32_t volatile cRefs;
RTLISTNODE nodeClient; /* In the client, for cleanup when a client disconnects. */
RTLISTNODE nodeProvider; /* In the provider, needed for cleanup when the provider is unregistered. */
VBOXHOSTCHCLIENT *pClient; /* The client which uses the channel. */
VBOXHOSTCHPROVIDER *pProvider; /* NULL if the provider was unregistered. */
void *pvChannel; /* Provider's context of the channel. */
uint32_t u32Handle; /* handle assigned to the channel by the service. */
} VBOXHOSTCHINSTANCE;
struct VBOXHOSTCHCTX
{
bool fInitialized;
RTLISTANCHOR listProviders;
};
/* The channel callbacks context. The provider passes the pointer as a callback parameter.
* Created for the provider and deleted when the provider says so.
*/
typedef struct VBOXHOSTCHCALLBACKCTX
{
RTLISTNODE nodeClient; /* In the client, for cleanup when a client disconnects. */
VBOXHOSTCHCLIENT *pClient; /* The client which uses the channel, NULL when the client does not exist. */
} VBOXHOSTCHCALLBACKCTX;
/* Only one service instance is supported. */
static VBOXHOSTCHCTX g_ctx = { false };
static VBOXHOSTCHANNELCALLBACKS g_callbacks =
{
HostChannelCallbackEvent,
HostChannelCallbackDeleted
};
/*
* Provider management.
*/
static void vhcProviderDestroy(VBOXHOSTCHPROVIDER *pProvider)
{
RTStrFree(pProvider->pszName);
}
static int32_t vhcProviderAddRef(VBOXHOSTCHPROVIDER *pProvider)
{
return ASMAtomicIncS32(&pProvider->cRefs);
}
static void vhcProviderRelease(VBOXHOSTCHPROVIDER *pProvider)
{
int32_t c = ASMAtomicDecS32(&pProvider->cRefs);
Assert(c >= 0);
if (c == 0)
{
vhcProviderDestroy(pProvider);
RTMemFree(pProvider);
}
}
static VBOXHOSTCHPROVIDER *vhcProviderFind(VBOXHOSTCHCTX *pCtx, const char *pszName)
{
VBOXHOSTCHPROVIDER *pProvider = NULL;
int rc = vboxHostChannelLock();
if (RT_SUCCESS(rc))
{
VBOXHOSTCHPROVIDER *pIter;
RTListForEach(&pCtx->listProviders, pIter, VBOXHOSTCHPROVIDER, nodeContext)
{
if (RTStrCmp(pIter->pszName, pszName) == 0)
{
pProvider = pIter;
vhcProviderAddRef(pProvider);
break;
}
}
vboxHostChannelUnlock();
}
return pProvider;
}
static int vhcProviderRegister(VBOXHOSTCHCTX *pCtx, VBOXHOSTCHPROVIDER *pProvider)
{
int rc = vboxHostChannelLock();
if (RT_SUCCESS(rc))
{
/* @todo check a duplicate. */
RTListAppend(&pCtx->listProviders, &pProvider->nodeContext);
vboxHostChannelUnlock();
}
if (RT_FAILURE(rc))
{
vhcProviderRelease(pProvider);
}
return rc;
}
static int vhcProviderUnregister(VBOXHOSTCHPROVIDER *pProvider)
{
int rc = vboxHostChannelLock();
if (RT_SUCCESS(rc))
{
/* @todo check that the provider is in the list. */
/* @todo mark the provider as invalid in each instance. also detach channels? */
RTListNodeRemove(&pProvider->nodeContext);
vboxHostChannelUnlock();
vhcProviderRelease(pProvider);
}
return rc;
}
/*
* Select an unique handle for the new channel.
* Works under the lock.
*/
static int vhcHandleCreate(VBOXHOSTCHCLIENT *pClient, uint32_t *pu32Handle)
{
bool fOver = false;
for(;;)
{
uint32_t u32Handle = ASMAtomicIncU32(&pClient->u32HandleSrc);
if (u32Handle == 0)
{
if (fOver)
{
return VERR_NOT_SUPPORTED;
}
fOver = true;
continue;
}
VBOXHOSTCHINSTANCE *pDuplicate = NULL;
VBOXHOSTCHINSTANCE *pIter;
RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
{
if (pIter->u32Handle == u32Handle)
{
pDuplicate = pIter;
break;
}
}
if (pDuplicate == NULL)
{
*pu32Handle = u32Handle;
break;
}
}
return VINF_SUCCESS;
}
/*
* Channel instance management.
*/
static void vhcInstanceDestroy(VBOXHOSTCHINSTANCE *pInstance)
{
HOSTCHLOG(("HostChannel: destroy %p\n", pInstance));
}
static int32_t vhcInstanceAddRef(VBOXHOSTCHINSTANCE *pInstance)
{
HOSTCHLOG(("INST: %p %d addref\n", pInstance, pInstance->cRefs));
return ASMAtomicIncS32(&pInstance->cRefs);
}
static void vhcInstanceRelease(VBOXHOSTCHINSTANCE *pInstance)
{
int32_t c = ASMAtomicDecS32(&pInstance->cRefs);
HOSTCHLOG(("INST: %p %d release\n", pInstance, pInstance->cRefs));
Assert(c >= 0);
if (c == 0)
{
vhcInstanceDestroy(pInstance);
RTMemFree(pInstance);
}
}
static int vhcInstanceCreate(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHINSTANCE **ppInstance)
{
int rc = VINF_SUCCESS;
VBOXHOSTCHINSTANCE *pInstance = (VBOXHOSTCHINSTANCE *)RTMemAllocZ(sizeof(VBOXHOSTCHINSTANCE));
if (pInstance)
{
rc = vboxHostChannelLock();
if (RT_SUCCESS(rc))
{
rc = vhcHandleCreate(pClient, &pInstance->u32Handle);
if (RT_SUCCESS(rc))
{
/* Used by the client, that is in the list of channels. */
vhcInstanceAddRef(pInstance);
/* Add to the list of created channel instances. It is inactive while pClient is 0. */
RTListAppend(&pClient->listChannels, &pInstance->nodeClient);
/* Return to the caller. */
vhcInstanceAddRef(pInstance);
*ppInstance = pInstance;
}
vboxHostChannelUnlock();
}
if (RT_FAILURE(rc))
{
RTMemFree(pInstance);
}
}
else
{
rc = VERR_NO_MEMORY;
}
return rc;
}
static VBOXHOSTCHINSTANCE *vhcInstanceFind(VBOXHOSTCHCLIENT *pClient, uint32_t u32Handle)
{
VBOXHOSTCHINSTANCE *pInstance = NULL;
int rc = vboxHostChannelLock();
if (RT_SUCCESS(rc))
{
VBOXHOSTCHINSTANCE *pIter;
RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
{
if ( pIter->pClient
&& pIter->u32Handle == u32Handle)
{
pInstance = pIter;
vhcInstanceAddRef(pInstance);
break;
}
}
vboxHostChannelUnlock();
}
return pInstance;
}
static VBOXHOSTCHINSTANCE *vhcInstanceFindByChannelPtr(VBOXHOSTCHCLIENT *pClient, void *pvChannel)
{
VBOXHOSTCHINSTANCE *pInstance = NULL;
if (pvChannel == NULL)
{
return NULL;
}
int rc = vboxHostChannelLock();
if (RT_SUCCESS(rc))
{
VBOXHOSTCHINSTANCE *pIter;
RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
{
if ( pIter->pClient
&& pIter->pvChannel == pvChannel)
{
pInstance = pIter;
vhcInstanceAddRef(pInstance);
break;
}
}
vboxHostChannelUnlock();
}
return pInstance;
}
static void vhcInstanceDetach(VBOXHOSTCHINSTANCE *pInstance)
{
HOSTCHLOG(("HostChannel: detach %p\n", pInstance));
if (pInstance->pProvider)
{
pInstance->pProvider->iface.HostChannelDetach(pInstance->pvChannel);
RTListNodeRemove(&pInstance->nodeProvider);
vhcProviderRelease(pInstance->pProvider);
pInstance->pProvider = NULL;
vhcInstanceRelease(pInstance); /* Not in the provider's list anymore. */
}
int rc = vboxHostChannelLock();
if (RT_SUCCESS(rc))
{
RTListNodeRemove(&pInstance->nodeClient);
vboxHostChannelUnlock();
vhcInstanceRelease(pInstance); /* Not used by the client anymore. */
}
}
/*
* Channel callback contexts.
*/
static int vhcCallbackCtxCreate(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHCALLBACKCTX **ppCallbackCtx)
{
int rc = VINF_SUCCESS;
VBOXHOSTCHCALLBACKCTX *pCallbackCtx = (VBOXHOSTCHCALLBACKCTX *)RTMemAllocZ(sizeof(VBOXHOSTCHCALLBACKCTX));
if (pCallbackCtx != NULL)
{
/* The callback context is accessed by the providers threads. */
rc = vboxHostChannelLock();
if (RT_SUCCESS(rc))
{
RTListAppend(&pClient->listContexts, &pCallbackCtx->nodeClient);
pCallbackCtx->pClient = pClient;
vboxHostChannelUnlock();
}
else
{
RTMemFree(pCallbackCtx);
}
}
else
{
rc = VERR_NO_MEMORY;
}
if (RT_SUCCESS(rc))
{
*ppCallbackCtx = pCallbackCtx;
}
return rc;
}
static int vhcCallbackCtxDelete(VBOXHOSTCHCALLBACKCTX *pCallbackCtx)
{
int rc = vboxHostChannelLock();
if (RT_SUCCESS(rc))
{
VBOXHOSTCHCLIENT *pClient = pCallbackCtx->pClient;
if (pClient != NULL)
{
/* The callback is associated with a client.
* Check that the callback is in the list and remove it from the list.
*/
bool fFound = false;
VBOXHOSTCHCALLBACKCTX *pIter;
RTListForEach(&pClient->listContexts, pIter, VBOXHOSTCHCALLBACKCTX, nodeClient)
{
if (pIter == pCallbackCtx)
{
fFound = true;
break;
}
}
if (fFound)
{
RTListNodeRemove(&pCallbackCtx->nodeClient);
}
else
{
AssertFailed();
rc = VERR_INVALID_PARAMETER;
}
}
else
{
/* It is not in the clients anymore. May be the client has been disconnected.
* Just free the memory.
*/
}
vboxHostChannelUnlock();
}
if (RT_SUCCESS(rc))
{
RTMemFree(pCallbackCtx);
}
return rc;
}
/*
* Host channel service functions.
*/
int vboxHostChannelInit(void)
{
VBOXHOSTCHCTX *pCtx = &g_ctx;
if (pCtx->fInitialized)
{
return VERR_NOT_SUPPORTED;
}
pCtx->fInitialized = true;
RTListInit(&pCtx->listProviders);
return VINF_SUCCESS;
}
void vboxHostChannelDestroy(void)
{
VBOXHOSTCHCTX *pCtx = &g_ctx;
VBOXHOSTCHPROVIDER *pIter;
VBOXHOSTCHPROVIDER *pIterNext;
RTListForEachSafe(&pCtx->listProviders, pIter, pIterNext, VBOXHOSTCHPROVIDER, nodeContext)
{
vhcProviderUnregister(pIter);
}
pCtx->fInitialized = false;
}
int vboxHostChannelClientConnect(VBOXHOSTCHCLIENT *pClient)
{
/* A guest client is connecting to the service.
* Later the client will use Attach calls to connect to channel providers.
* pClient is already zeroed.
*/
pClient->pCtx = &g_ctx;
RTListInit(&pClient->listChannels);
RTListInit(&pClient->listEvents);
RTListInit(&pClient->listContexts);
return VINF_SUCCESS;
}
void vboxHostChannelClientDisconnect(VBOXHOSTCHCLIENT *pClient)
{
/* Clear the list of contexts and prevent acceess to the client. */
int rc = vboxHostChannelLock();
if (RT_SUCCESS(rc))
{
VBOXHOSTCHCALLBACKCTX *pIter;
VBOXHOSTCHCALLBACKCTX *pNext;
RTListForEachSafe(&pClient->listContexts, pIter, pNext, VBOXHOSTCHCALLBACKCTX, nodeClient)
{
pIter->pClient = NULL;
RTListNodeRemove(&pIter->nodeClient);
}
vboxHostChannelUnlock();
}
/* If there are attached channels, detach them. */
VBOXHOSTCHINSTANCE *pIter;
VBOXHOSTCHINSTANCE *pIterNext;
RTListForEachSafe(&pClient->listChannels, pIter, pIterNext, VBOXHOSTCHINSTANCE, nodeClient)
{
vhcInstanceDetach(pIter);
}
}
int vboxHostChannelAttach(VBOXHOSTCHCLIENT *pClient,
uint32_t *pu32Handle,
const char *pszName,
uint32_t u32Flags)
{
int rc = VINF_SUCCESS;
HOSTCHLOG(("HostChannel: Attach: (%d) [%s] 0x%08X\n", pClient->u32ClientID, pszName, u32Flags));
/* Look if there is a provider. */
VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pClient->pCtx, pszName);
if (pProvider)
{
VBOXHOSTCHINSTANCE *pInstance = NULL;
rc = vhcInstanceCreate(pClient, &pInstance);
if (RT_SUCCESS(rc))
{
VBOXHOSTCHCALLBACKCTX *pCallbackCtx = NULL;
rc = vhcCallbackCtxCreate(pClient, &pCallbackCtx);
if (RT_SUCCESS(rc))
{
void *pvChannel = NULL;
rc = pProvider->iface.HostChannelAttach(pProvider->iface.pvProvider,
&pvChannel,
u32Flags,
&g_callbacks, pCallbackCtx);
if (RT_SUCCESS(rc))
{
vhcProviderAddRef(pProvider);
pInstance->pProvider = pProvider;
pInstance->pClient = pClient;
pInstance->pvChannel = pvChannel;
/* It is already in the channels list of the client. */
vhcInstanceAddRef(pInstance); /* Referenced by the list of provider's channels. */
RTListAppend(&pProvider->listChannels, &pInstance->nodeProvider);
*pu32Handle = pInstance->u32Handle;
HOSTCHLOG(("HostChannel: Attach: (%d) handle %d\n", pClient->u32ClientID, pInstance->u32Handle));
}
if (RT_FAILURE(rc))
{
vhcCallbackCtxDelete(pCallbackCtx);
}
}
if (RT_FAILURE(rc))
{
vhcInstanceDetach(pInstance);
}
vhcInstanceRelease(pInstance);
}
vhcProviderRelease(pProvider);
}
else
{
rc = VERR_NOT_SUPPORTED;
}
return rc;
}
int vboxHostChannelDetach(VBOXHOSTCHCLIENT *pClient,
uint32_t u32Handle)
{
HOSTCHLOG(("HostChannel: Detach: (%d) handle %d\n", pClient->u32ClientID, u32Handle));
int rc = VINF_SUCCESS;
VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
if (pInstance)
{
vhcInstanceDetach(pInstance);
vhcInstanceRelease(pInstance);
}
else
{
rc = VERR_NOT_SUPPORTED;
}
return rc;
}
int vboxHostChannelSend(VBOXHOSTCHCLIENT *pClient,
uint32_t u32Handle,
const void *pvData,
uint32_t cbData)
{
HOSTCHLOG(("HostChannel: Send: (%d) handle %d, %d bytes\n", pClient->u32ClientID, u32Handle, cbData));
int rc = VINF_SUCCESS;
VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
if (pInstance)
{
if (pInstance->pProvider)
{
pInstance->pProvider->iface.HostChannelSend(pInstance->pvChannel, pvData, cbData);
}
vhcInstanceRelease(pInstance);
}
else
{
rc = VERR_NOT_SUPPORTED;
}
return rc;
}
int vboxHostChannelRecv(VBOXHOSTCHCLIENT *pClient,
uint32_t u32Handle,
void *pvData,
uint32_t cbData,
uint32_t *pu32SizeReceived,
uint32_t *pu32SizeRemaining)
{
HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData));
int rc = VINF_SUCCESS;
VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
if (pInstance)
{
if (pInstance->pProvider)
{
rc = pInstance->pProvider->iface.HostChannelRecv(pInstance->pvChannel, pvData, cbData,
pu32SizeReceived, pu32SizeRemaining);
HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, rc %Rrc, recv %d, rem %d\n",
pClient->u32ClientID, u32Handle, rc, cbData, *pu32SizeReceived, *pu32SizeRemaining));
}
vhcInstanceRelease(pInstance);
}
else
{
rc = VERR_NOT_SUPPORTED;
}
return rc;
}
int vboxHostChannelControl(VBOXHOSTCHCLIENT *pClient,
uint32_t u32Handle,
uint32_t u32Code,
void *pvParm,
uint32_t cbParm,
void *pvData,
uint32_t cbData,
uint32_t *pu32SizeDataReturned)
{
HOSTCHLOG(("HostChannel: Control: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData));
int rc = VINF_SUCCESS;
VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
if (pInstance)
{
if (pInstance->pProvider)
{
pInstance->pProvider->iface.HostChannelControl(pInstance->pvChannel, u32Code,
pvParm, cbParm,
pvData, cbData, pu32SizeDataReturned);
}
vhcInstanceRelease(pInstance);
}
else
{
rc = VERR_NOT_SUPPORTED;
}
return rc;
}
typedef struct VBOXHOSTCHANNELEVENT
{
RTLISTNODE NodeEvent;
uint32_t u32ChannelHandle;
uint32_t u32Id;
void *pvEvent;
uint32_t cbEvent;
} VBOXHOSTCHANNELEVENT;
int vboxHostChannelEventWait(VBOXHOSTCHCLIENT *pClient,
bool *pfEvent,
VBOXHGCMCALLHANDLE callHandle,
VBOXHGCMSVCPARM *paParms)
{
int rc = vboxHostChannelLock();
if (RT_FAILURE(rc))
{
return rc;
}
if (pClient->fAsync)
{
/* If there is a wait request already, cancel it. */
vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0);
pClient->fAsync = false;
}
/* Check if there is something in the client's event queue. */
VBOXHOSTCHANNELEVENT *pEvent = RTListGetFirst(&pClient->listEvents, VBOXHOSTCHANNELEVENT, NodeEvent);
HOSTCHLOG(("HostChannel: QueryEvent: (%d), event %p\n", pClient->u32ClientID, pEvent));
if (pEvent)
{
/* Report the event. */
RTListNodeRemove(&pEvent->NodeEvent);
HOSTCHLOG(("HostChannel: QueryEvent: (%d), cbEvent %d\n",
pClient->u32ClientID, pEvent->cbEvent));
vboxHostChannelEventParmsSet(paParms, pEvent->u32ChannelHandle,
pEvent->u32Id, pEvent->pvEvent, pEvent->cbEvent);
*pfEvent = true;
RTMemFree(pEvent);
}
else
{
/* No event available at the time. Process asynchronously. */
pClient->fAsync = true;
pClient->async.callHandle = callHandle;
pClient->async.paParms = paParms;
/* Tell the caller that there is no event. */
*pfEvent = false;
}
vboxHostChannelUnlock();
return rc;
}
int vboxHostChannelEventCancel(VBOXHOSTCHCLIENT *pClient)
{
int rc = vboxHostChannelLock();
if (RT_SUCCESS(rc))
{
if (pClient->fAsync)
{
/* If there is a wait request alredy, cancel it. */
vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0);
pClient->fAsync = false;
}
vboxHostChannelUnlock();
}
return rc;
}
/* @thread provider */
static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvChannel,
uint32_t u32Id, const void *pvEvent, uint32_t cbEvent)
{
VBOXHOSTCHCALLBACKCTX *pCallbackCtx = (VBOXHOSTCHCALLBACKCTX *)pvCallbacks;
int rc = vboxHostChannelLock();
if (RT_FAILURE(rc))
{
return;
}
/* Check that the structure is still associated with a client.
* The client can disconnect and will be invalid.
*/
VBOXHOSTCHCLIENT *pClient = pCallbackCtx->pClient;
if (pClient == NULL)
{
vboxHostChannelUnlock();
HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client gone.\n"));
/* The client does not exist anymore, skip the event. */
return;
}
bool fFound = false;
VBOXHOSTCHCALLBACKCTX *pIter;
RTListForEach(&pClient->listContexts, pIter, VBOXHOSTCHCALLBACKCTX, nodeClient)
{
if (pIter == pCallbackCtx)
{
fFound = true;
break;
}
}
if (!fFound)
{
AssertFailed();
vboxHostChannelUnlock();
HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client does not have the context.\n"));
/* The context is not in the list of contexts. Skip the event. */
return;
}
VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFindByChannelPtr(pClient, pvChannel);
HOSTCHLOG(("HostChannel: CallbackEvent[%p]: (%d) instance %p\n",
pCallbackCtx, pClient->u32ClientID, pInstance));
if (!pInstance)
{
/* Instance was already detached. Skip the event. */
vboxHostChannelUnlock();
return;
}
uint32_t u32ChannelHandle = pInstance->u32Handle;
HOSTCHLOG(("HostChannel: CallbackEvent: (%d) handle %d, async %d, cbEvent %d\n",
pClient->u32ClientID, u32ChannelHandle, pClient->fAsync, cbEvent));
/* Check whether the event is waited. */
if (pClient->fAsync)
{
/* Report the event. */
vboxHostChannelReportAsync(pClient, u32ChannelHandle, u32Id, pvEvent, cbEvent);
pClient->fAsync = false;
}
else
{
/* Put it to the queue. */
VBOXHOSTCHANNELEVENT *pEvent = (VBOXHOSTCHANNELEVENT *)RTMemAlloc(sizeof(VBOXHOSTCHANNELEVENT) + cbEvent);
if (pEvent)
{
pEvent->u32ChannelHandle = u32ChannelHandle;
pEvent->u32Id = u32Id;
if (cbEvent)
{
pEvent->pvEvent = &pEvent[1];
memcpy(pEvent->pvEvent, pvEvent, cbEvent);
}
else
{
pEvent->pvEvent = NULL;
}
pEvent->cbEvent = cbEvent;
RTListAppend(&pClient->listEvents, &pEvent->NodeEvent);
}
}
vboxHostChannelUnlock();
vhcInstanceRelease(pInstance);
}
/* @thread provider */
static DECLCALLBACK(void) HostChannelCallbackDeleted(void *pvCallbacks, void *pvChannel)
{
vhcCallbackCtxDelete((VBOXHOSTCHCALLBACKCTX *)pvCallbacks);
}
int vboxHostChannelQuery(VBOXHOSTCHCLIENT *pClient,
const char *pszName,
uint32_t u32Code,
void *pvParm,
uint32_t cbParm,
void *pvData,
uint32_t cbData,
uint32_t *pu32SizeDataReturned)
{
HOSTCHLOG(("HostChannel: Query: (%d) name [%s], cbData %d\n", pClient->u32ClientID, pszName, cbData));
int rc = VINF_SUCCESS;
/* Look if there is a provider. */
VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pClient->pCtx, pszName);
if (pProvider)
{
pProvider->iface.HostChannelControl(NULL, u32Code,
pvParm, cbParm,
pvData, cbData, pu32SizeDataReturned);
vhcProviderRelease(pProvider);
}
else
{
rc = VERR_NOT_SUPPORTED;
}
return rc;
}
int vboxHostChannelRegister(const char *pszName,
const VBOXHOSTCHANNELINTERFACE *pInterface,
uint32_t cbInterface)
{
int rc = VINF_SUCCESS;
VBOXHOSTCHCTX *pCtx = &g_ctx;
VBOXHOSTCHPROVIDER *pProvider = (VBOXHOSTCHPROVIDER *)RTMemAllocZ(sizeof(VBOXHOSTCHPROVIDER));
if (pProvider)
{
pProvider->pCtx = pCtx;
pProvider->iface = *pInterface;
RTListInit(&pProvider->listChannels);
pProvider->pszName = RTStrDup(pszName);
if (pProvider->pszName)
{
vhcProviderAddRef(pProvider);
rc = vhcProviderRegister(pCtx, pProvider);
}
else
{
RTMemFree(pProvider);
rc = VERR_NO_MEMORY;
}
}
else
{
rc = VERR_NO_MEMORY;
}
return rc;
}
int vboxHostChannelUnregister(const char *pszName)
{
int rc = VINF_SUCCESS;
VBOXHOSTCHCTX *pCtx = &g_ctx;
VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pCtx, pszName);
if (pProvider)
{
rc = vhcProviderUnregister(pProvider);
vhcProviderRelease(pProvider);
}
return rc;
}