VirtualBox

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

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

Main: Add API to IHost for adding and removing USB device sources in addition to the default host one (only USB/IP backend supported so far which will be used in the future for automatic USB testing). Add support for it in VBoxManage

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