~ubuntu-branches/ubuntu/gutsy/virtualbox-ose/gutsy

« back to all changes in this revision

Viewing changes to src/VBox/Frontends/VBoxBFE/HostUSBImpl.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-09-08 16:44:58 UTC
  • Revision ID: james.westby@ubuntu.com-20070908164458-wao29470vqtr8ksy
Tags: upstream-1.5.0-dfsg2
ImportĀ upstreamĀ versionĀ 1.5.0-dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** @file
 
2
 *
 
3
 * VBox frontends: Basic Frontend (BFE):
 
4
 * Implementation of HostUSB
 
5
 */
 
6
 
 
7
/*
 
8
 * Copyright (C) 2006-2007 innotek GmbH
 
9
 *
 
10
 * This file is part of VirtualBox Open Source Edition (OSE), as
 
11
 * available from http://www.virtualbox.org. This file is free software;
 
12
 * you can redistribute it and/or modify it under the terms of the GNU
 
13
 * General Public License as published by the Free Software Foundation,
 
14
 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
 
15
 * distribution. VirtualBox OSE is distributed in the hope that it will
 
16
 * be useful, but WITHOUT ANY WARRANTY of any kind.
 
17
 */
 
18
 
 
19
/* r=michael: I have removed almost all functionality from the Main
 
20
 *            version of this file, but left the structure as similar
 
21
 *            as I could in case we do need some of it some day. */
 
22
 
 
23
#include <string>
 
24
 
 
25
#ifdef RT_OS_LINUX
 
26
#include <sys/types.h>
 
27
#include <sys/stat.h>
 
28
#include <unistd.h>
 
29
#include <sys/ioctl.h>
 
30
#include <fcntl.h>
 
31
#include <mntent.h>
 
32
/* bird: This is a hack to work around conflicts between these linux kernel headers
 
33
 *       and the GLIBC tcpip headers. They have different declarations of the 4
 
34
 *       standard byte order functions. */
 
35
#define _LINUX_BYTEORDER_GENERIC_H
 
36
#include <linux/cdrom.h>
 
37
#include <errno.h>
 
38
#endif /* RT_OS_LINUX */
 
39
 
 
40
#include "HostUSBImpl.h"
 
41
#include "HostUSBDeviceImpl.h"
 
42
#include "USBProxyService.h"
 
43
#include "Logging.h"
 
44
 
 
45
#include <VBox/pdm.h>
 
46
#include <VBox/vusb.h>
 
47
#include <VBox/usb.h>
 
48
#include <VBox/err.h>
 
49
#include <iprt/string.h>
 
50
#include <iprt/system.h>
 
51
#include <iprt/time.h>
 
52
#include <stdio.h>
 
53
 
 
54
#include <algorithm>
 
55
 
 
56
// destructor
 
57
/////////////////////////////////////////////////////////////////////////////
 
58
 
 
59
HostUSB::~HostUSB()
 
60
{
 
61
    if (isReady())
 
62
        uninit();
 
63
}
 
64
 
 
65
// public initializer/uninitializer for internal purposes only
 
66
/////////////////////////////////////////////////////////////////////////////
 
67
 
 
68
/**
 
69
 * Initializes the host USB object.
 
70
 *
 
71
 * @returns COM result indicator
 
72
 * @param parent handle of our parent object
 
73
 */
 
74
HRESULT HostUSB::init(PVM pVM)
 
75
{
 
76
    LogFlowMember(("HostUSB::init(): isReady=%d\n", isReady()));
 
77
 
 
78
    ComAssertRet (!isReady(), E_UNEXPECTED);
 
79
 
 
80
    /* Save pointer to the VM */
 
81
    mpVM = pVM;
 
82
 
 
83
/*
 
84
#ifdef RT_OS_LINUX
 
85
    mUSBProxyService = new USBProxyServiceLinux (this);
 
86
#elif defined RT_OS_WINDOWS
 
87
    mUSBProxyService = new USBProxyServiceWin32 (this);
 
88
*/
 
89
#ifdef RT_OS_L4
 
90
    mUSBProxyService = new USBProxyServiceLinux (this);
 
91
#else
 
92
    mUSBProxyService = new USBProxyService (this);
 
93
#endif
 
94
    /** @todo handle !mUSBProxySerivce->isActive() and mUSBProxyService->getLastError()
 
95
     * and somehow report or whatever that the proxy failed to startup.
 
96
     * Also, there might be init order issues... */
 
97
 
 
98
    setReady(true);
 
99
    return S_OK;
 
100
}
 
101
 
 
102
/**
 
103
 *  Uninitializes the host object and sets the ready flag to FALSE.
 
104
 *  Called either from FinalRelease() or by the parent when it gets destroyed.
 
105
 */
 
106
void HostUSB::uninit()
 
107
{
 
108
    LogFlowMember(("HostUSB::uninit(): isReady=%d\n", isReady()));
 
109
 
 
110
    AssertReturn (isReady(), (void) 0);
 
111
 
 
112
    delete mUSBProxyService;
 
113
    mUSBProxyService = NULL;
 
114
 
 
115
    mUSBDevices.clear();
 
116
 
 
117
    setReady (FALSE);
 
118
}
 
