Index: /trunk/src/VBox/HostServices/HostChannel/HostChannel.cpp
===================================================================
--- /trunk/src/VBox/HostServices/HostChannel/HostChannel.cpp	(revision 43352)
+++ /trunk/src/VBox/HostServices/HostChannel/HostChannel.cpp	(revision 43352)
@@ -0,0 +1,753 @@
+/** @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);
+
+
+/* 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;
+};
+
+/* Only one service instance is supported. */
+static VBOXHOSTCHCTX g_ctx = { false };
+
+static VBOXHOSTCHANNELCALLBACKS g_callbacks = 
+{
+    HostChannelCallbackEvent
+};
+
+
+/*
+ * 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)
+{
+    /* @todo free u32Handle? */
+}
+
+static int32_t vhcInstanceAddRef(VBOXHOSTCHINSTANCE *pInstance)
+{
+    return ASMAtomicIncS32(&pInstance->cRefs);
+}
+
+static void vhcInstanceRelease(VBOXHOSTCHINSTANCE *pInstance)
+{
+    int32_t c = ASMAtomicDecS32(&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))
+            {
+                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->u32Handle == u32Handle)
+            {
+                pInstance = pIter;
+
+                vhcInstanceAddRef(pInstance);
+
+                break;
+            }
+        }
+
+        vboxHostChannelUnlock();
+    }
+
+    return pInstance;
+}
+
+static VBOXHOSTCHINSTANCE *vhcInstanceFindByChannelPtr(VBOXHOSTCHCLIENT *pClient, void *pvChannel)
+{
+    VBOXHOSTCHINSTANCE *pInstance = NULL;
+
+    int rc = vboxHostChannelLock();
+
+    if (RT_SUCCESS(rc))
+    {
+        VBOXHOSTCHINSTANCE *pIter;
+        RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
+        {
+            if (pIter->pvChannel == pvChannel)
+            {
+                pInstance = pIter;
+
+                vhcInstanceAddRef(pInstance);
+
+                break;
+            }
+        }
+
+        vboxHostChannelUnlock();
+    }
+
+    return pInstance;
+}
+
+static void vhcInstanceDetach(VBOXHOSTCHINSTANCE *pInstance)
+{
+    if (pInstance->pProvider)
+    {
+        pInstance->pProvider->iface.HostChannelDetach(pInstance->pvChannel);
+        RTListNodeRemove(&pInstance->nodeProvider);
+        vhcProviderRelease(pInstance->pProvider);
+        vhcInstanceRelease(pInstance); /* Not in the list anymore. */
+    }
+
+    RTListNodeRemove(&pInstance->nodeClient);
+    vhcInstanceRelease(pInstance); /* Not in the list anymore. */
+}
+
+
+/*
+ * 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);
+
+    return VINF_SUCCESS;
+}
+
+void vboxHostChannelClientDisconnect(VBOXHOSTCHCLIENT *pClient)
+{
+    /* 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))
+        {
+            void *pvChannel = NULL;
+            rc = pProvider->iface.HostChannelAttach(pProvider->iface.pvProvider,
+                                                    &pvChannel,
+                                                    u32Flags,
+                                                    &g_callbacks, pClient);
+            if (RT_SUCCESS(rc))
+            {
+                vhcProviderAddRef(pProvider);
+                pInstance->pProvider = pProvider;
+
+                pInstance->pClient = pClient;
+                pInstance->pvChannel = pvChannel;
+
+                vhcInstanceAddRef(pInstance); /* Referenced by the list client's channels. */
+                RTListAppend(&pClient->listChannels, &pInstance->nodeClient);
+
+                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));
+            }
+
+            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
+        && 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
+        && 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
+        && 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;
+
+/* This is called under the lock. */
+int vboxHostChannelQueryEvent(VBOXHOSTCHCLIENT *pClient,
+                              bool *pfEvent,
+                              uint32_t *pu32Handle,
+                              uint32_t *pu32Id,
+                              void *pvParm,
+                              uint32_t cbParm,
+                              uint32_t *pcbParmOut)
+{
+    /* 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);
+
+        *pfEvent = true;
+        *pu32Handle = pEvent->u32ChannelHandle;
+        *pu32Id = pEvent->u32Id;
+
+        uint32_t cbToCopy = RT_MIN(cbParm, pEvent->cbEvent);
+
+        HOSTCHLOG(("HostChannel: QueryEvent: (%d), cbParm %d, cbEvent %d\n",
+                        pClient->u32ClientID, cbParm, pEvent->cbEvent));
+
+        if (cbToCopy > 0)
+        {
+            memcpy(pvParm, pEvent->pvEvent, cbToCopy);
+        }
+
+        *pcbParmOut = cbToCopy;
+
+        RTMemFree(pEvent);
+    }
+    else
+    {
+        /* Tell the caller that there is no event. */
+        *pfEvent = false;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvChannel,
+                                                   uint32_t u32Id, const void *pvEvent, uint32_t cbEvent)
+{
+    VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvCallbacks;
+
+    VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFindByChannelPtr(pClient, pvChannel);
+
+    HOSTCHLOG(("HostChannel: CallbackEvent: (%d) instance %p\n",
+               pClient->u32ClientID, pInstance));
+
+    if (!pInstance)
+    {
+#ifdef DEBUG_sunlover
+        AssertFailed();
+#endif
+        return;
+    }
+
+    int rc = vboxHostChannelLock();
+    if (RT_FAILURE(rc))
+    {
+        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[0];
+                memcpy(pEvent->pvEvent, pvEvent, cbEvent);
+            }
+            else
+            {
+                pEvent->pvEvent = NULL;
+            }
+
+            pEvent->cbEvent = cbEvent;
+
+            if (RT_SUCCESS(rc))
+            {
+                RTListAppend(&pClient->listEvents, &pEvent->NodeEvent);
+            }
+            else
+            {
+                RTMemFree(pEvent);
+            }
+        }
+    }
+
+    vboxHostChannelUnlock();
+}
+
+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;
+}
Index: /trunk/src/VBox/HostServices/HostChannel/HostChannel.h
===================================================================
--- /trunk/src/VBox/HostServices/HostChannel/HostChannel.h	(revision 43352)
+++ /trunk/src/VBox/HostServices/HostChannel/HostChannel.h	(revision 43352)
@@ -0,0 +1,115 @@
+/* @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.
+ */
+
+#ifndef __VBOXHOSTCHANNEL__H
+#define __VBOXHOSTCHANNEL__H
+
+#include <iprt/list.h>
+
+#define LOG_GROUP LOG_GROUP_HGCM
+#include <VBox/log.h>
+#include <VBox/HostServices/VBoxHostChannel.h>
+
+#define HOSTCHLOG Log
+
+#ifdef DEBUG_sunlover
+# undef HOSTCHLOG
+# define HOSTCHLOG LogRel
+#endif /* DEBUG_sunlover */
+
+struct VBOXHOSTCHCTX;
+typedef struct VBOXHOSTCHCTX VBOXHOSTCHCTX;
+
+typedef struct VBOXHOSTCHCLIENT
+{
+    RTLISTNODE nodeClient;
+
+    VBOXHOSTCHCTX *pCtx;
+
+    uint32_t u32ClientID;
+
+    RTLISTANCHOR listChannels;
+    uint32_t volatile u32HandleSrc;
+
+    RTLISTANCHOR listEvents;
+
+    bool fAsync;        /* Guest is waiting for a message. */
+
+    struct {
+        VBOXHGCMCALLHANDLE callHandle;
+        VBOXHGCMSVCPARM *paParms;
+    } async;
+
+} VBOXHOSTCHCLIENT;
+
+
+/*
+ * The service functions. Locking is between the service thread and the host channel provider thread.
+ */
+int vboxHostChannelLock(void);
+void vboxHostChannelUnlock(void);
+
+int vboxHostChannelInit(void);
+void vboxHostChannelDestroy(void);
+
+int vboxHostChannelClientConnect(VBOXHOSTCHCLIENT *pClient);
+void vboxHostChannelClientDisconnect(VBOXHOSTCHCLIENT *pClient);
+
+int vboxHostChannelAttach(VBOXHOSTCHCLIENT *pClient,
+                          uint32_t *pu32Handle,
+                          const char *pszName,
+                          uint32_t u32Flags);
+int vboxHostChannelDetach(VBOXHOSTCHCLIENT *pClient,
+                          uint32_t u32Handle);
+
+int vboxHostChannelSend(VBOXHOSTCHCLIENT *pClient,
+                        uint32_t u32Handle,
+                        const void *pvData,
+                        uint32_t cbData);
+int vboxHostChannelRecv(VBOXHOSTCHCLIENT *pClient,
+                        uint32_t u32Handle,
+                        void *pvData,
+                        uint32_t cbData,
+                        uint32_t *pu32DataReceived,
+                        uint32_t *pu32DataRemaining);
+int vboxHostChannelControl(VBOXHOSTCHCLIENT *pClient,
+                           uint32_t u32Handle,
+                           uint32_t u32Code,
+                           void *pvParm,
+                           uint32_t cbParm,
+                           void *pvData,
+                           uint32_t cbData,
+                           uint32_t *pu32SizeDataReturned);
+
+int vboxHostChannelQueryEvent(VBOXHOSTCHCLIENT *pClient,
+                              bool *pfEvent,
+                              uint32_t *pu32Handle,
+                              uint32_t *pu32Id,
+                              void *pvParm,
+                              uint32_t cbParm,
+                              uint32_t *pcbParmOut);
+
+int vboxHostChannelRegister(const char *pszName,
+                            const VBOXHOSTCHANNELINTERFACE *pInterface,
+                            uint32_t cbInterface);
+int vboxHostChannelUnregister(const char *pszName);
+
+
+void vboxHostChannelReportAsync(VBOXHOSTCHCLIENT *pClient, uint32_t u32ChannelHandle,
+                                uint32_t u32Id, const void *pvEvent, uint32_t cbEvent);
+
+#endif /* __VBOXHOSTCHANNEL__H */
Index: /trunk/src/VBox/HostServices/HostChannel/Makefile.kmk
===================================================================
--- /trunk/src/VBox/HostServices/HostChannel/Makefile.kmk	(revision 43352)
+++ /trunk/src/VBox/HostServices/HostChannel/Makefile.kmk	(revision 43352)
@@ -0,0 +1,38 @@
+# $Id$
+## @file
+# Sub-Makefile for the Host Channel Service.
+#
+
+#
+# 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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# The service DLL.
+#
+DLLS += VBoxHostChannel
+VBoxHostChannel_TEMPLATE  = VBOXR3
+VBoxHostChannel_DEFS      = VBOX_WITH_HGCM
+VBoxHostChannel_INCS.win  = \
+	$(VBOX_PATH_SDK)
+
+VBoxHostChannel_SOURCES = \
+	service.cpp \
+	HostChannel.cpp
+
+VBoxHostChannel_LIBS = \
+	$(LIB_VMM) \
+	$(LIB_RUNTIME)
+
+include $(FILE_KBUILD_SUB_FOOTER)
Index: /trunk/src/VBox/HostServices/HostChannel/service.cpp
===================================================================
--- /trunk/src/VBox/HostServices/HostChannel/service.cpp	(revision 43352)
+++ /trunk/src/VBox/HostServices/HostChannel/service.cpp	(revision 43352)
@@ -0,0 +1,874 @@
+/* $Id$ */
+/* @file
+ * Host Channel: Host service entry points.
+ */
+
+/*
+ * 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.
+ */
+
+
+/*
+ * The HostChannel host service provides a generic proxy between a host's
+ * channel provider and a client running in the guest.
+ *
+ * Host providers must register via a HostCall.
+ *
+ * A guest client can connect to a host provider and send/receive data.
+ *
+ * GuestCalls:
+ *    * Attach      - attach to a host channel
+ *    * Detach      - completely detach from a channel
+ *    * Send        - send data from the guest to the channel
+ *    * Recv        - non blocking read of available data from the channel
+ *    * Control     - generic channel specific command exchange
+ *    * EventWait   - wait for a host event
+ *    * EventCancel - make the blocking EventWait call to return
+ * HostCalls:
+ *    * Register    - register a host channel
+ *    * Unregister  - unregister it
+ *
+ * The guest HGCM client connects to the service. The client can attach multiple channels.
+ *
+ */
+
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <VBox/vmm/ssm.h>
+
+#include "HostChannel.h"
+
+
+static void VBoxHGCMParmUInt32Set(VBOXHGCMSVCPARM *pParm, uint32_t u32)
+{
+    pParm->type = VBOX_HGCM_SVC_PARM_32BIT;
+    pParm->u.uint32 = u32;
+}
+
+static int VBoxHGCMParmUInt32Get(VBOXHGCMSVCPARM *pParm, uint32_t *pu32)
+{
+    if (pParm->type == VBOX_HGCM_SVC_PARM_32BIT)
+    {
+        *pu32 = pParm->u.uint32;
+        return VINF_SUCCESS;
+    }
+
+    AssertFailed();
+    return VERR_INVALID_PARAMETER;
+}
+
+static void VBoxHGCMParmPtrSet(VBOXHGCMSVCPARM *pParm, void *pv, uint32_t cb)
+{
+    pParm->type             = VBOX_HGCM_SVC_PARM_PTR;
+    pParm->u.pointer.size   = cb;
+    pParm->u.pointer.addr   = pv;
+}
+
+static int VBoxHGCMParmPtrGet(VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb)
+{
+    if (pParm->type == VBOX_HGCM_SVC_PARM_PTR)
+    {
+        *ppv = pParm->u.pointer.addr;
+        *pcb = pParm->u.pointer.size;
+        return VINF_SUCCESS;
+    }
+
+    AssertFailed();
+    return VERR_INVALID_PARAMETER;
+}
+
+
+static PVBOXHGCMSVCHELPERS g_pHelpers = NULL;
+
+static RTCRITSECT g_critsect;
+
+/*
+ * Helpers.
+ */
+
+int vboxHostChannelLock(void)
+{
+    return RTCritSectEnter(&g_critsect);
+}
+
+void vboxHostChannelUnlock(void)
+{
+    RTCritSectLeave(&g_critsect);
+}
+
+/* This is called under the lock. */
+void vboxHostChannelReportAsync(VBOXHOSTCHCLIENT *pClient,
+                                uint32_t u32ChannelHandle,
+                                uint32_t u32Id,
+                                const void *pvEvent,
+                                uint32_t cbEvent)
+{
+    if (cbEvent > 0)
+    {
+        void *pvParm = NULL;
+        uint32_t cbParm = 0;
+
+        VBoxHGCMParmPtrGet(&pClient->async.paParms[2], &pvParm, &cbParm);
+
+        uint32_t cbToCopy = RT_MIN(cbParm, cbEvent);
+        if (cbToCopy > 0)
+        {
+            memcpy(pvParm, pvEvent, cbToCopy);
+        }
+    }
+
+    VBoxHGCMParmUInt32Set(&pClient->async.paParms[0], u32ChannelHandle);
+    VBoxHGCMParmUInt32Set(&pClient->async.paParms[1], u32Id);
+    VBoxHGCMParmUInt32Set(&pClient->async.paParms[3], cbEvent);
+
+    LogRelFlow(("svcCall: CallComplete for pending\n"));
+
+    g_pHelpers->pfnCallComplete (pClient->async.callHandle, VINF_SUCCESS);
+}
+
+
+/*
+ *  Service entry points.
+ */
+
+static DECLCALLBACK(int) svcUnload(void *pvService)
+{
+    NOREF(pvService);
+    vboxHostChannelDestroy();
+    RTCritSectDelete(&g_critsect);
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) svcDisconnect(void *pvService, uint32_t u32ClientID, void *pvClient)
+{
+    NOREF(pvService);
+
+    VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+    vboxHostChannelClientDisconnect(pClient);
+
+    memset(pClient, 0, sizeof(VBOXHOSTCHCLIENT));
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) svcConnect(void *pvService, uint32_t u32ClientID, void *pvClient)
+{
+    VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+    int rc = VINF_SUCCESS;
+
+    /* Register the client. */
+    memset(pClient, 0, sizeof(VBOXHOSTCHCLIENT));
+
+    pClient->u32ClientID = u32ClientID;
+
+    rc = vboxHostChannelClientConnect(pClient);
+
+    LogRel2(("svcConnect: rc = %Rrc\n", rc));
+
+    return rc;
+}
+
+static DECLCALLBACK(void) svcCall(void *pvService,
+                                  VBOXHGCMCALLHANDLE callHandle,
+                                  uint32_t u32ClientID,
+                                  void *pvClient,
+                                  uint32_t u32Function,
+                                  uint32_t cParms,
+                                  VBOXHGCMSVCPARM paParms[])
+{
+    NOREF(pvService);
+
+    int rc = VINF_SUCCESS;
+
+    LogRel2(("svcCall: u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
+             u32ClientID, u32Function, cParms, paParms));
+
+    VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+    bool fAsynchronousProcessing = false;
+
+#ifdef DEBUG
+    uint32_t i;
+
+    for (i = 0; i < cParms; i++)
+    {
+        /** @todo parameters other than 32 bit */
+        LogRel2(("    pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32));
+    }
+#endif
+
+    switch (u32Function)
+    {
+        case VBOX_HOST_CHANNEL_FN_ATTACH:
+        {
+            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_ATTACH\n"));
+
+            if (cParms != 3)
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_PTR     /* name */
+                     || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT   /* flags */
+                     || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT   /* handle */
+                    )
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else
+            {
+                uint32_t u32Flags;
+                void *pvName;
+                uint32_t cbName;
+
+                rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);
+
+                if (RT_SUCCESS(rc))
+                {
+                    rc = VBoxHGCMParmUInt32Get(&paParms[1], &u32Flags);
+
+                    if (RT_SUCCESS(rc))
+                    {
+                        uint32_t u32Handle = 0;
+
+                        rc = vboxHostChannelAttach(pClient, &u32Handle, (const char *)pvName, u32Flags);
+
+                        if (RT_SUCCESS(rc))
+                        {
+                            VBoxHGCMParmUInt32Set(&paParms[2], u32Handle);
+                        }
+                    }
+                }
+            }
+        } break;
+
+        case VBOX_HOST_CHANNEL_FN_DETACH:
+        {
+            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_DETACH\n"));
+
+            if (cParms != 1)
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT   /* handle */
+                    )
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else
+            {
+                uint32_t u32Handle;
+
+                rc = VBoxHGCMParmUInt32Get(&paParms[0], &u32Handle);
+
+                if (RT_SUCCESS(rc))
+                {
+                    rc = vboxHostChannelDetach(pClient, u32Handle);
+                }
+            }
+        } break;
+
+        case VBOX_HOST_CHANNEL_FN_SEND:
+        {
+            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_SEND\n"));
+
+            if (cParms != 2)
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT   /* handle */
+                     || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR     /* data */
+                    )
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else
+            {
+                uint32_t u32Handle;
+                void *pvData;
+                uint32_t cbData;
+
+                rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle);
+
+                if (RT_SUCCESS (rc))
+                {
+                    rc = VBoxHGCMParmPtrGet (&paParms[1], &pvData, &cbData);
+
+                    if (RT_SUCCESS (rc))
+                    {
+                        rc = vboxHostChannelSend(pClient, u32Handle, pvData, cbData);
+                    }
+                }
+            }
+        } break;
+
+        case VBOX_HOST_CHANNEL_FN_RECV:
+        {
+            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_RECV\n"));
+
+            if (cParms != 4)
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT   /* handle */
+                     || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR     /* data */
+                     || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT   /* sizeReceived */
+                     || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT   /* sizeRemaining */
+                    )
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else
+            {
+                uint32_t u32Handle;
+                void *pvData;
+                uint32_t cbData;
+
+                rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle);
+
+                if (RT_SUCCESS (rc))
+                {
+                    rc = VBoxHGCMParmPtrGet (&paParms[1], &pvData, &cbData);
+
+                    if (RT_SUCCESS (rc))
+                    {
+                        uint32_t u32SizeReceived = 0;
+                        uint32_t u32SizeRemaining = 0;
+
+                        rc = vboxHostChannelRecv(pClient, u32Handle,
+                                                 pvData, cbData,
+                                                 &u32SizeReceived, &u32SizeRemaining);
+
+                        if (RT_SUCCESS(rc))
+                        {
+                            VBoxHGCMParmUInt32Set(&paParms[2], u32SizeReceived);
+                            VBoxHGCMParmUInt32Set(&paParms[3], u32SizeRemaining);
+                        }
+                    }
+                }
+            }
+        } break;
+
+        case VBOX_HOST_CHANNEL_FN_CONTROL:
+        {
+            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_CONTROL\n"));
+
+            if (cParms != 5)
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT   /* handle */
+                     || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT   /* code */
+                     || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR     /* parm */
+                     || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR     /* data */
+                     || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT   /* sizeDataReturned */
+                    )
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else
+            {
+                uint32_t u32Handle;
+                uint32_t u32Code;
+                void *pvParm;
+                uint32_t cbParm;
+                void *pvData;
+                uint32_t cbData;
+
+                rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle);
+
+                if (RT_SUCCESS (rc))
+                {
+                    rc = VBoxHGCMParmUInt32Get (&paParms[1], &u32Code);
+
+                    if (RT_SUCCESS (rc))
+                    {
+                        rc = VBoxHGCMParmPtrGet (&paParms[2], &pvParm, &cbParm);
+
+                        if (RT_SUCCESS (rc))
+                        {
+                            rc = VBoxHGCMParmPtrGet (&paParms[3], &pvData, &cbData);
+
+                            if (RT_SUCCESS (rc))
+                            {
+                                uint32_t u32SizeDataReturned = 0;
+
+                                rc = vboxHostChannelControl(pClient, u32Handle, u32Code,
+                                                            pvParm, cbParm,
+                                                            pvData, cbData, &u32SizeDataReturned);
+                                if (RT_SUCCESS(rc))
+                                {
+                                    VBoxHGCMParmUInt32Set(&paParms[4], u32SizeDataReturned);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } break;
+
+        case VBOX_HOST_CHANNEL_FN_EVENT_WAIT:
+        {
+            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_EVENT_WAIT\n"));
+
+            if (cParms != 4)
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT   /* handle */
+                     || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT   /* id */
+                     || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR     /* parm */
+                     || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT   /* sizeReturned */
+                    )
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else
+            {
+                void *pvParm;
+                uint32_t cbParm;
+
+                rc = VBoxHGCMParmPtrGet(&paParms[2], &pvParm, &cbParm);
+
+                if (RT_SUCCESS(rc))
+                {
+                    /* This is accessed from the SVC thread and other threads. */
+                    rc = vboxHostChannelLock();
+
+                    if (RT_SUCCESS(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;
+                        }
+
+                        bool fEvent = false;
+                        uint32_t u32Handle = 0;
+                        uint32_t u32Id = 0;
+                        uint32_t cbParmOut = 0;
+
+                        rc = vboxHostChannelQueryEvent(pClient, &fEvent, &u32Handle, &u32Id,
+                                                       pvParm, cbParm, &cbParmOut);
+
+                        if (RT_SUCCESS(rc))
+                        {
+                            if (fEvent)
+                            {
+                                VBoxHGCMParmUInt32Set(&paParms[0], u32Handle);
+                                VBoxHGCMParmUInt32Set(&paParms[1], u32Id);
+                                VBoxHGCMParmUInt32Set(&paParms[3], cbParmOut);
+                            }
+                            else
+                            {
+                                /* No event available at the time. Process asynchronously. */
+                                fAsynchronousProcessing = true;
+
+                                pClient->fAsync           = true;
+                                pClient->async.callHandle = callHandle;
+                                pClient->async.paParms    = paParms;
+
+                                LogRel2(("svcCall: async.\n"));
+                            }
+                        }
+
+                        vboxHostChannelUnlock();
+                    }
+                }
+            }
+        } break;
+
+        case VBOX_HOST_CHANNEL_FN_EVENT_CANCEL:
+        {
+            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_EVENT_CANCEL\n"));
+
+            if (cParms != 0)
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else
+            {
+                /* This is accessed from the SVC thread and other threads. */
+                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();
+                }
+            }
+        } break;
+
+        default:
+        {
+            rc = VERR_NOT_IMPLEMENTED;
+        }
+    }
+
+    LogRelFlow(("svcCall: rc = %Rrc, async %d\n", rc, fAsynchronousProcessing));
+
+    if (!fAsynchronousProcessing)
+    {
+        g_pHelpers->pfnCallComplete(callHandle, rc);
+    }
+}
+
+static DECLCALLBACK(int) svcHostCall(void *pvService,
+                                     uint32_t u32Function,
+                                     uint32_t cParms,
+                                     VBOXHGCMSVCPARM paParms[])
+{
+    NOREF(pvService);
+
+    int rc = VINF_SUCCESS;
+
+    LogRel2(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n",
+             u32Function, cParms, paParms));
+
+    switch (u32Function)
+    {
+        case VBOX_HOST_CHANNEL_HOST_FN_REGISTER:
+        {
+            LogRel2(("svcCall: VBOX_HOST_CHANNEL_HOST_FN_REGISTER\n"));
+
+            if (cParms != 2)
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* name */
+                     || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* iface */
+                    )
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else
+            {
+                void *pvName;
+                uint32_t cbName;
+                void *pvInterface;
+                uint32_t cbInterface;
+
+                rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);
+
+                if (RT_SUCCESS(rc))
+                {
+                    rc = VBoxHGCMParmPtrGet(&paParms[1], &pvInterface, &cbInterface);
+
+                    if (RT_SUCCESS(rc))
+                    {
+                        rc = vboxHostChannelRegister((const char *)pvName,
+                                                     (VBOXHOSTCHANNELINTERFACE *)pvInterface, cbInterface);
+                    }
+                }
+            }
+        } break;
+
+        case VBOX_HOST_CHANNEL_HOST_FN_UNREGISTER:
+        {
+            LogRel2(("svcCall: VBOX_HOST_CHANNEL_HOST_FN_UNREGISTER\n"));
+
+            if (cParms != 1)
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* name */
+                    )
+            {
+                rc = VERR_INVALID_PARAMETER;
+            }
+            else
+            {
+                void *pvName;
+                uint32_t cbName;
+
+                rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);
+
+                if (RT_SUCCESS(rc))
+                {
+                    rc = vboxHostChannelUnregister((const char *)pvName);
+                }
+            }
+        } break;
+
+        default:
+            break;
+    }
+
+    LogRelFlow(("svcHostCall: rc = %Rrc\n", rc));
+    return rc;
+}
+
+#if 0
+/** If the client in the guest is waiting for a read operation to complete
+ * then complete it, otherwise return.  See the protocol description in the
+ * shared clipboard module description. */
+void vboxSvcClipboardCompleteReadData(VBOXHOSTCHCLIENT *pClient, int rc, uint32_t cbActual)
+{
+    VBOXHGCMCALLHANDLE callHandle = NULL;
+    VBOXHGCMSVCPARM *paParms = NULL;
+    bool fReadPending = false;
+    if (vboxSvcClipboardLock())  /* if not can we do anything useful? */
+    {
+        callHandle   = pClient->asyncRead.callHandle;
+        paParms      = pClient->asyncRead.paParms;
+        fReadPending = pClient->fReadPending;
+        pClient->fReadPending = false;
+        vboxSvcClipboardUnlock();
+    }
+    if (fReadPending)
+    {
+        VBoxHGCMParmUInt32Set (&paParms[2], cbActual);
+        g_pHelpers->pfnCallComplete (callHandle, rc);
+    }
+}
+
+/**
+ * SSM descriptor table for the VBOXHOSTCHCLIENT structure.
+ */
+static SSMFIELD const g_aClipboardClientDataFields[] =
+{
+    SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, u32ClientID),  /* for validation purposes */
+    SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgQuit),
+    SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgReadData),
+    SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgFormats),
+    SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, u32RequestedFormat),
+    SSMFIELD_ENTRY_TERM()
+};
+
+static DECLCALLBACK(int) svcSaveState(void *pvService, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
+{
+    NOREF(pvService);
+
+    /* If there are any pending requests, they must be completed here. Since
+     * the service is single threaded, there could be only requests
+     * which the service itself has postponed.
+     *
+     * HGCM knows that the state is being saved and that the pfnComplete
+     * calls are just clean ups. These requests are saved by the VMMDev.
+     *
+     * When the state will be restored, these requests will be reissued
+     * by VMMDev. The service therefore must save state as if there were no
+     * pending request.
+     */
+    LogRel2 (("svcSaveState: u32ClientID = %d\n", u32ClientID));
+
+    VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+    /* This field used to be the length. We're using it as a version field
+       with the high bit set. */
+    SSMR3PutU32 (pSSM, UINT32_C (0x80000002));
+    int rc = SSMR3PutStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
+    AssertRCReturn (rc, rc);
+
+    if (pClient->fAsync)
+    {
+        g_pHelpers->pfnCallComplete (pClient->async.callHandle, VINF_SUCCESS /* error code is not important here. */);
+        pClient->fAsync = false;
+    }
+
+    vboxSvcClipboardCompleteReadData (pClient, VINF_SUCCESS, 0);
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * This structure corresponds to the original layout of the
+ * VBOXHOSTCHCLIENT structure.  As the structure was saved as a whole
+ * when saving state, we need to remember it forever in order to preserve
+ * compatibility.
+ *
+ * (Starting with 3.1 this is no longer used.)
+ *
+ * @remarks Putting this outside svcLoadState to avoid visibility warning caused
+ *          by -Wattributes.
+ */
+typedef struct CLIPSAVEDSTATEDATA
+{
+    struct CLIPSAVEDSTATEDATA *pNext;
+    struct CLIPSAVEDSTATEDATA *pPrev;
+
+    VBOXCLIPBOARDCONTEXT *pCtx;
+
+    uint32_t u32ClientID;
+
+    bool fAsync: 1; /* Guest is waiting for a message. */
+
+    bool fMsgQuit: 1;
+    bool fMsgReadData: 1;
+    bool fMsgFormats: 1;
+
+    struct {
+        VBOXHGCMCALLHANDLE callHandle;
+        VBOXHGCMSVCPARM *paParms;
+    } async;
+
+    struct {
+         void *pv;
+         uint32_t cb;
+         uint32_t u32Format;
+    } data;
+
+    uint32_t u32AvailableFormats;
+    uint32_t u32RequestedFormat;
+
+} CLIPSAVEDSTATEDATA;
+
+static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
+{
+    LogRel2 (("svcLoadState: u32ClientID = %d\n", u32ClientID));
+
+    VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+    /* Existing client can not be in async state yet. */
+    Assert (!pClient->fAsync);
+
+    /* Save the client ID for data validation. */
+    /** @todo isn't this the same as u32ClientID? Playing safe for now... */
+    uint32_t const u32ClientIDOld = pClient->u32ClientID;
+
+    /* Restore the client data. */
+    uint32_t lenOrVer;
+    int rc = SSMR3GetU32 (pSSM, &lenOrVer);
+    AssertRCReturn (rc, rc);
+    if (lenOrVer == UINT32_C (0x80000002))
+    {
+        rc = SSMR3GetStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
+        AssertRCReturn (rc, rc);
+    }
+    else if (lenOrVer == (SSMR3HandleHostBits (pSSM) == 64 ? 72 : 48))
+    {
+        /**
+         * SSM descriptor table for the CLIPSAVEDSTATEDATA structure.
+         */
+        static SSMFIELD const s_aClipSavedStateDataFields30[] =
+        {
+            SSMFIELD_ENTRY_IGN_HCPTR(       CLIPSAVEDSTATEDATA, pNext),
+            SSMFIELD_ENTRY_IGN_HCPTR(       CLIPSAVEDSTATEDATA, pPrev),
+            SSMFIELD_ENTRY_IGN_HCPTR(       CLIPSAVEDSTATEDATA, pCtx),
+            SSMFIELD_ENTRY(                 CLIPSAVEDSTATEDATA, u32ClientID),
+            SSMFIELD_ENTRY_CUSTOM(fMsgQuit+fMsgReadData+fMsgFormats, RT_OFFSETOF(CLIPSAVEDSTATEDATA, u32ClientID) + 4, 4),
+            SSMFIELD_ENTRY_IGN_HCPTR(       CLIPSAVEDSTATEDATA, async.callHandle),
+            SSMFIELD_ENTRY_IGN_HCPTR(       CLIPSAVEDSTATEDATA, async.paParms),
+            SSMFIELD_ENTRY_IGNORE(          CLIPSAVEDSTATEDATA, data.pv),
+            SSMFIELD_ENTRY_IGNORE(          CLIPSAVEDSTATEDATA, data.cb),
+            SSMFIELD_ENTRY_IGNORE(          CLIPSAVEDSTATEDATA, data.u32Format),
+            SSMFIELD_ENTRY_IGNORE(          CLIPSAVEDSTATEDATA, u32AvailableFormats),
+            SSMFIELD_ENTRY(                 CLIPSAVEDSTATEDATA, u32RequestedFormat),
+            SSMFIELD_ENTRY_TERM()
+        };
+
+        CLIPSAVEDSTATEDATA savedState;
+        RT_ZERO (savedState);
+        rc = SSMR3GetStructEx (pSSM, &savedState, sizeof(savedState), SSMSTRUCT_FLAGS_MEM_BAND_AID,
+                               &s_aClipSavedStateDataFields30[0], NULL);
+        AssertRCReturn (rc, rc);
+
+        pClient->fMsgQuit           = savedState.fMsgQuit;
+        pClient->fMsgReadData       = savedState.fMsgReadData;
+        pClient->fMsgFormats        = savedState.fMsgFormats;
+        pClient->u32RequestedFormat = savedState.u32RequestedFormat;
+    }
+    else
+    {
+        LogRel (("Client data size mismatch: got %#x\n", lenOrVer));
+        return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+    }
+
+    /* Verify the client ID. */
+    if (pClient->u32ClientID != u32ClientIDOld)
+    {
+        LogRel (("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClient->u32ClientID));
+        pClient->u32ClientID = u32ClientIDOld;
+        return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+    }
+
+    /* Actual host data are to be reported to guest (SYNC). */
+    vboxClipboardSync (pClient);
+
+    return VINF_SUCCESS;
+}
+#endif
+
+static int svcInit(void)
+{
+    int rc = RTCritSectInit(&g_critsect);
+
+    if (RT_SUCCESS (rc))
+    {
+        rc = vboxHostChannelInit();
+
+        /* Clean up on failure, because 'svnUnload' will not be called
+         * if the 'svcInit' returns an error.
+         */
+        if (RT_FAILURE(rc))
+        {
+            RTCritSectDelete(&g_critsect);
+        }
+    }
+
+    return rc;
+}
+
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
+{
+    int rc = VINF_SUCCESS;
+
+    LogRelFlowFunc(("pTable = %p\n", pTable));
+
+    if (!pTable)
+    {
+        rc = VERR_INVALID_PARAMETER;
+    }
+    else
+    {
+        LogRel2(("VBoxHGCMSvcLoad: pTable->cbSize = %d, pTable->u32Version = 0x%08X\n",
+                  pTable->cbSize, pTable->u32Version));
+
+        if (   pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
+            || pTable->u32Version != VBOX_HGCM_SVC_VERSION)
+        {
+            rc = VERR_INVALID_PARAMETER;
+        }
+        else
+        {
+            g_pHelpers = pTable->pHelpers;
+
+            pTable->cbClient = sizeof(VBOXHOSTCHCLIENT);
+
+            pTable->pfnUnload     = svcUnload;
+            pTable->pfnConnect    = svcConnect;
+            pTable->pfnDisconnect = svcDisconnect;
+            pTable->pfnCall       = svcCall;
+            pTable->pfnHostCall   = svcHostCall;
+            pTable->pfnSaveState  = NULL; // svcSaveState;
+            pTable->pfnLoadState  = NULL; // svcLoadState;
+            pTable->pfnRegisterExtension  = NULL;
+            pTable->pvService     = NULL;
+
+            /* Service specific initialization. */
+            rc = svcInit();
+        }
+    }
+
+    return rc;
+}
