VirtualBox

source: vbox/trunk/src/VBox/Main/linux/USBProxyServiceLinux.cpp@ 34341

Last change on this file since 34341 was 34341, checked in by vboxsync, 14 years ago

Main/linux/USB and RDP/client/vrdp: clean up the handling of the USB device tree path and get rid of data duplication

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