VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

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