VBoxUSB-solaris.c revision d72aa6b0dab3e9b60aa78bfca99c767c48a406b0
/* $Id$ */
/** @file
* VirtualBox USB Client Driver, Solaris Hosts.
*/
/*
* Copyright (C) 2008 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <VBox/usblib-solaris.h>
#include <iprt/initterm.h>
#include <iprt/semaphore.h>
#define USBDRV_MAJOR_VER 2
#define USBDRV_MINOR_VER 0
#include "usbai_private.h"
#include <sys/archsystm.h>
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The module name. */
#define DEVICE_NAME "vboxusb"
/** The module description as seen in 'modinfo'. */
#define DEVICE_DESC_DRV "VirtualBox USB"
/** Endpoint states */
#define VBOXUSB_EP_INITIALIZED 0xa1fa1fa
#define VBOXUSB_EP_STATE_NONE RT_BIT(0)
/** Polling states */
#define VBOXUSB_POLL_OFF RT_BIT(0)
/** -=-=-=-=-=-=- Standard Specifics -=-=-=-=-=-=- */
/** Max. supported endpoints */
#define VBOXUSB_MAX_ENDPOINTS 32
/** Size of USB Ctrl Xfer Header */
#define VBOXUSB_CTRL_XFER_SIZE 0x08
/**
* number of additional transaction opportunities per microframe.
*/
/** Endpoint Xfer Type */
/** Endpoint Xfer Direction */
/** -=-=-=-=-=-=- Tunable Parameters -=-=-=-=-=-=- */
/** Time to wait while draining inflight UBRs on suspend, in seconds. */
#define VBOXUSB_DRAIN_TIME 30
/** Ctrl Xfer timeout in seconds. */
#define VBOXUSB_CTRL_XFER_TIMEOUT 10
/** Bulk Xfer timeout in seconds. */
#define VBOXUSB_BULK_XFER_TIMEOUT 10
/** Intr Xfer timeout in seconds. */
#define VBOXUSB_INTR_XFER_TIMEOUT 10
/** Maximum URB queue length. */
#define VBOXUSB_URB_QUEUE_SIZE 64
/** Maximum asynchronous requests per pipe */
#define VBOXUSB_MAX_PIPE_ASYNC_REQS 2
#if defined(DEBUG_ramshankar)
# define LogFlowFunc LogRel
#endif
/** For enabling global symbols while debugging **/
#if defined(DEBUG_ramshankar)
# define LOCAL
#else
# define LOCAL static
#endif
/*******************************************************************************
* Kernel Entry Hook *
*******************************************************************************/
int VBoxUSBSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead);
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
*/
static struct cb_ops g_VBoxUSBSolarisCbOps =
{
nodev, /* b strategy */
nodev, /* b dump */
nodev, /* b print */
nodev, /* c devmap */
nodev, /* c mmap */
nodev, /* c segmap */
ddi_prop_op, /* property ops */
NULL, /* streamtab */
CB_REV, /* revision */
nodev, /* c aread */
nodev /* c awrite */
};
/**
* dev_ops: for driver device operations
*/
static struct dev_ops g_VBoxUSBSolarisDevOps =
{
DEVO_REV, /* driver build revision */
0, /* ref count */
nulldev, /* identify */
nulldev, /* probe */
nodev, /* reset */
NULL, /* bus ops */
};
/**
* modldrv: export driver specifics to the kernel
*/
static struct modldrv g_VBoxUSBSolarisModule =
{
&mod_driverops, /* extern from kernel */
};
/**
*/
static struct modlinkage g_VBoxUSBSolarisModLinkage =
{
NULL,
};
/**
* vboxusb_ep_t: Endpoint structure with info. for managing an endpoint.
*/
typedef struct vboxusb_ep_t
{
bool fIsocPolling; /* Whether Isoc. IN polling is enabled */
} vboxusb_ep_t;
/**
* vboxusb_isoc_req_t: Isoc IN. requests queued from device till they are reaped.
*/
typedef struct vboxusb_isoc_req_t
{
/**
* VBOXUSB_URB_STATE: Internal USB URB state.
*/
typedef enum VBOXUSB_URB_STATE
{
VBOXUSB_URB_STATE_FREE = 0x00,
VBOXUSB_URB_STATE_INFLIGHT = 0x04,
VBOXUSB_URB_STATE_LANDED = 0x08
/**
* vboxusb_urb_t: kernel URB representation.
*/
typedef struct vboxusb_urb_t
{
void *pvUrbR3; /* Userspace URB address (untouched, returned while reaping) */
/**
* vboxusb_power_t: Per Device Power Management info.
*/
typedef struct vboxusb_power_t
{
int PowerBusy; /* Busy counter */
bool fPowerWakeup; /* Whether remote power wakeup is enabled */
bool fPowerRaise; /* Whether to raise the power level */
/**
* vboxusb_state_t: Per Device instance state info.
*/
typedef struct vboxusb_state_t
{
bool fClosed; /* Whether the device (default control pipe) is closed */
bool fRestoreCfg; /* Whether we changed configs to restore while tearing down */
bool fGetCfgReqDone; /* First GET_CONFIG request has been circumvented */
int fPoll; /* Polling status flag */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
LOCAL int vboxUSBSolarisInitEndPoint(vboxusb_state_t *pState, usb_ep_data_t *pEpData, uchar_t uCfgValue,
LOCAL int vboxUSBSolarisInitEndPointsForInterfaceAlt(vboxusb_state_t *pState, uint8_t uInterface, uint8_t uAlt);
LOCAL vboxusb_urb_t *vboxUSBSolarisQueueURB(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, mblk_t *pMsg);
LOCAL int vboxUSBSolarisProcessIOCtl(int iFunction, void *pvState, int Mode, PVBOXUSBREQ pUSBReq, void *pvBuf, size_t *pcbDataOut);
/** Device Operation Hooks */
/** Hotplug & Power Management Hooks */
/** Monitor Hooks */
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Global list of all device instances. */
static void *g_pVBoxUSBSolarisState;
/** The default endpoint descriptor */
/** Hotplug events */
static usb_event_t g_VBoxUSBSolarisEvents =
{
NULL, /* presuspend */
NULL /* postresume */
};
/**
* Kernel entry points
*/
int _init(void)
{
/*
* Prevent module autounloading.
*/
if (pModCtl)
else
/*
* Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
*/
if (RT_SUCCESS(rc))
{
if (!rc)
{
if (!rc)
return rc;
}
else
RTR0Term();
}
else
return RTErrConvertToErrno(rc);
}
int _fini(void)
{
int rc;
if (!rc)
{
RTR0Term();
}
return rc;
}
{
}
/**
* 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.
*/
{
int rc;
switch (enmCmd)
{
case DDI_ATTACH:
{
if (rc == DDI_SUCCESS)
{
if (pState)
{
pState->fRestoreCfg = false;
pState->fGetCfgReqDone = false;
pState->cInflightUrbs = 0;
/*
* There is a bug in usb_client_attach() as of Nevada 120 which panics when we bind to
* a non-USB device. So check if we are really binding to a USB device or not.
*/
{
/*
* Here starts the USB specifics.
*/
if (rc == USB_SUCCESS)
{
/*
* Parse out the entire descriptor.
*/
if (rc == USB_SUCCESS)
{
#ifdef DEBUG_ramshankar
#endif
/*
* Initialize state locks.
*/
/*
* Get maximum bulk transfer size supported by the HCD.
*/
if (rc == USB_SUCCESS)
{
/*
* Initialize all endpoints.
*/
if (RT_SUCCESS(rc))
{
/*
* Set the device state.
*/
/*
* Initialize power management for the device.
*/
if (RT_SUCCESS(rc))
{
/*
* Update endpoints (descriptors) for the current config.
*/
/*
* Publish the minor node.
*/
"none", "none", 0666);
{
/*
* Register hotplug callbacks.
*/
{
/*
* Register with our monitor driver.
*/
char szDevicePath[MAXPATHLEN];
"/devices%s:%s",
"%#x:%#x:%d:%s",
if (RT_SUCCESS(rc))
return DDI_SUCCESS;
else
{
}
}
else
}
else
}
else
}
else
}
else
LogRel((DEVICE_NAME ":VBoxUSBSolarisAttach usb_pipe_get_max_bulk_transfer_size failed! rc=%d\n", rc));
}
else
}
else
}
else
LogFlow((DEVICE_NAME ":VBoxUSBSolarisAttach not a USB device.\n")); /* This would appear on every boot if it were Rel */
}
else
}
else
return DDI_FAILURE;
}
case DDI_RESUME:
{
if (RT_UNLIKELY(!pState))
{
return DDI_FAILURE;
}
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.
*/
{
if (RT_UNLIKELY(!pState))
{
return DDI_FAILURE;
}
switch (enmCmd)
{
case DDI_DETACH:
{
/*
* At this point it must be assumed that the default control pipe has
* already been closed by userland (via VBoxUSBSolarisClose() entry point).
* Once it's closed we can no longer open or reference the device here.
*/
/*
* Notify userland if any that we're gone (while resetting device held by us).
*/
/*
* Unregister hotplug callback events first without holding the mutex as the callbacks
* would otherwise block on the mutex.
*/
/*
* Serialize: paranoid; drain other driver activity.
*/
/*
* Close all endpoints.
*/
/*
* Deinitialize power, destroy endpoints.
*/
/*
* Free up all URBs.
*/
{
}
{
}
pState->cInflightUrbs = 0;
/*
* Destroy locks, free up descriptor and detach from USBA.
*/
/*
* Deregister with our Monitor driver.
*/
return DDI_SUCCESS;
}
case DDI_SUSPEND:
{
if (RT_SUCCESS(rc))
return DDI_SUCCESS;
return DDI_FAILURE;
}
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.
*/
{
switch (enmCmd)
{
case DDI_INFO_DEVT2DEVINFO:
{
/*
* One is to one mapping of instance & minor number as we publish only one minor node per device.
*/
if (pState)
{
return DDI_SUCCESS;
}
else
return DDI_FAILURE;
}
case DDI_INFO_DEVT2INSTANCE:
{
return DDI_SUCCESS;
}
default:
return DDI_FAILURE;
}
}
{
LogFlowFunc((DEVICE_NAME ":VBoxUSBSolarisOpen pDev=%p fFlag=%d fType=%d pCred=%p\n", pDev, fFlag, fType, pCred));
/*
* Verify we are being opened as a character device
*/
return EINVAL;
/*
* One is to one mapping. (Minor<=>Instance).
*/
if (!pState)
{
return ENXIO;
}
/*
* Only one user process can open a device instance at a time.
*/
{
return EBUSY;
}
return 0;
}
{
LogFlowFunc((DEVICE_NAME ":VBoxUSBSolarisClose Dev=%d fFlag=%d fType=%d pCred=%p\n", Dev, fFlag, fType, pCred));
if (RT_UNLIKELY(!pState))
{
LogRel((DEVICE_NAME ":VBoxUSBSolarisClose failed to get device state for instance %d\n", instance));
return ENXIO;
}
return 0;
}
{
return ENOTSUP;
}
{
return ENOTSUP;
}
int VBoxUSBSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead)
{
LogFlowFunc((DEVICE_NAME ":VBoxUSBSolarisPoll Dev=%d fEvents=%d fAnyYet=%d pReqEvents=%p\n", Dev, fEvents, fAnyYet, pReqEvents));
/*
* Get the device state (one to one mapping).
*/
if (RT_UNLIKELY(!pState))
{
return ENXIO;
}
/*
* "fEvents" HAS to be POLLIN. We won't bother to test it. The caller
* must always requests input events. Disconnect event (POLLHUP) is invalid in "fEvents".
*/
fEvents = 0;
{
}
{
}
if ( !fEvents
&& !fAnyYet)
{
}
*pReqEvents = fEvents;
return 0;
}
{
LogFlowFunc((DEVICE_NAME ":VBoxUSBSolarisPower pDip=%p Component=%d Level=%d\n", pDip, Component, Level));
if (RT_UNLIKELY(!pState))
{
return DDI_FAILURE;
}
return DDI_SUCCESS;
int rc = USB_FAILURE;
{
/*
* Check if we are transitioning to a valid power state.
*/
{
switch (Level)
{
case USB_DEV_OS_PWR_OFF:
{
break;
/*
* USB D3 command.
*/
break;
}
case USB_DEV_OS_FULL_PWR:
{
/*
* Can happen during shutdown of the OS.
*/
break;
}
default: /* Power levels 1, 2 not implemented */
break;
}
}
else
}
else
rc = USB_SUCCESS;
}
/** @def IOCPARM_LEN
* Gets the length from the ioctl number.
*/
#ifndef IOCPARM_LEN
#endif
{
/* LogFlowFunc((DEVICE_NAME ":VBoxUSBSolarisIOCtl Dev=%d Cmd=%d pArg=%p Mode=%d\n", Dev, Cmd, pArg)); */
/*
* Get the device state (one to one mapping).
*/
if (RT_UNLIKELY(!pState))
{
return EINVAL;
}
/*
* Read the request wrapper.
*/
{
LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: bad request %#x size=%d expected=%d\n", Cmd, IOCPARM_LEN(Cmd), sizeof(ReqWrap)));
return ENOTTY;
}
if (RT_UNLIKELY(rc))
{
LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: ddi_copyin failed to read header pArg=%p Cmd=%d. rc=%d.\n", pArg, Cmd, rc));
return EINVAL;
}
{
LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: bad magic %#x; pArg=%p Cmd=%d.\n", ReqWrap.u32Magic, pArg, Cmd));
return EINVAL;
}
{
LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: bad size %#x; pArg=%p Cmd=%d.\n", ReqWrap.cbData, pArg, Cmd));
return EINVAL;
}
/*
* Read the request.
*/
if (RT_UNLIKELY(!pvBuf))
{
LogRel((DEVICE_NAME ":VBoxUSBSolarisIOCtl: RTMemTmpAlloc failed to alloc %d bytes.\n", ReqWrap.cbData));
return ENOMEM;
}
if (RT_UNLIKELY(rc))
{
LogRel((DEVICE_NAME ":VBoxUSBSolarisIOCtl: ddi_copyin failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc));
return EFAULT;
}
{
LogRel((DEVICE_NAME ":VBoxUSBSolarisIOCtl: invalid request pvBuf=%p cbData=%d\n", pvBuf, ReqWrap.cbData));
return EINVAL;
}
/*
* Process the IOCtl.
*/
rc = 0;
{
LogRel((DEVICE_NAME ":VBoxUSBSolarisIOCtl: too much output data %d expected %d Truncating!\n", cbDataOut, ReqWrap.cbData));
}
/*
* Copy VBOXUSBREQ back to userspace (which contains rc for USB operation).
*/
{
/*
* Copy payload (if any) back to userspace.
*/
if (cbDataOut > 0)
{
if (RT_UNLIKELY(rc))
{
LogRel((DEVICE_NAME ":VBoxUSBSolarisIOCtl: ddi_copyout failed; pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc));
}
}
}
else
{
LogRel((DEVICE_NAME ":VBoxUSBSolarisIOCtl: ddi_copyout(1)failed; pReqWrap=%p pArg=%p Cmd=%d. rc=%d\n", &ReqWrap, pArg, Cmd, rc));
}
return rc;
}
/**
* IOCtl processor for user to kernel and kernel to kernel communication.
*
* @returns VBox status code.
*
* @param iFunction The requested function.
* @param pvState The USB device instance.
* @param Mode The IOCtl mode.
* @param pUSBReq Pointer to the VBOXUSB request.
* @param pvBuf Pointer to the ring-3 URB.
* @param pcbDataOut Where to store the IOCtl OUT data size.
*/
LOCAL int vboxUSBSolarisProcessIOCtl(int iFunction, void *pvState, int Mode, PVBOXUSBREQ pUSBReq, void *pvBuf, size_t *pcbDataOut)
{
// LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisProcessIOCtl iFunction=%d pvState=%p pUSBReq=%p\n", iFunction, pvState, pUSBReq));
int rc;
do { \
{ \
LogRel((DEVICE_NAME ":vboxUSBSolarisProcessIOCtl: " mnemonic ": cbData=%#zx (%zu) min is %#zx (%zu)\n", \
return VERR_BUFFER_OVERFLOW; \
} \
{ \
return VERR_INVALID_PARAMETER; \
} \
} while (0)
switch (iFunction)
{
case VBOXUSB_IOCTL_SEND_URB:
{
*pcbDataOut = 0;
break;
}
case VBOXUSB_IOCTL_REAP_URB:
{
*pcbDataOut = sizeof(VBOXUSBREQ_URB);
break;
}
case VBOXUSB_IOCTL_CLEAR_EP:
{
*pcbDataOut = 0;
break;
}
case VBOXUSB_IOCTL_SET_CONFIG:
{
*pcbDataOut = 0;
break;
}
{
rc = vboxUSBSolarisSetInterface(pState, pSetInterfaceReq->bInterface, pSetInterfaceReq->bAlternate);
*pcbDataOut = 0;
break;
}
{
*pcbDataOut = 0;
break;
}
case VBOXUSB_IOCTL_ABORT_PIPE:
{
*pcbDataOut = 0;
break;
}
case VBOXUSB_IOCTL_GET_CONFIG:
{
*pcbDataOut = sizeof(VBOXUSBREQ_GET_CONFIG);
break;
}
{
*pcbDataOut = sizeof(VBOXUSBREQ_GET_VERSION);
rc = VINF_SUCCESS;
break;
}
default:
{
*pcbDataOut = 0;
break;
}
}
return rc;
}
/**
* Initialize device power management functions.
*
* @param pState The USB device instance.
*
* @returns VBox status code.
*/
{
if (rc == USB_SUCCESS)
{
{
if (rc == USB_SUCCESS)
{
if (rc != DDI_SUCCESS)
{
}
}
else
return VINF_SUCCESS;
}
else
rc = VERR_NO_MEMORY;
}
else
{
rc = VINF_SUCCESS;
}
return rc;
}
/**
* Destroy device power management functions.
*
* @param pState The USB device instance.
* @remarks Requires the device state mutex to be held.
*
* @returns VBox status code.
*/
{
{
int rc = -1;
{
if (rc != DDI_SUCCESS)
if (rc != DDI_SUCCESS)
}
else
if (rc != DDI_SUCCESS)
}
}
/**
* Convert Solaris' USBA URB status to VBox's USB URB status.
*
* @param Status Solaris USBA USB URB status.
*
* @returns VBox USB URB status.
*/
{
switch (Status)
{
case USB_CR_OK: return VUSBSTATUS_OK;
case USB_CR_CRC: return VUSBSTATUS_CRC;
case USB_CR_DEV_NOT_RESP: return VUSBSTATUS_DNR;
case USB_CR_DATA_UNDERRUN: return VUSBSTATUS_DATA_UNDERRUN;
case USB_CR_DATA_OVERRUN: return VUSBSTATUS_DATA_OVERRUN;
case USB_CR_STALL: return VUSBSTATUS_STALL;
/*
case USB_CR_BITSTUFFING:
case USB_CR_DATA_TOGGLE_MM:
case USB_CR_PID_CHECKFAILURE:
case USB_CR_UNEXP_PID:
case USB_CR_BUFFER_OVERRUN:
case USB_CR_BUFFER_UNDERRUN:
case USB_CR_TIMEOUT:
case USB_CR_NOT_ACCESSED:
case USB_CR_NO_RESOURCES:
case USB_CR_UNSPECIFIED_ERR:
case USB_CR_STOPPED_POLLING:
case USB_CR_PIPE_CLOSING:
case USB_CR_PIPE_RESET:
case USB_CR_NOT_SUPPORTED:
case USB_CR_FLUSHED:
case USB_CR_HC_HARDWARE_ERR:
*/
default: return VUSBSTATUS_INVALID;
}
}
/**
* Convert Solaris' USBA error code to VBox's error code.
*
* @param UsbRc Solaris USBA error code.
*
* @returns VBox error code.
*/
static inline int vboxUSBSolarisToVBoxRC(int UsbRc)
{
switch (UsbRc)
{
case USB_SUCCESS: return VINF_SUCCESS;
case USB_INVALID_ARGS: return VERR_INVALID_PARAMETER;
case USB_INVALID_PIPE: return VERR_BAD_PIPE;
case USB_INVALID_CONTEXT: return VERR_INVALID_CONTEXT;
case USB_BUSY: return VERR_PIPE_BUSY;
case USB_PIPE_ERROR: return VERR_PIPE_IO_ERROR;
/*
case USB_FAILURE:
case USB_NO_RESOURCES:
case USB_NO_BANDWIDTH:
case USB_NOT_SUPPORTED:
case USB_PIPE_ERROR:
case USB_NO_FRAME_NUMBER:
case USB_INVALID_START_FRAME:
case USB_HC_HARDWARE_ERROR:
case USB_INVALID_REQUEST:
case USB_INVALID_VERSION:
case USB_INVALID_PERM:
*/
default: return VERR_GENERAL_FAILURE;
}
}
/**
* Convert Solaris' USBA device state to VBox's error code.
*
* @param UsbRc Solaris USBA error code.
*
* @returns VBox error code.
*/
{
switch (uDeviceState)
{
case USB_DEV_ONLINE: return VINF_SUCCESS;
case USB_DEV_SUSPENDED: return VERR_VUSB_DEVICE_IS_SUSPENDED;
case USB_DEV_DISCONNECTED:
case USB_DEV_PWRED_DOWN: return VERR_VUSB_DEVICE_NOT_ATTACHED;
default: return VERR_GENERAL_FAILURE;
}
}
/**
* Check if the device is a USB device.
*
* @param pDip Pointer to this device info. structure.
*
* @returns If this is really a USB device returns true, otherwise false.
*/
{
int rc = DDI_FAILURE;
/*
* Check device for "usb" compatible property, root hubs->device would likely mean parent has no "usb" property.
*/
char **ppszCompatible = NULL;
rc = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, pDip, DDI_PROP_DONTPASS, "compatible", &ppszCompatible, &cCompatible);
{
while (cCompatible--)
{
LogFlow((DEVICE_NAME ":vboxUSBSolarisIsUSBDevice compatible[%d]=%s\n", cCompatible, ppszCompatible[cCompatible]));
{
LogFlow((DEVICE_NAME ":vboxUSBSolarisIsUSBDevice verified device as USB. pszCompatible=%s\n", ppszCompatible[cCompatible]));
return true;
}
}
}
else
/*
* Check parent for "usb" compatible property.
*/
if (pParentDip)
{
rc = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, pParentDip, DDI_PROP_DONTPASS, "compatible", &ppszCompatible, &cCompatible);
{
while (cCompatible--)
{
LogFlow((DEVICE_NAME ":vboxUSBSolarisIsUSBDevice parent compatible[%d]=%s\n", cCompatible, ppszCompatible[cCompatible]));
{
LogFlow((DEVICE_NAME ":vboxUSBSolarisIsUSBDevice verified device as USB. parent pszCompatible=%s\n",
return true;
}
}
}
else
}
else
LogFlow((DEVICE_NAME ":vboxUSBSolarisIsUSBDevice failed to obtain parent device for property lookup.\n"));
return false;
}
/**
* Submit a URB.
*
* @param pState The USB device instance.
* @param pUrb Pointer to the VBox USB URB.
* @param Mode The IOCtl mode.
*
* @returns VBox error code.
*/
{
// LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisSendUrb pState=%p pUrbReq=%p bEndpoint=%#x[%d] enmDir=%#x enmType=%#x cbData=%d pvData=%p\n",
// pState, pUrbReq, pUrbReq->bEndpoint, EndPtIndex, pUrbReq->enmDir, pUrbReq->enmType, pUrbReq->cbData, pUrbReq->pvData));
{
return VERR_INVALID_POINTER;
}
/*
* Allocate message block & copy userspace buffer for host to device Xfers and for
* Control Xfers (since input has Setup header that needs copying).
*/
int rc = VINF_SUCCESS;
{
if (RT_UNLIKELY(!pMsg))
{
return VERR_NO_MEMORY;
}
if (RT_UNLIKELY(rc))
{
return VERR_NO_MEMORY;
}
}
if (pState->fClosed) /* Required for Isoc. IN Xfers which don't Xfer through the pipe after polling starts */
if (RT_SUCCESS(rc))
{
/*
* Open the pipe if needed.
*/
{
LogRel((DEVICE_NAME ":vboxUSBSolarisSendUrb OpenPipe failed. pState=%p pUrbReq=%p bEndpoint=%#x enmDir=%#x enmType=%#x cbData=%d pvData=%p rc=%d\n",
pState, pUrbReq, pUrbReq->bEndpoint, pUrbReq->enmDir, pUrbReq->enmType, pUrbReq->cbData, pUrbReq->pvData, rc));
return VERR_BAD_PIPE;
}
else
{
{
case VUSBXFERTYPE_MSG:
{
break;
}
case VUSBXFERTYPE_BULK:
{
break;
}
case VUSBXFERTYPE_INTR:
{
break;
}
case VUSBXFERTYPE_ISOC:
{
break;
}
default:
{
break;
}
}
if (RT_FAILURE(rc))
{
{
}
else
{
}
}
}
else
{
rc = VERR_NO_MEMORY;
}
if ( RT_FAILURE(rc)
&& pUrb)
{
{
pState->cInflightUrbs--;
}
}
}
else
{
}
return rc;
}
/**
*
* @param pState The USB device instance.
* @param pUrbReq Pointer to the VBox USB URB.
* @param Mode The IOCtl mode.
*
* @returns VBox error code.
*/
{
// LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisReapUrb pState=%p pUrbReq=%p\n", pState, pUrbReq));
int rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
if (pUrb)
{
/*
* Copy the URB which will then be copied to user-space.
*/
{
/*
* Chain copy the message back into the user buffer.
*/
{
/*
* Paranoia: we should have a single message block almost always.
*/
{
if (RT_UNLIKELY(rc != 0))
{
}
}
else
{
while (pMsg)
{
if (cbMsg > 0)
{
if (RT_UNLIKELY(rc != 0))
{
break;
}
}
break;
}
}
LogFlow((DEVICE_NAME ":vboxUSBSolarisReapUrb pvUrbR3=%p pvDataR3=%p cbData=%d\n", pUrbReq->pvUrbR3, pUrbReq->pvData, pUrbReq->cbData));
}
else
{
}
/*
* Free buffer allocated in VBOXUSB_IOCTL_SEND_URB.
*/
}
else
{
{
else
{
}
}
else
{
}
}
/*
* Copy Isoc packet descriptors.
*/
{
#if 0
{
}
#else
#endif
{
}
}
if (pUrb)
{
/*
*/
}
}
else
}
else
return rc;
}
/**
* Clear a pipe (CLEAR_FEATURE).
*
* @param pState The USB device instance.
* @param bEndpoint The Endpoint address.
*
* @returns VBox error code.
*/
{
LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisClearEndPoint pState=%p bEndpoint=%#x\n", pState, bEndpoint));
/*
* Serialize access: single threaded per Endpoint, one request at a time.
*/
if (RT_SUCCESS(rc))
{
{
/*
* Check if the endpoint is open to be cleared.
*/
{
#if 0
/*
* Asynchronous clear pipe.
*/
USB_FLAGS_NOSLEEP, /* Asynchronous */
NULL, /* Completion callback */
NULL); /* Exception callback */
#endif
/*
* Synchronous reset pipe.
*/
USB_FLAGS_SLEEP, /* Synchronous */
NULL, /* Completion callback */
NULL); /* Exception callback */
LogFlow((DEVICE_NAME ":vboxUSBSolarisClearEndPoint bEndpoint=%#x[%d] returns %d\n", bEndpoint, EndPtIndex, rc));
rc = VINF_SUCCESS;
}
else
{
LogFlow((DEVICE_NAME ":vboxUSBSolarisClearEndPoint not opened to be cleared. Faking success. bEndpoint=%#x.\n", bEndpoint));
rc = VINF_SUCCESS;
}
}
else
{
LogRel((DEVICE_NAME ":vboxUSBSolarisClearEndPoint Endpoint missing!! bEndpoint=%#x EndPtIndex=%d.\n", bEndpoint, EndPtIndex));
}
}
else
LogFlow((DEVICE_NAME ":vboxUSBSolarisClearEndPoint device state=%d not online.\n", pState->DevState));
return rc;
}
/**
* Set configuration (SET_CONFIGURATION)
*
* @param pState The USB device instance.
* @param uCfgValue The Configuration value.
*
* @returns VBox error code.
*/
{
/*
* Serialize access: single threaded per Endpoint, one request at a time.
*/
if (RT_SUCCESS(rc))
{
if (iCfgIndex >= 0)
{
/*
* Switch Config synchronously.
*/
rc = usb_set_cfg(pState->pDip, (uint_t)iCfgIndex, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback data */);
if (rc == USB_SUCCESS)
{
pState->fRestoreCfg = true;
rc = VINF_SUCCESS;
}
else
{
LogRel((DEVICE_NAME ":vboxUSBSolarisSetConfig usb_set_cfg failed for iCfgIndex=%#x bCfgValue=%#x rc=%d\n",
}
}
else
{
LogRel((DEVICE_NAME ":vboxUSBSolarisSetConfig invalid iCfgIndex=%d bCfgValue=%#x\n", iCfgIndex, bCfgValue));
}
}
return rc;
}
/**
* Get configuration (GET_CONFIGURATION)
*
* @param pState The USB device instance.
* @param pCfgValue Where to store the configuration value.
*
* @returns VBox error code.
*/
{
/*
* Solaris keeps the currently active configuration for the first time. Thus for the first request
* we simply pass the cached configuration back to the user.
*/
if (!pState->fGetCfgReqDone)
{
pState->fGetCfgReqDone = true;
if (pCurrCfg)
{
LogFlow((DEVICE_NAME ":vboxUSBSolarisGetConfig cached config returned. CfgValue=%d\n", *pCfgValue));
return VINF_SUCCESS;
}
}
/*
* Get Config synchronously.
*/
{
rc = VINF_SUCCESS;
}
else
{
}
return rc;
}
/**
* Set interface (SET_INTERFACE)
*
* @param pState The USB device instance.
* @param uInterface The Interface number.
* @param uAlt The Alternate setting number.
*
* @returns VBox error code.
*/
{
LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisSetInterface pState=%p uInterface=%#x uAlt=%#x\n", pState, uInterface, uAlt));
/*
* Serialize access: single threaded per Endpoint, one request at a time.
*/
if (RT_SUCCESS(rc))
{
/*
* Set Interface & Alt setting synchronously.
*/
rc = usb_set_alt_if(pState->pDip, uInterface, uAlt, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback data */);
if (rc == USB_SUCCESS)
{
rc = VINF_SUCCESS;
}
else
{
LogRel((DEVICE_NAME ":vboxUSBSolarisSetInterface usb_set_alt_if failed for uInterface=%#x bAlt=%#x rc=%d\n",
}
}
return rc;
}
/**
* Close the USB device and reset it if required.
*
* @param pState The USB device instance.
* @param ResetLevel The reset level.
*
* @returns VBox error code.
*/
{
/*
* Serialize access: single threaded per Endpoint, one request at a time.
*/
if (enmReset == VBOXUSB_RESET_LEVEL_NONE)
{
}
else
if (RT_SUCCESS(rc))
{
switch (enmReset)
{
break;
case VBOXUSB_RESET_LEVEL_SOFT:
break;
default:
rc = USB_SUCCESS;
break;
}
}
return rc;
}
/**
* Abort pending requests and reset the pipe.
*
* @param pState The USB device instance.
* @param bEndpoint The Endpoint address.
*
* @returns VBox error code.
*/
{
/*
* Serialize access: single threaded per Endpoint, one request at a time.
*/
if (RT_SUCCESS(rc))
{
{
{
/*
* Default Endpoint; aborting requests not supported, fake success.
*/
{
return VERR_NOT_SUPPORTED;
}
/*
* Serialize access: single threaded per Endpoint, one request at a time.
*/
USB_FLAGS_SLEEP, /* Synchronous */
NULL, /* Completion callback */
NULL); /* Callback data */
/*
* Allow pending async requests to complete.
*/
USB_FLAGS_SLEEP, /* Synchronous */
5, /* Timeout (seconds) */
NULL, /* Completion callback */
NULL); /* Callback data*/
}
else
{
}
}
else
{
LogRel((DEVICE_NAME ":vboxUSBSolarisAbortPipe invalid pipe index %d bEndpoint=%#x\n", EndPtIndex, bEndpoint));
}
}
return rc;
}
/**
* Initialize an endpoint.
*
* @param pState The USB device instance.
* @param pEpData The Endpoint data.
* @param uCfgValue The Configuration value.
* @param uCfgIndex The Configuration index.
* @param uInterface The Interface.
* @param uAlt The Alternate setting.
*
* @returns VBox error code.
*/
LOCAL int vboxUSBSolarisInitEndPoint(vboxusb_state_t *pState, usb_ep_data_t *pEpData, uchar_t uCfgValue,
{
LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisInitEndPoint pState=%p pEpData=%p CfgVal=%d Iface=%d Alt=%d", pState,
/*
* Is this the default endpoint?
*/
int EpIndex = 0;
if (!pEpData)
{
EpIndex = 0;
}
else
{
}
/*
* Initialize the endpoint data structure.
*/
{
pEp->fIsocPolling = false;
pEp->cIsocInUrbs = 0;
list_create(&pEp->hIsocInLandedReqs, sizeof(vboxusb_isoc_req_t), offsetof(vboxusb_isoc_req_t, hListLink));
pEp->cbIsocInLandedReqs = 0;
pEp->cbMaxIsocData = 0;
}
LogFlow((DEVICE_NAME ":vboxUSBSolarisInitEndPoint done. %s:[%d] bEndpoint=%#x\n", !pEpData ? "Default " : "Endpoint",
return VINF_SUCCESS;
}
/**
* Initialize all Endpoint structures.
*
* @param pState The USB device instance.
*
* @returns VBox status code.
*/
{
/*
* Initialize all Endpoints for all Alternate settings of all Interfaces of all Configs.
*/
int rc = vboxUSBSolarisInitEndPoint(pState, NULL /* pEp */, 0 /* uCfgValue */, 0 /* uInterface */, 0 /* uAlt */);
if (RT_SUCCESS(rc))
{
/*
* Initialize all Endpoints for all Alternate settings of all Interfaces of all Configs.
*/
{
if (RT_FAILURE(rc))
{
LogRel((DEVICE_NAME ":vboxUSBSolarisInitAllEndPoints: vboxUSBSolarisInitEndPoints uCfgIndex=%d failed. rc=%d\n", uCfgIndex, rc));
return rc;
}
}
}
else
return rc;
}
/**
* Initialize Endpoints structures for the given Config.
*
* @param pState The USB device instance.
* @param uCfgIndex The current Config. index.
*
* @returns VBox status code.
*/
{
LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisInitEndPointsForConfig pState=%p uCfgIndex=%d\n", pState, uCfgIndex));
{
{
{
if (RT_FAILURE(rc))
{
LogRel((DEVICE_NAME ":vboxUSBSolarisInitEndPointsForConfig: vboxUSBSolarisInitEndPoint failed! pEp=%p uCfgValue=%u uCfgIndex=%u uInterface=%u, uAlt=%u\n",
return rc;
}
}
}
}
return VINF_SUCCESS;
}
/**
* Initialize Endpoints structures for the given Interface & Alternate setting.
*
* @param pState The USB device instance.
* @param uInterface The interface being switched to.
* @param uAlt The alt being switched to.
*
* @returns VBox status code.
*/
LOCAL int vboxUSBSolarisInitEndPointsForInterfaceAlt(vboxusb_state_t *pState, uint8_t uInterface, uint8_t uAlt)
{
LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisInitEndPointsForInterfaceAlt pState=%p uInterface=%d uAlt=%d\n", pState, uInterface, uAlt));
/* Doesn't hurt to be paranoid */
{
LogRel((DEVICE_NAME ":vboxUSBSolarisInitEndPointsForInterfaceAlt invalid current config index %d\n", uCfgIndex));
return VERR_GENERAL_FAILURE;
}
int rc = VINF_SUCCESS;
if (RT_LIKELY(pInterface))
{
{
{
if (RT_FAILURE(rc))
{
LogRel((DEVICE_NAME ":vboxUSBSolarisInitEndPointsForInterfaceAlt: vboxUSBSolarisInitEndPoint failed! pEp=%p uCfgValue=%u uCfgIndex=%u uInterface=%u, uAlt=%u\n",
return rc;
}
}
}
else
{
}
}
else
{
}
return rc;
}
/**
* Destroy all Endpoint Xfer structures.
*
* @param pState The USB device instance.
* @remarks Requires the state mutex to be held!!
* Call only from Detach() or similar as callbacks
*/
{
for (unsigned i = 0; i < VBOXUSB_MAX_ENDPOINTS; i++)
{
if (pEp)
{
}
}
}
/**
* Destroy an Endpoint.
*
* @param pState The USB device instance.
* @param pEp The Endpoint.
*/
{
{
while (pUrb)
{
}
pEp->cIsocInUrbs = 0;
while (pIsocReq)
{
}
pEp->cbIsocInLandedReqs = 0;
pEp->fInitialized = 0;
}
}
/**
* Close all non-default Endpoints and drains the default pipe.
*
* @param pState The USB device instance.
* @param fDefault Whether to close the default control pipe.
*
* @remarks Requires the device state mutex to be held.
*/
{
for (int i = 1; i < VBOXUSB_MAX_ENDPOINTS; i++)
{
if ( pEp
{
}
}
if (fDefault)
{
if ( pEp
{
}
}
}
/**
* Closes all pipes for a given interface.
*
* @param pState The USB device instance.
* @param bInterface The Interface.
*/
{
LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisCloseInterface pState=%p bInterface=%#x\n", pState, bInterface));
for (int i = 1; i < VBOXUSB_MAX_ENDPOINTS; i++)
{
if ( pEp
{
}
}
}
/**
* Open the pipe for an Endpoint.
*
* @param pState The USB device instance.
* @param pEp The Endpoint.
*
* @returns VBox status code.
*/
{
// LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisOpenPipe pState=%p pEp=%p\n", pState, pEp));
/*
* Make sure the Endpoint isn't open already.
*/
return VINF_SUCCESS;
/*
* Default Endpoint; already opened just copy the pipe handle.
*/
{
return VINF_SUCCESS;
}
/*
* Open the non-default pipe for the Endpoint.
*/
int rc = usb_pipe_open(pState->pDip, &pEp->EpDesc, &pEp->PipePolicy, USB_FLAGS_NOSLEEP, &pEp->pPipe);
if (rc == USB_SUCCESS)
{
/*
* Determine input buffer size for Isoc. IN transfers.
*/
{
/*
* wMaxPacketSize bits 10..0 specifies maximum packet size which can hold 1024 bytes.
* If bits 12..11 is non-zero, cbMax will be more than 1024 and thus the Endpoint is a
* high-bandwidth Endpoint.
*/
if (cbMax <= 1024)
{
/* Buffer 1 second for highspeed and 8 seconds for fullspeed Endpoints. */
}
else
{
/* Buffer about 400 milliseconds of data for highspeed high-bandwidth endpoints. */
}
}
rc = VINF_SUCCESS;
}
else
{
rc = VERR_BAD_PIPE;
}
return rc;
}
/**
* Close the pipe of the Endpoint.
*
* @param pState The USB device instance.
* @param pEp The Endpoint.
*
* @remarks Requires the device state mutex to be held.
*/
{
{
/*
* Default pipe: allow completion of pending requests.
*/
{
usb_pipe_drain_reqs(pState->pDip, pEp->pPipe, 0, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback arg. */);
}
else
{
/*
* Stop Isoc. IN polling if required.
*/
if (pEp->fIsocPolling)
{
pEp->fIsocPolling = false;
}
/*
* Non-default pipe: close it.
*/
LogFlow((DEVICE_NAME ":vboxUSBSolarisClosePipe pipe bmAttributes=%#x bEndpointAddress=%#x\n", pEp->EpDesc.bmAttributes, pEp->EpDesc.bEndpointAddress));
usb_pipe_close(pState->pDip, pEp->pPipe, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback arg. */);
}
/*
* Free the Endpoint data message block and reset pipe handle.
*/
}
}
/**
* Check if any non-default Endpoints are open.
*
* @param pState The USB device instance.
*
* @returns Returns true if any non-default Endpoint is open, otherwise false.
*/
{
for (int i = 1; i < VBOXUSB_MAX_ENDPOINTS; i++)
{
{
LogFlow((DEVICE_NAME ":vboxUSBSolarisIsAnyPipeOpen pState=%p pEp=%p returns true.\n", pState, pEp));
return true;
}
}
return false;
}
/**
* Find the Configuration index for the passed in Configuration value.
*
* @param pState The USB device instance.
* @param uCfgValue The Configuration value.
*
* @returns The configuration index if found, otherwise -1.
*/
{
{
return CfgIndex;
}
return -1;
}
/**
* Allocates and initializes an Isoc. In URB from the ring-3 equivalent.
*
* @param pState The USB device instance.
* @param pUrb The URB to initialize.
* @param pUrbReq Opaque pointer to the complete request.
* @param pMsg Pointer to the allocated request data.
*
* @returns The allocated Isoc. In URB to be used.
*/
{
/*
* Isoc. In URBs are not queued into the Inflight list like every other URBs.
* For now we allocate each URB which gets queued into the respective Endpoint during Xfer.
*/
{
{
}
}
else
LogRel((DEVICE_NAME ":vboxUSBSolarisGetIsocInURB failed to alloc %d bytes.\n", sizeof(vboxusb_urb_t)));
return pUrb;
}
/**
* Queues a URB reusing previously allocated URBs as required.
*
* @param pState The USB device instance.
* @param pUrbReq Opaque pointer to the complete request.
* @param pMsg Pointer to the allocated request data.
*
* @returns The allocated URB to be used.
*/
LOCAL vboxusb_urb_t *vboxUSBSolarisQueueURB(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, mblk_t *pMsg)
{
/*
* Discard oldest queued URB if we've queued max URBs and none of them have completed.
*/
{
{
{
}
}
}
if ( !pUrb
|| ( pUrb
{
if (RT_UNLIKELY(!pUrb))
{
return NULL;
}
}
else
{
/*
* Remove from head and move to tail so that when several URBs are reaped continuously we get to use
* up each one free 'head'.
*/
}
++pState->cInflightUrbs;
{
{
}
}
return pUrb;
}
/**
* Dequeues a completed URB into the landed list and informs user-land.
*
* @param pUrb The URB to move.
* @param URBStatus The Solaris URB completion code.
*
* @remarks All pipes could be closed at this point (e.g. Device disconnected during inflight URBs)
*/
{
{
/*
* Remove it from the inflight list & move it to landed list.
*/
--pState->cInflightUrbs;
}
else
{
}
}
/**
* Concatenates a chain message block into a single message block if possible.
*
* @param pUrb The URB to move.
*/
{
/*
* Concatenate the whole message rather than doing a chained copy while reaping.
*/
{
{
}
}
}
/**
* User process poll wake up wrapper for asynchronous URB completion.
*
* @param pState The USB device instance.
* @remarks Requires the device state mutex to be held!!
*/
{
{
}
}
/**
* User process poll wake up wrapper for hotplug events.
*
* @param pState The USB device instance.
* @remarks Requires the device state mutex to be held.
*/
{
{
}
}
/**
* Perform a Control Xfer.
*
* @param pState The USB device instance.
* @param pEp The Endpoint for the Xfer.
* @param pUrb The VBox USB URB.
*
* @returns VBox status code.
* @remarks Any errors, the caller should free pUrb->pMsg.
*/
{
LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisCtrlXfer pState=%p pEp=%p pUrb=%p enmDir=%d cbData=%d\n", pState, pEp, pUrb, pUrb->enmDir, pUrb->cbDataR3));
/*
* Solaris USBA gives us garbage and incorrect message lengths making it impossible to use
* pre-allocated control messages. The allocation of "ctrl_data" is not documented well.
*/
/*
* Allocate a wrapper request.
*/
int rc = VINF_SUCCESS;
{
/*
* Initialize the Ctrl Xfer Header.
*/
&& cbData > 0)
{
}
/*
* Initialize callbacks and timeouts.
*/
pReq->ctrl_attributes = USB_ATTRS_AUTOCLEARING | (pUrb->enmDir == VUSBDIRECTION_IN ? USB_ATTRS_SHORT_XFER_OK : 0);
/*
* Submit the request.
*/
return VINF_SUCCESS;
else
{
}
}
else
{
rc = VERR_NO_MEMORY;
}
return rc;
}
/**
* Completion/Exception callback for Control Xfers.
*
* @param pPipe The Ctrl pipe handle.
* @param pReq The Ctrl request.
*/
{
{
/*
* Funky stuff: We need to reconstruct the header for control transfers.
* Let us chain along the data and while we dequeue the URB we attempt to
* concatenate the entire message there.
*/
{
#if defined(DEBUG_ramshankar)
{
LogFlow((DEVICE_NAME ":vboxUSBSolarisCtrlXferCompleted prepended header rc=%d cbData=%d.\n", pReq->ctrl_completion_reason,
}
#endif
/*
* Update the URB and move to landed list for reaping.
*/
return;
}
else
LogRel((DEVICE_NAME ":vboxUSBSolarisCtrlXferCompleted failed to alloc %d bytes for Setup Header.\n", sizeof(VUSBSETUP)));
}
else
}
/**
* Perform a Bulk Xfer.
*
* @param pState The USB device instance.
* @param pEp The Endpoint for the Xfer.
* @param pUrb The VBox USB URB.
*
* @returns VBox status code.
* @remarks Any errors, the caller should free pUrb->pMsg.
*/
{
LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisBulkXfer pState=%p pEp=%p pUrb=%p enmDir=%d cbData=%d\n", pState, pEp, pUrb, pUrb->enmDir, pUrb->cbDataR3));
/*
* Allocate a wrapper request.
*/
int rc = VINF_SUCCESS;
usb_bulk_req_t *pReq = usb_alloc_bulk_req(pState->pDip, pUrb->enmDir == VUSBDIRECTION_IN ? pUrb->cbDataR3 : 0, USB_FLAGS_NOSLEEP);
{
/*
* Initialize Bulk Xfer, callbacks and timeouts.
*/
pReq->bulk_attributes = USB_ATTRS_AUTOCLEARING | (pUrb->enmDir == VUSBDIRECTION_IN ? USB_ATTRS_SHORT_XFER_OK : 0);
/* Don't obtain state lock here, we're just reading unchanging data... */
{
LogRel((DEVICE_NAME ":vboxUSBSolarisBulkXfer requesting %d bytes when only %d bytes supported by device\n",
}
/*
* Submit the request.
*/
return VINF_SUCCESS;
else
{
LogRel((DEVICE_NAME ":vboxUSBSolarisBulkXfer usb_pipe_bulk_xfer enmDir=%#x Ep=%#x failed! rc=%d\n", pUrb->enmDir, pUrb->bEndpoint, rc));
}
}
else
{
rc = VERR_NO_MEMORY;
}
return rc;
}
/**
* Completion/Exception callback for Bulk Xfers.
*
* @param pPipe The Bulk pipe handle.
* @param pReq The Bulk request.
*/
{
{
{
else
{
{
}
}
/*
* Update the URB and move to tail for reaping.
*/
return;
}
else
LogRel((DEVICE_NAME ":vboxUSBSolarisBulkXferCompleted Extreme error! private request data missing.\n"));
}
else
}
/**
* Perform an Interrupt Xfer.
*
* @param pState The USB device instance.
* @param pEp The Endpoint for the Xfer.
* @param pUrb The VBox USB URB.
*
* @returns VBox status code.
* @remarks Any errors, the caller should free pUrb->pMsg.
*/
{
LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisIntrXfer pState=%p pEp=%p pUrb=%p enmDir=%d cbData=%d\n", pState, pEp, pUrb, pUrb->enmDir, pUrb->cbDataR3));
int rc = VINF_SUCCESS;
{
/*
* Initialize Intr Xfer, callbacks & timeouts.
*/
{
}
else
{
}
/*
* Submit the request.
*/
return VINF_SUCCESS;
else
{
}
}
else
{
rc = VERR_NO_MEMORY;
}
return rc;
}
/**
* Completion/Exception callback for Intr Xfers.
*
* @param pPipe The Intr pipe handle.
* @param pReq The Intr request.
*/
{
{
{
else
{
{
}
}
LogFlow((DEVICE_NAME ":vboxUSBSolarisIntrXferCompleted rc=%d pMsg=%p enmDir=%#x\n", pReq->intr_completion_reason, pUrb->pMsg,
/*
* Update the URB and move to landed list for reaping.
*/
return;
}
else
LogRel((DEVICE_NAME ":vboxUSBSolarisIntrXferCompleted Extreme error! private request data missing.\n"));
}
else
}
/**
* Perform an Isochronous Xfer.
*
* @param pState The USB device instance.
* @param pEp The Endpoint for the Xfer.
* @param pUrb The VBox USB URB.
*
* @returns VBox status code.
* @remarks Any errors, the caller should free pUrb->pMsg.
*/
{
// LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisIsocXfer pState=%p pEp=%p pUrb=%p\n", pState, pEp, pUrb));
/*
* For Isoc. IN transfers we perform one request and USBA polls the device continuously
* and supplies our Xfer callback with input data. We cannot perform one-shot Isoc. In transfers.
*/
size_t cbData = (pUrb->enmDir == VUSBDIRECTION_IN ? pUrb->cIsocPkts * pUrb->aIsocPkts[0].cbPkt : 0);
{
if (pEp->fIsocPolling)
{
/*
* Queue a maximum of cbMaxIsocData bytes, else fail.
*/
{
LogFlow((DEVICE_NAME ":vboxUSBSolarisIsocXfer Max Isoc. data %d bytes queued\n", pEp->cbMaxIsocData));
return VERR_TOO_MUCH_DATA;
}
++pEp->cIsocInUrbs;
return VINF_SUCCESS;
}
}
int rc = VINF_SUCCESS;
usb_isoc_req_t *pReq = usb_alloc_isoc_req(pState->pDip, pUrb->cIsocPkts, cbData, USB_FLAGS_NOSLEEP);
LogFlow((DEVICE_NAME ":vboxUSBSolarisIsocXfer enmDir=%#x cIsocPkts=%d aIsocPkts[0]=%d cbDataR3=%d\n", pUrb->enmDir,
{
/*
* Initialize Isoc Xfer, callbacks & timeouts.
*/
{
}
else
{
pReq->isoc_attributes = USB_ATTRS_AUTOCLEARING | USB_ATTRS_ISOC_XFER_ASAP | USB_ATTRS_SHORT_XFER_OK;
}
/*
* Submit the request.
*/
{
{
/*
* Add the first Isoc. IN URB to the queue as well.
*/
++pEp->cIsocInUrbs;
pEp->fIsocPolling = true;
}
return VINF_SUCCESS;
}
else
{
{
if (pIsocFailedUrb)
{
--pEp->cIsocInUrbs;
}
pEp->fIsocPolling = false;
}
}
}
else
{
LogRel((DEVICE_NAME ":vboxUSBSolarisIsocXfer failed to alloc isoc req for %d packets\n", pUrb->cIsocPkts));
rc = VERR_NO_MEMORY;
}
return rc;
}
/**
* Completion/Exception callback for Isoc IN Xfers.
*
* @param pPipe The Intr pipe handle.
* @param pReq The Intr request.
*
* @remarks Completion callback executes in interrupt context!
*/
{
// LogFlowFunc((DEVICE_NAME ":vboxUSBSolarisIsocInXferCompleted pPipe=%p pReq=%p\n", pPipe, pReq));
{
if ( pEp
{
#if 0
/*
* Stop polling if all packets failed.
*/
{
pEp->fIsocPolling = false;
}
#endif
{
LogFlow((DEVICE_NAME ":vboxUSBSolarisIsocInXferCompleted cIsocInUrbs=%d cbIsocInLandedReqs=%d\n", pEp->cIsocInUrbs, pEp->cbIsocInLandedReqs));
/*
* If there are waiting URBs, satisfy the oldest one.
*/
if ( pEp->cIsocInUrbs > 0
&& pEp->cbIsocInLandedReqs == 0)
{
{
--pEp->cIsocInUrbs;
#if 0
for (unsigned i = 0; i < pReq->isoc_pkts_count; i++)
{
}
#else
#endif
/*
* Move to landed list
*/
}
else
{
/* Huh!? cIsocInUrbs is wrong then! Should never happen unless we decide to decrement cIsocInUrbs in Reap time */
pEp->cIsocInUrbs = 0;
}
return;
}
#if 0
/*
* If the maximum buffer size is reached, discard the oldest data.
*/
{
{
}
}
/*
* Buffer incoming data if the guest has not yet queued any Input URBs.
*/
{
#if 0
for (unsigned i = 0; i < pReq->isoc_pkts_count; i++)
{
pIsocReq->aIsocPkts[i].enmStatus = vboxUSBSolarisGetUrbStatus(pReq->isoc_pkt_descr[i].isoc_pkt_status);
}
#else
bcopy(pReq->isoc_pkt_descr, pIsocReq->aIsocPkts, pReq->isoc_pkts_count * sizeof(VUSBISOC_PKT_DESC));
#endif
}
else
{
LogRel((DEVICE_NAME ":vboxUSBSolarisIsocInXferCompleted failed to alloc %d bytes for Isoc. queueing\n",
sizeof(vboxusb_isoc_req_t)));
}
/*
* Drain the input URB buffer with the device buffer, queueing them with the landed URBs.
*/
while (pEp->cIsocInUrbs)
{
if (RT_UNLIKELY(!pUrb))
break;
if (!pBuffReq)
{
break;
}
--pEp->cIsocInUrbs;
#if 0
{
}
#else
#endif
/*
* Move to landed list
*/
}
#endif
return;
}
else
}
else
}
else
}
/**
* Exception callback for Isoc IN Xfers.
*
* @param pPipe The Intr pipe handle.
* @param pReq The Intr request.
* @remarks Completion callback executes in interrupt context!
*/
{
if (RT_UNLIKELY(!pState))
{
return;
}
if (RT_UNLIKELY(!pEp))
{
return;
}
switch(pReq->isoc_completion_reason)
{
case USB_CR_NO_RESOURCES:
{
/*
* Resubmit the request in case the original request did not complete due to
* immediately unavailable requests
*/
LogFlow((DEVICE_NAME ":vboxUSBSolarisIsocInXferError resubmitted Isoc. IN request due to immediately unavailable resources.\n"));
return;
}
case USB_CR_PIPE_CLOSING:
case USB_CR_STOPPED_POLLING:
case USB_CR_PIPE_RESET:
{
pEp->fIsocPolling = false;
break;
}
default:
{
LogFlow((DEVICE_NAME ":vboxUSBSolarisIsocInXferError stopping Isoc. In. polling due to rc=%d\n", pReq->isoc_completion_reason));
pEp->fIsocPolling = false;
break;
}
}
/*
* Dequeue i.e. delete the last queued Isoc In. URB. as failed.
*/
if (pUrb)
{
--pEp->cIsocInUrbs;
}
}
/**
* Completion/Exception callback for Isoc OUT Xfers.
*
* @param pPipe The Intr pipe handle.
* @param pReq The Intr request.
* @remarks Completion callback executes in interrupt context!
*/
{
{
{
for (int i = 0; i < pReq->isoc_pkts_count; i++)
{
}
LogFlow((DEVICE_NAME ":vboxUSBSolarisIsocOutXferCompleted cIsocPkts=%d cbData=%d cbActPkt=%d\n", pUrb->cIsocPkts, pUrb->cbDataR3, cbActPkt));
{
{
}
}
/*
* Update the URB and move to landed list for reaping.
*/
return;
}
else
LogFlow((DEVICE_NAME ":vboxUSBSolarisIsocOutXferCompleted missing private data!?! Dropping OUT pUrb.\n"));
}
else
}
/**
* Callback when the device gets disconnected.
*
* @param pDip The module structure instance.
*
* @returns Solaris USB error code.
*/
{
{
/*
* Serialize access: exclusive access to the state.
*/
return USB_SUCCESS;
}
return USB_FAILURE;
}
/**
* Callback when the device gets reconnected.
*
* @param pDip The module structure instance.
*
* @returns Solaris USB error code.
*/
{
{
return USB_SUCCESS;
}
return USB_FAILURE;
}
/**
* Restore device state after a reconnect or resume.
*
* @param pState The USB device instance.
*/
{
/*
* Raise device power.
*/
/*
* Check if the same device is resumed/reconnected.
*/
NULL, /* log handle */
USB_LOG_L2, /* log level */
-1, /* log mask */
USB_CHK_ALL, /* check level */
NULL); /* device string */
if (rc != USB_SUCCESS)
{
/* Do we need to inform userland here? */
return;
}
/*
* Serialize access to not race with other PM functions.
*/
}
/**
* Restore device state after a reconnect or resume.
*
* @param pState The USB device instance.
*
* @returns VBox status code.
*/
{
int rc = VERR_VUSB_DEVICE_IS_SUSPENDED;
{
case USB_DEV_SUSPENDED:
{
break;
}
case USB_DEV_ONLINE:
case USB_DEV_DISCONNECTED:
case USB_DEV_PWRED_DOWN:
{
/*
* Drain pending URBs.
*/
for (int i = 0; i < VBOXUSB_DRAIN_TIME; i++)
{
break;
}
/*
* Deny suspend if we still have pending URBs.
*/
if (pState->cInflightUrbs > 0)
{
return VERR_RESOURCE_BUSY;
}
pState->cInflightUrbs = 0;
/*
* Close all pipes including the default pipe.
*/
return VINF_SUCCESS;
}
}
return rc;
}
/**
* Restore device state after a reconnect or resume.
*
* @param pState The USB device instance.
*/
{
return vboxUSBSolarisDeviceRestore(pState);
}
/**
* Flag the PM component as busy so the system will not manage it's power.
*
* @param pState The USB device instance.
*/
{
{
if (rc != DDI_SUCCESS)
{
}
}
else
}
/**
* Flag the PM component as idle so its power managed by the system.
*
* @param pState The USB device instance.
*/
{
{
if (rc == DDI_SUCCESS)
{
}
else
}
}