summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/common/VBoxGuest/VBoxGuest-netbsd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/common/VBoxGuest/VBoxGuest-netbsd.c')
-rw-r--r--src/VBox/Additions/common/VBoxGuest/VBoxGuest-netbsd.c1094
1 files changed, 1094 insertions, 0 deletions
diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-netbsd.c b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-netbsd.c
new file mode 100644
index 00000000..cd60e4aa
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-netbsd.c
@@ -0,0 +1,1094 @@
+/* $Id: VBoxGuest-netbsd.c $ */
+/** @file
+ * VirtualBox Guest Additions Driver for NetBSD.
+ */
+
+/*
+ * Copyright (C) 2007-2022 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/select.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/kmem.h>
+#include <sys/module.h>
+#include <sys/device.h>
+#include <sys/bus.h>
+#include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/kauth.h>
+#include <sys/stat.h>
+#include <sys/selinfo.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/malloc.h>
+#include <sys/uio.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/vfs_syscalls.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcidevs.h>
+
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsmousevar.h>
+#include <dev/wscons/tpcalibvar.h>
+
+#ifdef PVM
+# undef PVM
+#endif
+#include "VBoxGuestInternal.h"
+#include <VBox/log.h>
+#include <iprt/err.h>
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+#include <iprt/process.h>
+#include <iprt/mem.h>
+#include <iprt/asm.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The module name. */
+#define DEVICE_NAME "vboxguest"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct VBoxGuestDeviceState
+{
+ device_t sc_dev;
+ pci_chipset_tag_t sc_pc;
+
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ bus_addr_t sc_iobase;
+ bus_size_t sc_iosize;
+
+ bus_space_tag_t sc_memt;
+ bus_space_handle_t sc_memh;
+
+ /** Size of the memory area. */
+ bus_size_t sc_memsize;
+
+ /** IRQ resource handle. */
+ pci_intr_handle_t ih;
+ /** Pointer to the IRQ handler. */
+ void *pfnIrqHandler;
+
+ /** Controller features, limits and status. */
+ u_int vboxguest_state;
+
+ device_t sc_wsmousedev;
+ VMMDevReqMouseStatus *sc_vmmmousereq;
+ PVBOXGUESTSESSION sc_session;
+ struct tpcalib_softc sc_tpcalib;
+} vboxguest_softc;
+
+
+struct vboxguest_fdata
+{
+ vboxguest_softc *sc;
+ PVBOXGUESTSESSION session;
+};
+
+#define VBOXGUEST_STATE_INITOK 1 << 0
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+/*
+ * Driver(9) autoconf machinery.
+ */
+static int VBoxGuestNetBSDMatch(device_t parent, cfdata_t match, void *aux);
+static void VBoxGuestNetBSDAttach(device_t parent, device_t self, void *aux);
+static void VBoxGuestNetBSDWsmAttach(vboxguest_softc *sc);
+static int VBoxGuestNetBSDDetach(device_t self, int flags);
+
+/*
+ * IRQ related functions.
+ */
+static int VBoxGuestNetBSDAddIRQ(vboxguest_softc *sc, struct pci_attach_args *pa);
+static void VBoxGuestNetBSDRemoveIRQ(vboxguest_softc *sc);
+static int VBoxGuestNetBSDISR(void *pvState);
+
+/*
+ * Character device file handlers.
+ */
+static int VBoxGuestNetBSDOpen(dev_t device, int flags, int fmt, struct lwp *process);
+static int VBoxGuestNetBSDClose(struct file *fp);
+static int VBoxGuestNetBSDIOCtl(struct file *fp, u_long cmd, void *addr);
+static int VBoxGuestNetBSDIOCtlSlow(struct vboxguest_fdata *fdata, u_long command, void *data);
+static int VBoxGuestNetBSDPoll(struct file *fp, int events);
+
+/*
+ * wsmouse(4) accessops
+ */
+static int VBoxGuestNetBSDWsmEnable(void *cookie);
+static void VBoxGuestNetBSDWsmDisable(void *cookie);
+static int VBoxGuestNetBSDWsmIOCtl(void *cookie, u_long cmd, void *data, int flag, struct lwp *l);
+
+static int VBoxGuestNetBSDSetMouseStatus(vboxguest_softc *sc, uint32_t fStatus);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern struct cfdriver vboxguest_cd; /* CFDRIVER_DECL */
+extern struct cfattach vboxguest_ca; /* CFATTACH_DECL */
+
+/*
+ * The /dev/vboxguest character device entry points.
+ */
+static struct cdevsw g_VBoxGuestNetBSDChrDevSW =
+{
+ .d_open = VBoxGuestNetBSDOpen,
+ .d_close = noclose,
+ .d_read = noread,
+ .d_write = nowrite,
+ .d_ioctl = noioctl,
+ .d_stop = nostop,
+ .d_tty = notty,
+ .d_poll = nopoll,
+ .d_mmap = nommap,
+ .d_kqfilter = nokqfilter,
+};
+
+static const struct fileops vboxguest_fileops = {
+ .fo_read = fbadop_read,
+ .fo_write = fbadop_write,
+ .fo_ioctl = VBoxGuestNetBSDIOCtl,
+ .fo_fcntl = fnullop_fcntl,
+ .fo_poll = VBoxGuestNetBSDPoll,
+ .fo_stat = fbadop_stat,
+ .fo_close = VBoxGuestNetBSDClose,
+ .fo_kqfilter = fnullop_kqfilter,
+ .fo_restart = fnullop_restart
+};
+
+
+const struct wsmouse_accessops vboxguest_wsm_accessops = {
+ VBoxGuestNetBSDWsmEnable,
+ VBoxGuestNetBSDWsmIOCtl,
+ VBoxGuestNetBSDWsmDisable,
+};
+
+
+/*
+ * XXX: wsmux(4) doesn't properly handle the case when two mice with
+ * absolute position events but different calibration data are being
+ * multiplexed. Without GAs the absolute events will be reported
+ * through the tablet ums(4) device with the range of 32k, but with
+ * GAs the absolute events will be reported through the VMM device
+ * (wsmouse at vboxguest) and VMM uses the range of 64k. Which one
+ * responds to the calibration ioctl depends on the order of
+ * attachment. On boot kernel attaches ums first and GAs later, so
+ * it's VMM (this driver) that gets the ioctl. After save/restore the
+ * ums will be detached and re-attached and after that it's ums that
+ * will get the ioctl, but the events (with a wider range) will still
+ * come via the VMM, confusing X, wsmoused, etc. Hack around that by
+ * forcing the range here to match the tablet's range.
+ *
+ * We force VMM range into the ums range and rely on the fact that no
+ * actual calibration is done and both devices are used in the raw
+ * mode. See tpcalib_trans call below.
+ *
+ * Cf. src/VBox/Devices/Input/UsbMouse.cpp
+ */
+#define USB_TABLET_RANGE_MIN 0
+#define USB_TABLET_RANGE_MAX 0x7fff
+
+static struct wsmouse_calibcoords vboxguest_wsm_default_calib = {
+ .minx = USB_TABLET_RANGE_MIN, // VMMDEV_MOUSE_RANGE_MIN,
+ .miny = USB_TABLET_RANGE_MIN, // VMMDEV_MOUSE_RANGE_MIN,
+ .maxx = USB_TABLET_RANGE_MAX, // VMMDEV_MOUSE_RANGE_MAX,
+ .maxy = USB_TABLET_RANGE_MAX, // VMMDEV_MOUSE_RANGE_MAX,
+ .samplelen = WSMOUSE_CALIBCOORDS_RESET,
+};
+
+/** Device extention & session data association structure. */
+static VBOXGUESTDEVEXT g_DevExt;
+
+static vboxguest_softc *g_SC;
+
+/** Reference counter */
+static volatile uint32_t cUsers;
+/** selinfo structure used for polling. */
+static struct selinfo g_SelInfo;
+
+
+CFATTACH_DECL_NEW(vboxguest, sizeof(vboxguest_softc),
+ VBoxGuestNetBSDMatch, VBoxGuestNetBSDAttach, VBoxGuestNetBSDDetach, NULL);
+
+
+static int VBoxGuestNetBSDMatch(device_t parent, cfdata_t match, void *aux)
+{
+ const struct pci_attach_args *pa = aux;
+
+ if (RT_UNLIKELY(g_SC != NULL)) /* should not happen */
+ return 0;
+
+ if ( PCI_VENDOR(pa->pa_id) == VMMDEV_VENDORID
+ && PCI_PRODUCT(pa->pa_id) == VMMDEV_DEVICEID)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void VBoxGuestNetBSDAttach(device_t parent, device_t self, void *aux)
+{
+ int rc = VINF_SUCCESS;
+ int iResId = 0;
+ vboxguest_softc *sc;
+ struct pci_attach_args *pa = aux;
+ bus_space_tag_t iot, memt;
+ bus_space_handle_t ioh, memh;
+ bus_dma_segment_t seg;
+ int ioh_valid, memh_valid;
+
+ KASSERT(g_SC == NULL);
+
+ cUsers = 0;
+
+ aprint_normal(": VirtualBox Guest\n");
+
+ sc = device_private(self);
+ sc->sc_dev = self;
+
+ /*
+ * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
+ */
+ rc = RTR0Init(0);
+ if (RT_FAILURE(rc))
+ {
+ LogFunc(("RTR0Init failed.\n"));
+ aprint_error_dev(sc->sc_dev, "RTR0Init failed\n");
+ return;
+ }
+
+ sc->sc_pc = pa->pa_pc;
+
+ /*
+ * Allocate I/O port resource.
+ */
+ ioh_valid = (pci_mapreg_map(pa, PCI_BAR0,
+ PCI_MAPREG_TYPE_IO, 0,
+ &sc->sc_iot, &sc->sc_ioh,
+ &sc->sc_iobase, &sc->sc_iosize) == 0);
+
+ if (ioh_valid)
+ {
+
+ /*
+ * Map the MMIO region.
+ */
+ memh_valid = (pci_mapreg_map(pa, PCI_BAR1,
+ PCI_MAPREG_TYPE_MEM, BUS_SPACE_MAP_LINEAR,
+ &sc->sc_memt, &sc->sc_memh,
+ NULL, &sc->sc_memsize) == 0);
+ if (memh_valid)
+ {
+ /*
+ * Call the common device extension initializer.
+ */
+ rc = VGDrvCommonInitDevExt(&g_DevExt, sc->sc_iobase,
+ bus_space_vaddr(sc->sc_memt, sc->sc_memh),
+ sc->sc_memsize,
+#if ARCH_BITS == 64
+ VBOXOSTYPE_NetBSD_x64,
+#else
+ VBOXOSTYPE_NetBSD,
+#endif
+ VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Add IRQ of VMMDev.
+ */
+ rc = VBoxGuestNetBSDAddIRQ(sc, pa);
+ if (RT_SUCCESS(rc))
+ {
+ sc->vboxguest_state |= VBOXGUEST_STATE_INITOK;
+
+ /*
+ * Read host configuration.
+ */
+ VGDrvCommonProcessOptionsFromHost(&g_DevExt);
+
+ /*
+ * Attach wsmouse.
+ */
+ VBoxGuestNetBSDWsmAttach(sc);
+
+ g_SC = sc;
+ return;
+ }
+ VGDrvCommonDeleteDevExt(&g_DevExt);
+ }
+ else
+ {
+ aprint_error_dev(sc->sc_dev, "init failed\n");
+ }
+ bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_memsize);
+ }
+ else
+ {
+ aprint_error_dev(sc->sc_dev, "MMIO mapping failed\n");
+ }
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
+ }
+ else
+ {
+ aprint_error_dev(sc->sc_dev, "IO mapping failed\n");
+ }
+
+ RTR0Term();
+ return;
+}
+
+
+/**
+ * Sets IRQ for VMMDev.
+ *
+ * @returns NetBSD error code.
+ * @param sc Pointer to the state info structure.
+ * @param pa Pointer to the PCI attach arguments.
+ */
+static int VBoxGuestNetBSDAddIRQ(vboxguest_softc *sc, struct pci_attach_args *pa)
+{
+ int iResId = 0;
+ int rc = 0;
+ const char *intrstr;
+#if __NetBSD_Prereq__(6, 99, 39)
+ char intstrbuf[100];
+#endif
+
+ LogFlow((DEVICE_NAME ": %s\n", __func__));
+
+ if (pci_intr_map(pa, &sc->ih))
+ {
+ aprint_error_dev(sc->sc_dev, "couldn't map interrupt.\n");
+ return VERR_DEV_IO_ERROR;
+ }
+
+ intrstr = pci_intr_string(sc->sc_pc, sc->ih
+#if __NetBSD_Prereq__(6, 99, 39)
+ , intstrbuf, sizeof(intstrbuf)
+#endif
+ );
+ aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr);
+
+ sc->pfnIrqHandler = pci_intr_establish(sc->sc_pc, sc->ih, IPL_BIO, VBoxGuestNetBSDISR, sc);
+ if (sc->pfnIrqHandler == NULL)
+ {
+ aprint_error_dev(sc->sc_dev, "couldn't establish interrupt\n");
+ return VERR_DEV_IO_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Optionally attach wsmouse(4) device as a child.
+ */
+static void VBoxGuestNetBSDWsmAttach(vboxguest_softc *sc)
+{
+ struct wsmousedev_attach_args am = { &vboxguest_wsm_accessops, sc };
+
+ PVBOXGUESTSESSION session = NULL;
+ VMMDevReqMouseStatus *req = NULL;
+ int rc;
+
+ rc = VGDrvCommonCreateKernelSession(&g_DevExt, &session);
+ if (RT_FAILURE(rc))
+ goto fail;
+
+ rc = VbglR0GRAlloc((VMMDevRequestHeader **)&req, sizeof(*req),
+ VMMDevReq_GetMouseStatus);
+ if (RT_FAILURE(rc))
+ goto fail;
+
+#if __NetBSD_Prereq__(9,99,88)
+ sc->sc_wsmousedev = config_found(sc->sc_dev, &am, wsmousedevprint,
+ CFARGS(.iattr = "wsmousedev"));
+#elif __NetBSD_Prereq__(9,99,82)
+ sc->sc_wsmousedev = config_found(sc->sc_dev, &am, wsmousedevprint,
+ CFARG_IATTR, "wsmousedev",
+ CFARG_EOL);
+#else
+ sc->sc_wsmousedev = config_found_ia(sc->sc_dev, "wsmousedev",
+ &am, wsmousedevprint);
+#endif
+
+ if (sc->sc_wsmousedev == NULL)
+ goto fail;
+
+ sc->sc_session = session;
+ sc->sc_vmmmousereq = req;
+
+ tpcalib_init(&sc->sc_tpcalib);
+ tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
+ &vboxguest_wsm_default_calib, 0, 0);
+ return;
+
+ fail:
+ if (session != NULL)
+ VGDrvCommonCloseSession(&g_DevExt, session);
+ if (req != NULL)
+ VbglR0GRFree((VMMDevRequestHeader *)req);
+}
+
+
+static int VBoxGuestNetBSDDetach(device_t self, int flags)
+{
+ vboxguest_softc *sc;
+ sc = device_private(self);
+
+ LogFlow((DEVICE_NAME ": %s\n", __func__));
+
+ if (cUsers > 0)
+ return EBUSY;
+
+ if ((sc->vboxguest_state & VBOXGUEST_STATE_INITOK) == 0)
+ return 0;
+
+ /*
+ * Reverse what we did in VBoxGuestNetBSDAttach.
+ */
+ if (sc->sc_vmmmousereq != NULL)
+ VbglR0GRFree((VMMDevRequestHeader *)sc->sc_vmmmousereq);
+
+ VBoxGuestNetBSDRemoveIRQ(sc);
+
+ VGDrvCommonDeleteDevExt(&g_DevExt);
+
+ bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_memsize);
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
+
+ RTR0Term();
+
+ return config_detach_children(self, flags);
+}
+
+
+/**
+ * Removes IRQ for VMMDev.
+ *
+ * @param sc Opaque pointer to the state info structure.
+ */
+static void VBoxGuestNetBSDRemoveIRQ(vboxguest_softc *sc)
+{
+ LogFlow((DEVICE_NAME ": %s\n", __func__));
+
+ if (sc->pfnIrqHandler)
+ {
+ pci_intr_disestablish(sc->sc_pc, sc->pfnIrqHandler);
+ }
+}
+
+
+/**
+ * Interrupt service routine.
+ *
+ * @returns Whether the interrupt was from VMMDev.
+ * @param pvState Opaque pointer to the device state.
+ */
+static int VBoxGuestNetBSDISR(void *pvState)
+{
+ LogFlow((DEVICE_NAME ": %s: pvState=%p\n", __func__, pvState));
+
+ bool fOurIRQ = VGDrvCommonISR(&g_DevExt);
+
+ return fOurIRQ ? 1 : 0;
+}
+
+
+/*
+ * Called by VGDrvCommonISR() if mouse position changed
+ */
+void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
+{
+ vboxguest_softc *sc = g_SC;
+
+ LogFlow((DEVICE_NAME ": %s\n", __func__));
+
+ /*
+ * Wake up poll waiters.
+ */
+ selnotify(&g_SelInfo, 0, 0);
+
+ if (sc->sc_vmmmousereq != NULL) {
+ int x, y;
+ int rc;
+
+ sc->sc_vmmmousereq->mouseFeatures = 0;
+ sc->sc_vmmmousereq->pointerXPos = 0;
+ sc->sc_vmmmousereq->pointerYPos = 0;
+
+ rc = VbglR0GRPerform(&sc->sc_vmmmousereq->header);
+ if (RT_FAILURE(rc))
+ return;
+
+ /* XXX: see the comment for vboxguest_wsm_default_calib */
+ int rawx = (unsigned)sc->sc_vmmmousereq->pointerXPos >> 1;
+ int rawy = (unsigned)sc->sc_vmmmousereq->pointerYPos >> 1;
+ tpcalib_trans(&sc->sc_tpcalib, rawx, rawy, &x, &y);
+
+ wsmouse_input(sc->sc_wsmousedev,
+ 0, /* buttons */
+ x, y,
+ 0, 0, /* z, w */
+ WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
+ }
+}
+
+
+bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
+{
+ RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
+ return false;
+}
+
+
+static int VBoxGuestNetBSDSetMouseStatus(vboxguest_softc *sc, uint32_t fStatus)
+{
+ VBGLIOCSETMOUSESTATUS Req;
+ int rc;
+
+ VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS);
+ Req.u.In.fStatus = fStatus;
+ rc = VGDrvCommonIoCtl(VBGL_IOCTL_SET_MOUSE_STATUS,
+ &g_DevExt,
+ sc->sc_session,
+ &Req.Hdr, sizeof(Req));
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+
+ return rc;
+}
+
+
+static int
+VBoxGuestNetBSDWsmEnable(void *cookie)
+{
+ vboxguest_softc *sc = cookie;
+ int rc;
+
+ rc = VBoxGuestNetBSDSetMouseStatus(sc, VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
+ | VMMDEV_MOUSE_NEW_PROTOCOL);
+ if (RT_FAILURE(rc))
+ return RTErrConvertToErrno(rc);
+
+ return 0;
+}
+
+
+static void
+VBoxGuestNetBSDWsmDisable(void *cookie)
+{
+ vboxguest_softc *sc = cookie;
+ VBoxGuestNetBSDSetMouseStatus(sc, 0);
+}
+
+
+static int
+VBoxGuestNetBSDWsmIOCtl(void *cookie, u_long cmd, void *data, int flag, struct lwp *l)
+{
+ vboxguest_softc *sc = cookie;
+
+ switch (cmd) {
+ case WSMOUSEIO_GTYPE:
+ *(u_int *)data = WSMOUSE_TYPE_TPANEL;
+ break;
+
+ case WSMOUSEIO_SCALIBCOORDS:
+ case WSMOUSEIO_GCALIBCOORDS:
+ return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, l);
+
+ default:
+ return EPASSTHROUGH;
+ }
+ return 0;
+}
+
+
+/**
+ * File open handler
+ *
+ */
+static int VBoxGuestNetBSDOpen(dev_t device, int flags, int fmt, struct lwp *pLwp)
+{
+ vboxguest_softc *sc;
+ struct vboxguest_fdata *fdata;
+ file_t *fp;
+ int fd, error;
+
+ LogFlow((DEVICE_NAME ": %s\n", __func__));
+
+ if ((sc = device_lookup_private(&vboxguest_cd, minor(device))) == NULL)
+ {
+ printf("device_lookup_private failed\n");
+ return (ENXIO);
+ }
+
+ if ((sc->vboxguest_state & VBOXGUEST_STATE_INITOK) == 0)
+ {
+ aprint_error_dev(sc->sc_dev, "device not configured\n");
+ return (ENXIO);
+ }
+
+ fdata = kmem_alloc(sizeof(*fdata), KM_SLEEP);
+ if (fdata != NULL)
+ {
+ fdata->sc = sc;
+
+ error = fd_allocfile(&fp, &fd);
+ if (error == 0)
+ {
+ /*
+ * Create a new session.
+ */
+ struct kauth_cred *pCred = pLwp->l_cred;
+ int fHaveCred = (pCred != NULL && pCred != NOCRED && pCred != FSCRED);
+ uint32_t fRequestor;
+ int fIsWheel;
+ int rc;
+
+ fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN;
+
+ /* uid */
+ if (fHaveCred && kauth_cred_geteuid(pCred) == (uid_t)0)
+ fRequestor |= VMMDEV_REQUESTOR_USR_ROOT;
+ else
+ fRequestor |= VMMDEV_REQUESTOR_USR_USER;
+
+ /* gid */
+ if (fHaveCred
+ && (kauth_cred_getegid(pCred) == (gid_t)0
+ || (kauth_cred_ismember_gid(pCred, 0, &fIsWheel) == 0
+ && fIsWheel)))
+ fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL;
+
+#if 0 /** @todo implement /dev/vboxuser */
+ if (!fUnrestricted)
+ fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE;
+#else
+ fRequestor |= VMMDEV_REQUESTOR_NO_USER_DEVICE;
+#endif
+
+ /** @todo can we find out if pLwp is on the console? */
+ fRequestor |= VMMDEV_REQUESTOR_CON_DONT_KNOW;
+
+ rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &fdata->session);
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicIncU32(&cUsers);
+ return fd_clone(fp, fd, flags, &vboxguest_fileops, fdata);
+ }
+
+ aprint_error_dev(sc->sc_dev, "VBox session creation failed\n");
+ closef(fp); /* ??? */
+ error = RTErrConvertToErrno(rc);
+ }
+ kmem_free(fdata, sizeof(*fdata));
+ }
+ else
+ error = ENOMEM;
+ return error;
+}
+
+/**
+ * File close handler
+ *
+ */
+static int VBoxGuestNetBSDClose(struct file *fp)
+{
+ struct vboxguest_fdata *fdata = fp->f_data;
+ vboxguest_softc *sc = fdata->sc;
+
+ LogFlow((DEVICE_NAME ": %s\n", __func__));
+
+ VGDrvCommonCloseSession(&g_DevExt, fdata->session);
+ ASMAtomicDecU32(&cUsers);
+
+ kmem_free(fdata, sizeof(*fdata));
+
+ return 0;
+}
+
+/**
+ * IOCTL handler
+ *
+ */
+static int VBoxGuestNetBSDIOCtl(struct file *fp, u_long command, void *data)
+{
+ struct vboxguest_fdata *fdata = fp->f_data;
+
+ if (VBGL_IOCTL_IS_FAST(command))
+ return VGDrvCommonIoCtlFast(command, &g_DevExt, fdata->session);
+
+ return VBoxGuestNetBSDIOCtlSlow(fdata, command, data);
+}
+
+static int VBoxGuestNetBSDIOCtlSlow(struct vboxguest_fdata *fdata, u_long command, void *data)
+{
+ vboxguest_softc *sc = fdata->sc;
+ size_t cbReq = IOCPARM_LEN(command);
+ PVBGLREQHDR pHdr = NULL;
+ void *pvUser = NULL;
+ int err, rc;
+
+ LogFlow(("%s: command=%#lx data=%p\n", __func__, command, data));
+
+ /*
+ * Buffered request?
+ */
+ if ((command & IOC_DIRMASK) == IOC_INOUT)
+ {
+ /* will be validated by VGDrvCommonIoCtl() */
+ pHdr = (PVBGLREQHDR)data;
+ }
+
+ /*
+ * Big unbuffered request? "data" is the userland pointer.
+ */
+ else if ((command & IOC_DIRMASK) == IOC_VOID && cbReq != 0)
+ {
+ /*
+ * Read the header, validate it and figure out how much that
+ * needs to be buffered.
+ */
+ VBGLREQHDR Hdr;
+
+ if (RT_UNLIKELY(cbReq < sizeof(Hdr)))
+ return ENOTTY;
+
+ pvUser = data;
+ err = copyin(pvUser, &Hdr, sizeof(Hdr));
+ if (RT_UNLIKELY(err != 0))
+ return err;
+
+ if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION))
+ return ENOTTY;
+
+ if (cbReq > 16 * _1M)
+ return EINVAL;
+
+ if (Hdr.cbOut == 0)
+ Hdr.cbOut = Hdr.cbIn;
+
+ if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr) || Hdr.cbIn > cbReq
+ || Hdr.cbOut < sizeof(Hdr) || Hdr.cbOut > cbReq))
+ return EINVAL;
+
+ /*
+ * Allocate buffer and copy in the data.
+ */
+ cbReq = RT_MAX(Hdr.cbIn, Hdr.cbOut);
+
+ pHdr = (PVBGLREQHDR)RTMemTmpAlloc(cbReq);
+ if (RT_UNLIKELY(pHdr == NULL))
+ {
+ LogRel(("%s: command=%#lx data=%p: unable to allocate %zu bytes\n",
+ __func__, command, data, cbReq));
+ return ENOMEM;
+ }
+
+ err = copyin(pvUser, pHdr, Hdr.cbIn);
+ if (err != 0)
+ {
+ RTMemTmpFree(pHdr);
+ return err;
+ }
+
+ if (Hdr.cbIn < cbReq)
+ memset((uint8_t *)pHdr + Hdr.cbIn, '\0', cbReq - Hdr.cbIn);
+ }
+
+ /*
+ * Process the IOCtl.
+ */
+ rc = VGDrvCommonIoCtl(command, &g_DevExt, fdata->session, pHdr, cbReq);
+ if (RT_SUCCESS(rc))
+ {
+ err = 0;
+
+ /*
+ * If unbuffered, copy back the result before returning.
+ */
+ if (pvUser != NULL)
+ {
+ size_t cbOut = pHdr->cbOut;
+ if (cbOut > cbReq)
+ {
+ LogRel(("%s: command=%#lx data=%p: too much output: %zu > %zu\n",
+ __func__, command, data, cbOut, cbReq));
+ cbOut = cbReq;
+ }
+
+ err = copyout(pHdr, pvUser, cbOut);
+ RTMemTmpFree(pHdr);
+ }
+ }
+ else
+ {
+ LogRel(("%s: command=%#lx data=%p: error %Rrc\n",
+ __func__, command, data, rc));
+
+ if (pvUser != NULL)
+ RTMemTmpFree(pHdr);
+
+ err = RTErrConvertToErrno(rc);
+ }
+
+ return err;
+}
+
+static int VBoxGuestNetBSDPoll(struct file *fp, int events)
+{
+ struct vboxguest_fdata *fdata = fp->f_data;
+ vboxguest_softc *sc = fdata->sc;
+
+ int rc = 0;
+ int events_processed;
+
+ uint32_t u32CurSeq;
+
+ LogFlow((DEVICE_NAME ": %s\n", __func__));
+
+ u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
+ if (fdata->session->u32MousePosChangedSeq != u32CurSeq)
+ {
+ events_processed = events & (POLLIN | POLLRDNORM);
+ fdata->session->u32MousePosChangedSeq = u32CurSeq;
+ }
+ else
+ {
+ events_processed = 0;
+
+ selrecord(curlwp, &g_SelInfo);
+ }
+
+ return events_processed;
+}
+
+
+/**
+ * @note This code is duplicated on other platforms with variations, so please
+ * keep them all up to date when making changes!
+ */
+int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
+{
+ /*
+ * Simple request validation (common code does the rest).
+ */
+ int rc;
+ if ( RT_VALID_PTR(pReqHdr)
+ && cbReq >= sizeof(*pReqHdr))
+ {
+ /*
+ * All requests except the connect one requires a valid session.
+ */
+ PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
+ if (pSession)
+ {
+ if ( RT_VALID_PTR(pSession)
+ && pSession->pDevExt == &g_DevExt)
+ rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
+ else
+ rc = VERR_INVALID_HANDLE;
+ }
+ else if (uReq == VBGL_IOCTL_IDC_CONNECT)
+ {
+ rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
+ if (RT_FAILURE(rc))
+ VGDrvCommonCloseSession(&g_DevExt, pSession);
+ }
+ }
+ else
+ rc = VERR_INVALID_HANDLE;
+ }
+ else
+ rc = VERR_INVALID_POINTER;
+ return rc;
+}
+
+
+MODULE(MODULE_CLASS_DRIVER, vboxguest, "pci");
+
+/*
+ * XXX: See netbsd/vboxguest.ioconf for the details.
+*/
+#if 0
+#include "ioconf.c"
+#else
+
+static const struct cfiattrdata wsmousedevcf_iattrdata = {
+ "wsmousedev", 1, {
+ { "mux", "0", 0 },
+ }
+};
+
+/* device vboxguest: wsmousedev */
+static const struct cfiattrdata * const vboxguest_attrs[] = { &wsmousedevcf_iattrdata, NULL };
+CFDRIVER_DECL(vboxguest, DV_DULL, vboxguest_attrs);
+
+static struct cfdriver * const cfdriver_ioconf_vboxguest[] = {
+ &vboxguest_cd, NULL
+};
+
+
+static const struct cfparent vboxguest_pspec = {
+ "pci", "pci", DVUNIT_ANY
+};
+static int vboxguest_loc[] = { -1, -1 };
+
+
+static const struct cfparent wsmousedev_pspec = {
+ "wsmousedev", "vboxguest", DVUNIT_ANY
+};
+static int wsmousedev_loc[] = { 0 };
+
+
+static struct cfdata cfdata_ioconf_vboxguest[] = {
+ /* vboxguest0 at pci? dev ? function ? */
+ {
+ .cf_name = "vboxguest",
+ .cf_atname = "vboxguest",
+ .cf_unit = 0, /* Only unit 0 is ever used */
+ .cf_fstate = FSTATE_NOTFOUND,
+ .cf_loc = vboxguest_loc,
+ .cf_flags = 0,
+ .cf_pspec = &vboxguest_pspec,
+ },
+
+ /* wsmouse* at vboxguest? */
+ { "wsmouse", "wsmouse", 0, FSTATE_STAR, wsmousedev_loc, 0, &wsmousedev_pspec },
+
+ { NULL, NULL, 0, 0, NULL, 0, NULL }
+};
+
+static struct cfattach * const vboxguest_cfattachinit[] = {
+ &vboxguest_ca, NULL
+};
+
+static const struct cfattachinit cfattach_ioconf_vboxguest[] = {
+ { "vboxguest", vboxguest_cfattachinit },
+ { NULL, NULL }
+};
+#endif
+
+
+static int
+vboxguest_modcmd(modcmd_t cmd, void *opaque)
+{
+ devmajor_t bmajor, cmajor;
+#if !__NetBSD_Prereq__(8,99,46)
+ register_t retval;
+#endif
+ int error;
+
+ LogFlow((DEVICE_NAME ": %s\n", __func__));
+
+ switch (cmd)
+ {
+ case MODULE_CMD_INIT:
+ error = config_init_component(cfdriver_ioconf_vboxguest,
+ cfattach_ioconf_vboxguest,
+ cfdata_ioconf_vboxguest);
+ if (error)
+ break;
+
+ bmajor = cmajor = NODEVMAJOR;
+ error = devsw_attach("vboxguest",
+ NULL, &bmajor,
+ &g_VBoxGuestNetBSDChrDevSW, &cmajor);
+ if (error)
+ {
+ if (error == EEXIST)
+ error = 0; /* maybe built-in ... improve eventually */
+ else
+ break;
+ }
+
+ error = do_sys_mknod(curlwp, "/dev/vboxguest",
+ 0666|S_IFCHR, makedev(cmajor, 0),
+#if !__NetBSD_Prereq__(8,99,46)
+ &retval,
+#endif
+ UIO_SYSSPACE);
+ if (error == EEXIST) {
+ error = 0;
+
+ /*
+ * Since NetBSD doesn't yet have a major reserved for
+ * vboxguest, the (first free) major we get will
+ * change when new devices are added, so an existing
+ * /dev/vboxguest may now point to some other device,
+ * creating confusion (tripped me up a few times).
+ */
+ aprint_normal("vboxguest: major %d:"
+ " check existing /dev/vboxguest\n", cmajor);
+ }
+ break;
+
+ case MODULE_CMD_FINI:
+ error = config_fini_component(cfdriver_ioconf_vboxguest,
+ cfattach_ioconf_vboxguest,
+ cfdata_ioconf_vboxguest);
+ if (error)
+ break;
+
+ devsw_detach(NULL, &g_VBoxGuestNetBSDChrDevSW);
+ break;
+
+ default:
+ return ENOTTY;
+ }
+ return error;
+}