VirtualBox

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

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

Main/USB/linux: move USBProxyLinuxChooseMethod to USBGetDevices

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.0 KB
Line 
1/* $Id: USBProxyServiceLinux.cpp 37618 2011-06-23 17:16:39Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Service, Linux Specialization.
4 */
5
6/*
7 * Copyright (C) 2006-2011 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 Log(("USBProxyServiceLinux::captureDevice: %p {%s}\n", aDevice, aDevice->getName().c_str()));
225 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
226 AssertReturn(aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
227
228 /*
229 * Don't think we need to do anything when the device is held... fake it.
230 */
231 Assert(aDevice->getUnistate() == kHostUSBDeviceState_Capturing);
232 interruptWait();
233
234 return VINF_SUCCESS;
235}
236
237
238int USBProxyServiceLinux::releaseDevice(HostUSBDevice *aDevice)
239{
240 Log(("USBProxyServiceLinux::releaseDevice: %p\n", aDevice));
241 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
242 AssertReturn(aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
243
244 /*
245 * We're not really holding it atm., just fake it.
246 */
247 Assert(aDevice->getUnistate() == kHostUSBDeviceState_ReleasingToHost);
248 interruptWait();
249
250 return VINF_SUCCESS;
251}
252
253
254bool USBProxyServiceLinux::updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine)
255{
256 if ( aUSBDevice->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
257 && aDevice->mUsb->enmState == USBDEVICESTATE_USED_BY_HOST)
258 LogRel(("USBProxy: Device %04x:%04x (%s) has become accessible.\n",
259 aUSBDevice->idVendor, aUSBDevice->idProduct, aUSBDevice->pszAddress));
260 return updateDeviceStateFake(aDevice, aUSBDevice, aRunFilters, aIgnoreMachine);
261}
262
263
264/**
265 * A device was added, we need to adjust mUdevPolls.
266 *
267 * See USBProxyService::deviceAdded for details.
268 */
269void USBProxyServiceLinux::deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, SessionMachinesList &llOpenedMachines, PUSBDEVICE aUSBDevice)
270{
271 if (aUSBDevice->enmState == USBDEVICESTATE_USED_BY_HOST)
272 {
273 LogRel(("USBProxy: Device %04x:%04x (%s) isn't accessible. giving udev a few seconds to fix this...\n",
274 aUSBDevice->idVendor, aUSBDevice->idProduct, aUSBDevice->pszAddress));
275 mUdevPolls = 10; /* (10 * 500ms = 5s) */
276 }
277
278 USBProxyService::deviceAdded(aDevice, llOpenedMachines, aUSBDevice);
279}
280
281
282int USBProxyServiceLinux::wait(RTMSINTERVAL aMillies)
283{
284 int rc;
285 if (mUsingUsbfsDevices)
286 rc = waitUsbfs(aMillies);
287 else
288 rc = waitSysfs(aMillies);
289 return rc;
290}
291
292
293/** String written to the wakeup pipe. */
294#define WAKE_UP_STRING "WakeUp!"
295/** Length of the string written. */
296#define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )
297
298int USBProxyServiceLinux::waitUsbfs(RTMSINTERVAL aMillies)
299{
300 struct pollfd PollFds[2];
301
302 /* Cap the wait interval if we're polling for udevd changing device permissions. */
303 if (aMillies > 500 && mUdevPolls > 0)
304 {
305 mUdevPolls--;
306 aMillies = 500;
307 }
308
309 memset(&PollFds, 0, sizeof(PollFds));
310 PollFds[0].fd = RTFileToNative(mhFile);
311 PollFds[0].events = POLLIN;
312 PollFds[1].fd = RTPipeToNative(mhWakeupPipeR);
313 PollFds[1].events = POLLIN | POLLERR | POLLHUP;
314
315 int rc = poll(&PollFds[0], 2, aMillies);
316 if (rc == 0)
317 return VERR_TIMEOUT;
318 if (rc > 0)
319 {
320 /* drain the pipe */
321 if (PollFds[1].revents & POLLIN)
322 {
323 char szBuf[WAKE_UP_STRING_LEN];
324 rc = RTPipeReadBlocking(mhWakeupPipeR, szBuf, sizeof(szBuf), NULL);
325 AssertRC(rc);
326 }
327 return VINF_SUCCESS;
328 }
329 return RTErrConvertFromErrno(errno);
330}
331
332
333int USBProxyServiceLinux::waitSysfs(RTMSINTERVAL aMillies)
334{
335#ifdef VBOX_USB_WITH_SYSFS
336 int rc = mpWaiter->Wait(aMillies);
337 if (rc == VERR_TRY_AGAIN)
338 {
339 RTThreadYield();
340 rc = VINF_SUCCESS;
341 }
342 return rc;
343#else /* !VBOX_USB_WITH_SYSFS */
344 return USBProxyService::wait(aMillies);
345#endif /* !VBOX_USB_WITH_SYSFS */
346}
347
348
349int USBProxyServiceLinux::interruptWait(void)
350{
351#ifdef VBOX_USB_WITH_SYSFS
352 LogFlowFunc(("mUsingUsbfsDevices=%d\n", mUsingUsbfsDevices));
353 if (!mUsingUsbfsDevices)
354 {
355 mpWaiter->Interrupt();
356 LogFlowFunc(("Returning VINF_SUCCESS\n"));
357 return VINF_SUCCESS;
358 }
359#endif /* VBOX_USB_WITH_SYSFS */
360 int rc = RTPipeWriteBlocking(mhWakeupPipeW, WAKE_UP_STRING, WAKE_UP_STRING_LEN, NULL);
361 if (RT_SUCCESS(rc))
362 RTPipeFlush(mhWakeupPipeW);
363 LogFlowFunc(("returning %Rrc\n", rc));
364 return rc;
365}
366
367
368PUSBDEVICE USBProxyServiceLinux::getDevices(void)
369{
370 return USBProxyLinuxGetDevices(mDevicesRoot.c_str(), !mUsingUsbfsDevices);
371}
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