VirtualBox

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

Last change on this file since 32437 was 32431, checked in by vboxsync, 15 years ago

scm cleanup

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette