VirtualBox

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

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

*: spelling fixes, thanks Timeless!

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