VirtualBox

source: vbox/trunk/src/VBox/RDP/client/vrdp/USBProxyDevice-linux.c@ 28800

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

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.5 KB
Line 
1/** @file
2 *
3 * VUSB Device - USB Device Proxy, the Linux backend.
4 */
5
6/*
7 * Copyright (C) 2006-2007 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* Defined Constants And Macros *
21*******************************************************************************/
22/** Define NO_PORT_RESET to skip the slow and broken linux port reset.
23 * Resetting will break PalmOne. */
24#define NO_PORT_RESET
25/** Define NO_LOGICAL_RECONNECT to skip the broken logical reconnect handling. */
26#define NO_LOGICAL_RECONNECT
27
28
29/*******************************************************************************
30* Header Files *
31*******************************************************************************/
32#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
33#if defined(VBOX) && !defined(RDESKTOP)
34# include <iprt/stdint.h>
35#endif
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <sys/vfs.h>
39#include <sys/ioctl.h>
40#include <sys/poll.h>
41#include <stdint.h>
42#include <stdio.h>
43#include <string.h>
44#include <stdlib.h>
45#include <limits.h>
46#include <unistd.h>
47#include <fcntl.h>
48#include <errno.h>
49#include <ctype.h>
50#ifdef VBOX_WITH_LINUX_COMPILER_H
51# include <linux/compiler.h>
52#endif
53#include <linux/usbdevice_fs.h>
54/*
55 * Backlevel 2.4 headers doesn't have these two defines.
56 * They were added some time between 2.4.21 and 2.4.26, probably in 2.4.23.
57 */
58#ifndef USBDEVFS_DISCONNECT
59# define USBDEVFS_DISCONNECT _IO('U', 22)
60# define USBDEVFS_CONNECT _IO('U', 23)
61#endif
62
63#ifndef USBDEVFS_URB_SHORT_NOT_OK
64# define USBDEVFS_URB_SHORT_NOT_OK 0 /* rhel3 doesn't have this. darn! */
65#endif
66
67
68/* FedoraCore 4 does not have the bit defined by default. */
69#ifndef POLLWRNORM
70# define POLLWRNORM 0x0100
71#endif
72
73#if defined(VBOX) && !defined(RDESKTOP)
74# include <VBox/pdm.h>
75# include <VBox/err.h>
76# include <VBox/log.h>
77# include <iprt/assert.h>
78# include <iprt/stream.h>
79# include <iprt/alloc.h>
80# include <iprt/thread.h>
81# include <iprt/time.h>
82# include <iprt/asm.h>
83# include <iprt/string.h>
84# include <iprt/file.h>
85# include "../USBProxyDevice.h"
86#else
87
88# include "../rdesktop.h"
89# include "vrdpusb.h"
90#endif
91
92
93/*******************************************************************************
94* Structures and Typedefs *
95*******************************************************************************/
96/**
97 * Wrapper around the linux urb request structure.
98 * This is required to track in-flight and landed URBs.
99 */
100typedef struct USBPROXYURBLNX
101{
102 /** The kernel URB data */
103 struct usbdevfs_urb KUrb;
104 /** The millisecond timestamp when this URB was submitted. */
105 uint64_t u64SubmitTS;
106 /** Pointer to the next linux URB. */
107 struct USBPROXYURBLNX *pNext;
108 /** Pointer to the previous linux URB. */
109 struct USBPROXYURBLNX *pPrev;
110 /** If we've split the VUSBURB up into multiple linux URBs, this is points to the head. */
111 struct USBPROXYURBLNX *pSplitHead;
112 /** The next linux URB if split up. */
113 struct USBPROXYURBLNX *pSplitNext;
114 /** Whether it has timed out and should be shot down on the next failing reap call. */
115 bool fTimedOut;
116 /** Indicates that this URB has been canceled by timeout and should return an CRC error. */
117 bool fCanceledByTimedOut;
118 /** Don't report these back. */
119 bool fCanceledBySubmit;
120 /** This split element is reaped. */
121 bool fSplitElementReaped;
122} USBPROXYURBLNX, *PUSBPROXYURBLNX;
123
124/**
125 * Data for the linux usb proxy backend.
126 */
127typedef struct USBPROXYDEVLNX
128{
129 /** The open file. */
130 RTFILE File;
131 /** Critical section protecting the two lists. */
132 RTCRITSECT CritSect;
133 /** The list of free linux URBs. Singly linked. */
134 PUSBPROXYURBLNX pFreeHead;
135 /** The list of active linux URBs. Doubly linked.
136 * We must maintain this so we can properly reap URBs of a detached device.
137 * Only the split head will appear in this list. */
138 PUSBPROXYURBLNX pInFlightHead;
139 /** The list of landed linux URBs. Doubly linked.
140 * Only the split head will appear in this list. */
141 PUSBPROXYURBLNX pTaxingHead;
142 /** The tail of the landed linux URBs. */
143 PUSBPROXYURBLNX pTaxingTail;
144} USBPROXYDEVLNX, *PUSBPROXYDEVLNX;
145
146
147/*******************************************************************************
148* Internal Functions *
149*******************************************************************************/
150static int usbProxyLinuxDoIoCtl(PUSBPROXYDEV pProxyDev, int iCmd, void *pvArg, bool fHandleNoDev, uint32_t cTries);
151static void usbProxLinuxUrbUnplugged(PUSBPROXYDEV pProxyDev);
152static void usbProxyLinuxSetConnected(PUSBPROXYDEV pProyxDev, int iIf, bool fConnect, bool fQuiet);
153static PUSBPROXYURBLNX usbProxyLinuxUrbAlloc(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pSplitHead);
154static void usbProxyLinuxUrbFree(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pUrbLnx);
155static void usbProxyLinuxUrbFreeSplitList(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pUrbLnx);
156static int usbProxyLinuxFindActiveConfig(PUSBPROXYDEV pProxyDev, const char *pszAddress, int *iFirstCfg);
157
158
159
160/**
161 * Wrapper for the ioctl call.
162 *
163 * This wrapper will repeate the call if we get an EINTR or EAGAIN. It can also
164 * handle ENODEV (detached device) errors.
165 *
166 * @returns whatever ioctl returns.
167 * @param pProxyDev The proxy device.
168 * @param iCmd The ioctl command / function.
169 * @param pvArg The ioctl argument / data.
170 * @param fHandleNoDev Whether to handle ENODEV.
171 * @param cTries The number of retries. Use UINT32_MAX for (kind of) indefinite retries.
172 * @internal
173 */
174static int usbProxyLinuxDoIoCtl(PUSBPROXYDEV pProxyDev, int iCmd, void *pvArg, bool fHandleNoDev, uint32_t cTries)
175{
176 int rc;
177 PUSBPROXYDEVLNX pDevLnx = (PUSBPROXYDEVLNX)pProxyDev->Backend.pv;
178 do
179 {
180 do
181 {
182 rc = ioctl(pDevLnx->File, iCmd, pvArg);
183 if (rc >= 0)
184 return rc;
185 } while (errno == EINTR);
186
187 if (errno == ENODEV && fHandleNoDev)
188 {
189 usbProxLinuxUrbUnplugged(pProxyDev);
190 Log(("usb-linux: ENODEV -> unplugged. pProxyDev=%p[%s]\n", pProxyDev, pProxyDev->Dev.pszName));
191 errno = ENODEV;
192 break;
193 }
194 if (errno != EAGAIN)
195 break;
196 } while (cTries-- > 0);
197
198 return rc;
199}
200
201
202/**
203 * The device has been unplugged.
204 * Cancel all in-flight URBs and put them up for reaping.
205 */
206static void usbProxLinuxUrbUnplugged(PUSBPROXYDEV pProxyDev)
207{
208 PUSBPROXYDEVLNX pDevLnx = (PUSBPROXYDEVLNX)pProxyDev->Backend.pv;
209
210 /*
211 * Shoot down all flying URBs.
212 */
213 RTCritSectEnter(&pDevLnx->CritSect);
214
215 PUSBPROXYURBLNX pUrbTaxing = NULL;
216 PUSBPROXYURBLNX pUrbLnx = pDevLnx->pInFlightHead;
217 pDevLnx->pInFlightHead = NULL;
218 while (pUrbLnx)
219 {
220 PUSBPROXYURBLNX pCur = pUrbLnx;
221 pUrbLnx = pUrbLnx->pNext;
222
223 ioctl(pDevLnx->File, USBDEVFS_DISCARDURB, &pCur->KUrb); /* not sure if this is required.. */
224 if (!pCur->KUrb.status)
225 pCur->KUrb.status = -ENODEV;
226
227 /* insert into the taxing list. */
228 pCur->pPrev = NULL;
229 if ( !pCur->pSplitHead
230 || pCur == pCur->pSplitHead)
231 {
232 pCur->pNext = pUrbTaxing;
233 if (pUrbTaxing)
234 pUrbTaxing->pPrev = pCur;
235 pUrbTaxing = pCur;
236 }
237 else
238 pCur->pNext = NULL;
239 }
240
241 /* Append the URBs we shot down to the taxing queue. */
242 if (pUrbTaxing)
243 {
244 pUrbTaxing->pPrev = pDevLnx->pTaxingTail;
245 if (pUrbTaxing->pPrev)
246 pUrbTaxing->pPrev->pNext = pUrbTaxing;
247 else
248 pDevLnx->pTaxingTail = pDevLnx->pTaxingHead = pUrbTaxing;
249 }
250
251 RTCritSectLeave(&pDevLnx->CritSect);
252
253 vusbDevUnplugged(&pProxyDev->Dev);
254}
255
256
257/**
258 * Set the connect state seen by kernel drivers
259 * @internal
260 */
261static void usbProxyLinuxSetConnected(PUSBPROXYDEV pProxyDev, int iIf, bool fConnect, bool fQuiet)
262{
263 struct usbdevfs_ioctl IoCtl;
264 if (!fQuiet)
265 LogFlow(("usbProxyLinuxSetConnected: pProxyDev=%p[%s] iIf=%#x fConnect=%d\n",
266 pProxyDev, pProxyDev->Dev.pszName, iIf, fConnect));
267
268 IoCtl.ifno = iIf;
269 IoCtl.ioctl_code = fConnect ? USBDEVFS_CONNECT : USBDEVFS_DISCONNECT;
270 IoCtl.data = NULL;
271 if ( usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_IOCTL, &IoCtl, true, UINT32_MAX)
272 && !fQuiet)
273 Log(("usbProxyLinuxSetConnected: failure, errno=%d. pProxyDev=%p[%s]\n",
274 errno, pProxyDev, pProxyDev->Dev.pszName));
275}
276
277
278/**
279 * Allocates a linux URB request structure.
280 * @returns Pointer to an active URB request.
281 * @returns NULL on failure.
282 * @param pProxyDev The proxy device instance.
283 * @param pSplitHead The split list head if allocating for a split list.
284 */
285static PUSBPROXYURBLNX usbProxyLinuxUrbAlloc(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pSplitHead)
286{
287 PUSBPROXYDEVLNX pDevLnx = (PUSBPROXYDEVLNX)pProxyDev->Backend.pv;
288 PUSBPROXYURBLNX pUrbLnx;
289
290 RTCritSectEnter(&pDevLnx->CritSect);
291
292 /*
293 * Try remove a linux URB from the free list, if none there allocate a new one.
294 */
295 pUrbLnx = pDevLnx->pFreeHead;
296 if (pUrbLnx)
297 pDevLnx->pFreeHead = pUrbLnx->pNext;
298 else
299 {
300 RTCritSectLeave(&pDevLnx->CritSect);
301 pUrbLnx = (PUSBPROXYURBLNX)RTMemAlloc(sizeof(*pUrbLnx));
302 if (!pUrbLnx)
303 return NULL;
304 RTCritSectEnter(&pDevLnx->CritSect);
305 }
306 pUrbLnx->pSplitHead = pSplitHead;
307 pUrbLnx->pSplitNext = NULL;
308 pUrbLnx->fTimedOut = false;
309 pUrbLnx->fCanceledByTimedOut = false;
310 pUrbLnx->fCanceledBySubmit = false;
311 pUrbLnx->fSplitElementReaped = false;
312
313 /*
314 * Link it into the active list
315 */
316 if (!pSplitHead)
317 {
318 pUrbLnx->pPrev = NULL;
319 pUrbLnx->pNext = pDevLnx->pInFlightHead;
320 if (pUrbLnx->pNext)
321 pUrbLnx->pNext->pPrev = pUrbLnx;
322 pDevLnx->pInFlightHead = pUrbLnx;
323 }
324 else
325 pUrbLnx->pPrev = pUrbLnx->pNext = (PUSBPROXYURBLNX)0xdead;
326
327 RTCritSectLeave(&pDevLnx->CritSect);
328 return pUrbLnx;
329}
330
331
332/**
333 * Frees a linux URB request structure.
334 *
335 * @param pProxyDev The proxy device instance.
336 * @param pUrbLnx The linux URB to free.
337 */
338static void usbProxyLinuxUrbFree(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pUrbLnx)
339{
340 PUSBPROXYDEVLNX pDevLnx = (PUSBPROXYDEVLNX)pProxyDev->Backend.pv;
341
342 RTCritSectEnter(&pDevLnx->CritSect);
343
344 /*
345 * Remove from the active list.
346 */
347 if ( !pUrbLnx->pSplitHead
348 || pUrbLnx->pSplitHead == pUrbLnx)
349 {
350 if (pUrbLnx->pNext)
351 pUrbLnx->pNext->pPrev = pUrbLnx->pPrev;
352 if (pUrbLnx->pPrev)
353 pUrbLnx->pPrev->pNext = pUrbLnx->pNext;
354 else
355 pDevLnx->pInFlightHead = pUrbLnx->pNext;
356 }
357 pUrbLnx->pSplitHead = pUrbLnx->pSplitNext = NULL;
358
359 /*
360 * Link it into the free list.
361 */
362 pUrbLnx->pPrev = NULL;
363 pUrbLnx->pNext = pDevLnx->pFreeHead;
364 pDevLnx->pFreeHead = pUrbLnx;
365
366 RTCritSectLeave(&pDevLnx->CritSect);
367}
368
369
370/**
371 * Frees split list of a linux URB request structure.
372 *
373 * @param pProxyDev The proxy device instance.
374 * @param pUrbLnx A linux URB to in the split list to be freed.
375 */
376static void usbProxyLinuxUrbFreeSplitList(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pUrbLnx)
377{
378 PUSBPROXYDEVLNX pDevLnx = (PUSBPROXYDEVLNX)pProxyDev->Backend.pv;
379
380 RTCritSectEnter(&pDevLnx->CritSect);
381
382 pUrbLnx = pUrbLnx->pSplitHead;
383 Assert(pUrbLnx);
384 while (pUrbLnx)
385 {
386 PUSBPROXYURBLNX pFree = pUrbLnx;
387 pUrbLnx = pUrbLnx->pSplitNext;
388 Assert(pFree->pSplitHead);
389 usbProxyLinuxUrbFree(pProxyDev, pFree);
390 }
391
392 RTCritSectLeave(&pDevLnx->CritSect);
393}
394
395
396/**
397 * This finds the device in the /proc/bus/usb/bus/addr file and finds
398 * the config with an asterix.
399 *
400 * @returns The Cfg#.
401 * @returns -1 if no active config.
402 * @param pszAddress The path to the device. We infere the location of the
403 * devices file, which bus and device number we're looking for.
404 * @param iFirstCfg The first configuration. (optional)
405 * @internal
406 */
407static int usbProxyLinuxFindActiveConfig(PUSBPROXYDEV pProxyDev, const char *pszAddress, int *piFirstCfg)
408{
409 /*
410 * Set return defaults.
411 */
412 int iActiveCfg = -1;
413 if (piFirstCfg)
414 *piFirstCfg = 1;
415
416 /*
417 * Interpret the address and turn it into a path to the devices file.
418 */
419 size_t cchAddress = strlen(pszAddress);
420 char *pszDevices = (char *)RTMemDupEx(pszAddress, cchAddress, sizeof("devices"));
421 AssertReturn(pszDevices, iActiveCfg);
422
423 /* the device number */
424 char *psz = pszDevices + cchAddress;
425 while (*psz != '/')
426 psz--;
427 Assert(pszDevices < psz);
428 uint32_t uDev;
429 int rc = RTStrToUInt32Ex(psz + 1, NULL, 10, &uDev);
430 if (RT_SUCCESS(rc))
431 {
432 /* the bus number */
433 *psz-- = '\0';
434 while (*psz != '/')
435 psz--;
436 Assert(pszDevices < psz);
437 uint32_t uBus;
438 rc = RTStrToUInt32Ex(psz + 1, NULL, 10, &uBus);
439 if (RT_SUCCESS(rc))
440 {
441 strcpy(psz + 1, "devices");
442
443 /*
444 * Open and scan the devices file.
445 * We're ASSUMING that each device starts off with a 'T:' line.
446 */
447 PRTSTREAM pFile;
448 rc = RTStrmOpen(pszDevices, "r", &pFile);
449 if (RT_SUCCESS(rc))
450 {
451 char szLine[1024];
452 while (RT_SUCCESS(RTStrmGetLine(pFile, szLine, sizeof(szLine))))
453 {
454 /* we're only interested in 'T:' lines. */
455 psz = RTStrStripL(szLine);
456 if (psz[0] != 'T' || psz[1] != ':')
457 continue;
458
459 /* Skip ahead to 'Bus' and compare */
460 psz = RTStrStripL(psz + 2); Assert(!strncmp(psz, "Bus=", 4));
461 psz = RTStrStripL(psz + 4);
462 char *pszNext;
463 uint32_t u;
464 rc = RTStrToUInt32Ex(psz, &pszNext, 10, &u); AssertRC(rc);
465 if (RT_FAILURE(rc))
466 continue;
467 if (u != uBus)
468 continue;
469
470 /* Skip ahead to 'Dev#' and compare */
471 psz = strstr(psz, "Dev#="); Assert(psz);
472 if (!psz)
473 continue;
474 psz = RTStrStripL(psz + 5);
475 rc = RTStrToUInt32Ex(psz, &pszNext, 10, &u); AssertRC(rc);
476 if (RT_FAILURE(rc))
477 continue;
478 if (u != uDev)
479 continue;
480
481 /*
482 * Ok, we've found the device.
483 * Scan until we find a selected configuration, the next device, or EOF.
484 */
485 while (RT_SUCCESS(RTStrmGetLine(pFile, szLine, sizeof(szLine))))
486 {
487 psz = RTStrStripL(szLine);
488 if (psz[0] == 'T')
489 break;
490 if (psz[0] != 'C' || psz[1] != ':')
491 continue;
492 const bool fActive = psz[2] == '*';
493 if (!fActive && !piFirstCfg)
494 continue;
495
496 /* Get the 'Cfg#' value. */
497 psz = strstr(psz, "Cfg#="); Assert(psz);
498 if (psz)
499 {
500 psz = RTStrStripL(psz + 5);
501 rc = RTStrToUInt32Ex(psz, &pszNext, 10, &u); AssertRC(rc);
502 if (RT_SUCCESS(rc))
503 {
504 if (piFirstCfg)
505 {
506 *piFirstCfg = u;
507 piFirstCfg = NULL;
508 }
509 if (fActive)
510 iActiveCfg = u;
511 }
512 }
513 if (fActive)
514 break;
515 }
516 break;
517 }
518 RTStrmClose(pFile);
519 }
520 }
521 }
522 RTMemFree(pszDevices);
523
524 return iActiveCfg;
525}
526
527int dev2fd (PUSBPROXYDEV pProxyDev)
528{
529 if (pProxyDev)
530 {
531 PUSBPROXYDEVLNX pDevLnx = (PUSBPROXYDEVLNX)pProxyDev->Backend.pv;
532
533 if (pDevLnx)
534 {
535 return pDevLnx->File;
536 }
537 }
538 return -1;
539}
540
541/**
542 * Opens the /proc/bus/usb/bus/addr file.
543 *
544 * @returns VBox status code.
545 * @param pProxyDev The device instance.
546 * @param pszAddress The path to the device.
547 * @param pvBackend Backend specific pointer, unused for the linux backend.
548 */
549static int usbProxyLinuxOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress, void *pvBackend)
550{
551 LogFlow(("usbProxyLinuxOpen: pProxyDev=%p pszAddress=%s\n", pProxyDev, pszAddress));
552 RTFILE File;
553 int rc = RTFileOpen(&File, pszAddress, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
554 if (RT_SUCCESS(rc))
555 {
556 /*
557 * Allocate and initialize the linux backend data.
558 */
559 PUSBPROXYDEVLNX pDevLnx = (PUSBPROXYDEVLNX)RTMemAllocZ(sizeof(*pDevLnx));
560 if (pDevLnx)
561 {
562 pDevLnx->File = File;
563 rc = RTCritSectInit(&pDevLnx->CritSect);
564 if (RT_SUCCESS(rc))
565 {
566 pProxyDev->Backend.pv = pDevLnx;
567
568 /* brute force rulez */
569 unsigned iIf;
570 for (iIf = 0; iIf < 256; iIf++)
571 usbProxyLinuxSetConnected(pProxyDev, iIf, false, true);
572
573 /*
574 * Determin the active configuration.
575 *
576 * If there isn't any active configuration, we will get EHOSTUNREACH (113) errors
577 * when trying to read the device descriptors in usbProxyDevCreate. So, we'll make
578 * the first one active (usually 1) then.
579 */
580 pProxyDev->cIgnoreSetConfigs = 1;
581 int iFirstCfg;
582 pProxyDev->iActiveCfg = usbProxyLinuxFindActiveConfig(pProxyDev, pszAddress, &iFirstCfg);
583 if (pProxyDev->iActiveCfg == -1)
584 {
585 usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_SETCONFIGURATION, &iFirstCfg, false, UINT32_MAX);
586 pProxyDev->iActiveCfg = usbProxyLinuxFindActiveConfig(pProxyDev, pszAddress, NULL);
587 Log(("usbProxyLinuxOpen: No active config! Tried to set %d: iActiveCfg=%d\n", iFirstCfg, pProxyDev->iActiveCfg));
588 }
589
590 LogFlow(("usbProxyLinuxOpen(%p, %s): returns successfully File=%d iActiveCfg=%d\n",
591 pProxyDev, pszAddress, pDevLnx->File, pProxyDev->iActiveCfg));
592
593 return VINF_SUCCESS;
594 }
595
596 RTMemFree(pDevLnx);
597 }
598 else
599 rc = VERR_NO_MEMORY;
600 RTFileClose(File);
601 }
602 else if (rc == VERR_ACCESS_DENIED)
603 rc = VERR_VUSB_USBFS_PERMISSION;
604
605 Log(("usbProxyLinuxOpen(%p, %s) failed, rc=%d!\n", pProxyDev, pszAddress, rc));
606 pProxyDev->Backend.pv = NULL;
607
608 NOREF(pvBackend);
609 return rc;
610}
611
612
613/**
614 * Closes the proxy device.
615 */
616static void usbProxyLinuxClose(PUSBPROXYDEV pProxyDev)
617{
618 LogFlow(("usbProxyLinuxClose: pProxyDev=%p[%s]\n", pProxyDev, pProxyDev->Dev.pszName));
619 PUSBPROXYDEVLNX pDevLnx = (PUSBPROXYDEVLNX)pProxyDev->Backend.pv;
620 Assert(pDevLnx);
621 if (!pDevLnx)
622 return;
623
624 /*
625 * Try put the device in a state which linux can cope with before we release it.
626 * Resetting it would be a nice start, although we must remember
627 * that it might have been disconnected...
628 */
629 /* ASSUMES: thread == EMT */
630 if (!usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_RESET, NULL, false, 10))
631 {
632 /* Connect drivers. */
633 unsigned iIf;
634 for (iIf = 0; iIf < 256; iIf++)
635 usbProxyLinuxSetConnected(pProxyDev, iIf, true, true);
636 }
637 else
638 Log(("usbProxyLinuxClose: Reset failed, errno=%d.\n", errno));
639
640 /*
641 * Now we can close it and free all the resources.
642 */
643 RTFileClose(pDevLnx->File);
644 pDevLnx->File = NIL_RTFILE;
645
646 RTCritSectDelete(&pDevLnx->CritSect);
647
648 PUSBPROXYURBLNX pUrbLnx;
649 while ((pUrbLnx = pDevLnx->pInFlightHead) != NULL)
650 {
651 pDevLnx->pInFlightHead = pUrbLnx->pNext;
652 if ( usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pUrbLnx->KUrb, false, UINT32_MAX)
653 && errno != ENODEV
654 && errno != ENOENT)
655 AssertMsgFailed(("errno=%d\n", errno));
656 if (pUrbLnx->pSplitHead)
657 {
658 PUSBPROXYURBLNX pCur = pUrbLnx->pSplitNext;
659 while (pCur)
660 {
661 PUSBPROXYURBLNX pFree = pCur;
662 pCur = pFree->pSplitNext;
663 if ( !pFree->fSplitElementReaped
664 && usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pFree->KUrb, false, UINT32_MAX)
665 && errno != ENODEV
666 && errno != ENOENT)
667 AssertMsgFailed(("errno=%d\n", errno));
668 RTMemFree(pFree);
669 }
670 }
671 else
672 Assert(!pUrbLnx->pSplitNext);
673 RTMemFree(pUrbLnx);
674 }
675
676 while ((pUrbLnx = pDevLnx->pFreeHead) != NULL)
677 {
678 pDevLnx->pFreeHead = pUrbLnx->pNext;
679 RTMemFree(pUrbLnx);
680 }
681
682 RTMemFree(pDevLnx);
683 pProxyDev->Backend.pv = NULL;
684 LogFlow(("usbProxyLinuxClose: returns\n"));
685}
686
687
688#if defined(NO_PORT_RESET) && !defined(NO_LOGICAL_RECONNECT)
689/**
690 * Look for the logically reconnected device.
691 * After 5 seconds we'll give up.
692 *
693 * @returns VBox status code.
694 * @thread Reset thread or EMT.
695 */
696static int usb_reset_logical_reconnect(PUSBPROXYDEV pDev)
697{
698 FILE * pFile;
699 uint64_t u64StartTS = RTTimeMilliTS();
700
701 Log2(("usb_reset_logical_reconnect: pDev=%p:{.bBus=%#x, .bDevNum=%#x, .idVendor=%#x, .idProduct=%#x, .bcdDevice=%#x, .u64SerialHash=%#llx .bDevNumParent=%#x .bPort=%#x .bLevel=%#x}\n",
702 pDev, pDev->Info.bBus, pDev->Info.bDevNum, pDev->Info.idVendor, pDev->Info.idProduct, pDev->Info.bcdDevice,
703 pDev->Info.u64SerialHash, pDev->Info.bDevNumParent, pDev->Info.bPort, pDev->Info.bLevel));
704
705 /* First, let hubd get a chance to logically reconnect the device. */
706 if (!RTThreadYield())
707 RTThreadSleep(1);
708
709 /*
710 * Search for the new device address.
711 */
712 pFile = get_devices_file();
713 if (!pFile)
714 return VERR_FILE_NOT_FOUND;
715
716 /*
717 * Loop until found or 5seconds have elapsed.
718 */
719 for (;;) {
720 struct pollfd pfd;
721 uint8_t tmp;
722 int rc;
723 char buf[512];
724 uint64_t u64Elapsed;
725 int got = 0;
726 struct usb_dev_entry id = {0};
727
728 /*
729 * Since this is kernel ABI we don't need to be too fussy about
730 * the parsing.
731 */
732 while (fgets(buf, sizeof(buf), pFile)) {
733 char *psz = strchr(buf, '\n');
734 if ( psz == NULL ) {
735 AssertMsgFailed(("usb_reset_logical_reconnect: Line to long!!\n"));
736 break;
737 }
738 *psz = '\0';
739
740 switch ( buf[0] ) {
741 case 'T': /* topology */
742 /* Check if we've got enough for a device. */
743 if (got >= 2) {
744 Log2(("usb_reset_logical_reconnect: {.bBus=%#x, .bDevNum=%#x, .idVendor=%#x, .idProduct=%#x, .bcdDevice=%#x, .u64SerialHash=%#llx, .bDevNumParent=%#x, .bPort=%#x, .bLevel=%#x}\n",
745 id.bBus, id.bDevNum, id.idVendor, id.idProduct, id.bcdDevice, id.u64SerialHash, id.bDevNumParent, id.bPort, id.bLevel));
746 if ( id.bDevNumParent == pDev->Info.bDevNumParent
747 && id.idVendor == pDev->Info.idVendor
748 && id.idProduct == pDev->Info.idProduct
749 && id.bcdDevice == pDev->Info.bcdDevice
750 && id.u64SerialHash == pDev->Info.u64SerialHash
751 && id.bBus == pDev->Info.bBus
752 && id.bPort == pDev->Info.bPort
753 && id.bLevel == pDev->Info.bLevel) {
754 goto l_found;
755 }
756 }
757
758 /* restart */
759 got = 0;
760 memset(&id, 0, sizeof(id));
761
762 /*T: Bus=04 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=1.5 MxCh= 0*/
763 Log2(("usb_reset_logical_reconnect: %s\n", buf));
764 buf[10] = '\0';
765 if ( !get_u8(buf + 8, &id.bBus) )
766 break;
767 buf[49] = '\0';
768 psz = buf + 46;
769 while ( *psz == ' ' )
770 psz++;
771 if ( !get_u8(psz, &id.bDevNum) )
772 break;
773
774 buf[17] = '\0';
775 if ( !get_u8(buf + 15, &id.bLevel) )
776 break;
777 buf[25] = '\0';
778 if ( !get_u8(buf + 23, &id.bDevNumParent) )
779 break;
780 buf[33] = '\0';
781 if ( !get_u8(buf + 31, &id.bPort) )
782 break;
783 got++;
784 break;
785
786 case 'P': /* product */
787 Log2(("usb_reset_logical_reconnect: %s\n", buf));
788 buf[15] = '\0';
789 if ( !get_x16(buf + 11, &id.idVendor) )
790 break;
791 buf[27] = '\0';
792 if ( !get_x16(buf + 23, &id.idProduct) )
793 break;
794 buf[34] = '\0';
795 if ( buf[32] == ' ' )
796 buf[32] = '0';
797 id.bcdDevice = 0;
798 if ( !get_x8(buf + 32, &tmp) )
799 break;
800 id.bcdDevice = tmp << 8;
801 if ( !get_x8(buf + 35, &tmp) )
802 break;
803 id.bcdDevice |= tmp;
804 got++;
805 break;
806
807 case 'S': /* String descriptor */
808 /* Skip past "S:" and then the whitespace */
809 for(psz = buf + 2; *psz != '\0'; psz++)
810 if ( !isspace(*psz) )
811 break;
812
813 /* If it is a serial number string, skip past
814 * "SerialNumber="
815 */
816 if ( strncmp(psz, "SerialNumber=", sizeof("SerialNumber=") - 1) )
817 break;
818
819 Log2(("usb_reset_logical_reconnect: %s\n", buf));
820 psz += sizeof("SerialNumber=") - 1;
821
822 usb_serial_hash(psz, &id.u64SerialHash);
823 break;
824 }
825 }
826
827 /*
828 * Check last.
829 */
830 if ( got >= 2
831 && id.bDevNumParent == pDev->Info.bDevNumParent
832 && id.idVendor == pDev->Info.idVendor
833 && id.idProduct == pDev->Info.idProduct
834 && id.bcdDevice == pDev->Info.bcdDevice
835 && id.u64SerialHash == pDev->Info.u64SerialHash
836 && id.bBus == pDev->Info.bBus
837 && id.bPort == pDev->Info.bPort
838 && id.bLevel == pDev->Info.bLevel) {
839 l_found:
840 /* close the existing file descriptor. */
841 RTFileClose(pDevLnx->File);
842 pDevLnx->File = NIL_RTFILE;
843
844 /* open stuff at the new address. */
845 pDev->Info = id;
846 if (usbProxyLinuxOpen(pDev, &id))
847 return VINF_SUCCESS;
848 break;
849 }
850
851 /*
852 * Wait for a while and then check the file again.
853 */
854 u64Elapsed = RTTimeMilliTS() - u64StartTS;
855 if (u64Elapsed >= 5000/*ms*/)
856 break; /* done */
857
858 pfd.fd = fileno(pFile);
859 pfd.events = POLLIN;
860 rc = poll(&pfd, 1, 5000 - u64Elapsed);
861 if (rc < 0) {
862 AssertMsg(errno == EINTR, ("errno=%d\n", errno));
863 RTThreadSleep(32); /* paranoia: don't eat cpu on failure */
864 }
865
866 rewind(pFile);
867 } /* for loop */
868
869 return VERR_GENERAL_FAILURE;
870}
871#endif /* !NO_PORT_RESET && !NO_LOGICAL_RECONNECT */
872
873
874/**
875 * Reset a device.
876 *
877 * @returns VBox status code.
878 * @param pDev The device to reset.
879 */
880static int usbProxyLinuxReset(PUSBPROXYDEV pProxyDev)
881{
882#ifdef NO_PORT_RESET
883 LogFlow(("usbProxyLinuxReset: pProxyDev=%p[%s] - NO_PORT_RESET\n", pProxyDev, pProxyDev->Dev.pszName));
884 pProxyDev->cIgnoreSetConfigs = 2;
885
886#else /* !NO_PORT_RESET */
887 LogFlow(("usbProxyLinuxReset: pProxyDev=%p[%s]\n", pProxyDev, pProxyDev->Dev.pszName));
888# ifndef NO_LOGICAL_RECONNECT
889 ASMAtomicIncU32(&g_cResetActive);
890# endif
891
892 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_RESET, NULL, false, 10))
893 {
894 int rc = errno;
895# ifndef NO_LOGICAL_RECONNECT
896 if (rc == ENODEV)
897 {
898 /*
899 * This usually happens because of a 'logical disconnection'.
900 * So, we're in for a real treat from our excellent OS now...
901 */
902 rc2 = usb_reset_logical_reconnect(pProxyDev);
903 if (RT_FAILURE(rc2))
904 usbProxLinuxUrbUnplugged(pProxyDev);
905 if (RT_SUCCESS(rc2))
906 {
907 ASMAtomicDecU32(&g_cResetActive);
908 LogFlow(("usbProxyLinuxReset: returns success (after recovering disconnected device!)\n"));
909 return VINF_SUCCESS;
910 }
911 }
912 ASMAtomicDecU32(&g_cResetActive);
913# endif /* NO_LOGICAL_RECONNECT */
914
915 Log(("usb-linux: Reset failed, rc=%Rrc errno=%d.\n", RTErrConvertFromErrno(rc), rc));
916 pProxyDev->iActiveCfg = -1;
917 return RTErrConvertFromErrno(rc);
918 }
919
920# ifndef NO_LOGICAL_RECONNECT
921 ASMAtomicDecU32(&g_cResetActive);
922# endif
923
924 pProxyDev->cIgnoreSetConfigs = 2;
925 LogFlow(("usbProxyLinuxReset: returns success\n"));
926#endif /* !NO_PORT_RESET */
927 return VINF_SUCCESS;
928}
929
930
931/**
932 * SET_CONFIGURATION.
933 *
934 * The caller makes sure that it's not called first time after open or reset
935 * with the active interface.
936 *
937 * @returns success indicator.
938 * @param pProxyDev The device instance data.
939 * @param iCfg The configuration to set.
940 */
941static int usbProxyLinuxSetConfig(PUSBPROXYDEV pProxyDev, int iCfg)
942{
943 LogFlow(("usbProxyLinuxSetConfig: pProxyDev=%p[%s] cfg=%#x\n",
944 pProxyDev, pProxyDev->Dev.pszName, iCfg));
945
946 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_SETCONFIGURATION, &iCfg, true, UINT32_MAX))
947 {
948 Log(("usb-linux: Set configuration. errno=%d\n", errno));
949 return false;
950 }
951 return true;
952}
953
954
955/**
956 * Claims an interface.
957 * @returns success indicator.
958 */
959static int usbProxyLinuxClaimInterface(PUSBPROXYDEV pProxyDev, int iIf)
960{
961 LogFlow(("usbProxyLinuxClaimInterface: pProxyDev=%p[%s] ifnum=%#x\n", pProxyDev, pProxyDev->Dev.pszName, iIf));
962 usbProxyLinuxSetConnected(pProxyDev, iIf, false, false);
963
964 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_CLAIMINTERFACE, &iIf, true, UINT32_MAX))
965 {
966 Log(("usb-linux: Claim interface. errno=%d pProxyDev=%p[%s]\n", errno, pProxyDev, pProxyDev->Dev.pszName));
967 return false;
968 }
969 return true;
970}
971
972
973/**
974 * Releases an interface.
975 * @returns success indicator.
976 */
977static int usbProxyLinuxReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf)
978{
979 LogFlow(("usbProxyLinuxReleaseInterface: pProxyDev=%p[%s] ifnum=%#x\n", pProxyDev, pProxyDev->Dev.pszName, iIf));
980
981 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_RELEASEINTERFACE, &iIf, true, UINT32_MAX))
982 {
983 Log(("usb-linux: Release interface, errno=%d. pProxyDev=%p[%s]\n", errno, pProxyDev, pProxyDev->Dev.pszName));
984 return false;
985 }
986 return true;
987}
988
989
990/**
991 * SET_INTERFACE.
992 *
993 * @returns success indicator.
994 */
995static int usbProxyLinuxSetInterface(PUSBPROXYDEV pProxyDev, int iIf, int iAlt)
996{
997 struct usbdevfs_setinterface SetIf;
998 LogFlow(("usbProxyLinuxSetInterface: pProxyDev=%p iIf=%#x iAlt=%#x\n", pProxyDev, iIf, iAlt));
999
1000 SetIf.interface = iIf;
1001 SetIf.altsetting = iAlt;
1002 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_SETINTERFACE, &SetIf, true, UINT32_MAX))
1003 {
1004 Log(("usb-linux: Set interface, errno=%d. pProxyDev=%p[%s]\n", errno, pProxyDev, pProxyDev->Dev.pszName));
1005 return false;
1006 }
1007 return true;
1008}
1009
1010
1011/**
1012 * Clears the halted endpoint 'EndPt'.
1013 */
1014static bool usbProxyLinuxClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int EndPt)
1015{
1016 LogFlow(("usbProxyLinuxClearHaltedEp: pProxyDev=%p[%s] EndPt=%u\n", pProxyDev, pProxyDev->Dev.pszName, EndPt));
1017
1018 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_CLEAR_HALT, &EndPt, true, UINT32_MAX))
1019 {
1020 /*
1021 * Unfortunately this doesn't work on control pipes.
1022 * Windows doing this on the default endpoint and possibly other pipes too,
1023 * so we'll feign success for ENOENT errors.
1024 */
1025 if (errno == ENOENT)
1026 {
1027 Log(("usb-linux: clear_halted_ep failed errno=%d. pProxyDev=%p[%s] ep=%d - IGNORED\n",
1028 errno, pProxyDev, pProxyDev->Dev.pszName, EndPt));
1029 return true;
1030 }
1031 Log(("usb-linux: clear_halted_ep failed errno=%d. pProxyDev=%p[%s] ep=%d\n",
1032 errno, pProxyDev, pProxyDev->Dev.pszName, EndPt));
1033 return false;
1034 }
1035 return true;
1036}
1037
1038
1039/**
1040 * Setup packet byte-swapping routines.
1041 */
1042static void usbProxyLinuxUrbSwapSetup(PVUSBSETUP pSetup)
1043{
1044 pSetup->wValue = RT_LE2H_U16(pSetup->wValue);
1045 pSetup->wIndex = RT_LE2H_U16(pSetup->wIndex);
1046 pSetup->wLength = RT_LE2H_U16(pSetup->wLength);
1047}
1048
1049
1050/** The split size. */
1051#define SPLIT_SIZE 0x2000
1052
1053/**
1054 * Try split up a VUSB URB into smaller URBs which the
1055 * linux kernel can deal with.
1056 *
1057 * @returns true / false.
1058 * @param pProxyDev The proxy device.
1059 * @param pUrbLnx The linux URB which was rejected because of being too big.
1060 * @param pUrb The VUSB URB.
1061 */
1062static int usbProxyLinuxUrbQueueSplit(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pUrbLnx, PVUSBURB pUrb)
1063{
1064 unsigned cTries;
1065 PUSBPROXYDEVLNX pDevLnx = (PUSBPROXYDEVLNX)pProxyDev->Backend.pv;
1066
1067 /*
1068 * Split it up into SPLIT_SIZE sized blocks.
1069 */
1070 const unsigned cKUrbs = (pUrb->cbData + SPLIT_SIZE - 1) / SPLIT_SIZE;
1071 LogFlow(("usbProxyLinuxUrbQueueSplit: pUrb=%p cKUrbs=%d cbData=%d\n", pUrb, cKUrbs, pUrb->cbData));
1072
1073 uint32_t cbLeft = pUrb->cbData;
1074 uint8_t *pb = &pUrb->abData[0];
1075
1076 /* the first one (already allocated) */
1077 switch (pUrb->enmType)
1078 {
1079 default: /* shut up gcc */
1080 case VUSBXFERTYPE_BULK: pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_BULK; break;
1081 case VUSBXFERTYPE_INTR: pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_INTERRUPT; break;
1082 case VUSBXFERTYPE_ISOC: pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_ISO; break;
1083 case VUSBXFERTYPE_MSG: pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_CONTROL; break;
1084 }
1085 pUrbLnx->KUrb.endpoint = pUrb->EndPt;
1086 if (pUrb->enmDir == VUSBDIRECTION_IN)
1087 pUrbLnx->KUrb.endpoint |= 0x80;
1088 pUrbLnx->KUrb.status = 0;
1089 pUrbLnx->KUrb.flags = pUrb->fShortNotOk ? USBDEVFS_URB_SHORT_NOT_OK : 0; /* ISO_ASAP too? */
1090 pUrbLnx->KUrb.buffer = pb;
1091 pUrbLnx->KUrb.buffer_length = MIN(cbLeft, SPLIT_SIZE);
1092 pUrbLnx->KUrb.actual_length = 0;
1093 pUrbLnx->KUrb.start_frame = 0;
1094 pUrbLnx->KUrb.number_of_packets = 0;
1095 pUrbLnx->KUrb.error_count = 0;
1096 pUrbLnx->KUrb.signr = 0;
1097 pUrbLnx->KUrb.usercontext = pUrb;
1098 pUrbLnx->pSplitHead = pUrbLnx;
1099 pUrbLnx->pSplitNext = NULL;
1100
1101 pb += pUrbLnx->KUrb.buffer_length;
1102 cbLeft -= pUrbLnx->KUrb.buffer_length;
1103
1104 /* the rest. */
1105 unsigned i;
1106 PUSBPROXYURBLNX pCur = pUrbLnx;
1107 for (i = 1; i < cKUrbs; i++)
1108 {
1109 Assert(cbLeft != 0);
1110 pCur = pCur->pSplitNext = usbProxyLinuxUrbAlloc(pProxyDev, pUrbLnx);
1111 if (!pCur)
1112 {
1113 usbProxyLinuxUrbFreeSplitList(pProxyDev, pUrbLnx);
1114 return false;
1115 }
1116 Assert(pUrbLnx->pNext != pCur); Assert(pUrbLnx->pPrev != pCur); Assert(pCur->pNext == pCur->pPrev);
1117 Assert(pCur->pSplitHead == pUrbLnx);
1118 Assert(pCur->pSplitNext == NULL);
1119
1120 pCur->KUrb = pUrbLnx->KUrb;
1121 pCur->KUrb.buffer = pb;
1122 pCur->KUrb.buffer_length = MIN(cbLeft, SPLIT_SIZE);
1123 pCur->KUrb.actual_length = 0;
1124
1125 pb += pCur->KUrb.buffer_length;
1126 cbLeft -= pCur->KUrb.buffer_length;
1127 }
1128 Assert(cbLeft == 0);
1129
1130 /*
1131 * Submit them.
1132 */
1133 bool fUnplugged = false;
1134 bool fFailed = false;
1135 pCur = pUrbLnx;
1136 for (i = 0; i < cKUrbs; i++, pCur = pCur->pSplitNext)
1137 {
1138 cTries = 0;
1139 while (ioctl(pDevLnx->File, USBDEVFS_SUBMITURB, &pCur->KUrb))
1140 {
1141 if (errno == EINTR)
1142 continue;
1143 if (errno == ENODEV)
1144 {
1145 Log(("usbProxyLinuxUrbQueueSplit: ENODEV -> unplugged. pProxyDev=%p[%s]\n", pProxyDev, pProxyDev->Dev.pszName));
1146 fFailed = fUnplugged = true;
1147 break;
1148 }
1149
1150 Log(("usb-linux: Queue URB %p -> %d!!! type=%d ep=%#x buffer_length=%#x cTries=%d\n",
1151 pUrb, errno, pCur->KUrb.type, pCur->KUrb.endpoint, pCur->KUrb.buffer_length, cTries));
1152 if (errno != EBUSY && ++cTries < 3) /* this doesn't work for the floppy :/ */
1153 continue;
1154 fFailed = true;
1155 break;
1156 }
1157 if (fFailed)
1158 break;
1159 pCur->u64SubmitTS = RTTimeMilliTS();
1160 }
1161 if (!fFailed)
1162 {
1163 pUrb->Dev.pvPrivate = pUrbLnx;
1164 LogFlow(("usbProxyLinuxUrbQueueSplit: ok\n"));
1165 return true;
1166 }
1167
1168 /*
1169 * Clean up.
1170 */
1171 if (pUrb->enmType == VUSBXFERTYPE_MSG)
1172 usbProxyLinuxUrbSwapSetup((PVUSBSETUP)pUrb->abData);
1173
1174 /* discard and reap later (walking with pUrbLnx). */
1175 if (pUrbLnx != pCur)
1176 {
1177 for (;;)
1178 {
1179 pUrbLnx->fCanceledBySubmit = true;
1180 pUrbLnx->KUrb.usercontext = NULL;
1181 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pUrbLnx->KUrb, false, UINT32_MAX))
1182 {
1183 if (errno == ENODEV)
1184 fUnplugged = true;
1185 else if (errno == ENOENT)
1186 pUrbLnx->fSplitElementReaped = true;
1187 else
1188 LogRel(("SUB: Failed to discard %p! errno=%d (pUrb=%p)\n", pUrbLnx->KUrb.usercontext, errno, pUrb)); /* serious! */
1189 }
1190 if (pUrbLnx->pSplitNext == pCur)
1191 {
1192 pUrbLnx->pSplitNext = NULL;
1193 break;
1194 }
1195 pUrbLnx = pUrbLnx->pSplitNext; Assert(pUrbLnx);
1196 }
1197 }
1198
1199 /* free the unsubmitted ones. */
1200 while (pCur)
1201 {
1202 PUSBPROXYURBLNX pFree = pCur;
1203 pCur = pCur->pSplitNext;
1204 usbProxyLinuxUrbFree(pProxyDev, pFree);
1205 }
1206
1207 /* send unplug event if we failed with ENODEV originally. */
1208 if (fUnplugged)
1209 usbProxLinuxUrbUnplugged(pProxyDev);
1210 return false;
1211}
1212
1213
1214/**
1215 * @copydoc USBPROXYBACK::pfbUrbQueue
1216 */
1217static int usbProxyLinuxUrbQueue(PVUSBURB pUrb)
1218{
1219 unsigned cTries;
1220 PUSBPROXYDEV pProxyDev = (PUSBPROXYDEV)pUrb->pDev;
1221 PUSBPROXYDEVLNX pDevLnx = (PUSBPROXYDEVLNX)pProxyDev->Backend.pv;
1222 LogFlow(("usbProxyLinuxUrbQueue: pProxyDev=%p[%s] pUrb=%p EndPt=%d cbData=%d\n",
1223 pProxyDev, pProxyDev->Dev.pszName, pUrb, pUrb->EndPt, pUrb->cbData));
1224
1225 /*
1226 * Allocate a linux urb.
1227 */
1228 PUSBPROXYURBLNX pUrbLnx = usbProxyLinuxUrbAlloc(pProxyDev, NULL);
1229 if (!pUrbLnx)
1230 return false;
1231
1232 switch (pUrb->enmType)
1233 {
1234 case VUSBXFERTYPE_MSG:
1235 pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_CONTROL;
1236 if (pUrb->cbData < sizeof(VUSBSETUP))
1237 {
1238 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx);
1239 return false;
1240 }
1241 usbProxyLinuxUrbSwapSetup((PVUSBSETUP)pUrb->abData);
1242 LogFlow(("usbProxyLinuxUrbQueue: message\n"));
1243 break;
1244 case VUSBXFERTYPE_BULK:
1245 pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_BULK;
1246 break;
1247 case VUSBXFERTYPE_ISOC:
1248 pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_ISO;
1249 break;
1250 case VUSBXFERTYPE_INTR:
1251 pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_INTERRUPT;
1252 break;
1253 default:
1254 goto l_err;
1255 }
1256 pUrbLnx->KUrb.endpoint = pUrb->EndPt;
1257 pUrbLnx->KUrb.status = 0;
1258 pUrbLnx->KUrb.flags = pUrb->fShortNotOk ? USBDEVFS_URB_SHORT_NOT_OK : 0; /* ISO_ASAP too? */
1259 pUrbLnx->KUrb.buffer = pUrb->abData;
1260 pUrbLnx->KUrb.buffer_length = pUrb->cbData;
1261 pUrbLnx->KUrb.actual_length = 0;
1262 pUrbLnx->KUrb.start_frame = 0;
1263 pUrbLnx->KUrb.number_of_packets = 0;
1264 pUrbLnx->KUrb.error_count = 0;
1265 pUrbLnx->KUrb.signr = 0;
1266 pUrbLnx->KUrb.usercontext = pUrb;
1267 switch (pUrb->enmDir)
1268 {
1269 case VUSBDIRECTION_IN:
1270 pUrbLnx->KUrb.endpoint |= 0x80;
1271 break;
1272 case VUSBDIRECTION_OUT:
1273 break;
1274 default:
1275 AssertMsgFailed(("usbProxyLinuxUrbQueue: Invalid directorion %d\n", pUrb->enmDir));
1276 goto l_err;
1277 }
1278
1279 /*
1280 * Submit it.
1281 */
1282 cTries = 0;
1283 while (ioctl(pDevLnx->File, USBDEVFS_SUBMITURB, &pUrbLnx->KUrb))
1284 {
1285 if (errno == EINTR)
1286 continue;
1287 if (errno == ENODEV)
1288 {
1289 Log(("usbProxyLinuxUrbQueue: ENODEV -> unplugged. pProxyDev=%p[%s]\n", pProxyDev, pProxyDev->Dev.pszName));
1290 if (pUrb->enmType == VUSBXFERTYPE_MSG)
1291 usbProxyLinuxUrbSwapSetup((PVUSBSETUP)pUrb->abData);
1292 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx);
1293
1294 usbProxLinuxUrbUnplugged(pProxyDev);
1295 return false;
1296 }
1297
1298 /*
1299 * usbfs has or used to have a low buffer limit (16KB) in order to prevent
1300 * processes wasting kmalloc'ed memory. It will return EINVAL if break that
1301 * limit, and we'll have to split the VUSB URB up into multiple linux URBs.
1302 *
1303 * Since this is a limit which is subject to change, we cannot check for it
1304 * before submitting the URB. We just have to try and fail.
1305 */
1306 if ( errno == EINVAL
1307 && pUrb->cbData >= 8*_1K)
1308 return usbProxyLinuxUrbQueueSplit(pProxyDev, pUrbLnx, pUrb);
1309
1310 Log(("usb-linux: Queue URB %p -> %d!!! type=%d ep=%#x buffer_length=%#x cTries=%d\n",
1311 pUrb, errno, pUrbLnx->KUrb.type, pUrbLnx->KUrb.endpoint, pUrbLnx->KUrb.buffer_length, cTries));
1312 if (errno != EBUSY && ++cTries < 3) /* this doesn't work for the floppy :/ */
1313 continue;
1314l_err:
1315 if (pUrb->enmType == VUSBXFERTYPE_MSG)
1316 usbProxyLinuxUrbSwapSetup((PVUSBSETUP)pUrb->abData);
1317 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx);
1318 return false;
1319 }
1320 pUrbLnx->u64SubmitTS = RTTimeMilliTS();
1321
1322 LogFlow(("usbProxyLinuxUrbQueue: ok\n"));
1323 pUrb->Dev.pvPrivate = pUrbLnx;
1324 return true;
1325}
1326
1327
1328/**
1329 * Check if any or the in-flight URBs are taking too long and should be cancelled.
1330 *
1331 * Cancelling is done in three turns, first a URB is marked for timeout if it's
1332 * exceeding a certain time limit. Then the next time it's encountered it is actually
1333 * cancelled. The idea now is that it's supposed to be reaped and returned in the next
1334 * round of calls.
1335 *
1336 * @param pProxyDev The proxy device.
1337 * @param pDevLnx The linux backend data.
1338 *
1339 * @todo Make the HCI do proper timeout handling! Current timeout is 3 min and 20 seconds
1340 * as not to break bloomberg which queues IN packages with 3 min timeouts.
1341 */
1342static void vusbProxyLinuxUrbDoTimeouts(PUSBPROXYDEV pProxyDev, PUSBPROXYDEVLNX pDevLnx)
1343{
1344 RTCritSectEnter(&pDevLnx->CritSect);
1345 uint64_t u64MilliTS = RTTimeMilliTS();
1346 PUSBPROXYURBLNX pCur;
1347 for (pCur = pDevLnx->pInFlightHead;
1348 pCur;
1349 pCur = pCur->pNext)
1350 {
1351 if (pCur->fTimedOut)
1352 {
1353 if (pCur->pSplitHead)
1354 {
1355 /* split */
1356 Assert(pCur == pCur->pSplitHead);
1357 unsigned cFailures = 0;
1358 PUSBPROXYURBLNX pCur2;
1359 for ( pCur2 = pCur; pCur2; pCur2 = pCur2->pSplitNext)
1360 {
1361 if (pCur2->fSplitElementReaped)
1362 continue;
1363
1364 if ( !usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pCur2->KUrb, true, UINT32_MAX)
1365 || errno == ENOENT)
1366 pCur2->fCanceledByTimedOut = true;
1367 else if (errno != ENODEV)
1368 Log(("vusbProxyLinuxUrbDoTimeouts: pUrb=%p failed errno=%d (!!split!!)\n", pCur2->KUrb.usercontext, errno));
1369 else
1370 goto l_leave; /* ENODEV means break and everything cancelled elsewhere. */
1371 }
1372 LogRel(("USB: Cancelled URB (%p) after %lldms!! (cFailures=%d)\n",
1373 pCur->KUrb.usercontext, u64MilliTS - pCur->u64SubmitTS, cFailures));
1374 }
1375 else
1376 {
1377 /* unsplit */
1378 if ( !usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pCur->KUrb, true, UINT32_MAX)
1379 || errno == -ENOENT)
1380 {
1381 pCur->fCanceledByTimedOut = true;
1382 LogRel(("USB: Cancelled URB (%p) after %lldms!!\n", pCur->KUrb.usercontext, u64MilliTS - pCur->u64SubmitTS));
1383 }
1384 else if (errno != ENODEV)
1385 LogFlow(("vusbProxyLinuxUrbDoTimeouts: pUrb=%p failed errno=%d\n", pCur->KUrb.usercontext, errno));
1386 else
1387 goto l_leave; /* ENODEV means break and everything cancelled elsewhere. */
1388 }
1389 }
1390 else if (u64MilliTS - pCur->u64SubmitTS >= 200*1000 /* 200 sec (180 sec has been observed with XP) */)
1391 pCur->fTimedOut = true;
1392 }
1393
1394l_leave:
1395 RTCritSectLeave(&pDevLnx->CritSect);
1396}
1397
1398
1399/**
1400 * Get and translates the linux status to a VUSB status.
1401 */
1402static VUSBSTATUS vusbProxyLinuxUrbGetStatus(PUSBPROXYURBLNX pUrbLnx)
1403{
1404 switch (pUrbLnx->KUrb.status)
1405 {
1406 case 0:
1407 if (!pUrbLnx->fCanceledByTimedOut)
1408 return VUSBSTATUS_OK;
1409 /* fall thru */
1410
1411 case -EILSEQ:
1412 return VUSBSTATUS_CRC;
1413
1414 case -EREMOTEIO: /* ehci and ohci uses this for underflow error. */
1415 return VUSBSTATUS_UNDERFLOW;
1416
1417 case -ENODEV:
1418 return VUSBSTATUS_DNR;
1419
1420 //case -ECOMM:
1421 // return VUSBSTATUS_BUFFER_OVERRUN;
1422 //case -ENOSR:
1423 // return VUSBSTATUS_BUFFER_UNDERRUN;
1424
1425 //case -EPROTO:
1426 // return VUSBSTATUS_BIT_STUFFING;
1427
1428 default:
1429 Log(("usbProxyLinuxUrbReap: pKUrb status %d!!\n", pUrbLnx->KUrb.status));
1430 return VUSBSTATUS_STALL;
1431 }
1432}
1433
1434
1435/**
1436 * Reap URBs in-flight on a device.
1437 *
1438 * @returns Pointer to a completed URB.
1439 * @returns NULL if no URB was completed.
1440 * @param pProxyDev The device.
1441 * @param cMillies Number of milliseconds to wait. Use 0 to not wait at all.
1442 */
1443static PVUSBURB usbProxyLinuxUrbReap(PUSBPROXYDEV pProxyDev, unsigned cMillies)
1444{
1445 PUSBPROXYURBLNX pUrbLnx = NULL;
1446 PUSBPROXYDEVLNX pDevLnx = (PUSBPROXYDEVLNX)pProxyDev->Backend.pv;
1447
1448 /*
1449 * Any URBs pending delivery?
1450 */
1451 if (pDevLnx->pTaxingHead)
1452 {
1453 RTCritSectEnter(&pDevLnx->CritSect);
1454 pUrbLnx = pDevLnx->pTaxingHead;
1455 if (pUrbLnx)
1456 {
1457 /* unlink from the pending delivery list */
1458 if (pUrbLnx->pNext)
1459 {
1460 pUrbLnx->pNext->pPrev = NULL;
1461 pDevLnx->pTaxingHead = pUrbLnx->pNext;
1462 }
1463 else
1464 pDevLnx->pTaxingHead = pDevLnx->pTaxingTail = NULL;
1465
1466 /* temporarily into the active list, so free works right. */
1467 pUrbLnx->pPrev = NULL;
1468 pUrbLnx->pNext = pDevLnx->pInFlightHead;
1469 if (pUrbLnx->pNext)
1470 pUrbLnx->pNext->pPrev = pUrbLnx;
1471 pDevLnx->pInFlightHead = pUrbLnx;
1472 }
1473 RTCritSectLeave(&pDevLnx->CritSect);
1474 }
1475 if (!pUrbLnx)
1476 {
1477 /*
1478 * Don't block if nothing is in the air.
1479 */
1480 if (!pDevLnx->pInFlightHead)
1481 return NULL;
1482
1483 /*
1484 * Block for requested period.
1485 *
1486 * It seems to me that the path of poll() is shorter and
1487 * involves less semaphores than ioctl() on usbfs. So, we'll
1488 * do a poll regardless of whether cMillies == 0 or not.
1489 */
1490 if (cMillies)
1491 {
1492
1493 for (;;)
1494 {
1495 struct pollfd pfd;
1496 int rc;
1497
1498 pfd.fd = pDevLnx->File;
1499 pfd.events = POLLOUT | POLLWRNORM /* completed async */
1500 | POLLERR | POLLHUP /* disconnected */;
1501 pfd.revents = 0;
1502 rc = poll(&pfd, 1, cMillies);
1503 Log(("usbProxyLinuxUrbReap: poll rc = %d\n", rc));
1504 if (rc >= 1)
1505 break;
1506 if (rc >= 0 /*|| errno == ETIMEOUT*/)
1507 {
1508 vusbProxyLinuxUrbDoTimeouts(pProxyDev, pDevLnx);
1509 return NULL;
1510 }
1511 if (errno != EAGAIN)
1512 {
1513 Log(("usb-linux: Reap URB - poll -> %d errno=%d pProxyDev=%p[%s]\n", rc, errno, pProxyDev, pProxyDev->Dev.pszName));
1514 return NULL;
1515 }
1516 Log(("usbProxyLinuxUrbReap: poll again - weird!!!\n"));
1517 }
1518 }
1519
1520 /*
1521 * Reap URBs, non-blocking.
1522 */
1523 for (;;)
1524 {
1525 struct usbdevfs_urb *pKUrb;
1526 while (ioctl(pDevLnx->File, USBDEVFS_REAPURBNDELAY, &pKUrb))
1527 if (errno != EINTR)
1528 {
1529 if (errno == ENODEV)
1530 usbProxLinuxUrbUnplugged(pProxyDev);
1531 else if (errno == EAGAIN)
1532 vusbProxyLinuxUrbDoTimeouts(pProxyDev, pDevLnx);
1533 else
1534 Log(("usb-linux: Reap URB. errno=%d pProxyDev=%p[%s]\n", errno, pProxyDev, pProxyDev->Dev.pszName));
1535 return NULL;
1536 }
1537 pUrbLnx = (PUSBPROXYURBLNX)pKUrb;
1538
1539 /* split list: Is the entire split list done yet? */
1540 if (pUrbLnx->pSplitHead)
1541 {
1542 pUrbLnx->fSplitElementReaped = true;
1543 PUSBPROXYURBLNX pCur;
1544 for (pCur = pUrbLnx->pSplitHead; pCur; pCur = pCur->pSplitNext)
1545 if (!pCur->fSplitElementReaped)
1546 {
1547 pUrbLnx = NULL;
1548 break;
1549 }
1550 if (!pUrbLnx)
1551 continue;
1552 pUrbLnx = pUrbLnx->pSplitHead;
1553 }
1554 break;
1555 }
1556 }
1557
1558 /*
1559 * Ok, we got one!
1560 */
1561 PVUSBURB pUrb = (PVUSBURB)pUrbLnx->KUrb.usercontext;
1562 if ( pUrb
1563 && !pUrbLnx->fCanceledBySubmit)
1564 {
1565 if (pUrbLnx->pSplitHead)
1566 {
1567 /* split - find the end byte and the first error status. */
1568 Assert(pUrbLnx == pUrbLnx->pSplitHead);
1569 uint8_t *pbEnd = &pUrb->abData[0];
1570 pUrb->enmStatus = VUSBSTATUS_OK;
1571 PUSBPROXYURBLNX pCur;
1572 for (pCur = pUrbLnx; pCur; pCur = pCur->pSplitNext)
1573 {
1574 if (pCur->KUrb.actual_length)
1575 pbEnd = (uint8_t *)pCur->KUrb.buffer + pCur->KUrb.actual_length;
1576 if (pUrb->enmStatus == VUSBSTATUS_OK)
1577 pUrb->enmStatus = vusbProxyLinuxUrbGetStatus(pCur);
1578 }
1579 pUrb->cbData = pbEnd - &pUrb->abData[0];
1580 usbProxyLinuxUrbFreeSplitList(pProxyDev, pUrbLnx);
1581 }
1582 else
1583 {
1584 /* unsplit. */
1585 pUrb->enmStatus = vusbProxyLinuxUrbGetStatus(pUrbLnx);
1586 pUrb->cbData = pUrbLnx->KUrb.actual_length;
1587 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx);
1588 }
1589 pUrb->Dev.pvPrivate = NULL;
1590
1591 /* some adjustments for message transfers. */
1592 if (pUrb->enmType == VUSBXFERTYPE_MSG)
1593 {
1594 pUrb->cbData += sizeof(VUSBSETUP);
1595 usbProxyLinuxUrbSwapSetup((PVUSBSETUP)pUrb->abData);
1596 }
1597 }
1598 else
1599 {
1600 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx);
1601 pUrb = NULL;
1602 }
1603
1604 LogFlow(("usbProxyLinuxUrbReap: dev=%p[%s] returns %p\n", pProxyDev, pProxyDev->Dev.pszName, pUrb));
1605 return pUrb;
1606}
1607
1608
1609/**
1610 * Cancels the URB.
1611 * The URB requires reaping, so we don't change its state.
1612 */
1613static void usbProxyLinuxUrbCancel(PVUSBURB pUrb)
1614{
1615 PUSBPROXYDEV pProxyDev = (PUSBPROXYDEV)pUrb->pDev;
1616 PUSBPROXYURBLNX pUrbLnx = (PUSBPROXYURBLNX)pUrb->Dev.pvPrivate;
1617 if (pUrbLnx->pSplitHead)
1618 {
1619 /* split */
1620 Assert(pUrbLnx == pUrbLnx->pSplitHead);
1621 PUSBPROXYURBLNX pCur;
1622 for (pCur = pUrbLnx; pCur; pCur = pCur->pSplitNext)
1623 {
1624 if (pCur->fSplitElementReaped)
1625 continue;
1626 if ( !usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pCur->KUrb, true, UINT32_MAX)
1627 || errno == ENOENT)
1628 continue;
1629 if (errno == ENODEV)
1630 break;
1631 Log(("usb-linux: Discard URB %p failed, errno=%d. pProxyDev=%p[%s]!!! (split)\n",
1632 pUrb, errno, pProxyDev, pProxyDev->Dev.pszName));
1633 }
1634 }
1635 else
1636 {
1637 /* unsplit */
1638 if ( usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pUrbLnx->KUrb, true, UINT32_MAX)
1639 && errno != ENODEV /* deal with elsewhere. */
1640 && errno != ENOENT)
1641 Log(("usb-linux: Discard URB %p failed, errno=%d. pProxyDev=%p[%s]!!!\n",
1642 pUrb, errno, pProxyDev, pProxyDev->Dev.pszName));
1643 }
1644}
1645
1646
1647/**
1648 * The Linux USB Proxy Backend.
1649 */
1650#ifdef __cplusplus
1651extern
1652#endif
1653const USBPROXYBACK g_USBProxyDeviceHost =
1654{
1655 "host",
1656 usbProxyLinuxOpen,
1657 usbProxyLinuxClose,
1658 usbProxyLinuxReset,
1659 usbProxyLinuxSetConfig,
1660 usbProxyLinuxClaimInterface,
1661 usbProxyLinuxReleaseInterface,
1662 usbProxyLinuxSetInterface,
1663 usbProxyLinuxClearHaltedEp,
1664 usbProxyLinuxUrbQueue,
1665 usbProxyLinuxUrbCancel,
1666 usbProxyLinuxUrbReap,
1667 0
1668};
1669
1670
1671/*
1672 * Local Variables:
1673 * mode: c
1674 * c-file-style: "bsd"
1675 * c-basic-offset: 4
1676 * tab-width: 4
1677 * indent-tabs-mode: s
1678 * End:
1679 */
1680
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