VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/linux/USBProxyServiceLinux.cpp@ 53517

Last change on this file since 53517 was 51092, checked in by vboxsync, 11 years ago

6813 src-client/MachineDebuggerImpl.cpp + various formatting changes

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