VirtualBox

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

Last change on this file since 82968 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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