diff options
Diffstat (limited to 'src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSBMon-solaris.c')
-rw-r--r-- | src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSBMon-solaris.c | 1048 |
1 files changed, 1048 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSBMon-solaris.c b/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSBMon-solaris.c new file mode 100644 index 00000000..3d9b9ab3 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSBMon-solaris.c @@ -0,0 +1,1048 @@ +/* $Id: VBoxUSBMon-solaris.c $ */ +/** @file + * VirtualBox USB Monitor Driver, Solaris Hosts. + */ + +/* + * Copyright (C) 2008-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_USB_DRV +#include "VBoxUSBFilterMgr.h" +#include <VBox/usblib-solaris.h> +#include <VBox/version.h> +#include <VBox/log.h> +#include <VBox/cdefs.h> +#include <VBox/types.h> +#include <VBox/version.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/initterm.h> +#include <iprt/mem.h> +#include <iprt/process.h> +#include <iprt/path.h> +#include <iprt/semaphore.h> +#include <iprt/string.h> + +#define USBDRV_MAJOR_VER 2 +#define USBDRV_MINOR_VER 0 +#include <sys/usb/usba.h> +#include <sys/conf.h> +#include <sys/modctl.h> +#include <sys/mutex.h> +#include <sys/stat.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/open.h> +#include <sys/cmn_err.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The module name. */ +#define DEVICE_NAME "vboxusbmon" +/** The module description as seen in 'modinfo'. */ +#define DEVICE_DESC_DRV "VirtualBox USBMon" + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int VBoxUSBMonSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred); +static int VBoxUSBMonSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred); +static int VBoxUSBMonSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred); +static int VBoxUSBMonSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred); +static int VBoxUSBMonSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal); +static int VBoxUSBMonSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult); +static int VBoxUSBMonSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd); +static int VBoxUSBMonSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd); + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * cb_ops: for drivers that support char/block entry points + */ +static struct cb_ops g_VBoxUSBMonSolarisCbOps = +{ + VBoxUSBMonSolarisOpen, + VBoxUSBMonSolarisClose, + nodev, /* b strategy */ + nodev, /* b dump */ + nodev, /* b print */ + VBoxUSBMonSolarisRead, + VBoxUSBMonSolarisWrite, + VBoxUSBMonSolarisIOCtl, + nodev, /* c devmap */ + nodev, /* c mmap */ + nodev, /* c segmap */ + nochpoll, /* c poll */ + ddi_prop_op, /* property ops */ + NULL, /* streamtab */ + D_NEW | D_MP, /* compat. flag */ + CB_REV /* revision */ +}; + +/** + * dev_ops: for driver device operations + */ +static struct dev_ops g_VBoxUSBMonSolarisDevOps = +{ + DEVO_REV, /* driver build revision */ + 0, /* ref count */ + VBoxUSBMonSolarisGetInfo, + nulldev, /* identify */ + nulldev, /* probe */ + VBoxUSBMonSolarisAttach, + VBoxUSBMonSolarisDetach, + nodev, /* reset */ + &g_VBoxUSBMonSolarisCbOps, + (struct bus_ops *)0, + nodev, /* power */ + ddi_quiesce_not_needed +}; + +/** + * modldrv: export driver specifics to the kernel + */ +static struct modldrv g_VBoxUSBMonSolarisModule = +{ + &mod_driverops, /* extern from kernel */ + DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV), + &g_VBoxUSBMonSolarisDevOps +}; + +/** + * modlinkage: export install/remove/info to the kernel + */ +static struct modlinkage g_VBoxUSBMonSolarisModLinkage = +{ + MODREV_1, + &g_VBoxUSBMonSolarisModule, + NULL, +}; + +/** + * Client driver info. + */ +typedef struct vboxusbmon_client_t +{ + dev_info_t *pDip; /* Client device info. pointer */ + VBOXUSB_CLIENT_INFO Info; /* Client registration data. */ + struct vboxusbmon_client_t *pNext; /* Pointer to next client */ +} vboxusbmon_client_t; + +/** + * Device state info. + */ +typedef struct +{ + RTPROCESS Process; /* The process (id) of the session */ +} vboxusbmon_state_t; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Global Device handle we only support one instance. */ +static dev_info_t *g_pDip = NULL; +/** Global Mutex. */ +static kmutex_t g_VBoxUSBMonSolarisMtx; +/** Global list of client drivers registered with us. */ +vboxusbmon_client_t *g_pVBoxUSBMonSolarisClients = NULL; +/** Opaque pointer to list of soft states. */ +static void *g_pVBoxUSBMonSolarisState; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int vboxUSBMonSolarisProcessIOCtl(int iFunction, void *pvState, void *pvData, size_t cbData, size_t *pcbReturnedData); +static int vboxUSBMonSolarisResetDevice(char *pszDevicePath, bool fReattach); + + +/********************************************************************************************************************************* +* Monitor Global Hooks * +*********************************************************************************************************************************/ +static int vboxUSBMonSolarisClientInfo(vboxusbmon_state_t *pState, PVBOXUSB_CLIENT_INFO pClientInfo); +int VBoxUSBMonSolarisRegisterClient(dev_info_t *pClientDip, PVBOXUSB_CLIENT_INFO pClientInfo); +int VBoxUSBMonSolarisUnregisterClient(dev_info_t *pClientDip); +int VBoxUSBMonSolarisElectDriver(usb_dev_descr_t *pDevDesc, usb_dev_str_t *pDevStrings, char *pszDevicePath, int Bus, int Port, + char **ppszDrv, void *pvReserved); + + +/** + * Kernel entry points + */ +int _init(void) +{ + int rc; + + LogFunc((DEVICE_NAME ": _init\n")); + + g_pDip = NULL; + + /* + * Prevent module autounloading. + */ + modctl_t *pModCtl = mod_getctl(&g_VBoxUSBMonSolarisModLinkage); + if (pModCtl) + pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD; + else + LogRel((DEVICE_NAME ": _init: Failed to disable autounloading!\n")); + + /* + * Initialize IPRT R0 driver, which internally calls OS-specific r0 init. + */ + rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + /* + * Initialize global mutex. + */ + mutex_init(&g_VBoxUSBMonSolarisMtx, NULL, MUTEX_DRIVER, NULL); + rc = VBoxUSBFilterInit(); + if (RT_SUCCESS(rc)) + { + rc = ddi_soft_state_init(&g_pVBoxUSBMonSolarisState, sizeof(vboxusbmon_state_t), 1); + if (!rc) + { + rc = mod_install(&g_VBoxUSBMonSolarisModLinkage); + if (!rc) + return rc; + + LogRel((DEVICE_NAME ": _init: mod_install failed! rc=%d\n", rc)); + ddi_soft_state_fini(&g_pVBoxUSBMonSolarisState); + } + else + LogRel((DEVICE_NAME ": _init: ddi_soft_state_init failed! rc=%d\n", rc)); + } + else + LogRel((DEVICE_NAME ": _init: VBoxUSBFilterInit failed! rc=%d\n", rc)); + + mutex_destroy(&g_VBoxUSBMonSolarisMtx); + RTR0Term(); + } + else + LogRel((DEVICE_NAME ": _init: RTR0Init failed! rc=%d\n", rc)); + + return -1; +} + + +int _fini(void) +{ + int rc; + + LogFunc((DEVICE_NAME ": _fini\n")); + + rc = mod_remove(&g_VBoxUSBMonSolarisModLinkage); + if (!rc) + { + ddi_soft_state_fini(&g_pVBoxUSBMonSolarisState); + VBoxUSBFilterTerm(); + mutex_destroy(&g_VBoxUSBMonSolarisMtx); + + RTR0Term(); + } + return rc; +} + + +int _info(struct modinfo *pModInfo) +{ + LogFunc((DEVICE_NAME ": _info\n")); + + return mod_info(&g_VBoxUSBMonSolarisModLinkage, pModInfo); +} + + +/** + * 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) + * + * @returns corresponding solaris error code. + */ +static int VBoxUSBMonSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd) +{ + LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisAttach: pDip=%p enmCmd=%d\n", pDip, enmCmd)); + switch (enmCmd) + { + case DDI_ATTACH: + { + if (RT_UNLIKELY(g_pDip)) + { + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisAttach: Global instance already initialized\n")); + return DDI_FAILURE; + } + + g_pDip = pDip; + int instance = ddi_get_instance(pDip); + int rc = ddi_create_priv_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, 0, + "none", "none", 0660); + if (rc == DDI_SUCCESS) + { + rc = usb_register_dev_driver(g_pDip, VBoxUSBMonSolarisElectDriver); + if (rc == DDI_SUCCESS) + { + ddi_report_dev(pDip); + return DDI_SUCCESS; + } + + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisAttach: Failed to register driver election callback! rc=%d\n", rc)); + } + else + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisAttach: ddi_create_minor_node failed! rc=%d\n", rc)); + return DDI_FAILURE; + } + + case DDI_RESUME: + { + /* We don't have to bother about power management. */ + 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) + * + * @returns corresponding solaris error code. + */ +static int VBoxUSBMonSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd) +{ + LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisDetach\n")); + + switch (enmCmd) + { + case DDI_DETACH: + { + /* + * Free all registered clients' info. + */ + mutex_enter(&g_VBoxUSBMonSolarisMtx); + vboxusbmon_client_t *pCur = g_pVBoxUSBMonSolarisClients; + while (pCur) + { + vboxusbmon_client_t *pNext = pCur->pNext; + RTMemFree(pCur); + pCur = pNext; + } + mutex_exit(&g_VBoxUSBMonSolarisMtx); + + usb_unregister_dev_driver(g_pDip); + + ddi_remove_minor_node(pDip, NULL); + g_pDip = NULL; + return DDI_SUCCESS; + } + + case DDI_SUSPEND: + { + /* We don't have to bother about power management. */ + 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. + * + * @returns corresponding solaris error code. + */ +static int VBoxUSBMonSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult) +{ + int rc = DDI_SUCCESS; + + LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisGetInfo\n")); + + switch (enmCmd) + { + case DDI_INFO_DEVT2DEVINFO: + *ppvResult = (void *)g_pDip; + break; + + case DDI_INFO_DEVT2INSTANCE: + *ppvResult = (void *)(uintptr_t)ddi_get_instance(g_pDip); + break; + + default: + rc = DDI_FAILURE; + break; + } + return rc; +} + + +static int VBoxUSBMonSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred) +{ + vboxusbmon_state_t *pState = NULL; + unsigned iOpenInstance; + + LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisOpen\n")); + + /* + * Verify we are being opened as a character device. + */ + if (fType != OTYP_CHR) + return EINVAL; + + /* + * Verify that we're called after attach. + */ + if (!g_pDip) + { + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisOpen: Invalid state for opening\n")); + return ENXIO; + } + + for (iOpenInstance = 0; iOpenInstance < 4096; iOpenInstance++) + { + if ( !ddi_get_soft_state(g_pVBoxUSBMonSolarisState, iOpenInstance) /* faster */ + && ddi_soft_state_zalloc(g_pVBoxUSBMonSolarisState, iOpenInstance) == DDI_SUCCESS) + { + pState = ddi_get_soft_state(g_pVBoxUSBMonSolarisState, iOpenInstance); + break; + } + } + if (!pState) + { + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisOpen: Too many open instances")); + return ENXIO; + } + + pState->Process = RTProcSelf(); + *pDev = makedevice(getmajor(*pDev), iOpenInstance); + + NOREF(fFlag); + NOREF(pCred); + + return 0; +} + + +static int VBoxUSBMonSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred) +{ + vboxusbmon_state_t *pState = NULL; + LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisClose\n")); + + pState = ddi_get_soft_state(g_pVBoxUSBMonSolarisState, getminor(Dev)); + if (!pState) + { + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisClose: Failed to get state\n")); + return EFAULT; + } + + /* + * Remove all filters for this client process. + */ + VBoxUSBFilterRemoveOwner(pState->Process); + + ddi_soft_state_free(g_pVBoxUSBMonSolarisState, getminor(Dev)); + pState = NULL; + + NOREF(fFlag); + NOREF(fType); + NOREF(pCred); + + return 0; +} + + +static int VBoxUSBMonSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred) +{ + LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisRead\n")); + return 0; +} + + +static int VBoxUSBMonSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred) +{ + LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisWrite\n")); + return 0; +} + + +/** @def IOCPARM_LEN + * Gets the length from the ioctl number. + * This is normally defined by sys/ioccom.h on BSD systems... + */ +#ifndef IOCPARM_LEN +# define IOCPARM_LEN(Code) (((Code) >> 16) & IOCPARM_MASK) +#endif + +static int VBoxUSBMonSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal) +{ + LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: Dev=%d Cmd=%d pArg=%p Mode=%d\n", Dev, Cmd, pArg)); + + /* + * Get the session from the soft state item. + */ + vboxusbmon_state_t *pState = ddi_get_soft_state(g_pVBoxUSBMonSolarisState, getminor(Dev)); + if (!pState) + { + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: No state data for minor instance %d\n", getminor(Dev))); + return EINVAL; + } + + /* + * Read the request wrapper. Though We don't really need wrapper struct. now + * it's room for the future as Solaris isn't generous regarding the size. + */ + VBOXUSBREQ ReqWrap; + if (IOCPARM_LEN(Cmd) != sizeof(ReqWrap)) + { + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: bad request %#x size=%d expected=%d\n", Cmd, IOCPARM_LEN(Cmd), + sizeof(ReqWrap))); + return ENOTTY; + } + + int rc = ddi_copyin((void *)pArg, &ReqWrap, sizeof(ReqWrap), Mode); + if (RT_UNLIKELY(rc)) + { + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: ddi_copyin failed to read header pArg=%p Cmd=%d. rc=%d\n", pArg, Cmd, rc)); + return EINVAL; + } + + if (ReqWrap.u32Magic != VBOXUSBMON_MAGIC) + { + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: Bad magic %#x; pArg=%p Cmd=%d\n", ReqWrap.u32Magic, pArg, Cmd)); + return EINVAL; + } + if (RT_UNLIKELY( ReqWrap.cbData == 0 + || ReqWrap.cbData > _1M*16)) + { + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: Bad size %#x; pArg=%p Cmd=%d\n", ReqWrap.cbData, pArg, Cmd)); + return EINVAL; + } + + /* + * Read the request. + */ + void *pvBuf = RTMemTmpAlloc(ReqWrap.cbData); + if (RT_UNLIKELY(!pvBuf)) + { + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: RTMemTmpAlloc failed to alloc %d bytes\n", ReqWrap.cbData)); + return ENOMEM; + } + + rc = ddi_copyin((void *)(uintptr_t)ReqWrap.pvDataR3, pvBuf, ReqWrap.cbData, Mode); + if (RT_UNLIKELY(rc)) + { + RTMemTmpFree(pvBuf); + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: ddi_copyin failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, + rc)); + return EFAULT; + } + if (RT_UNLIKELY( ReqWrap.cbData != 0 + && !VALID_PTR(pvBuf))) + { + RTMemTmpFree(pvBuf); + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: pvBuf Invalid pointer %p\n", pvBuf)); + return EINVAL; + } + Log((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: pid=%d\n", (int)RTProcSelf())); + + /* + * Process the IOCtl. + */ + size_t cbDataReturned = 0; + rc = vboxUSBMonSolarisProcessIOCtl(Cmd, pState, pvBuf, ReqWrap.cbData, &cbDataReturned); + ReqWrap.rc = rc; + rc = 0; + + if (RT_UNLIKELY(cbDataReturned > ReqWrap.cbData)) + { + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: Too much output data %d expected %d\n", cbDataReturned, ReqWrap.cbData)); + cbDataReturned = ReqWrap.cbData; + } + + ReqWrap.cbData = cbDataReturned; + + /* + * Copy the request back to user space. + */ + rc = ddi_copyout(&ReqWrap, (void *)pArg, sizeof(ReqWrap), Mode); + if (RT_LIKELY(!rc)) + { + /* + * Copy the payload (if any) back to user space. + */ + if (cbDataReturned > 0) + { + rc = ddi_copyout(pvBuf, (void *)(uintptr_t)ReqWrap.pvDataR3, cbDataReturned, Mode); + if (RT_UNLIKELY(rc)) + { + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: ddi_copyout failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, + pArg, Cmd, rc)); + rc = EFAULT; + } + } + } + else + { + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisIOCtl: ddi_copyout(1) failed pArg=%p Cmd=%d\n", pArg, Cmd)); + rc = EFAULT; + } + + *pVal = rc; + RTMemTmpFree(pvBuf); + return rc; +} + + +/** + * IOCtl processor for user to kernel and kernel to kernel communication. + * + * @returns VBox status code. + * + * @param iFunction The requested function. + * @param pvState Opaque pointer to driver state used for getting + * ring-3 process (Id). + * @param pvData The input/output data buffer. Can be NULL + * depending on the function. + * @param cbData The max size of the data buffer. + * @param pcbReturnedData Where to store the amount of returned data. Can + * be NULL. + */ +static int vboxUSBMonSolarisProcessIOCtl(int iFunction, void *pvState, void *pvData, size_t cbData, size_t *pcbReturnedData) +{ + LogFunc((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: iFunction=%d pvBuf=%p cbBuf=%zu\n", iFunction, pvData, cbData)); + + AssertPtrReturn(pvState, VERR_INVALID_POINTER); + vboxusbmon_state_t *pState = (vboxusbmon_state_t *)pvState; + int rc; + +#define CHECKRET_MIN_SIZE(mnemonic, cbMin) \ + do { \ + if (cbData < (cbMin)) \ + { \ + LogRel(("vboxUSBSolarisProcessIOCtl: " mnemonic ": cbData=%#zx (%zu) min is %#zx (%zu)\n", \ + cbData, cbData, (size_t)(cbMin), (size_t)(cbMin))); \ + return VERR_BUFFER_OVERFLOW; \ + } \ + if ((cbMin) != 0 && !VALID_PTR(pvData)) \ + { \ + LogRel(("vboxUSBSolarisProcessIOCtl: " mnemonic ": Invalid pointer %p\n", pvData)); \ + return VERR_INVALID_POINTER; \ + } \ + } while (0) + + switch (iFunction) + { + case VBOXUSBMON_IOCTL_ADD_FILTER: + { + CHECKRET_MIN_SIZE("ADD_FILTER", sizeof(VBOXUSBREQ_ADD_FILTER)); + + VBOXUSBREQ_ADD_FILTER *pReq = (VBOXUSBREQ_ADD_FILTER *)pvData; + PUSBFILTER pFilter = (PUSBFILTER)&pReq->Filter; + + Log(("vboxUSBMonSolarisProcessIOCtl: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x " + "bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x\n", + USBFilterGetNum(pFilter, USBFILTERIDX_VENDOR_ID), + USBFilterGetNum(pFilter, USBFILTERIDX_PRODUCT_ID), + USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_REV), + USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_CLASS), + USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS), + USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_PROTOCOL), + USBFilterGetNum(pFilter, USBFILTERIDX_BUS), + USBFilterGetNum(pFilter, USBFILTERIDX_PORT))); + Log(("vboxUSBMonSolarisProcessIOCtl: Manufacturer=%s Product=%s Serial=%s\n", + USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) : "<null>", + USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) : "<null>", + USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>")); + + rc = USBFilterSetMustBePresent(pFilter, USBFILTERIDX_BUS, false /* fMustBePresent */); AssertRC(rc); + + rc = VBoxUSBFilterAdd(pFilter, pState->Process, &pReq->uId); + *pcbReturnedData = cbData; + Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: ADD_FILTER (Process:%d) returned %d\n", pState->Process, rc)); + break; + } + + case VBOXUSBMON_IOCTL_REMOVE_FILTER: + { + CHECKRET_MIN_SIZE("REMOVE_FILTER", sizeof(VBOXUSBREQ_REMOVE_FILTER)); + + VBOXUSBREQ_REMOVE_FILTER *pReq = (VBOXUSBREQ_REMOVE_FILTER *)pvData; + rc = VBoxUSBFilterRemove(pState->Process, (uintptr_t)pReq->uId); + *pcbReturnedData = 0; + Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: REMOVE_FILTER (Process:%d) returned %d\n", pState->Process, rc)); + break; + } + + case VBOXUSBMON_IOCTL_RESET_DEVICE: + { + CHECKRET_MIN_SIZE("RESET_DEVICE", sizeof(VBOXUSBREQ_RESET_DEVICE)); + + VBOXUSBREQ_RESET_DEVICE *pReq = (VBOXUSBREQ_RESET_DEVICE *)pvData; + rc = vboxUSBMonSolarisResetDevice(pReq->szDevicePath, pReq->fReattach); + *pcbReturnedData = 0; + Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: RESET_DEVICE (Process:%d) returned %d\n", pState->Process, rc)); + break; + } + + case VBOXUSBMON_IOCTL_CLIENT_INFO: + { + CHECKRET_MIN_SIZE("CLIENT_INFO", sizeof(VBOXUSBREQ_CLIENT_INFO)); + + VBOXUSBREQ_CLIENT_INFO *pReq = (VBOXUSBREQ_CLIENT_INFO *)pvData; + rc = vboxUSBMonSolarisClientInfo(pState, pReq); + *pcbReturnedData = cbData; + Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: CLIENT_INFO (Process:%d) returned %d\n", pState->Process, rc)); + break; + } + + case VBOXUSBMON_IOCTL_GET_VERSION: + { + CHECKRET_MIN_SIZE("GET_VERSION", sizeof(VBOXUSBREQ_GET_VERSION)); + + PVBOXUSBREQ_GET_VERSION pGetVersionReq = (PVBOXUSBREQ_GET_VERSION)pvData; + pGetVersionReq->u32Major = VBOXUSBMON_VERSION_MAJOR; + pGetVersionReq->u32Minor = VBOXUSBMON_VERSION_MINOR; + *pcbReturnedData = sizeof(VBOXUSBREQ_GET_VERSION); + rc = VINF_SUCCESS; + Log((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: GET_VERSION returned %d\n", rc)); + break; + } + + default: + { + LogRel((DEVICE_NAME ": vboxUSBMonSolarisProcessIOCtl: Unknown request (Process:%d) %#x\n", pState->Process, + iFunction)); + *pcbReturnedData = 0; + rc = VERR_NOT_SUPPORTED; + break; + } + } + return rc; +} + + +static int vboxUSBMonSolarisResetDevice(char *pszDevicePath, bool fReattach) +{ + int rc = VERR_GENERAL_FAILURE; + + LogFunc((DEVICE_NAME ": vboxUSBMonSolarisResetDevice: pszDevicePath=%s fReattach=%d\n", pszDevicePath, fReattach)); + + /* + * Try grabbing the dev_info_t. + */ + dev_info_t *pDeviceInfo = e_ddi_hold_devi_by_path(pszDevicePath, 0); + if (pDeviceInfo) + { + ddi_release_devi(pDeviceInfo); + + /* + * Grab the root device node from the parent hub for resetting. + */ + dev_info_t *pTmpDeviceInfo = NULL; + for (;;) + { + pTmpDeviceInfo = ddi_get_parent(pDeviceInfo); + if (!pTmpDeviceInfo) + { + LogRel((DEVICE_NAME ":vboxUSBMonSolarisResetDevice: Failed to get parent device info for %s\n", pszDevicePath)); + return VERR_GENERAL_FAILURE; + } + + if (ddi_prop_exists(DDI_DEV_T_ANY, pTmpDeviceInfo, DDI_PROP_DONTPASS, "usb-port-count")) /* parent hub */ + break; + + pDeviceInfo = pTmpDeviceInfo; + } + + /* + * Try re-enumerating the device. + */ + rc = usb_reset_device(pDeviceInfo, fReattach ? USB_RESET_LVL_REATTACH : USB_RESET_LVL_DEFAULT); + Log((DEVICE_NAME ": vboxUSBMonSolarisResetDevice: usb_reset_device for %s level=%s rc=%d\n", pszDevicePath, + fReattach ? "ReAttach" : "Default", rc)); + + switch (rc) + { + case USB_SUCCESS: rc = VINF_SUCCESS; break; + case USB_INVALID_PERM: rc = VERR_PERMISSION_DENIED; break; + case USB_INVALID_ARGS: rc = VERR_INVALID_PARAMETER; break; + case USB_BUSY: rc = VERR_RESOURCE_BUSY; break; + case USB_INVALID_CONTEXT: rc = VERR_INVALID_CONTEXT; break; + case USB_FAILURE: rc = VERR_GENERAL_FAILURE; break; + + default: rc = VERR_UNRESOLVED_ERROR; break; + } + } + else + { + rc = VERR_INVALID_HANDLE; + LogRel((DEVICE_NAME ": vboxUSBMonSolarisResetDevice: Cannot obtain device info for %s\n", pszDevicePath)); + } + + return rc; +} + + +/** + * Query client driver information. This also has a side-effect that it informs + * the client driver which upcoming VM process should be allowed to open it. + * + * @returns VBox status code. + * @param pState Pointer to the device state. + * @param pClientInfo Pointer to the client info. object. + */ +static int vboxUSBMonSolarisClientInfo(vboxusbmon_state_t *pState, PVBOXUSB_CLIENT_INFO pClientInfo) +{ + LogFunc((DEVICE_NAME ": vboxUSBMonSolarisClientInfo: pState=%p pClientInfo=%p\n", pState, pClientInfo)); + + AssertPtrReturn(pState, VERR_INVALID_POINTER); + AssertPtrReturn(pClientInfo, VERR_INVALID_POINTER); + + mutex_enter(&g_VBoxUSBMonSolarisMtx); + vboxusbmon_client_t *pCur = g_pVBoxUSBMonSolarisClients; + vboxusbmon_client_t *pPrev = NULL; + while (pCur) + { + if (strncmp(pClientInfo->szDeviceIdent, pCur->Info.szDeviceIdent, sizeof(pCur->Info.szDeviceIdent) - 1) == 0) + { + pClientInfo->Instance = pCur->Info.Instance; + RTStrPrintf(pClientInfo->szClientPath, sizeof(pClientInfo->szClientPath), "%s", pCur->Info.szClientPath); + + /* + * Inform the client driver that this is the client process that is going to open it. We can predict the future! + */ + int rc; + if (pCur->Info.pfnSetConsumerCredentials) + { + rc = pCur->Info.pfnSetConsumerCredentials(pState->Process, pCur->Info.Instance, NULL /* pvReserved */); + if (RT_FAILURE(rc)) + LogRel((DEVICE_NAME ": vboxUSBMonSolarisClientInfo: pfnSetConsumerCredentials failed! rc=%d\n", rc)); + } + else + rc = VERR_INVALID_FUNCTION; + + mutex_exit(&g_VBoxUSBMonSolarisMtx); + + Log((DEVICE_NAME ": vboxUSBMonSolarisClientInfo: Found %s, rc=%d\n", pClientInfo->szDeviceIdent, rc)); + return rc; + } + pPrev = pCur; + pCur = pCur->pNext; + } + + mutex_exit(&g_VBoxUSBMonSolarisMtx); + + LogRel((DEVICE_NAME ": vboxUSBMonSolarisClientInfo: Failed to find client %s\n", pClientInfo->szDeviceIdent)); + return VERR_NOT_FOUND; +} + + +/** + * Registers client driver. + * + * @returns VBox status code. + */ +int VBoxUSBMonSolarisRegisterClient(dev_info_t *pClientDip, PVBOXUSB_CLIENT_INFO pClientInfo) +{ + LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisRegisterClient: pClientDip=%p pClientInfo=%p\n", pClientDip, pClientInfo)); + AssertPtrReturn(pClientInfo, VERR_INVALID_PARAMETER); + + if (RT_LIKELY(g_pDip)) + { + vboxusbmon_client_t *pClient = RTMemAllocZ(sizeof(vboxusbmon_client_t)); + if (RT_LIKELY(pClient)) + { + pClient->Info.Instance = pClientInfo->Instance; + strncpy(pClient->Info.szClientPath, pClientInfo->szClientPath, sizeof(pClient->Info.szClientPath)); + strncpy(pClient->Info.szDeviceIdent, pClientInfo->szDeviceIdent, sizeof(pClient->Info.szDeviceIdent)); + pClient->Info.pfnSetConsumerCredentials = pClientInfo->pfnSetConsumerCredentials; + pClient->pDip = pClientDip; + + mutex_enter(&g_VBoxUSBMonSolarisMtx); + pClient->pNext = g_pVBoxUSBMonSolarisClients; + g_pVBoxUSBMonSolarisClients = pClient; + mutex_exit(&g_VBoxUSBMonSolarisMtx); + + Log((DEVICE_NAME ": Client registered (ClientPath=%s Ident=%s)\n", pClient->Info.szClientPath, + pClient->Info.szDeviceIdent)); + return VINF_SUCCESS; + } + return VERR_NO_MEMORY; + } + return VERR_INVALID_STATE; +} + + +/** + * Deregisters client driver. + * + * @returns VBox status code. + */ +int VBoxUSBMonSolarisUnregisterClient(dev_info_t *pClientDip) +{ + LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisUnregisterClient: pClientDip=%p\n", pClientDip)); + AssertReturn(pClientDip, VERR_INVALID_PARAMETER); + + if (RT_LIKELY(g_pDip)) + { + mutex_enter(&g_VBoxUSBMonSolarisMtx); + + vboxusbmon_client_t *pCur = g_pVBoxUSBMonSolarisClients; + vboxusbmon_client_t *pPrev = NULL; + while (pCur) + { + if (pCur->pDip == pClientDip) + { + if (pPrev) + pPrev->pNext = pCur->pNext; + else + g_pVBoxUSBMonSolarisClients = pCur->pNext; + + mutex_exit(&g_VBoxUSBMonSolarisMtx); + + Log((DEVICE_NAME ": Client unregistered (ClientPath=%s Ident=%s)\n", pCur->Info.szClientPath, + pCur->Info.szDeviceIdent)); + RTMemFree(pCur); + return VINF_SUCCESS; + } + pPrev = pCur; + pCur = pCur->pNext; + } + + mutex_exit(&g_VBoxUSBMonSolarisMtx); + + LogRel((DEVICE_NAME ": VBoxUSBMonSolarisUnregisterClient: Failed to find registered client %p\n", pClientDip)); + return VERR_NOT_FOUND; + } + return VERR_INVALID_STATE; +} + + +/** + * USBA driver election callback. + * + * @returns USB_SUCCESS if we want to capture the device, USB_FAILURE otherwise. + * @param pDevDesc The parsed device descriptor (does not include subconfigs). + * @param pDevStrings Device strings: Manufacturer, Product, Serial Number. + * @param pszDevicePath The physical path of the device being attached. + * @param Bus The Bus number on which the device is on. + * @param Port The Port number on the bus. + * @param ppszDrv The name of the driver we wish to capture the device with. + * @param pvReserved Reserved for future use. + */ +int VBoxUSBMonSolarisElectDriver(usb_dev_descr_t *pDevDesc, usb_dev_str_t *pDevStrings, char *pszDevicePath, int Bus, int Port, + char **ppszDrv, void *pvReserved) +{ + LogFunc((DEVICE_NAME ": VBoxUSBMonSolarisElectDriver: pDevDesc=%p pDevStrings=%p pszDevicePath=%s Bus=%d Port=%d\n", pDevDesc, + pDevStrings, pszDevicePath, Bus, Port)); + + AssertPtrReturn(pDevDesc, USB_FAILURE); + AssertPtrReturn(pDevStrings, USB_FAILURE); + + /* + * Create a filter from the device being attached. + */ + USBFILTER Filter; + USBFilterInit(&Filter, USBFILTERTYPE_CAPTURE); + USBFilterSetNumExact(&Filter, USBFILTERIDX_VENDOR_ID, pDevDesc->idVendor, true); + USBFilterSetNumExact(&Filter, USBFILTERIDX_PRODUCT_ID, pDevDesc->idProduct, true); + USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_REV, pDevDesc->bcdDevice, true); + USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_CLASS, pDevDesc->bDeviceClass, true); + USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_SUB_CLASS, pDevDesc->bDeviceSubClass, true); + USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_PROTOCOL, pDevDesc->bDeviceProtocol, true); + USBFilterSetNumExact(&Filter, USBFILTERIDX_BUS, 0x0 /* Bus */, true); /* Use 0x0 as userland initFilterFromDevice function in Main: see comment on "SetMustBePresent" below */ + USBFilterSetNumExact(&Filter, USBFILTERIDX_PORT, Port, true); + USBFilterSetStringExact(&Filter, USBFILTERIDX_MANUFACTURER_STR, pDevStrings->usb_mfg ? pDevStrings->usb_mfg : "", + true /*fMustBePresent*/, true /*fPurge*/); + USBFilterSetStringExact(&Filter, USBFILTERIDX_PRODUCT_STR, pDevStrings->usb_product ? pDevStrings->usb_product : "", + true /*fMustBePresent*/, true /*fPurge*/); + USBFilterSetStringExact(&Filter, USBFILTERIDX_SERIAL_NUMBER_STR, pDevStrings->usb_serialno ? pDevStrings->usb_serialno : "", + true /*fMustBePresent*/, true /*fPurge*/); + + /* This doesn't work like it should (USBFilterMatch fails on matching field (6) i.e. Bus despite this. Investigate later. */ + USBFilterSetMustBePresent(&Filter, USBFILTERIDX_BUS, false /* fMustBePresent */); + + Log((DEVICE_NAME ": VBoxUSBMonSolarisElectDriver: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x " + "bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x\n", + USBFilterGetNum(&Filter, USBFILTERIDX_VENDOR_ID), + USBFilterGetNum(&Filter, USBFILTERIDX_PRODUCT_ID), + USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_REV), + USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_CLASS), + USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_SUB_CLASS), + USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_PROTOCOL), + USBFilterGetNum(&Filter, USBFILTERIDX_BUS), + USBFilterGetNum(&Filter, USBFILTERIDX_PORT))); + Log((DEVICE_NAME ": VBoxUSBMonSolarisElectDriver: Manufacturer=%s Product=%s Serial=%s\n", + USBFilterGetString(&Filter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(&Filter, USBFILTERIDX_MANUFACTURER_STR) : "<null>", + USBFilterGetString(&Filter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(&Filter, USBFILTERIDX_PRODUCT_STR) : "<null>", + USBFilterGetString(&Filter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(&Filter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>")); + + /* + * Run through user filters and try to see if it has a match. + */ + uintptr_t uId = 0; + RTPROCESS Owner = VBoxUSBFilterMatch(&Filter, &uId); + USBFilterDelete(&Filter); + if (Owner == NIL_RTPROCESS) + { + Log((DEVICE_NAME ": VBoxUSBMonSolarisElectDriver: No matching filters, device %#x:%#x uninteresting\n", + pDevDesc->idVendor, pDevDesc->idProduct)); + return USB_FAILURE; + } + + *ppszDrv = ddi_strdup(VBOXUSB_DRIVER_NAME, KM_SLEEP); +#if 0 + LogRel((DEVICE_NAME ": Capturing %s %s %#x:%#x:%s Bus=%d Port=%d\n", + pDevStrings->usb_mfg ? pDevStrings->usb_mfg : "<Unknown Manufacturer>", + pDevStrings->usb_product ? pDevStrings->usb_product : "<Unnamed USB device>", + pDevDesc->idVendor, pDevDesc->idProduct, pszDevicePath, Bus, Port)); +#else + /* Until IPRT R0 logging is fixed. See @bugref{6657#c7} */ + cmn_err(CE_CONT, "Capturing %s %s 0x%x:0x%x:%s Bus=%d Port=%d\n", + pDevStrings->usb_mfg ? pDevStrings->usb_mfg : "<Unknown Manufacturer>", + pDevStrings->usb_product ? pDevStrings->usb_product : "<Unnamed USB device>", + pDevDesc->idVendor, pDevDesc->idProduct, pszDevicePath, Bus, Port); +#endif + return USB_SUCCESS; +} + |