VirtualBox

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

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