VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/linux/USBProxyBackendLinux.cpp@ 93115

Last change on this file since 93115 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: 10.3 KB
Line 
1/* $Id: USBProxyBackendLinux.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Service, Linux 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 "USBProxyService.h"
24#include "USBGetDevices.h"
25#include "LoggingNew.h"
26
27#include <VBox/usb.h>
28#include <VBox/usblib.h>
29#include <iprt/errcore.h>
30
31#include <iprt/string.h>
32#include <iprt/alloc.h>
33#include <iprt/assert.h>
34#include <iprt/ctype.h>
35#include <iprt/dir.h>
36#include <iprt/env.h>
37#include <iprt/file.h>
38#include <iprt/errcore.h>
39#include <iprt/mem.h>
40#include <iprt/param.h>
41#include <iprt/path.h>
42#include <iprt/pipe.h>
43#include <iprt/stream.h>
44#include <iprt/linux/sysfs.h>
45
46#include <stdlib.h>
47#include <string.h>
48#include <stdio.h>
49#include <errno.h>
50#include <fcntl.h>
51#include <unistd.h>
52#include <sys/statfs.h>
53#include <sys/poll.h>
54#ifdef VBOX_WITH_LINUX_COMPILER_H
55# include <linux/compiler.h>
56#endif
57#include <linux/usbdevice_fs.h>
58
59
60/**
61 * Initialize data members.
62 */
63USBProxyBackendLinux::USBProxyBackendLinux()
64 : USBProxyBackend(), mhFile(NIL_RTFILE), mhWakeupPipeR(NIL_RTPIPE), mhWakeupPipeW(NIL_RTPIPE), mpWaiter(NULL)
65{
66 LogFlowThisFunc(("\n"));
67}
68
69
70/**
71 * Stop all service threads and free the device chain.
72 */
73USBProxyBackendLinux::~USBProxyBackendLinux()
74{
75 LogFlowThisFunc(("\n"));
76}
77
78/**
79 * Initializes the object (called right after construction).
80 *
81 * @returns VBox status code.
82 */
83int USBProxyBackendLinux::init(USBProxyService *pUsbProxyService, const com::Utf8Str &strId,
84 const com::Utf8Str &strAddress, bool fLoadingSettings)
85{
86 USBProxyBackend::init(pUsbProxyService, strId, strAddress, fLoadingSettings);
87
88 unconst(m_strBackend) = Utf8Str("host");
89
90 const char *pcszDevicesRoot;
91 int rc = USBProxyLinuxChooseMethod(&mUsingUsbfsDevices, &pcszDevicesRoot);
92 if (RT_SUCCESS(rc))
93 {
94 mDevicesRoot = pcszDevicesRoot;
95 rc = mUsingUsbfsDevices ? initUsbfs() : initSysfs();
96 /* For the day when we have VBoxSVC release logging... */
97 LogRel((RT_SUCCESS(rc) ? "Successfully initialised host USB using %s\n"
98 : "Failed to initialise host USB using %s\n",
99 mUsingUsbfsDevices ? "USBFS" : "sysfs"));
100 }
101
102 return rc;
103}
104
105void USBProxyBackendLinux::uninit()
106{
107 /*
108 * Stop the service.
109 */
110 if (isActive())
111 stop();
112
113 /*
114 * Free resources.
115 */
116 doUsbfsCleanupAsNeeded();
117#ifdef VBOX_USB_WITH_SYSFS
118 if (mpWaiter)
119 delete mpWaiter;
120#endif
121
122 USBProxyBackend::uninit();
123}
124
125
126/**
127 * Initialization routine for the usbfs based operation.
128 *
129 * @returns iprt status code.
130 */
131int USBProxyBackendLinux::initUsbfs(void)
132{
133 Assert(mUsingUsbfsDevices);
134
135 /*
136 * Open the devices file.
137 */
138 int rc;
139 char *pszDevices = RTPathJoinA(mDevicesRoot.c_str(), "devices");
140 if (pszDevices)
141 {
142 rc = RTFileOpen(&mhFile, pszDevices, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
143 if (RT_SUCCESS(rc))
144 {
145 rc = RTPipeCreate(&mhWakeupPipeR, &mhWakeupPipeW, 0 /*fFlags*/);
146 if (RT_SUCCESS(rc))
147 {
148 /*
149 * Start the poller thread.
150 */
151 rc = start();
152 if (RT_SUCCESS(rc))
153 {
154 RTStrFree(pszDevices);
155 LogFlowThisFunc(("returns successfully\n"));
156 return VINF_SUCCESS;
157 }
158
159 RTPipeClose(mhWakeupPipeR);
160 RTPipeClose(mhWakeupPipeW);
161 mhWakeupPipeW = mhWakeupPipeR = NIL_RTPIPE;
162 }
163 else
164 Log(("USBProxyBackendLinux::USBProxyBackendLinux: RTFilePipe failed with rc=%Rrc\n", rc));
165 RTFileClose(mhFile);
166 }
167
168 RTStrFree(pszDevices);
169 }
170 else
171 {
172 rc = VERR_NO_MEMORY;
173 Log(("USBProxyBackendLinux::USBProxyBackendLinux: out of memory!\n"));
174 }
175
176 LogFlowThisFunc(("returns failure!!! (rc=%Rrc)\n", rc));
177 return rc;
178}
179
180
181/**
182 * Initialization routine for the sysfs based operation.
183 *
184 * @returns iprt status code
185 */
186int USBProxyBackendLinux::initSysfs(void)
187{
188 Assert(!mUsingUsbfsDevices);
189
190#ifdef VBOX_USB_WITH_SYSFS
191 try
192 {
193 mpWaiter = new VBoxMainHotplugWaiter(mDevicesRoot.c_str());
194 }
195 catch(std::bad_alloc &e)
196 {
197 return VERR_NO_MEMORY;
198 }
199 int rc = mpWaiter->getStatus();
200 if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT || rc == VERR_TRY_AGAIN)
201 rc = start();
202 else if (rc == VERR_NOT_SUPPORTED)
203 /* This can legitimately happen if hal or DBus are not running, but of
204 * course we can't start in this case. */
205 rc = VINF_SUCCESS;
206 return rc;
207
208#else /* !VBOX_USB_WITH_SYSFS */
209 return VERR_NOT_IMPLEMENTED;
210#endif /* !VBOX_USB_WITH_SYSFS */
211}
212
213
214/**
215 * If any Usbfs-related resources are currently allocated, then free them
216 * and mark them as freed.
217 */
218void USBProxyBackendLinux::doUsbfsCleanupAsNeeded()
219{
220 /*
221 * Free resources.
222 */
223 if (mhFile != NIL_RTFILE)
224 RTFileClose(mhFile);
225 mhFile = NIL_RTFILE;
226
227 if (mhWakeupPipeR != NIL_RTPIPE)
228 RTPipeClose(mhWakeupPipeR);
229 if (mhWakeupPipeW != NIL_RTPIPE)
230 RTPipeClose(mhWakeupPipeW);
231 mhWakeupPipeW = mhWakeupPipeR = NIL_RTPIPE;
232}
233
234
235int USBProxyBackendLinux::captureDevice(HostUSBDevice *aDevice)
236{
237 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
238 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
239
240 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
241 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
242
243 /*
244 * Don't think we need to do anything when the device is held... fake it.
245 */
246 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_Capturing);
247 devLock.release();
248 interruptWait();
249
250 return VINF_SUCCESS;
251}
252
253
254int USBProxyBackendLinux::releaseDevice(HostUSBDevice *aDevice)
255{
256 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
257 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
258
259 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
260 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
261
262 /*
263 * We're not really holding it atm., just fake it.
264 */
265 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost);
266 devLock.release();
267 interruptWait();
268
269 return VINF_SUCCESS;
270}
271
272
273/**
274 * A device was added, we need to adjust mUdevPolls.
275 */
276void USBProxyBackendLinux::deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, PUSBDEVICE pDev)
277{
278 AssertReturnVoid(aDevice);
279 AssertReturnVoid(!aDevice->isWriteLockOnCurrentThread());
280 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
281 if (pDev->enmState == USBDEVICESTATE_USED_BY_HOST)
282 {
283 LogRel(("USBProxyBackendLinux: Device %04x:%04x (%s) isn't accessible. giving udev a few seconds to fix this...\n",
284 pDev->idVendor, pDev->idProduct, pDev->pszAddress));
285 mUdevPolls = 10; /* (10 * 500ms = 5s) */
286 }
287
288 devLock.release();
289}
290
291
292bool USBProxyBackendLinux::isFakeUpdateRequired()
293{
294 return true;
295}
296
297int USBProxyBackendLinux::wait(RTMSINTERVAL aMillies)
298{
299 int rc;
300 if (mUsingUsbfsDevices)
301 rc = waitUsbfs(aMillies);
302 else
303 rc = waitSysfs(aMillies);
304 return rc;
305}
306
307
308/** String written to the wakeup pipe. */
309#define WAKE_UP_STRING "WakeUp!"
310/** Length of the string written. */
311#define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )
312
313int USBProxyBackendLinux::waitUsbfs(RTMSINTERVAL aMillies)
314{
315 struct pollfd PollFds[2];
316
317 /* Cap the wait interval if we're polling for udevd changing device permissions. */
318 if (aMillies > 500 && mUdevPolls > 0)
319 {
320 mUdevPolls--;
321 aMillies = 500;
322 }
323
324 RT_ZERO(PollFds);
325 PollFds[0].fd = (int)RTFileToNative(mhFile);
326 PollFds[0].events = POLLIN;
327 PollFds[1].fd = (int)RTPipeToNative(mhWakeupPipeR);
328 PollFds[1].events = POLLIN | POLLERR | POLLHUP;
329
330 int rc = poll(&PollFds[0], 2, aMillies);
331 if (rc == 0)
332 return VERR_TIMEOUT;
333 if (rc > 0)
334 {
335 /* drain the pipe */
336 if (PollFds[1].revents & POLLIN)
337 {
338 char szBuf[WAKE_UP_STRING_LEN];
339 rc = RTPipeReadBlocking(mhWakeupPipeR, szBuf, sizeof(szBuf), NULL);
340 AssertRC(rc);
341 }
342 return VINF_SUCCESS;
343 }
344 return RTErrConvertFromErrno(errno);
345}
346
347
348int USBProxyBackendLinux::waitSysfs(RTMSINTERVAL aMillies)
349{
350#ifdef VBOX_USB_WITH_SYSFS
351 int rc = mpWaiter->Wait(aMillies);
352 if (rc == VERR_TRY_AGAIN)
353 {
354 RTThreadYield();
355 rc = VINF_SUCCESS;
356 }
357 return rc;
358#else /* !VBOX_USB_WITH_SYSFS */
359 return USBProxyService::wait(aMillies);
360#endif /* !VBOX_USB_WITH_SYSFS */
361}
362
363
364int USBProxyBackendLinux::interruptWait(void)
365{
366 AssertReturn(!isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
367
368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
369#ifdef VBOX_USB_WITH_SYSFS
370 LogFlowFunc(("mUsingUsbfsDevices=%d\n", mUsingUsbfsDevices));
371 if (!mUsingUsbfsDevices)
372 {
373 mpWaiter->Interrupt();
374 LogFlowFunc(("Returning VINF_SUCCESS\n"));
375 return VINF_SUCCESS;
376 }
377#endif /* VBOX_USB_WITH_SYSFS */
378 int rc = RTPipeWriteBlocking(mhWakeupPipeW, WAKE_UP_STRING, WAKE_UP_STRING_LEN, NULL);
379 if (RT_SUCCESS(rc))
380 RTPipeFlush(mhWakeupPipeW);
381 LogFlowFunc(("returning %Rrc\n", rc));
382 return rc;
383}
384
385
386PUSBDEVICE USBProxyBackendLinux::getDevices(void)
387{
388 return USBProxyLinuxGetDevices(mDevicesRoot.c_str(), !mUsingUsbfsDevices);
389}
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