119
 
 
120
// private methods
 
121
////////////////////////////////////////////////////////////////////////////////
 
122
 
 
123
/**
 
124
 *  Called by USB proxy service when a new device is physically attached
 
125
 *  to the host.
 
126
 *
 
127
 *  @param      aDevice     Pointer to the device which has been attached.
 
128
 */
 
129
void HostUSB::onUSBDeviceAttached (HostUSBDevice *aDevice)
 
130
{
 
131
    LogFlowMember (("HostUSB::onUSBDeviceAttached: aDevice=%p\n",
 
132
                    aDevice));
 
133
    HostUSB::AutoLock alock (this);
 
134
 
 
135
    // add to the collecion
 
136
    mUSBDevices.push_back (aDevice);
 
137
 
 
138
    // apply all filters (no need to lock the device, nobody can access it yet)
 
139
    HRESULT rc = AttachUSBDevice(aDevice);
 
140
    AssertComRC (rc);
 
141
}
 
142
 
 
143
/**
 
144
 *  Called by USB proxy service (?) when the device is physically detached
 
145
 *  from the host.
 
146
 *
 
147
 *  @param      aDevice     Pointer to the device which has been detached.
 
148
 */
 
149
void HostUSB::onUSBDeviceDetached (HostUSBDevice *aDevice)
 
150
{
 
151
    LogFlowMember (("HostUSB::onUSBDeviceDetached: aDevice=%p\n",
 
152
                    &aDevice));
 
153
    HostUSB::AutoLock alock (this);
 
154
 
 
155
    RTUUID id = aDevice->id();
 
156
 
 
157
    HostUSBDevice *device = 0;
 
158
    HostUSB::USBDeviceList::iterator it = mUSBDevices.begin();
 
159
    while (it != mUSBDevices.end())
 
160
    {
 
161
        if (RTUuidCompare(&(*it)->id(), &id) == 0)
 
162
        {
 
163
            device = (*it);
 
164
            break;
 
165
        }
 
166
        ++ it;
 
167
    }
 
168
 
 
169
    AssertReturn (!!device, (void) 0);
 
170
 
 
171
    // remove from the collecion
 
172
    mUSBDevices.erase (it);
 
173
 
 
174
    if (device->isCaptured())
 
175
    {
 
176
        // the device is captured, release it
 
177
        alock.unlock();
 
178
        HRESULT rc = DetachUSBDevice (device);
 
179
        AssertComRC (rc);
 
180
    }
 
181
}
 
182
 
 
183
/**
 
184
 *  Called by USB proxy service  when the state of the host-driven device
 
185
 *  has changed because of non proxy interaction.
 
186
 *
 
187
 *  @param  aDevice     The device in question.
 
188
 */
 
189
void HostUSB::onUSBDeviceStateChanged (HostUSBDevice *aDevice)
 
190
{
 
191
    LogFlowMember (("HostUSB::onUSBDeviceStateChanged: \n"));
 
192
    HostUSB::AutoLock alock (this);
 
193
 
 
194
    /** @todo dmik, is there anything we should do here? For instance if the device now is available? */
 
195
}
 
196
 
 
197
STDMETHODIMP HostUSB::AttachUSBDevice (HostUSBDevice *hostDevice)
 
