VirtualBox

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

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

Main/USBProxyService: Save any additional USB device sources in the global configuration and load them during startup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 KB
Line 
1/* $Id: USBProxyBackendSolaris.cpp 60107 2016-03-19 10:22:46Z 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()
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 pCur->pszProduct = RTStrDup(pStr);
285
286 if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "usb-vendor-name", &pStr) > 0)
287 pCur->pszManufacturer = RTStrDup(pStr);
288
289 if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "usb-serialno", &pStr) > 0)
290 pCur->pszSerialNumber = RTStrDup(pStr);
291
292 if (pCur->bcdUSB == 0x300)
293 pCur->enmSpeed = USBDEVICESPEED_SUPER;
294 else if (di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "low-speed", &pInt) >= 0)
295 pCur->enmSpeed = USBDEVICESPEED_LOW;
296 else if (di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "high-speed", &pInt) >= 0)
297 pCur->enmSpeed = USBDEVICESPEED_HIGH;
298 else
299 pCur->enmSpeed = USBDEVICESPEED_FULL;
300
301 /* Determine state of the USB device. */
302 pCur->enmState = solarisDetermineUSBDeviceState(pCur, Node);
303
304 /*
305 * Valid device, add it to the list.
306 */
307 fValidDevice = true;
308 pCur->pPrev = pList->pTail;
309 if (pList->pTail)
310 pList->pTail = pList->pTail->pNext = pCur;
311 else
312 pList->pTail = pList->pHead = pCur;
313
314 rc = DI_WALK_CONTINUE;
315 } while (0);
316
317 di_devfs_path_free(pszDevicePath);
318 if (!fValidDevice)
319 solarisFreeUSBDevice(pCur);
320 }
321 return rc;
322}
323
324
325static USBDEVICESTATE solarisDetermineUSBDeviceState(PUSBDEVICE pDevice, di_node_t Node)
326{
327 char *pszDriverName = di_driver_name(Node);
328
329 /* Not possible unless a user explicitly unbinds the default driver. */
330 if (!pszDriverName)
331 return USBDEVICESTATE_UNUSED;
332
333 if (!strncmp(pszDriverName, RT_STR_TUPLE(VBOXUSB_DRIVER_NAME)))
334 return USBDEVICESTATE_HELD_BY_PROXY;
335
336 NOREF(pDevice);
337 return USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
338}
339
340
341int USBProxyBackendSolaris::captureDevice(HostUSBDevice *aDevice)
342{
343 /*
344 * Check preconditions.
345 */
346 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
347 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
348
349 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
350 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
351
352 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_Capturing);
353 AssertReturn(aDevice->i_getUsbData(), VERR_INVALID_POINTER);
354
355 /*
356 * Create a one-shot capture filter for the device and reset the device.
357 */
358 USBFILTER Filter;
359 USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_CAPTURE);
360 initFilterFromDevice(&Filter, aDevice);
361
362 void *pvId = USBLibAddFilter(&Filter);
363 if (!pvId)
364 {
365 LogRel(("USBService: failed to add filter\n"));
366 return VERR_GENERAL_FAILURE;
367 }
368
369 PUSBDEVICE pDev = aDevice->i_getUsbData();
370 int rc = USBLibResetDevice(pDev->pszDevicePath, true);
371 if (RT_SUCCESS(rc))
372 aDevice->i_setBackendUserData(pvId);
373 else
374 {
375 USBLibRemoveFilter(pvId);
376 pvId = NULL;
377 }
378 LogFlowThisFunc(("returns %Rrc pvId=%p\n", rc, pvId));
379 return rc;
380}
381
382
383void USBProxyBackendSolaris::captureDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess)
384{
385 AssertReturnVoid(aDevice->isWriteLockOnCurrentThread());
386 /*
387 * Remove the one-shot filter if necessary.
388 */
389 LogFlowThisFunc(("aDevice=%s aSuccess=%RTbool mOneShotId=%p\n", aDevice->i_getName().c_str(), aSuccess, aDevice->i_getBackendUserData()));
390 if (!aSuccess && aDevice->i_getBackendUserData())
391 USBLibRemoveFilter(aDevice->i_getBackendUserData());
392 aDevice->i_setBackendUserData(NULL);
393 USBProxyBackend::captureDeviceCompleted(aDevice, aSuccess);
394}
395
396
397int USBProxyBackendSolaris::releaseDevice(HostUSBDevice *aDevice)
398{
399 /*
400 * Check preconditions.
401 */
402 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
403 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
404
405 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
406 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
407
408 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost);
409 AssertReturn(aDevice->i_getUsbData(), VERR_INVALID_POINTER);
410
411 /*
412 * Create a one-shot ignore filter for the device and reset it.
413 */
414 USBFILTER Filter;
415 USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_IGNORE);
416 initFilterFromDevice(&Filter, aDevice);
417
418 void *pvId = USBLibAddFilter(&Filter);
419 if (!pvId)
420 {
421 LogRel(("USBService: Adding ignore filter failed!\n"));
422 return VERR_GENERAL_FAILURE;
423 }
424
425 PUSBDEVICE pDev = aDevice->i_getUsbData();
426 int rc = USBLibResetDevice(pDev->pszDevicePath, true /* Re-attach */);
427 if (RT_SUCCESS(rc))
428 aDevice->i_setBackendUserData(pvId);
429 else
430 {
431 USBLibRemoveFilter(pvId);
432 pvId = NULL;
433 }
434 LogFlowThisFunc(("returns %Rrc pvId=%p\n", rc, pvId));
435 return rc;
436}
437
438
439void USBProxyBackendSolaris::releaseDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess)
440{
441 AssertReturnVoid(aDevice->isWriteLockOnCurrentThread());
442 /*
443 * Remove the one-shot filter if necessary.
444 */
445 LogFlowThisFunc(("aDevice=%s aSuccess=%RTbool mOneShotId=%p\n", aDevice->i_getName().c_str(), aSuccess, aDevice->i_getBackendUserData()));
446 if (!aSuccess && aDevice->i_getBackendUserData())
447 USBLibRemoveFilter(aDevice->i_getBackendUserData());
448 aDevice->i_setBackendUserData(NULL);
449 USBProxyBackend::releaseDeviceCompleted(aDevice, aSuccess);
450}
451
452
453bool USBProxyBackendSolaris::updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters,
454 SessionMachine **aIgnoreMachine)
455{
456 AssertReturn(aDevice, false);
457 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), false);
458 return USBProxyBackend::updateDeviceState(aDevice, aUSBDevice, aRunFilters, aIgnoreMachine);
459}
460
461/**
462 * Wrapper called by walkDeviceNode.
463 *
464 * @param pDevice The USB device to free.
465 */
466void solarisFreeUSBDevice(PUSBDEVICE pDevice)
467{
468 USBProxyBackend::freeDevice(pDevice);
469}
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