VirtualBox

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

Last change on this file since 76190 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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