198
{
 
199
    AutoLock alock (this);
 
200
    /* This originally checked that the console object was ready.
 
201
     * Unfortunately, this method now belongs to HostUSB, and can get
 
202
     * called during construction - so before we are "ready". */
 
203
//    CHECK_READY();
 
204
 
 
205
    /*
 
206
     * Don't proceed unless we've found the usb controller.
 
207
     */
 
208
    if (!mpVM)
 
209
        return setError (E_FAIL, tr ("VM is not powered up"));
 
210
    PPDMIBASE pBase;
 
211
    int vrc = PDMR3QueryLun (mpVM, "usb-ohci", 0, 0, &pBase);
 
212
    if (VBOX_FAILURE (vrc))
 
213
        return setError (E_FAIL, tr ("VM doesn't have a USB controller"));
 
214
    /*
 
215
     * Make sure that the device is in a captureable state
 
216
     */
 
217
    USBDeviceState_T eState = hostDevice->state();
 
218
    if (eState != USBDeviceState_USBDeviceBusy &&
 
219
        eState != USBDeviceState_USBDeviceAvailable &&
 
220
        eState != USBDeviceState_USBDeviceHeld)
 
221
        return setError (E_FAIL,
 
222
                         tr ("Device is not in a capturable state"));
 
223
    PVUSBIRHCONFIG pRhConfig = (PVUSBIRHCONFIG)pBase->pfnQueryInterface (pBase, PDMINTERFACE_VUSB_RH_CONFIG);
 
224
    AssertReturn (pRhConfig, E_FAIL);
 
225
 
 
226
    /*
 
227
     * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub method in EMT.
 
228
     */
 
229
    std::string Address;
 
230
    STDMETHODIMP hrc = hostDevice->COMGETTER (Address) (&Address);
 
231
    AssertComRC (hrc);
 
232
 
 
233
    RTUUID Uuid;
 
234
    hrc = hostDevice->COMGETTER (Id) (Uuid);
 
235
    AssertComRC (hrc);
 
236
 
 
237
    /* No remote devices for now */
 
238
    BOOL fRemote = FALSE;
 
239
    void *pvRemote = NULL;
 
240
 
 
241
    LogFlowMember (("Console::AttachUSBDevice: Proxying USB device '%s' %Vuuid...\n", Address.c_str(), &Uuid));
 
242
    PVMREQ pReq;
 
243
    vrc = VMR3ReqCall (mpVM, &pReq, RT_INDEFINITE_WAIT,
 
244
                       (PFNRT)pRhConfig->pfnCreateProxyDevice,
 
245
                       5, pRhConfig, &Uuid, fRemote,
 
246
                       Address.c_str(), pvRemote);
 
247
    if (VBOX_SUCCESS (vrc))
 
248
        vrc = pReq->iStatus;
 
249
    VMR3ReqFree (pReq);
 
250
    if (VBOX_SUCCESS (vrc))
 
251
        hostDevice->setCaptured();
 
252
    else
 
253
    {
 
254
        Log (("Console::AttachUSBDevice: Failed to create proxy device for '%s' %Vuuid, vrc=%Vrc\n", Address.c_str(),
 
255
        &Uuid, vrc));
 
256
        AssertRC (vrc);
 
257
    /* michael: I presume this is not needed. */
 
258
/*        hrc = mControl->ReleaseUSBDevice (Uuid);
 
259
        AssertComRC (hrc); */
 
260
        switch (vrc)
 
261
        {
 
262
            case VERR_VUSB_NO_PORTS:
 
263
                hrc = setError (E_FAIL, tr ("No available ports on the USB controller"));
 
264
                break;
 
265
            case VERR_VUSB_USBFS_PERMISSION:
 
266
                hrc = setError (E_FAIL, tr ("Not permitted to open the USB device, check usbfs options"));
 
267
                break;
 
268
            default:
 
269
                hrc = setError (E_FAIL, tr ("Failed to create USB proxy device: %Vrc"), vrc);
 
270
                break;
 
271
        }
 
272
        return hrc;
 
273
    }
 
274
 
 
275
    return S_OK;
 
276
}
 
277
 
 
278
STDMETHODIMP HostUSB::DetachUSBDevice (HostUSBDevice *aDevice)
 
279
{
 
280
    AutoLock alock (this);
 
281
    /* This originally checked that the console object was ready.
 
282
     * Unfortunately, this method now belongs to HostUSB, and can get
 
283
     * called during construction - so before we are "ready". */
 
284
//    CHECK_READY();
 
285
 
 
286
    /*
 
287
     * Detach the device from the VM
 
288
     */
 
289
    int vrc = VERR_PDM_DEVICE_NOT_FOUND;
 
290
    if (mpVM)
 
291
    {
 
292
        PPDMIBASE pBase;
 
293
        vrc = PDMR3QueryLun (mpVM, "usb-ohci", 0, 0, &pBase);
 
294
        if (VBOX_SUCCESS (vrc))
 
295
        {
 
296
            PVUSBIRHCONFIG pRhConfig = (PVUSBIRHCONFIG)pBase->pfnQueryInterface (pBase, PDMINTERFACE_VUSB_RH_CONFIG);
 
297
            Assert (pRhConfig);
 
298
 
 
299
            RTUUID Uuid = aDevice->id();
 
300
            LogFlowMember (("Console::DetachUSBDevice: Detaching USB proxy device %Vuuid...\n", &Uuid));
 
301
            PVMREQ pReq;
 
302
            vrc = VMR3ReqCall (mpVM, &pReq, RT_INDEFINITE_WAIT, (PFNRT)pRhConfig->pfnDestroyProxyDevice,
 
303
                               2, pRhConfig, &Uuid);
 
304
            if (VBOX_SUCCESS (vrc))
 
305
                vrc = pReq->iStatus;
 
306
            VMR3ReqFree (pReq);
 
307
        }
 
308
    }
 
309
    if (    vrc == VERR_PDM_DEVICE_NOT_FOUND
 
310
        ||  vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
 
311
    {
 
312
        Log (("Console::DetachUSBDevice: USB isn't enabled.\n"));
 
313
        vrc = VINF_SUCCESS;
 
314
    }
 
315
    if (VBOX_SUCCESS (vrc))
 
316
        return S_OK;
 
317
    Log (("Console::AttachUSBDevice: Failed to detach the device from the USB controller, vrc=%Vrc.\n", vrc));
 
318
    return(setError (E_UNEXPECTED, tr ("Failed to destroy the USB proxy device: %Vrc"), vrc));
 
319
}