VirtualBox

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

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