VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/solaris/USBProxyBackendSolaris.cpp@ 61464

Last change on this file since 61464 was 60758, checked in by vboxsync, 9 years ago

Main/{USBProxyBackend|HostUSBDeviceImpl}: Don't hardcode certain host OS specific behavior during attach/release now that we can mix several backends with different requirements but make the backends report which behavior they need (should fix USB testing on Windows, OS X and Solaris hosts)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.8 KB
Line 
1/* $Id: USBProxyBackendSolaris.cpp 60758 2016-04-29 11:18:03Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Service, Solaris Specialization.
4 */
5
6/*
7 * Copyright (C) 2005-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include "USBProxyBackend.h"
23#include "Logging.h"
24
25#include <VBox/usb.h>
26#include <VBox/usblib.h>
27#include <VBox/err.h>
28#include <iprt/semaphore.h>
29#include <iprt/path.h>
30
31#include <sys/usb/usba.h>
32#include <syslog.h>
33
34
35/*********************************************************************************************************************************
36* Internal Functions *
37*********************************************************************************************************************************/
38static int solarisWalkDeviceNode(di_node_t Node, void *pvArg);
39static void solarisFreeUSBDevice(PUSBDEVICE pDevice);
40static USBDEVICESTATE solarisDetermineUSBDeviceState(PUSBDEVICE pDevice, di_node_t Node);
41
42
43/*********************************************************************************************************************************
44* Structures and Typedefs *
45*********************************************************************************************************************************/
46typedef struct USBDEVICELIST
47{
48 PUSBDEVICE pHead;
49 PUSBDEVICE pTail;
50} USBDEVICELIST;
51typedef USBDEVICELIST *PUSBDEVICELIST;
52
53
54/**
55 * Initialize data members.
56 */
57USBProxyBackendSolaris::USBProxyBackendSolaris()
58 : USBProxyBackend(), mUSBLibInitialized(false)
59{
60 LogFlowThisFunc(("\n"));
61}
62
63USBProxyBackendSolaris::~USBProxyBackendSolaris()
64{
65}
66
67/**
68 * Initializes the object (called right after construction).
69 *
70 * @returns VBox status code.
71 */
72int USBProxyBackendSolaris::init(USBProxyService *aUsbProxyService, const com::Utf8Str &strId, const com::Utf8Str &strAddress)
73{
74 USBProxyBackend::init(aUsbProxyService, strId, strAddress);
75
76 unconst(m_strBackend) = Utf8Str("host");
77
78 /*
79 * Create semaphore.
80 */
81 int rc = RTSemEventCreate(&mNotifyEventSem);
82 if (RT_FAILURE(rc))
83 return rc;
84
85 /*
86 * Initialize the USB library.
87 */
88 rc = USBLibInit();
89 if (RT_FAILURE(rc))
90 {
91 RTSemEventDestroy(mNotifyEventSem);
92 return rc;
93 }
94
95 mUSBLibInitialized = true;
96
97 /*
98 * Start the poller thread.
99 */
100 start();
101 return VINF_SUCCESS;
102}
103
104
105/**
106 * Stop all service threads and free the device chain.
107 */
108void USBProxyBackendSolaris::uninit()
109{
110 LogFlowThisFunc(("destruct\n"));
111
112 /*
113 * Stop the service.
114 */
115 if (isActive())
116 stop();
117
118 /*
119 * Terminate the USB library
120 */
121 if (mUSBLibInitialized)
122 {
123 USBLibTerm();
124 mUSBLibInitialized = false;
125 }
126
127 RTSemEventDestroy(mNotifyEventSem);
128 mNotifyEventSem = NULL;
129}
130
131
132void *USBProxyBackendSolaris::insertFilter(PCUSBFILTER aFilter)
133{
134 return USBLibAddFilter(aFilter);
135}
136
137
138void USBProxyBackendSolaris::removeFilter(void *pvID)
139{
140 USBLibRemoveFilter(pvID);
141}
142
143
144int USBProxyBackendSolaris::wait(RTMSINTERVAL aMillies)
145{
146 return RTSemEventWait(mNotifyEventSem, aMillies < 1000 ? 1000 : RT_MIN(aMillies, 5000));
147}
148
149
150int USBProxyBackendSolaris::interruptWait(void)
151{
152 return RTSemEventSignal(mNotifyEventSem);
153}
154
155
156PUSBDEVICE USBProxyBackendSolaris::getDevices(void)
157{
158 USBDEVICELIST DevList;
159 DevList.pHead = NULL;
160 DevList.pTail = NULL;
161 di_node_t RootNode = di_init("/", DINFOCPYALL);
162 if (RootNode != DI_NODE_NIL)
163 di_walk_node(RootNode, DI_WALK_CLDFIRST, &DevList, solarisWalkDeviceNode);
164
165 di_fini(RootNode);
166 return DevList.pHead;
167}
168
169
170static int solarisWalkDeviceNode(di_node_t Node, void *pvArg)
171{
172 PUSBDEVICELIST pList = (PUSBDEVICELIST)pvArg;
173 AssertPtrReturn(pList, DI_WALK_TERMINATE);
174
175 /*
176 * Check if it's a USB device in the first place.
177 */
178 bool fUSBDevice = false;
179 char *pszCompatNames = NULL;
180 int cCompatNames = di_compatible_names(Node, &pszCompatNames);
181 for (int i = 0; i < cCompatNames; i++, pszCompatNames += strlen(pszCompatNames) + 1)
182 if (!strncmp(pszCompatNames, RT_STR_TUPLE("usb")))
183 {
184 fUSBDevice = true;
185 break;
186 }
187
188 if (!fUSBDevice)
189 return DI_WALK_CONTINUE;
190
191 /*
192 * Check if it's a device node or interface.
193 */
194 int *pInt = NULL;
195 char *pStr = NULL;
196 int rc = DI_WALK_CONTINUE;
197 if (di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "interface", &pInt) < 0)
198 {
199 /* It's a device node. */
200 char *pszDevicePath = di_devfs_path(Node);
201 PUSBDEVICE pCur = (PUSBDEVICE)RTMemAllocZ(sizeof(*pCur));
202 if (!pCur)
203 {
204 LogRel(("USBService: failed to allocate %d bytes for PUSBDEVICE.\n", sizeof(*pCur)));
205 return DI_WALK_TERMINATE;
206 }
207
208 bool fValidDevice = false;
209 do
210 {
211 AssertBreak(pszDevicePath);
212
213 char *pszDriverName = di_driver_name(Node);
214
215 /*
216 * Skip hubs
217 */
218 if ( pszDriverName
219 && !strcmp(pszDriverName, "hubd"))
220 {
221 break;
222 }
223
224 /*
225 * Mandatory.
226 * snv_85 and above have usb-dev-descriptor node properties, but older one's do not.
227 * So if we cannot obtain the entire device descriptor, we try falling back to the
228 * individual properties (those must not fail, if it does we drop the device).
229 */
230 uchar_t *pDevData = NULL;
231 int cbProp = di_prop_lookup_bytes(DDI_DEV_T_ANY, Node, "usb-dev-descriptor", &pDevData);
232 if ( cbProp > 0
233 && pDevData)
234 {
235 usb_dev_descr_t *pDeviceDescriptor = (usb_dev_descr_t *)pDevData;
236 pCur->bDeviceClass = pDeviceDescriptor->bDeviceClass;
237 pCur->bDeviceSubClass = pDeviceDescriptor->bDeviceSubClass;
238 pCur->bDeviceProtocol = pDeviceDescriptor->bDeviceProtocol;
239 pCur->idVendor = pDeviceDescriptor->idVendor;
240 pCur->idProduct = pDeviceDescriptor->idProduct;
241 pCur->bcdDevice = pDeviceDescriptor->bcdDevice;
242 pCur->bcdUSB = pDeviceDescriptor->bcdUSB;
243 pCur->bNumConfigurations = pDeviceDescriptor->bNumConfigurations;
244 pCur->fPartialDescriptor = false;
245 }
246 else
247 {
248 AssertBreak(di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "usb-vendor-id", &pInt) > 0);
249 pCur->idVendor = (uint16_t)*pInt;
250
251 AssertBreak(di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "usb-product-id", &pInt) > 0);
252 pCur->idProduct = (uint16_t)*pInt;
253
254 AssertBreak(di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "usb-revision-id", &pInt) > 0);
255 pCur->bcdDevice = (uint16_t)*pInt;
256
257 AssertBreak(di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "usb-release", &pInt) > 0);
258 pCur->bcdUSB = (uint16_t)*pInt;
259
260 pCur->fPartialDescriptor = true;
261 }
262
263 char *pszPortAddr = di_bus_addr(Node);
264 if (pszPortAddr)
265 pCur->bPort = RTStrToUInt8(pszPortAddr); /* Bus & Port are mixed up (kernel driver/userland) */
266 else
267 pCur->bPort = 0;
268
269 char szBuf[PATH_MAX + 48];
270 RTStrPrintf(szBuf, sizeof(szBuf), "%#x:%#x:%d:%s", pCur->idVendor, pCur->idProduct, pCur->bcdDevice, pszDevicePath);
271 pCur->pszAddress = RTStrDup(szBuf);
272 AssertBreak(pCur->pszAddress);
273
274 pCur->pszDevicePath = RTStrDup(pszDevicePath);
275 AssertBreak(pCur->pszDevicePath);
276
277 pCur->pszBackend = RTStrDup("host");
278 AssertBreak(pCur->pszBackend);
279
280 /*
281 * Optional (some devices don't have all these)
282 */
283 if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "usb-product-name", &pStr) > 0)
284 {
285 pCur->pszProduct = RTStrDup(pStr);
286 USBLibPurgeEncoding(pCur->pszProduct);
287 }
288
289 if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "usb-vendor-name", &pStr) > 0)
290 {
291 pCur->pszManufacturer = RTStrDup(pStr);
292 USBLibPurgeEncoding(pCur->pszManufacturer);
293 }
294
295 if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "usb-serialno", &pStr) > 0)
296 {
297 pCur->pszSerialNumber = RTStrDup(pStr);
298 USBLibPurgeEncoding(pCur->pszSerialNumber);
299 }
300
301 if (pCur->bcdUSB == 0x300)
302 pCur->enmSpeed = USBDEVICESPEED_SUPER;
303 else if (di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "low-speed", &pInt) >= 0)
304 pCur->enmSpeed = USBDEVICESPEED_LOW;
305 else if (di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "high-speed", &pInt) >= 0)
306 pCur->enmSpeed = USBDEVICESPEED_HIGH;
307 else
308 pCur->enmSpeed = USBDEVICESPEED_FULL;
309
310 /* Determine state of the USB device. */
311 pCur->enmState = solarisDetermineUSBDeviceState(pCur, Node);
312
313 /*
314 * Valid device, add it to the list.
315 */
316 fValidDevice = true;
317 pCur->pPrev = pList->pTail;
318 if (pList->pTail)
319 pList->pTail = pList->pTail->pNext = pCur;
320 else
321 pList->pTail = pList->pHead = pCur;
322
323 rc = DI_WALK_CONTINUE;
324 } while (0);
325
326 di_devfs_path_free(pszDevicePath);
327 if (!fValidDevice)
328 solarisFreeUSBDevice(pCur);
329 }
330 return rc;
331}
332
333
334static USBDEVICESTATE solarisDetermineUSBDeviceState(PUSBDEVICE pDevice, di_node_t Node)
335{
336 char *pszDriverName = di_driver_name(Node);
337
338 /* Not possible unless a user explicitly unbinds the default driver. */
339 if (!pszDriverName)
340 return USBDEVICESTATE_UNUSED;
341
342 if (!strncmp(pszDriverName, RT_STR_TUPLE(VBOXUSB_DRIVER_NAME)))
343 return USBDEVICESTATE_HELD_BY_PROXY;
344
345 NOREF(pDevice);
346 return USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
347}
348
349
350int USBProxyBackendSolaris::captureDevice(HostUSBDevice *aDevice)
351{
352 /*
353 * Check preconditions.
354 */
355 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
356 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
357
358 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
359 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
360
361 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_Capturing);
362 AssertReturn(aDevice->i_getUsbData(), VERR_INVALID_POINTER);
363
364 /*
365 * Create a one-shot capture filter for the device and reset the device.
366 */
367 USBFILTER Filter;
368 USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_CAPTURE);
369 initFilterFromDevice(&Filter, aDevice);
370
371 void *pvId = USBLibAddFilter(&Filter);
372 if (!pvId)
373 {
374 LogRel(("USBService: failed to add filter\n"));
375 return VERR_GENERAL_FAILURE;
376 }
377
378 PUSBDEVICE pDev = aDevice->i_getUsbData();
379 int rc = USBLibResetDevice(pDev->pszDevicePath, true);
380 if (RT_SUCCESS(rc))
381 aDevice->i_setBackendUserData(pvId);
382 else
383 {
384 USBLibRemoveFilter(pvId);
385 pvId = NULL;
386 }
387 LogFlowThisFunc(("returns %Rrc pvId=%p\n", rc, pvId));
388 return rc;
389}
390
391
392void USBProxyBackendSolaris::captureDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess)
393{
394 AssertReturnVoid(aDevice->isWriteLockOnCurrentThread());
395 /*
396 * Remove the one-shot filter if necessary.
397 */
398 LogFlowThisFunc(("aDevice=%s aSuccess=%RTbool mOneShotId=%p\n", aDevice->i_getName().c_str(), aSuccess, aDevice->i_getBackendUserData()));
399 if (!aSuccess && aDevice->i_getBackendUserData())
400 USBLibRemoveFilter(aDevice->i_getBackendUserData());
401 aDevice->i_setBackendUserData(NULL);
402 USBProxyBackend::captureDeviceCompleted(aDevice, aSuccess);
403}
404
405
406int USBProxyBackendSolaris::releaseDevice(HostUSBDevice *aDevice)
407{
408 /*
409 * Check preconditions.
410 */
411 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
412 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
413
414 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
415 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
416
417 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost);
418 AssertReturn(aDevice->i_getUsbData(), VERR_INVALID_POINTER);
419
420 /*
421 * Create a one-shot ignore filter for the device and reset it.
422 */
423 USBFILTER Filter;
424 USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_IGNORE);
425 initFilterFromDevice(&Filter, aDevice);
426
427 void *pvId = USBLibAddFilter(&Filter);
428 if (!pvId)
429 {
430 LogRel(("USBService: Adding ignore filter failed!\n"));
431 return VERR_GENERAL_FAILURE;
432 }
433
434 PUSBDEVICE pDev = aDevice->i_getUsbData();
435 int rc = USBLibResetDevice(pDev->pszDevicePath, true /* Re-attach */);
436 if (RT_SUCCESS(rc))
437 aDevice->i_setBackendUserData(pvId);
438 else
439 {
440 USBLibRemoveFilter(pvId);
441 pvId = NULL;
442 }
443 LogFlowThisFunc(("returns %Rrc pvId=%p\n", rc, pvId));
444 return rc;
445}
446
447
448void USBProxyBackendSolaris::releaseDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess)
449{
450 AssertReturnVoid(aDevice->isWriteLockOnCurrentThread());
451 /*
452 * Remove the one-shot filter if necessary.
453 */
454 LogFlowThisFunc(("aDevice=%s aSuccess=%RTbool mOneShotId=%p\n", aDevice->i_getName().c_str(), aSuccess, aDevice->i_getBackendUserData()));
455 if (!aSuccess && aDevice->i_getBackendUserData())
456 USBLibRemoveFilter(aDevice->i_getBackendUserData());
457 aDevice->i_setBackendUserData(NULL);
458 USBProxyBackend::releaseDeviceCompleted(aDevice, aSuccess);
459}
460
461
462/**
463 * Returns whether devices reported by this backend go through a de/re-attach
464 * and device re-enumeration cycle when they are captured or released.
465 */
466bool USBProxyBackendSolaris::i_isDevReEnumerationRequired()
467{
468 return true;
469}
470
471
472/**
473 * Wrapper called by walkDeviceNode.
474 *
475 * @param pDevice The USB device to free.
476 */
477void solarisFreeUSBDevice(PUSBDEVICE pDevice)
478{
479 USBProxyBackend::freeDevice(pDevice);
480}
Note: See TracBrowser for help on using the repository browser.

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