Index: trunk/src/VBox/Additions/solaris/Mouse/Makefile.kmk
===================================================================
--- trunk/src/VBox/Additions/solaris/Mouse/Makefile.kmk	(revision 42367)
+++ trunk/src/VBox/Additions/solaris/Mouse/Makefile.kmk	(revision 42368)
@@ -40,5 +40,5 @@
 vboxms_DEPS      += $(VBOX_SVN_REV_KMK)
 vboxms_SOURCES    = \
-	vboxmouse.c
+	vboxms.c
 vboxms_LIBS       = \
 	$(VBOX_LIB_VBGL_R0)
@@ -61,5 +61,5 @@
  tstVBoxMouse-solaris_TEMPLATE = VBOXR3TSTEXE
  tstVBoxMouse-solaris_SOURCES  = \
- 	vboxmouse.c \
+ 	vboxms.c \
  	testcase/tstVBoxMouse-solaris.c
  tstVBoxMouse-solaris_DEFS     = TESTCASE
Index: trunk/src/VBox/Additions/solaris/Mouse/vboxmouse.c
===================================================================
--- trunk/src/VBox/Additions/solaris/Mouse/vboxmouse.c	(revision 42367)
+++ 	(revision )
@@ -1,1433 +1,0 @@
-/* $Id$ */
-/** @file
- * VirtualBox Guest Additions Driver for Solaris.
- */
-
-/*
- * 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 contents of this file may alternatively be used under the terms
- * of the Common Development and Distribution License Version 1.0
- * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
- * VirtualBox OSE distribution, in which case the provisions of the
- * CDDL are applicable instead of those of the GPL.
- *
- * You may elect to license modified versions of this file under the
- * terms and conditions of either the GPL or the CDDL or both.
- */
-
-#define LOG_GROUP LOG_GROUP_DRV_MOUSE
-
-/******************************************************************************
-*   Header Files                                                              *
-******************************************************************************/
-
-#include <VBox/VBoxGuestLib.h>
-#include <VBox/log.h>
-#include <VBox/version.h>
-#include <iprt/assert.h>
-#include <iprt/asm.h>
-
-#ifndef TESTCASE
-# include <sys/modctl.h>
-# include <sys/msio.h>
-# include <sys/stat.h>
-# include <sys/ddi.h>
-# include <sys/strsun.h>
-# include <sys/stropts.h>
-# include <sys/sunddi.h>
-# include <sys/vuid_event.h>
-# include <sys/vuid_wheel.h>
-#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
-#else  /* TESTCASE */
-# undef IN_RING3
-# define IN_RING0
-#endif  /* TESTCASE */
-
-#ifdef TESTCASE  /* Include this last as we . */
-# include "testcase/solaris.h"
-# include <iprt/test.h>
-#endif  /* TESTCASE */
-
-
-/******************************************************************************
-*   Defined Constants And Macros                                              *
-******************************************************************************/
-
-/** The module name. */
-#define DEVICE_NAME              "vboxmouse"
-/** The module description as seen in 'modinfo'. */
-#define DEVICE_DESC              "VBoxMouseIntegr"
-
-
-/******************************************************************************
-*   Internal functions used in global structures                              *
-******************************************************************************/
-
-static int vbmsSolAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
-static int vbmsSolDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
-static int vbmsSolGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg,
-                          void **ppvResult);
-static int vbmsSolOpen(queue_t *pReadQueue, dev_t *pDev, int fFlag,
-                                int fMode, cred_t *pCred);
-static int vbmsSolClose(queue_t *pReadQueue, int fFlag, cred_t *pCred);
-static int vbmsSolWPut(queue_t *pWriteQueue, mblk_t *pMBlk);
-
-
-/******************************************************************************
-*   Driver global structures                                                  *
-******************************************************************************/
-
-#ifndef TESTCASE  /* I see no value in including these in the test. */
-
-/*
- * mod_info: STREAMS module information.
- */
-static struct module_info g_vbmsSolModInfo =
-{
-    0,                      /* module id number */
-    "vboxms",
-    0,                      /* minimum packet size */
-    INFPSZ,                 /* maximum packet size accepted */
-    512,                    /* high water mark for data flow control */
-    128                     /* low water mark */
-};
-
-/*
- * rinit: read queue structure for handling messages coming from below.  In
- * our case this means the host and the virtual hardware, so we do not need
- * the put and service procedures.
- */
-static struct qinit g_vbmsSolRInit =
-{
-    NULL,                /* put */
-    NULL,                /* service thread procedure */
-    vbmsSolOpen,
-    vbmsSolClose,
-    NULL,                /* reserved */
-    &g_vbmsSolModInfo,
-    NULL                 /* module statistics structure */
-};
-
-/* 
- * winit: write queue structure for handling messages coming from above.  Above
- * means user space applications: either Guest Additions user space tools or
- * applications reading pointer input.  Messages from the last most likely pass
- * through at least the "consms" console mouse streams module which multiplexes
- * hardware pointer drivers to a single virtual pointer.
- */
-static struct qinit g_vbmsSolWInit =
-{
-    vbmsSolWPut,
-    NULL,                   /* service thread procedure */
-    NULL,                   /* open */
-    NULL,                   /* close */
-    NULL,                   /* reserved */
-    &g_vbmsSolModInfo,
-    NULL                    /* module statistics structure */
-};
-
-/**
- * streamtab: for drivers that support char/block entry points.
- */
-static struct streamtab g_vbmsSolStreamTab =
-{
-    &g_vbmsSolRInit,
-    &g_vbmsSolWInit,
-    NULL,                   /* MUX rinit */
-    NULL                    /* MUX winit */
-};
-
-/**
- * cb_ops: for drivers that support char/block entry points
- */
-static struct cb_ops g_vbmsSolCbOps =
-{
-    nodev,                  /* open */
-    nodev,                  /* close */
-    nodev,                  /* b strategy */
-    nodev,                  /* b dump */
-    nodev,                  /* b print */
-    nodev,                  /* c read */
-    nodev,                  /* c write */
-    nodev,                  /* c ioctl */
-    nodev,                  /* c devmap */
-    nodev,                  /* c mmap */
-    nodev,                  /* c segmap */
-    nochpoll,               /* c poll */
-    ddi_prop_op,            /* property ops */
-    &g_vbmsSolStreamTab,
-    D_MP,
-    CB_REV                  /* revision */
-};
-
-/**
- * dev_ops: for driver device operations
- */
-static struct dev_ops g_vbmsSolDevOps =
-{
-    DEVO_REV,               /* driver build revision */
-    0,                      /* ref count */
-    vbmsSolGetInfo,
-    nulldev,                /* identify */
-    nulldev,                /* probe */
-    vbmsSolAttach,
-    vbmsSolDetach,
-    nodev,                  /* reset */
-    &g_vbmsSolCbOps,
-    NULL,                   /* bus operations */
-    nodev                   /* power */
-};
-
-/**
- * modldrv: export driver specifics to the kernel
- */
-static struct modldrv g_vbmsSolModule =
-{
-    &mod_driverops,         /* extern from kernel */
-    DEVICE_DESC " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
-    &g_vbmsSolDevOps
-};
-
-/**
- * modlinkage: export install/remove/info to the kernel.
- */
-static struct modlinkage g_vbmsSolModLinkage =
-{
-    MODREV_1,               /* loadable module system revision */
-    &g_vbmsSolModule,
-    NULL                    /* terminate array of linkage structures */
-};
-
-#else  /* TESTCASE */
-static void *g_vbmsSolModLinkage;
-#endif  /* TESTCASE */
-
-/**
- * State info for each open file handle.
- */
-typedef struct
-{
-    /** Device handle. */
-    dev_info_t           *pDip;
-    /** Mutex protecting the guest library against multiple initialistation or
-     * uninitialisation. */
-    kmutex_t              InitMtx;
-    /** Initialisation counter for the guest library. */
-    size_t                cInits;
-    /** The STREAMS write queue which we need for sending messages up to
-     * user-space. */
-    queue_t              *pWriteQueue;
-    /** Pre-allocated mouse status VMMDev request for use in the IRQ
-     * handler. */
-    VMMDevReqMouseStatus *pMouseStatusReq;
-    /* The current greatest horizontal pixel offset on the screen, used for
-     * absolute mouse position reporting.
-     */
-    int                   cMaxScreenX;
-    /* The current greatest vertical pixel offset on the screen, used for
-     * absolute mouse position reporting.
-     */
-    int                   cMaxScreenY;
-} VBMSSTATE, *PVBMSSTATE;
-
-
-/******************************************************************************
-*   Global Variables                                                          *
-******************************************************************************/
-
-/** Global driver state.  Actually this could be allocated dynamically. */
-static VBMSSTATE            g_OpenNodeState /* = { 0 } */;
-
-
-/******************************************************************************
-*   Kernel entry points                                                       *
-******************************************************************************/
-
-/** Driver initialisation. */
-int _init(void)
-{
-    int rc;
-    LogRelFlow((DEVICE_NAME ": built on " __DATE__ " at " __TIME__ "\n"));
-    mutex_init(&g_OpenNodeState.InitMtx, NULL, MUTEX_DRIVER, NULL);
-    /*
-     * Prevent module autounloading.
-     */
-    modctl_t *pModCtl = mod_getctl(&g_vbmsSolModLinkage);
-    if (pModCtl)
-        pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
-    else
-        LogRel((DEVICE_NAME ": failed to disable autounloading!\n"));
-    rc = mod_install(&g_vbmsSolModLinkage);
-
-    LogRel((DEVICE_NAME ": initialisation returning %d.\n", rc));
-    return rc;
-}
-
-
-#ifdef TESTCASE
-/** Simple test of the flow through _init. */
-static void test_init(RTTEST hTest)
-{
-    RTTestSub(hTest, "Testing _init");
-    RTTEST_CHECK(hTest, _init() == 0);
-}
-#endif
-
-
-/** Driver cleanup. */
-int _fini(void)
-{
-    int rc;
-
-    LogRelFlow((DEVICE_NAME ":_fini\n"));
-    rc = mod_remove(&g_vbmsSolModLinkage);
-    mutex_destroy(&g_OpenNodeState.InitMtx);
-
-    return rc;
-}
-
-
-/** Driver identification. */
-int _info(struct modinfo *pModInfo)
-{
-    int rc;
-    LogRelFlow((DEVICE_NAME ":_info\n"));
-    rc = mod_info(&g_vbmsSolModLinkage, pModInfo);
-    LogRelFlow((DEVICE_NAME ":_info returning %d\n", rc));
-    return rc;
-}
-
-
-/******************************************************************************
-*   Initialisation entry points                                               *
-******************************************************************************/
-
-/**
- * Attach entry point, to attach a device to the system or resume it.
- *
- * @param   pDip            The module structure instance.
- * @param   enmCmd          Attach type (ddi_attach_cmd_t)
- *
- * @return  corresponding solaris error code.
- */
-int vbmsSolAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
-{
-    LogRelFlow((DEVICE_NAME "::Attach\n"));
-    switch (enmCmd)
-    {
-        case DDI_ATTACH:
-        {
-            int rc;
-            int instance = ddi_get_instance(pDip);
-            /* Only one instance supported. */
-            if (!ASMAtomicCmpXchgPtr(&g_OpenNodeState.pDip, pDip, NULL))
-                return DDI_FAILURE;
-            rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, 0);
-            if (rc == DDI_SUCCESS)
-                return DDI_SUCCESS;
-            ASMAtomicWritePtr(&g_OpenNodeState.pDip, NULL);
-            return DDI_FAILURE;
-        }
-
-        case DDI_RESUME:
-        {
-            /** @todo implement resume for guest driver. */
-            return DDI_SUCCESS;
-        }
-
-        default:
-            return DDI_FAILURE;
-    }
-}
-
-
-/**
- * Detach entry point, to detach a device to the system or suspend it.
- *
- * @param   pDip            The module structure instance.
- * @param   enmCmd          Attach type (ddi_attach_cmd_t)
- *
- * @return  corresponding solaris error code.
- */
-int vbmsSolDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
-{
-    LogRelFlow((DEVICE_NAME "::Detach\n"));
-    switch (enmCmd)
-    {
-        case DDI_DETACH:
-        {
-            ddi_remove_minor_node(pDip, NULL);
-            ASMAtomicWritePtr(&g_OpenNodeState.pDip, NULL);
-            return DDI_SUCCESS;
-        }
-
-        case DDI_SUSPEND:
-        {
-            /** @todo implement suspend for guest driver. */
-            return DDI_SUCCESS;
-        }
-
-        default:
-            return DDI_FAILURE;
-    }
-}
-
-
-/**
- * Info entry point, called by solaris kernel for obtaining driver info.
- *
- * @param   pDip            The module structure instance (do not use).
- * @param   enmCmd          Information request type.
- * @param   pvArg           Type specific argument.
- * @param   ppvResult       Where to store the requested info.
- *
- * @return  corresponding solaris error code.
- */
-int vbmsSolGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg,
-                   void **ppvResult)
-{
-    LogRelFlow((DEVICE_NAME "::GetInfo\n"));
-
-    int rc = DDI_SUCCESS;
-    switch (enmCmd)
-    {
-        case DDI_INFO_DEVT2DEVINFO:
-            *ppvResult = (void *)g_OpenNodeState.pDip;
-            break;
-
-        case DDI_INFO_DEVT2INSTANCE:
-            *ppvResult = (void *)(uintptr_t)ddi_get_instance(g_OpenNodeState.pDip);
-            break;
-
-        default:
-            rc = DDI_FAILURE;
-            break;
-    }
-
-    NOREF(pvArg);
-    return rc;
-}
-
-
-/******************************************************************************
-*   Main code                                                                 *
-******************************************************************************/
-
-static void vbmsSolNotify(void *pvState);
-static void vbmsSolVUIDPutAbsEvent(PVBMSSTATE pState, ushort_t cEvent,
-                                   int cValue);
-
-/**
- * Open callback for the read queue, which we use as a generic device open
- * handler.
- */
-int vbmsSolOpen(queue_t *pReadQueue, dev_t *pDev, int fFlag, int fMode,
-                 cred_t *pCred)
-{
-    PVBMSSTATE pState = NULL;
-    int rc = VINF_SUCCESS;
-
-    NOREF(fFlag);
-    NOREF(pCred);
-    LogRelFlow((DEVICE_NAME "::Open, pWriteQueue=%p\n", WR(pReadQueue)));
-
-    /*
-     * Sanity check on the mode parameter - only open as a driver, not a
-     * module, and we do cloning ourselves.
-     */
-    if (fMode)
-    {
-        LogRel(("::Open: invalid attempt to clone device."));
-        return EINVAL;
-    }
-
-    pState = &g_OpenNodeState;
-    mutex_enter(&pState->InitMtx);
-    /*
-     * Check and remember our STREAM queue.
-     */
-    if (   pState->pWriteQueue
-        && (pState->pWriteQueue != WR(pReadQueue)))
-    {
-        mutex_exit(&pState->InitMtx);
-        LogRel((DEVICE_NAME "::Open: unexpectedly called with a different queue to previous calls.  Exiting.\n"));
-        return EINVAL;
-    }
-    if (!pState->cInits)
-    {
-        /*
-         * Initialize IPRT R0 driver, which internally calls OS-specific r0
-         * init, and create a new session.
-         */
-        rc = VbglInit();
-        if (RT_SUCCESS(rc))
-        {
-            rc = VbglGRAlloc((VMMDevRequestHeader **)
-                             &pState->pMouseStatusReq,
-                             sizeof(*pState->pMouseStatusReq),
-                             VMMDevReq_GetMouseStatus);
-            if (RT_FAILURE(rc))
-                VbglTerminate();
-            else
-            {
-                int rc2;
-                /* Initialise user data for the queues to our state and
-                 * vice-versa. */
-                pState->pWriteQueue = WR(pReadQueue);
-                WR(pReadQueue)->q_ptr = (char *)pState;
-                pReadQueue->q_ptr = (char *)pState;
-                qprocson(pReadQueue);
-                /* Enable our IRQ handler. */
-                rc2 = VbglSetMouseNotifyCallback(vbmsSolNotify,
-                                                 (void *)pState);
-                if (RT_FAILURE(rc2))
-                    /* Log the failure.  I may well have not understood what
-                     * is going on here, and the logging may help me. */
-                    LogRelFlow(("Failed to install the event handler call-back, rc=%Rrc\n",
-                                rc2));
-            }
-        }
-    }
-    if (RT_SUCCESS(rc))
-        ++pState->cInits;
-    mutex_exit(&pState->InitMtx);
-    if (RT_FAILURE(rc))
-    {
-        LogRel(("open time initialisation failed. rc=%d\n", rc));
-        ASMAtomicWriteNullPtr(&pState->pWriteQueue);
-        return EINVAL;
-    }
-    return 0;
-}
-
-
-/**
- * Notification callback, called when the VBoxGuest mouse pointer is moved. 
- * We send a VUID event up to user space.  We may send a miscalculated event
- * if a resolution change is half-way through, but that is pretty much to be
- * expected, so we won't worry about it.
- */
-void vbmsSolNotify(void *pvState)
-{
-    PVBMSSTATE pState = (PVBMSSTATE)pvState;
-    int rc;
-
-    pState->pMouseStatusReq->mouseFeatures = 0;
-    pState->pMouseStatusReq->pointerXPos = 0;
-    pState->pMouseStatusReq->pointerYPos = 0;
-    rc = VbglGRPerform(&pState->pMouseStatusReq->header);
-    if (RT_SUCCESS(rc))
-    {
-        int cMaxScreenX  = pState->cMaxScreenX;
-        int cMaxScreenY  = pState->cMaxScreenY;
-        int x = pState->pMouseStatusReq->pointerXPos;
-        int y = pState->pMouseStatusReq->pointerYPos;
-
-        if (cMaxScreenX && cMaxScreenY)
-        {
-            vbmsSolVUIDPutAbsEvent(pState, LOC_X_ABSOLUTE,
-                                   x * cMaxScreenX / VMMDEV_MOUSE_RANGE_MAX);
-            vbmsSolVUIDPutAbsEvent(pState, LOC_Y_ABSOLUTE,
-                                   y * cMaxScreenY / VMMDEV_MOUSE_RANGE_MAX);
-        }
-    }
-}
-
-
-void vbmsSolVUIDPutAbsEvent(PVBMSSTATE pState, ushort_t cEvent,
-                            int cValue)
-{
-    queue_t *pReadQueue = RD(pState->pWriteQueue);
-    mblk_t *pMBlk = allocb(sizeof(Firm_event), BPRI_HI);
-    Firm_event *pEvent;
-    AssertReturnVoid(cEvent == LOC_X_ABSOLUTE || cEvent == LOC_Y_ABSOLUTE);
-    if (!pMBlk)
-        return;  /* If kernel memory is short a missed event is acceptable! */
-    pEvent = (Firm_event *)pMBlk->b_wptr;
-    pEvent->id        = cEvent;
-    pEvent->pair_type = FE_PAIR_DELTA;
-    pEvent->pair      = cEvent == LOC_X_ABSOLUTE ? LOC_X_DELTA : LOC_Y_DELTA;
-    pEvent->value     = cValue;
-    uniqtime32(&pEvent->time);
-    pMBlk->b_wptr += sizeof(Firm_event);
-    /* Put the message on the queue immediately if it is not blocked. */
-    if (canput(pReadQueue->q_next))
-        putnext(pReadQueue, pMBlk);
-    else
-        putq(pReadQueue, pMBlk);
-}
-
-
-/**
- * Close callback for the read queue, which we use as a generic device close
- * handler.
- */
-int vbmsSolClose(queue_t *pReadQueue, int fFlag, cred_t *pCred)
-{
-    PVBMSSTATE pState = (PVBMSSTATE)pReadQueue->q_ptr;
-
-    LogRelFlow((DEVICE_NAME "::Close, pWriteQueue=%p\n", WR(pReadQueue)));
-    NOREF(fFlag);
-    NOREF(pCred);
-
-    if (!pState)
-    {
-        Log((DEVICE_NAME "::Close: failed to get pState.\n"));
-        return EFAULT;
-    }
-
-    mutex_enter(&pState->InitMtx);
-    --pState->cInits;
-    if (!pState->cInits)
-    {
-        VbglSetMouseStatus(0);
-        /* Disable our IRQ handler. */
-        VbglSetMouseNotifyCallback(NULL, NULL);
-        qprocsoff(pReadQueue);
-
-        /*
-         * Close the session.
-         */
-        ASMAtomicWriteNullPtr(&pState->pWriteQueue);
-        pReadQueue->q_ptr = NULL;
-        VbglGRFree(&pState->pMouseStatusReq->header);
-        VbglTerminate();
-    }
-    mutex_exit(&pState->InitMtx);
-    return 0;
-}
-
-
-#ifdef TESTCASE
-/** Simple test of vbmsSolOpen and vbmsSolClose. */
-static void testOpenClose(RTTEST hTest)
-{
-    queue_t aQueues[2];
-    dev_t device = 0;
-    int rc;
-
-    RTTestSub(hTest, "Testing vbmsSolOpen and vbmsSolClose");
-    RT_ZERO(g_OpenNodeState);
-    RT_ZERO(aQueues);
-    doInitQueues(&aQueues[0]);
-    rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
-    RTTEST_CHECK(hTest, rc == 0);
-    RTTEST_CHECK(hTest, g_OpenNodeState.pWriteQueue == WR(&aQueues[0]));
-    vbmsSolClose(RD(&aQueues[0]), 0, NULL);
-}
-#endif
-
-
-/* Helper for vbmsSolWPut. */
-static int vbmsSolDispatchIOCtl(PVBMSSTATE pState, mblk_t *pMBlk);
-
-/**
- * Handler for messages sent from above (user-space and upper modules) which
- * land in our write queue.
- */
-int vbmsSolWPut(queue_t *pWriteQueue, mblk_t *pMBlk)
-{
-    PVBMSSTATE pState = (PVBMSSTATE)pWriteQueue->q_ptr;
-    LogRelFlowFunc((DEVICE_NAME "::"));
-    switch (pMBlk->b_datap->db_type)
-    {
-        case M_FLUSH:
-            LogRelFlow(("M_FLUSH, FLUSHW=%RTbool, FLUSHR=%RTbool\n",
-                        *pMBlk->b_rptr & FLUSHW, *pMBlk->b_rptr & FLUSHR));
-            /* Flush the write queue if so requested. */
-            if (*pMBlk->b_rptr & FLUSHW)
-                flushq(pWriteQueue, FLUSHDATA);
-
-            /* Flush the read queue if so requested. */
-            if (*pMBlk->b_rptr & FLUSHR)
-                flushq(RD(pWriteQueue), FLUSHDATA);
-
-            /* We have no one below us to pass the message on to. */
-            return 0;
-        /* M_IOCDATA is additional data attached to (at least) transparent
-         * IOCtls.  We handle the two together here and separate them further
-         * down. */
-        case M_IOCTL:
-        case M_IOCDATA:
-        {
-            int err;
-
-            LogRelFlow((  pMBlk->b_datap->db_type == M_IOCTL
-                        ? "M_IOCTL\n" : "M_IOCDATA\n"));
-            err = vbmsSolDispatchIOCtl(pState, pMBlk);
-            if (!err)
-                qreply(pWriteQueue, pMBlk);
-            else
-                miocnak(pWriteQueue, pMBlk, 0, err);
-            break;
-        }
-        default:
-            LogRelFlow(("Unknown command, not acknowledging.\n"));
-    }
-    return 0;
-}
-
-
-#ifdef TESTCASE
-/* Constants, definitions and test functions for testWPut. */
-static const int g_ccTestWPutFirmEvent = VUID_FIRM_EVENT;
-# define PVGFORMAT (&g_ccTestWPutFirmEvent)
-# define CBGFORMAT (sizeof(g_ccTestWPutFirmEvent))
-static const Ms_screen_resolution g_TestResolution = { 640, 480 };
-# define PMSIOSRES (&g_TestResolution)
-# define CBMSIOSRES (sizeof(g_TestResolution))
-
-static inline bool testSetResolution(RTTEST hTest, queue_t *pWriteQueue,
-                                     struct msgb *pMBlk)
-{
-    PVBMSSTATE pState = (PVBMSSTATE)pWriteQueue->q_ptr;
-    RTTEST_CHECK_MSG_RET(hTest,    pState->cMaxScreenX
-                               == g_TestResolution.width - 1,
-                         (hTest, "pState->cMaxScreenX=%d\n",
-                          pState->cMaxScreenX), false);
-    RTTEST_CHECK_MSG_RET(hTest,    pState->cMaxScreenY
-                               == g_TestResolution.height - 1,
-                         (hTest, "pState->cMaxScreenY=%d\n",
-                          pState->cMaxScreenY), false);
-    return true;
-}
-
-/** Data table for testWPut. */
-static struct
-{
-    int iIOCCmd;
-    size_t cbData;
-    const void *pvDataIn;
-    size_t cbDataIn;
-    const void *pvDataOut;
-    size_t cbDataOut;
-    int rcExp;
-    bool (*pfnExtra)(RTTEST hTest, queue_t *pWriteQueue, struct msgb *pMBlk);
-    bool fCanTransparent;
-} g_asTestWPut[] =
-{
-   /* iIOCCmd          cbData           pvDataIn   cbDataIn
-      pvDataOut   cbDataOut rcExp   pfnExtra           fCanTransparent */
-    { VUIDGFORMAT,     sizeof(int),     NULL,      0,
-      PVGFORMAT, CBGFORMAT, 0,      NULL,              true },
-    { VUIDGFORMAT,     sizeof(int) - 1, NULL,      0,
-      NULL,       0,        EINVAL, NULL,              false },
-    { VUIDGFORMAT,     sizeof(int) + 1, NULL,      0,
-      PVGFORMAT, CBGFORMAT, 0,      NULL,              true },
-    { VUIDSFORMAT,     sizeof(int),     PVGFORMAT, CBGFORMAT,
-      NULL,       0,        0,      NULL,              true },
-    { MSIOSRESOLUTION, CBMSIOSRES,      PMSIOSRES, CBMSIOSRES,
-      NULL,       0,        0,      testSetResolution, true },
-    { VUIDGWHEELINFO,  0,               NULL,      0,
-      NULL,       0,        EINVAL, NULL,              true }
-};
-
-# undef PVGFORMAT
-# undef CBGFORMAT
-# undef PMSIOSRES
-# undef CBMSIOSRES
-
-/* Helpers for testWPut. */
-static void testWPutStreams(RTTEST hTest, unsigned i);
-static void testWPutTransparent(RTTEST hTest, unsigned i);
-static void testWPutIOCDataIn(RTTEST hTest, unsigned i);
-static void testWPutIOCDataOut(RTTEST hTest, unsigned i);
-
-/** Test WPut's handling of different IOCtls, which is bulk of the logic in
- * this file. */
-static void testWPut(RTTEST hTest)
-{
-    unsigned i;
-
-    RTTestSub(hTest, "Testing vbmsWPut");
-    for (i = 0; i < RT_ELEMENTS(g_asTestWPut); ++i)
-    {
-        AssertReturnVoid(g_asTestWPut[i].cbDataIn <= g_asTestWPut[i].cbData);
-        AssertReturnVoid(g_asTestWPut[i].cbDataOut <= g_asTestWPut[i].cbData);
-        testWPutStreams(hTest, i);
-        if (g_asTestWPut[i].fCanTransparent)
-            testWPutTransparent(hTest, i);
-        if (g_asTestWPut[i].fCanTransparent && g_asTestWPut[i].cbDataIn)
-            testWPutIOCDataIn(hTest, i);
-        if (g_asTestWPut[i].fCanTransparent && g_asTestWPut[i].cbDataOut)
-            testWPutIOCDataOut(hTest, i);
-    }
-}
-
-
-#define MSG_DATA_SIZE 1024
-
-/** Simulate sending a streams IOCtl to WPut with the parameters from table
- * line @a i. */
-void testWPutStreams(RTTEST hTest, unsigned i)
-{
-    queue_t aQueues[2];
-    dev_t device = 0;
-    struct msgb *pMBlk = allocb(sizeof(struct iocblk), BPRI_MED);
-    struct msgb *pMBlkCont = allocb(MSG_DATA_SIZE, BPRI_MED);
-    struct iocblk *pIOCBlk = pMBlk ? (struct iocblk *)pMBlk->b_rptr : NULL;
-    int rc, cFormat = 0;
-
-    AssertReturnVoid(pMBlk);
-    AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
-    RT_ZERO(aQueues);
-    doInitQueues(&aQueues[0]);
-    rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
-    RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
-    RTTEST_CHECK_MSG(hTest,    g_OpenNodeState.pWriteQueue
-                            == WR(&aQueues[0]), (hTest, "i=%u\n", i));
-    pMBlk->b_datap->db_type = M_IOCTL;
-    pIOCBlk->ioc_cmd = g_asTestWPut[i].iIOCCmd;
-    pIOCBlk->ioc_count = g_asTestWPut[i].cbData;
-    AssertReturnVoid(g_asTestWPut[i].cbData <= MSG_DATA_SIZE);
-    memcpy(pMBlkCont->b_rptr, g_asTestWPut[i].pvDataIn,
-           g_asTestWPut[i].cbDataIn);
-    pMBlk->b_cont = pMBlkCont;
-    rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
-    RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_error == g_asTestWPut[i].rcExp,
-                     (hTest, "i=%u, IOCBlk.ioc_error=%d\n", i,
-                      pIOCBlk->ioc_error));
-    RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_count == g_asTestWPut[i].cbDataOut,
-                     (hTest, "i=%u, ioc_count=%u\n", i, pIOCBlk->ioc_count));
-    RTTEST_CHECK_MSG(hTest, !memcmp(pMBlkCont->b_rptr,
-                                    g_asTestWPut[i].pvDataOut,
-                                    g_asTestWPut[i].cbDataOut),
-                     (hTest, "i=%u\n", i));
-    /* Hack to ensure that miocpullup() gets called when needed. */
-    if (g_asTestWPut[i].cbData > 0)
-        RTTEST_CHECK_MSG(hTest, pMBlk->b_flag == 1, (hTest, "i=%u\n", i));
-    if (!g_asTestWPut[i].rcExp)
-        RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
-                         (hTest, "i=%u\n", i));
-    if (g_asTestWPut[i].pfnExtra)
-        if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
-            RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
-                         __PRETTY_FUNCTION__);
-    vbmsSolClose(RD(&aQueues[1]), 0, NULL);
-    freemsg(pMBlk);
-}
-
-
-#define USER_ADDRESS 0xfeedbacc
-
-/** Simulate sending a transparent IOCtl to WPut with the parameters from table
- * line @a i. */
-void testWPutTransparent(RTTEST hTest, unsigned i)
-{
-    queue_t aQueues[2];
-    dev_t device = 0;
-    struct msgb *pMBlk = allocb(sizeof(struct iocblk), BPRI_MED);
-    struct msgb *pMBlkCont = allocb(sizeof(void *), BPRI_MED);
-    struct iocblk *pIOCBlk = pMBlk ? (struct iocblk *)pMBlk->b_rptr : NULL;
-    struct copyreq *pCopyReq;
-    int rc, cFormat = 0;
-
-    /* if (g_asTestWPut[i].cbDataIn == 0 && g_asTestWPut[i].cbDataOut != 0)
-        return; */  /* This case will be handled once the current ones work. */
-    AssertReturnVoid(pMBlk);
-    AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
-    RT_ZERO(aQueues);
-    doInitQueues(&aQueues[0]);
-    rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
-    RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
-    RTTEST_CHECK_MSG(hTest,    g_OpenNodeState.pWriteQueue
-                            == WR(&aQueues[0]), (hTest, "i=%u\n", i));
-    pMBlk->b_datap->db_type = M_IOCTL;
-    pIOCBlk->ioc_cmd = g_asTestWPut[i].iIOCCmd;
-    pIOCBlk->ioc_count = TRANSPARENT;
-    *(void **)pMBlkCont->b_rptr = (void *)USER_ADDRESS;
-    pMBlk->b_cont = pMBlkCont;
-    rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
-    pCopyReq = (struct copyreq *)pMBlk->b_rptr;
-    RTTEST_CHECK_MSG(hTest, (   (   g_asTestWPut[i].cbDataIn
-                                 && (pMBlk->b_datap->db_type == M_COPYIN))
-                             || (   g_asTestWPut[i].cbDataOut
-                                 && (pMBlk->b_datap->db_type == M_COPYOUT))
-                             || (   (g_asTestWPut[i].rcExp == 0)
-                                 && pMBlk->b_datap->db_type == M_IOCACK)
-                             || (pMBlk->b_datap->db_type == M_IOCNAK)),
-                     (hTest, "i=%u, db_type=%u\n", i,
-                      (unsigned) pMBlk->b_datap->db_type));
-    /* Our TRANSPARENT IOCtls can only return non-zero if they have no payload.
-     * Others should either return zero or be non-TRANSPARENT only. */
-    if (pMBlk->b_datap->db_type == M_IOCNAK)
-        RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_error == g_asTestWPut[i].rcExp,
-                         (hTest, "i=%u, IOCBlk.ioc_error=%d\n", i,
-                          pIOCBlk->ioc_error));
-    if (g_asTestWPut[i].cbData)
-    {
-        RTTEST_CHECK_MSG(hTest, pCopyReq->cq_addr == (char *)USER_ADDRESS,
-                         (hTest, "i=%u, cq_addr=%p\n", i, pCopyReq->cq_addr));
-        RTTEST_CHECK_MSG(   hTest, pCopyReq->cq_size
-                         ==   g_asTestWPut[i].cbDataIn
-                            ? g_asTestWPut[i].cbDataIn
-                            : g_asTestWPut[i].cbDataOut,
-                         (hTest, "i=%u, cq_size=%llu\n", i,
-                          (unsigned long long)pCopyReq->cq_size));
-    }
-    /* Implementation detail - check that the private pointer is correctly
-     * set to the user address *for two direction IOCtls* or NULL otherwise. */
-    if (g_asTestWPut[i].cbDataIn && g_asTestWPut[i].cbDataOut)
-        RTTEST_CHECK_MSG(hTest, pCopyReq->cq_private == (mblk_t *)USER_ADDRESS,
-                         (hTest, "i=%u, cq_private=%p\n", i,
-                          pCopyReq->cq_private));
-    else if (   (pMBlk->b_datap->db_type == M_COPYIN)
-             || (pMBlk->b_datap->db_type == M_COPYOUT))
-        RTTEST_CHECK_MSG(hTest, !pCopyReq->cq_private,
-                         (hTest, "i=%u, cq_private=%p\n", i,
-                          pCopyReq->cq_private));
-    if (!g_asTestWPut[i].rcExp)
-        RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
-                         (hTest, "i=%u\n", i));
-    if (g_asTestWPut[i].pfnExtra && !g_asTestWPut[i].cbData)
-        if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
-            RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
-                         __PRETTY_FUNCTION__);
-    vbmsSolClose(RD(&aQueues[1]), 0, NULL);
-    freemsg(pMBlk);
-}
-
-
-/** Simulate sending follow-on IOCData messages to a transparent IOCtl to WPut
- * with the parameters from table line @a i. */
-void testWPutIOCDataIn(RTTEST hTest, unsigned i)
-{
-    queue_t aQueues[2];
-    dev_t device = 0;
-    struct msgb *pMBlk = allocb(sizeof(struct copyresp), BPRI_MED);
-    struct msgb *pMBlkCont = allocb(MSG_DATA_SIZE, BPRI_MED);
-    struct copyresp *pCopyResp = pMBlk ? (struct copyresp *)pMBlk->b_rptr
-                                       : NULL;
-    void *pvData = pMBlkCont ? pMBlkCont->b_rptr : NULL;
-    struct copyreq *pCopyReq;
-    int rc, cFormat = 0;
-
-    AssertReturnVoid(pMBlk);
-    AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
-    RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%s: i=%u\n", __PRETTY_FUNCTION__,
-                 i);
-    AssertReturnVoidStmt(g_asTestWPut[i].cbDataIn, freemsg(pMBlk));
-    RT_ZERO(aQueues);
-    doInitQueues(&aQueues[0]);
-    rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
-    RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
-    RTTEST_CHECK_MSG(hTest,    g_OpenNodeState.pWriteQueue
-                            == WR(&aQueues[0]), (hTest, "i=%u\n", i));
-    pMBlk->b_datap->db_type = M_IOCDATA;
-    pCopyResp->cp_cmd = g_asTestWPut[i].iIOCCmd;
-    if (g_asTestWPut[i].cbDataOut)
-        pCopyResp->cp_private = USER_ADDRESS;
-    AssertReturnVoid(g_asTestWPut[i].cbData <= MSG_DATA_SIZE);
-    memcpy(pMBlkCont->b_rptr, g_asTestWPut[i].pvDataIn, g_asTestWPut[i].cbDataIn);
-    pMBlk->b_cont = pMBlkCont;
-    rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
-    pCopyReq = (struct copyreq *)pMBlk->b_rptr;
-    RTTEST_CHECK_MSG(hTest, (   (   g_asTestWPut[i].cbDataOut
-                                 && (pMBlk->b_datap->db_type == M_COPYOUT))
-                             || (   (g_asTestWPut[i].rcExp == 0)
-                                 && pMBlk->b_datap->db_type == M_IOCACK)
-                             || (pMBlk->b_datap->db_type == M_IOCNAK)),
-                     (hTest, "i=%u, db_type=%u\n", i,
-                      (unsigned) pMBlk->b_datap->db_type));
-    if (g_asTestWPut[i].cbDataOut)
-    {
-        RTTEST_CHECK_MSG(hTest, pCopyReq->cq_addr == (char *)pvData,
-                         (hTest, "i=%u, cq_addr=%p\n", i, pCopyReq->cq_addr));
-        RTTEST_CHECK_MSG(hTest, pCopyReq->cq_size == g_asTestWPut[i].cbData,
-                         (hTest, "i=%u, cq_size=%llu\n", i,
-                          (unsigned long long)pCopyReq->cq_size));
-        RTTEST_CHECK_MSG(hTest, !memcmp(pvData, g_asTestWPut[i].pvDataOut,
-                                        g_asTestWPut[i].cbDataOut),
-                         (hTest, "i=%u\n", i));
-    }
-    RTTEST_CHECK_MSG(hTest, !pCopyReq->cq_private,
-                     (hTest, "i=%u, cq_private=%p\n", i,
-                      pCopyReq->cq_private));
-    if (!g_asTestWPut[i].rcExp)
-        RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
-                         (hTest, "i=%u\n", i));
-    if (g_asTestWPut[i].pfnExtra && !g_asTestWPut[i].cbDataOut)
-        if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
-            RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
-                         __PRETTY_FUNCTION__);
-    vbmsSolClose(RD(&aQueues[1]), 0, NULL);
-    freemsg(pMBlk);
-}
-
-
-/** Simulate sending follow-on IOCData messages to a transparent IOCtl to WPut
- * with the parameters from table line @a i. */
-void testWPutIOCDataOut(RTTEST hTest, unsigned i)
-{
-    queue_t aQueues[2];
-    dev_t device = 0;
-    struct msgb *pMBlk = allocb(sizeof(struct copyresp), BPRI_MED);
-    struct copyresp *pCopyResp = pMBlk ? (struct copyresp *)pMBlk->b_rptr
-                                       : NULL;
-    int rc, cFormat = 0;
-
-    AssertReturnVoid(pMBlk);
-    AssertReturnVoidStmt(g_asTestWPut[i].cbDataOut, freemsg(pMBlk));
-    RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%s: i=%u\n", __PRETTY_FUNCTION__,
-                 i);
-    RT_ZERO(aQueues);
-    doInitQueues(&aQueues[0]);
-    rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
-    RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
-    RTTEST_CHECK_MSG(hTest,    g_OpenNodeState.pWriteQueue
-                            == WR(&aQueues[0]), (hTest, "i=%u\n", i));
-    pMBlk->b_datap->db_type = M_IOCDATA;
-    pCopyResp->cp_cmd = g_asTestWPut[i].iIOCCmd;
-    rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
-    RTTEST_CHECK_MSG(hTest, pMBlk->b_datap->db_type == M_IOCACK,
-                     (hTest, "i=%u, db_type=%u\n", i,
-                      (unsigned) pMBlk->b_datap->db_type));
-    if (!g_asTestWPut[i].rcExp)
-        RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
-                         (hTest, "i=%u\n", i));
-    vbmsSolClose(RD(&aQueues[1]), 0, NULL);
-    freemsg(pMBlk);
-}
-#endif
-
-
-/** Data transfer direction of an IOCtl.  This is used for describing
- * transparent IOCtls, and @a UNSPECIFIED is not a valid value for them. */
-enum IOCTLDIRECTION
-{
-    /** This IOCtl transfers no data. */
-    NONE,
-    /** This IOCtl only transfers data from user to kernel. */
-    IN,
-    /** This IOCtl only transfers data from kernel to user. */
-    OUT,
-    /** This IOCtl transfers data from user to kernel and back. */
-    BOTH,
-    /** We aren't saying anything about how the IOCtl transfers data. */
-    UNSPECIFIED
-};
-
-/**
- * IOCtl handler function.
- * @returns 0 on success, error code on failure.
- * @param iCmd      The IOCtl command number.
- * @param pvData    Buffer for the user data.
- * @param cbBuffer  Size of the buffer in @a pvData or zero.
- * @param pcbData   Where to set the size of the data returned.  Required for
- *                  handlers which return data.
- * @param prc       Where to store the return code.  Default is zero.  Only
- *                  used for IOCtls without data for convenience of
- *                  implemention.
- */
-typedef int FNVBMSSOLIOCTL(PVBMSSTATE pState, int iCmd, void *pvData,
-                            size_t cbBuffer, size_t *pcbData, int *prc);
-typedef FNVBMSSOLIOCTL *PFNVBMSSOLIOCTL;
-
-/* Helpers for vbmsSolDispatchIOCtl. */
-static int vbmsSolHandleIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
-                               PFNVBMSSOLIOCTL pfnHandler,
-                               int iCmd, size_t cbCmd,
-                               enum IOCTLDIRECTION enmDirection);
-static int vbmsSolVUIDIOCtl(PVBMSSTATE pState, int iCmd, void *pvData,
-                             size_t cbBuffer, size_t *pcbData, int *prc);
-
-/** Table of supported VUID IOCtls. */
-struct
-{
-    /** The IOCtl number. */
-    int iCmd;
-    /** The size of the buffer which needs to be copied between user and kernel
-     * space, or zero if unknown (must be known for tranparent IOCtls). */
-    size_t cbBuffer;
-    /** The direction the buffer data needs to be copied.  This must be
-     * specified for transparent IOCtls. */
-    enum IOCTLDIRECTION enmDirection;
-} g_aVUIDIOCtlDescriptions[] =
-{
-   { VUIDGFORMAT,     sizeof(int),                  OUT         },
-   { VUIDSFORMAT,     sizeof(int),                  IN          },
-   { VUIDGADDR,       0,                            UNSPECIFIED },
-   { VUIDGADDR,       0,                            UNSPECIFIED },
-   { MSIOGETPARMS,    sizeof(Ms_parms),             OUT         },
-   { MSIOSETPARMS,    sizeof(Ms_parms),             IN          },
-   { MSIOSRESOLUTION, sizeof(Ms_screen_resolution), IN          },
-   { MSIOBUTTONS,     sizeof(int),                  OUT         },
-   { VUIDGWHEELCOUNT, sizeof(int),                  OUT         },
-   { VUIDGWHEELINFO,  0,                            UNSPECIFIED },
-   { VUIDGWHEELSTATE, 0,                            UNSPECIFIED },
-   { VUIDSWHEELSTATE, 0,                            UNSPECIFIED }
-};
-
-/**
- * Handle a STREAMS IOCtl message for our driver on the write stream.  This
- * function takes care of the IOCtl logic only and does not call qreply() or
- * miocnak() at all - the caller must call these on success or failure
- * respectively.
- * @returns  0 on success or the IOCtl error code on failure.
- * @param  pState       pointer to the state structure.
- * @param  pMBlk        pointer to the STREAMS message block structure.
- */
-static int vbmsSolDispatchIOCtl(PVBMSSTATE pState, mblk_t *pMBlk)
-{
-    struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
-    int iCmd = pIOCBlk->ioc_cmd, iCmdType = iCmd & (0xff << 8);
-    size_t cbBuffer;
-    enum IOCTLDIRECTION enmDirection;
-
-    LogRelFlowFunc((DEVICE_NAME "::pIOCBlk=%p, iCmdType=%c, iCmd=0x%x\n",
-                    pIOCBlk, (char) (iCmdType >> 8), (unsigned)iCmd));
-    switch (iCmdType)
-    {
-        case MSIOC:
-        case VUIOC:
-        {
-            unsigned i;
-            
-            for (i = 0; i < RT_ELEMENTS(g_aVUIDIOCtlDescriptions); ++i)
-                if (g_aVUIDIOCtlDescriptions[i].iCmd == iCmd)
-                {
-                    cbBuffer     = g_aVUIDIOCtlDescriptions[i].cbBuffer;
-                    enmDirection = g_aVUIDIOCtlDescriptions[i].enmDirection;
-                    return vbmsSolHandleIOCtl(pState, pMBlk,
-                                              vbmsSolVUIDIOCtl, iCmd,
-                                              cbBuffer, enmDirection);
-                }
-            return EINVAL;
-        }
-        default:
-            return ENOTTY;
-    }
-}
-
-
-/* Helpers for vbmsSolHandleIOCtl. */
-static int vbmsSolHandleIOCtlData(PVBMSSTATE pState, mblk_t *pMBlk,
-                                  PFNVBMSSOLIOCTL pfnHandler, int iCmd,
-                                  size_t cbCmd,
-                                  enum IOCTLDIRECTION enmDirection);
-
-static int vbmsSolHandleTransparentIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
-                                         PFNVBMSSOLIOCTL pfnHandler,
-                                         int iCmd, size_t cbCmd,
-                                         enum IOCTLDIRECTION enmDirection);
-
-static int vbmsSolHandleIStrIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
-                                   PFNVBMSSOLIOCTL pfnHandler, int iCmd);
-
-static void vbmsSolAcknowledgeIOCtl(mblk_t *pMBlk, int cbData, int rc)
-{
-    struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
-
-    pMBlk->b_datap->db_type = M_IOCACK;
-    pIOCBlk->ioc_count = cbData;
-    pIOCBlk->ioc_rval = rc;
-    pIOCBlk->ioc_error = 0;
-}
-
-/**
- * Generic code for handling STREAMS-specific IOCtl logic and boilerplate.  It
- * calls the IOCtl handler passed to it without the handler having to be aware
- * of STREAMS structures, or whether this is a transparent (traditional) or an
- * I_STR (using a STREAMS structure to describe the data) IOCtl.  With the
- * caveat that we only support transparent IOCtls which pass all data in a
- * single buffer of a fixed size (I_STR IOCtls are restricted to a single
- * buffer anyway, but the caller can choose the buffer size).
- * @returns  0 on success or the IOCtl error code on failure.
- * @param  pState         pointer to the state structure.
- * @param  pMBlk          pointer to the STREAMS message block structure.
- * @param  pfnHandler     pointer to the right IOCtl handler function for this
- *                        IOCtl number.
- * @param  iCmd           IOCtl command number.
- * @param  cbCmd          size of the user space buffer for this IOCtl number,
- *                        used for processing transparent IOCtls.  Pass zero
- *                        for IOCtls with no maximum buffer size (which will
- *                        not be able to be handled as transparent) or with
- *                        no argument.
- * @param  enmDirection   data transfer direction of the IOCtl.
- */
-static int vbmsSolHandleIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
-                              PFNVBMSSOLIOCTL pfnHandler, int iCmd,
-                              size_t cbCmd, enum IOCTLDIRECTION enmDirection)
-{
-    struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
-
-    LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d\n",
-                 (unsigned)iCmd, (int)cbCmd, (int)enmDirection));
-    if (pMBlk->b_datap->db_type == M_IOCDATA)
-        return vbmsSolHandleIOCtlData(pState, pMBlk, pfnHandler, iCmd,
-                                      cbCmd, enmDirection);
-    else if (   pMBlk->b_datap->db_type == M_IOCTL
-             && pIOCBlk->ioc_count == TRANSPARENT)
-        return vbmsSolHandleTransparentIOCtl(pState, pMBlk, pfnHandler,
-                                             iCmd, cbCmd, enmDirection);
-    else if (pMBlk->b_datap->db_type == M_IOCTL)
-        return vbmsSolHandleIStrIOCtl(pState, pMBlk, pfnHandler, iCmd);
-    return EINVAL;
-}
-
-
-/**
- * Helper for vbmsSolHandleIOCtl.  This rather complicated-looking
- * code is basically the standard boilerplate for handling any streams IOCtl
- * additional data, which we currently only use for transparent IOCtls.
- * @copydoc vbmsSolHandleIOCtl
- */
-static int vbmsSolHandleIOCtlData(PVBMSSTATE pState, mblk_t *pMBlk,
-                                  PFNVBMSSOLIOCTL pfnHandler, int iCmd,
-                                  size_t cbCmd,
-                                  enum IOCTLDIRECTION enmDirection)
-{
-    struct copyresp *pCopyResp = (struct copyresp *)pMBlk->b_rptr;
-
-    LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d, cp_rval=%d, cp_private=%p\n",
-                 (unsigned)iCmd, (int)cbCmd, (int)enmDirection,
-                 (int)(uintptr_t)pCopyResp->cp_rval,
-                 (void *)pCopyResp->cp_private));
-    if (pCopyResp->cp_rval)  /* cp_rval is a pointer used as a boolean. */
-    {
-        freemsg(pMBlk);
-        return EAGAIN;
-    }
-    if ((pCopyResp->cp_private && enmDirection == BOTH) || enmDirection == IN)
-    {
-        size_t cbData = 0;
-        void *pvData = NULL;
-        int err;
-
-        if (!pMBlk->b_cont)
-            return EINVAL;
-        if (enmDirection == BOTH && !pCopyResp->cp_private)
-            return EINVAL;
-        pvData = pMBlk->b_cont->b_rptr;
-        err = pfnHandler(pState, iCmd, pvData, cbCmd, &cbData, NULL);
-        if (!err && enmDirection == BOTH)
-            mcopyout(pMBlk, NULL, cbData, pCopyResp->cp_private, NULL);
-        else if (!err && enmDirection == IN)
-            vbmsSolAcknowledgeIOCtl(pMBlk, 0, 0);
-        return err;
-    }
-    else
-    {
-        AssertReturn(enmDirection == OUT || enmDirection == BOTH, EINVAL);
-        vbmsSolAcknowledgeIOCtl(pMBlk, 0, 0);
-        return 0;
-    }
-}
-
-/**
- * Helper for vbmsSolHandleIOCtl.  This rather complicated-looking
- * code is basically the standard boilerplate for handling transparent IOCtls,
- * that is, IOCtls which are not re-packed inside STREAMS IOCtls.
- * @copydoc vbmsSolHandleIOCtl
- */
-int vbmsSolHandleTransparentIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
-                                  PFNVBMSSOLIOCTL pfnHandler, int iCmd,
-                                  size_t cbCmd,
-                                  enum IOCTLDIRECTION enmDirection)
-{
-    int err = 0, rc = 0;
-    size_t cbData = 0;
-
-    LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d\n",
-                 (unsigned)iCmd, (int)cbCmd, (int)enmDirection));
-    if (   (enmDirection != NONE && !pMBlk->b_cont)
-        || enmDirection == UNSPECIFIED)
-        return EINVAL;
-    if (enmDirection == IN || enmDirection == BOTH)
-    {
-        void *pUserAddr = NULL;
-        /* We only need state data if there is something to copy back. */
-        if (enmDirection == BOTH)
-            pUserAddr = *(void **)pMBlk->b_cont->b_rptr;
-	    mcopyin(pMBlk, pUserAddr /* state data */, cbCmd, NULL);
-	}
-	else if (enmDirection == OUT)
-    {
-        mblk_t *pMBlkOut = allocb(cbCmd, BPRI_MED);
-        void *pvData;
-
-        if (!pMBlkOut)
-            return EAGAIN;
-        pvData = pMBlkOut->b_rptr;
-        err = pfnHandler(pState, iCmd, pvData, cbCmd, &cbData, NULL);
-        if (!err)
-            mcopyout(pMBlk, NULL, cbData, NULL, pMBlkOut);
-        else
-            freemsg(pMBlkOut);
-    }
-    else
-    {
-        AssertReturn(enmDirection == NONE, EINVAL);
-        err = pfnHandler(pState, iCmd, NULL, 0, NULL, &rc);
-        if (!err)
-            vbmsSolAcknowledgeIOCtl(pMBlk, 0, rc);
-    }
-    return err;
-}
-                 
-/**
- * Helper for vbmsSolHandleIOCtl.  This rather complicated-looking
- * code is basically the standard boilerplate for handling any streams IOCtl.
- * @copydoc vbmsSolHandleIOCtl
- */
-static int vbmsSolHandleIStrIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
-                                  PFNVBMSSOLIOCTL pfnHandler, int iCmd)
-{
-    struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
-    uint_t cbBuffer = pIOCBlk->ioc_count;
-    void *pvData = NULL;
-    int err, rc = 0;
-    size_t cbData = 0;
-    
-    LogFlowFunc(("iCmd=0x%x, cbBuffer=%u, b_cont=%p\n",
-                 (unsigned)iCmd, cbBuffer, (void *)pMBlk->b_cont));
-    if (cbBuffer && !pMBlk->b_cont)
-        return EINVAL;
-    /* Repack the whole buffer into a single message block if needed. */
-    if (cbBuffer)
-    {
-        err = miocpullup(pMBlk, cbBuffer);
-        if (err)
-            return err;
-        pvData = pMBlk->b_cont->b_rptr;
-    }
-    else if (pMBlk->b_cont)  /* consms forgets to set ioc_count. */
-    {
-        pvData = pMBlk->b_cont->b_rptr;
-        cbBuffer =   pMBlk->b_cont->b_datap->db_lim
-                   - pMBlk->b_cont->b_datap->db_base;
-    }
-    err = pfnHandler(pState, iCmd, pvData, cbBuffer, &cbData, &rc);
-    if (!err)
-    {
-        LogRelFlowFunc(("pMBlk=%p, pMBlk->b_datap=%p, pMBlk->b_rptr=%p\n",
-                        pMBlk, pMBlk->b_datap, pMBlk->b_rptr));
-        vbmsSolAcknowledgeIOCtl(pMBlk, cbData, rc);
-    }
-    return err;
-}
-
-
-/**
- * Handle a VUID input device IOCtl.
- * @copydoc FNVBMSSOLIOCTL
- */
-static int vbmsSolVUIDIOCtl(PVBMSSTATE pState, int iCmd, void *pvData,
-                             size_t cbBuffer, size_t *pcbData, int *prc)
-{
-    LogRelFlowFunc((DEVICE_NAME "::pvData=%p " /* no '\n' */, pvData));
-    switch (iCmd)
-    {
-        case VUIDGFORMAT:
-        {
-            LogRelFlowFunc(("VUIDGFORMAT\n"));
-            if (cbBuffer < sizeof(int))
-                return EINVAL;
-            *(int *)pvData = VUID_FIRM_EVENT;
-            *pcbData = sizeof(int);
-            return 0;
-        }
-        case VUIDSFORMAT:
-            LogRelFlowFunc(("VUIDSFORMAT\n"));
-            /* We define our native format to be VUID_FIRM_EVENT, so there
-             * is nothing more to do and we exit here on success or on
-             * failure. */
-            return 0;
-        case VUIDGADDR:
-        case VUIDSADDR:
-            LogRelFlowFunc(("VUIDGADDR/VUIDSADDR\n"));
-            return ENOTTY;
-        case MSIOGETPARMS:
-        {
-            Ms_parms parms = { 0 };
-
-            LogRelFlowFunc(("MSIOGETPARMS\n"));
-            if (cbBuffer < sizeof(Ms_parms))
-                return EINVAL;
-            *(Ms_parms *)pvData = parms;
-            *pcbData = sizeof(Ms_parms);
-            return 0;
-        }
-        case MSIOSETPARMS:
-            LogRelFlowFunc(("MSIOSETPARMS\n"));
-            return 0;
-        case MSIOSRESOLUTION:
-        {
-            Ms_screen_resolution *pResolution = (Ms_screen_resolution *)pvData;
-            int rc;
-
-            LogRelFlowFunc(("MSIOSRESOLUTION, cbBuffer=%d, sizeof(Ms_screen_resolution)=%d\n",
-                            (int) cbBuffer,
-                            (int) sizeof(Ms_screen_resolution)));
-            if (cbBuffer < sizeof(Ms_screen_resolution))
-                return EINVAL;
-            LogRelFlowFunc(("%dx%d\n", pResolution->width,
-                            pResolution->height));
-            pState->cMaxScreenX = pResolution->width  - 1;
-            pState->cMaxScreenY = pResolution->height - 1;
-            /* Note: we don't disable this again until session close. */
-            rc = VbglSetMouseStatus(  VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
-                                    | VMMDEV_MOUSE_NEW_PROTOCOL);
-            if (RT_SUCCESS(rc))
-                return 0;
-            pState->cMaxScreenX = 0;
-            pState->cMaxScreenY = 0;
-            return ENODEV;
-        }
-        case MSIOBUTTONS:
-        {
-            LogRelFlowFunc(("MSIOBUTTONS\n"));
-            if (cbBuffer < sizeof(int))
-                return EINVAL;
-            *(int *)pvData = 0;
-            *pcbData = sizeof(int);
-            return 0;
-        }
-        case VUIDGWHEELCOUNT:
-        {
-            LogRelFlowFunc(("VUIDGWHEELCOUNT\n"));
-            if (cbBuffer < sizeof(int))
-                return EINVAL;
-            *(int *)pvData = 0;
-            *pcbData = sizeof(int);
-            return 0;
-        }
-        case VUIDGWHEELINFO:
-        case VUIDGWHEELSTATE:
-        case VUIDSWHEELSTATE:
-            LogRelFlowFunc(("VUIDGWHEELINFO/VUIDGWHEELSTATE/VUIDSWHEELSTATE\n"));
-            return EINVAL;
-        default:
-            LogRelFlowFunc(("Invalid IOCtl command %x\n", iCmd));
-            return EINVAL;
-    }
-}
-
-
-#ifdef TESTCASE
-int main(void)
-{
-    RTTEST hTest;
-    int rc = RTTestInitAndCreate("tstVBoxGuest-solaris", &hTest);
-    if (rc)
-        return rc;
-    RTTestBanner(hTest);
-    test_init(hTest);
-    testOpenClose(hTest);
-    testWPut(hTest);
-
-    /*
-     * Summary.
-     */
-    return RTTestSummaryAndDestroy(hTest);
-}
-#endif
Index: trunk/src/VBox/Additions/solaris/Mouse/vboxms.c
===================================================================
--- trunk/src/VBox/Additions/solaris/Mouse/vboxms.c	(revision 42368)
+++ trunk/src/VBox/Additions/solaris/Mouse/vboxms.c	(revision 42368)
@@ -0,0 +1,1433 @@
+/* $Id$ */
+/** @file
+ * VirtualBox Guest Additions Driver for Solaris.
+ */
+
+/*
+ * 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 contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+#define LOG_GROUP LOG_GROUP_DRV_MOUSE
+
+/******************************************************************************
+*   Header Files                                                              *
+******************************************************************************/
+
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/log.h>
+#include <VBox/version.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+
+#ifndef TESTCASE
+# include <sys/modctl.h>
+# include <sys/msio.h>
+# include <sys/stat.h>
+# include <sys/ddi.h>
+# include <sys/strsun.h>
+# include <sys/stropts.h>
+# include <sys/sunddi.h>
+# include <sys/vuid_event.h>
+# include <sys/vuid_wheel.h>
+#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
+#else  /* TESTCASE */
+# undef IN_RING3
+# define IN_RING0
+#endif  /* TESTCASE */
+
+#ifdef TESTCASE  /* Include this last as we . */
+# include "testcase/solaris.h"
+# include <iprt/test.h>
+#endif  /* TESTCASE */
+
+
+/******************************************************************************
+*   Defined Constants And Macros                                              *
+******************************************************************************/
+
+/** The module name. */
+#define DEVICE_NAME              "vboxmouse"
+/** The module description as seen in 'modinfo'. */
+#define DEVICE_DESC              "VBoxMouseIntegr"
+
+
+/******************************************************************************
+*   Internal functions used in global structures                              *
+******************************************************************************/
+
+static int vbmsSolAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
+static int vbmsSolDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+static int vbmsSolGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg,
+                          void **ppvResult);
+static int vbmsSolOpen(queue_t *pReadQueue, dev_t *pDev, int fFlag,
+                                int fMode, cred_t *pCred);
+static int vbmsSolClose(queue_t *pReadQueue, int fFlag, cred_t *pCred);
+static int vbmsSolWPut(queue_t *pWriteQueue, mblk_t *pMBlk);
+
+
+/******************************************************************************
+*   Driver global structures                                                  *
+******************************************************************************/
+
+#ifndef TESTCASE  /* I see no value in including these in the test. */
+
+/*
+ * mod_info: STREAMS module information.
+ */
+static struct module_info g_vbmsSolModInfo =
+{
+    0,                      /* module id number */
+    "vboxms",
+    0,                      /* minimum packet size */
+    INFPSZ,                 /* maximum packet size accepted */
+    512,                    /* high water mark for data flow control */
+    128                     /* low water mark */
+};
+
+/*
+ * rinit: read queue structure for handling messages coming from below.  In
+ * our case this means the host and the virtual hardware, so we do not need
+ * the put and service procedures.
+ */
+static struct qinit g_vbmsSolRInit =
+{
+    NULL,                /* put */
+    NULL,                /* service thread procedure */
+    vbmsSolOpen,
+    vbmsSolClose,
+    NULL,                /* reserved */
+    &g_vbmsSolModInfo,
+    NULL                 /* module statistics structure */
+};
+
+/* 
+ * winit: write queue structure for handling messages coming from above.  Above
+ * means user space applications: either Guest Additions user space tools or
+ * applications reading pointer input.  Messages from the last most likely pass
+ * through at least the "consms" console mouse streams module which multiplexes
+ * hardware pointer drivers to a single virtual pointer.
+ */
+static struct qinit g_vbmsSolWInit =
+{
+    vbmsSolWPut,
+    NULL,                   /* service thread procedure */
+    NULL,                   /* open */
+    NULL,                   /* close */
+    NULL,                   /* reserved */
+    &g_vbmsSolModInfo,
+    NULL                    /* module statistics structure */
+};
+
+/**
+ * streamtab: for drivers that support char/block entry points.
+ */
+static struct streamtab g_vbmsSolStreamTab =
+{
+    &g_vbmsSolRInit,
+    &g_vbmsSolWInit,
+    NULL,                   /* MUX rinit */
+    NULL                    /* MUX winit */
+};
+
+/**
+ * cb_ops: for drivers that support char/block entry points
+ */
+static struct cb_ops g_vbmsSolCbOps =
+{
+    nodev,                  /* open */
+    nodev,                  /* close */
+    nodev,                  /* b strategy */
+    nodev,                  /* b dump */
+    nodev,                  /* b print */
+    nodev,                  /* c read */
+    nodev,                  /* c write */
+    nodev,                  /* c ioctl */
+    nodev,                  /* c devmap */
+    nodev,                  /* c mmap */
+    nodev,                  /* c segmap */
+    nochpoll,               /* c poll */
+    ddi_prop_op,            /* property ops */
+    &g_vbmsSolStreamTab,
+    D_MP,
+    CB_REV                  /* revision */
+};
+
+/**
+ * dev_ops: for driver device operations
+ */
+static struct dev_ops g_vbmsSolDevOps =
+{
+    DEVO_REV,               /* driver build revision */
+    0,                      /* ref count */
+    vbmsSolGetInfo,
+    nulldev,                /* identify */
+    nulldev,                /* probe */
+    vbmsSolAttach,
+    vbmsSolDetach,
+    nodev,                  /* reset */
+    &g_vbmsSolCbOps,
+    NULL,                   /* bus operations */
+    nodev                   /* power */
+};
+
+/**
+ * modldrv: export driver specifics to the kernel
+ */
+static struct modldrv g_vbmsSolModule =
+{
+    &mod_driverops,         /* extern from kernel */
+    DEVICE_DESC " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+    &g_vbmsSolDevOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel.
+ */
+static struct modlinkage g_vbmsSolModLinkage =
+{
+    MODREV_1,               /* loadable module system revision */
+    &g_vbmsSolModule,
+    NULL                    /* terminate array of linkage structures */
+};
+
+#else  /* TESTCASE */
+static void *g_vbmsSolModLinkage;
+#endif  /* TESTCASE */
+
+/**
+ * State info for each open file handle.
+ */
+typedef struct
+{
+    /** Device handle. */
+    dev_info_t           *pDip;
+    /** Mutex protecting the guest library against multiple initialistation or
+     * uninitialisation. */
+    kmutex_t              InitMtx;
+    /** Initialisation counter for the guest library. */
+    size_t                cInits;
+    /** The STREAMS write queue which we need for sending messages up to
+     * user-space. */
+    queue_t              *pWriteQueue;
+    /** Pre-allocated mouse status VMMDev request for use in the IRQ
+     * handler. */
+    VMMDevReqMouseStatus *pMouseStatusReq;
+    /* The current greatest horizontal pixel offset on the screen, used for
+     * absolute mouse position reporting.
+     */
+    int                   cMaxScreenX;
+    /* The current greatest vertical pixel offset on the screen, used for
+     * absolute mouse position reporting.
+     */
+    int                   cMaxScreenY;
+} VBMSSTATE, *PVBMSSTATE;
+
+
+/******************************************************************************
+*   Global Variables                                                          *
+******************************************************************************/
+
+/** Global driver state.  Actually this could be allocated dynamically. */
+static VBMSSTATE            g_OpenNodeState /* = { 0 } */;
+
+
+/******************************************************************************
+*   Kernel entry points                                                       *
+******************************************************************************/
+
+/** Driver initialisation. */
+int _init(void)
+{
+    int rc;
+    LogRelFlow((DEVICE_NAME ": built on " __DATE__ " at " __TIME__ "\n"));
+    mutex_init(&g_OpenNodeState.InitMtx, NULL, MUTEX_DRIVER, NULL);
+    /*
+     * Prevent module autounloading.
+     */
+    modctl_t *pModCtl = mod_getctl(&g_vbmsSolModLinkage);
+    if (pModCtl)
+        pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
+    else
+        LogRel((DEVICE_NAME ": failed to disable autounloading!\n"));
+    rc = mod_install(&g_vbmsSolModLinkage);
+
+    LogRel((DEVICE_NAME ": initialisation returning %d.\n", rc));
+    return rc;
+}
+
+
+#ifdef TESTCASE
+/** Simple test of the flow through _init. */
+static void test_init(RTTEST hTest)
+{
+    RTTestSub(hTest, "Testing _init");
+    RTTEST_CHECK(hTest, _init() == 0);
+}
+#endif
+
+
+/** Driver cleanup. */
+int _fini(void)
+{
+    int rc;
+
+    LogRelFlow((DEVICE_NAME ":_fini\n"));
+    rc = mod_remove(&g_vbmsSolModLinkage);
+    mutex_destroy(&g_OpenNodeState.InitMtx);
+
+    return rc;
+}
+
+
+/** Driver identification. */
+int _info(struct modinfo *pModInfo)
+{
+    int rc;
+    LogRelFlow((DEVICE_NAME ":_info\n"));
+    rc = mod_info(&g_vbmsSolModLinkage, pModInfo);
+    LogRelFlow((DEVICE_NAME ":_info returning %d\n", rc));
+    return rc;
+}
+
+
+/******************************************************************************
+*   Initialisation entry points                                               *
+******************************************************************************/
+
+/**
+ * Attach entry point, to attach a device to the system or resume it.
+ *
+ * @param   pDip            The module structure instance.
+ * @param   enmCmd          Attach type (ddi_attach_cmd_t)
+ *
+ * @return  corresponding solaris error code.
+ */
+int vbmsSolAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
+{
+    LogRelFlow((DEVICE_NAME "::Attach\n"));
+    switch (enmCmd)
+    {
+        case DDI_ATTACH:
+        {
+            int rc;
+            int instance = ddi_get_instance(pDip);
+            /* Only one instance supported. */
+            if (!ASMAtomicCmpXchgPtr(&g_OpenNodeState.pDip, pDip, NULL))
+                return DDI_FAILURE;
+            rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, 0);
+            if (rc == DDI_SUCCESS)
+                return DDI_SUCCESS;
+            ASMAtomicWritePtr(&g_OpenNodeState.pDip, NULL);
+            return DDI_FAILURE;
+        }
+
+        case DDI_RESUME:
+        {
+            /** @todo implement resume for guest driver. */
+            return DDI_SUCCESS;
+        }
+
+        default:
+            return DDI_FAILURE;
+    }
+}
+
+
+/**
+ * Detach entry point, to detach a device to the system or suspend it.
+ *
+ * @param   pDip            The module structure instance.
+ * @param   enmCmd          Attach type (ddi_attach_cmd_t)
+ *
+ * @return  corresponding solaris error code.
+ */
+int vbmsSolDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+    LogRelFlow((DEVICE_NAME "::Detach\n"));
+    switch (enmCmd)
+    {
+        case DDI_DETACH:
+        {
+            ddi_remove_minor_node(pDip, NULL);
+            ASMAtomicWritePtr(&g_OpenNodeState.pDip, NULL);
+            return DDI_SUCCESS;
+        }
+
+        case DDI_SUSPEND:
+        {
+            /** @todo implement suspend for guest driver. */
+            return DDI_SUCCESS;
+        }
+
+        default:
+            return DDI_FAILURE;
+    }
+}
+
+
+/**
+ * Info entry point, called by solaris kernel for obtaining driver info.
+ *
+ * @param   pDip            The module structure instance (do not use).
+ * @param   enmCmd          Information request type.
+ * @param   pvArg           Type specific argument.
+ * @param   ppvResult       Where to store the requested info.
+ *
+ * @return  corresponding solaris error code.
+ */
+int vbmsSolGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg,
+                   void **ppvResult)
+{
+    LogRelFlow((DEVICE_NAME "::GetInfo\n"));
+
+    int rc = DDI_SUCCESS;
+    switch (enmCmd)
+    {
+        case DDI_INFO_DEVT2DEVINFO:
+            *ppvResult = (void *)g_OpenNodeState.pDip;
+            break;
+
+        case DDI_INFO_DEVT2INSTANCE:
+            *ppvResult = (void *)(uintptr_t)ddi_get_instance(g_OpenNodeState.pDip);
+            break;
+
+        default:
+            rc = DDI_FAILURE;
+            break;
+    }
+
+    NOREF(pvArg);
+    return rc;
+}
+
+
+/******************************************************************************
+*   Main code                                                                 *
+******************************************************************************/
+
+static void vbmsSolNotify(void *pvState);
+static void vbmsSolVUIDPutAbsEvent(PVBMSSTATE pState, ushort_t cEvent,
+                                   int cValue);
+
+/**
+ * Open callback for the read queue, which we use as a generic device open
+ * handler.
+ */
+int vbmsSolOpen(queue_t *pReadQueue, dev_t *pDev, int fFlag, int fMode,
+                 cred_t *pCred)
+{
+    PVBMSSTATE pState = NULL;
+    int rc = VINF_SUCCESS;
+
+    NOREF(fFlag);
+    NOREF(pCred);
+    LogRelFlow((DEVICE_NAME "::Open, pWriteQueue=%p\n", WR(pReadQueue)));
+
+    /*
+     * Sanity check on the mode parameter - only open as a driver, not a
+     * module, and we do cloning ourselves.
+     */
+    if (fMode)
+    {
+        LogRel(("::Open: invalid attempt to clone device."));
+        return EINVAL;
+    }
+
+    pState = &g_OpenNodeState;
+    mutex_enter(&pState->InitMtx);
+    /*
+     * Check and remember our STREAM queue.
+     */
+    if (   pState->pWriteQueue
+        && (pState->pWriteQueue != WR(pReadQueue)))
+    {
+        mutex_exit(&pState->InitMtx);
+        LogRel((DEVICE_NAME "::Open: unexpectedly called with a different queue to previous calls.  Exiting.\n"));
+        return EINVAL;
+    }
+    if (!pState->cInits)
+    {
+        /*
+         * Initialize IPRT R0 driver, which internally calls OS-specific r0
+         * init, and create a new session.
+         */
+        rc = VbglInit();
+        if (RT_SUCCESS(rc))
+        {
+            rc = VbglGRAlloc((VMMDevRequestHeader **)
+                             &pState->pMouseStatusReq,
+                             sizeof(*pState->pMouseStatusReq),
+                             VMMDevReq_GetMouseStatus);
+            if (RT_FAILURE(rc))
+                VbglTerminate();
+            else
+            {
+                int rc2;
+                /* Initialise user data for the queues to our state and
+                 * vice-versa. */
+                pState->pWriteQueue = WR(pReadQueue);
+                WR(pReadQueue)->q_ptr = (char *)pState;
+                pReadQueue->q_ptr = (char *)pState;
+                qprocson(pReadQueue);
+                /* Enable our IRQ handler. */
+                rc2 = VbglSetMouseNotifyCallback(vbmsSolNotify,
+                                                 (void *)pState);
+                if (RT_FAILURE(rc2))
+                    /* Log the failure.  I may well have not understood what
+                     * is going on here, and the logging may help me. */
+                    LogRelFlow(("Failed to install the event handler call-back, rc=%Rrc\n",
+                                rc2));
+            }
+        }
+    }
+    if (RT_SUCCESS(rc))
+        ++pState->cInits;
+    mutex_exit(&pState->InitMtx);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("open time initialisation failed. rc=%d\n", rc));
+        ASMAtomicWriteNullPtr(&pState->pWriteQueue);
+        return EINVAL;
+    }
+    return 0;
+}
+
+
+/**
+ * Notification callback, called when the VBoxGuest mouse pointer is moved. 
+ * We send a VUID event up to user space.  We may send a miscalculated event
+ * if a resolution change is half-way through, but that is pretty much to be
+ * expected, so we won't worry about it.
+ */
+void vbmsSolNotify(void *pvState)
+{
+    PVBMSSTATE pState = (PVBMSSTATE)pvState;
+    int rc;
+
+    pState->pMouseStatusReq->mouseFeatures = 0;
+    pState->pMouseStatusReq->pointerXPos = 0;
+    pState->pMouseStatusReq->pointerYPos = 0;
+    rc = VbglGRPerform(&pState->pMouseStatusReq->header);
+    if (RT_SUCCESS(rc))
+    {
+        int cMaxScreenX  = pState->cMaxScreenX;
+        int cMaxScreenY  = pState->cMaxScreenY;
+        int x = pState->pMouseStatusReq->pointerXPos;
+        int y = pState->pMouseStatusReq->pointerYPos;
+
+        if (cMaxScreenX && cMaxScreenY)
+        {
+            vbmsSolVUIDPutAbsEvent(pState, LOC_X_ABSOLUTE,
+                                   x * cMaxScreenX / VMMDEV_MOUSE_RANGE_MAX);
+            vbmsSolVUIDPutAbsEvent(pState, LOC_Y_ABSOLUTE,
+                                   y * cMaxScreenY / VMMDEV_MOUSE_RANGE_MAX);
+        }
+    }
+}
+
+
+void vbmsSolVUIDPutAbsEvent(PVBMSSTATE pState, ushort_t cEvent,
+                            int cValue)
+{
+    queue_t *pReadQueue = RD(pState->pWriteQueue);
+    mblk_t *pMBlk = allocb(sizeof(Firm_event), BPRI_HI);
+    Firm_event *pEvent;
+    AssertReturnVoid(cEvent == LOC_X_ABSOLUTE || cEvent == LOC_Y_ABSOLUTE);
+    if (!pMBlk)
+        return;  /* If kernel memory is short a missed event is acceptable! */
+    pEvent = (Firm_event *)pMBlk->b_wptr;
+    pEvent->id        = cEvent;
+    pEvent->pair_type = FE_PAIR_DELTA;
+    pEvent->pair      = cEvent == LOC_X_ABSOLUTE ? LOC_X_DELTA : LOC_Y_DELTA;
+    pEvent->value     = cValue;
+    uniqtime32(&pEvent->time);
+    pMBlk->b_wptr += sizeof(Firm_event);
+    /* Put the message on the queue immediately if it is not blocked. */
+    if (canput(pReadQueue->q_next))
+        putnext(pReadQueue, pMBlk);
+    else
+        putq(pReadQueue, pMBlk);
+}
+
+
+/**
+ * Close callback for the read queue, which we use as a generic device close
+ * handler.
+ */
+int vbmsSolClose(queue_t *pReadQueue, int fFlag, cred_t *pCred)
+{
+    PVBMSSTATE pState = (PVBMSSTATE)pReadQueue->q_ptr;
+
+    LogRelFlow((DEVICE_NAME "::Close, pWriteQueue=%p\n", WR(pReadQueue)));
+    NOREF(fFlag);
+    NOREF(pCred);
+
+    if (!pState)
+    {
+        Log((DEVICE_NAME "::Close: failed to get pState.\n"));
+        return EFAULT;
+    }
+
+    mutex_enter(&pState->InitMtx);
+    --pState->cInits;
+    if (!pState->cInits)
+    {
+        VbglSetMouseStatus(0);
+        /* Disable our IRQ handler. */
+        VbglSetMouseNotifyCallback(NULL, NULL);
+        qprocsoff(pReadQueue);
+
+        /*
+         * Close the session.
+         */
+        ASMAtomicWriteNullPtr(&pState->pWriteQueue);
+        pReadQueue->q_ptr = NULL;
+        VbglGRFree(&pState->pMouseStatusReq->header);
+        VbglTerminate();
+    }
+    mutex_exit(&pState->InitMtx);
+    return 0;
+}
+
+
+#ifdef TESTCASE
+/** Simple test of vbmsSolOpen and vbmsSolClose. */
+static void testOpenClose(RTTEST hTest)
+{
+    queue_t aQueues[2];
+    dev_t device = 0;
+    int rc;
+
+    RTTestSub(hTest, "Testing vbmsSolOpen and vbmsSolClose");
+    RT_ZERO(g_OpenNodeState);
+    RT_ZERO(aQueues);
+    doInitQueues(&aQueues[0]);
+    rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
+    RTTEST_CHECK(hTest, rc == 0);
+    RTTEST_CHECK(hTest, g_OpenNodeState.pWriteQueue == WR(&aQueues[0]));
+    vbmsSolClose(RD(&aQueues[0]), 0, NULL);
+}
+#endif
+
+
+/* Helper for vbmsSolWPut. */
+static int vbmsSolDispatchIOCtl(PVBMSSTATE pState, mblk_t *pMBlk);
+
+/**
+ * Handler for messages sent from above (user-space and upper modules) which
+ * land in our write queue.
+ */
+int vbmsSolWPut(queue_t *pWriteQueue, mblk_t *pMBlk)
+{
+    PVBMSSTATE pState = (PVBMSSTATE)pWriteQueue->q_ptr;
+    LogRelFlowFunc((DEVICE_NAME "::"));
+    switch (pMBlk->b_datap->db_type)
+    {
+        case M_FLUSH:
+            LogRelFlow(("M_FLUSH, FLUSHW=%RTbool, FLUSHR=%RTbool\n",
+                        *pMBlk->b_rptr & FLUSHW, *pMBlk->b_rptr & FLUSHR));
+            /* Flush the write queue if so requested. */
+            if (*pMBlk->b_rptr & FLUSHW)
+                flushq(pWriteQueue, FLUSHDATA);
+
+            /* Flush the read queue if so requested. */
+            if (*pMBlk->b_rptr & FLUSHR)
+                flushq(RD(pWriteQueue), FLUSHDATA);
+
+            /* We have no one below us to pass the message on to. */
+            return 0;
+        /* M_IOCDATA is additional data attached to (at least) transparent
+         * IOCtls.  We handle the two together here and separate them further
+         * down. */
+        case M_IOCTL:
+        case M_IOCDATA:
+        {
+            int err;
+
+            LogRelFlow((  pMBlk->b_datap->db_type == M_IOCTL
+                        ? "M_IOCTL\n" : "M_IOCDATA\n"));
+            err = vbmsSolDispatchIOCtl(pState, pMBlk);
+            if (!err)
+                qreply(pWriteQueue, pMBlk);
+            else
+                miocnak(pWriteQueue, pMBlk, 0, err);
+            break;
+        }
+        default:
+            LogRelFlow(("Unknown command, not acknowledging.\n"));
+    }
+    return 0;
+}
+
+
+#ifdef TESTCASE
+/* Constants, definitions and test functions for testWPut. */
+static const int g_ccTestWPutFirmEvent = VUID_FIRM_EVENT;
+# define PVGFORMAT (&g_ccTestWPutFirmEvent)
+# define CBGFORMAT (sizeof(g_ccTestWPutFirmEvent))
+static const Ms_screen_resolution g_TestResolution = { 640, 480 };
+# define PMSIOSRES (&g_TestResolution)
+# define CBMSIOSRES (sizeof(g_TestResolution))
+
+static inline bool testSetResolution(RTTEST hTest, queue_t *pWriteQueue,
+                                     struct msgb *pMBlk)
+{
+    PVBMSSTATE pState = (PVBMSSTATE)pWriteQueue->q_ptr;
+    RTTEST_CHECK_MSG_RET(hTest,    pState->cMaxScreenX
+                               == g_TestResolution.width - 1,
+                         (hTest, "pState->cMaxScreenX=%d\n",
+                          pState->cMaxScreenX), false);
+    RTTEST_CHECK_MSG_RET(hTest,    pState->cMaxScreenY
+                               == g_TestResolution.height - 1,
+                         (hTest, "pState->cMaxScreenY=%d\n",
+                          pState->cMaxScreenY), false);
+    return true;
+}
+
+/** Data table for testWPut. */
+static struct
+{
+    int iIOCCmd;
+    size_t cbData;
+    const void *pvDataIn;
+    size_t cbDataIn;
+    const void *pvDataOut;
+    size_t cbDataOut;
+    int rcExp;
+    bool (*pfnExtra)(RTTEST hTest, queue_t *pWriteQueue, struct msgb *pMBlk);
+    bool fCanTransparent;
+} g_asTestWPut[] =
+{
+   /* iIOCCmd          cbData           pvDataIn   cbDataIn
+      pvDataOut   cbDataOut rcExp   pfnExtra           fCanTransparent */
+    { VUIDGFORMAT,     sizeof(int),     NULL,      0,
+      PVGFORMAT, CBGFORMAT, 0,      NULL,              true },
+    { VUIDGFORMAT,     sizeof(int) - 1, NULL,      0,
+      NULL,       0,        EINVAL, NULL,              false },
+    { VUIDGFORMAT,     sizeof(int) + 1, NULL,      0,
+      PVGFORMAT, CBGFORMAT, 0,      NULL,              true },
+    { VUIDSFORMAT,     sizeof(int),     PVGFORMAT, CBGFORMAT,
+      NULL,       0,        0,      NULL,              true },
+    { MSIOSRESOLUTION, CBMSIOSRES,      PMSIOSRES, CBMSIOSRES,
+      NULL,       0,        0,      testSetResolution, true },
+    { VUIDGWHEELINFO,  0,               NULL,      0,
+      NULL,       0,        EINVAL, NULL,              true }
+};
+
+# undef PVGFORMAT
+# undef CBGFORMAT
+# undef PMSIOSRES
+# undef CBMSIOSRES
+
+/* Helpers for testWPut. */
+static void testWPutStreams(RTTEST hTest, unsigned i);
+static void testWPutTransparent(RTTEST hTest, unsigned i);
+static void testWPutIOCDataIn(RTTEST hTest, unsigned i);
+static void testWPutIOCDataOut(RTTEST hTest, unsigned i);
+
+/** Test WPut's handling of different IOCtls, which is bulk of the logic in
+ * this file. */
+static void testWPut(RTTEST hTest)
+{
+    unsigned i;
+
+    RTTestSub(hTest, "Testing vbmsWPut");
+    for (i = 0; i < RT_ELEMENTS(g_asTestWPut); ++i)
+    {
+        AssertReturnVoid(g_asTestWPut[i].cbDataIn <= g_asTestWPut[i].cbData);
+        AssertReturnVoid(g_asTestWPut[i].cbDataOut <= g_asTestWPut[i].cbData);
+        testWPutStreams(hTest, i);
+        if (g_asTestWPut[i].fCanTransparent)
+            testWPutTransparent(hTest, i);
+        if (g_asTestWPut[i].fCanTransparent && g_asTestWPut[i].cbDataIn)
+            testWPutIOCDataIn(hTest, i);
+        if (g_asTestWPut[i].fCanTransparent && g_asTestWPut[i].cbDataOut)
+            testWPutIOCDataOut(hTest, i);
+    }
+}
+
+
+#define MSG_DATA_SIZE 1024
+
+/** Simulate sending a streams IOCtl to WPut with the parameters from table
+ * line @a i. */
+void testWPutStreams(RTTEST hTest, unsigned i)
+{
+    queue_t aQueues[2];
+    dev_t device = 0;
+    struct msgb *pMBlk = allocb(sizeof(struct iocblk), BPRI_MED);
+    struct msgb *pMBlkCont = allocb(MSG_DATA_SIZE, BPRI_MED);
+    struct iocblk *pIOCBlk = pMBlk ? (struct iocblk *)pMBlk->b_rptr : NULL;
+    int rc, cFormat = 0;
+
+    AssertReturnVoid(pMBlk);
+    AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
+    RT_ZERO(aQueues);
+    doInitQueues(&aQueues[0]);
+    rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
+    RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
+    RTTEST_CHECK_MSG(hTest,    g_OpenNodeState.pWriteQueue
+                            == WR(&aQueues[0]), (hTest, "i=%u\n", i));
+    pMBlk->b_datap->db_type = M_IOCTL;
+    pIOCBlk->ioc_cmd = g_asTestWPut[i].iIOCCmd;
+    pIOCBlk->ioc_count = g_asTestWPut[i].cbData;
+    AssertReturnVoid(g_asTestWPut[i].cbData <= MSG_DATA_SIZE);
+    memcpy(pMBlkCont->b_rptr, g_asTestWPut[i].pvDataIn,
+           g_asTestWPut[i].cbDataIn);
+    pMBlk->b_cont = pMBlkCont;
+    rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
+    RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_error == g_asTestWPut[i].rcExp,
+                     (hTest, "i=%u, IOCBlk.ioc_error=%d\n", i,
+                      pIOCBlk->ioc_error));
+    RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_count == g_asTestWPut[i].cbDataOut,
+                     (hTest, "i=%u, ioc_count=%u\n", i, pIOCBlk->ioc_count));
+    RTTEST_CHECK_MSG(hTest, !memcmp(pMBlkCont->b_rptr,
+                                    g_asTestWPut[i].pvDataOut,
+                                    g_asTestWPut[i].cbDataOut),
+                     (hTest, "i=%u\n", i));
+    /* Hack to ensure that miocpullup() gets called when needed. */
+    if (g_asTestWPut[i].cbData > 0)
+        RTTEST_CHECK_MSG(hTest, pMBlk->b_flag == 1, (hTest, "i=%u\n", i));
+    if (!g_asTestWPut[i].rcExp)
+        RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
+                         (hTest, "i=%u\n", i));
+    if (g_asTestWPut[i].pfnExtra)
+        if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
+            RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
+                         __PRETTY_FUNCTION__);
+    vbmsSolClose(RD(&aQueues[1]), 0, NULL);
+    freemsg(pMBlk);
+}
+
+
+#define USER_ADDRESS 0xfeedbacc
+
+/** Simulate sending a transparent IOCtl to WPut with the parameters from table
+ * line @a i. */
+void testWPutTransparent(RTTEST hTest, unsigned i)
+{
+    queue_t aQueues[2];
+    dev_t device = 0;
+    struct msgb *pMBlk = allocb(sizeof(struct iocblk), BPRI_MED);
+    struct msgb *pMBlkCont = allocb(sizeof(void *), BPRI_MED);
+    struct iocblk *pIOCBlk = pMBlk ? (struct iocblk *)pMBlk->b_rptr : NULL;
+    struct copyreq *pCopyReq;
+    int rc, cFormat = 0;
+
+    /* if (g_asTestWPut[i].cbDataIn == 0 && g_asTestWPut[i].cbDataOut != 0)
+        return; */  /* This case will be handled once the current ones work. */
+    AssertReturnVoid(pMBlk);
+    AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
+    RT_ZERO(aQueues);
+    doInitQueues(&aQueues[0]);
+    rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
+    RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
+    RTTEST_CHECK_MSG(hTest,    g_OpenNodeState.pWriteQueue
+                            == WR(&aQueues[0]), (hTest, "i=%u\n", i));
+    pMBlk->b_datap->db_type = M_IOCTL;
+    pIOCBlk->ioc_cmd = g_asTestWPut[i].iIOCCmd;
+    pIOCBlk->ioc_count = TRANSPARENT;
+    *(void **)pMBlkCont->b_rptr = (void *)USER_ADDRESS;
+    pMBlk->b_cont = pMBlkCont;
+    rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
+    pCopyReq = (struct copyreq *)pMBlk->b_rptr;
+    RTTEST_CHECK_MSG(hTest, (   (   g_asTestWPut[i].cbDataIn
+                                 && (pMBlk->b_datap->db_type == M_COPYIN))
+                             || (   g_asTestWPut[i].cbDataOut
+                                 && (pMBlk->b_datap->db_type == M_COPYOUT))
+                             || (   (g_asTestWPut[i].rcExp == 0)
+                                 && pMBlk->b_datap->db_type == M_IOCACK)
+                             || (pMBlk->b_datap->db_type == M_IOCNAK)),
+                     (hTest, "i=%u, db_type=%u\n", i,
+                      (unsigned) pMBlk->b_datap->db_type));
+    /* Our TRANSPARENT IOCtls can only return non-zero if they have no payload.
+     * Others should either return zero or be non-TRANSPARENT only. */
+    if (pMBlk->b_datap->db_type == M_IOCNAK)
+        RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_error == g_asTestWPut[i].rcExp,
+                         (hTest, "i=%u, IOCBlk.ioc_error=%d\n", i,
+                          pIOCBlk->ioc_error));
+    if (g_asTestWPut[i].cbData)
+    {
+        RTTEST_CHECK_MSG(hTest, pCopyReq->cq_addr == (char *)USER_ADDRESS,
+                         (hTest, "i=%u, cq_addr=%p\n", i, pCopyReq->cq_addr));
+        RTTEST_CHECK_MSG(   hTest, pCopyReq->cq_size
+                         ==   g_asTestWPut[i].cbDataIn
+                            ? g_asTestWPut[i].cbDataIn
+                            : g_asTestWPut[i].cbDataOut,
+                         (hTest, "i=%u, cq_size=%llu\n", i,
+                          (unsigned long long)pCopyReq->cq_size));
+    }
+    /* Implementation detail - check that the private pointer is correctly
+     * set to the user address *for two direction IOCtls* or NULL otherwise. */
+    if (g_asTestWPut[i].cbDataIn && g_asTestWPut[i].cbDataOut)
+        RTTEST_CHECK_MSG(hTest, pCopyReq->cq_private == (mblk_t *)USER_ADDRESS,
+                         (hTest, "i=%u, cq_private=%p\n", i,
+                          pCopyReq->cq_private));
+    else if (   (pMBlk->b_datap->db_type == M_COPYIN)
+             || (pMBlk->b_datap->db_type == M_COPYOUT))
+        RTTEST_CHECK_MSG(hTest, !pCopyReq->cq_private,
+                         (hTest, "i=%u, cq_private=%p\n", i,
+                          pCopyReq->cq_private));
+    if (!g_asTestWPut[i].rcExp)
+        RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
+                         (hTest, "i=%u\n", i));
+    if (g_asTestWPut[i].pfnExtra && !g_asTestWPut[i].cbData)
+        if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
+            RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
+                         __PRETTY_FUNCTION__);
+    vbmsSolClose(RD(&aQueues[1]), 0, NULL);
+    freemsg(pMBlk);
+}
+
+
+/** Simulate sending follow-on IOCData messages to a transparent IOCtl to WPut
+ * with the parameters from table line @a i. */
+void testWPutIOCDataIn(RTTEST hTest, unsigned i)
+{
+    queue_t aQueues[2];
+    dev_t device = 0;
+    struct msgb *pMBlk = allocb(sizeof(struct copyresp), BPRI_MED);
+    struct msgb *pMBlkCont = allocb(MSG_DATA_SIZE, BPRI_MED);
+    struct copyresp *pCopyResp = pMBlk ? (struct copyresp *)pMBlk->b_rptr
+                                       : NULL;
+    void *pvData = pMBlkCont ? pMBlkCont->b_rptr : NULL;
+    struct copyreq *pCopyReq;
+    int rc, cFormat = 0;
+
+    AssertReturnVoid(pMBlk);
+    AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
+    RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%s: i=%u\n", __PRETTY_FUNCTION__,
+                 i);
+    AssertReturnVoidStmt(g_asTestWPut[i].cbDataIn, freemsg(pMBlk));
+    RT_ZERO(aQueues);
+    doInitQueues(&aQueues[0]);
+    rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
+    RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
+    RTTEST_CHECK_MSG(hTest,    g_OpenNodeState.pWriteQueue
+                            == WR(&aQueues[0]), (hTest, "i=%u\n", i));
+    pMBlk->b_datap->db_type = M_IOCDATA;
+    pCopyResp->cp_cmd = g_asTestWPut[i].iIOCCmd;
+    if (g_asTestWPut[i].cbDataOut)
+        pCopyResp->cp_private = USER_ADDRESS;
+    AssertReturnVoid(g_asTestWPut[i].cbData <= MSG_DATA_SIZE);
+    memcpy(pMBlkCont->b_rptr, g_asTestWPut[i].pvDataIn, g_asTestWPut[i].cbDataIn);
+    pMBlk->b_cont = pMBlkCont;
+    rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
+    pCopyReq = (struct copyreq *)pMBlk->b_rptr;
+    RTTEST_CHECK_MSG(hTest, (   (   g_asTestWPut[i].cbDataOut
+                                 && (pMBlk->b_datap->db_type == M_COPYOUT))
+                             || (   (g_asTestWPut[i].rcExp == 0)
+                                 && pMBlk->b_datap->db_type == M_IOCACK)
+                             || (pMBlk->b_datap->db_type == M_IOCNAK)),
+                     (hTest, "i=%u, db_type=%u\n", i,
+                      (unsigned) pMBlk->b_datap->db_type));
+    if (g_asTestWPut[i].cbDataOut)
+    {
+        RTTEST_CHECK_MSG(hTest, pCopyReq->cq_addr == (char *)pvData,
+                         (hTest, "i=%u, cq_addr=%p\n", i, pCopyReq->cq_addr));
+        RTTEST_CHECK_MSG(hTest, pCopyReq->cq_size == g_asTestWPut[i].cbData,
+                         (hTest, "i=%u, cq_size=%llu\n", i,
+                          (unsigned long long)pCopyReq->cq_size));
+        RTTEST_CHECK_MSG(hTest, !memcmp(pvData, g_asTestWPut[i].pvDataOut,
+                                        g_asTestWPut[i].cbDataOut),
+                         (hTest, "i=%u\n", i));
+    }
+    RTTEST_CHECK_MSG(hTest, !pCopyReq->cq_private,
+                     (hTest, "i=%u, cq_private=%p\n", i,
+                      pCopyReq->cq_private));
+    if (!g_asTestWPut[i].rcExp)
+        RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
+                         (hTest, "i=%u\n", i));
+    if (g_asTestWPut[i].pfnExtra && !g_asTestWPut[i].cbDataOut)
+        if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
+            RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
+                         __PRETTY_FUNCTION__);
+    vbmsSolClose(RD(&aQueues[1]), 0, NULL);
+    freemsg(pMBlk);
+}
+
+
+/** Simulate sending follow-on IOCData messages to a transparent IOCtl to WPut
+ * with the parameters from table line @a i. */
+void testWPutIOCDataOut(RTTEST hTest, unsigned i)
+{
+    queue_t aQueues[2];
+    dev_t device = 0;
+    struct msgb *pMBlk = allocb(sizeof(struct copyresp), BPRI_MED);
+    struct copyresp *pCopyResp = pMBlk ? (struct copyresp *)pMBlk->b_rptr
+                                       : NULL;
+    int rc, cFormat = 0;
+
+    AssertReturnVoid(pMBlk);
+    AssertReturnVoidStmt(g_asTestWPut[i].cbDataOut, freemsg(pMBlk));
+    RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%s: i=%u\n", __PRETTY_FUNCTION__,
+                 i);
+    RT_ZERO(aQueues);
+    doInitQueues(&aQueues[0]);
+    rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
+    RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
+    RTTEST_CHECK_MSG(hTest,    g_OpenNodeState.pWriteQueue
+                            == WR(&aQueues[0]), (hTest, "i=%u\n", i));
+    pMBlk->b_datap->db_type = M_IOCDATA;
+    pCopyResp->cp_cmd = g_asTestWPut[i].iIOCCmd;
+    rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
+    RTTEST_CHECK_MSG(hTest, pMBlk->b_datap->db_type == M_IOCACK,
+                     (hTest, "i=%u, db_type=%u\n", i,
+                      (unsigned) pMBlk->b_datap->db_type));
+    if (!g_asTestWPut[i].rcExp)
+        RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
+                         (hTest, "i=%u\n", i));
+    vbmsSolClose(RD(&aQueues[1]), 0, NULL);
+    freemsg(pMBlk);
+}
+#endif
+
+
+/** Data transfer direction of an IOCtl.  This is used for describing
+ * transparent IOCtls, and @a UNSPECIFIED is not a valid value for them. */
+enum IOCTLDIRECTION
+{
+    /** This IOCtl transfers no data. */
+    NONE,
+    /** This IOCtl only transfers data from user to kernel. */
+    IN,
+    /** This IOCtl only transfers data from kernel to user. */
+    OUT,
+    /** This IOCtl transfers data from user to kernel and back. */
+    BOTH,
+    /** We aren't saying anything about how the IOCtl transfers data. */
+    UNSPECIFIED
+};
+
+/**
+ * IOCtl handler function.
+ * @returns 0 on success, error code on failure.
+ * @param iCmd      The IOCtl command number.
+ * @param pvData    Buffer for the user data.
+ * @param cbBuffer  Size of the buffer in @a pvData or zero.
+ * @param pcbData   Where to set the size of the data returned.  Required for
+ *                  handlers which return data.
+ * @param prc       Where to store the return code.  Default is zero.  Only
+ *                  used for IOCtls without data for convenience of
+ *                  implemention.
+ */
+typedef int FNVBMSSOLIOCTL(PVBMSSTATE pState, int iCmd, void *pvData,
+                            size_t cbBuffer, size_t *pcbData, int *prc);
+typedef FNVBMSSOLIOCTL *PFNVBMSSOLIOCTL;
+
+/* Helpers for vbmsSolDispatchIOCtl. */
+static int vbmsSolHandleIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
+                               PFNVBMSSOLIOCTL pfnHandler,
+                               int iCmd, size_t cbCmd,
+                               enum IOCTLDIRECTION enmDirection);
+static int vbmsSolVUIDIOCtl(PVBMSSTATE pState, int iCmd, void *pvData,
+                             size_t cbBuffer, size_t *pcbData, int *prc);
+
+/** Table of supported VUID IOCtls. */
+struct
+{
+    /** The IOCtl number. */
+    int iCmd;
+    /** The size of the buffer which needs to be copied between user and kernel
+     * space, or zero if unknown (must be known for tranparent IOCtls). */
+    size_t cbBuffer;
+    /** The direction the buffer data needs to be copied.  This must be
+     * specified for transparent IOCtls. */
+    enum IOCTLDIRECTION enmDirection;
+} g_aVUIDIOCtlDescriptions[] =
+{
+   { VUIDGFORMAT,     sizeof(int),                  OUT         },
+   { VUIDSFORMAT,     sizeof(int),                  IN          },
+   { VUIDGADDR,       0,                            UNSPECIFIED },
+   { VUIDGADDR,       0,                            UNSPECIFIED },
+   { MSIOGETPARMS,    sizeof(Ms_parms),             OUT         },
+   { MSIOSETPARMS,    sizeof(Ms_parms),             IN          },
+   { MSIOSRESOLUTION, sizeof(Ms_screen_resolution), IN          },
+   { MSIOBUTTONS,     sizeof(int),                  OUT         },
+   { VUIDGWHEELCOUNT, sizeof(int),                  OUT         },
+   { VUIDGWHEELINFO,  0,                            UNSPECIFIED },
+   { VUIDGWHEELSTATE, 0,                            UNSPECIFIED },
+   { VUIDSWHEELSTATE, 0,                            UNSPECIFIED }
+};
+
+/**
+ * Handle a STREAMS IOCtl message for our driver on the write stream.  This
+ * function takes care of the IOCtl logic only and does not call qreply() or
+ * miocnak() at all - the caller must call these on success or failure
+ * respectively.
+ * @returns  0 on success or the IOCtl error code on failure.
+ * @param  pState       pointer to the state structure.
+ * @param  pMBlk        pointer to the STREAMS message block structure.
+ */
+static int vbmsSolDispatchIOCtl(PVBMSSTATE pState, mblk_t *pMBlk)
+{
+    struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+    int iCmd = pIOCBlk->ioc_cmd, iCmdType = iCmd & (0xff << 8);
+    size_t cbBuffer;
+    enum IOCTLDIRECTION enmDirection;
+
+    LogRelFlowFunc((DEVICE_NAME "::pIOCBlk=%p, iCmdType=%c, iCmd=0x%x\n",
+                    pIOCBlk, (char) (iCmdType >> 8), (unsigned)iCmd));
+    switch (iCmdType)
+    {
+        case MSIOC:
+        case VUIOC:
+        {
+            unsigned i;
+            
+            for (i = 0; i < RT_ELEMENTS(g_aVUIDIOCtlDescriptions); ++i)
+                if (g_aVUIDIOCtlDescriptions[i].iCmd == iCmd)
+                {
+                    cbBuffer     = g_aVUIDIOCtlDescriptions[i].cbBuffer;
+                    enmDirection = g_aVUIDIOCtlDescriptions[i].enmDirection;
+                    return vbmsSolHandleIOCtl(pState, pMBlk,
+                                              vbmsSolVUIDIOCtl, iCmd,
+                                              cbBuffer, enmDirection);
+                }
+            return EINVAL;
+        }
+        default:
+            return ENOTTY;
+    }
+}
+
+
+/* Helpers for vbmsSolHandleIOCtl. */
+static int vbmsSolHandleIOCtlData(PVBMSSTATE pState, mblk_t *pMBlk,
+                                  PFNVBMSSOLIOCTL pfnHandler, int iCmd,
+                                  size_t cbCmd,
+                                  enum IOCTLDIRECTION enmDirection);
+
+static int vbmsSolHandleTransparentIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
+                                         PFNVBMSSOLIOCTL pfnHandler,
+                                         int iCmd, size_t cbCmd,
+                                         enum IOCTLDIRECTION enmDirection);
+
+static int vbmsSolHandleIStrIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
+                                   PFNVBMSSOLIOCTL pfnHandler, int iCmd);
+
+static void vbmsSolAcknowledgeIOCtl(mblk_t *pMBlk, int cbData, int rc)
+{
+    struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+
+    pMBlk->b_datap->db_type = M_IOCACK;
+    pIOCBlk->ioc_count = cbData;
+    pIOCBlk->ioc_rval = rc;
+    pIOCBlk->ioc_error = 0;
+}
+
+/**
+ * Generic code for handling STREAMS-specific IOCtl logic and boilerplate.  It
+ * calls the IOCtl handler passed to it without the handler having to be aware
+ * of STREAMS structures, or whether this is a transparent (traditional) or an
+ * I_STR (using a STREAMS structure to describe the data) IOCtl.  With the
+ * caveat that we only support transparent IOCtls which pass all data in a
+ * single buffer of a fixed size (I_STR IOCtls are restricted to a single
+ * buffer anyway, but the caller can choose the buffer size).
+ * @returns  0 on success or the IOCtl error code on failure.
+ * @param  pState         pointer to the state structure.
+ * @param  pMBlk          pointer to the STREAMS message block structure.
+ * @param  pfnHandler     pointer to the right IOCtl handler function for this
+ *                        IOCtl number.
+ * @param  iCmd           IOCtl command number.
+ * @param  cbCmd          size of the user space buffer for this IOCtl number,
+ *                        used for processing transparent IOCtls.  Pass zero
+ *                        for IOCtls with no maximum buffer size (which will
+ *                        not be able to be handled as transparent) or with
+ *                        no argument.
+ * @param  enmDirection   data transfer direction of the IOCtl.
+ */
+static int vbmsSolHandleIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
+                              PFNVBMSSOLIOCTL pfnHandler, int iCmd,
+                              size_t cbCmd, enum IOCTLDIRECTION enmDirection)
+{
+    struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+
+    LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d\n",
+                 (unsigned)iCmd, (int)cbCmd, (int)enmDirection));
+    if (pMBlk->b_datap->db_type == M_IOCDATA)
+        return vbmsSolHandleIOCtlData(pState, pMBlk, pfnHandler, iCmd,
+                                      cbCmd, enmDirection);
+    else if (   pMBlk->b_datap->db_type == M_IOCTL
+             && pIOCBlk->ioc_count == TRANSPARENT)
+        return vbmsSolHandleTransparentIOCtl(pState, pMBlk, pfnHandler,
+                                             iCmd, cbCmd, enmDirection);
+    else if (pMBlk->b_datap->db_type == M_IOCTL)
+        return vbmsSolHandleIStrIOCtl(pState, pMBlk, pfnHandler, iCmd);
+    return EINVAL;
+}
+
+
+/**
+ * Helper for vbmsSolHandleIOCtl.  This rather complicated-looking
+ * code is basically the standard boilerplate for handling any streams IOCtl
+ * additional data, which we currently only use for transparent IOCtls.
+ * @copydoc vbmsSolHandleIOCtl
+ */
+static int vbmsSolHandleIOCtlData(PVBMSSTATE pState, mblk_t *pMBlk,
+                                  PFNVBMSSOLIOCTL pfnHandler, int iCmd,
+                                  size_t cbCmd,
+                                  enum IOCTLDIRECTION enmDirection)
+{
+    struct copyresp *pCopyResp = (struct copyresp *)pMBlk->b_rptr;
+
+    LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d, cp_rval=%d, cp_private=%p\n",
+                 (unsigned)iCmd, (int)cbCmd, (int)enmDirection,
+                 (int)(uintptr_t)pCopyResp->cp_rval,
+                 (void *)pCopyResp->cp_private));
+    if (pCopyResp->cp_rval)  /* cp_rval is a pointer used as a boolean. */
+    {
+        freemsg(pMBlk);
+        return EAGAIN;
+    }
+    if ((pCopyResp->cp_private && enmDirection == BOTH) || enmDirection == IN)
+    {
+        size_t cbData = 0;
+        void *pvData = NULL;
+        int err;
+
+        if (!pMBlk->b_cont)
+            return EINVAL;
+        if (enmDirection == BOTH && !pCopyResp->cp_private)
+            return EINVAL;
+        pvData = pMBlk->b_cont->b_rptr;
+        err = pfnHandler(pState, iCmd, pvData, cbCmd, &cbData, NULL);
+        if (!err && enmDirection == BOTH)
+            mcopyout(pMBlk, NULL, cbData, pCopyResp->cp_private, NULL);
+        else if (!err && enmDirection == IN)
+            vbmsSolAcknowledgeIOCtl(pMBlk, 0, 0);
+        return err;
+    }
+    else
+    {
+        AssertReturn(enmDirection == OUT || enmDirection == BOTH, EINVAL);
+        vbmsSolAcknowledgeIOCtl(pMBlk, 0, 0);
+        return 0;
+    }
+}
+
+/**
+ * Helper for vbmsSolHandleIOCtl.  This rather complicated-looking
+ * code is basically the standard boilerplate for handling transparent IOCtls,
+ * that is, IOCtls which are not re-packed inside STREAMS IOCtls.
+ * @copydoc vbmsSolHandleIOCtl
+ */
+int vbmsSolHandleTransparentIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
+                                  PFNVBMSSOLIOCTL pfnHandler, int iCmd,
+                                  size_t cbCmd,
+                                  enum IOCTLDIRECTION enmDirection)
+{
+    int err = 0, rc = 0;
+    size_t cbData = 0;
+
+    LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d\n",
+                 (unsigned)iCmd, (int)cbCmd, (int)enmDirection));
+    if (   (enmDirection != NONE && !pMBlk->b_cont)
+        || enmDirection == UNSPECIFIED)
+        return EINVAL;
+    if (enmDirection == IN || enmDirection == BOTH)
+    {
+        void *pUserAddr = NULL;
+        /* We only need state data if there is something to copy back. */
+        if (enmDirection == BOTH)
+            pUserAddr = *(void **)pMBlk->b_cont->b_rptr;
+	    mcopyin(pMBlk, pUserAddr /* state data */, cbCmd, NULL);
+	}
+	else if (enmDirection == OUT)
+    {
+        mblk_t *pMBlkOut = allocb(cbCmd, BPRI_MED);
+        void *pvData;
+
+        if (!pMBlkOut)
+            return EAGAIN;
+        pvData = pMBlkOut->b_rptr;
+        err = pfnHandler(pState, iCmd, pvData, cbCmd, &cbData, NULL);
+        if (!err)
+            mcopyout(pMBlk, NULL, cbData, NULL, pMBlkOut);
+        else
+            freemsg(pMBlkOut);
+    }
+    else
+    {
+        AssertReturn(enmDirection == NONE, EINVAL);
+        err = pfnHandler(pState, iCmd, NULL, 0, NULL, &rc);
+        if (!err)
+            vbmsSolAcknowledgeIOCtl(pMBlk, 0, rc);
+    }
+    return err;
+}
+                 
+/**
+ * Helper for vbmsSolHandleIOCtl.  This rather complicated-looking
+ * code is basically the standard boilerplate for handling any streams IOCtl.
+ * @copydoc vbmsSolHandleIOCtl
+ */
+static int vbmsSolHandleIStrIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
+                                  PFNVBMSSOLIOCTL pfnHandler, int iCmd)
+{
+    struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+    uint_t cbBuffer = pIOCBlk->ioc_count;
+    void *pvData = NULL;
+    int err, rc = 0;
+    size_t cbData = 0;
+    
+    LogFlowFunc(("iCmd=0x%x, cbBuffer=%u, b_cont=%p\n",
+                 (unsigned)iCmd, cbBuffer, (void *)pMBlk->b_cont));
+    if (cbBuffer && !pMBlk->b_cont)
+        return EINVAL;
+    /* Repack the whole buffer into a single message block if needed. */
+    if (cbBuffer)
+    {
+        err = miocpullup(pMBlk, cbBuffer);
+        if (err)
+            return err;
+        pvData = pMBlk->b_cont->b_rptr;
+    }
+    else if (pMBlk->b_cont)  /* consms forgets to set ioc_count. */
+    {
+        pvData = pMBlk->b_cont->b_rptr;
+        cbBuffer =   pMBlk->b_cont->b_datap->db_lim
+                   - pMBlk->b_cont->b_datap->db_base;
+    }
+    err = pfnHandler(pState, iCmd, pvData, cbBuffer, &cbData, &rc);
+    if (!err)
+    {
+        LogRelFlowFunc(("pMBlk=%p, pMBlk->b_datap=%p, pMBlk->b_rptr=%p\n",
+                        pMBlk, pMBlk->b_datap, pMBlk->b_rptr));
+        vbmsSolAcknowledgeIOCtl(pMBlk, cbData, rc);
+    }
+    return err;
+}
+
+
+/**
+ * Handle a VUID input device IOCtl.
+ * @copydoc FNVBMSSOLIOCTL
+ */
+static int vbmsSolVUIDIOCtl(PVBMSSTATE pState, int iCmd, void *pvData,
+                             size_t cbBuffer, size_t *pcbData, int *prc)
+{
+    LogRelFlowFunc((DEVICE_NAME "::pvData=%p " /* no '\n' */, pvData));
+    switch (iCmd)
+    {
+        case VUIDGFORMAT:
+        {
+            LogRelFlowFunc(("VUIDGFORMAT\n"));
+            if (cbBuffer < sizeof(int))
+                return EINVAL;
+            *(int *)pvData = VUID_FIRM_EVENT;
+            *pcbData = sizeof(int);
+            return 0;
+        }
+        case VUIDSFORMAT:
+            LogRelFlowFunc(("VUIDSFORMAT\n"));
+            /* We define our native format to be VUID_FIRM_EVENT, so there
+             * is nothing more to do and we exit here on success or on
+             * failure. */
+            return 0;
+        case VUIDGADDR:
+        case VUIDSADDR:
+            LogRelFlowFunc(("VUIDGADDR/VUIDSADDR\n"));
+            return ENOTTY;
+        case MSIOGETPARMS:
+        {
+            Ms_parms parms = { 0 };
+
+            LogRelFlowFunc(("MSIOGETPARMS\n"));
+            if (cbBuffer < sizeof(Ms_parms))
+                return EINVAL;
+            *(Ms_parms *)pvData = parms;
+            *pcbData = sizeof(Ms_parms);
+            return 0;
+        }
+        case MSIOSETPARMS:
+            LogRelFlowFunc(("MSIOSETPARMS\n"));
+            return 0;
+        case MSIOSRESOLUTION:
+        {
+            Ms_screen_resolution *pResolution = (Ms_screen_resolution *)pvData;
+            int rc;
+
+            LogRelFlowFunc(("MSIOSRESOLUTION, cbBuffer=%d, sizeof(Ms_screen_resolution)=%d\n",
+                            (int) cbBuffer,
+                            (int) sizeof(Ms_screen_resolution)));
+            if (cbBuffer < sizeof(Ms_screen_resolution))
+                return EINVAL;
+            LogRelFlowFunc(("%dx%d\n", pResolution->width,
+                            pResolution->height));
+            pState->cMaxScreenX = pResolution->width  - 1;
+            pState->cMaxScreenY = pResolution->height - 1;
+            /* Note: we don't disable this again until session close. */
+            rc = VbglSetMouseStatus(  VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
+                                    | VMMDEV_MOUSE_NEW_PROTOCOL);
+            if (RT_SUCCESS(rc))
+                return 0;
+            pState->cMaxScreenX = 0;
+            pState->cMaxScreenY = 0;
+            return ENODEV;
+        }
+        case MSIOBUTTONS:
+        {
+            LogRelFlowFunc(("MSIOBUTTONS\n"));
+            if (cbBuffer < sizeof(int))
+                return EINVAL;
+            *(int *)pvData = 0;
+            *pcbData = sizeof(int);
+            return 0;
+        }
+        case VUIDGWHEELCOUNT:
+        {
+            LogRelFlowFunc(("VUIDGWHEELCOUNT\n"));
+            if (cbBuffer < sizeof(int))
+                return EINVAL;
+            *(int *)pvData = 0;
+            *pcbData = sizeof(int);
+            return 0;
+        }
+        case VUIDGWHEELINFO:
+        case VUIDGWHEELSTATE:
+        case VUIDSWHEELSTATE:
+            LogRelFlowFunc(("VUIDGWHEELINFO/VUIDGWHEELSTATE/VUIDSWHEELSTATE\n"));
+            return EINVAL;
+        default:
+            LogRelFlowFunc(("Invalid IOCtl command %x\n", iCmd));
+            return EINVAL;
+    }
+}
+
+
+#ifdef TESTCASE
+int main(void)
+{
+    RTTEST hTest;
+    int rc = RTTestInitAndCreate("tstVBoxGuest-solaris", &hTest);
+    if (rc)
+        return rc;
+    RTTestBanner(hTest);
+    test_init(hTest);
+    testOpenClose(hTest);
+    testWPut(hTest);
+
+    /*
+     * Summary.
+     */
+    return RTTestSummaryAndDestroy(hTest);
+}
+#endif
