VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxUSB/solaris/VBoxUSB-solaris.c@ 93912

Last change on this file since 93912 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 140.7 KB
Line 
1/* $Id: VBoxUSB-solaris.c 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * VirtualBox USB Client Driver, Solaris Hosts.
4 */
5
6/*
7 * Copyright (C) 2008-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP LOG_GROUP_USB_DRV
32#include <VBox/version.h>
33#include <VBox/log.h>
34#include <VBox/err.h>
35#include <VBox/cdefs.h>
36#include <VBox/sup.h>
37#include <VBox/usblib-solaris.h>
38
39#include <iprt/assert.h>
40#include <iprt/initterm.h>
41#include <iprt/semaphore.h>
42#include <iprt/mem.h>
43#include <iprt/process.h>
44#include <iprt/string.h>
45#include <iprt/path.h>
46#include <iprt/thread.h>
47#include <iprt/dbg.h>
48
49#define USBDRV_MAJOR_VER 2
50#define USBDRV_MINOR_VER 0
51#include <sys/usb/usba.h>
52#include <sys/strsun.h>
53#include "usbai_private.h"
54
55
56/*********************************************************************************************************************************
57* Defined Constants And Macros *
58*********************************************************************************************************************************/
59/** The module name. */
60#define DEVICE_NAME "vboxusb"
61/** The module description as seen in 'modinfo'. */
62#define DEVICE_DESC_DRV "VirtualBox USB"
63
64/** -=-=-=-=-=-=- Standard Specifics -=-=-=-=-=-=- */
65/** Max. supported endpoints. */
66#define VBOXUSB_MAX_ENDPOINTS 32
67/** Size of USB Ctrl Xfer Header in bytes. */
68#define VBOXUSB_CTRL_XFER_SIZE 8
69/**
70 * USB2.0 (Sec. 9-13) Bits 10..0 is the max packet size; for high speed Isoc/Intr, bits 12..11 is
71 * number of additional transaction opportunities per microframe.
72 */
73#define VBOXUSB_PKT_SIZE(pkt) (pkt & 0x07FF) * (1 + ((pkt >> 11) & 3))
74/** Endpoint Xfer Type. */
75#define VBOXUSB_XFER_TYPE(endp) ((endp)->EpDesc.bmAttributes & USB_EP_ATTR_MASK)
76/** Endpoint Xfer Direction. */
77#define VBOXUSB_XFER_DIR(endp) ((endp)->EpDesc.bEndpointAddress & USB_EP_DIR_IN)
78/** Create an Endpoint index from an Endpoint address. */
79#define VBOXUSB_GET_EP_INDEX(epaddr) (((epaddr) & USB_EP_NUM_MASK) + \
80 (((epaddr) & USB_EP_DIR_MASK) ? 16 : 0))
81
82
83/** -=-=-=-=-=-=- Tunable Parameters -=-=-=-=-=-=- */
84/** Time to wait while draining inflight UBRs on suspend, in seconds. */
85#define VBOXUSB_DRAIN_TIME 20
86/** Ctrl Xfer timeout in seconds. */
87#define VBOXUSB_CTRL_XFER_TIMEOUT 15
88/** Maximum URB queue length. */
89#define VBOXUSB_URB_QUEUE_SIZE 512
90/** Maximum asynchronous requests per pipe. */
91#define VBOXUSB_MAX_PIPE_ASYNC_REQS 2
92
93/** For enabling global symbols while debugging. **/
94#if defined(DEBUG_ramshankar)
95# define LOCAL
96#else
97# define LOCAL static
98#endif
99
100
101/*********************************************************************************************************************************
102* Kernel Entry Hooks *
103*********************************************************************************************************************************/
104int VBoxUSBSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred);
105int VBoxUSBSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred);
106int VBoxUSBSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred);
107int VBoxUSBSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred);
108int VBoxUSBSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal);
109int VBoxUSBSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead);
110int VBoxUSBSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
111int VBoxUSBSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
112int VBoxUSBSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
113int VBoxUSBSolarisPower(dev_info_t *pDip, int Component, int Level);
114
115
116/*********************************************************************************************************************************
117* Structures and Typedefs *
118*********************************************************************************************************************************/
119/**
120 * cb_ops: for drivers that support char/block entry points
121 */
122static struct cb_ops g_VBoxUSBSolarisCbOps =
123{
124 VBoxUSBSolarisOpen,
125 VBoxUSBSolarisClose,
126 nodev, /* b strategy */
127 nodev, /* b dump */
128 nodev, /* b print */
129 VBoxUSBSolarisRead,
130 VBoxUSBSolarisWrite,
131 VBoxUSBSolarisIOCtl,
132 nodev, /* c devmap */
133 nodev, /* c mmap */
134 nodev, /* c segmap */
135 VBoxUSBSolarisPoll,
136 ddi_prop_op, /* property ops */
137 NULL, /* streamtab */
138 D_NEW | D_MP, /* compat. flag */
139 CB_REV, /* revision */
140 nodev, /* c aread */
141 nodev /* c awrite */
142};
143
144/**
145 * dev_ops: for driver device operations
146 */
147static struct dev_ops g_VBoxUSBSolarisDevOps =
148{
149 DEVO_REV, /* driver build revision */
150 0, /* ref count */
151 VBoxUSBSolarisGetInfo,
152 nulldev, /* identify */
153 nulldev, /* probe */
154 VBoxUSBSolarisAttach,
155 VBoxUSBSolarisDetach,
156 nodev, /* reset */
157 &g_VBoxUSBSolarisCbOps,
158 NULL, /* bus ops */
159 VBoxUSBSolarisPower,
160 ddi_quiesce_not_needed
161};
162
163/**
164 * modldrv: export driver specifics to the kernel
165 */
166static struct modldrv g_VBoxUSBSolarisModule =
167{
168 &mod_driverops, /* extern from kernel */
169 DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
170 &g_VBoxUSBSolarisDevOps
171};
172
173/**
174 * modlinkage: export install/remove/info to the kernel
175 */
176static struct modlinkage g_VBoxUSBSolarisModLinkage =
177{
178 MODREV_1,
179 &g_VBoxUSBSolarisModule,
180 NULL,
181};
182
183/**
184 * vboxusb_ep_t: Endpoint structure with info. for managing an endpoint.
185 */
186typedef struct vboxusb_ep_t
187{
188 bool fInitialized; /* Whether this Endpoint is initialized */
189 usb_ep_descr_t EpDesc; /* Endpoint descriptor */
190 usb_pipe_handle_t pPipe; /* Endpoint pipe handle */
191 usb_pipe_policy_t PipePolicy; /* Endpoint policy */
192 bool fIsocPolling; /* Whether Isoc. IN polling is enabled */
193 list_t hIsocInUrbs; /* Isoc. IN inflight URBs */
194 uint16_t cIsocInUrbs; /* Number of Isoc. IN inflight URBs */
195 list_t hIsocInLandedReqs; /* Isoc. IN landed requests */
196 uint16_t cbIsocInLandedReqs; /* Cumulative size of landed Isoc. IN requests */
197 size_t cbMaxIsocData; /* Maximum size of Isoc. IN landed buffer */
198} vboxusb_ep_t;
199
200/**
201 * vboxusb_isoc_req_t: Isoc IN. requests queued from device till they are reaped.
202 */
203typedef struct vboxusb_isoc_req_t
204{
205 mblk_t *pMsg; /* Pointer to the data buffer */
206 uint32_t cIsocPkts; /* Number of Isoc pkts */
207 VUSBISOC_PKT_DESC aIsocPkts[8]; /* Array of Isoc pkt descriptors */
208 list_node_t hListLink;
209} vboxusb_isoc_req_t;
210
211/**
212 * VBOXUSB_URB_STATE: Internal USB URB state.
213 */
214typedef enum VBOXUSB_URB_STATE
215{
216 VBOXUSB_URB_STATE_FREE = 0x00,
217 VBOXUSB_URB_STATE_INFLIGHT = 0x04,
218 VBOXUSB_URB_STATE_LANDED = 0x08
219} VBOXUSB_URB_STATE;
220
221/**
222 * vboxusb_urb_t: kernel URB representation.
223 */
224typedef struct vboxusb_urb_t
225{
226 void *pvUrbR3; /* Userspace URB address (untouched, returned while reaping) */
227 uint8_t bEndpoint; /* Endpoint address */
228 VUSBXFERTYPE enmType; /* Xfer type */
229 VUSBDIRECTION enmDir; /* Xfer direction */
230 VUSBSTATUS enmStatus; /* URB status */
231 bool fShortOk; /* Whether receiving less data than requested is acceptable */
232 RTR3PTR pvDataR3; /* Userspace address of the original data buffer */
233 size_t cbDataR3; /* Size of the data buffer */
234 mblk_t *pMsg; /* Pointer to the data buffer */
235 uint32_t cIsocPkts; /* Number of Isoc pkts */
236 VUSBISOC_PKT_DESC aIsocPkts[8]; /* Array of Isoc pkt descriptors */
237 VBOXUSB_URB_STATE enmState; /* URB state (free/in-flight/landed). */
238 struct vboxusb_state_t *pState; /* Pointer to the device instance */
239 list_node_t hListLink; /* List node link handle */
240} vboxusb_urb_t;
241
242/**
243 * vboxusb_power_t: Per Device Power Management info.
244 */
245typedef struct vboxusb_power_t
246{
247 uint_t PowerStates; /* Bit mask of the power states */
248 int PowerBusy; /* Busy reference counter */
249 bool fPowerWakeup; /* Whether remote power wakeup is enabled */
250 bool fPowerRaise; /* Whether to raise the power level */
251 uint8_t PowerLevel; /* Current power level */
252} vboxusb_power_t;
253
254/**
255 * vboxusb_state_t: Per Device instance state info.
256 */
257typedef struct vboxusb_state_t
258{
259 dev_info_t *pDip; /* Per instance device info. */
260 usb_client_dev_data_t *pDevDesc; /* Parsed & complete device descriptor */
261 uint8_t DevState; /* Current USB Device state */
262 bool fDefaultPipeOpen; /* Whether the device (default control pipe) is closed */
263 bool fPollPending; /* Whether the userland process' poll is pending */
264 kmutex_t Mtx; /* Mutex state protection */
265 usb_serialization_t StateMulti; /* State serialization */
266 size_t cbMaxBulkXfer; /* Maximum bulk xfer size */
267 vboxusb_ep_t aEps[VBOXUSB_MAX_ENDPOINTS]; /* Array of all endpoints structures */
268 list_t hFreeUrbs; /* List of free URBs */
269 list_t hInflightUrbs; /* List of inflight URBs */
270 list_t hLandedUrbs; /* List of landed URBs */
271 uint32_t cFreeUrbs; /* Number of free URBs */
272 uint32_t cInflightUrbs; /* Number of inflight URBs */
273 uint32_t cLandedUrbs; /* Number of landed URBs */
274 pollhead_t PollHead; /* Handle to pollhead for waking polling processes */
275 RTPROCESS Process; /* The process (pid) of the user session */
276 VBOXUSBREQ_CLIENT_INFO ClientInfo; /* Registration data */
277 vboxusb_power_t *pPower; /* Power Management */
278 char szMfg[255]; /* Parsed manufacturer string */
279 char szProduct[255]; /* Parsed product string */
280} vboxusb_state_t;
281AssertCompileMemberSize(vboxusb_state_t, szMfg, USB_MAXSTRINGLEN);
282AssertCompileMemberSize(vboxusb_state_t, szProduct, USB_MAXSTRINGLEN);
283
284
285/*********************************************************************************************************************************
286* Internal Functions *
287*********************************************************************************************************************************/
288LOCAL int vboxUsbSolarisInitEp(vboxusb_state_t *pState, usb_ep_data_t *pEpData);
289LOCAL int vboxUsbSolarisInitEpsForCfg(vboxusb_state_t *pState);
290LOCAL int vboxUsbSolarisInitEpsForIfAlt(vboxusb_state_t *pState, uint8_t bIf, uint8_t bAlt);
291LOCAL void vboxUsbSolarisDestroyAllEps(vboxusb_state_t *pState);
292LOCAL void vboxUsbSolarisDestroyEp(vboxusb_state_t *pState, vboxusb_ep_t *pEp);
293LOCAL void vboxUsbSolarisCloseAllPipes(vboxusb_state_t *pState, bool fControlPipe);
294LOCAL int vboxUsbSolarisOpenPipe(vboxusb_state_t *pState, vboxusb_ep_t *pEp);
295LOCAL void vboxUsbSolarisClosePipe(vboxusb_state_t *pState, vboxusb_ep_t *pEp);
296LOCAL int vboxUsbSolarisCtrlXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb);
297LOCAL void vboxUsbSolarisCtrlXferCompleted(usb_pipe_handle_t pPipe, usb_ctrl_req_t *pReq);
298LOCAL int vboxUsbSolarisBulkXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *purb);
299LOCAL void vboxUsbSolarisBulkXferCompleted(usb_pipe_handle_t pPipe, usb_bulk_req_t *pReq);
300LOCAL int vboxUsbSolarisIntrXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb);
301LOCAL void vboxUsbSolarisIntrXferCompleted(usb_pipe_handle_t pPipe, usb_intr_req_t *pReq);
302LOCAL int vboxUsbSolarisIsocXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb);
303LOCAL void vboxUsbSolarisIsocInXferCompleted(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq);
304LOCAL void vboxUsbSolarisIsocInXferError(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq);
305LOCAL void vboxUsbSolarisIsocOutXferCompleted(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq);
306LOCAL vboxusb_urb_t *vboxUsbSolarisGetIsocInUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq);
307LOCAL vboxusb_urb_t *vboxUsbSolarisQueueUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, mblk_t *pMsg);
308LOCAL VUSBSTATUS vboxUsbSolarisGetUrbStatus(usb_cr_t Status);
309LOCAL void vboxUsbSolarisConcatMsg(vboxusb_urb_t *pUrb);
310LOCAL void vboxUsbSolarisDeQueueUrb(vboxusb_urb_t *pUrb, int URBStatus);
311LOCAL void vboxUsbSolarisNotifyComplete(vboxusb_state_t *pState);
312LOCAL int vboxUsbSolarisProcessIOCtl(int iFunction, void *pvState, int Mode, PVBOXUSBREQ pUSBReq, void *pvBuf,
313 size_t *pcbDataOut);
314LOCAL bool vboxUsbSolarisIsUSBDevice(dev_info_t *pDip);
315
316/** @name Device Operation Hooks
317 * @{ */
318LOCAL int vboxUsbSolarisSendUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, int Mode);
319LOCAL int vboxUsbSolarisReapUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, int Mode);
320LOCAL int vboxUsbSolarisClearEndPoint(vboxusb_state_t *pState, uint8_t bEndpoint);
321LOCAL int vboxUsbSolarisSetConfig(vboxusb_state_t *pState, uint8_t bCfgValue);
322LOCAL int vboxUsbSolarisGetConfig(vboxusb_state_t *pState, uint8_t *pCfgValue);
323LOCAL int vboxUsbSolarisSetInterface(vboxusb_state_t *pState, uint8_t bIf, uint8_t bAlt);
324LOCAL int vboxUsbSolarisCloseDevice(vboxusb_state_t *pState, VBOXUSB_RESET_LEVEL enmReset);
325LOCAL int vboxUsbSolarisAbortPipe(vboxusb_state_t *pState, uint8_t bEndpoint);
326LOCAL int vboxUsbSolarisGetConfigIndex(vboxusb_state_t *pState, uint_t bCfgValue);
327/** @} */
328
329/** @name Hotplug & Power Management Hooks
330 * @{ */
331LOCAL void vboxUsbSolarisNotifyUnplug(vboxusb_state_t *pState);
332LOCAL int vboxUsbSolarisDeviceDisconnected(dev_info_t *pDip);
333LOCAL int vboxUsbSolarisDeviceReconnected(dev_info_t *pDip);
334
335LOCAL int vboxUsbSolarisInitPower(vboxusb_state_t *pState);
336LOCAL void vboxUsbSolarisDestroyPower(vboxusb_state_t *pState);
337LOCAL int vboxUsbSolarisDeviceSuspend(vboxusb_state_t *pState);
338LOCAL void vboxUsbSolarisDeviceResume(vboxusb_state_t *pState);
339LOCAL void vboxUsbSolarisDeviceRestore(vboxusb_state_t *pState);
340LOCAL void vboxUsbSolarisPowerBusy(vboxusb_state_t *pState);
341LOCAL void vboxUsbSolarisPowerIdle(vboxusb_state_t *pState);
342/** @} */
343
344/** @name Monitor Hooks
345 * @{ */
346int VBoxUSBMonSolarisRegisterClient(dev_info_t *pClientDip, PVBOXUSB_CLIENT_INFO pClientInfo);
347int VBoxUSBMonSolarisUnregisterClient(dev_info_t *pClientDip);
348/** @} */
349
350/** @name Callbacks from Monitor
351 * @{ */
352LOCAL int vboxUsbSolarisSetConsumerCredentials(RTPROCESS Process, int Instance, void *pvReserved);
353/** @} */
354
355
356/*********************************************************************************************************************************
357* Global Variables *
358*********************************************************************************************************************************/
359/** Global list of all device instances. */
360static void *g_pVBoxUSBSolarisState;
361
362/** The default endpoint descriptor */
363static usb_ep_descr_t g_VBoxUSBSolarisDefaultEpDesc = { 7, 5, 0, USB_EP_ATTR_CONTROL, 8, 0 };
364
365/** Size of the usb_ep_data_t struct (used to index into data). */
366static size_t g_cbUsbEpData = ~0UL;
367
368/** The offset of usb_ep_data_t::ep_desc. */
369static size_t g_offUsbEpDataDescr = ~0UL;
370
371
372#ifdef LOG_ENABLED
373/**
374 * Gets the description of an Endpoint's transfer type.
375 *
376 * @param pEp The Endpoint.
377 * @returns The type of the Endpoint.
378 */
379static const char *vboxUsbSolarisEpType(vboxusb_ep_t *pEp)
380{
381 uint8_t uType = VBOXUSB_XFER_TYPE(pEp);
382 switch (uType)
383 {
384 case 0: return "CTRL";
385 case 1: return "ISOC";
386 case 2: return "BULK";
387 default: return "INTR";
388 }
389}
390
391
392/**
393 * Gets the description of an Endpoint's direction.
394 *
395 * @param pEp The Endpoint.
396 * @returns The direction of the Endpoint.
397 */
398static const char *vboxUsbSolarisEpDir(vboxusb_ep_t *pEp)
399{
400 return VBOXUSB_XFER_DIR(pEp) == USB_EP_DIR_IN ? "IN " : "OUT";
401}
402#endif
403
404
405/**
406 * Caches device strings from the parsed device descriptors.
407 *
408 * @param pState The USB device instance.
409 *
410 * @remarks Must only be called after usb_get_dev_data().
411 */
412static void vboxUsbSolarisGetDeviceStrings(vboxusb_state_t *pState)
413{
414 AssertReturnVoid(pState);
415 AssertReturnVoid(pState->pDevDesc);
416
417 if (pState->pDevDesc->dev_product)
418 strlcpy(&pState->szMfg[0], pState->pDevDesc->dev_mfg, sizeof(pState->szMfg));
419 else
420 strlcpy(&pState->szMfg[0], "<Unknown Manufacturer>", sizeof(pState->szMfg));
421
422 if (pState->pDevDesc->dev_product)
423 strlcpy(&pState->szProduct[0], pState->pDevDesc->dev_product, sizeof(pState->szProduct));
424 else
425 strlcpy(&pState->szProduct[0], "<Unnamed USB device>", sizeof(pState->szProduct));
426}
427
428
429/**
430 * Queries the necessary symbols at runtime.
431 *
432 * @returns VBox status code.
433 */
434LOCAL int vboxUsbSolarisQuerySymbols(void)
435{
436 RTDBGKRNLINFO hKrnlDbgInfo;
437 int rc = RTR0DbgKrnlInfoOpen(&hKrnlDbgInfo, 0 /* fFlags */);
438 if (RT_SUCCESS(rc))
439 {
440 /*
441 * Query and sanitize the size of usb_ep_data_t struct.
442 */
443 size_t cbPrevUsbEpData = g_cbUsbEpData;
444 rc = RTR0DbgKrnlInfoQuerySize(hKrnlDbgInfo, "usba", "usb_ep_data_t", &g_cbUsbEpData);
445 if (RT_FAILURE(rc))
446 {
447 LogRel(("Failed to query size of \"usb_ep_data_t\" in the \"usba\" module, rc=%Rrc\n", rc));
448 return rc;
449 }
450 if (g_cbUsbEpData > _4K)
451 {
452 LogRel(("Size of \"usb_ep_data_t\" (%u bytes) seems implausible, too paranoid to continue\n", g_cbUsbEpData));
453 return VERR_MISMATCH;
454 }
455
456 /*
457 * Query and sanitizie the offset of usb_ep_data_t::ep_descr.
458 */
459 size_t offPrevUsbEpDataDescr = g_offUsbEpDataDescr;
460 rc = RTR0DbgKrnlInfoQueryMember(hKrnlDbgInfo, "usba", "usb_ep_data_t", "ep_descr", &g_offUsbEpDataDescr);
461 if (RT_FAILURE(rc))
462 {
463 LogRel(("Failed to query offset of usb_ep_data_t::ep_descr, rc=%Rrc\n", rc));
464 return rc;
465 }
466 if (g_offUsbEpDataDescr > _4K - sizeof(usb_ep_descr_t))
467 {
468 LogRel(("Offset of \"ep_desrc\" (%u) seems implausible, too paranoid to continue\n", g_offUsbEpDataDescr));
469 return VERR_MISMATCH;
470 }
471
472 /*
473 * Log only when it changes / first time, since _init() seems to be called often (e.g. on failed attaches).
474 * cmn_err, CE_CONT and '!' is used to not show the message on console during boot each time.
475 */
476 if ( cbPrevUsbEpData != g_cbUsbEpData
477 || offPrevUsbEpDataDescr != g_offUsbEpDataDescr)
478 {
479 cmn_err(CE_CONT, "!usba_ep_data_t is %lu bytes\n", g_cbUsbEpData);
480 cmn_err(CE_CONT, "!usba_ep_data_t::ep_descr @ 0x%lx (%ld)\n", g_offUsbEpDataDescr, g_offUsbEpDataDescr);
481 }
482
483 RTR0DbgKrnlInfoRelease(hKrnlDbgInfo);
484 }
485
486 return rc;
487}
488
489
490/**
491 * Kernel entry points
492 */
493int _init(void)
494{
495 LogFunc((DEVICE_NAME ": _init\n"));
496
497 /*
498 * Prevent module autounloading.
499 */
500 modctl_t *pModCtl = mod_getctl(&g_VBoxUSBSolarisModLinkage);
501 if (pModCtl)
502 pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
503 else
504 LogRel((DEVICE_NAME ": _init: failed to disable autounloading!\n"));
505
506 /*
507 * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
508 */
509 int rc = RTR0Init(0);
510 if (RT_SUCCESS(rc))
511 {
512 rc = vboxUsbSolarisQuerySymbols();
513 if (RT_FAILURE(rc))
514 {
515 RTR0Term();
516 return EINVAL;
517 }
518
519 rc = ddi_soft_state_init(&g_pVBoxUSBSolarisState, sizeof(vboxusb_state_t), 4 /* pre-alloc */);
520 if (!rc)
521 {
522 rc = mod_install(&g_VBoxUSBSolarisModLinkage);
523 if (!rc)
524 return rc;
525
526 LogRel((DEVICE_NAME ": _init: mod_install failed! rc=%d\n", rc));
527 ddi_soft_state_fini(&g_pVBoxUSBSolarisState);
528 }
529 else
530 LogRel((DEVICE_NAME ": _init: failed to initialize soft state\n"));
531
532 RTR0Term();
533 }
534 else
535 LogRel((DEVICE_NAME ": _init: RTR0Init failed! rc=%d\n", rc));
536 return RTErrConvertToErrno(rc);
537}
538
539
540int _fini(void)
541{
542 int rc;
543
544 LogFunc((DEVICE_NAME ": _fini\n"));
545
546 rc = mod_remove(&g_VBoxUSBSolarisModLinkage);
547 if (!rc)
548 {
549 ddi_soft_state_fini(&g_pVBoxUSBSolarisState);
550 RTR0Term();
551 }
552
553 return rc;
554}
555
556
557int _info(struct modinfo *pModInfo)
558{
559 LogFunc((DEVICE_NAME ": _info\n"));
560
561 return mod_info(&g_VBoxUSBSolarisModLinkage, pModInfo);
562}
563
564
565/**
566 * Attach entry point, to attach a device to the system or resume it.
567 *
568 * @param pDip The module structure instance.
569 * @param enmCmd Attach type (ddi_attach_cmd_t)
570 *
571 * @returns Solaris error code.
572 */
573int VBoxUSBSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
574{
575 LogFunc((DEVICE_NAME ": VBoxUSBSolarisAttach: pDip=%p enmCmd=%d\n", pDip, enmCmd));
576
577 int rc;
578 int instance = ddi_get_instance(pDip);
579 vboxusb_state_t *pState = NULL;
580
581 switch (enmCmd)
582 {
583 case DDI_ATTACH:
584 {
585 rc = ddi_soft_state_zalloc(g_pVBoxUSBSolarisState, instance);
586 if (rc == DDI_SUCCESS)
587 {
588 pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
589 if (RT_LIKELY(pState))
590 {
591 pState->pDip = pDip;
592 pState->pDevDesc = NULL;
593 pState->fPollPending = false;
594 pState->cInflightUrbs = 0;
595 pState->cFreeUrbs = 0;
596 pState->cLandedUrbs = 0;
597 pState->Process = NIL_RTPROCESS;
598 pState->pPower = NULL;
599 bzero(pState->aEps, sizeof(pState->aEps));
600 list_create(&pState->hFreeUrbs, sizeof(vboxusb_urb_t), offsetof(vboxusb_urb_t, hListLink));
601 list_create(&pState->hInflightUrbs, sizeof(vboxusb_urb_t), offsetof(vboxusb_urb_t, hListLink));
602 list_create(&pState->hLandedUrbs, sizeof(vboxusb_urb_t), offsetof(vboxusb_urb_t, hListLink));
603
604 /*
605 * There is a bug in usb_client_attach() as of Nevada 120 which panics when we bind to
606 * a non-USB device. So check if we are really binding to a USB device or not.
607 */
608 if (vboxUsbSolarisIsUSBDevice(pState->pDip))
609 {
610 /*
611 * Here starts the USB specifics.
612 */
613 rc = usb_client_attach(pState->pDip, USBDRV_VERSION, 0);
614 if (rc == USB_SUCCESS)
615 {
616 pState->fDefaultPipeOpen = true;
617
618 /*
619 * Parse out the entire descriptor.
620 */
621 rc = usb_get_dev_data(pState->pDip, &pState->pDevDesc, USB_PARSE_LVL_ALL, 0 /* Unused */);
622 if (rc == USB_SUCCESS)
623 {
624 /*
625 * Cache some device descriptor strings.
626 */
627 vboxUsbSolarisGetDeviceStrings(pState);
628#ifdef DEBUG_ramshankar
629 usb_print_descr_tree(pState->pDip, pState->pDevDesc);
630#endif
631
632 /*
633 * Initialize state locks.
634 */
635 mutex_init(&pState->Mtx, NULL, MUTEX_DRIVER, pState->pDevDesc->dev_iblock_cookie);
636 pState->StateMulti = usb_init_serialization(pState->pDip, USB_INIT_SER_CHECK_SAME_THREAD);
637
638 /*
639 * Get maximum bulk transfer size supported by the HCD.
640 */
641 rc = usb_pipe_get_max_bulk_transfer_size(pState->pDip, &pState->cbMaxBulkXfer);
642 if (rc == USB_SUCCESS)
643 {
644 Log((DEVICE_NAME ": VBoxUSBSolarisAttach: cbMaxBulkXfer=%d\n", pState->cbMaxBulkXfer));
645
646 /*
647 * Initialize the default endpoint.
648 */
649 rc = vboxUsbSolarisInitEp(pState, NULL /* pEp */);
650 if (RT_SUCCESS(rc))
651 {
652 /*
653 * Set the device state.
654 */
655 pState->DevState = USB_DEV_ONLINE;
656
657 /*
658 * Initialize power management for the device.
659 */
660 rc = vboxUsbSolarisInitPower(pState);
661 if (RT_SUCCESS(rc))
662 {
663 /*
664 * Initialize endpoints for the current config.
665 */
666 rc = vboxUsbSolarisInitEpsForCfg(pState);
667 AssertRC(rc);
668
669 /*
670 * Publish the minor node.
671 */
672 rc = ddi_create_priv_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, 0,
673 "none", "none", 0666);
674 if (RT_LIKELY(rc == DDI_SUCCESS))
675 {
676 /*
677 * Register hotplug callbacks.
678 */
679 rc = usb_register_hotplug_cbs(pState->pDip, &vboxUsbSolarisDeviceDisconnected,
680 &vboxUsbSolarisDeviceReconnected);
681 if (RT_LIKELY(rc == USB_SUCCESS))
682 {
683 /*
684 * Register with our monitor driver.
685 */
686 bzero(&pState->ClientInfo, sizeof(pState->ClientInfo));
687 char szDevicePath[MAXPATHLEN];
688 ddi_pathname(pState->pDip, szDevicePath);
689 RTStrPrintf(pState->ClientInfo.szClientPath,
690 sizeof(pState->ClientInfo.szClientPath),
691 "/devices%s:%s", szDevicePath, DEVICE_NAME);
692 RTStrPrintf(pState->ClientInfo.szDeviceIdent,
693 sizeof(pState->ClientInfo.szDeviceIdent),
694 "%#x:%#x:%d:%s",
695 pState->pDevDesc->dev_descr->idVendor,
696 pState->pDevDesc->dev_descr->idProduct,
697 pState->pDevDesc->dev_descr->bcdDevice, szDevicePath);
698 pState->ClientInfo.Instance = instance;
699 pState->ClientInfo.pfnSetConsumerCredentials = &vboxUsbSolarisSetConsumerCredentials;
700 rc = VBoxUSBMonSolarisRegisterClient(pState->pDip, &pState->ClientInfo);
701 if (RT_SUCCESS(rc))
702 {
703#if 0
704 LogRel((DEVICE_NAME ": Captured %s %s (Ident=%s)\n", pState->szMfg,
705 pState->szProduct, pState->ClientInfo.szDeviceIdent));
706#else
707 /* Until IPRT R0 logging is fixed. See @bugref{6657#c7} */
708 cmn_err(CE_CONT, "Captured %s %s (Ident=%s)\n", pState->szMfg,
709 pState->szProduct, pState->ClientInfo.szDeviceIdent);
710#endif
711 return DDI_SUCCESS;
712 }
713
714 LogRel((DEVICE_NAME ": VBoxUSBMonSolarisRegisterClient failed! rc=%d "
715 "path=%s instance=%d\n", rc, pState->ClientInfo.szClientPath,
716 instance));
717
718 usb_unregister_hotplug_cbs(pState->pDip);
719 }
720 else
721 LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to register hotplug callbacks! rc=%d\n", rc));
722
723 ddi_remove_minor_node(pState->pDip, NULL);
724 }
725 else
726 LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: ddi_create_minor_node failed! rc=%d\n", rc));
727
728 mutex_enter(&pState->Mtx);
729 vboxUsbSolarisDestroyPower(pState);
730 mutex_exit(&pState->Mtx);
731 }
732 else
733 LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to init power management! rc=%d\n", rc));
734 }
735 else
736 LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: vboxUsbSolarisInitEp failed! rc=%d\n", rc));
737 }
738 else
739 LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: usb_pipe_get_max_bulk_transfer_size failed! rc=%d\n", rc));
740
741 usb_fini_serialization(pState->StateMulti);
742 mutex_destroy(&pState->Mtx);
743 usb_free_dev_data(pState->pDip, pState->pDevDesc);
744 }
745 else
746 LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to get device descriptor. rc=%d\n", rc));
747
748 usb_client_detach(pState->pDip, NULL);
749 }
750 else
751 LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: usb_client_attach failed! rc=%d\n", rc));
752 }
753 else
754 {
755 /* This would appear on every boot if it were LogRel() */
756 Log((DEVICE_NAME ": VBoxUSBSolarisAttach: Not a USB device\n"));
757 }
758 }
759 else
760 LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to get soft state\n", sizeof(*pState)));
761
762 ddi_soft_state_free(g_pVBoxUSBSolarisState, instance);
763 }
764 else
765 LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: Failed to alloc soft state. rc=%d\n", rc));
766
767 return DDI_FAILURE;
768 }
769
770 case DDI_RESUME:
771 {
772 pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
773 if (RT_UNLIKELY(!pState))
774 {
775 LogRel((DEVICE_NAME ": VBoxUSBSolarisAttach: DDI_RESUME failed to get soft state on detach\n"));
776 return DDI_FAILURE;
777 }
778
779 vboxUsbSolarisDeviceResume(pState);
780 return DDI_SUCCESS;
781 }
782
783 default:
784 return DDI_FAILURE;
785 }
786}
787
788
789/**
790 * Detach entry point, to detach a device to the system or suspend it.
791 *
792 * @param pDip The module structure instance.
793 * @param enmCmd Attach type (ddi_attach_cmd_t)
794 *
795 * @returns Solaris error code.
796 */
797int VBoxUSBSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
798{
799 LogFunc((DEVICE_NAME ": VBoxUSBSolarisDetach: pDip=%p enmCmd=%d\n", pDip, enmCmd));
800
801 int instance = ddi_get_instance(pDip);
802 vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
803 if (RT_UNLIKELY(!pState))
804 {
805 LogRel((DEVICE_NAME ": VBoxUSBSolarisDetach: Failed to get soft state on detach\n"));
806 return DDI_FAILURE;
807 }
808
809 switch (enmCmd)
810 {
811 case DDI_DETACH:
812 {
813 /*
814 * At this point it must be assumed that the default control pipe has
815 * already been closed by userland (via VBoxUSBSolarisClose() entry point).
816 * Once it's closed we can no longer open or reference the device here.
817 */
818
819 /*
820 * Notify userland if any that we're gone (while resetting device held by us).
821 */
822 mutex_enter(&pState->Mtx);
823 pState->DevState = USB_DEV_DISCONNECTED;
824 vboxUsbSolarisNotifyUnplug(pState);
825 mutex_exit(&pState->Mtx);
826
827
828 /*
829 * Unregister hotplug callback events first without holding the mutex as the callbacks
830 * would otherwise block on the mutex.
831 */
832 usb_unregister_hotplug_cbs(pDip);
833
834 /*
835 * Serialize: paranoid; drain other driver activity.
836 */
837 usb_serialize_access(pState->StateMulti, USB_WAIT, 0 /* timeout */);
838 usb_release_access(pState->StateMulti);
839 mutex_enter(&pState->Mtx);
840
841 /*
842 * Close all pipes.
843 */
844 vboxUsbSolarisCloseAllPipes(pState, true /* ControlPipe */);
845 Assert(!pState->fDefaultPipeOpen);
846
847 /*
848 * Deinitialize power, destroy all endpoints.
849 */
850 vboxUsbSolarisDestroyPower(pState);
851 vboxUsbSolarisDestroyAllEps(pState);
852
853 /*
854 * Free up all URB lists.
855 */
856 vboxusb_urb_t *pUrb = NULL;
857 while ((pUrb = list_remove_head(&pState->hFreeUrbs)) != NULL)
858 {
859 if (pUrb->pMsg)
860 freemsg(pUrb->pMsg);
861 RTMemFree(pUrb);
862 }
863 while ((pUrb = list_remove_head(&pState->hInflightUrbs)) != NULL)
864 {
865 if (pUrb->pMsg)
866 freemsg(pUrb->pMsg);
867 RTMemFree(pUrb);
868 }
869 while ((pUrb = list_remove_head(&pState->hLandedUrbs)) != NULL)
870 {
871 if (pUrb->pMsg)
872 freemsg(pUrb->pMsg);
873 RTMemFree(pUrb);
874 }
875 pState->cFreeUrbs = 0;
876 pState->cLandedUrbs = 0;
877 pState->cInflightUrbs = 0;
878 list_destroy(&pState->hFreeUrbs);
879 list_destroy(&pState->hInflightUrbs);
880 list_destroy(&pState->hLandedUrbs);
881
882 /*
883 * Destroy locks, free up descriptor and detach from USBA.
884 */
885 mutex_exit(&pState->Mtx);
886 usb_fini_serialization(pState->StateMulti);
887 mutex_destroy(&pState->Mtx);
888
889 usb_free_dev_data(pState->pDip, pState->pDevDesc);
890 usb_client_detach(pState->pDip, NULL);
891
892 /*
893 * Deregister with our Monitor driver.
894 */
895 VBoxUSBMonSolarisUnregisterClient(pState->pDip);
896
897 ddi_remove_minor_node(pState->pDip, NULL);
898
899#if 0
900 LogRel((DEVICE_NAME ": Released %s %s (Ident=%s)\n", pState->szMfg, pState->szProduct,
901 pState->ClientInfo.szDeviceIdent));
902#else
903 /* Until IPRT R0 logging is fixed. See @bugref{6657#c7} */
904 cmn_err(CE_CONT, "Released %s %s (Ident=%s)\n", pState->szMfg, pState->szProduct, pState->ClientInfo.szDeviceIdent);
905#endif
906
907 ddi_soft_state_free(g_pVBoxUSBSolarisState, instance);
908 pState = NULL;
909 return DDI_SUCCESS;
910 }
911
912 case DDI_SUSPEND:
913 {
914 int rc = vboxUsbSolarisDeviceSuspend(pState);
915 if (RT_SUCCESS(rc))
916 return DDI_SUCCESS;
917
918 return DDI_FAILURE;
919 }
920
921 default:
922 return DDI_FAILURE;
923 }
924}
925
926
927/**
928 * Info entry point, called by solaris kernel for obtaining driver info.
929 *
930 * @param pDip The module structure instance (do not use).
931 * @param enmCmd Information request type.
932 * @param pvArg Type specific argument.
933 * @param ppvResult Where to store the requested info.
934 *
935 * @returns Solaris error code.
936 */
937int VBoxUSBSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
938{
939 LogFunc((DEVICE_NAME ": VBoxUSBSolarisGetInfo\n"));
940
941 vboxusb_state_t *pState = NULL;
942 int instance = getminor((dev_t)pvArg);
943
944 switch (enmCmd)
945 {
946 case DDI_INFO_DEVT2DEVINFO:
947 {
948 /*
949 * One is to one mapping of instance & minor number as we publish only one minor node per device.
950 */
951 pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
952 if (pState)
953 {
954 *ppvResult = (void *)pState->pDip;
955 return DDI_SUCCESS;
956 }
957 else
958 LogRel((DEVICE_NAME ": VBoxUSBSolarisGetInfo: Failed to get device state\n"));
959 return DDI_FAILURE;
960 }
961
962 case DDI_INFO_DEVT2INSTANCE:
963 {
964 *ppvResult = (void *)(uintptr_t)instance;
965 return DDI_SUCCESS;
966 }
967
968 default:
969 return DDI_FAILURE;
970 }
971}
972
973
974/**
975 * Callback invoked from the VirtualBox USB Monitor driver when a VM process
976 * tries to access this USB client instance.
977 *
978 * This determines which VM process will be allowed to open and access this USB
979 * device.
980 *
981 * @returns VBox status code.
982 *
983 * @param Process The VM process performing the client info. query.
984 * @param Instance This client instance (the one set while we register
985 * ourselves to the Monitor driver)
986 * @param pvReserved Reserved for future, unused.
987 */
988LOCAL int vboxUsbSolarisSetConsumerCredentials(RTPROCESS Process, int Instance, void *pvReserved)
989{
990 LogFunc((DEVICE_NAME ": vboxUsbSolarisSetConsumerCredentials: Process=%u Instance=%d\n", Process, Instance));
991 vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, Instance);
992 if (!pState)
993 {
994 LogRel((DEVICE_NAME ": vboxUsbSolarisSetConsumerCredentials: Failed to get device state for instance %d\n", Instance));
995 return VERR_INVALID_STATE;
996 }
997
998 int rc = VINF_SUCCESS;
999 mutex_enter(&pState->Mtx);
1000
1001 if (pState->Process == NIL_RTPROCESS)
1002 pState->Process = Process;
1003 else
1004 {
1005 LogRel((DEVICE_NAME ": vboxUsbSolarisSetConsumerCredentials: Failed! Process %u already has client open\n",
1006 pState->Process));
1007 rc = VERR_RESOURCE_BUSY;
1008 }
1009
1010 mutex_exit(&pState->Mtx);
1011
1012 return rc;
1013}
1014
1015
1016int VBoxUSBSolarisOpen(dev_t *pDev, int fFlag, int fType, cred_t *pCred)
1017{
1018 LogFunc((DEVICE_NAME ": VBoxUSBSolarisOpen: pDev=%p fFlag=%d fType=%d pCred=%p\n", pDev, fFlag, fType, pCred));
1019
1020 /*
1021 * Verify we are being opened as a character device
1022 */
1023 if (fType != OTYP_CHR)
1024 return EINVAL;
1025
1026 /*
1027 * One is to one mapping. (Minor<=>Instance).
1028 */
1029 int instance = getminor((dev_t)*pDev);
1030 vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
1031 if (!pState)
1032 {
1033 LogRel((DEVICE_NAME ": VBoxUSBSolarisOpen: Failed to get device state for instance %d\n", instance));
1034 return ENXIO;
1035 }
1036
1037 mutex_enter(&pState->Mtx);
1038
1039 /*
1040 * Only one user process can open a device instance at a time.
1041 */
1042 if (pState->Process != RTProcSelf())
1043 {
1044 if (pState->Process == NIL_RTPROCESS)
1045 LogRel((DEVICE_NAME ": VBoxUSBSolarisOpen: No prior information about authorized process\n"));
1046 else
1047 LogRel((DEVICE_NAME ": VBoxUSBSolarisOpen: Process %u is already using this device instance\n", pState->Process));
1048
1049 mutex_exit(&pState->Mtx);
1050 return EPERM;
1051 }
1052
1053 mutex_exit(&pState->Mtx);
1054
1055 NOREF(fFlag);
1056 NOREF(pCred);
1057
1058 return 0;
1059}
1060
1061
1062int VBoxUSBSolarisClose(dev_t Dev, int fFlag, int fType, cred_t *pCred)
1063{
1064 LogFunc((DEVICE_NAME ": VBoxUSBSolarisClose: Dev=%d fFlag=%d fType=%d pCred=%p\n", Dev, fFlag, fType, pCred));
1065
1066 int instance = getminor((dev_t)Dev);
1067 vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
1068 if (RT_UNLIKELY(!pState))
1069 {
1070 LogRel((DEVICE_NAME ": VBoxUSBSolarisClose: Failed to get device state for instance %d\n", instance));
1071 return ENXIO;
1072 }
1073
1074 mutex_enter(&pState->Mtx);
1075 pState->fPollPending = false;
1076 pState->Process = NIL_RTPROCESS;
1077 mutex_exit(&pState->Mtx);
1078
1079 return 0;
1080}
1081
1082
1083int VBoxUSBSolarisRead(dev_t Dev, struct uio *pUio, cred_t *pCred)
1084{
1085 LogFunc((DEVICE_NAME ": VBoxUSBSolarisRead\n"));
1086 return ENOTSUP;
1087}
1088
1089
1090int VBoxUSBSolarisWrite(dev_t Dev, struct uio *pUio, cred_t *pCred)
1091{
1092 LogFunc((DEVICE_NAME ": VBoxUSBSolarisWrite\n"));
1093 return ENOTSUP;
1094}
1095
1096
1097int VBoxUSBSolarisPoll(dev_t Dev, short fEvents, int fAnyYet, short *pReqEvents, struct pollhead **ppPollHead)
1098{
1099 LogFunc((DEVICE_NAME ": VBoxUSBSolarisPoll: Dev=%d fEvents=%d fAnyYet=%d pReqEvents=%p\n", Dev, fEvents, fAnyYet, pReqEvents));
1100
1101 /*
1102 * Get the device state (one to one mapping).
1103 */
1104 int instance = getminor((dev_t)Dev);
1105 vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
1106 if (RT_UNLIKELY(!pState))
1107 {
1108 LogRel((DEVICE_NAME ": VBoxUSBSolarisPoll: No state data for %d\n", instance));
1109 return ENXIO;
1110 }
1111
1112 mutex_enter(&pState->Mtx);
1113
1114 /*
1115 * Disconnect event (POLLHUP) is invalid in "fEvents".
1116 */
1117 if (pState->DevState == USB_DEV_DISCONNECTED)
1118 *pReqEvents |= POLLHUP;
1119 else if (pState->cLandedUrbs)
1120 *pReqEvents |= POLLIN;
1121 else
1122 {
1123 *pReqEvents = 0;
1124 if (!fAnyYet)
1125 {
1126 *ppPollHead = &pState->PollHead;
1127 pState->fPollPending = true;
1128 }
1129 }
1130
1131 mutex_exit(&pState->Mtx);
1132
1133 return 0;
1134}
1135
1136
1137int VBoxUSBSolarisPower(dev_info_t *pDip, int Component, int Level)
1138{
1139 LogFunc((DEVICE_NAME ": VBoxUSBSolarisPower: pDip=%p Component=%d Level=%d\n", pDip, Component, Level));
1140
1141 int instance = ddi_get_instance(pDip);
1142 vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
1143 if (RT_UNLIKELY(!pState))
1144 {
1145 LogRel((DEVICE_NAME ": VBoxUSBSolarisPower: Failed! State Gone\n"));
1146 return DDI_FAILURE;
1147 }
1148
1149 if (!pState->pPower)
1150 return DDI_SUCCESS;
1151
1152 usb_serialize_access(pState->StateMulti, USB_WAIT, 0);
1153 mutex_enter(&pState->Mtx);
1154
1155 int rc = USB_FAILURE;
1156 if (pState->DevState == USB_DEV_ONLINE)
1157 {
1158 /*
1159 * Check if we are transitioning to a valid power state.
1160 */
1161 if (!USB_DEV_PWRSTATE_OK(pState->pPower->PowerStates, Level))
1162 {
1163 switch (Level)
1164 {
1165 case USB_DEV_OS_PWR_OFF:
1166 {
1167 if (pState->pPower->PowerBusy)
1168 break;
1169
1170 /*
1171 * USB D3 command.
1172 */
1173 pState->pPower->PowerLevel = USB_DEV_OS_PWR_OFF;
1174 mutex_exit(&pState->Mtx);
1175 rc = USB_SUCCESS; /* usb_set_device_pwrlvl3(pDip); */
1176 mutex_enter(&pState->Mtx);
1177 break;
1178 }
1179
1180 case USB_DEV_OS_FULL_PWR:
1181 {
1182 /*
1183 * Can happen during shutdown of the OS.
1184 */
1185 pState->pPower->PowerLevel = USB_DEV_OS_FULL_PWR;
1186 mutex_exit(&pState->Mtx);
1187 rc = USB_SUCCESS; /* usb_set_device_pwrlvl0(pDip); */
1188 mutex_enter(&pState->Mtx);
1189 break;
1190 }
1191
1192 default: /* Power levels 1, 2 not implemented */
1193 break;
1194 }
1195 }
1196 else
1197 Log((DEVICE_NAME ": VBoxUSBSolarisPower: USB_DEV_PWRSTATE_OK failed\n"));
1198 }
1199 else
1200 rc = USB_SUCCESS;
1201
1202 mutex_exit(&pState->Mtx);
1203 usb_release_access(pState->StateMulti);
1204 return rc == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE;
1205}
1206
1207
1208/** @def IOCPARM_LEN
1209 * Gets the length from the ioctl number.
1210 * This is normally defined by sys/ioccom.h on BSD systems...
1211 */
1212#ifndef IOCPARM_LEN
1213# define IOCPARM_LEN(Code) (((Code) >> 16) & IOCPARM_MASK)
1214#endif
1215
1216int VBoxUSBSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cred_t *pCred, int *pVal)
1217{
1218 /* LogFunc((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Dev=%d Cmd=%d pArg=%p Mode=%d\n", Dev, Cmd, pArg)); */
1219
1220 /*
1221 * Get the device state (one to one mapping).
1222 */
1223 int instance = getminor((dev_t)Dev);
1224 vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
1225 if (RT_UNLIKELY(!pState))
1226 {
1227 LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: No state data for %d\n", instance));
1228 return EINVAL;
1229 }
1230
1231 /*
1232 * Read the request wrapper.
1233 */
1234 VBOXUSBREQ ReqWrap;
1235 if (IOCPARM_LEN(Cmd) != sizeof(ReqWrap))
1236 {
1237 LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Bad request %#x size=%d expected=%d\n", Cmd, IOCPARM_LEN(Cmd),
1238 sizeof(ReqWrap)));
1239 return ENOTTY;
1240 }
1241
1242 int rc = ddi_copyin((void *)pArg, &ReqWrap, sizeof(ReqWrap), Mode);
1243 if (RT_UNLIKELY(rc))
1244 {
1245 LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: ddi_copyin failed to read header pArg=%p Cmd=%d. rc=%d\n", pArg, Cmd, rc));
1246 return EINVAL;
1247 }
1248
1249 if (ReqWrap.u32Magic != VBOXUSB_MAGIC)
1250 {
1251 LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Bad magic %#x; pArg=%p Cmd=%d\n", ReqWrap.u32Magic, pArg, Cmd));
1252 return EINVAL;
1253 }
1254 if (RT_UNLIKELY( ReqWrap.cbData == 0
1255 || ReqWrap.cbData > _1M*16))
1256 {
1257 LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Bad size %#x; pArg=%p Cmd=%d\n", ReqWrap.cbData, pArg, Cmd));
1258 return EINVAL;
1259 }
1260
1261 /*
1262 * Read the request.
1263 */
1264 void *pvBuf = RTMemTmpAlloc(ReqWrap.cbData);
1265 if (RT_UNLIKELY(!pvBuf))
1266 {
1267 LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: RTMemTmpAlloc failed to alloc %d bytes\n", ReqWrap.cbData));
1268 return ENOMEM;
1269 }
1270
1271 rc = ddi_copyin((void *)(uintptr_t)ReqWrap.pvDataR3, pvBuf, ReqWrap.cbData, Mode);
1272 if (RT_UNLIKELY(rc))
1273 {
1274 RTMemTmpFree(pvBuf);
1275 LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: ddi_copyin failed! pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg, Cmd, rc));
1276 return EFAULT;
1277 }
1278 if (RT_UNLIKELY( ReqWrap.cbData == 0
1279 || pvBuf == NULL))
1280 {
1281 RTMemTmpFree(pvBuf);
1282 LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Invalid request! pvBuf=%p cbData=%d\n", pvBuf, ReqWrap.cbData));
1283 return EINVAL;
1284 }
1285
1286 /*
1287 * Process the IOCtl.
1288 */
1289 size_t cbDataOut = 0;
1290 rc = vboxUsbSolarisProcessIOCtl(Cmd, pState, Mode, &ReqWrap, pvBuf, &cbDataOut);
1291 ReqWrap.rc = rc;
1292 rc = 0;
1293
1294 if (RT_UNLIKELY(cbDataOut > ReqWrap.cbData))
1295 {
1296 LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: Too much output data %d expected %d Truncating!\n", cbDataOut,
1297 ReqWrap.cbData));
1298 cbDataOut = ReqWrap.cbData;
1299 }
1300
1301 ReqWrap.cbData = cbDataOut;
1302
1303 /*
1304 * Copy VBOXUSBREQ back to userspace (which contains rc for USB operation).
1305 */
1306 rc = ddi_copyout(&ReqWrap, (void *)pArg, sizeof(ReqWrap), Mode);
1307 if (RT_LIKELY(!rc))
1308 {
1309 /*
1310 * Copy payload (if any) back to userspace.
1311 */
1312 if (cbDataOut > 0)
1313 {
1314 rc = ddi_copyout(pvBuf, (void *)(uintptr_t)ReqWrap.pvDataR3, cbDataOut, Mode);
1315 if (RT_UNLIKELY(rc))
1316 {
1317 LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: ddi_copyout failed! pvBuf=%p pArg=%p Cmd=%d. rc=%d\n", pvBuf, pArg,
1318 Cmd, rc));
1319 rc = EFAULT;
1320 }
1321 }
1322 }
1323 else
1324 {
1325 LogRel((DEVICE_NAME ": VBoxUSBSolarisIOCtl: ddi_copyout(1)failed! pReqWrap=%p pArg=%p Cmd=%d. rc=%d\n", &ReqWrap, pArg,
1326 Cmd, rc));
1327 rc = EFAULT;
1328 }
1329
1330 *pVal = rc;
1331 RTMemTmpFree(pvBuf);
1332 return rc;
1333}
1334
1335
1336/**
1337 * IOCtl processor for user to kernel and kernel to kernel communication.
1338 *
1339 * @returns VBox status code.
1340 *
1341 * @param iFunction The requested function.
1342 * @param pvState The USB device instance.
1343 * @param Mode The IOCtl mode.
1344 * @param pUSBReq Pointer to the VBOXUSB request.
1345 * @param pvBuf Pointer to the ring-3 URB.
1346 * @param pcbDataOut Where to store the IOCtl OUT data size.
1347 */
1348LOCAL int vboxUsbSolarisProcessIOCtl(int iFunction, void *pvState, int Mode, PVBOXUSBREQ pUSBReq, void *pvBuf,
1349 size_t *pcbDataOut)
1350{
1351 /* LogFunc((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: iFunction=%d pvState=%p pUSBReq=%p\n", iFunction, pvState, pUSBReq)); */
1352
1353 AssertPtrReturn(pvState, VERR_INVALID_PARAMETER);
1354 vboxusb_state_t *pState = (vboxusb_state_t *)pvState;
1355 size_t cbData = pUSBReq->cbData;
1356 int rc;
1357
1358#define CHECKRET_MIN_SIZE(mnemonic, cbMin) \
1359 do { \
1360 if (RT_UNLIKELY(cbData < (cbMin))) \
1361 { \
1362 LogRel((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: " mnemonic ": cbData=%#zx (%zu) min is %#zx (%zu)\n", \
1363 cbData, cbData, (size_t)(cbMin), (size_t)(cbMin))); \
1364 return VERR_BUFFER_OVERFLOW; \
1365 } \
1366 if (RT_UNLIKELY((cbMin) != 0 && !RT_VALID_PTR(pvBuf))) \
1367 { \
1368 LogRel((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: " mnemonic ": Invalid pointer %p\n", pvBuf)); \
1369 return VERR_INVALID_PARAMETER; \
1370 } \
1371 } while (0)
1372
1373 switch (iFunction)
1374 {
1375 case VBOXUSB_IOCTL_SEND_URB:
1376 {
1377 CHECKRET_MIN_SIZE("SEND_URB", sizeof(VBOXUSBREQ_URB));
1378
1379 PVBOXUSBREQ_URB pUrbReq = (PVBOXUSBREQ_URB)pvBuf;
1380 rc = vboxUsbSolarisSendUrb(pState, pUrbReq, Mode);
1381 *pcbDataOut = 0;
1382 Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: SEND_URB returned %d\n", rc));
1383 break;
1384 }
1385
1386 case VBOXUSB_IOCTL_REAP_URB:
1387 {
1388 CHECKRET_MIN_SIZE("REAP_URB", sizeof(VBOXUSBREQ_URB));
1389
1390 PVBOXUSBREQ_URB pUrbReq = (PVBOXUSBREQ_URB)pvBuf;
1391 rc = vboxUsbSolarisReapUrb(pState, pUrbReq, Mode);
1392 *pcbDataOut = sizeof(VBOXUSBREQ_URB);
1393 Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: REAP_URB returned %d\n", rc));
1394 break;
1395 }
1396
1397 case VBOXUSB_IOCTL_CLEAR_EP:
1398 {
1399 CHECKRET_MIN_SIZE("CLEAR_EP", sizeof(VBOXUSBREQ_CLEAR_EP));
1400
1401 PVBOXUSBREQ_CLEAR_EP pClearEpReq = (PVBOXUSBREQ_CLEAR_EP)pvBuf;
1402 rc = vboxUsbSolarisClearEndPoint(pState, pClearEpReq->bEndpoint);
1403 *pcbDataOut = 0;
1404 Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: CLEAR_EP returned %d\n", rc));
1405 break;
1406 }
1407
1408 case VBOXUSB_IOCTL_SET_CONFIG:
1409 {
1410 CHECKRET_MIN_SIZE("SET_CONFIG", sizeof(VBOXUSBREQ_SET_CONFIG));
1411
1412 PVBOXUSBREQ_SET_CONFIG pSetCfgReq = (PVBOXUSBREQ_SET_CONFIG)pvBuf;
1413 rc = vboxUsbSolarisSetConfig(pState, pSetCfgReq->bConfigValue);
1414 *pcbDataOut = 0;
1415 Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: SET_CONFIG returned %d\n", rc));
1416 break;
1417 }
1418
1419 case VBOXUSB_IOCTL_SET_INTERFACE:
1420 {
1421 CHECKRET_MIN_SIZE("SET_INTERFACE", sizeof(VBOXUSBREQ_SET_INTERFACE));
1422
1423 PVBOXUSBREQ_SET_INTERFACE pSetInterfaceReq = (PVBOXUSBREQ_SET_INTERFACE)pvBuf;
1424 rc = vboxUsbSolarisSetInterface(pState, pSetInterfaceReq->bInterface, pSetInterfaceReq->bAlternate);
1425 *pcbDataOut = 0;
1426 Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: SET_INTERFACE returned %d\n", rc));
1427 break;
1428 }
1429
1430 case VBOXUSB_IOCTL_CLOSE_DEVICE:
1431 {
1432 CHECKRET_MIN_SIZE("CLOSE_DEVICE", sizeof(VBOXUSBREQ_CLOSE_DEVICE));
1433
1434 PVBOXUSBREQ_CLOSE_DEVICE pCloseDeviceReq = (PVBOXUSBREQ_CLOSE_DEVICE)pvBuf;
1435 if ( pCloseDeviceReq->ResetLevel != VBOXUSB_RESET_LEVEL_REATTACH
1436 || (Mode & FKIOCTL))
1437 {
1438 rc = vboxUsbSolarisCloseDevice(pState, pCloseDeviceReq->ResetLevel);
1439 }
1440 else
1441 {
1442 /* Userland IOCtls are not allowed to perform a reattach of the device. */
1443 rc = VERR_NOT_SUPPORTED;
1444 }
1445 *pcbDataOut = 0;
1446 Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: CLOSE_DEVICE returned %d\n", rc));
1447 break;
1448 }
1449
1450 case VBOXUSB_IOCTL_ABORT_PIPE:
1451 {
1452 CHECKRET_MIN_SIZE("ABORT_PIPE", sizeof(VBOXUSBREQ_ABORT_PIPE));
1453
1454 PVBOXUSBREQ_ABORT_PIPE pAbortPipeReq = (PVBOXUSBREQ_ABORT_PIPE)pvBuf;
1455 rc = vboxUsbSolarisAbortPipe(pState, pAbortPipeReq->bEndpoint);
1456 *pcbDataOut = 0;
1457 Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: ABORT_PIPE returned %d\n", rc));
1458 break;
1459 }
1460
1461 case VBOXUSB_IOCTL_GET_CONFIG:
1462 {
1463 CHECKRET_MIN_SIZE("GET_CONFIG", sizeof(VBOXUSBREQ_GET_CONFIG));
1464
1465 PVBOXUSBREQ_GET_CONFIG pGetCfgReq = (PVBOXUSBREQ_GET_CONFIG)pvBuf;
1466 rc = vboxUsbSolarisGetConfig(pState, &pGetCfgReq->bConfigValue);
1467 *pcbDataOut = sizeof(VBOXUSBREQ_GET_CONFIG);
1468 Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: GET_CONFIG returned %d\n", rc));
1469 break;
1470 }
1471
1472 case VBOXUSB_IOCTL_GET_VERSION:
1473 {
1474 CHECKRET_MIN_SIZE("GET_VERSION", sizeof(VBOXUSBREQ_GET_VERSION));
1475
1476 PVBOXUSBREQ_GET_VERSION pGetVersionReq = (PVBOXUSBREQ_GET_VERSION)pvBuf;
1477 pGetVersionReq->u32Major = VBOXUSB_VERSION_MAJOR;
1478 pGetVersionReq->u32Minor = VBOXUSB_VERSION_MINOR;
1479 *pcbDataOut = sizeof(VBOXUSBREQ_GET_VERSION);
1480 rc = VINF_SUCCESS;
1481 Log((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: GET_VERSION returned %d\n", rc));
1482 break;
1483 }
1484
1485 default:
1486 {
1487 LogRel((DEVICE_NAME ": vboxUsbSolarisProcessIOCtl: Unknown request %#x\n", iFunction));
1488 rc = VERR_NOT_SUPPORTED;
1489 *pcbDataOut = 0;
1490 break;
1491 }
1492 }
1493
1494 pUSBReq->cbData = *pcbDataOut;
1495 return rc;
1496}
1497
1498
1499/**
1500 * Initializes device power management.
1501 *
1502 * @param pState The USB device instance.
1503 *
1504 * @returns VBox status code.
1505 */
1506LOCAL int vboxUsbSolarisInitPower(vboxusb_state_t *pState)
1507{
1508 LogFunc((DEVICE_NAME ": vboxUsbSolarisInitPower: pState=%p\n", pState));
1509
1510 int rc = usb_handle_remote_wakeup(pState->pDip, USB_REMOTE_WAKEUP_ENABLE);
1511 if (rc == USB_SUCCESS)
1512 {
1513 vboxusb_power_t *pPower = RTMemAllocZ(sizeof(vboxusb_power_t));
1514 if (RT_LIKELY(pPower))
1515 {
1516 mutex_enter(&pState->Mtx);
1517 pState->pPower = pPower;
1518 pState->pPower->fPowerWakeup = false;
1519 mutex_exit(&pState->Mtx);
1520
1521 uint_t PowerStates;
1522 rc = usb_create_pm_components(pState->pDip, &PowerStates);
1523 if (rc == USB_SUCCESS)
1524 {
1525 pState->pPower->fPowerWakeup = true;
1526 pState->pPower->PowerLevel = USB_DEV_OS_FULL_PWR;
1527 pState->pPower->PowerStates = PowerStates;
1528
1529 rc = pm_raise_power(pState->pDip, 0 /* component */, USB_DEV_OS_FULL_PWR);
1530
1531 if (rc != DDI_SUCCESS)
1532 {
1533 LogRel((DEVICE_NAME ": vboxUsbSolarisInitPower: Failed to raise power level usb(%#x,%#x)\n",
1534 pState->pDevDesc->dev_descr->idVendor, pState->pDevDesc->dev_descr->idProduct));
1535 }
1536 }
1537 else
1538 Log((DEVICE_NAME ": vboxUsbSolarisInitPower: Failed to create power components\n"));
1539
1540 return VINF_SUCCESS;
1541 }
1542 else
1543 rc = VERR_NO_MEMORY;
1544 }
1545 else
1546 {
1547 Log((DEVICE_NAME ": vboxUsbSolarisInitPower: Failed to enable remote wakeup, No PM!\n"));
1548 rc = VINF_SUCCESS;
1549 }
1550
1551 return rc;
1552}
1553
1554
1555/**
1556 * Destroys device power management.
1557 *
1558 * @param pState The USB device instance.
1559 * @remarks Requires the device state mutex to be held.
1560 *
1561 * @returns VBox status code.
1562 */
1563LOCAL void vboxUsbSolarisDestroyPower(vboxusb_state_t *pState)
1564{
1565 LogFunc((DEVICE_NAME ": vboxUsbSolarisDestroyPower: pState=%p\n", pState));
1566
1567 if (pState->pPower)
1568 {
1569 mutex_exit(&pState->Mtx);
1570 vboxUsbSolarisPowerBusy(pState);
1571 mutex_enter(&pState->Mtx);
1572
1573 int rc = -1;
1574 if ( pState->pPower->fPowerWakeup
1575 && pState->DevState != USB_DEV_DISCONNECTED)
1576 {
1577 mutex_exit(&pState->Mtx);
1578 rc = pm_raise_power(pState->pDip, 0 /* component */, USB_DEV_OS_FULL_PWR);
1579 if (rc != DDI_SUCCESS)
1580 Log((DEVICE_NAME ": vboxUsbSolarisDestroyPower: Raising power failed! rc=%d\n", rc));
1581
1582 rc = usb_handle_remote_wakeup(pState->pDip, USB_REMOTE_WAKEUP_DISABLE);
1583 if (rc != DDI_SUCCESS)
1584 Log((DEVICE_NAME ": vboxUsbSolarisDestroyPower: Failed to disable remote wakeup\n"));
1585 }
1586 else
1587 mutex_exit(&pState->Mtx);
1588
1589 rc = pm_lower_power(pState->pDip, 0 /* component */, USB_DEV_OS_PWR_OFF);
1590 if (rc != DDI_SUCCESS)
1591 Log((DEVICE_NAME ": vboxUsbSolarisDestroyPower: Lowering power failed! rc=%d\n", rc));
1592
1593 vboxUsbSolarisPowerIdle(pState);
1594 mutex_enter(&pState->Mtx);
1595 RTMemFree(pState->pPower);
1596 pState->pPower = NULL;
1597 }
1598}
1599
1600
1601/**
1602 * Converts Solaris' USBA URB status to VBox's USB URB status.
1603 *
1604 * @param Status Solaris USBA USB URB status.
1605 *
1606 * @returns VBox USB URB status.
1607 */
1608LOCAL VUSBSTATUS vboxUsbSolarisGetUrbStatus(usb_cr_t Status)
1609{
1610 switch (Status)
1611 {
1612 case USB_CR_OK: return VUSBSTATUS_OK;
1613 case USB_CR_CRC: return VUSBSTATUS_CRC;
1614 case USB_CR_DEV_NOT_RESP: return VUSBSTATUS_DNR;
1615 case USB_CR_DATA_UNDERRUN: return VUSBSTATUS_DATA_UNDERRUN;
1616 case USB_CR_DATA_OVERRUN: return VUSBSTATUS_DATA_OVERRUN;
1617 case USB_CR_STALL: return VUSBSTATUS_STALL;
1618 /*
1619 case USB_CR_BITSTUFFING:
1620 case USB_CR_DATA_TOGGLE_MM:
1621 case USB_CR_PID_CHECKFAILURE:
1622 case USB_CR_UNEXP_PID:
1623 case USB_CR_BUFFER_OVERRUN:
1624 case USB_CR_BUFFER_UNDERRUN:
1625 case USB_CR_TIMEOUT:
1626 case USB_CR_NOT_ACCESSED:
1627 case USB_CR_NO_RESOURCES:
1628 case USB_CR_UNSPECIFIED_ERR:
1629 case USB_CR_STOPPED_POLLING:
1630 case USB_CR_PIPE_CLOSING:
1631 case USB_CR_PIPE_RESET:
1632 case USB_CR_NOT_SUPPORTED:
1633 case USB_CR_FLUSHED:
1634 case USB_CR_HC_HARDWARE_ERR:
1635 */
1636 default: return VUSBSTATUS_INVALID;
1637 }
1638}
1639
1640
1641/**
1642 * Converts Solaris' USBA error code to VBox's error code.
1643 *
1644 * @param UsbRc Solaris USBA error code.
1645 *
1646 * @returns VBox error code.
1647 */
1648static int vboxUsbSolarisToVBoxRC(int UsbRc)
1649{
1650 switch (UsbRc)
1651 {
1652 case USB_SUCCESS: return VINF_SUCCESS;
1653 case USB_INVALID_ARGS: return VERR_INVALID_PARAMETER;
1654 case USB_INVALID_PIPE: return VERR_BAD_PIPE;
1655 case USB_INVALID_CONTEXT: return VERR_INVALID_CONTEXT;
1656 case USB_BUSY: return VERR_PIPE_BUSY;
1657 case USB_PIPE_ERROR: return VERR_PIPE_IO_ERROR;
1658 /*
1659 case USB_FAILURE:
1660 case USB_NO_RESOURCES:
1661 case USB_NO_BANDWIDTH:
1662 case USB_NOT_SUPPORTED:
1663 case USB_PIPE_ERROR:
1664 case USB_NO_FRAME_NUMBER:
1665 case USB_INVALID_START_FRAME:
1666 case USB_HC_HARDWARE_ERROR:
1667 case USB_INVALID_REQUEST:
1668 case USB_INVALID_VERSION:
1669 case USB_INVALID_PERM:
1670 */
1671 default: return VERR_GENERAL_FAILURE;
1672 }
1673}
1674
1675
1676/**
1677 * Converts Solaris' USBA device state to VBox's error code.
1678 *
1679 * @param uDeviceState The USB device state to convert.
1680 *
1681 * @returns VBox error code.
1682 */
1683static int vboxUsbSolarisDeviceState(uint8_t uDeviceState)
1684{
1685 switch (uDeviceState)
1686 {
1687 case USB_DEV_ONLINE: return VINF_SUCCESS;
1688 case USB_DEV_SUSPENDED: return VERR_VUSB_DEVICE_IS_SUSPENDED;
1689 case USB_DEV_DISCONNECTED:
1690 case USB_DEV_PWRED_DOWN: return VERR_VUSB_DEVICE_NOT_ATTACHED;
1691 default: return VERR_GENERAL_FAILURE;
1692 }
1693}
1694
1695
1696/**
1697 * Checks if the device is a USB device.
1698 *
1699 * @param pDip Pointer to this device info. structure.
1700 *
1701 * @returns If this is really a USB device returns true, otherwise false.
1702 */
1703LOCAL bool vboxUsbSolarisIsUSBDevice(dev_info_t *pDip)
1704{
1705 int rc = DDI_FAILURE;
1706
1707 /*
1708 * Check device for "usb" compatible property, root hubs->device would likely mean parent has no "usb" property.
1709 */
1710 char **ppszCompatible = NULL;
1711 uint_t cCompatible;
1712 rc = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, pDip, DDI_PROP_DONTPASS, "compatible", &ppszCompatible, &cCompatible);
1713 if (RT_LIKELY(rc == DDI_PROP_SUCCESS))
1714 {
1715 while (cCompatible--)
1716 {
1717 Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Compatible[%d]=%s\n", cCompatible, ppszCompatible[cCompatible]));
1718 if (!strncmp(ppszCompatible[cCompatible], "usb", 3))
1719 {
1720 Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Verified device as USB. pszCompatible=%s\n",
1721 ppszCompatible[cCompatible]));
1722 ddi_prop_free(ppszCompatible);
1723 return true;
1724 }
1725 }
1726
1727 ddi_prop_free(ppszCompatible);
1728 ppszCompatible = NULL;
1729 }
1730 else
1731 Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: USB property lookup failed, rc=%d\n", rc));
1732
1733 /*
1734 * Check parent for "usb" compatible property.
1735 */
1736 dev_info_t *pParentDip = ddi_get_parent(pDip);
1737 if (pParentDip)
1738 {
1739 rc = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, pParentDip, DDI_PROP_DONTPASS, "compatible", &ppszCompatible,
1740 &cCompatible);
1741 if (RT_LIKELY(rc == DDI_PROP_SUCCESS))
1742 {
1743 while (cCompatible--)
1744 {
1745 Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Parent compatible[%d]=%s\n", cCompatible,
1746 ppszCompatible[cCompatible]));
1747 if (!strncmp(ppszCompatible[cCompatible], "usb", 3))
1748 {
1749 Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Verified device as USB. parent pszCompatible=%s\n",
1750 ppszCompatible[cCompatible]));
1751 ddi_prop_free(ppszCompatible);
1752 return true;
1753 }
1754 }
1755
1756 ddi_prop_free(ppszCompatible);
1757 ppszCompatible = NULL;
1758 }
1759 else
1760 Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: USB parent property lookup failed. rc=%d\n", rc));
1761 }
1762 else
1763 Log((DEVICE_NAME ": vboxUsbSolarisIsUSBDevice: Failed to obtain parent device for property lookup\n"));
1764
1765 return false;
1766}
1767
1768
1769/**
1770 * Submits a URB.
1771 *
1772 * @param pState The USB device instance.
1773 * @param pUrbReq Pointer to the VBox USB URB.
1774 * @param Mode The IOCtl mode.
1775 *
1776 * @returns VBox error code.
1777 */
1778LOCAL int vboxUsbSolarisSendUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, int Mode)
1779{
1780 int iEpIndex = VBOXUSB_GET_EP_INDEX(pUrbReq->bEndpoint);
1781 Assert(iEpIndex >= 0 && iEpIndex < RT_ELEMENTS(pState->aEps));
1782 vboxusb_ep_t *pEp = &pState->aEps[iEpIndex];
1783 AssertPtrReturn(pEp, VERR_INVALID_POINTER);
1784 Assert(pUrbReq);
1785
1786#if 0
1787 LogFunc((DEVICE_NAME ": vboxUsbSolarisSendUrb: pState=%p pUrbReq=%p bEndpoint=%#x[%d] enmDir=%#x enmType=%#x "
1788 "cbData=%d pvData=%p\n", pState, pUrbReq, pUrbReq->bEndpoint, iEpIndex, pUrbReq->enmDir,
1789 pUrbReq->enmType, pUrbReq->cbData, pUrbReq->pvData));
1790#endif
1791
1792 if (RT_UNLIKELY(!pUrbReq->pvData))
1793 {
1794 LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: Invalid request - No data\n"));
1795 return VERR_INVALID_POINTER;
1796 }
1797
1798 /*
1799 * Allocate message block & copy userspace buffer for host to device Xfers and for
1800 * Control Xfers (since input has Setup header that needs copying).
1801 */
1802 mblk_t *pMsg = NULL;
1803 int rc = VINF_SUCCESS;
1804 if ( pUrbReq->enmDir == VUSBDIRECTION_OUT
1805 || pUrbReq->enmType == VUSBXFERTYPE_MSG)
1806 {
1807 pMsg = allocb(pUrbReq->cbData, BPRI_HI);
1808 if (RT_UNLIKELY(!pMsg))
1809 {
1810 LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: Failed to allocate %u bytes\n", pUrbReq->cbData));
1811 return VERR_NO_MEMORY;
1812 }
1813
1814 rc = ddi_copyin(pUrbReq->pvData, pMsg->b_wptr, pUrbReq->cbData, Mode);
1815 if (RT_UNLIKELY(rc))
1816 {
1817 LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: ddi_copyin failed! rc=%d\n", rc));
1818 freemsg(pMsg);
1819 return VERR_NO_MEMORY;
1820 }
1821
1822 pMsg->b_wptr += pUrbReq->cbData;
1823 }
1824
1825 mutex_enter(&pState->Mtx);
1826 rc = vboxUsbSolarisDeviceState(pState->DevState);
1827 if (!pState->fDefaultPipeOpen) /* Required for Isoc. IN Xfers which don't Xfer through the pipe after polling starts */
1828 rc = VERR_VUSB_DEVICE_NOT_ATTACHED;
1829 if (RT_SUCCESS(rc))
1830 {
1831 /*
1832 * Open the pipe if needed.
1833 */
1834 rc = vboxUsbSolarisOpenPipe(pState, pEp);
1835 if (RT_UNLIKELY(RT_FAILURE(rc)))
1836 {
1837 mutex_exit(&pState->Mtx);
1838 freemsg(pMsg);
1839 LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: OpenPipe failed! pState=%p pUrbReq=%p bEndpoint=%#x enmDir=%#x "
1840 "enmType=%#x cbData=%d pvData=%p rc=%d\n", pState, pUrbReq, pUrbReq->bEndpoint, pUrbReq->enmDir,
1841 pUrbReq->enmType, pUrbReq->cbData, pUrbReq->pvData, rc));
1842 return VERR_BAD_PIPE;
1843 }
1844
1845 mutex_exit(&pState->Mtx);
1846
1847 vboxusb_urb_t *pUrb = NULL;
1848 if ( pUrbReq->enmType == VUSBXFERTYPE_ISOC
1849 && pUrbReq->enmDir == VUSBDIRECTION_IN)
1850 pUrb = vboxUsbSolarisGetIsocInUrb(pState, pUrbReq);
1851 else
1852 pUrb = vboxUsbSolarisQueueUrb(pState, pUrbReq, pMsg);
1853
1854 if (RT_LIKELY(pUrb))
1855 {
1856 switch (pUrb->enmType)
1857 {
1858 case VUSBXFERTYPE_MSG:
1859 {
1860 rc = vboxUsbSolarisCtrlXfer(pState, pEp, pUrb);
1861 break;
1862 }
1863
1864 case VUSBXFERTYPE_BULK:
1865 {
1866 rc = vboxUsbSolarisBulkXfer(pState, pEp, pUrb);
1867 break;
1868 }
1869
1870 case VUSBXFERTYPE_INTR:
1871 {
1872 rc = vboxUsbSolarisIntrXfer(pState, pEp, pUrb);
1873 break;
1874 }
1875
1876 case VUSBXFERTYPE_ISOC:
1877 {
1878 rc = vboxUsbSolarisIsocXfer(pState, pEp, pUrb);
1879 break;
1880 }
1881
1882 default:
1883 {
1884 LogRelMax(5, (DEVICE_NAME ": vboxUsbSolarisSendUrb: URB type unsupported %d\n", pUrb->enmType));
1885 rc = VERR_NOT_SUPPORTED;
1886 break;
1887 }
1888 }
1889
1890 if (RT_FAILURE(rc))
1891 {
1892 mutex_enter(&pState->Mtx);
1893 freemsg(pUrb->pMsg);
1894 pUrb->pMsg = NULL;
1895 pMsg = NULL;
1896
1897 if ( pUrb->enmType == VUSBXFERTYPE_ISOC
1898 && pUrb->enmDir == VUSBDIRECTION_IN)
1899 {
1900 RTMemFree(pUrb);
1901 pUrb = NULL;
1902 }
1903 else
1904 {
1905 /*
1906 * Xfer failed, move URB back to the free list.
1907 */
1908 list_remove(&pState->hInflightUrbs, pUrb);
1909 Assert(pState->cInflightUrbs > 0);
1910 --pState->cInflightUrbs;
1911
1912 pUrb->enmState = VBOXUSB_URB_STATE_FREE;
1913 Assert(!pUrb->pMsg);
1914 list_insert_head(&pState->hFreeUrbs, pUrb);
1915 ++pState->cFreeUrbs;
1916 }
1917 mutex_exit(&pState->Mtx);
1918 }
1919 }
1920 else
1921 {
1922 LogRel((DEVICE_NAME ": vboxUsbSolarisSendUrb: Failed to queue URB\n"));
1923 rc = VERR_NO_MEMORY;
1924 freemsg(pMsg);
1925 }
1926 }
1927 else
1928 {
1929 mutex_exit(&pState->Mtx);
1930 freemsg(pMsg);
1931 }
1932
1933 return rc;
1934}
1935
1936
1937/**
1938 * Reaps a completed URB.
1939 *
1940 * @param pState The USB device instance.
1941 * @param pUrbReq Pointer to the VBox USB URB.
1942 * @param Mode The IOCtl mode.
1943 *
1944 * @returns VBox error code.
1945 */
1946LOCAL int vboxUsbSolarisReapUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, int Mode)
1947{
1948 /* LogFunc((DEVICE_NAME ": vboxUsbSolarisReapUrb: pState=%p pUrbReq=%p\n", pState, pUrbReq)); */
1949
1950 AssertPtrReturn(pUrbReq, VERR_INVALID_POINTER);
1951
1952 int rc = VINF_SUCCESS;
1953 mutex_enter(&pState->Mtx);
1954 rc = vboxUsbSolarisDeviceState(pState->DevState);
1955 if (!pState->fDefaultPipeOpen)
1956 rc = VERR_VUSB_DEVICE_NOT_ATTACHED;
1957 if (RT_SUCCESS(rc))
1958 {
1959 vboxusb_urb_t *pUrb = list_remove_head(&pState->hLandedUrbs);
1960 if (pUrb)
1961 {
1962 Assert(pState->cLandedUrbs > 0);
1963 --pState->cLandedUrbs;
1964 }
1965
1966 /*
1967 * It is safe to access pUrb->pMsg outside the state mutex because this is from the landed URB list
1968 * and not the inflight URB list.
1969 */
1970 mutex_exit(&pState->Mtx);
1971 if (pUrb)
1972 {
1973 /*
1974 * Copy the URB which will then be copied to user-space.
1975 */
1976 pUrbReq->pvUrbR3 = pUrb->pvUrbR3;
1977 pUrbReq->bEndpoint = pUrb->bEndpoint;
1978 pUrbReq->enmType = pUrb->enmType;
1979 pUrbReq->enmDir = pUrb->enmDir;
1980 pUrbReq->enmStatus = pUrb->enmStatus;
1981 pUrbReq->pvData = (void *)pUrb->pvDataR3;
1982 pUrbReq->cbData = pUrb->cbDataR3;
1983
1984 if (RT_LIKELY(pUrb->pMsg))
1985 {
1986 /*
1987 * Copy the message back into the user buffer.
1988 */
1989 if (RT_LIKELY(pUrb->pvDataR3 != NIL_RTR3PTR))
1990 {
1991 Assert(!pUrb->pMsg->b_cont); /* We really should have a single message block always. */
1992 size_t cbData = RT_MIN(MBLKL(pUrb->pMsg), pUrb->cbDataR3);
1993 pUrbReq->cbData = cbData;
1994
1995 if (RT_LIKELY(cbData))
1996 {
1997 rc = ddi_copyout(pUrb->pMsg->b_rptr, (void *)pUrbReq->pvData, cbData, Mode);
1998 if (RT_UNLIKELY(rc))
1999 {
2000 LogRel((DEVICE_NAME ": vboxUsbSolarisReapUrb: ddi_copyout failed! rc=%d\n", rc));
2001 pUrbReq->enmStatus = VUSBSTATUS_INVALID;
2002 }
2003 }
2004
2005 Log((DEVICE_NAME ": vboxUsbSolarisReapUrb: pvUrbR3=%p pvDataR3=%p cbData=%d\n", pUrbReq->pvUrbR3,
2006 pUrbReq->pvData, pUrbReq->cbData));
2007 }
2008 else
2009 {
2010 pUrbReq->cbData = 0;
2011 rc = VERR_INVALID_POINTER;
2012 LogRel((DEVICE_NAME ": vboxUsbSolarisReapUrb: Missing pvDataR3!!\n"));
2013 }
2014
2015 /*
2016 * Free buffer allocated in vboxUsbSolarisSendUrb or vboxUsbSolaris[Ctrl|Bulk|Intr]Xfer().
2017 */
2018 freemsg(pUrb->pMsg);
2019 pUrb->pMsg = NULL;
2020 }
2021 else
2022 {
2023 if ( pUrb->enmType == VUSBXFERTYPE_ISOC
2024 && pUrb->enmDir == VUSBDIRECTION_IN)
2025 {
2026 pUrbReq->enmStatus = VUSBSTATUS_INVALID;
2027 pUrbReq->cbData = 0;
2028 }
2029 }
2030
2031 /*
2032 * Copy Isoc packet descriptors.
2033 */
2034 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
2035 {
2036 AssertCompile(sizeof(pUrbReq->aIsocPkts) == sizeof(pUrb->aIsocPkts));
2037 pUrbReq->cIsocPkts = pUrb->cIsocPkts;
2038
2039 for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
2040 {
2041 pUrbReq->aIsocPkts[i].cbPkt = pUrb->aIsocPkts[i].cbPkt;
2042 pUrbReq->aIsocPkts[i].cbActPkt = pUrb->aIsocPkts[i].cbActPkt;
2043 pUrbReq->aIsocPkts[i].enmStatus = pUrb->aIsocPkts[i].enmStatus;
2044 }
2045
2046 if (pUrb->enmDir == VUSBDIRECTION_IN)
2047 {
2048 RTMemFree(pUrb);
2049 pUrb = NULL;
2050 }
2051 }
2052
2053 if (pUrb)
2054 {
2055 /*
2056 * Add URB back to the free list.
2057 */
2058 Assert(!pUrb->pMsg);
2059 pUrb->cbDataR3 = 0;
2060 pUrb->pvDataR3 = NIL_RTR3PTR;
2061 pUrb->enmState = VBOXUSB_URB_STATE_FREE;
2062 mutex_enter(&pState->Mtx);
2063 list_insert_head(&pState->hFreeUrbs, pUrb);
2064 ++pState->cFreeUrbs;
2065 mutex_exit(&pState->Mtx);
2066 }
2067 }
2068 else
2069 {
2070 pUrbReq->pvUrbR3 = NULL;
2071 pUrbReq->cbData = 0;
2072 pUrbReq->pvData = NULL;
2073 pUrbReq->enmStatus = VUSBSTATUS_INVALID;
2074 }
2075 }
2076 else
2077 mutex_exit(&pState->Mtx);
2078
2079 return rc;
2080}
2081
2082
2083/**
2084 * Clears a pipe (CLEAR_FEATURE).
2085 *
2086 * @param pState The USB device instance.
2087 * @param bEndpoint The Endpoint address.
2088 *
2089 * @returns VBox error code.
2090 */
2091LOCAL int vboxUsbSolarisClearEndPoint(vboxusb_state_t *pState, uint8_t bEndpoint)
2092{
2093 LogFunc((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: pState=%p bEndpoint=%#x\n", pState, bEndpoint));
2094
2095 mutex_enter(&pState->Mtx);
2096 int rc = vboxUsbSolarisDeviceState(pState->DevState);
2097 if (RT_SUCCESS(rc))
2098 {
2099 int iEpIndex = VBOXUSB_GET_EP_INDEX(bEndpoint);
2100 Assert(iEpIndex >= 0 && iEpIndex < RT_ELEMENTS(pState->aEps));
2101 vboxusb_ep_t *pEp = &pState->aEps[iEpIndex];
2102 if (RT_LIKELY(pEp))
2103 {
2104 /*
2105 * Check if the endpoint is open to be cleared.
2106 */
2107 if (pEp->pPipe)
2108 {
2109 mutex_exit(&pState->Mtx);
2110
2111 /*
2112 * Synchronous reset pipe.
2113 */
2114 usb_pipe_reset(pState->pDip, pEp->pPipe,
2115 USB_FLAGS_SLEEP, /* Synchronous */
2116 NULL, /* Completion callback */
2117 NULL); /* Exception callback */
2118
2119 mutex_enter(&pState->Mtx);
2120
2121 Log((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: bEndpoint=%#x[%d] returns %d\n", bEndpoint, iEpIndex, rc));
2122
2123 rc = VINF_SUCCESS;
2124 }
2125 else
2126 {
2127 Log((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: Not opened to be cleared. Faking success. bEndpoint=%#x\n",
2128 bEndpoint));
2129 rc = VINF_SUCCESS;
2130 }
2131 }
2132 else
2133 {
2134 LogRel((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: Endpoint missing! bEndpoint=%#x[%d]\n", bEndpoint, iEpIndex));
2135 rc = VERR_GENERAL_FAILURE;
2136 }
2137 }
2138 else
2139 Log((DEVICE_NAME ": vboxUsbSolarisClearEndPoint: Device not online, state=%d\n", pState->DevState));
2140
2141 mutex_exit(&pState->Mtx);
2142 return rc;
2143}
2144
2145
2146/**
2147 * Sets configuration (SET_CONFIGURATION)
2148 *
2149 * @param pState The USB device instance.
2150 * @param bConfig The Configuration.
2151 *
2152 * @returns VBox error code.
2153 */
2154LOCAL int vboxUsbSolarisSetConfig(vboxusb_state_t *pState, uint8_t bConfig)
2155{
2156 LogFunc((DEVICE_NAME ": vboxUsbSolarisSetConfig: pState=%p bConfig=%u\n", pState, bConfig));
2157
2158 mutex_enter(&pState->Mtx);
2159 int rc = vboxUsbSolarisDeviceState(pState->DevState);
2160 if (RT_SUCCESS(rc))
2161 {
2162 vboxUsbSolarisCloseAllPipes(pState, false /* ControlPipe */);
2163 int iCfgIndex = vboxUsbSolarisGetConfigIndex(pState, bConfig);
2164
2165 if ( iCfgIndex >= 0
2166 && iCfgIndex < pState->pDevDesc->dev_n_cfg)
2167 {
2168 /*
2169 * Switch Config synchronously.
2170 */
2171 mutex_exit(&pState->Mtx);
2172 rc = usb_set_cfg(pState->pDip, (uint_t)iCfgIndex, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback data */);
2173 mutex_enter(&pState->Mtx);
2174
2175 if (rc == USB_SUCCESS)
2176 {
2177 int rc2 = vboxUsbSolarisInitEpsForCfg(pState);
2178 AssertRC(rc2); NOREF(rc2);
2179 rc = VINF_SUCCESS;
2180 }
2181 else
2182 {
2183 LogRel((DEVICE_NAME ": vboxUsbSolarisSetConfig: usb_set_cfg failed for iCfgIndex=%#x bConfig=%u rc=%d\n",
2184 iCfgIndex, bConfig, rc));
2185 rc = vboxUsbSolarisToVBoxRC(rc);
2186 }
2187 }
2188 else
2189 {
2190 LogRel((DEVICE_NAME ": vboxUsbSolarisSetConfig: Invalid iCfgIndex=%d bConfig=%u\n", iCfgIndex, bConfig));
2191 rc = VERR_OUT_OF_RANGE;
2192 }
2193 }
2194
2195 mutex_exit(&pState->Mtx);
2196
2197 return rc;
2198}
2199
2200
2201/**
2202 * Gets configuration (GET_CONFIGURATION)
2203 *
2204 * @param pState The USB device instance.
2205 * @param pbConfig Where to store the Configuration.
2206 *
2207 * @returns VBox error code.
2208 */
2209LOCAL int vboxUsbSolarisGetConfig(vboxusb_state_t *pState, uint8_t *pbConfig)
2210{
2211 LogFunc((DEVICE_NAME ": vboxUsbSolarisGetConfig: pState=%p pbConfig=%p\n", pState, pbConfig));
2212 AssertPtrReturn(pbConfig, VERR_INVALID_POINTER);
2213
2214 /*
2215 * Get Config synchronously.
2216 */
2217 uint_t bConfig;
2218 int rc = usb_get_cfg(pState->pDip, &bConfig, USB_FLAGS_SLEEP);
2219 if (RT_LIKELY(rc == USB_SUCCESS))
2220 {
2221 *pbConfig = bConfig;
2222 rc = VINF_SUCCESS;
2223 }
2224 else
2225 {
2226 LogRel((DEVICE_NAME ": vboxUsbSolarisGetConfig: Failed, rc=%d\n", rc));
2227 rc = vboxUsbSolarisToVBoxRC(rc);
2228 }
2229
2230 Log((DEVICE_NAME ": vboxUsbSolarisGetConfig: Returns %d bConfig=%u\n", rc, *pbConfig));
2231 return rc;
2232}
2233
2234
2235/**
2236 * Sets interface (SET_INTERFACE) and alternate.
2237 *
2238 * @param pState The USB device instance.
2239 * @param bIf The Interface.
2240 * @param bAlt The Alternate setting.
2241 *
2242 * @returns VBox error code.
2243 */
2244LOCAL int vboxUsbSolarisSetInterface(vboxusb_state_t *pState, uint8_t bIf, uint8_t bAlt)
2245{
2246 LogFunc((DEVICE_NAME ": vboxUsbSolarisSetInterface: pState=%p bIf=%#x bAlt=%#x\n", pState, bIf, bAlt));
2247
2248 mutex_enter(&pState->Mtx);
2249 int rc = vboxUsbSolarisDeviceState(pState->DevState);
2250 if (RT_SUCCESS(rc))
2251 {
2252 /*
2253 * Set Interface & Alt setting synchronously.
2254 */
2255 mutex_exit(&pState->Mtx);
2256 rc = usb_set_alt_if(pState->pDip, bIf, bAlt, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback data */);
2257 mutex_enter(&pState->Mtx);
2258
2259 if (rc == USB_SUCCESS)
2260 {
2261 Log((DEVICE_NAME ": vboxUsbSolarisSetInterface: Success, bIf=%#x bAlt=%#x\n", bIf, bAlt, rc));
2262 int rc2 = vboxUsbSolarisInitEpsForIfAlt(pState, bIf, bAlt);
2263 AssertRC(rc2); NOREF(rc2);
2264 rc = VINF_SUCCESS;
2265 }
2266 else
2267 {
2268 LogRel((DEVICE_NAME ": vboxUsbSolarisSetInterface: usb_set_alt_if failed for bIf=%#x bAlt=%#x rc=%d\n", bIf, bAlt, rc));
2269 rc = vboxUsbSolarisToVBoxRC(rc);
2270 }
2271 }
2272
2273 mutex_exit(&pState->Mtx);
2274
2275 return rc;
2276}
2277
2278
2279/**
2280 * Closes the USB device and optionally resets it.
2281 *
2282 * @param pState The USB device instance.
2283 * @param enmReset The reset level.
2284 *
2285 * @returns VBox error code.
2286 */
2287LOCAL int vboxUsbSolarisCloseDevice(vboxusb_state_t *pState, VBOXUSB_RESET_LEVEL enmReset)
2288{
2289 LogFunc((DEVICE_NAME ": vboxUsbSolarisCloseDevice: pState=%p enmReset=%d\n", pState, enmReset));
2290
2291 mutex_enter(&pState->Mtx);
2292 int rc = vboxUsbSolarisDeviceState(pState->DevState);
2293
2294 if (enmReset == VBOXUSB_RESET_LEVEL_CLOSE)
2295 vboxUsbSolarisCloseAllPipes(pState, true /* ControlPipe */);
2296 else
2297 vboxUsbSolarisCloseAllPipes(pState, false /* ControlPipe */);
2298
2299 mutex_exit(&pState->Mtx);
2300
2301 if (RT_SUCCESS(rc))
2302 {
2303 switch (enmReset)
2304 {
2305 case VBOXUSB_RESET_LEVEL_REATTACH:
2306 rc = usb_reset_device(pState->pDip, USB_RESET_LVL_REATTACH);
2307 break;
2308
2309 case VBOXUSB_RESET_LEVEL_SOFT:
2310 rc = usb_reset_device(pState->pDip, USB_RESET_LVL_DEFAULT);
2311 break;
2312
2313 default:
2314 rc = USB_SUCCESS;
2315 break;
2316 }
2317
2318 rc = vboxUsbSolarisToVBoxRC(rc);
2319 }
2320
2321 Log((DEVICE_NAME ": vboxUsbSolarisCloseDevice: Returns %d\n", rc));
2322 return rc;
2323}
2324
2325
2326/**
2327 * Aborts pending requests and reset the pipe.
2328 *
2329 * @param pState The USB device instance.
2330 * @param bEndpoint The Endpoint address.
2331 *
2332 * @returns VBox error code.
2333 */
2334LOCAL int vboxUsbSolarisAbortPipe(vboxusb_state_t *pState, uint8_t bEndpoint)
2335{
2336 LogFunc((DEVICE_NAME ": vboxUsbSolarisAbortPipe: pState=%p bEndpoint=%#x\n", pState, bEndpoint));
2337
2338 mutex_enter(&pState->Mtx);
2339 int rc = vboxUsbSolarisDeviceState(pState->DevState);
2340 if (RT_SUCCESS(rc))
2341 {
2342 int iEpIndex = VBOXUSB_GET_EP_INDEX(bEndpoint);
2343 Assert(iEpIndex >= 0 && iEpIndex < RT_ELEMENTS(pState->aEps));
2344 vboxusb_ep_t *pEp = &pState->aEps[iEpIndex];
2345 if (RT_LIKELY(pEp))
2346 {
2347 if (pEp->pPipe)
2348 {
2349 /*
2350 * Aborting requests not supported for the default control pipe.
2351 */
2352 if ((pEp->EpDesc.bEndpointAddress & USB_EP_NUM_MASK) == 0)
2353 {
2354 mutex_exit(&pState->Mtx);
2355 LogRel((DEVICE_NAME ": vboxUsbSolarisAbortPipe: Cannot reset default control pipe\n"));
2356 return VERR_NOT_SUPPORTED;
2357 }
2358
2359 mutex_exit(&pState->Mtx);
2360 usb_pipe_reset(pState->pDip, pEp->pPipe,
2361 USB_FLAGS_SLEEP, /* Synchronous */
2362 NULL, /* Completion callback */
2363 NULL); /* Callback's parameter */
2364
2365 /*
2366 * Allow pending async requests to complete.
2367 */
2368 /** @todo this is most likely not required. */
2369 rc = usb_pipe_drain_reqs(pState->pDip, pEp->pPipe,
2370 USB_FLAGS_SLEEP, /* Synchronous */
2371 5, /* Timeout (seconds) */
2372 NULL, /* Completion callback */
2373 NULL); /* Callback's parameter */
2374
2375 mutex_enter(&pState->Mtx);
2376
2377 Log((DEVICE_NAME ": vboxUsbSolarisAbortPipe: usb_pipe_drain_reqs returns %d\n", rc));
2378 rc = vboxUsbSolarisToVBoxRC(rc);
2379 }
2380 else
2381 {
2382 LogRel((DEVICE_NAME ": vboxUsbSolarisAbortPipe: pipe not open. bEndpoint=%#x\n", bEndpoint));
2383 rc = VERR_PIPE_IO_ERROR;
2384 }
2385 }
2386 else
2387 {
2388 LogRel((DEVICE_NAME ": vboxUsbSolarisAbortPipe: Invalid pipe bEndpoint=%#x[%d]\n", bEndpoint, iEpIndex));
2389 rc = VERR_INVALID_HANDLE;
2390 }
2391 }
2392
2393 mutex_exit(&pState->Mtx);
2394
2395 LogFunc((DEVICE_NAME ": vboxUsbSolarisAbortPipe: Returns %d\n", rc));
2396 return rc;
2397}
2398
2399
2400/**
2401 * Initializes an Endpoint.
2402 *
2403 * @param pState The USB device instance.
2404 * @param pEpData The Endpoint data (NULL implies the default
2405 * endpoint).
2406 *
2407 * @returns VBox error code.
2408 */
2409LOCAL int vboxUsbSolarisInitEp(vboxusb_state_t *pState, usb_ep_data_t *pEpData)
2410{
2411 LogFunc((DEVICE_NAME ": vboxUsbSolarisInitEp: pState=%p pEpData=%p", pState, pEpData));
2412
2413 /*
2414 * Is this the default endpoint?
2415 */
2416 usb_ep_descr_t *pEpDesc = NULL;
2417 vboxusb_ep_t *pEp = NULL;
2418 int iEpIndex;
2419 if (!pEpData)
2420 {
2421 iEpIndex = 0;
2422 pEpDesc = &g_VBoxUSBSolarisDefaultEpDesc;
2423 }
2424 else
2425 {
2426 iEpIndex = VBOXUSB_GET_EP_INDEX(pEpData->ep_descr.bEndpointAddress);
2427 pEpDesc = (usb_ep_descr_t *)((uint8_t *)pEpData + g_offUsbEpDataDescr);
2428 }
2429
2430 Assert(iEpIndex >= 0 && iEpIndex < RT_ELEMENTS(pState->aEps));
2431 pEp = &pState->aEps[iEpIndex];
2432
2433 /*
2434 * Initialize the endpoint.
2435 */
2436 pEp->EpDesc = *pEpDesc;
2437 if (!pEp->fInitialized)
2438 {
2439 pEp->pPipe = NULL;
2440 bzero(&pEp->PipePolicy, sizeof(pEp->PipePolicy));
2441 pEp->PipePolicy.pp_max_async_reqs = VBOXUSB_MAX_PIPE_ASYNC_REQS;
2442 pEp->fIsocPolling = false;
2443 list_create(&pEp->hIsocInUrbs, sizeof(vboxusb_urb_t), offsetof(vboxusb_urb_t, hListLink));
2444 pEp->cIsocInUrbs = 0;
2445 list_create(&pEp->hIsocInLandedReqs, sizeof(vboxusb_isoc_req_t), offsetof(vboxusb_isoc_req_t, hListLink));
2446 pEp->cbIsocInLandedReqs = 0;
2447 pEp->cbMaxIsocData = 0;
2448 pEp->fInitialized = true;
2449 }
2450
2451 Log((DEVICE_NAME ": vboxUsbSolarisInitEp: Success, %s[%2d] %s %s bEndpoint=%#x\n", !pEpData ? "Default " : "Endpoint",
2452 iEpIndex, vboxUsbSolarisEpType(pEp), vboxUsbSolarisEpDir(pEp), pEp->EpDesc.bEndpointAddress));
2453 return VINF_SUCCESS;
2454}
2455
2456
2457/**
2458 * Initializes Endpoints for the current configuration, all interfaces and
2459 * alternate setting 0 for each interface.
2460 *
2461 * @param pState The USB device instance.
2462 *
2463 * @returns VBox status code.
2464 */
2465LOCAL int vboxUsbSolarisInitEpsForCfg(vboxusb_state_t *pState)
2466{
2467 uint_t uCfgIndex = usb_get_current_cfgidx(pState->pDip);
2468 if (uCfgIndex >= pState->pDevDesc->dev_n_cfg)
2469 {
2470 LogRel((DEVICE_NAME ": vboxUsbSolarisInitEpsForCfg: Invalid current config index %u\n", uCfgIndex));
2471 return VERR_OUT_OF_RANGE;
2472 }
2473
2474 usb_cfg_data_t *pConfig = &pState->pDevDesc->dev_cfg[uCfgIndex];
2475 uchar_t bConfig = pConfig->cfg_descr.bConfigurationValue;
2476
2477 LogFunc((DEVICE_NAME ": vboxUsbSolarisInitEpsForCfg: pState=%p bConfig=%u uCfgIndex=%u\n", pState, bConfig, uCfgIndex));
2478
2479 const uint_t cIfs = pConfig->cfg_n_if;
2480 for (uchar_t uIf = 0; uIf < cIfs; uIf++)
2481 {
2482 usb_if_data_t *pIf = &pConfig->cfg_if[uIf];
2483 const uint_t cAlts = pIf->if_n_alt;
2484 for (uchar_t uAlt = 0; uAlt < cAlts; uAlt++)
2485 {
2486 usb_alt_if_data_t *pAlt = &pIf->if_alt[uAlt];
2487 if (pAlt->altif_descr.bAlternateSetting == 0) /* Refer USB 2.0 spec 9.6.5 "Interface" */
2488 {
2489 const uint_t cEps = pAlt->altif_n_ep;
2490 for (uchar_t uEp = 0; uEp < cEps; uEp++)
2491 {
2492 uint8_t *pbEpData = (uint8_t *)&pAlt->altif_ep[0];
2493 usb_ep_data_t *pEpData = (usb_ep_data_t *)(pbEpData + uEp * g_cbUsbEpData);
2494 int rc = vboxUsbSolarisInitEp(pState, pEpData);
2495 if (RT_FAILURE(rc))
2496 {
2497 LogRel((DEVICE_NAME ": vboxUsbSolarisInitEpsForCfg: Failed to init endpoint! "
2498 "bConfig=%u bIf=%#x bAlt=%#x\n", bConfig, pAlt->altif_descr.bInterfaceNumber,
2499 pAlt->altif_descr.bAlternateSetting));
2500 return rc;
2501 }
2502 }
2503 break; /* move on to next interface. */
2504 }
2505 }
2506 }
2507 return VINF_SUCCESS;
2508}
2509
2510
2511/**
2512 * Initializes Endpoints for the given Interface & Alternate setting.
2513 *
2514 * @param pState The USB device instance.
2515 * @param bIf The Interface.
2516 * @param bAlt The Alterate.
2517 *
2518 * @returns VBox status code.
2519 */
2520LOCAL int vboxUsbSolarisInitEpsForIfAlt(vboxusb_state_t *pState, uint8_t bIf, uint8_t bAlt)
2521{
2522 LogFunc((DEVICE_NAME ": vboxUsbSolarisInitEpsForIfAlt: pState=%p bIf=%d uAlt=%d\n", pState, bIf, bAlt));
2523
2524 /* Doesn't hurt to be paranoid */
2525 uint_t uCfgIndex = usb_get_current_cfgidx(pState->pDip);
2526 if (uCfgIndex >= pState->pDevDesc->dev_n_cfg)
2527 {
2528 LogRel((DEVICE_NAME ": vboxUsbSolarisInitEpsForIfAlt: Invalid current config index %d\n", uCfgIndex));
2529 return VERR_OUT_OF_RANGE;
2530 }
2531
2532 usb_cfg_data_t *pConfig = &pState->pDevDesc->dev_cfg[uCfgIndex];
2533 for (uchar_t uIf = 0; uIf < pConfig->cfg_n_if; uIf++)
2534 {
2535 usb_if_data_t *pInterface = &pConfig->cfg_if[uIf];
2536 const uint_t cAlts = pInterface->if_n_alt;
2537 for (uchar_t uAlt = 0; uAlt < cAlts; uAlt++)
2538 {
2539 usb_alt_if_data_t *pAlt = &pInterface->if_alt[uAlt];
2540 if ( pAlt->altif_descr.bInterfaceNumber == bIf
2541 && pAlt->altif_descr.bAlternateSetting == bAlt)
2542 {
2543 const uint_t cEps = pAlt->altif_n_ep;
2544 for (uchar_t uEp = 0; uEp < cEps; uEp++)
2545 {
2546 uint8_t *pbEpData = (uint8_t *)&pAlt->altif_ep[0];
2547 usb_ep_data_t *pEpData = (usb_ep_data_t *)(pbEpData + uEp * g_cbUsbEpData);
2548 int rc = vboxUsbSolarisInitEp(pState, pEpData);
2549 if (RT_FAILURE(rc))
2550 {
2551 uint8_t bCfgValue = pConfig->cfg_descr.bConfigurationValue;
2552 LogRel((DEVICE_NAME ": vboxUsbSolarisInitEpsForIfAlt: Failed to init endpoint! "
2553 "bCfgValue=%u bIf=%#x bAlt=%#x\n", bCfgValue, bIf, bAlt));
2554 return rc;
2555 }
2556 }
2557 return VINF_SUCCESS;
2558 }
2559 }
2560 }
2561 return VERR_NOT_FOUND;
2562}
2563
2564
2565/**
2566 * Destroys all Endpoints.
2567 *
2568 * @param pState The USB device instance.
2569 *
2570 * @remarks Requires the state mutex to be held.
2571 * Call only from Detach() or similar as callbacks
2572 */
2573LOCAL void vboxUsbSolarisDestroyAllEps(vboxusb_state_t *pState)
2574{
2575 LogFunc((DEVICE_NAME ": vboxUsbSolarisDestroyAllEps: pState=%p\n", pState));
2576
2577 Assert(mutex_owned(&pState->Mtx));
2578 for (unsigned i = 0; i < VBOXUSB_MAX_ENDPOINTS; i++)
2579 {
2580 vboxusb_ep_t *pEp = &pState->aEps[i];
2581 if (pEp->fInitialized)
2582 vboxUsbSolarisDestroyEp(pState, pEp);
2583 }
2584}
2585
2586
2587/**
2588 * Destroys an Endpoint.
2589 *
2590 * @param pState The USB device instance.
2591 * @param pEp The Endpoint.
2592 *
2593 * @remarks Requires the state mutex to be held.
2594 */
2595LOCAL void vboxUsbSolarisDestroyEp(vboxusb_state_t *pState, vboxusb_ep_t *pEp)
2596{
2597 LogFunc((DEVICE_NAME ": vboxUsbSolarisDestroyEp: pState=%p pEp=%p\n", pState, pEp));
2598
2599 Assert(pEp->fInitialized);
2600 Assert(mutex_owned(&pState->Mtx));
2601 vboxusb_urb_t *pUrb = list_remove_head(&pEp->hIsocInUrbs);
2602 while (pUrb)
2603 {
2604 if (pUrb->pMsg)
2605 freemsg(pUrb->pMsg);
2606 RTMemFree(pUrb);
2607 pUrb = list_remove_head(&pEp->hIsocInUrbs);
2608 }
2609 pEp->cIsocInUrbs = 0;
2610 list_destroy(&pEp->hIsocInUrbs);
2611
2612 vboxusb_isoc_req_t *pIsocReq = list_remove_head(&pEp->hIsocInLandedReqs);
2613 while (pIsocReq)
2614 {
2615 kmem_free(pIsocReq, sizeof(vboxusb_isoc_req_t));
2616 pIsocReq = list_remove_head(&pEp->hIsocInLandedReqs);
2617 }
2618 pEp->cbIsocInLandedReqs = 0;
2619 list_destroy(&pEp->hIsocInLandedReqs);
2620
2621 pEp->fInitialized = false;
2622}
2623
2624
2625/**
2626 * Closes all non-default pipes and drains the default pipe.
2627 *
2628 * @param pState The USB device instance.
2629 * @param fDefault Whether to close the default control pipe.
2630 *
2631 * @remarks Requires the device state mutex to be held.
2632 */
2633LOCAL void vboxUsbSolarisCloseAllPipes(vboxusb_state_t *pState, bool fDefault)
2634{
2635 LogFunc((DEVICE_NAME ": vboxUsbSolarisCloseAllPipes: pState=%p\n", pState));
2636
2637 for (int i = 1; i < VBOXUSB_MAX_ENDPOINTS; i++)
2638 {
2639 vboxusb_ep_t *pEp = &pState->aEps[i];
2640 if ( pEp
2641 && pEp->pPipe)
2642 {
2643 Log((DEVICE_NAME ": vboxUsbSolarisCloseAllPipes: Closing[%d]\n", i));
2644 vboxUsbSolarisClosePipe(pState, pEp);
2645 }
2646 }
2647
2648 if (fDefault)
2649 {
2650 vboxusb_ep_t *pEp = &pState->aEps[0];
2651 if ( pEp
2652 && pEp->pPipe)
2653 {
2654 vboxUsbSolarisClosePipe(pState, pEp);
2655 Log((DEVICE_NAME ": vboxUsbSolarisCloseAllPipes: Closed default pipe\n"));
2656 }
2657 }
2658}
2659
2660
2661/**
2662 * Opens the pipe associated with an Endpoint.
2663 *
2664 * @param pState The USB device instance.
2665 * @param pEp The Endpoint.
2666 * @remarks Requires the device state mutex to be held.
2667 *
2668 * @returns VBox status code.
2669 */
2670LOCAL int vboxUsbSolarisOpenPipe(vboxusb_state_t *pState, vboxusb_ep_t *pEp)
2671{
2672 Assert(mutex_owned(&pState->Mtx));
2673
2674 /*
2675 * Make sure the Endpoint isn't open already.
2676 */
2677 if (pEp->pPipe)
2678 return VINF_SUCCESS;
2679
2680 /*
2681 * Default Endpoint; already opened just copy the pipe handle.
2682 */
2683 if ((pEp->EpDesc.bEndpointAddress & USB_EP_NUM_MASK) == 0)
2684 {
2685 pEp->pPipe = pState->pDevDesc->dev_default_ph;
2686 Log((DEVICE_NAME ": vboxUsbSolarisOpenPipe: Default pipe opened\n"));
2687 return VINF_SUCCESS;
2688 }
2689
2690 /*
2691 * Open the non-default pipe for the Endpoint.
2692 */
2693 mutex_exit(&pState->Mtx);
2694 int rc = usb_pipe_open(pState->pDip, &pEp->EpDesc, &pEp->PipePolicy, USB_FLAGS_NOSLEEP, &pEp->pPipe);
2695 mutex_enter(&pState->Mtx);
2696 if (rc == USB_SUCCESS)
2697 {
2698 LogFunc((DEVICE_NAME ": vboxUsbSolarisOpenPipe: Opened pipe, pState=%p pEp=%p\n", pState, pEp));
2699 usb_pipe_set_private(pEp->pPipe, (usb_opaque_t)pEp);
2700
2701 /*
2702 * Determine input buffer size for Isoc. IN transfers.
2703 */
2704 if ( VBOXUSB_XFER_TYPE(pEp) == VUSBXFERTYPE_ISOC
2705 && VBOXUSB_XFER_DIR(pEp) == VUSB_DIR_TO_HOST)
2706 {
2707 /*
2708 * wMaxPacketSize bits 10..0 specifies maximum packet size which can hold 1024 bytes.
2709 * If bits 12..11 is non-zero, cbMax will be more than 1024 and thus the Endpoint is a
2710 * high-bandwidth Endpoint.
2711 */
2712 uint16_t cbMax = VBOXUSB_PKT_SIZE(pEp->EpDesc.wMaxPacketSize);
2713 if (cbMax <= 1024)
2714 {
2715 /* Buffer 1 second for highspeed and 8 seconds for fullspeed Endpoints. */
2716 pEp->cbMaxIsocData = 1000 * cbMax * 8;
2717 }
2718 else
2719 {
2720 /* Buffer about 400 milliseconds of data for highspeed high-bandwidth endpoints. */
2721 pEp->cbMaxIsocData = 400 * cbMax * 8;
2722 }
2723 Log((DEVICE_NAME ": vboxUsbSolarisOpenPipe: bEndpoint=%#x cbMaxIsocData=%u\n", pEp->EpDesc.bEndpointAddress,
2724 pEp->cbMaxIsocData));
2725 }
2726
2727 rc = VINF_SUCCESS;
2728 }
2729 else
2730 {
2731 LogRel((DEVICE_NAME ": vboxUsbSolarisOpenPipe: Failed! rc=%d pState=%p pEp=%p\n", rc, pState, pEp));
2732 rc = VERR_BAD_PIPE;
2733 }
2734
2735 return rc;
2736}
2737
2738
2739/**
2740 * Closes the pipe associated with an Endpoint.
2741 *
2742 * @param pState The USB device instance.
2743 * @param pEp The Endpoint.
2744 *
2745 * @remarks Requires the device state mutex to be held.
2746 */
2747LOCAL void vboxUsbSolarisClosePipe(vboxusb_state_t *pState, vboxusb_ep_t *pEp)
2748{
2749 LogFunc((DEVICE_NAME ": vboxUsbSolarisClosePipe: pState=%p pEp=%p\n", pState, pEp));
2750 AssertPtr(pEp);
2751
2752 if (pEp->pPipe)
2753 {
2754 /*
2755 * Default pipe: allow completion of pending requests.
2756 */
2757 if (pEp->pPipe == pState->pDevDesc->dev_default_ph)
2758 {
2759 mutex_exit(&pState->Mtx);
2760 usb_pipe_drain_reqs(pState->pDip, pEp->pPipe, 0, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback arg. */);
2761 mutex_enter(&pState->Mtx);
2762 Log((DEVICE_NAME ": vboxUsbSolarisClosePipe: Closed default pipe\n"));
2763 pState->fDefaultPipeOpen = false;
2764 }
2765 else
2766 {
2767 /*
2768 * Stop Isoc. IN polling if required.
2769 */
2770 if (pEp->fIsocPolling)
2771 {
2772 pEp->fIsocPolling = false;
2773 mutex_exit(&pState->Mtx);
2774 usb_pipe_stop_isoc_polling(pEp->pPipe, USB_FLAGS_NOSLEEP);
2775 mutex_enter(&pState->Mtx);
2776 }
2777
2778 /*
2779 * Non-default pipe: close it.
2780 */
2781 Log((DEVICE_NAME ": vboxUsbSolarisClosePipe: Pipe bmAttributes=%#x bEndpoint=%#x\n", pEp->EpDesc.bmAttributes,
2782 pEp->EpDesc.bEndpointAddress));
2783 mutex_exit(&pState->Mtx);
2784 usb_pipe_close(pState->pDip, pEp->pPipe, USB_FLAGS_SLEEP, NULL /* callback */, NULL /* callback arg. */);
2785 mutex_enter(&pState->Mtx);
2786 }
2787
2788 /*
2789 * Free the Endpoint data message block and reset pipe handle.
2790 */
2791 pEp->pPipe = NULL;
2792
2793 Log((DEVICE_NAME ": vboxUsbSolarisClosePipe: Success, bEndpoint=%#x\n", pEp->EpDesc.bEndpointAddress));
2794 }
2795
2796 Assert(pEp->pPipe == NULL);
2797}
2798
2799
2800/**
2801 * Finds the Configuration index for the passed in Configuration value.
2802 *
2803 * @param pState The USB device instance.
2804 * @param bConfig The Configuration.
2805 *
2806 * @returns The configuration index if found, otherwise -1.
2807 */
2808LOCAL int vboxUsbSolarisGetConfigIndex(vboxusb_state_t *pState, uint_t bConfig)
2809{
2810 for (int CfgIndex = 0; CfgIndex < pState->pDevDesc->dev_n_cfg; CfgIndex++)
2811 {
2812 usb_cfg_data_t *pConfig = &pState->pDevDesc->dev_cfg[CfgIndex];
2813 if (pConfig->cfg_descr.bConfigurationValue == bConfig)
2814 return CfgIndex;
2815 }
2816
2817 return -1;
2818}
2819
2820
2821/**
2822 * Allocates and initializes an Isoc. In URB from the ring-3 equivalent.
2823 *
2824 * @param pState The USB device instance.
2825 * @param pUrbReq Opaque pointer to the complete request.
2826 *
2827 * @returns The allocated Isoc. In URB to be used.
2828 */
2829LOCAL vboxusb_urb_t *vboxUsbSolarisGetIsocInUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq)
2830{
2831 /*
2832 * Isoc. In URBs are not queued into the Inflight list like every other URBs.
2833 * For now we allocate each URB which gets queued into the respective Endpoint during Xfer.
2834 */
2835 vboxusb_urb_t *pUrb = RTMemAllocZ(sizeof(vboxusb_urb_t));
2836 if (RT_LIKELY(pUrb))
2837 {
2838 pUrb->enmState = VBOXUSB_URB_STATE_INFLIGHT;
2839 pUrb->pState = pState;
2840
2841 if (RT_LIKELY(pUrbReq))
2842 {
2843 pUrb->pvUrbR3 = pUrbReq->pvUrbR3;
2844 pUrb->bEndpoint = pUrbReq->bEndpoint;
2845 pUrb->enmType = pUrbReq->enmType;
2846 pUrb->enmDir = pUrbReq->enmDir;
2847 pUrb->enmStatus = pUrbReq->enmStatus;
2848 pUrb->cbDataR3 = pUrbReq->cbData;
2849 pUrb->pvDataR3 = (RTR3PTR)pUrbReq->pvData;
2850 pUrb->cIsocPkts = pUrbReq->cIsocPkts;
2851
2852 for (unsigned i = 0; i < pUrbReq->cIsocPkts; i++)
2853 pUrb->aIsocPkts[i].cbPkt = pUrbReq->aIsocPkts[i].cbPkt;
2854
2855 pUrb->pMsg = NULL;
2856 }
2857 }
2858 else
2859 LogRel((DEVICE_NAME ": vboxUsbSolarisGetIsocInUrb: Failed to alloc %d bytes\n", sizeof(vboxusb_urb_t)));
2860 return pUrb;
2861}
2862
2863
2864/**
2865 * Queues a URB reusing previously allocated URBs as required.
2866 *
2867 * @param pState The USB device instance.
2868 * @param pUrbReq Opaque pointer to the complete request.
2869 * @param pMsg Pointer to the allocated request data.
2870 *
2871 * @returns The allocated URB to be used, or NULL upon failure.
2872 */
2873LOCAL vboxusb_urb_t *vboxUsbSolarisQueueUrb(vboxusb_state_t *pState, PVBOXUSBREQ_URB pUrbReq, mblk_t *pMsg)
2874{
2875 Assert(pUrbReq);
2876 LogFunc((DEVICE_NAME ": vboxUsbSolarisQueueUrb: pState=%p pUrbReq=%p\n", pState, pUrbReq));
2877
2878 mutex_enter(&pState->Mtx);
2879
2880 /*
2881 * Grab a URB from the free list.
2882 */
2883 vboxusb_urb_t *pUrb = list_remove_head(&pState->hFreeUrbs);
2884 if (pUrb)
2885 {
2886 Assert(pUrb->enmState == VBOXUSB_URB_STATE_FREE);
2887 Assert(!pUrb->pMsg);
2888 Assert(pState->cFreeUrbs > 0);
2889 --pState->cFreeUrbs;
2890 }
2891 else
2892 {
2893 /*
2894 * We can't discard "old" URBs. For instance, INTR IN URBs that don't complete as
2895 * they don't have a timeout can essentially take arbitrarily long to complete depending
2896 * on the device and it's not safe to discard them in case they -do- complete. However,
2897 * we also have to reasonably assume a device doesn't have too many pending URBs always.
2898 *
2899 * Thus we just use a large queue and simply refuse further transfers. This is not
2900 * a situation which normally ever happens as usually there are at most than 4 or 5 URBs
2901 * in-flight until we reap them.
2902 */
2903 uint32_t const cTotalUrbs = pState->cInflightUrbs + pState->cFreeUrbs + pState->cLandedUrbs;
2904 if (cTotalUrbs >= VBOXUSB_URB_QUEUE_SIZE)
2905 {
2906 mutex_exit(&pState->Mtx);
2907 LogRelMax(5, (DEVICE_NAME ": vboxUsbSolarisQueueUrb: Max queue size %u reached, refusing further transfers",
2908 cTotalUrbs));
2909 return NULL;
2910 }
2911
2912 /*
2913 * Allocate a new URB as we have no free URBs.
2914 */
2915 mutex_exit(&pState->Mtx);
2916 pUrb = RTMemAllocZ(sizeof(vboxusb_urb_t));
2917 if (RT_UNLIKELY(!pUrb))
2918 {
2919 LogRel((DEVICE_NAME ": vboxUsbSolarisQueueUrb: Failed to alloc %d bytes\n", sizeof(vboxusb_urb_t)));
2920 return NULL;
2921 }
2922 mutex_enter(&pState->Mtx);
2923 }
2924
2925 /*
2926 * Add the URB to the inflight list.
2927 */
2928 list_insert_tail(&pState->hInflightUrbs, pUrb);
2929 ++pState->cInflightUrbs;
2930
2931 Assert(!pUrb->pMsg);
2932 pUrb->pMsg = pMsg;
2933 pUrb->pState = pState;
2934 pUrb->enmState = VBOXUSB_URB_STATE_INFLIGHT;
2935 pUrb->pvUrbR3 = pUrbReq->pvUrbR3;
2936 pUrb->bEndpoint = pUrbReq->bEndpoint;
2937 pUrb->enmType = pUrbReq->enmType;
2938 pUrb->enmDir = pUrbReq->enmDir;
2939 pUrb->enmStatus = pUrbReq->enmStatus;
2940 pUrb->fShortOk = pUrbReq->fShortOk;
2941 pUrb->pvDataR3 = (RTR3PTR)pUrbReq->pvData;
2942 pUrb->cbDataR3 = pUrbReq->cbData;
2943 pUrb->cIsocPkts = pUrbReq->cIsocPkts;
2944 if (pUrbReq->enmType == VUSBXFERTYPE_ISOC)
2945 {
2946 for (unsigned i = 0; i < pUrbReq->cIsocPkts; i++)
2947 pUrb->aIsocPkts[i].cbPkt = pUrbReq->aIsocPkts[i].cbPkt;
2948 }
2949
2950 mutex_exit(&pState->Mtx);
2951 return pUrb;
2952}
2953
2954
2955/**
2956 * Dequeues a completed URB into the landed list and informs user-land.
2957 *
2958 * @param pUrb The URB to move.
2959 * @param URBStatus The Solaris URB completion code.
2960 *
2961 * @remarks All pipes could be closed at this point (e.g. Device disconnected during inflight URBs)
2962 */
2963LOCAL void vboxUsbSolarisDeQueueUrb(vboxusb_urb_t *pUrb, int URBStatus)
2964{
2965 LogFunc((DEVICE_NAME ": vboxUsbSolarisDeQueue: pUrb=%p\n", pUrb));
2966 AssertPtrReturnVoid(pUrb);
2967
2968 pUrb->enmStatus = vboxUsbSolarisGetUrbStatus(URBStatus);
2969 if (pUrb->enmStatus != VUSBSTATUS_OK)
2970 Log((DEVICE_NAME ": vboxUsbSolarisDeQueueUrb: URB failed! URBStatus=%d bEndpoint=%#x\n", URBStatus, pUrb->bEndpoint));
2971
2972 vboxusb_state_t *pState = pUrb->pState;
2973 if (RT_LIKELY(pState))
2974 {
2975 mutex_enter(&pState->Mtx);
2976 pUrb->enmState = VBOXUSB_URB_STATE_LANDED;
2977
2978 /*
2979 * Remove it from the inflight list & move it to the landed list.
2980 */
2981 list_remove(&pState->hInflightUrbs, pUrb);
2982 Assert(pState->cInflightUrbs > 0);
2983 --pState->cInflightUrbs;
2984
2985 list_insert_tail(&pState->hLandedUrbs, pUrb);
2986 ++pState->cLandedUrbs;
2987
2988 vboxUsbSolarisNotifyComplete(pUrb->pState);
2989 mutex_exit(&pState->Mtx);
2990 return;
2991 }
2992
2993 /* Well, let's at least not leak memory... */
2994 freemsg(pUrb->pMsg);
2995 pUrb->pMsg = NULL;
2996 pUrb->enmStatus = VUSBSTATUS_INVALID;
2997
2998 LogRel((DEVICE_NAME ": vboxUsbSolarisDeQueue: State Gone\n"));
2999}
3000
3001
3002/**
3003 * Concatenates a chain message block into a single message block if possible.
3004 *
3005 * @param pUrb The URB to move.
3006 */
3007LOCAL void vboxUsbSolarisConcatMsg(vboxusb_urb_t *pUrb)
3008{
3009 /*
3010 * Concatenate the whole message rather than doing a chained copy while reaping.
3011 */
3012 if ( pUrb->pMsg
3013 && pUrb->pMsg->b_cont)
3014 {
3015 mblk_t *pFullMsg = msgpullup(pUrb->pMsg, -1 /* all data */);
3016 if (RT_LIKELY(pFullMsg))
3017 {
3018 freemsg(pUrb->pMsg);
3019 pUrb->pMsg = pFullMsg;
3020 }
3021 else
3022 LogRel((DEVICE_NAME ": vboxUsbSolarisConcatMsg: Failed. Expect glitches due to truncated data!\n"));
3023 }
3024}
3025
3026
3027/**
3028 * Wakes up a user process signalling URB completion.
3029 *
3030 * @param pState The USB device instance.
3031 * @remarks Requires the device state mutex to be held.
3032 */
3033LOCAL void vboxUsbSolarisNotifyComplete(vboxusb_state_t *pState)
3034{
3035 if (pState->fPollPending)
3036 {
3037 pollhead_t *pPollHead = &pState->PollHead;
3038 pState->fPollPending = false;
3039 mutex_exit(&pState->Mtx);
3040 pollwakeup(pPollHead, POLLIN);
3041 mutex_enter(&pState->Mtx);
3042 }
3043}
3044
3045
3046/**
3047 * Wakes up a user process signalling a device unplug events.
3048 *
3049 * @param pState The USB device instance.
3050 * @remarks Requires the device state mutex to be held.
3051 */
3052LOCAL void vboxUsbSolarisNotifyUnplug(vboxusb_state_t *pState)
3053{
3054 if (pState->fPollPending)
3055 {
3056 pollhead_t *pPollHead = &pState->PollHead;
3057 pState->fPollPending = false;
3058 mutex_exit(&pState->Mtx);
3059 pollwakeup(pPollHead, POLLHUP);
3060 mutex_enter(&pState->Mtx);
3061 }
3062}
3063
3064
3065/**
3066 * Performs a Control Xfer.
3067 *
3068 * @param pState The USB device instance.
3069 * @param pEp The Endpoint for the Xfer.
3070 * @param pUrb The VBox USB URB.
3071 *
3072 * @returns VBox status code.
3073 */
3074LOCAL int vboxUsbSolarisCtrlXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb)
3075{
3076 LogFunc((DEVICE_NAME ": vboxUsbSolarisCtrlXfer: pState=%p pEp=%p pUrb=%p enmDir=%d cbData=%d\n", pState, pEp, pUrb,
3077 pUrb->enmDir, pUrb->cbDataR3));
3078
3079 AssertPtrReturn(pUrb->pMsg, VERR_INVALID_PARAMETER);
3080 const size_t cbData = pUrb->cbDataR3 > VBOXUSB_CTRL_XFER_SIZE ? pUrb->cbDataR3 - VBOXUSB_CTRL_XFER_SIZE : 0;
3081
3082 /*
3083 * Allocate a wrapper request.
3084 */
3085 usb_ctrl_req_t *pReq = usb_alloc_ctrl_req(pState->pDip, cbData, USB_FLAGS_SLEEP);
3086 if (RT_LIKELY(pReq))
3087 {
3088 uchar_t *pSetupData = pUrb->pMsg->b_rptr;
3089
3090 /*
3091 * Initialize the Ctrl Xfer Header.
3092 */
3093 pReq->ctrl_bmRequestType = pSetupData[0];
3094 pReq->ctrl_bRequest = pSetupData[1];
3095 pReq->ctrl_wValue = (pSetupData[3] << VBOXUSB_CTRL_XFER_SIZE) | pSetupData[2];
3096 pReq->ctrl_wIndex = (pSetupData[5] << VBOXUSB_CTRL_XFER_SIZE) | pSetupData[4];
3097 pReq->ctrl_wLength = (pSetupData[7] << VBOXUSB_CTRL_XFER_SIZE) | pSetupData[6];
3098
3099 if ( pUrb->enmDir == VUSBDIRECTION_OUT
3100 && cbData)
3101 {
3102 bcopy(pSetupData + VBOXUSB_CTRL_XFER_SIZE, pReq->ctrl_data->b_wptr, cbData);
3103 pReq->ctrl_data->b_wptr += cbData;
3104 }
3105
3106 freemsg(pUrb->pMsg);
3107 pUrb->pMsg = NULL;
3108
3109 /*
3110 * Initialize callbacks and timeouts.
3111 */
3112 pReq->ctrl_cb = vboxUsbSolarisCtrlXferCompleted;
3113 pReq->ctrl_exc_cb = vboxUsbSolarisCtrlXferCompleted;
3114 pReq->ctrl_timeout = VBOXUSB_CTRL_XFER_TIMEOUT;
3115 pReq->ctrl_attributes = USB_ATTRS_AUTOCLEARING | USB_ATTRS_SHORT_XFER_OK;
3116 pReq->ctrl_client_private = (usb_opaque_t)pUrb;
3117
3118 /*
3119 * Submit the request.
3120 */
3121 int rc = usb_pipe_ctrl_xfer(pEp->pPipe, pReq, USB_FLAGS_NOSLEEP);
3122 if (RT_LIKELY(rc == USB_SUCCESS))
3123 return VINF_SUCCESS;
3124
3125 LogRel((DEVICE_NAME ": vboxUsbSolarisCtrlXfer: Request failed! bEndpoint=%#x rc=%d\n", pUrb->bEndpoint, rc));
3126
3127 usb_free_ctrl_req(pReq);
3128 return VERR_PIPE_IO_ERROR;
3129 }
3130
3131 LogRel((DEVICE_NAME ": vboxUsbSolarisCtrlXfer: Failed to alloc request for %u bytes\n", cbData));
3132 return VERR_NO_MEMORY;
3133}
3134
3135
3136/**
3137 * Completion/Exception callback for Control Xfers.
3138 *
3139 * @param pPipe The Ctrl pipe handle.
3140 * @param pReq The Ctrl request.
3141 */
3142LOCAL void vboxUsbSolarisCtrlXferCompleted(usb_pipe_handle_t pPipe, usb_ctrl_req_t *pReq)
3143{
3144 LogFunc((DEVICE_NAME ": vboxUsbSolarisCtrlXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq));
3145 Assert(pReq);
3146 Assert(!(pReq->ctrl_cb_flags & USB_CB_INTR_CONTEXT));
3147
3148 vboxusb_urb_t *pUrb = (vboxusb_urb_t *)pReq->ctrl_client_private;
3149 if (RT_LIKELY(pUrb))
3150 {
3151 /*
3152 * Funky stuff: We need to reconstruct the header for control transfers.
3153 * Let us chain along the data and concatenate the entire message.
3154 */
3155 mblk_t *pSetupMsg = allocb(sizeof(VUSBSETUP), BPRI_MED);
3156 if (RT_LIKELY(pSetupMsg))
3157 {
3158 VUSBSETUP SetupData;
3159 SetupData.bmRequestType = pReq->ctrl_bmRequestType;
3160 SetupData.bRequest = pReq->ctrl_bRequest;
3161 SetupData.wValue = pReq->ctrl_wValue;
3162 SetupData.wIndex = pReq->ctrl_wIndex;
3163 SetupData.wLength = pReq->ctrl_wLength;
3164
3165 bcopy(&SetupData, pSetupMsg->b_wptr, sizeof(VUSBSETUP));
3166 pSetupMsg->b_wptr += sizeof(VUSBSETUP);
3167
3168 /*
3169 * Should be safe to update pMsg here without the state mutex as typically nobody else
3170 * touches this URB in the inflight list.
3171 *
3172 * The reason we choose to use vboxUsbSolarisConcatMsg here is that we don't assume the
3173 * message returned by Solaris is one contiguous chunk in 'pMsg->b_rptr'.
3174 */
3175 Assert(!pUrb->pMsg);
3176 pUrb->pMsg = pSetupMsg;
3177 pUrb->pMsg->b_cont = pReq->ctrl_data;
3178 pReq->ctrl_data = NULL;
3179 vboxUsbSolarisConcatMsg(pUrb);
3180 }
3181 else
3182 LogRel((DEVICE_NAME ": vboxUsbSolarisCtrlXferCompleted: Failed to alloc %u bytes for header\n", sizeof(VUSBSETUP)));
3183
3184 /*
3185 * Update the URB and move to landed list for reaping.
3186 */
3187 vboxUsbSolarisDeQueueUrb(pUrb, pReq->ctrl_completion_reason);
3188 }
3189 else
3190 LogRel((DEVICE_NAME ": vboxUsbSolarisCtrlXferCompleted: Extreme error! missing private data\n"));
3191
3192 usb_free_ctrl_req(pReq);
3193}
3194
3195
3196/**
3197 * Performs a Bulk Xfer.
3198 *
3199 * @param pState The USB device instance.
3200 * @param pEp The Endpoint for the Xfer.
3201 * @param pUrb The VBox USB URB.
3202 *
3203 * @returns VBox status code.
3204 * @remarks Any errors, the caller should free pUrb->pMsg.
3205 */
3206LOCAL int vboxUsbSolarisBulkXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb)
3207{
3208 LogFunc((DEVICE_NAME ": vboxUsbSolarisBulkXfer: pState=%p pEp=%p pUrb=%p enmDir=%d cbData=%d\n", pState, pEp, pUrb,
3209 pUrb->enmDir, pUrb->cbDataR3));
3210
3211 /*
3212 * Allocate a wrapper request.
3213 */
3214 size_t const cbAlloc = pUrb->enmDir == VUSBDIRECTION_IN ? pUrb->cbDataR3 : 0;
3215 usb_bulk_req_t *pReq = usb_alloc_bulk_req(pState->pDip, cbAlloc, USB_FLAGS_SLEEP);
3216 if (RT_LIKELY(pReq))
3217 {
3218 /*
3219 * Initialize Bulk Xfer, callbacks and timeouts.
3220 */
3221 usb_req_attrs_t fAttributes = USB_ATTRS_AUTOCLEARING;
3222 if (pUrb->enmDir == VUSBDIRECTION_OUT)
3223 {
3224 pReq->bulk_data = pUrb->pMsg;
3225 pUrb->pMsg = NULL;
3226 }
3227 else if ( pUrb->enmDir == VUSBDIRECTION_IN
3228 && pUrb->fShortOk)
3229 {
3230 fAttributes |= USB_ATTRS_SHORT_XFER_OK;
3231 }
3232
3233 Assert(!pUrb->pMsg);
3234 pReq->bulk_len = pUrb->cbDataR3;
3235 pReq->bulk_cb = vboxUsbSolarisBulkXferCompleted;
3236 pReq->bulk_exc_cb = vboxUsbSolarisBulkXferCompleted;
3237 pReq->bulk_timeout = 0;
3238 pReq->bulk_attributes = fAttributes;
3239 pReq->bulk_client_private = (usb_opaque_t)pUrb;
3240
3241 /* Don't obtain state lock here, we're just reading unchanging data... */
3242 if (RT_UNLIKELY(pUrb->cbDataR3 > pState->cbMaxBulkXfer))
3243 {
3244 LogRel((DEVICE_NAME ": vboxUsbSolarisBulkXfer: Requesting %d bytes when only %d bytes supported by device\n",
3245 pUrb->cbDataR3, pState->cbMaxBulkXfer));
3246 }
3247
3248 /*
3249 * Submit the request.
3250 */
3251 int rc = usb_pipe_bulk_xfer(pEp->pPipe, pReq, USB_FLAGS_NOSLEEP);
3252 if (RT_LIKELY(rc == USB_SUCCESS))
3253 return VINF_SUCCESS;
3254
3255 LogRel((DEVICE_NAME ": vboxUsbSolarisBulkXfer: Request failed! Ep=%#x rc=%d cbData=%u\n", pUrb->bEndpoint, rc,
3256 pReq->bulk_len));
3257
3258 usb_free_bulk_req(pReq);
3259 return VERR_PIPE_IO_ERROR;
3260 }
3261
3262 LogRel((DEVICE_NAME ": vboxUsbSolarisBulkXfer: Failed to alloc bulk request\n"));
3263 return VERR_NO_MEMORY;
3264}
3265
3266
3267/**
3268 * Completion/Exception callback for Bulk Xfers.
3269 *
3270 * @param pPipe The Bulk pipe handle.
3271 * @param pReq The Bulk request.
3272 */
3273LOCAL void vboxUsbSolarisBulkXferCompleted(usb_pipe_handle_t pPipe, usb_bulk_req_t *pReq)
3274{
3275 LogFunc((DEVICE_NAME ": vboxUsbSolarisBulkXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq));
3276
3277 Assert(pReq);
3278 Assert(!(pReq->bulk_cb_flags & USB_CB_INTR_CONTEXT));
3279
3280 vboxusb_ep_t *pEp = (vboxusb_ep_t *)usb_pipe_get_private(pPipe);
3281 if (RT_LIKELY(pEp))
3282 {
3283 vboxusb_urb_t *pUrb = (vboxusb_urb_t *)pReq->bulk_client_private;
3284 if (RT_LIKELY(pUrb))
3285 {
3286 Assert(!pUrb->pMsg);
3287 if ( pUrb->enmDir == VUSBDIRECTION_IN
3288 && pReq->bulk_data)
3289 {
3290 pUrb->pMsg = pReq->bulk_data;
3291 pReq->bulk_data = NULL;
3292 vboxUsbSolarisConcatMsg(pUrb);
3293 }
3294
3295 /*
3296 * Update the URB and move to tail for reaping.
3297 */
3298 vboxUsbSolarisDeQueueUrb(pUrb, pReq->bulk_completion_reason);
3299 }
3300 else
3301 LogRel((DEVICE_NAME ": vboxUsbSolarisBulkXferCompleted: Extreme error! private request data missing!\n"));
3302 }
3303 else
3304 Log((DEVICE_NAME ": vboxUsbSolarisBulkXferCompleted: Pipe Gone!\n"));
3305
3306 usb_free_bulk_req(pReq);
3307}
3308
3309
3310/**
3311 * Performs an Interrupt Xfer.
3312 *
3313 * @param pState The USB device instance.
3314 * @param pEp The Endpoint for the Xfer.
3315 * @param pUrb The VBox USB URB.
3316 *
3317 * @returns VBox status code.
3318 * @remarks Any errors, the caller should free pUrb->pMsg.
3319 */
3320LOCAL int vboxUsbSolarisIntrXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb)
3321{
3322 LogFunc((DEVICE_NAME ": vboxUsbSolarisIntrXfer: pState=%p pEp=%p pUrb=%p enmDir=%d cbData=%d\n", pState, pEp, pUrb,
3323 pUrb->enmDir, pUrb->cbDataR3));
3324
3325 usb_intr_req_t *pReq = usb_alloc_intr_req(pState->pDip, 0 /* length */, USB_FLAGS_SLEEP);
3326 if (RT_LIKELY(pReq))
3327 {
3328 /*
3329 * Initialize Intr Xfer, callbacks & timeouts.
3330 */
3331 usb_req_attrs_t fAttributes = USB_ATTRS_AUTOCLEARING;
3332 if (pUrb->enmDir == VUSBDIRECTION_OUT)
3333 {
3334 pReq->intr_data = pUrb->pMsg;
3335 pUrb->pMsg = NULL;
3336 }
3337 else
3338 {
3339 Assert(pUrb->enmDir == VUSBDIRECTION_IN);
3340 fAttributes |= USB_ATTRS_ONE_XFER;
3341 if (pUrb->fShortOk)
3342 fAttributes |= USB_ATTRS_SHORT_XFER_OK;
3343 }
3344
3345 Assert(!pUrb->pMsg);
3346 pReq->intr_len = pUrb->cbDataR3; /* Not pEp->EpDesc.wMaxPacketSize */
3347 pReq->intr_cb = vboxUsbSolarisIntrXferCompleted;
3348 pReq->intr_exc_cb = vboxUsbSolarisIntrXferCompleted;
3349 pReq->intr_timeout = 0;
3350 pReq->intr_attributes = fAttributes;
3351 pReq->intr_client_private = (usb_opaque_t)pUrb;
3352
3353 /*
3354 * Submit the request.
3355 */
3356 int rc = usb_pipe_intr_xfer(pEp->pPipe, pReq, USB_FLAGS_NOSLEEP);
3357 if (RT_LIKELY(rc == USB_SUCCESS))
3358 return VINF_SUCCESS;
3359
3360 LogRel((DEVICE_NAME ": vboxUsbSolarisIntrXfer: usb_pipe_intr_xfer failed! rc=%d bEndpoint=%#x\n", rc, pUrb->bEndpoint));
3361
3362 usb_free_intr_req(pReq);
3363 return VERR_PIPE_IO_ERROR;
3364 }
3365
3366 LogRel((DEVICE_NAME ": vboxUsbSolarisIntrXfer: Failed to alloc intr request\n"));
3367 return VERR_NO_MEMORY;
3368}
3369
3370
3371/**
3372 * Completion/Exception callback for Intr Xfers.
3373 *
3374 * @param pPipe The Intr pipe handle.
3375 * @param pReq The Intr request.
3376 */
3377LOCAL void vboxUsbSolarisIntrXferCompleted(usb_pipe_handle_t pPipe, usb_intr_req_t *pReq)
3378{
3379 LogFunc((DEVICE_NAME ": vboxUsbSolarisIntrXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq));
3380
3381 Assert(pReq);
3382 Assert(!(pReq->intr_cb_flags & USB_CB_INTR_CONTEXT));
3383
3384 vboxusb_urb_t *pUrb = (vboxusb_urb_t *)pReq->intr_client_private;
3385 if (RT_LIKELY(pUrb))
3386 {
3387 if ( pUrb->enmDir == VUSBDIRECTION_IN
3388 && pReq->intr_data)
3389 {
3390 pUrb->pMsg = pReq->intr_data;
3391 pReq->intr_data = NULL;
3392 vboxUsbSolarisConcatMsg(pUrb);
3393 }
3394
3395 /*
3396 * Update the URB and move to landed list for reaping.
3397 */
3398 vboxUsbSolarisDeQueueUrb(pUrb, pReq->intr_completion_reason);
3399 }
3400 else
3401 LogRel((DEVICE_NAME ": vboxUsbSolarisIntrXferCompleted: Extreme error! private request data missing\n"));
3402
3403 usb_free_intr_req(pReq);
3404}
3405
3406
3407/**
3408 * Performs an Isochronous Xfer.
3409 *
3410 * @param pState The USB device instance.
3411 * @param pEp The Endpoint for the Xfer.
3412 * @param pUrb The VBox USB URB.
3413 *
3414 * @returns VBox status code.
3415 * @remarks Any errors, the caller should free pUrb->pMsg.
3416 */
3417LOCAL int vboxUsbSolarisIsocXfer(vboxusb_state_t *pState, vboxusb_ep_t *pEp, vboxusb_urb_t *pUrb)
3418{
3419 /* LogFunc((DEVICE_NAME ": vboxUsbSolarisIsocXfer: pState=%p pEp=%p pUrb=%p\n", pState, pEp, pUrb)); */
3420
3421 /*
3422 * For Isoc. IN transfers we perform one request and USBA polls the device continuously
3423 * and supplies our Xfer callback with input data. We cannot perform one-shot Isoc. In transfers.
3424 */
3425 size_t cbData = (pUrb->enmDir == VUSBDIRECTION_IN ? pUrb->cIsocPkts * pUrb->aIsocPkts[0].cbPkt : 0);
3426 if (pUrb->enmDir == VUSBDIRECTION_IN)
3427 {
3428 Log((DEVICE_NAME ": vboxUsbSolarisIsocXfer: Isoc. IN - Queueing\n"));
3429
3430 mutex_enter(&pState->Mtx);
3431 if (pEp->fIsocPolling)
3432 {
3433 /*
3434 * Queue a maximum of cbMaxIsocData bytes, else fail.
3435 */
3436 if (pEp->cbIsocInLandedReqs + cbData > pEp->cbMaxIsocData)
3437 {
3438 mutex_exit(&pState->Mtx);
3439 Log((DEVICE_NAME ": vboxUsbSolarisIsocXfer: Max Isoc. data %d bytes queued\n", pEp->cbMaxIsocData));
3440 return VERR_TOO_MUCH_DATA;
3441 }
3442
3443 list_insert_tail(&pEp->hIsocInUrbs, pUrb);
3444 ++pEp->cIsocInUrbs;
3445
3446 mutex_exit(&pState->Mtx);
3447 return VINF_SUCCESS;
3448 }
3449 mutex_exit(&pState->Mtx);
3450 }
3451
3452 int rc = VINF_SUCCESS;
3453 usb_isoc_req_t *pReq = usb_alloc_isoc_req(pState->pDip, pUrb->cIsocPkts, cbData, USB_FLAGS_NOSLEEP);
3454 Log((DEVICE_NAME ": vboxUsbSolarisIsocXfer: enmDir=%#x cIsocPkts=%d aIsocPkts[0]=%d cbDataR3=%d\n", pUrb->enmDir,
3455 pUrb->cIsocPkts, pUrb->aIsocPkts[0].cbPkt, pUrb->cbDataR3));
3456 if (RT_LIKELY(pReq))
3457 {
3458 /*
3459 * Initialize Isoc Xfer, callbacks & timeouts.
3460 */
3461 for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
3462 pReq->isoc_pkt_descr[i].isoc_pkt_length = pUrb->aIsocPkts[i].cbPkt;
3463
3464 if (pUrb->enmDir == VUSBDIRECTION_OUT)
3465 {
3466 pReq->isoc_data = pUrb->pMsg;
3467 pReq->isoc_attributes = USB_ATTRS_AUTOCLEARING | USB_ATTRS_ISOC_XFER_ASAP;
3468 pReq->isoc_cb = vboxUsbSolarisIsocOutXferCompleted;
3469 pReq->isoc_exc_cb = vboxUsbSolarisIsocOutXferCompleted;
3470 pReq->isoc_client_private = (usb_opaque_t)pUrb;
3471 }
3472 else
3473 {
3474 pReq->isoc_attributes = USB_ATTRS_AUTOCLEARING | USB_ATTRS_ISOC_XFER_ASAP | USB_ATTRS_SHORT_XFER_OK;
3475 pReq->isoc_cb = vboxUsbSolarisIsocInXferCompleted;
3476 pReq->isoc_exc_cb = vboxUsbSolarisIsocInXferError;
3477 pReq->isoc_client_private = (usb_opaque_t)pState;
3478 }
3479 pReq->isoc_pkts_count = pUrb->cIsocPkts;
3480 pReq->isoc_pkts_length = 0; /* auto compute */
3481
3482 /*
3483 * Submit the request.
3484 */
3485 rc = usb_pipe_isoc_xfer(pEp->pPipe, pReq, USB_FLAGS_NOSLEEP);
3486 if (RT_LIKELY(rc == USB_SUCCESS))
3487 {
3488 if (pUrb->enmDir == VUSBDIRECTION_IN)
3489 {
3490 /*
3491 * Add the first Isoc. IN URB to the queue as well.
3492 */
3493 mutex_enter(&pState->Mtx);
3494 list_insert_tail(&pEp->hIsocInUrbs, pUrb);
3495 ++pEp->cIsocInUrbs;
3496 pEp->fIsocPolling = true;
3497 mutex_exit(&pState->Mtx);
3498 }
3499
3500 return VINF_SUCCESS;
3501 }
3502 else
3503 {
3504 LogRel((DEVICE_NAME ": vboxUsbSolarisIsocXfer: usb_pipe_isoc_xfer failed! rc=%d\n", rc));
3505 rc = VERR_PIPE_IO_ERROR;
3506
3507 if (pUrb->enmDir == VUSBDIRECTION_IN)
3508 {
3509 mutex_enter(&pState->Mtx);
3510 vboxusb_urb_t *pIsocFailedUrb = list_remove_tail(&pEp->hIsocInUrbs);
3511 if (pIsocFailedUrb)
3512 {
3513 RTMemFree(pIsocFailedUrb);
3514 --pEp->cIsocInUrbs;
3515 }
3516 pEp->fIsocPolling = false;
3517 mutex_exit(&pState->Mtx);
3518 }
3519 }
3520
3521 if (pUrb->enmDir == VUSBDIRECTION_OUT)
3522 {
3523 freemsg(pUrb->pMsg);
3524 pUrb->pMsg = NULL;
3525 }
3526
3527 usb_free_isoc_req(pReq);
3528 }
3529 else
3530 {
3531 LogRel((DEVICE_NAME ": vboxUsbSolarisIsocXfer: Failed to alloc isoc req for %d packets\n", pUrb->cIsocPkts));
3532 rc = VERR_NO_MEMORY;
3533 }
3534
3535 return rc;
3536}
3537
3538
3539/**
3540 * Completion/Exception callback for Isoc IN Xfers.
3541 *
3542 * @param pPipe The Intr pipe handle.
3543 * @param pReq The Intr request.
3544 *
3545 * @remarks Completion callback executes in interrupt context!
3546 */
3547LOCAL void vboxUsbSolarisIsocInXferCompleted(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq)
3548{
3549 /* LogFunc((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq)); */
3550
3551 vboxusb_state_t *pState = (vboxusb_state_t *)pReq->isoc_client_private;
3552 if (RT_LIKELY(pState))
3553 {
3554 vboxusb_ep_t *pEp = (vboxusb_ep_t *)usb_pipe_get_private(pPipe);
3555 if ( pEp
3556 && pEp->pPipe)
3557 {
3558#if 0
3559 /*
3560 * Stop polling if all packets failed.
3561 */
3562 if (pReq->isoc_error_count == pReq->isoc_pkts_count)
3563 {
3564 Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: Stopping polling! Too many errors\n"));
3565 mutex_exit(&pState->Mtx);
3566 usb_pipe_stop_isoc_polling(pPipe, USB_FLAGS_NOSLEEP);
3567 mutex_enter(&pState->Mtx);
3568 pEp->fIsocPolling = false;
3569 }
3570#endif
3571
3572 /** @todo Query and verify this at runtime. */
3573 AssertCompile(sizeof(VUSBISOC_PKT_DESC) == sizeof(usb_isoc_pkt_descr_t));
3574 if (RT_LIKELY(pReq->isoc_data))
3575 {
3576 Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: cIsocInUrbs=%d cbIsocInLandedReqs=%d\n", pEp->cIsocInUrbs,
3577 pEp->cbIsocInLandedReqs));
3578
3579 mutex_enter(&pState->Mtx);
3580
3581 /*
3582 * If there are waiting URBs, satisfy the oldest one.
3583 */
3584 if ( pEp->cIsocInUrbs > 0
3585 && pEp->cbIsocInLandedReqs == 0)
3586 {
3587 vboxusb_urb_t *pUrb = list_remove_head(&pEp->hIsocInUrbs);
3588 if (RT_LIKELY(pUrb))
3589 {
3590 --pEp->cIsocInUrbs;
3591 mutex_exit(&pState->Mtx);
3592
3593 for (unsigned i = 0; i < pReq->isoc_pkts_count; i++)
3594 {
3595 pUrb->aIsocPkts[i].cbActPkt = pReq->isoc_pkt_descr[i].isoc_pkt_actual_length;
3596 pUrb->aIsocPkts[i].enmStatus = vboxUsbSolarisGetUrbStatus(pReq->isoc_pkt_descr[i].isoc_pkt_status);
3597 }
3598
3599 pUrb->pMsg = pReq->isoc_data;
3600 pReq->isoc_data = NULL;
3601
3602 /*
3603 * Move to landed list
3604 */
3605 mutex_enter(&pState->Mtx);
3606 list_insert_tail(&pState->hLandedUrbs, pUrb);
3607 ++pState->cLandedUrbs;
3608 vboxUsbSolarisNotifyComplete(pState);
3609 }
3610 else
3611 {
3612 /* Huh!? cIsocInUrbs is wrong then! Should never happen unless we decide to decrement cIsocInUrbs in
3613 Reap time */
3614 pEp->cIsocInUrbs = 0;
3615 LogRel((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: Extreme error! Isoc. counter borked!\n"));
3616 }
3617
3618 mutex_exit(&pState->Mtx);
3619 usb_free_isoc_req(pReq);
3620 return;
3621 }
3622
3623 mutex_exit(&pState->Mtx);
3624 }
3625 else
3626 LogRel((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: Data missing\n"));
3627 }
3628 else
3629 LogRel((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: Pipe Gone\n"));
3630 }
3631 else
3632 Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferCompleted: State Gone\n"));
3633
3634 usb_free_isoc_req(pReq);
3635}
3636
3637
3638/**
3639 * Exception callback for Isoc IN Xfers.
3640 *
3641 * @param pPipe The Intr pipe handle.
3642 * @param pReq The Intr request.
3643 * @remarks Completion callback executes in interrupt context!
3644 */
3645LOCAL void vboxUsbSolarisIsocInXferError(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq)
3646{
3647 LogFunc((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: pPipe=%p pReq=%p\n", pPipe, pReq));
3648
3649 vboxusb_state_t *pState = (vboxusb_state_t *)pReq->isoc_client_private;
3650 if (RT_UNLIKELY(!pState))
3651 {
3652 Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: State Gone\n"));
3653 usb_free_isoc_req(pReq);
3654 return;
3655 }
3656
3657 mutex_enter(&pState->Mtx);
3658 vboxusb_ep_t *pEp = (vboxusb_ep_t *)usb_pipe_get_private(pPipe);
3659 if (RT_UNLIKELY(!pEp))
3660 {
3661 Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: Pipe Gone\n"));
3662 mutex_exit(&pState->Mtx);
3663 usb_free_isoc_req(pReq);
3664 return;
3665 }
3666
3667 switch(pReq->isoc_completion_reason)
3668 {
3669 case USB_CR_NO_RESOURCES:
3670 {
3671 /*
3672 * Resubmit the request in case the original request did not complete due to
3673 * immediately unavailable requests
3674 */
3675 mutex_exit(&pState->Mtx);
3676 usb_pipe_isoc_xfer(pPipe, pReq, USB_FLAGS_NOSLEEP);
3677 Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: Resubmitted Isoc. IN request due to unavailable resources\n"));
3678 return;
3679 }
3680
3681 case USB_CR_PIPE_CLOSING:
3682 case USB_CR_STOPPED_POLLING:
3683 case USB_CR_PIPE_RESET:
3684 {
3685 pEp->fIsocPolling = false;
3686 usb_free_isoc_req(pReq);
3687 break;
3688 }
3689
3690 default:
3691 {
3692 Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: Stopping Isoc. IN polling due to rc=%d\n",
3693 pReq->isoc_completion_reason));
3694 pEp->fIsocPolling = false;
3695 mutex_exit(&pState->Mtx);
3696 usb_pipe_stop_isoc_polling(pPipe, USB_FLAGS_NOSLEEP);
3697 usb_free_isoc_req(pReq);
3698 mutex_enter(&pState->Mtx);
3699 break;
3700 }
3701 }
3702
3703 /*
3704 * Dequeue i.e. delete the last queued Isoc In. URB. as failed.
3705 */
3706 vboxusb_urb_t *pUrb = list_remove_tail(&pEp->hIsocInUrbs);
3707 if (pUrb)
3708 {
3709 --pEp->cIsocInUrbs;
3710 Log((DEVICE_NAME ": vboxUsbSolarisIsocInXferError: Deleting last queued URB as it failed\n"));
3711 freemsg(pUrb->pMsg);
3712 RTMemFree(pUrb);
3713 vboxUsbSolarisNotifyComplete(pState);
3714 }
3715
3716 mutex_exit(&pState->Mtx);
3717}
3718
3719
3720/**
3721 * Completion/Exception callback for Isoc OUT Xfers.
3722 *
3723 * @param pPipe The Intr pipe handle.
3724 * @param pReq The Intr request.
3725 * @remarks Completion callback executes in interrupt context!
3726 */
3727LOCAL void vboxUsbSolarisIsocOutXferCompleted(usb_pipe_handle_t pPipe, usb_isoc_req_t *pReq)
3728{
3729 LogFunc((DEVICE_NAME ": vboxUsbSolarisIsocOutXferCompleted: pPipe=%p pReq=%p\n", pPipe, pReq));
3730
3731 vboxusb_ep_t *pEp = (vboxusb_ep_t *)usb_pipe_get_private(pPipe);
3732 if (RT_LIKELY(pEp))
3733 {
3734 vboxusb_urb_t *pUrb = (vboxusb_urb_t *)pReq->isoc_client_private;
3735 if (RT_LIKELY(pUrb))
3736 {
3737 size_t cbActPkt = 0;
3738 for (int i = 0; i < pReq->isoc_pkts_count; i++)
3739 {
3740 cbActPkt += pReq->isoc_pkt_descr[i].isoc_pkt_actual_length;
3741 pUrb->aIsocPkts[i].cbActPkt = pReq->isoc_pkt_descr[i].isoc_pkt_actual_length;
3742 pUrb->aIsocPkts[i].enmStatus = vboxUsbSolarisGetUrbStatus(pReq->isoc_pkt_descr[i].isoc_pkt_status);
3743 }
3744
3745 Log((DEVICE_NAME ": vboxUsbSolarisIsocOutXferCompleted: cIsocPkts=%d cbData=%d cbActPkt=%d\n", pUrb->cIsocPkts,
3746 pUrb->cbDataR3, cbActPkt));
3747
3748 if (pReq->isoc_completion_reason == USB_CR_OK)
3749 {
3750 if (RT_UNLIKELY(pUrb->pMsg != pReq->isoc_data)) /* Paranoia */
3751 {
3752 freemsg(pUrb->pMsg);
3753 pUrb->pMsg = pReq->isoc_data;
3754 }
3755 }
3756 pReq->isoc_data = NULL;
3757
3758 pUrb->cIsocPkts = pReq->isoc_pkts_count;
3759 pUrb->cbDataR3 = cbActPkt;
3760
3761 /*
3762 * Update the URB and move to landed list for reaping.
3763 */
3764 vboxUsbSolarisDeQueueUrb(pUrb, pReq->isoc_completion_reason);
3765 }
3766 else
3767 Log((DEVICE_NAME ": vboxUsbSolarisIsocOutXferCompleted: Missing private data!?! Dropping OUT pUrb\n"));
3768 }
3769 else
3770 Log((DEVICE_NAME ": vboxUsbSolarisIsocOutXferCompleted: Pipe Gone\n"));
3771
3772 usb_free_isoc_req(pReq);
3773}
3774
3775
3776/**
3777 * Callback when the device gets disconnected.
3778 *
3779 * @param pDip The module structure instance.
3780 *
3781 * @returns Solaris USB error code.
3782 */
3783LOCAL int vboxUsbSolarisDeviceDisconnected(dev_info_t *pDip)
3784{
3785 LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceDisconnected: pDip=%p\n", pDip));
3786
3787 int instance = ddi_get_instance(pDip);
3788 vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
3789
3790 if (RT_LIKELY(pState))
3791 {
3792 /*
3793 * Serialize access: exclusive access to the state.
3794 */
3795 usb_serialize_access(pState->StateMulti, USB_WAIT, 0);
3796 mutex_enter(&pState->Mtx);
3797
3798 pState->DevState = USB_DEV_DISCONNECTED;
3799
3800 vboxUsbSolarisCloseAllPipes(pState, true /* ControlPipe */);
3801 vboxUsbSolarisNotifyUnplug(pState);
3802
3803 mutex_exit(&pState->Mtx);
3804 usb_release_access(pState->StateMulti);
3805
3806 return USB_SUCCESS;
3807 }
3808
3809 LogRel((DEVICE_NAME ": vboxUsbSolarisDeviceDisconnected: Failed to get device state!\n"));
3810 return USB_FAILURE;
3811}
3812
3813
3814/**
3815 * Callback when the device gets reconnected.
3816 *
3817 * @param pDip The module structure instance.
3818 *
3819 * @returns Solaris USB error code.
3820 */
3821LOCAL int vboxUsbSolarisDeviceReconnected(dev_info_t *pDip)
3822{
3823 LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceReconnected: pDip=%p\n", pDip));
3824
3825 int instance = ddi_get_instance(pDip);
3826 vboxusb_state_t *pState = ddi_get_soft_state(g_pVBoxUSBSolarisState, instance);
3827
3828 if (RT_LIKELY(pState))
3829 {
3830 vboxUsbSolarisDeviceRestore(pState);
3831 return USB_SUCCESS;
3832 }
3833
3834 LogRel((DEVICE_NAME ": vboxUsbSolarisDeviceReconnected: Failed to get device state!\n"));
3835 return USB_FAILURE;
3836}
3837
3838
3839/**
3840 * Restores device state after a reconnect or resume.
3841 *
3842 * @param pState The USB device instance.
3843 */
3844LOCAL void vboxUsbSolarisDeviceRestore(vboxusb_state_t *pState)
3845{
3846 LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceRestore: pState=%p\n", pState));
3847 AssertPtrReturnVoid(pState);
3848
3849 /*
3850 * Raise device power.
3851 */
3852 vboxUsbSolarisPowerBusy(pState);
3853 int rc = pm_raise_power(pState->pDip, 0 /* component */, USB_DEV_OS_FULL_PWR);
3854
3855 /*
3856 * Check if the same device is resumed/reconnected.
3857 */
3858 rc = usb_check_same_device(pState->pDip,
3859 NULL, /* log handle */
3860 USB_LOG_L2, /* log level */
3861 -1, /* log mask */
3862 USB_CHK_ALL, /* check level */
3863 NULL); /* device string */
3864
3865 if (rc != USB_SUCCESS)
3866 {
3867 mutex_enter(&pState->Mtx);
3868 pState->DevState = USB_DEV_DISCONNECTED;
3869 mutex_exit(&pState->Mtx);
3870
3871 /* Do we need to inform userland here? */
3872 vboxUsbSolarisPowerIdle(pState);
3873 Log((DEVICE_NAME ": vboxUsbSolarisDeviceRestore: Not the same device\n"));
3874 return;
3875 }
3876
3877 /*
3878 * Serialize access to not race with other PM functions.
3879 */
3880 usb_serialize_access(pState->StateMulti, USB_WAIT, 0);
3881
3882 mutex_enter(&pState->Mtx);
3883 if (pState->DevState == USB_DEV_DISCONNECTED)
3884 pState->DevState = USB_DEV_ONLINE;
3885 else if (pState->DevState == USB_DEV_SUSPENDED)
3886 pState->DevState = USB_DEV_ONLINE;
3887
3888 mutex_exit(&pState->Mtx);
3889 usb_release_access(pState->StateMulti);
3890
3891 vboxUsbSolarisPowerIdle(pState);
3892}
3893
3894
3895/**
3896 * Restores device state after a reconnect or resume.
3897 *
3898 * @param pState The USB device instance.
3899 *
3900 * @returns VBox status code.
3901 */
3902LOCAL int vboxUsbSolarisDeviceSuspend(vboxusb_state_t *pState)
3903{
3904 LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceSuspend: pState=%p\n", pState));
3905
3906 int rc = VERR_VUSB_DEVICE_IS_SUSPENDED;
3907 mutex_enter(&pState->Mtx);
3908
3909 switch (pState->DevState)
3910 {
3911 case USB_DEV_SUSPENDED:
3912 {
3913 LogRel((DEVICE_NAME ": vboxUsbSolarisDeviceSuspend: Invalid device state %d\n", pState->DevState));
3914 break;
3915 }
3916
3917 case USB_DEV_ONLINE:
3918 case USB_DEV_DISCONNECTED:
3919 case USB_DEV_PWRED_DOWN:
3920 {
3921 int PreviousState = pState->DevState;
3922 pState->DevState = USB_DEV_DISCONNECTED;
3923
3924 /** @todo this doesn't make sense when for e.g. an INTR IN URB with infinite
3925 * timeout is pending on the device. Fix suspend logic later. */
3926 /*
3927 * Drain pending URBs.
3928 */
3929 for (int i = 0; i < VBOXUSB_DRAIN_TIME; i++)
3930 {
3931 if (pState->cInflightUrbs < 1)
3932 break;
3933
3934 mutex_exit(&pState->Mtx);
3935 delay(drv_usectohz(100000));
3936 mutex_enter(&pState->Mtx);
3937 }
3938
3939 /*
3940 * Deny suspend if we still have pending URBs.
3941 */
3942 if (pState->cInflightUrbs > 0)
3943 {
3944 pState->DevState = PreviousState;
3945 LogRel((DEVICE_NAME ": Cannot suspend %s %s (Ident=%s), %d inflight URBs\n", pState->szMfg, pState->szProduct,
3946 pState->ClientInfo.szDeviceIdent, pState->cInflightUrbs));
3947
3948 mutex_exit(&pState->Mtx);
3949 return VERR_RESOURCE_BUSY;
3950 }
3951
3952 pState->cInflightUrbs = 0;
3953
3954 /*
3955 * Serialize access to not race with Open/Detach/Close and
3956 * Close all pipes including the default pipe.
3957 */
3958 mutex_exit(&pState->Mtx);
3959 usb_serialize_access(pState->StateMulti, USB_WAIT, 0);
3960 mutex_enter(&pState->Mtx);
3961
3962 vboxUsbSolarisCloseAllPipes(pState, true /* default pipe */);
3963 vboxUsbSolarisNotifyUnplug(pState);
3964
3965 mutex_exit(&pState->Mtx);
3966 usb_release_access(pState->StateMulti);
3967
3968 LogRel((DEVICE_NAME ": Suspended %s %s (Ident=%s)\n", pState->szMfg, pState->szProduct,
3969 pState->ClientInfo.szDeviceIdent));
3970 return VINF_SUCCESS;
3971 }
3972 }
3973
3974 mutex_exit(&pState->Mtx);
3975 Log((DEVICE_NAME ": vboxUsbSolarisDeviceSuspend: Returns %d\n", rc));
3976 return rc;
3977}
3978
3979
3980/**
3981 * Restores device state after a reconnect or resume.
3982 *
3983 * @param pState The USB device instance.
3984 */
3985LOCAL void vboxUsbSolarisDeviceResume(vboxusb_state_t *pState)
3986{
3987 LogFunc((DEVICE_NAME ": vboxUsbSolarisDeviceResume: pState=%p\n", pState));
3988 return vboxUsbSolarisDeviceRestore(pState);
3989}
3990
3991
3992/**
3993 * Flags the PM component as busy so the system will not manage it's power.
3994 *
3995 * @param pState The USB device instance.
3996 */
3997LOCAL void vboxUsbSolarisPowerBusy(vboxusb_state_t *pState)
3998{
3999 LogFunc((DEVICE_NAME ": vboxUsbSolarisPowerBusy: pState=%p\n", pState));
4000 AssertPtrReturnVoid(pState);
4001
4002 mutex_enter(&pState->Mtx);
4003 if (pState->pPower)
4004 {
4005 pState->pPower->PowerBusy++;
4006 mutex_exit(&pState->Mtx);
4007
4008 int rc = pm_busy_component(pState->pDip, 0 /* component */);
4009 if (rc != DDI_SUCCESS)
4010 {
4011 Log((DEVICE_NAME ": vboxUsbSolarisPowerBusy: Busy component failed! rc=%d\n", rc));
4012 mutex_enter(&pState->Mtx);
4013 pState->pPower->PowerBusy--;
4014 mutex_exit(&pState->Mtx);
4015 }
4016 }
4017 else
4018 mutex_exit(&pState->Mtx);
4019}
4020
4021
4022/**
4023 * Flags the PM component as idle so its power managed by the system.
4024 *
4025 * @param pState The USB device instance.
4026 */
4027LOCAL void vboxUsbSolarisPowerIdle(vboxusb_state_t *pState)
4028{
4029 LogFunc((DEVICE_NAME ": vboxUsbSolarisPowerIdle: pState=%p\n", pState));
4030 AssertPtrReturnVoid(pState);
4031
4032 if (pState->pPower)
4033 {
4034 int rc = pm_idle_component(pState->pDip, 0 /* component */);
4035 if (rc == DDI_SUCCESS)
4036 {
4037 mutex_enter(&pState->Mtx);
4038 Assert(pState->pPower->PowerBusy > 0);
4039 pState->pPower->PowerBusy--;
4040 mutex_exit(&pState->Mtx);
4041 }
4042 else
4043 Log((DEVICE_NAME ": vboxUsbSolarisPowerIdle: Idle component failed! rc=%d\n", rc));
4044 }
4045}
4046
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette