summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/solaris/Mouse
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/solaris/Mouse')
-rw-r--r--src/VBox/Additions/solaris/Mouse/Makefile.kmk81
-rw-r--r--src/VBox/Additions/solaris/Mouse/deps.asm49
-rw-r--r--src/VBox/Additions/solaris/Mouse/testcase/Makefile.kup0
-rw-r--r--src/VBox/Additions/solaris/Mouse/testcase/solaris.h454
-rw-r--r--src/VBox/Additions/solaris/Mouse/testcase/tstVBoxMouse-solaris.c170
-rw-r--r--src/VBox/Additions/solaris/Mouse/vboxms.c1450
-rw-r--r--src/VBox/Additions/solaris/Mouse/vboxms.conf42
-rw-r--r--src/VBox/Additions/solaris/Mouse/vboxmslnk.c217
-rw-r--r--src/VBox/Additions/solaris/Mouse/vboxmslnk.xml92
9 files changed, 2555 insertions, 0 deletions
diff --git a/src/VBox/Additions/solaris/Mouse/Makefile.kmk b/src/VBox/Additions/solaris/Mouse/Makefile.kmk
new file mode 100644
index 00000000..b3fec956
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/Makefile.kmk
@@ -0,0 +1,81 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Solaris Mouse Integration kernel module.
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox 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.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#ifneq ($(KBUILD_HOST),solaris)
+#$(error "The Solaris guest additions can only be built on Solaris!")
+#endif
+
+#
+# vboxms - The Mouse Integration Driver
+#
+SYSMODS.solaris += vboxms
+vboxms_TEMPLATE = VBoxGuestR0Drv
+vboxms_DEFS = VBOX_WITH_HGCM VBOX_SVN_REV=$(VBOX_SVN_REV)
+vboxms_DEPS += $(VBOX_SVN_REV_KMK)
+vboxms_SOURCES = \
+ vboxms.c
+vboxms_LIBS = \
+ $(VBOX_LIB_VBGL_R0)
+ifeq ($(KBUILD_HOST),solaris)
+ vboxms_LDFLAGS += -N drv/vboxguest -N misc/ctf
+else
+ vboxms_SOURCES += deps.asm
+ vboxms_deps.asm_ASFLAGS = -f bin -g null
+endif
+
+
+PROGRAMS += vboxmslnk
+vboxmslnk_TEMPLATE = VBoxGuestR3Exe
+vboxmslnk_SOURCES = vboxmslnk.c
+
+
+if 0 # Broken - unresolved externals: vbglDriver*, RTR0AssertPanicSystem.
+ if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK)
+ PROGRAMS += tstVBoxMouse-solaris
+ tstVBoxMouse-solaris_TEMPLATE = VBoxR3TstExe
+ tstVBoxMouse-solaris_SOURCES = \
+ vboxms.c \
+ testcase/tstVBoxMouse-solaris.c
+ tstVBoxMouse-solaris_DEFS = TESTCASE
+ tstVBoxMouse-solaris_LIBS = $(LIB_RUNTIME)
+ endif
+endif
+
+include $(KBUILD_PATH)/subfooter.kmk
+
diff --git a/src/VBox/Additions/solaris/Mouse/deps.asm b/src/VBox/Additions/solaris/Mouse/deps.asm
new file mode 100644
index 00000000..d01557ed
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/deps.asm
@@ -0,0 +1,49 @@
+; $Id: deps.asm $
+;; @file
+; Solaris kernel module dependency
+;
+
+;
+; Copyright (C) 2012-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox 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.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "iprt/solaris/kmoddeps.mac"
+
+kmoddeps_header ; ELF header, section table and shared string table
+
+kmoddeps_dynstr_start ; ELF .dynstr section
+kmoddeps_dynstr_string str_misc_ctf, "misc/ctf"
+kmoddeps_dynstr_string str_drv_vboxguest, "drv/vboxguest"
+kmoddeps_dynstr_end
+
+kmoddeps_dynamic_start ; ELF .dynamic section
+kmoddeps_dynamic_needed str_misc_ctf
+kmoddeps_dynamic_needed str_drv_vboxguest
+kmoddeps_dynamic_end
diff --git a/src/VBox/Additions/solaris/Mouse/testcase/Makefile.kup b/src/VBox/Additions/solaris/Mouse/testcase/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/testcase/Makefile.kup
diff --git a/src/VBox/Additions/solaris/Mouse/testcase/solaris.h b/src/VBox/Additions/solaris/Mouse/testcase/solaris.h
new file mode 100644
index 00000000..2040bba6
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/testcase/solaris.h
@@ -0,0 +1,454 @@
+/* $Id: solaris.h $ */
+/** @file
+ * VBoxGuest - Guest Additions Driver for Solaris - testcase stubs.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef GA_INCLUDED_SRC_solaris_Mouse_testcase_solaris_h
+#define GA_INCLUDED_SRC_solaris_Mouse_testcase_solaris_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/assert.h>
+#include <iprt/string.h> /* RT_ZERO */
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/winsock2.h> /* struct timeval */
+#endif
+#include <errno.h>
+#include <time.h> /* struct timeval */
+
+/* Overrides */
+#define dev_t unsigned
+
+/* Constants */
+#define DDI_FAILURE (-1)
+#define DDI_SUCCESS (0)
+
+#define MODMAXNAMELEN 32
+#define MODMAXLINKINFOLEN 32
+#define MODMAXLINK 10
+
+#define MOD_NOAUTOUNLOAD 0x1
+
+#define M_DATA 0x00
+#define M_BREAK 0x08
+#define M_PASSFP 0x09
+#define M_EVENT 0x0a
+#define M_SIG 0x0b
+#define M_DELAY 0x0c
+#define M_CTL 0x0d
+#define M_IOCTL 0x0e
+#define M_SETOPTS 0x10
+#define M_RSE 0x11
+
+#define M_IOCACK 0x81
+#define M_IOCNAK 0x82
+#define M_PCPROTO 0x83
+#define M_PCSIG 0x84
+#define M_READ 0x85
+#define M_FLUSH 0x86
+#define M_STOP 0x87
+#define M_START 0x88
+#define M_HANGUP 0x89
+#define M_ERROR 0x8a
+#define M_COPYIN 0x8b
+#define M_COPYOUT 0x8c
+#define M_IOCDATA 0x8d
+#define M_PCRSE 0x8e
+#define M_STOPI 0x8f
+#define M_STARTI 0x90
+#define M_PCEVENT 0x91
+#define M_UNHANGUP 0x92
+#define M_CMD 0x93
+
+#define BPRI_LO 1
+#define BPRI_MED 2
+#define BPRI_HI 3
+
+#define FLUSHALL 1
+#define FLUSHDATA 0
+
+#define TRANSPARENT (unsigned int)(-1)
+
+#define FLUSHR 0x01
+#define FLUSHW 0x02
+
+#define MSIOC ('m'<<8)
+#define MSIOGETPARMS (MSIOC|1)
+#define MSIOSETPARMS (MSIOC|2)
+#define MSIOBUTTONS (MSIOC|3)
+#define MSIOSRESOLUTION (MSIOC|4)
+
+#define VUIOC ('v'<<8)
+#define VUIDSFORMAT (VUIOC|1)
+#define VUIDGFORMAT (VUIOC|2)
+#define VUID_NATIVE 0
+#define VUID_FIRM_EVENT 1
+
+#define VUIDSADDR (VUIOC|3)
+#define VUIDGADDR (VUIOC|4)
+
+#define VUID_WHEEL_MAX_COUNT 256
+#define VUIDGWHEELCOUNT (VUIOC|15)
+#define VUIDGWHEELINFO (VUIOC|16)
+#define VUIDGWHEELSTATE (VUIOC|17)
+#define VUIDSWHEELSTATE (VUIOC|18)
+
+#define DDI_DEVICE_ATTR_V0 0x0001
+#define DDI_DEVICE_ATTR_V1 0x0002
+
+#define DDI_NEVERSWAP_ACC 0x00
+#define DDI_STRUCTURE_LE_ACC 0x01
+#define DDI_STRUCTURE_BE_ACC 0x02
+
+#define DDI_STRICTORDER_ACC 0x00
+#define DDI_UNORDERED_OK_ACC 0x01
+#define DDI_MERGING_OK_ACC 0x02
+#define DDI_LOADCACHING_OK_ACC 0x03
+#define DDI_STORECACHING_OK_ACC 0x04
+
+/** @todo fix this */
+#define DDI_DEFAULT_ACC DDI_STRICTORDER_ACC
+
+#define DDI_INTR_CLAIMED 1
+#define DDI_INTR_UNCLAIMED 0
+
+#define DDI_INTR_TYPE_FIXED 0x1
+#define DDI_INTR_TYPE_MSI 0x2
+#define DDI_INTR_TYPE_MSIX 0x4
+
+#define LOC_FIRST_DELTA 32640
+#define LOC_X_DELTA 32640
+#define LOC_Y_DELTA 32641
+#define LOC_LAST_DELTA 32641
+#define LOC_FIRST_ABSOLUTE 32642
+#define LOC_X_ABSOLUTE 32642
+#define LOC_Y_ABSOLUTE 32643
+#define LOC_LAST_ABSOLUTE 32643
+
+#define FE_PAIR_NONE 0
+#define FE_PAIR_SET 1
+#define FE_PAIR_DELTA 2
+#define FE_PAIR_ABSOLUTE 3
+
+typedef struct __ldi_handle *ldi_handle_t;
+
+typedef enum
+{
+ DDI_INFO_DEVT2DEVINFO = 0,
+ DDI_INFO_DEVT2INSTANCE = 1
+} ddi_info_cmd_t;
+
+typedef enum
+{
+ DDI_ATTACH = 0,
+ DDI_RESUME = 1,
+ DDI_PM_RESUME = 2
+} ddi_attach_cmd_t;
+
+typedef enum
+{
+ DDI_DETACH = 0,
+ DDI_SUSPEND = 1,
+ DDI_PM_SUSPEND = 2,
+ DDI_HOTPLUG_DETACH = 3
+} ddi_detach_cmd_t;
+
+/* Simple types */
+
+typedef struct cred *cred_t;
+typedef struct dev_info *dev_info_t;
+typedef struct __ddi_acc_handle * ddi_acc_handle_t;
+typedef struct __ddi_intr_handle *ddi_intr_handle_t;
+typedef struct mutex *kmutex_t;
+typedef unsigned int uint_t;
+typedef unsigned short ushort_t;
+typedef unsigned char uchar_t;
+
+/* Structures */
+
+struct modspecific_info {
+ char msi_linkinfo[MODMAXLINKINFOLEN];
+ int msi_p0;
+};
+
+struct modinfo {
+ int mi_info;
+ int mi_state;
+ int mi_id;
+ int mi_nextid;
+ char *mi_base; /* Was caddr_t. */
+ size_t mi_size;
+ int mi_rev;
+ int mi_loadcnt;
+ char mi_name[MODMAXNAMELEN];
+ struct modspecific_info mi_msinfo[MODMAXLINK];
+};
+
+typedef struct queue
+{
+ struct qinit *q_qinfo;
+ struct msgb *q_first;
+ struct msgb *q_last;
+ struct queue *q_next;
+ void *q_ptr;
+ size_t q_count;
+ uint_t q_flag;
+ ssize_t q_minpsz;
+ ssize_t q_maxpsz;
+ size_t q_hiwat;
+ size_t q_lowat;
+} queue_t;
+
+typedef struct msgb
+{
+ struct msgb *b_next;
+ struct msgb *b_prev;
+ struct msgb *b_cont;
+ unsigned char *b_rptr;
+ unsigned char *b_wptr;
+ struct datab *b_datap;
+ unsigned char b_band;
+ unsigned short b_flag;
+} mblk_t;
+
+typedef struct datab
+{
+ unsigned char *db_base;
+ unsigned char *db_lim;
+ unsigned char db_ref;
+ unsigned char db_type;
+} dblk_t;
+
+struct iocblk
+{
+ int ioc_cmd;
+ cred_t *ioc_cr;
+ uint_t ioc_id;
+ uint_t ioc_flag;
+ size_t ioc_count;
+ int ioc_rval;
+ int ioc_error;
+#if defined(RT_ARCH_AMD64) /* Actually this should be LP64. */
+ int dummy; /* For simplicity, to ensure the structure size matches
+ struct copyreq. */
+#endif
+};
+
+struct copyreq
+{
+ int cq_cmd;
+ cred_t *cq_cr;
+ uint_t cq_id;
+ uint_t cq_flag;
+ mblk_t *cq_private;
+ char *cq_addr; /* Was caddr_t. */
+ size_t cq_size;
+};
+
+struct copyresp
+{
+ int cp_cmd;
+ cred_t *cp_cr;
+ uint_t cp_id;
+ uint_t cp_flag;
+ mblk_t *cp_private;
+ char *cp_rval; /* Was caddr_t. */
+};
+
+typedef struct modctl
+{
+ /* ... */
+ char mod_loadflags;
+ /* ... */
+} modctl_t;
+
+typedef struct {
+ int jitter_thresh;
+ int speed_law;
+ int speed_limit;
+} Ms_parms;
+
+typedef struct {
+ int height;
+ int width;
+} Ms_screen_resolution;
+
+typedef struct vuid_addr_probe {
+ short base;
+ union
+ {
+ short next;
+ short current;
+ } data;
+} Vuid_addr_probe;
+
+typedef struct ddi_device_acc_attr
+{
+ ushort_t devacc_attr_version;
+ uchar_t devacc_attr_endian_flags;
+ uchar_t devacc_attr_dataorder;
+ uchar_t devacc_attr_access;
+} ddi_device_acc_attr_t;
+
+typedef struct firm_event
+{
+ ushort_t id;
+ uchar_t pair_type;
+ uchar_t pair;
+ int value;
+ struct timeval time;
+} Firm_event;
+
+/* Prototypes */
+
+#define _init vboxguestSolarisInit
+extern int vboxguestSolarisInit(void);
+#define _fini vboxguestSolarisFini
+extern int vboxguestSolarisFini(void);
+#define _info vboxguestSolarisInfo
+extern int vboxguestSolarisInfo(struct modinfo *pModInfo);
+
+/* Simple API stubs */
+
+#define cmn_err(...) do {} while(0)
+#define mod_remove(...) 0
+#define mod_info(...) 0
+#define RTR0Init(...) VINF_SUCCESS
+#define RTR0Term(...) do {} while(0)
+#define RTR0AssertPanicSystem(...) do {} while(0)
+#define RTLogCreate(...) VINF_SUCCESS
+#define RTLogRelSetDefaultInstance(...) do {} while(0)
+#define RTLogDestroy(...) do {} while(0)
+#if 0
+#define VBoxGuestCreateKernelSession(...) VINF_SUCCESS
+#define VBoxGuestCreateUserSession(...) VINF_SUCCESS
+#define VBoxGuestCloseSession(...) do {} while(0)
+#define VBoxGuestInitDevExt(...) VINF_SUCCESS
+#define VBoxGuestDeleteDevExt(...) do {} while(0)
+#define VBoxGuestCommonIOCtl(...) VINF_SUCCESS
+#define VBoxGuestCommonISR(...) true
+#define VbglR0GRAlloc(...) VINF_SUCCESS
+#define VbglR0GRPerform(...) VINF_SUCCESS
+#define VbglR0GRFree(...) do {} while(0)
+#endif
+#define VbglR0InitClient(...) VINF_SUCCESS
+#define vbglDriverOpen(...) VINF_SUCCESS
+#define vbglDriverClose(...) do {} while(0)
+#define vbglDriverIOCtl(...) VINF_SUCCESS
+#define qprocson(...) do {} while(0)
+#define qprocsoff(...) do {} while(0)
+#define flushq(...) do {} while(0)
+#define putnext(...) do {} while(0)
+#define ddi_get_instance(...) 0
+#define pci_config_setup(...) DDI_SUCCESS
+#define pci_config_teardown(...) do {} while(0)
+#define ddi_regs_map_setup(...) DDI_SUCCESS
+#define ddi_regs_map_free(...) do {} while(0)
+#define ddi_dev_regsize(...) DDI_SUCCESS
+#define ddi_create_minor_node(...) DDI_SUCCESS
+#define ddi_remove_minor_node(...) do {} while(0)
+#define ddi_intr_get_supported_types(...) DDI_SUCCESS
+#define ddi_intr_get_nintrs(...) DDI_SUCCESS
+#define ddi_intr_get_navail(...) DDI_SUCCESS
+#define ddi_intr_alloc(...) DDI_SUCCESS
+#define ddi_intr_free(...) do {} while(0)
+#define ddi_intr_get_pri(...) DDI_SUCCESS
+#define ddi_intr_enable(...) DDI_SUCCESS
+#define ddi_intr_disable(...) DDI_SUCCESS
+#define ddi_intr_add_handler(...) DDI_SUCCESS
+#define ddi_intr_remove_handler(...) DDI_SUCCESS
+#define mutex_init(...) do {} while(0)
+#define mutex_destroy(...) do {} while(0)
+#define mutex_enter(...) do {} while(0)
+#define mutex_exit(...) do {} while(0)
+#define uniqtime32(...) do {} while(0)
+#define canput(...) true
+#define putbq(...) do {} while(0)
+
+/* Externally defined helpers. */
+
+/** Flags set in the struct mblk b_flag member for verification purposes.
+ * @{ */
+/** miocpullup was called for this message. */
+#define F_TEST_PULLUP 1
+/** @} */
+
+extern void miocack(queue_t *pWriteQueue, mblk_t *pMBlk, int cbData, int rc);
+extern void miocnak(queue_t *pWriteQueue, mblk_t *pMBlk, int cbData, int iErr);
+extern int miocpullup(mblk_t *pMBlk, size_t cbMsg);
+extern void mcopyin(mblk_t *pMBlk, void *pvState, size_t cbData, void *pvUser);
+extern void mcopyout(mblk_t *pMBlk, void *pvState, size_t cbData, void *pvUser,
+ mblk_t *pMBlkData);
+extern void qreply(queue_t *pQueue, mblk_t *pMBlk);
+extern mblk_t *allocb(size_t cb, uint_t cPrio);
+extern void freemsg(mblk_t *pMsg);
+
+/* API stubs with simple logic */
+
+static modctl_t s_ModCtl;
+static void **s_pvLinkage;
+
+static inline modctl_t *mod_getctl(void **linkage)
+{
+ s_pvLinkage = linkage;
+ return s_pvLinkage ? &s_ModCtl : NULL;
+}
+
+#define mod_install(linkage) (s_pvLinkage && ((linkage) == s_pvLinkage) ? 0 : EINVAL)
+#define QREADR 0x00000010
+#define OTHERQ(q) ((q)->q_flag & QREADR ? (q) + 1 : (q) - 1)
+#define WR(q) ((q)->q_flag & QREADR ? (q) + 1 : (q))
+#define RD(q) ((q)->q_flag & QREADR ? (q) : (q) - 1)
+
+
+/* Basic initialisation of a queue structure pair for testing. */
+static inline void doInitQueues(queue_t aQueues[2])
+{
+ aQueues[0].q_flag = QREADR;
+}
+
+static inline dev_t makedevice(unsigned cMajor, unsigned cMinor)
+{
+ return cMajor * 4096 + cMinor;
+}
+
+static inline unsigned getmajor(dev_t device)
+{
+ return device / 4096;
+}
+
+/* API stubs with controllable logic */
+
+#endif /* !GA_INCLUDED_SRC_solaris_Mouse_testcase_solaris_h */
diff --git a/src/VBox/Additions/solaris/Mouse/testcase/tstVBoxMouse-solaris.c b/src/VBox/Additions/solaris/Mouse/testcase/tstVBoxMouse-solaris.c
new file mode 100644
index 00000000..f95fbd99
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/testcase/tstVBoxMouse-solaris.c
@@ -0,0 +1,170 @@
+/** @file
+ * VirtualBox Guest Additions Driver for Solaris - Solaris helper functions.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+
+#include "solaris.h"
+#include <iprt/alloc.h>
+
+
+/*********************************************************************************************************************************
+* Helper functions *
+*********************************************************************************************************************************/
+
+void miocack(queue_t *pWriteQueue, 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;
+ qreply(pWriteQueue, pMBlk);
+}
+
+void miocnak(queue_t *pWriteQueue, mblk_t *pMBlk, int cbData, int iErr)
+{
+ struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+
+ pMBlk->b_datap->db_type = M_IOCNAK;
+ pIOCBlk->ioc_count = cbData;
+ pIOCBlk->ioc_error = iErr ? iErr : EINVAL;
+ pIOCBlk->ioc_rval = 0;
+ qreply(pWriteQueue, pMBlk);
+}
+
+/* This does not work like the real version, but does some sanity testing
+ * and sets a flag. */
+int miocpullup(mblk_t *pMBlk, size_t cbMsg)
+{
+ struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+
+ if (pIOCBlk->ioc_count == TRANSPARENT)
+ return EINVAL;
+ if ( !pMBlk->b_cont
+ || pMBlk->b_cont->b_wptr < pMBlk->b_cont->b_rptr + cbMsg)
+ return EINVAL;
+ pMBlk->b_flag |= F_TEST_PULLUP;
+ return 0;
+}
+
+void mcopyin(mblk_t *pMBlk, void *pvState, size_t cbData, void *pvUser)
+{
+ struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+ struct copyreq *pCopyReq = (struct copyreq *)pMBlk->b_rptr;
+
+ AssertReturnVoid( pvUser
+ || ( pMBlk->b_datap->db_type == M_IOCTL
+ && pIOCBlk->ioc_count == TRANSPARENT
+ && pMBlk->b_cont->b_rptr));
+ pMBlk->b_datap->db_type = M_COPYIN;
+ pMBlk->b_wptr = pMBlk->b_rptr + sizeof(*pCopyReq);
+ pCopyReq->cq_private = pvState;
+ pCopyReq->cq_size = cbData;
+ pCopyReq->cq_addr = pvUser ? pvUser : *(void **)pMBlk->b_cont->b_rptr;
+ if (pMBlk->b_cont)
+ {
+ freemsg(pMBlk->b_cont);
+ pMBlk->b_cont = NULL;
+ }
+}
+
+void mcopyout(mblk_t *pMBlk, void *pvState, size_t cbData, void *pvUser,
+ mblk_t *pMBlkData)
+{
+ struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
+ struct copyreq *pCopyReq = (struct copyreq *)pMBlk->b_rptr;
+
+ AssertReturnVoid( pvUser
+ || ( pMBlk->b_datap->db_type == M_IOCTL
+ && pIOCBlk->ioc_count == TRANSPARENT
+ && pMBlk->b_cont->b_rptr));
+ pMBlk->b_datap->db_type = M_COPYOUT;
+ pMBlk->b_wptr = pMBlk->b_rptr + sizeof(*pCopyReq);
+ pCopyReq->cq_private = pvState;
+ pCopyReq->cq_size = cbData;
+ pCopyReq->cq_addr = pvUser ? pvUser : *(void **)pMBlk->b_cont->b_rptr;
+ if (pMBlkData)
+ {
+ if (pMBlk->b_cont)
+ freemsg(pMBlk->b_cont);
+ pMBlk->b_cont = pMBlkData;
+ pMBlkData->b_wptr = pMBlkData->b_rptr + cbData;
+ }
+}
+
+/* This does not work like the real version but is easy to test the result of.
+ */
+void qreply(queue_t *pQueue, mblk_t *pMBlk)
+{
+ OTHERQ(pQueue)->q_first = pMBlk;
+}
+
+/** @todo reference counting */
+mblk_t *allocb(size_t cb, uint_t cPrio)
+{
+ unsigned char *pch = RTMemAllocZ(cb);
+ struct msgb *pMBlk = (struct msgb *)RTMemAllocZ(sizeof(struct msgb));
+ struct datab *pDBlk = (struct datab *)RTMemAllocZ(sizeof(struct datab));
+ if (!pch || !pMBlk || !pDBlk)
+ {
+ RTMemFree(pch);
+ RTMemFree(pMBlk);
+ RTMemFree(pDBlk);
+ return NULL;
+ }
+ NOREF(cPrio);
+ pMBlk->b_rptr = pch;
+ pMBlk->b_wptr = pMBlk->b_rptr + cb;
+ pMBlk->b_datap = pDBlk;
+ pDBlk->db_base = pMBlk->b_rptr;
+ pDBlk->db_lim = pMBlk->b_wptr;
+ pDBlk->db_type = M_DATA;
+ return pMBlk;
+}
+
+/** @todo reference counting */
+void freemsg(mblk_t *pMBlk)
+{
+ if (!pMBlk)
+ return;
+ RTMemFree(pMBlk->b_rptr);
+ RTMemFree(pMBlk->b_datap);
+ freemsg(pMBlk->b_cont);
+ RTMemFree(pMBlk);
+}
diff --git a/src/VBox/Additions/solaris/Mouse/vboxms.c b/src/VBox/Additions/solaris/Mouse/vboxms.c
new file mode 100644
index 00000000..173db83e
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/vboxms.c
@@ -0,0 +1,1450 @@
+/* $Id: vboxms.c $ */
+/** @file
+ * VirtualBox Guest Additions Mouse Driver for Solaris.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_MOUSE
+#include <VBox/VMMDev.h>
+#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 "vboxms"
+/** 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);
+
+ LogRelFlow((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);
+ if (!rc)
+ 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;
+ /* Only one instance supported. */
+ if (!ASMAtomicCmpXchgPtr(&g_OpenNodeState.pDip, pDip, NULL))
+ return DDI_FAILURE;
+ rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, 0 /* instance */, DDI_PSEUDO, 0 /* flags */);
+ 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;
+ if (!*ppvResult)
+ rc = DDI_FAILURE;
+ break;
+ }
+
+ case DDI_INFO_DEVT2INSTANCE:
+ {
+ /* There can only be a single-instance of this driver and thus its instance number is 0. */
+ *ppvResult = (void *)0;
+ 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 = VbglR0InitClient();
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0GRAlloc((VMMDevRequestHeader **)
+ &pState->pMouseStatusReq,
+ sizeof(*pState->pMouseStatusReq),
+ VMMDevReq_GetMouseStatus);
+ if (RT_FAILURE(rc))
+ VbglR0TerminateClient();
+ 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 = VbglR0SetMouseNotifyCallback(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 = VbglR0GRPerform(&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)
+ {
+ VbglR0SetMouseStatus(0);
+ /* Disable our IRQ handler. */
+ VbglR0SetMouseNotifyCallback(NULL, NULL);
+ qprocsoff(pReadQueue);
+
+ /*
+ * Close the session.
+ */
+ ASMAtomicWriteNullPtr(&pState->pWriteQueue);
+ pReadQueue->q_ptr = NULL;
+ VbglR0GRFree(&pState->pMouseStatusReq->header);
+ VbglR0TerminateClient();
+ }
+ 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. */
+ freemsg(pMBlk);
+ 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. */
+ 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;
+ 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);
+ if ((err || enmDirection == IN) && pCopyResp->cp_private)
+ freemsg(pCopyResp->cp_private);
+ return err;
+ }
+ else
+ {
+ if (pCopyResp->cp_private)
+ freemsg(pCopyResp->cp_private);
+ 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 = VbglR0SetMouseStatus( 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
+
diff --git a/src/VBox/Additions/solaris/Mouse/vboxms.conf b/src/VBox/Additions/solaris/Mouse/vboxms.conf
new file mode 100644
index 00000000..eb180e18
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/vboxms.conf
@@ -0,0 +1,42 @@
+# $Id: vboxms.conf $
+## @file
+# OpenSolaris Guest Mouse Driver Configuration
+#
+
+#
+# Copyright (C) 2012-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox 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.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# This needs to go into /usr/kernel/drv,
+# while the 64-bit driver object goes into the amd64
+# subdirectory (32-bit drivers goes into the same
+# directory).
+#
+name="vboxms" parent="pseudo" instance=0;
diff --git a/src/VBox/Additions/solaris/Mouse/vboxmslnk.c b/src/VBox/Additions/solaris/Mouse/vboxmslnk.c
new file mode 100644
index 00000000..12c5ad34
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/vboxmslnk.c
@@ -0,0 +1,217 @@
+/* $Id: vboxmslnk.c $ */
+/** @file
+ * VirtualBox Guest Additions Mouse Driver for Solaris: user space loader tool.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <VBox/version.h>
+#include <iprt/buildconfig.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <getopt.h>
+
+#define VBOXMSLNK_MUXID_FILE "/system/volatile/vboxmslnk.muxid"
+
+static const char *g_pszProgName;
+
+
+static void vboxmslnk_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ if (fmt[strlen(fmt) - 1] != '\n')
+ (void) fprintf(stderr, " The error reported was: %s\n", strerror(errno));
+ va_end(ap);
+
+ exit(EXIT_FAILURE);
+}
+
+static void vboxmslnk_start(bool fNoLogo)
+{
+ /* Open our pointer integration driver (vboxms). */
+ int hVBoxMS = open("/dev/vboxms", O_RDWR);
+ if (hVBoxMS < 0)
+ vboxmslnk_fatal("Failed to open /dev/vboxms - please make sure that the node exists and that\n"
+ "you have permission to open it.");
+
+ /* Open the Solaris virtual mouse driver (consms). */
+ int hConsMS = open("/dev/mouse", O_RDWR);
+ if (hConsMS < 0)
+ vboxmslnk_fatal("Failed to open /dev/mouse - please make sure that the node exists and that\n"
+ "you have permission to open it.");
+
+ /* Link vboxms to consms from below. What this means is that vboxms is
+ * added to the list of input sources multiplexed by consms, and vboxms
+ * will receive any control messages (such as information about guest
+ * resolution changes) sent to consms. The link can only be broken
+ * explicitly using the connection ID returned from the IOCtl. */
+ int idConnection = ioctl(hConsMS, I_PLINK, hVBoxMS);
+ if (idConnection < 0)
+ vboxmslnk_fatal("Failed to add /dev/vboxms (the pointer integration driver) to /dev/mouse\n"
+ "(the Solaris virtual master mouse).");
+
+ (void) close(hVBoxMS);
+ (void) close(hConsMS);
+
+ if (!fNoLogo)
+ (void) printf("Successfully enabled pointer integration. Connection ID number to the\n"
+ "Solaris virtual master mouse is:\n");
+ (void) printf("%d\n", idConnection);
+
+ /* Save the connection ID (aka mux ID) so it can be retrieved later. */
+ FILE *fp = fopen(VBOXMSLNK_MUXID_FILE, "w");
+ if (fp == NULL)
+ vboxmslnk_fatal("Failed to open %s for writing the connection ID.", VBOXMSLNK_MUXID_FILE);
+ int rc = fprintf(fp, "%d\n", idConnection);
+ if (rc <= 0)
+ vboxmslnk_fatal("Failed to write the connection ID to %s.", VBOXMSLNK_MUXID_FILE);
+ (void) fclose(fp);
+}
+
+static void vboxmslnk_stop()
+{
+ /* Open the Solaris virtual mouse driver (consms). */
+ int hConsMS = open("/dev/mouse", O_RDWR);
+ if (hConsMS < 0)
+ vboxmslnk_fatal("Failed to open /dev/mouse - please make sure that the node exists and that\n"
+ "you have permission to open it.");
+
+ /* Open the vboxmslnk.muxid file and retrieve the saved mux ID. */
+ FILE *fp = fopen(VBOXMSLNK_MUXID_FILE, "r");
+ if (fp == NULL)
+ vboxmslnk_fatal("Failed to open %s for reading the connection ID.", VBOXMSLNK_MUXID_FILE);
+ int idConnection;
+ int rc = fscanf(fp, "%d\n", &idConnection);
+ if (rc <= 0)
+ vboxmslnk_fatal("Failed to read the connection ID from %s.", VBOXMSLNK_MUXID_FILE);
+ (void) fclose(fp);
+ (void) unlink(VBOXMSLNK_MUXID_FILE);
+
+ /* Unlink vboxms from consms so that vboxms is able to be unloaded. */
+ rc = ioctl(hConsMS, I_PUNLINK, idConnection);
+ if (rc < 0)
+ vboxmslnk_fatal("Failed to disconnect /dev/vboxms (the pointer integration driver) from\n"
+ "/dev/mouse (the Solaris virtual master mouse).");
+ (void) close(hConsMS);
+}
+
+static void vboxmslnk_usage()
+{
+ (void) printf("Usage:\n"
+ " %s [--nologo] <--start | --stop>\n"
+ " %s [-V|--version]\n\n"
+ " -V|--version print the tool version.\n"
+ " --nologo do not display the logo text and only output the connection\n"
+ " ID number needed to disable pointer integration\n"
+ " again.\n"
+ " --start Connect the VirtualBox pointer integration kernel module\n"
+ " to the Solaris mouse driver kernel module.\n"
+ " --stop Disconnect the VirtualBox pointer integration kernel module\n"
+ " from the Solaris mouse driver kernel module.\n"
+ " -h|--help display this help text.\n",
+ g_pszProgName, g_pszProgName);
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ bool fShowVersion = false, fNoLogo = false, fStart = false, fStop = false;
+ int c;
+
+ g_pszProgName = basename(argv[0]);
+
+ static const struct option vboxmslnk_lopts[] = {
+ {"version", no_argument, 0, 'V' },
+ {"nologo", no_argument, 0, 'n' },
+ {"start", no_argument, 0, 's' },
+ {"stop", no_argument, 0, 't' },
+ {"help", no_argument, 0, 'h' },
+ { 0, 0, 0, 0}
+ };
+
+ while ((c = getopt_long(argc, argv, "Vh", vboxmslnk_lopts, NULL)) != -1)
+ {
+ switch (c)
+ {
+ case 'V':
+ fShowVersion = true;
+ break;
+ case 'n':
+ fNoLogo = true;
+ break;
+ case 's':
+ fStart = true;
+ break;
+ case 't':
+ fStop = true;
+ break;
+ case 'h':
+ default:
+ vboxmslnk_usage();
+ }
+ }
+
+ if ( (!fStart && !fStop && !fShowVersion)
+ || (fStart && fStop)
+ || (fShowVersion && (fNoLogo || fStart || fStop)))
+ vboxmslnk_usage();
+
+ if (fShowVersion)
+ {
+ (void) printf("%sr%u\n", VBOX_VERSION_STRING, RTBldCfgRevision());
+ exit(EXIT_SUCCESS);
+ }
+
+ if (!fNoLogo)
+ (void) printf(VBOX_PRODUCT
+ " Guest Additions utility for enabling Solaris pointer\nintegration Version "
+ VBOX_VERSION_STRING "\n"
+ "Copyright (C) " VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
+
+ if (fStart)
+ vboxmslnk_start(fNoLogo);
+
+ if (fStop)
+ vboxmslnk_stop();
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/src/VBox/Additions/solaris/Mouse/vboxmslnk.xml b/src/VBox/Additions/solaris/Mouse/vboxmslnk.xml
new file mode 100644
index 00000000..59662494
--- /dev/null
+++ b/src/VBox/Additions/solaris/Mouse/vboxmslnk.xml
@@ -0,0 +1,92 @@
+<?xml version='1.0'?>
+<!--
+#
+# Solaris SMF service manifest for VBoxService (timesync).
+#
+-->
+<!--
+ Copyright (C) 2012-2023 Oracle and/or its affiliates.
+
+ This file is part of VirtualBox base platform packages, as
+ available from https://www.virtualbox.org.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, in version 3 of the
+ License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses>.
+
+ The contents of this file may alternatively be used under the terms
+ of the Common Development and Distribution License Version 1.0
+ (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ in the VirtualBox 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.
+
+ SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+-->
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+
+<service_bundle type='manifest' name='SUNWvboxguest:vboxmslnk'>
+
+<service
+ name='application/virtualbox/vboxmslnk'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='true' />
+
+ <single_instance/>
+
+ <!-- Wait for devices to be initialized as we depend on vboxms (pseudo) -->
+ <dependency
+ name='milestone'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/milestone/devices:default' />
+ </dependency>
+
+ <!-- Wait for local filesystems to be mounted (just to be safe, don't start too early) -->
+ <dependency
+ name='filesystem-local'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/filesystem/local:default' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/usr/sbin/vboxmslnk --start'
+ timeout_seconds='30' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec='/usr/sbin/vboxmslnk --stop'
+ timeout_seconds='60' />
+
+ <property_group name='startd' type='framework'>
+ <propval name='duration' type='astring' value='transient' />
+ </property_group>
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>VirtualBox Mouse module link.</loctext>
+ </common_name>
+ </template>
+</service>
+
+</service_bundle>
+