VirtualBox

source: vbox/trunk/src/VBox/Devices/USB/linux/USBProxyDevice-linux.cpp@ 109193

Last change on this file since 109193 was 108622, checked in by vboxsync, 8 weeks ago

Devices/USB/linux/USBProxyDevice-linux.cpp: clang build fixes, bugref:10391

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 57.3 KB
Line 
1/* $Id: USBProxyDevice-linux.cpp 108622 2025-03-20 10:36:58Z vboxsync $ */
2/** @file
3 * USB device proxy - the Linux backend.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.215389.xyz.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Defined Constants And Macros *
31*********************************************************************************************************************************/
32
33
34/*********************************************************************************************************************************
35* Header Files *
36*********************************************************************************************************************************/
37#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
38
39#include <iprt/stdint.h>
40#include <iprt/err.h>
41#include <iprt/pipe.h>
42
43#include <sys/types.h>
44#include <sys/stat.h>
45#include <sys/vfs.h>
46#include <sys/ioctl.h>
47#include <sys/poll.h>
48#include <stdint.h>
49#include <stdio.h>
50#include <string.h>
51#include <stdlib.h>
52#include <limits.h>
53#include <unistd.h>
54#include <fcntl.h>
55#include <errno.h>
56#ifdef VBOX_WITH_LINUX_COMPILER_H
57# include <linux/compiler.h>
58#endif
59#include <linux/usbdevice_fs.h>
60
61#ifndef RDESKTOP
62# include <VBox/vmm/pdm.h>
63#else
64# define RTCRITSECT void *
65static inline int rtcsNoop() { return VINF_SUCCESS; }
66static inline bool rtcsTrue() { return true; }
67# define RTCritSectInit(a) rtcsNoop()
68# define RTCritSectDelete(a) rtcsNoop()
69# define RTCritSectEnter(a) rtcsNoop()
70# define RTCritSectLeave(a) rtcsNoop()
71# define RTCritSectIsOwner(a) rtcsTrue()
72#endif
73#include <VBox/err.h>
74#include <VBox/log.h>
75#include <iprt/alloc.h>
76#include <iprt/assert.h>
77#include <iprt/asm.h>
78#include <iprt/ctype.h>
79#include <iprt/file.h>
80#include <iprt/linux/sysfs.h>
81#include <iprt/stream.h>
82#include <iprt/string.h>
83#include <iprt/list.h>
84#include <iprt/time.h>
85#include "../USBProxyDevice.h"
86
87
88/*********************************************************************************************************************************
89* Structures and Typedefs *
90*********************************************************************************************************************************/
91/**
92 * Wrapper around the linux urb request structure.
93 * This is required to track in-flight and landed URBs.
94 */
95typedef struct USBPROXYURBLNX
96{
97 /** Node to link the URB in of the existing lists. */
98 RTLISTNODE NodeList;
99 /** If we've split the VUSBURB up into multiple linux URBs, this is points to the head. */
100 struct USBPROXYURBLNX *pSplitHead;
101 /** The next linux URB if split up. */
102 struct USBPROXYURBLNX *pSplitNext;
103 /** Don't report these back. */
104 bool fCanceledBySubmit;
105 /** This split element is reaped. */
106 bool fSplitElementReaped;
107 /** This URB was discarded. */
108 bool fDiscarded;
109 /** Size to transfer in remaining fragments of a split URB */
110 uint32_t cbSplitRemaining;
111
112#if RT_CLANG_PREREQ(3, 4)
113# pragma clang diagnostic push
114# pragma clang diagnostic ignored "-Wpedantic"
115#elif RT_GNUC_PREREQ(6, 0) /* gcc 6.2 complains about the [] member of KUrb */
116# pragma GCC diagnostic push
117# pragma GCC diagnostic ignored "-Wpedantic"
118#endif
119 /** The kernel URB data (variable size array included). */
120 struct usbdevfs_urb KUrb;
121#if RT_CLANG_PREREQ(3, 4)
122# pragma clang diagnostic pop
123#elif RT_GNUC_PREREQ(6, 0)
124# pragma GCC diagnostic pop
125#endif
126} USBPROXYURBLNX, *PUSBPROXYURBLNX;
127
128/**
129 * Data for the linux usb proxy backend.
130 */
131typedef struct USBPROXYDEVLNX
132{
133 /** The open file. */
134 RTFILE hFile;
135 /** Critical section protecting the lists. */
136 RTCRITSECT CritSect;
137 /** The list of free linux URBs (USBPROXYURBLNX). */
138 RTLISTANCHOR ListFree;
139 /** The list of active linux URBs.
140 * We must maintain this so we can properly reap URBs of a detached device.
141 * Only the split head will appear in this list. (USBPROXYURBLNX) */
142 RTLISTANCHOR ListInFlight;
143 /** Are we using sysfs to find the active configuration? */
144 bool fUsingSysfs;
145 /** Pipe handle for waking up - writing end. */
146 RTPIPE hPipeWakeupW;
147 /** Pipe handle for waking up - reading end. */
148 RTPIPE hPipeWakeupR;
149 /** The device node/sysfs path of the device.
150 * Used to figure out the configuration after a reset. */
151 char *pszPath;
152 /** Mask of claimed interfaces. */
153 uint32_t fClaimedIfsMask;
154} USBPROXYDEVLNX, *PUSBPROXYDEVLNX;
155
156
157/*********************************************************************************************************************************
158* Internal Functions *
159*********************************************************************************************************************************/
160static void usbProxLinuxUrbUnplugged(PUSBPROXYDEV pProxyDev);
161static DECLCALLBACK(int) usbProxyLinuxClaimInterface(PUSBPROXYDEV pProxyDev, int iIf);
162static DECLCALLBACK(int) usbProxyLinuxReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf);
163
164
165/**
166 * Wrapper for the ioctl call.
167 *
168 * This wrapper will repeat the call if we get an EINTR or EAGAIN. It can also
169 * handle ENODEV (detached device) errors.
170 *
171 * @returns whatever ioctl returns.
172 * @param pProxyDev The proxy device.
173 * @param iCmd The ioctl command / function.
174 * @param pvArg The ioctl argument / data.
175 * @param fHandleNoDev Whether to handle ENODEV.
176 * @param cTries The number of retries. Use UINT32_MAX for (kind of) indefinite retries.
177 * @internal
178 */
179static int usbProxyLinuxDoIoCtl(PUSBPROXYDEV pProxyDev, unsigned long iCmd, void *pvArg, bool fHandleNoDev, uint32_t cTries)
180{
181 int rc;
182 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
183 do
184 {
185 do
186 {
187 rc = ioctl(RTFileToNative(pDevLnx->hFile), iCmd, pvArg);
188 if (rc >= 0)
189 return rc;
190 } while (errno == EINTR);
191
192 if (errno == ENODEV && fHandleNoDev)
193 {
194 usbProxLinuxUrbUnplugged(pProxyDev);
195 Log(("usb-linux: ENODEV -> unplugged. pProxyDev=%s\n", usbProxyGetName(pProxyDev)));
196 errno = ENODEV;
197 break;
198 }
199 if (errno != EAGAIN)
200 break;
201 } while (cTries-- > 0);
202
203 return rc;
204}
205
206
207/**
208 * The device has been unplugged.
209 * Cancel all in-flight URBs and put them up for reaping.
210 */
211static void usbProxLinuxUrbUnplugged(PUSBPROXYDEV pProxyDev)
212{
213 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
214
215 /*
216 * Shoot down all flying URBs.
217 */
218 RTCritSectEnter(&pDevLnx->CritSect);
219 pProxyDev->fDetached = true;
220
221 PUSBPROXYURBLNX pUrbLnx;
222 PUSBPROXYURBLNX pUrbLnxNext;
223 RTListForEachSafe(&pDevLnx->ListInFlight, pUrbLnx, pUrbLnxNext, USBPROXYURBLNX, NodeList)
224 {
225 if (!pUrbLnx->fDiscarded)
226 {
227 pUrbLnx->fDiscarded = true;
228 /* Cancel the URB. It will be reaped normally. */
229 ioctl(RTFileToNative(pDevLnx->hFile), USBDEVFS_DISCARDURB, &pUrbLnx->KUrb);
230 if (!pUrbLnx->KUrb.status)
231 pUrbLnx->KUrb.status = -ENODEV;
232 }
233 }
234
235 RTCritSectLeave(&pDevLnx->CritSect);
236}
237
238
239/**
240 * Set the connect state seen by kernel drivers
241 * @internal
242 */
243static void usbProxyLinuxSetConnected(PUSBPROXYDEV pProxyDev, int iIf, bool fConnect, bool fQuiet)
244{
245 if ( iIf >= 32
246 || !(pProxyDev->fMaskedIfs & RT_BIT(iIf)))
247 {
248 struct usbdevfs_ioctl IoCtl;
249 if (!fQuiet)
250 LogFlow(("usbProxyLinuxSetConnected: pProxyDev=%s iIf=%#x fConnect=%s\n",
251 usbProxyGetName(pProxyDev), iIf, fConnect ? "true" : "false"));
252
253 IoCtl.ifno = iIf;
254 IoCtl.ioctl_code = fConnect ? USBDEVFS_CONNECT : USBDEVFS_DISCONNECT;
255 IoCtl.data = NULL;
256 if ( usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_IOCTL, &IoCtl, true, UINT32_MAX)
257 && !fQuiet)
258 Log(("usbProxyLinuxSetConnected: failure, errno=%d. pProxyDev=%s\n",
259 errno, usbProxyGetName(pProxyDev)));
260 }
261}
262
263
264/**
265 * Links the given URB into the in flight list.
266 *
267 * @param pDevLnx The proxy device instance - Linux specific data.
268 * @param pUrbLnx The URB to link into the in flight list.
269 */
270static void usbProxyLinuxUrbLinkInFlight(PUSBPROXYDEVLNX pDevLnx, PUSBPROXYURBLNX pUrbLnx)
271{
272 LogFlowFunc(("pDevLnx=%p pUrbLnx=%p\n", pDevLnx, pUrbLnx));
273 Assert(RTCritSectIsOwner(&pDevLnx->CritSect));
274 Assert(!pUrbLnx->pSplitHead || pUrbLnx->pSplitHead == pUrbLnx);
275 RTListAppend(&pDevLnx->ListInFlight, &pUrbLnx->NodeList);
276}
277
278
279/**
280 * Unlinks the given URB from the in flight list.
281 *
282 * @param pDevLnx The proxy device instance - Linux specific data.
283 * @param pUrbLnx The URB to link into the in flight list.
284 */
285static void usbProxyLinuxUrbUnlinkInFlight(PUSBPROXYDEVLNX pDevLnx, PUSBPROXYURBLNX pUrbLnx)
286{
287 LogFlowFunc(("pDevLnx=%p pUrbLnx=%p\n", pDevLnx, pUrbLnx));
288 RTCritSectEnter(&pDevLnx->CritSect);
289
290 /*
291 * Remove from the active list.
292 */
293 Assert(!pUrbLnx->pSplitHead || pUrbLnx->pSplitHead == pUrbLnx);
294
295 RTListNodeRemove(&pUrbLnx->NodeList);
296
297 RTCritSectLeave(&pDevLnx->CritSect);
298}
299
300
301/**
302 * Allocates a linux URB request structure.
303 *
304 * @returns Pointer to an active URB request.
305 * @returns NULL on failure.
306 * @param pProxyDev The proxy device instance.
307 * @param pSplitHead The split list head if allocating for a split list.
308 */
309static PUSBPROXYURBLNX usbProxyLinuxUrbAlloc(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pSplitHead)
310{
311 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
312 PUSBPROXYURBLNX pUrbLnx;
313
314 LogFlowFunc(("pProxyDev=%p pSplitHead=%p\n", pProxyDev, pSplitHead));
315
316 RTCritSectEnter(&pDevLnx->CritSect);
317
318 /*
319 * Try remove a linux URB from the free list, if none there allocate a new one.
320 */
321 pUrbLnx = RTListGetFirst(&pDevLnx->ListFree, USBPROXYURBLNX, NodeList);
322 if (pUrbLnx)
323 {
324 RTListNodeRemove(&pUrbLnx->NodeList);
325 RTCritSectLeave(&pDevLnx->CritSect);
326 }
327 else
328 {
329 RTCritSectLeave(&pDevLnx->CritSect);
330 PVUSBURB pVUrbDummy; RT_NOREF(pVUrbDummy);
331 pUrbLnx = (PUSBPROXYURBLNX)RTMemAlloc(RT_UOFFSETOF_DYN(USBPROXYURBLNX,
332 KUrb.iso_frame_desc[RT_ELEMENTS(pVUrbDummy->aIsocPkts)]));
333 if (!pUrbLnx)
334 return NULL;
335 }
336
337 pUrbLnx->pSplitHead = pSplitHead;
338 pUrbLnx->pSplitNext = NULL;
339 pUrbLnx->fCanceledBySubmit = false;
340 pUrbLnx->fSplitElementReaped = false;
341 pUrbLnx->fDiscarded = false;
342 LogFlowFunc(("returns pUrbLnx=%p\n", pUrbLnx));
343 return pUrbLnx;
344}
345
346
347/**
348 * Frees a linux URB request structure.
349 *
350 * @param pProxyDev The proxy device instance.
351 * @param pUrbLnx The linux URB to free.
352 */
353static void usbProxyLinuxUrbFree(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pUrbLnx)
354{
355 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
356
357 LogFlowFunc(("pProxyDev=%p pUrbLnx=%p\n", pProxyDev, pUrbLnx));
358
359 /*
360 * Link it into the free list.
361 */
362 RTCritSectEnter(&pDevLnx->CritSect);
363 RTListAppend(&pDevLnx->ListFree, &pUrbLnx->NodeList);
364 RTCritSectLeave(&pDevLnx->CritSect);
365}
366
367
368/**
369 * Frees split list of a linux URB request structure.
370 *
371 * @param pProxyDev The proxy device instance.
372 * @param pUrbLnx A linux URB to in the split list to be freed.
373 */
374static void usbProxyLinuxUrbFreeSplitList(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pUrbLnx)
375{
376 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
377
378 LogFlowFunc(("pProxyDev=%p pUrbLnx=%p\n", pProxyDev, pUrbLnx));
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 pFree->pSplitHead = pFree->pSplitNext = NULL;
390 usbProxyLinuxUrbFree(pProxyDev, pFree);
391 }
392
393 RTCritSectLeave(&pDevLnx->CritSect);
394}
395
396
397/**
398 * This finds the device in the /proc/bus/usb/bus/addr file and finds
399 * the config with an asterix.
400 *
401 * @returns The Cfg#.
402 * @returns -1 if no active config.
403 * @param pProxyDev The proxy device instance.
404 * @param pszDevNode The path to the device. We infere the location of
405 * the devices file, which bus and device number we're
406 * looking for.
407 * @param piFirstCfg The first configuration. (optional)
408 * @internal
409 */
410static int usbProxyLinuxFindActiveConfigUsbfs(PUSBPROXYDEV pProxyDev, const char *pszDevNode, int *piFirstCfg)
411{
412 RT_NOREF(pProxyDev);
413
414 /*
415 * Set return defaults.
416 */
417 int iActiveCfg = -1;
418 if (piFirstCfg)
419 *piFirstCfg = 1;
420
421 /*
422 * Parse the usbfs device node path and turn it into a path to the "devices" file,
423 * picking up the device number and bus along the way.
424 */
425 size_t cchDevNode = strlen(pszDevNode);
426 char *pszDevices = (char *)RTMemDupEx(pszDevNode, cchDevNode, sizeof("devices"));
427 AssertReturn(pszDevices, iActiveCfg);
428
429 /* the device number */
430 char *psz = pszDevices + cchDevNode;
431 while (*psz != '/')
432 psz--;
433 Assert(pszDevices < psz);
434 uint32_t uDev;
435 int rc = RTStrToUInt32Ex(psz + 1, NULL, 10, &uDev);
436 if (RT_SUCCESS(rc))
437 {
438 /* the bus number */
439 *psz-- = '\0';
440 while (*psz != '/')
441 psz--;
442 Assert(pszDevices < psz);
443 uint32_t uBus;
444 rc = RTStrToUInt32Ex(psz + 1, NULL, 10, &uBus);
445 if (RT_SUCCESS(rc))
446 {
447 strcpy(psz + 1, "devices");
448
449 /*
450 * Open and scan the devices file.
451 * We're ASSUMING that each device starts off with a 'T:' line.
452 */
453 PRTSTREAM pFile;
454 rc = RTStrmOpen(pszDevices, "r", &pFile);
455 if (RT_SUCCESS(rc))
456 {
457 char szLine[1024];
458 while (RT_SUCCESS(RTStrmGetLine(pFile, szLine, sizeof(szLine))))
459 {
460 /* we're only interested in 'T:' lines. */
461 psz = RTStrStripL(szLine);
462 if (psz[0] != 'T' || psz[1] != ':')
463 continue;
464
465 /* Skip ahead to 'Bus' and compare */
466 psz = RTStrStripL(psz + 2); Assert(!strncmp(psz, RT_STR_TUPLE("Bus=")));
467 psz = RTStrStripL(psz + 4);
468 char *pszNext;
469 uint32_t u;
470 rc = RTStrToUInt32Ex(psz, &pszNext, 10, &u); AssertRC(rc);
471 if (RT_FAILURE(rc))
472 continue;
473 if (u != uBus)
474 continue;
475
476 /* Skip ahead to 'Dev#' and compare */
477 psz = strstr(psz, "Dev#="); Assert(psz);
478 if (!psz)
479 continue;
480 psz = RTStrStripL(psz + 5);
481 rc = RTStrToUInt32Ex(psz, &pszNext, 10, &u); AssertRC(rc);
482 if (RT_FAILURE(rc))
483 continue;
484 if (u != uDev)
485 continue;
486
487 /*
488 * Ok, we've found the device.
489 * Scan until we find a selected configuration, the next device, or EOF.
490 */
491 while (RT_SUCCESS(RTStrmGetLine(pFile, szLine, sizeof(szLine))))
492 {
493 psz = RTStrStripL(szLine);
494 if (psz[0] == 'T')
495 break;
496 if (psz[0] != 'C' || psz[1] != ':')
497 continue;
498 const bool fActive = psz[2] == '*';
499 if (!fActive && !piFirstCfg)
500 continue;
501
502 /* Get the 'Cfg#' value. */
503 psz = strstr(psz, "Cfg#="); Assert(psz);
504 if (psz)
505 {
506 psz = RTStrStripL(psz + 5);
507 rc = RTStrToUInt32Ex(psz, &pszNext, 10, &u); AssertRC(rc);
508 if (RT_SUCCESS(rc))
509 {
510 if (piFirstCfg)
511 {
512 *piFirstCfg = u;
513 piFirstCfg = NULL;
514 }
515 if (fActive)
516 iActiveCfg = u;
517 }
518 }
519 if (fActive)
520 break;
521 }
522 break;
523 }
524 RTStrmClose(pFile);
525 }
526 }
527 }
528 RTMemFree(pszDevices);
529
530 return iActiveCfg;
531}
532
533
534/**
535 * This finds the active configuration from sysfs.
536 *
537 * @returns The Cfg#.
538 * @returns -1 if no active config.
539 * @param pProxyDev The proxy device instance.
540 * @param pszPath The sysfs path for the device.
541 * @param piFirstCfg The first configuration. (optional)
542 * @internal
543 */
544static int usbProxyLinuxFindActiveConfigSysfs(PUSBPROXYDEV pProxyDev, const char *pszPath, int *piFirstCfg)
545{
546#ifdef VBOX_USB_WITH_SYSFS
547 if (piFirstCfg != NULL)
548 *piFirstCfg = pProxyDev->paCfgDescs != NULL
549 ? pProxyDev->paCfgDescs[0].Core.bConfigurationValue
550 : 1;
551 int64_t bCfg = 0;
552 int rc = RTLinuxSysFsReadIntFile(10, &bCfg, "%s/bConfigurationValue", pszPath);
553 if (RT_FAILURE(rc))
554 bCfg = -1;
555 return (int)bCfg;
556#else /* !VBOX_USB_WITH_SYSFS */
557 return -1;
558#endif /* !VBOX_USB_WITH_SYSFS */
559}
560
561
562/**
563 * This finds the active configuration.
564 *
565 * @returns The Cfg#.
566 * @returns -1 if no active config.
567 * @param pProxyDev The proxy device instance.
568 * @param pszPath The sysfs path for the device, or the usbfs device
569 * node path.
570 * @param piFirstCfg The first configuration. (optional)
571 * @internal
572 */
573static int usbProxyLinuxFindActiveConfig(PUSBPROXYDEV pProxyDev, const char *pszPath, int *piFirstCfg)
574{
575 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
576 if (pDevLnx->fUsingSysfs)
577 return usbProxyLinuxFindActiveConfigSysfs(pProxyDev, pszPath, piFirstCfg);
578 return usbProxyLinuxFindActiveConfigUsbfs(pProxyDev, pszPath, piFirstCfg);
579}
580
581
582/**
583 * Extracts the Linux file descriptor associated with the kernel USB device.
584 * This is used by rdesktop-vrdp for polling for events.
585 * @returns the FD, or asserts and returns -1 on error
586 * @param pProxyDev The device instance
587 */
588RTDECL(int) USBProxyDeviceLinuxGetFD(PUSBPROXYDEV pProxyDev)
589{
590 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
591 AssertReturn(pDevLnx->hFile != NIL_RTFILE, -1);
592 return RTFileToNative(pDevLnx->hFile);
593}
594
595
596/**
597 * Opens the device file.
598 *
599 * @returns VBox status code.
600 * @param pProxyDev The device instance.
601 * @param pszAddress If we are using usbfs, this is the path to the
602 * device. If we are using sysfs, this is a string of
603 * the form "sysfs:<sysfs path>//device:<device node>".
604 * In the second case, the two paths are guaranteed
605 * not to contain the substring "//".
606 */
607static DECLCALLBACK(int) usbProxyLinuxOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress)
608{
609 LogFlow(("usbProxyLinuxOpen: pProxyDev=%p pszAddress=%s\n", pProxyDev, pszAddress));
610 const char *pszDevNode;
611 const char *pszPath;
612 size_t cchPath;
613 bool fUsingSysfs;
614
615 /*
616 * Are we using sysfs or usbfs?
617 */
618#ifdef VBOX_USB_WITH_SYSFS
619 fUsingSysfs = strncmp(pszAddress, RT_STR_TUPLE("sysfs:")) == 0;
620 if (fUsingSysfs)
621 {
622 pszDevNode = strstr(pszAddress, "//device:");
623 if (!pszDevNode)
624 {
625 LogRel(("usbProxyLinuxOpen: Invalid device address: '%s'\n", pszAddress));
626 return VERR_INVALID_PARAMETER;
627 }
628
629 pszPath = pszAddress + sizeof("sysfs:") - 1;
630 cchPath = pszDevNode - pszPath;
631 pszDevNode += sizeof("//device:") - 1;
632 }
633 else
634#endif /* VBOX_USB_WITH_SYSFS */
635 {
636#ifndef VBOX_USB_WITH_SYSFS
637 fUsingSysfs = false;
638#endif
639 pszPath = pszDevNode = pszAddress;
640 cchPath = strlen(pszPath);
641 }
642
643 /*
644 * Try open the device node.
645 */
646 RTFILE hFile;
647 int rc = RTFileOpen(&hFile, pszDevNode, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
648 if (RT_SUCCESS(rc))
649 {
650 /*
651 * Initialize the linux backend data.
652 */
653 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
654
655 RTListInit(&pDevLnx->ListFree);
656 RTListInit(&pDevLnx->ListInFlight);
657 pDevLnx->pszPath = RTStrDupN(pszPath, cchPath);
658 if (pDevLnx->pszPath)
659 {
660 rc = RTPipeCreate(&pDevLnx->hPipeWakeupR, &pDevLnx->hPipeWakeupW, 0);
661 if (RT_SUCCESS(rc))
662 {
663 pDevLnx->fUsingSysfs = fUsingSysfs;
664 pDevLnx->hFile = hFile;
665 pDevLnx->fClaimedIfsMask = 0;
666 rc = RTCritSectInit(&pDevLnx->CritSect);
667 if (RT_SUCCESS(rc))
668 {
669 LogFlow(("usbProxyLinuxOpen(%p, %s): returns successfully File=%RTfile iActiveCfg=%d\n",
670 pProxyDev, pszAddress, pDevLnx->hFile, pProxyDev->iActiveCfg));
671
672 return VINF_SUCCESS;
673 }
674 RTPipeClose(pDevLnx->hPipeWakeupR);
675 RTPipeClose(pDevLnx->hPipeWakeupW);
676 }
677 }
678 else
679 rc = VERR_NO_MEMORY;
680
681 RTFileClose(hFile);
682 }
683 else if (rc == VERR_ACCESS_DENIED)
684 rc = VERR_VUSB_USBFS_PERMISSION;
685
686 Log(("usbProxyLinuxOpen(%p, %s) failed, rc=%Rrc!\n", pProxyDev, pszAddress, rc));
687 return rc;
688}
689
690
691/**
692 * Claims all the interfaces and figures out the
693 * current configuration.
694 *
695 * @returns VINF_SUCCESS.
696 * @param pProxyDev The proxy device.
697 */
698static DECLCALLBACK(int) usbProxyLinuxInit(PUSBPROXYDEV pProxyDev)
699{
700 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
701
702 /*
703 * Brute force rulez.
704 * usbProxyLinuxSetConnected check for masked interfaces.
705 */
706 unsigned iIf;
707 for (iIf = 0; iIf < 256; iIf++)
708 usbProxyLinuxSetConnected(pProxyDev, iIf, false, true);
709
710 /*
711 * Determine the active configuration.
712 *
713 * If there isn't any active configuration, we will get EHOSTUNREACH (113) errors
714 * when trying to read the device descriptors in usbProxyDevCreate. So, we'll make
715 * the first one active (usually 1) then.
716 */
717 pProxyDev->cIgnoreSetConfigs = 1;
718 int iFirstCfg;
719 pProxyDev->iActiveCfg = usbProxyLinuxFindActiveConfig(pProxyDev, pDevLnx->pszPath, &iFirstCfg);
720 if (pProxyDev->iActiveCfg == -1)
721 {
722 usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_SETCONFIGURATION, &iFirstCfg, false, UINT32_MAX);
723 pProxyDev->iActiveCfg = usbProxyLinuxFindActiveConfig(pProxyDev, pDevLnx->pszPath, NULL);
724 Log(("usbProxyLinuxInit: No active config! Tried to set %d: iActiveCfg=%d\n", iFirstCfg, pProxyDev->iActiveCfg));
725 }
726 else
727 Log(("usbProxyLinuxInit(%p): iActiveCfg=%d\n", pProxyDev, pProxyDev->iActiveCfg));
728 return VINF_SUCCESS;
729}
730
731
732/**
733 * Closes the proxy device.
734 */
735static DECLCALLBACK(void) usbProxyLinuxClose(PUSBPROXYDEV pProxyDev)
736{
737 LogFlow(("usbProxyLinuxClose: pProxyDev=%s\n", usbProxyGetName(pProxyDev)));
738 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
739 AssertPtrReturnVoid(pDevLnx);
740
741 /*
742 * Try put the device in a state which linux can cope with before we release it.
743 * Resetting it would be a nice start, although we must remember
744 * that it might have been disconnected...
745 *
746 * Don't reset if we're masking interfaces or if construction failed.
747 */
748 if (pProxyDev->fInited)
749 {
750 /* ASSUMES: thread == EMT */
751 if ( pProxyDev->fMaskedIfs
752 || !usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_RESET, NULL, false, 10))
753 {
754 /* Connect drivers. */
755 unsigned iIf;
756 for (iIf = 0; iIf < 256; iIf++)
757 usbProxyLinuxSetConnected(pProxyDev, iIf, true, true);
758 Log(("USB: Successfully reset device pProxyDev=%s\n", usbProxyGetName(pProxyDev)));
759 }
760 else if (errno != ENODEV)
761 LogRel(("USB: Reset failed, errno=%d, pProxyDev=%s.\n", errno, usbProxyGetName(pProxyDev)));
762 else /* This will happen if device was detached. */
763 Log(("USB: Reset failed, errno=%d (ENODEV), pProxyDev=%s.\n", errno, usbProxyGetName(pProxyDev)));
764 }
765
766 /*
767 * Now we can free all the resources and close the device.
768 */
769 RTCritSectDelete(&pDevLnx->CritSect);
770
771 PUSBPROXYURBLNX pUrbLnx;
772 PUSBPROXYURBLNX pUrbLnxNext;
773 RTListForEachSafe(&pDevLnx->ListInFlight, pUrbLnx, pUrbLnxNext, USBPROXYURBLNX, NodeList)
774 {
775 RTListNodeRemove(&pUrbLnx->NodeList);
776
777 if ( usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pUrbLnx->KUrb, false, UINT32_MAX)
778 && errno != ENODEV
779 && errno != ENOENT)
780 AssertMsgFailed(("errno=%d\n", errno));
781
782 if (pUrbLnx->pSplitHead)
783 {
784 PUSBPROXYURBLNX pCur = pUrbLnx->pSplitNext;
785 while (pCur)
786 {
787 PUSBPROXYURBLNX pFree = pCur;
788 pCur = pFree->pSplitNext;
789 if ( !pFree->fSplitElementReaped
790 && usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pFree->KUrb, false, UINT32_MAX)
791 && errno != ENODEV
792 && errno != ENOENT)
793 AssertMsgFailed(("errno=%d\n", errno));
794 RTMemFree(pFree);
795 }
796 }
797 else
798 Assert(!pUrbLnx->pSplitNext);
799 RTMemFree(pUrbLnx);
800 }
801
802 RTListForEachSafe(&pDevLnx->ListFree, pUrbLnx, pUrbLnxNext, USBPROXYURBLNX, NodeList)
803 {
804 RTListNodeRemove(&pUrbLnx->NodeList);
805 RTMemFree(pUrbLnx);
806 }
807
808 RTFileClose(pDevLnx->hFile);
809 pDevLnx->hFile = NIL_RTFILE;
810
811 RTPipeClose(pDevLnx->hPipeWakeupR);
812 RTPipeClose(pDevLnx->hPipeWakeupW);
813
814 RTStrFree(pDevLnx->pszPath);
815
816 LogFlow(("usbProxyLinuxClose: returns\n"));
817}
818
819
820/** @interface_method_impl{USBPROXYBACK,pfnReset} */
821static DECLCALLBACK(int) usbProxyLinuxReset(PUSBPROXYDEV pProxyDev, bool fResetOnLinux)
822{
823 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
824 RT_NOREF(fResetOnLinux);
825 Assert(!pProxyDev->fMaskedIfs);
826 LogFlow(("usbProxyLinuxReset: pProxyDev=%s\n", usbProxyGetName(pProxyDev)));
827
828 uint32_t fActiveIfsMask = pDevLnx->fClaimedIfsMask;
829 unsigned i;
830
831 /*
832 * Before reset, release claimed interfaces. This less than obvious move
833 * prevents Linux from rebinding in-kernel drivers to the device after reset.
834 */
835 for (i = 0; i < (sizeof(fActiveIfsMask) * 8); ++i)
836 {
837 if (fActiveIfsMask & RT_BIT(i))
838 {
839 usbProxyLinuxReleaseInterface(pProxyDev, i);
840 }
841 }
842
843 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_RESET, NULL, false, 10))
844 {
845 int rc = errno;
846 LogRel(("usb-linux: Reset failed, rc=%Rrc errno=%d.\n", RTErrConvertFromErrno(rc), rc));
847 pProxyDev->iActiveCfg = -1;
848 return RTErrConvertFromErrno(rc);
849 }
850
851 /*
852 * Now reclaim previously claimed interfaces. If that doesn't work, let's hope
853 * the guest/VUSB can recover from that. Can happen if reset changes configuration.
854 */
855 for (i = 0; i < (sizeof(fActiveIfsMask) * 8); ++i)
856 {
857 if (fActiveIfsMask & RT_BIT(i))
858 {
859 usbProxyLinuxClaimInterface(pProxyDev, i);
860 }
861 }
862
863 /* find the active config - damn annoying. */
864 pProxyDev->iActiveCfg = usbProxyLinuxFindActiveConfig(pProxyDev, pDevLnx->pszPath, NULL);
865 LogFlow(("usbProxyLinuxReset: returns successfully iActiveCfg=%d\n", pProxyDev->iActiveCfg));
866
867 pProxyDev->cIgnoreSetConfigs = 2;
868 return VINF_SUCCESS;
869}
870
871
872/**
873 * SET_CONFIGURATION.
874 *
875 * The caller makes sure that it's not called first time after open or reset
876 * with the active interface.
877 *
878 * @returns success indicator.
879 * @param pProxyDev The device instance data.
880 * @param iCfg The configuration to set.
881 */
882static DECLCALLBACK(int) usbProxyLinuxSetConfig(PUSBPROXYDEV pProxyDev, int iCfg)
883{
884 LogFlow(("usbProxyLinuxSetConfig: pProxyDev=%s cfg=%#x\n",
885 usbProxyGetName(pProxyDev), iCfg));
886
887 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_SETCONFIGURATION, &iCfg, true, UINT32_MAX))
888 {
889 Log(("usb-linux: Set configuration. errno=%d\n", errno));
890 return RTErrConvertFromErrno(errno);
891 }
892 return VINF_SUCCESS;
893}
894
895
896/**
897 * Claims an interface.
898 * @returns success indicator.
899 */
900static DECLCALLBACK(int) usbProxyLinuxClaimInterface(PUSBPROXYDEV pProxyDev, int iIf)
901{
902 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
903
904 LogFlow(("usbProxyLinuxClaimInterface: pProxyDev=%s ifnum=%#x\n", usbProxyGetName(pProxyDev), iIf));
905 usbProxyLinuxSetConnected(pProxyDev, iIf, false, false);
906
907 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_CLAIMINTERFACE, &iIf, true, UINT32_MAX))
908 {
909 pDevLnx->fClaimedIfsMask &= ~RT_BIT(iIf);
910 LogRel(("usb-linux: Claim interface. errno=%d pProxyDev=%s\n", errno, usbProxyGetName(pProxyDev)));
911 return RTErrConvertFromErrno(errno);
912 }
913 pDevLnx->fClaimedIfsMask |= RT_BIT(iIf);
914 return VINF_SUCCESS;
915}
916
917
918/**
919 * Releases an interface.
920 * @returns success indicator.
921 */
922static DECLCALLBACK(int) usbProxyLinuxReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf)
923{
924 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
925
926 LogFlow(("usbProxyLinuxReleaseInterface: pProxyDev=%s ifnum=%#x\n", usbProxyGetName(pProxyDev), iIf));
927
928 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_RELEASEINTERFACE, &iIf, true, UINT32_MAX))
929 {
930 LogRel(("usb-linux: Release interface, errno=%d. pProxyDev=%s\n", errno, usbProxyGetName(pProxyDev)));
931 return RTErrConvertFromErrno(errno);
932 }
933 pDevLnx->fClaimedIfsMask &= ~RT_BIT(iIf);
934 return VINF_SUCCESS;
935}
936
937
938/**
939 * SET_INTERFACE.
940 *
941 * @returns success indicator.
942 */
943static DECLCALLBACK(int) usbProxyLinuxSetInterface(PUSBPROXYDEV pProxyDev, int iIf, int iAlt)
944{
945 struct usbdevfs_setinterface SetIf;
946 LogFlow(("usbProxyLinuxSetInterface: pProxyDev=%p iIf=%#x iAlt=%#x\n", pProxyDev, iIf, iAlt));
947
948 SetIf.interface = iIf;
949 SetIf.altsetting = iAlt;
950 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_SETINTERFACE, &SetIf, true, UINT32_MAX))
951 {
952 Log(("usb-linux: Set interface, errno=%d. pProxyDev=%s\n", errno, usbProxyGetName(pProxyDev)));
953 return RTErrConvertFromErrno(errno);
954 }
955 return VINF_SUCCESS;
956}
957
958
959/**
960 * Clears the halted endpoint 'EndPt'.
961 */
962static DECLCALLBACK(int) usbProxyLinuxClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int EndPt)
963{
964 LogFlow(("usbProxyLinuxClearHaltedEp: pProxyDev=%s EndPt=%u\n", usbProxyGetName(pProxyDev), EndPt));
965
966 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_CLEAR_HALT, &EndPt, true, UINT32_MAX))
967 {
968 /*
969 * Unfortunately this doesn't work on control pipes.
970 * Windows doing this on the default endpoint and possibly other pipes too,
971 * so we'll feign success for ENOENT errors.
972 */
973 if (errno == ENOENT)
974 {
975 Log(("usb-linux: clear_halted_ep failed errno=%d. pProxyDev=%s ep=%d - IGNORED\n",
976 errno, usbProxyGetName(pProxyDev), EndPt));
977 return VINF_SUCCESS;
978 }
979 Log(("usb-linux: clear_halted_ep failed errno=%d. pProxyDev=%s ep=%d\n",
980 errno, usbProxyGetName(pProxyDev), EndPt));
981 return RTErrConvertFromErrno(errno);
982 }
983 return VINF_SUCCESS;
984}
985
986
987/**
988 * Setup packet byte-swapping routines.
989 */
990static void usbProxyLinuxUrbSwapSetup(PVUSBSETUP pSetup)
991{
992 pSetup->wValue = RT_H2LE_U16(pSetup->wValue);
993 pSetup->wIndex = RT_H2LE_U16(pSetup->wIndex);
994 pSetup->wLength = RT_H2LE_U16(pSetup->wLength);
995}
996
997
998/**
999 * Clean up after a failed URB submit.
1000 */
1001static void usbProxyLinuxCleanupFailedSubmit(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pUrbLnx, PUSBPROXYURBLNX pCur,
1002 PVUSBURB pUrb, bool *pfUnplugged)
1003{
1004 if (pUrb->enmType == VUSBXFERTYPE_MSG)
1005 usbProxyLinuxUrbSwapSetup((PVUSBSETUP)pUrb->abData);
1006
1007 /* discard and reap later (walking with pUrbLnx). */
1008 if (pUrbLnx != pCur)
1009 {
1010 for (;;)
1011 {
1012 pUrbLnx->fCanceledBySubmit = true;
1013 pUrbLnx->KUrb.usercontext = NULL;
1014 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pUrbLnx->KUrb, false, UINT32_MAX))
1015 {
1016 if (errno == ENODEV)
1017 *pfUnplugged = true;
1018 else if (errno == ENOENT)
1019 pUrbLnx->fSplitElementReaped = true;
1020 else
1021 LogRel(("USB: Failed to discard %p! errno=%d (pUrb=%p)\n", pUrbLnx->KUrb.usercontext, errno, pUrb)); /* serious! */
1022 }
1023 if (pUrbLnx->pSplitNext == pCur)
1024 {
1025 pUrbLnx->pSplitNext = NULL;
1026 break;
1027 }
1028 pUrbLnx = pUrbLnx->pSplitNext; Assert(pUrbLnx);
1029 }
1030 }
1031
1032 /* free the unsubmitted ones. */
1033 while (pCur)
1034 {
1035 PUSBPROXYURBLNX pFree = pCur;
1036 pCur = pCur->pSplitNext;
1037 usbProxyLinuxUrbFree(pProxyDev, pFree);
1038 }
1039
1040 /* send unplug event if we failed with ENODEV originally. */
1041 if (*pfUnplugged)
1042 usbProxLinuxUrbUnplugged(pProxyDev);
1043}
1044
1045/**
1046 * Submit one URB through the usbfs IOCTL interface, with
1047 * retries
1048 *
1049 * @returns VBox status code.
1050 */
1051static int usbProxyLinuxSubmitURB(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pCur, PVUSBURB pUrb, bool *pfUnplugged)
1052{
1053 RT_NOREF(pUrb);
1054 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
1055 unsigned cTries = 0;
1056
1057 while (ioctl(RTFileToNative(pDevLnx->hFile), USBDEVFS_SUBMITURB, &pCur->KUrb))
1058 {
1059 if (errno == EINTR)
1060 continue;
1061 if (errno == ENODEV)
1062 {
1063 Log(("usbProxyLinuxSubmitURB: ENODEV -> unplugged. pProxyDev=%s\n", usbProxyGetName(pProxyDev)));
1064 *pfUnplugged = true;
1065 return RTErrConvertFromErrno(errno);
1066 }
1067
1068 Log(("usb-linux: Submit URB %p -> %d!!! type=%d ep=%#x buffer_length=%#x cTries=%d\n",
1069 pUrb, errno, pCur->KUrb.type, pCur->KUrb.endpoint, pCur->KUrb.buffer_length, cTries));
1070 if (errno != EBUSY && ++cTries < 3) /* this doesn't work for the floppy :/ */
1071 continue;
1072
1073 return RTErrConvertFromErrno(errno);
1074 }
1075 return VINF_SUCCESS;
1076}
1077
1078/** The split size. 16K in known Linux kernel versions. */
1079#define SPLIT_SIZE 0x4000
1080
1081/**
1082 * Create a URB fragment of up to SPLIT_SIZE size and hook it
1083 * into the list of fragments.
1084 *
1085 * @returns pointer to newly allocated URB fragment or NULL.
1086 */
1087static PUSBPROXYURBLNX usbProxyLinuxSplitURBFragment(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pHead, PUSBPROXYURBLNX pCur)
1088{
1089 PUSBPROXYURBLNX pNew;
1090 uint32_t cbLeft = pCur->cbSplitRemaining;
1091 uint8_t *pb = (uint8_t *)pCur->KUrb.buffer;
1092
1093 LogFlowFunc(("pProxyDev=%p pHead=%p pCur=%p\n", pProxyDev, pHead, pCur));
1094
1095 Assert(cbLeft != 0);
1096 pNew = pCur->pSplitNext = usbProxyLinuxUrbAlloc(pProxyDev, pHead);
1097 if (!pNew)
1098 {
1099 usbProxyLinuxUrbFreeSplitList(pProxyDev, pHead);
1100 return NULL;
1101 }
1102 Assert(pNew->pSplitHead == pHead);
1103 Assert(pNew->pSplitNext == NULL);
1104
1105 pNew->KUrb = pHead->KUrb;
1106 pNew->KUrb.buffer = pb + pCur->KUrb.buffer_length;
1107 pNew->KUrb.buffer_length = RT_MIN(cbLeft, SPLIT_SIZE);
1108 pNew->KUrb.actual_length = 0;
1109
1110 cbLeft -= pNew->KUrb.buffer_length;
1111 Assert(cbLeft < INT32_MAX);
1112 pNew->cbSplitRemaining = cbLeft;
1113 LogFlowFunc(("returns pNew=%p\n", pNew));
1114 return pNew;
1115}
1116
1117/**
1118 * Try splitting up a VUSB URB into smaller URBs which the
1119 * linux kernel (usbfs) can deal with.
1120 *
1121 * NB: For ShortOK reads things get a little tricky - we don't
1122 * know how much data is going to arrive and not all the
1123 * fragment URBs might be filled. We can only safely set up one
1124 * URB at a time -> worse performance but correct behaviour.
1125 *
1126 * @returns VBox status code.
1127 * @param pProxyDev The proxy device.
1128 * @param pUrbLnx The linux URB which was rejected because of being too big.
1129 * @param pUrb The VUSB URB.
1130 */
1131static int usbProxyLinuxUrbQueueSplit(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pUrbLnx, PVUSBURB pUrb)
1132{
1133 /*
1134 * Split it up into SPLIT_SIZE sized blocks.
1135 */
1136 const unsigned cKUrbs = (pUrb->cbData + SPLIT_SIZE - 1) / SPLIT_SIZE;
1137 LogFlow(("usbProxyLinuxUrbQueueSplit: pUrb=%p cKUrbs=%d cbData=%d\n", pUrb, cKUrbs, pUrb->cbData));
1138
1139 uint32_t cbLeft = pUrb->cbData;
1140 uint8_t *pb = &pUrb->abData[0];
1141
1142 /* the first one (already allocated) */
1143 switch (pUrb->enmType)
1144 {
1145 default: /* shut up gcc */
1146 case VUSBXFERTYPE_BULK: pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_BULK; break;
1147 case VUSBXFERTYPE_INTR: pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_INTERRUPT; break;
1148 case VUSBXFERTYPE_MSG: pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_CONTROL; break;
1149 case VUSBXFERTYPE_ISOC:
1150 AssertMsgFailed(("We can't split isochronous URBs!\n"));
1151 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx);
1152 return VERR_INVALID_PARAMETER; /** @todo Better status code. */
1153 }
1154 pUrbLnx->KUrb.endpoint = pUrb->EndPt;
1155 if (pUrb->enmDir == VUSBDIRECTION_IN)
1156 pUrbLnx->KUrb.endpoint |= 0x80;
1157 pUrbLnx->KUrb.flags = 0;
1158 if (pUrb->enmDir == VUSBDIRECTION_IN && pUrb->fShortNotOk)
1159 pUrbLnx->KUrb.flags |= USBDEVFS_URB_SHORT_NOT_OK;
1160 pUrbLnx->KUrb.status = 0;
1161 pUrbLnx->KUrb.buffer = pb;
1162 pUrbLnx->KUrb.buffer_length = RT_MIN(cbLeft, SPLIT_SIZE);
1163 pUrbLnx->KUrb.actual_length = 0;
1164 pUrbLnx->KUrb.start_frame = 0;
1165 pUrbLnx->KUrb.number_of_packets = 0;
1166 pUrbLnx->KUrb.error_count = 0;
1167 pUrbLnx->KUrb.signr = 0;
1168 pUrbLnx->KUrb.usercontext = pUrb;
1169 pUrbLnx->pSplitHead = pUrbLnx;
1170 pUrbLnx->pSplitNext = NULL;
1171
1172 PUSBPROXYURBLNX pCur = pUrbLnx;
1173
1174 cbLeft -= pUrbLnx->KUrb.buffer_length;
1175 pUrbLnx->cbSplitRemaining = cbLeft;
1176
1177 int rc = VINF_SUCCESS;
1178 bool fUnplugged = false;
1179 if (pUrb->enmDir == VUSBDIRECTION_IN && !pUrb->fShortNotOk)
1180 {
1181 /* Subsequent fragments will be queued only after the previous fragment is reaped
1182 * and only if necessary.
1183 */
1184 Log(("usb-linux: Large ShortOK read, only queuing first fragment.\n"));
1185 Assert(pUrbLnx->cbSplitRemaining > 0 && pUrbLnx->cbSplitRemaining < 256 * _1K);
1186 rc = usbProxyLinuxSubmitURB(pProxyDev, pUrbLnx, pUrb, &fUnplugged);
1187 }
1188 else
1189 {
1190 /* the rest. */
1191 unsigned i;
1192 for (i = 1; i < cKUrbs; i++)
1193 {
1194 pCur = usbProxyLinuxSplitURBFragment(pProxyDev, pUrbLnx, pCur);
1195 if (!pCur)
1196 return VERR_NO_MEMORY;
1197 }
1198 Assert(pCur->cbSplitRemaining == 0);
1199
1200 /* Submit the blocks. */
1201 pCur = pUrbLnx;
1202 for (i = 0; i < cKUrbs; i++, pCur = pCur->pSplitNext)
1203 {
1204 rc = usbProxyLinuxSubmitURB(pProxyDev, pCur, pUrb, &fUnplugged);
1205 if (RT_FAILURE(rc))
1206 break;
1207 }
1208 }
1209
1210 if (RT_SUCCESS(rc))
1211 {
1212 pUrb->Dev.pvPrivate = pUrbLnx;
1213 usbProxyLinuxUrbLinkInFlight(USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX), pUrbLnx);
1214 LogFlow(("usbProxyLinuxUrbQueueSplit: ok\n"));
1215 return VINF_SUCCESS;
1216 }
1217
1218 usbProxyLinuxCleanupFailedSubmit(pProxyDev, pUrbLnx, pCur, pUrb, &fUnplugged);
1219 return rc;
1220}
1221
1222
1223/**
1224 * @interface_method_impl{USBPROXYBACK,pfnUrbQueue}
1225 */
1226static DECLCALLBACK(int) usbProxyLinuxUrbQueue(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb)
1227{
1228 int rc = VINF_SUCCESS;
1229 unsigned cTries;
1230 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
1231 LogFlow(("usbProxyLinuxUrbQueue: pProxyDev=%s pUrb=%p EndPt=%d cbData=%d\n",
1232 usbProxyGetName(pProxyDev), pUrb, pUrb->EndPt, pUrb->cbData));
1233
1234 /*
1235 * Allocate a linux urb.
1236 */
1237 PUSBPROXYURBLNX pUrbLnx = usbProxyLinuxUrbAlloc(pProxyDev, NULL);
1238 if (!pUrbLnx)
1239 return VERR_NO_MEMORY;
1240
1241 pUrbLnx->KUrb.endpoint = pUrb->EndPt | (pUrb->enmDir == VUSBDIRECTION_IN ? 0x80 : 0);
1242 pUrbLnx->KUrb.status = 0;
1243 pUrbLnx->KUrb.flags = 0;
1244 if (pUrb->enmDir == VUSBDIRECTION_IN && pUrb->fShortNotOk)
1245 pUrbLnx->KUrb.flags |= USBDEVFS_URB_SHORT_NOT_OK;
1246 pUrbLnx->KUrb.buffer = pUrb->abData;
1247 pUrbLnx->KUrb.buffer_length = pUrb->cbData;
1248 pUrbLnx->KUrb.actual_length = 0;
1249 pUrbLnx->KUrb.start_frame = 0;
1250 pUrbLnx->KUrb.number_of_packets = 0;
1251 pUrbLnx->KUrb.error_count = 0;
1252 pUrbLnx->KUrb.signr = 0;
1253 pUrbLnx->KUrb.usercontext = pUrb;
1254
1255 switch (pUrb->enmType)
1256 {
1257 case VUSBXFERTYPE_MSG:
1258 pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_CONTROL;
1259 if (pUrb->cbData < sizeof(VUSBSETUP))
1260 {
1261 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx);
1262 return VERR_BUFFER_UNDERFLOW;
1263 }
1264 usbProxyLinuxUrbSwapSetup((PVUSBSETUP)pUrb->abData);
1265 LogFlow(("usbProxyLinuxUrbQueue: message\n"));
1266 break;
1267 case VUSBXFERTYPE_BULK:
1268 pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_BULK;
1269 break;
1270 case VUSBXFERTYPE_ISOC:
1271 pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_ISO;
1272 pUrbLnx->KUrb.flags |= USBDEVFS_URB_ISO_ASAP;
1273 pUrbLnx->KUrb.number_of_packets = pUrb->cIsocPkts;
1274 unsigned i;
1275 for (i = 0; i < pUrb->cIsocPkts; i++)
1276 {
1277#if RT_GNUC_PREREQ(4, 6)
1278# pragma GCC diagnostic push
1279# pragma GCC diagnostic ignored "-Warray-bounds"
1280#endif
1281 pUrbLnx->KUrb.iso_frame_desc[i].length = pUrb->aIsocPkts[i].cb;
1282 pUrbLnx->KUrb.iso_frame_desc[i].actual_length = 0;
1283 pUrbLnx->KUrb.iso_frame_desc[i].status = 0x7fff;
1284#if RT_GNUC_PREREQ(4, 6)
1285# pragma GCC diagnostic pop
1286#endif
1287 }
1288 break;
1289 case VUSBXFERTYPE_INTR:
1290 pUrbLnx->KUrb.type = USBDEVFS_URB_TYPE_INTERRUPT;
1291 break;
1292 default:
1293 rc = VERR_INVALID_PARAMETER; /** @todo better status code. */
1294 }
1295
1296 /*
1297 * We have to serialize access by using the critial section here because this
1298 * thread might be suspended after submitting the URB but before linking it into
1299 * the in flight list. This would get us in trouble when reaping the URB on another
1300 * thread while it isn't in the in flight list.
1301 *
1302 * Linking the URB into the list before submitting it like it was done in the past is not
1303 * possible either because submitting the URB might fail here because the device gets
1304 * detached. The reaper thread gets this event too and might race this thread before we
1305 * can unlink the URB from the active list and the common code might end up freeing
1306 * the common URB structure twice.
1307 */
1308 RTCritSectEnter(&pDevLnx->CritSect);
1309 /*
1310 * Submit it.
1311 */
1312 cTries = 0;
1313 while (ioctl(RTFileToNative(pDevLnx->hFile), USBDEVFS_SUBMITURB, &pUrbLnx->KUrb))
1314 {
1315 if (errno == EINTR)
1316 continue;
1317 if (errno == ENODEV)
1318 {
1319 rc = RTErrConvertFromErrno(errno);
1320 Log(("usbProxyLinuxUrbQueue: ENODEV -> unplugged. pProxyDev=%s\n", usbProxyGetName(pProxyDev)));
1321 if (pUrb->enmType == VUSBXFERTYPE_MSG)
1322 usbProxyLinuxUrbSwapSetup((PVUSBSETUP)pUrb->abData);
1323
1324 RTCritSectLeave(&pDevLnx->CritSect);
1325 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx);
1326 usbProxLinuxUrbUnplugged(pProxyDev);
1327 return rc;
1328 }
1329
1330 /*
1331 * usbfs has or used to have a low buffer limit (16KB) in order to prevent
1332 * processes wasting kmalloc'ed memory. It will return EINVAL if break that
1333 * limit, and we'll have to split the VUSB URB up into multiple linux URBs.
1334 *
1335 * Since this is a limit which is subject to change, we cannot check for it
1336 * before submitting the URB. We just have to try and fail.
1337 */
1338 if ( errno == EINVAL
1339 && pUrb->cbData >= 8*_1K)
1340 {
1341 rc = usbProxyLinuxUrbQueueSplit(pProxyDev, pUrbLnx, pUrb);
1342 RTCritSectLeave(&pDevLnx->CritSect);
1343 return rc;
1344 }
1345
1346 Log(("usb-linux: Queue URB %p -> %d!!! type=%d ep=%#x buffer_length=%#x cTries=%d\n",
1347 pUrb, errno, pUrbLnx->KUrb.type, pUrbLnx->KUrb.endpoint, pUrbLnx->KUrb.buffer_length, cTries));
1348 if (errno != EBUSY && ++cTries < 3) /* this doesn't work for the floppy :/ */
1349 continue;
1350
1351 RTCritSectLeave(&pDevLnx->CritSect);
1352 rc = RTErrConvertFromErrno(errno);
1353 if (pUrb->enmType == VUSBXFERTYPE_MSG)
1354 usbProxyLinuxUrbSwapSetup((PVUSBSETUP)pUrb->abData);
1355 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx);
1356 return rc;
1357 }
1358
1359 usbProxyLinuxUrbLinkInFlight(pDevLnx, pUrbLnx);
1360 RTCritSectLeave(&pDevLnx->CritSect);
1361
1362 LogFlow(("usbProxyLinuxUrbQueue: ok\n"));
1363 pUrb->Dev.pvPrivate = pUrbLnx;
1364 return rc;
1365}
1366
1367
1368/**
1369 * Translate the linux status to a VUSB status.
1370 *
1371 * @remarks see cc_to_error in ohci.h, uhci_map_status in uhci-q.c,
1372 * sitd_complete+itd_complete in ehci-sched.c, and qtd_copy_status in
1373 * ehci-q.c.
1374 */
1375static VUSBSTATUS vusbProxyLinuxStatusToVUsbStatus(int iStatus)
1376{
1377 switch (iStatus)
1378 {
1379 /** @todo VUSBSTATUS_NOT_ACCESSED */
1380 case -EXDEV: /* iso transfer, partial result. */
1381 case 0:
1382 return VUSBSTATUS_OK;
1383
1384 case -EILSEQ:
1385 return VUSBSTATUS_CRC;
1386
1387 case -EREMOTEIO: /* ehci and ohci uses this for underflow error. */
1388 return VUSBSTATUS_DATA_UNDERRUN;
1389 case -EOVERFLOW:
1390 return VUSBSTATUS_DATA_OVERRUN;
1391
1392 case -ETIME:
1393 case -ENODEV:
1394 return VUSBSTATUS_DNR;
1395
1396 //case -ECOMM:
1397 // return VUSBSTATUS_BUFFER_OVERRUN;
1398 //case -ENOSR:
1399 // return VUSBSTATUS_BUFFER_UNDERRUN;
1400
1401 case -EPROTO:
1402 Log(("vusbProxyLinuxStatusToVUsbStatus: DNR/EPPROTO!!\n"));
1403 return VUSBSTATUS_DNR;
1404
1405 case -EPIPE:
1406 Log(("vusbProxyLinuxStatusToVUsbStatus: STALL/EPIPE!!\n"));
1407 return VUSBSTATUS_STALL;
1408
1409 case -ESHUTDOWN:
1410 Log(("vusbProxyLinuxStatusToVUsbStatus: SHUTDOWN!!\n"));
1411 return VUSBSTATUS_STALL;
1412
1413 case -ENOENT:
1414 Log(("vusbProxyLinuxStatusToVUsbStatus: ENOENT!!\n"));
1415 return VUSBSTATUS_STALL;
1416
1417 default:
1418 Log(("vusbProxyLinuxStatusToVUsbStatus: status %d!!\n", iStatus));
1419 return VUSBSTATUS_STALL;
1420 }
1421}
1422
1423
1424/**
1425 * Get and translates the linux status to a VUSB status.
1426 */
1427static VUSBSTATUS vusbProxyLinuxUrbGetStatus(PUSBPROXYURBLNX pUrbLnx)
1428{
1429 return vusbProxyLinuxStatusToVUsbStatus(pUrbLnx->KUrb.status);
1430}
1431
1432
1433/**
1434 * Reap URBs in-flight on a device.
1435 *
1436 * @returns Pointer to a completed URB.
1437 * @returns NULL if no URB was completed.
1438 * @param pProxyDev The device.
1439 * @param cMillies Number of milliseconds to wait. Use 0 to not wait at all.
1440 */
1441static DECLCALLBACK(PVUSBURB) usbProxyLinuxUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies)
1442{
1443 PUSBPROXYURBLNX pUrbLnx = NULL;
1444 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
1445
1446 /*
1447 * Block for requested period.
1448 *
1449 * It seems to me that the path of poll() is shorter and
1450 * involves less semaphores than ioctl() on usbfs. So, we'll
1451 * do a poll regardless of whether cMillies == 0 or not.
1452 */
1453 if (cMillies)
1454 {
1455 int cMilliesWait = cMillies == RT_INDEFINITE_WAIT ? -1 : cMillies;
1456
1457 for (;;)
1458 {
1459 struct pollfd pfd[2];
1460 pfd[0].fd = RTFileToNative(pDevLnx->hFile);
1461 pfd[0].events = POLLOUT | POLLWRNORM /* completed async */
1462 | POLLERR | POLLHUP /* disconnected */;
1463 pfd[0].revents = 0;
1464
1465 pfd[1].fd = RTPipeToNative(pDevLnx->hPipeWakeupR);
1466 pfd[1].events = POLLIN | POLLHUP;
1467 pfd[1].revents = 0;
1468
1469 int rc = poll(&pfd[0], 2, cMilliesWait);
1470 Log(("usbProxyLinuxUrbReap: poll rc = %d\n", rc));
1471 if (rc >= 1)
1472 {
1473 /* If the pipe caused the return drain it. */
1474 if (pfd[1].revents & POLLIN)
1475 {
1476 uint8_t bRead;
1477 size_t cbIgnored = 0;
1478 RTPipeRead(pDevLnx->hPipeWakeupR, &bRead, 1, &cbIgnored);
1479 }
1480 break;
1481 }
1482 if (rc >= 0)
1483 return NULL;
1484
1485 if (errno != EAGAIN)
1486 {
1487 Log(("usb-linux: Reap URB - poll -> %d errno=%d pProxyDev=%s\n", rc, errno, usbProxyGetName(pProxyDev)));
1488 return NULL;
1489 }
1490 Log(("usbProxyLinuxUrbReap: poll again - weird!!!\n"));
1491 }
1492 }
1493
1494 /*
1495 * Reap URBs, non-blocking.
1496 */
1497 for (;;)
1498 {
1499 struct usbdevfs_urb *pKUrb;
1500 while (ioctl(RTFileToNative(pDevLnx->hFile), USBDEVFS_REAPURBNDELAY, &pKUrb))
1501 if (errno != EINTR)
1502 {
1503 if (errno == ENODEV)
1504 usbProxLinuxUrbUnplugged(pProxyDev);
1505 else
1506 Log(("usb-linux: Reap URB. errno=%d pProxyDev=%s\n", errno, usbProxyGetName(pProxyDev)));
1507 return NULL;
1508 }
1509 pUrbLnx = RT_FROM_MEMBER(pKUrb, USBPROXYURBLNX, KUrb);
1510
1511 /* split list: Is the entire split list done yet? */
1512 if (pUrbLnx->pSplitHead)
1513 {
1514 pUrbLnx->fSplitElementReaped = true;
1515
1516 /* for variable size URBs, we may need to queue more if the just-reaped URB was completely filled */
1517 if (pUrbLnx->cbSplitRemaining && (pKUrb->actual_length == pKUrb->buffer_length) && !pUrbLnx->pSplitNext)
1518 {
1519 bool fUnplugged = false;
1520 bool fSucceeded;
1521
1522 Assert(pUrbLnx->pSplitHead);
1523 Assert((pKUrb->endpoint & 0x80) && !(pKUrb->flags & USBDEVFS_URB_SHORT_NOT_OK));
1524 PUSBPROXYURBLNX pNew = usbProxyLinuxSplitURBFragment(pProxyDev, pUrbLnx->pSplitHead, pUrbLnx);
1525 if (!pNew)
1526 {
1527 Log(("usb-linux: Allocating URB fragment failed. errno=%d pProxyDev=%s\n", errno, usbProxyGetName(pProxyDev)));
1528 return NULL;
1529 }
1530 PVUSBURB pUrb = (PVUSBURB)pUrbLnx->KUrb.usercontext;
1531 fSucceeded = usbProxyLinuxSubmitURB(pProxyDev, pNew, pUrb, &fUnplugged);
1532 if (fUnplugged)
1533 usbProxLinuxUrbUnplugged(pProxyDev);
1534 if (!fSucceeded)
1535 return NULL;
1536 continue; /* try reaping another URB */
1537 }
1538 PUSBPROXYURBLNX pCur;
1539 for (pCur = pUrbLnx->pSplitHead; pCur; pCur = pCur->pSplitNext)
1540 if (!pCur->fSplitElementReaped)
1541 {
1542 pUrbLnx = NULL;
1543 break;
1544 }
1545 if (!pUrbLnx)
1546 continue;
1547 pUrbLnx = pUrbLnx->pSplitHead;
1548 }
1549 break;
1550 }
1551
1552 /*
1553 * Ok, we got one!
1554 */
1555 PVUSBURB pUrb = (PVUSBURB)pUrbLnx->KUrb.usercontext;
1556 if ( pUrb
1557 && !pUrbLnx->fCanceledBySubmit)
1558 {
1559 if (pUrbLnx->pSplitHead)
1560 {
1561 /* split - find the end byte and the first error status. */
1562 Assert(pUrbLnx == pUrbLnx->pSplitHead);
1563 uint8_t *pbEnd = &pUrb->abData[0];
1564 pUrb->enmStatus = VUSBSTATUS_OK;
1565 PUSBPROXYURBLNX pCur;
1566 for (pCur = pUrbLnx; pCur; pCur = pCur->pSplitNext)
1567 {
1568 if (pCur->KUrb.actual_length)
1569 pbEnd = (uint8_t *)pCur->KUrb.buffer + pCur->KUrb.actual_length;
1570 if (pUrb->enmStatus == VUSBSTATUS_OK)
1571 pUrb->enmStatus = vusbProxyLinuxUrbGetStatus(pCur);
1572 }
1573 pUrb->cbData = pbEnd - &pUrb->abData[0];
1574 usbProxyLinuxUrbUnlinkInFlight(pDevLnx, pUrbLnx);
1575 usbProxyLinuxUrbFreeSplitList(pProxyDev, pUrbLnx);
1576 }
1577 else
1578 {
1579 /* unsplit. */
1580 pUrb->enmStatus = vusbProxyLinuxUrbGetStatus(pUrbLnx);
1581 pUrb->cbData = pUrbLnx->KUrb.actual_length;
1582 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
1583 {
1584 unsigned i, off;
1585 for (i = 0, off = 0; i < pUrb->cIsocPkts; i++)
1586 {
1587#if RT_GNUC_PREREQ(4, 6)
1588# pragma GCC diagnostic push
1589# pragma GCC diagnostic ignored "-Warray-bounds"
1590#endif
1591 pUrb->aIsocPkts[i].enmStatus = vusbProxyLinuxStatusToVUsbStatus(pUrbLnx->KUrb.iso_frame_desc[i].status);
1592 Assert(pUrb->aIsocPkts[i].off == off);
1593 pUrb->aIsocPkts[i].cb = pUrbLnx->KUrb.iso_frame_desc[i].actual_length;
1594 off += pUrbLnx->KUrb.iso_frame_desc[i].length;
1595#if RT_GNUC_PREREQ(4, 6)
1596# pragma GCC diagnostic pop
1597#endif
1598 }
1599 }
1600 usbProxyLinuxUrbUnlinkInFlight(pDevLnx, pUrbLnx);
1601 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx);
1602 }
1603 pUrb->Dev.pvPrivate = NULL;
1604
1605 /* some adjustments for message transfers. */
1606 if (pUrb->enmType == VUSBXFERTYPE_MSG)
1607 {
1608 pUrb->cbData += sizeof(VUSBSETUP);
1609 usbProxyLinuxUrbSwapSetup((PVUSBSETUP)pUrb->abData);
1610 }
1611 }
1612 else
1613 {
1614 usbProxyLinuxUrbUnlinkInFlight(pDevLnx, pUrbLnx);
1615 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx);
1616 pUrb = NULL;
1617 }
1618
1619 LogFlow(("usbProxyLinuxUrbReap: pProxyDev=%s returns %p\n", usbProxyGetName(pProxyDev), pUrb));
1620 return pUrb;
1621}
1622
1623
1624/**
1625 * Cancels the URB.
1626 * The URB requires reaping, so we don't change its state.
1627 */
1628static DECLCALLBACK(int) usbProxyLinuxUrbCancel(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb)
1629{
1630 int rc = VINF_SUCCESS;
1631 PUSBPROXYURBLNX pUrbLnx = (PUSBPROXYURBLNX)pUrb->Dev.pvPrivate;
1632 if (pUrbLnx->pSplitHead)
1633 {
1634 /* split */
1635 Assert(pUrbLnx == pUrbLnx->pSplitHead);
1636 PUSBPROXYURBLNX pCur;
1637 for (pCur = pUrbLnx; pCur; pCur = pCur->pSplitNext)
1638 {
1639 if (pCur->fSplitElementReaped)
1640 continue;
1641 if ( !usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pCur->KUrb, true, UINT32_MAX)
1642 || errno == ENOENT)
1643 continue;
1644 if (errno == ENODEV)
1645 break;
1646 /** @todo Think about how to handle errors wrt. to the status code. */
1647 Log(("usb-linux: Discard URB %p failed, errno=%d. pProxyDev=%s!!! (split)\n",
1648 pUrb, errno, usbProxyGetName(pProxyDev)));
1649 }
1650 }
1651 else
1652 {
1653 /* unsplit */
1654 if ( usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pUrbLnx->KUrb, true, UINT32_MAX)
1655 && errno != ENODEV /* deal with elsewhere. */
1656 && errno != ENOENT)
1657 {
1658 Log(("usb-linux: Discard URB %p failed, errno=%d. pProxyDev=%s!!!\n",
1659 pUrb, errno, usbProxyGetName(pProxyDev)));
1660 rc = RTErrConvertFromErrno(errno);
1661 }
1662 }
1663
1664 return rc;
1665}
1666
1667
1668static DECLCALLBACK(int) usbProxyLinuxWakeup(PUSBPROXYDEV pProxyDev)
1669{
1670 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX);
1671 size_t cbIgnored;
1672
1673 LogFlowFunc(("pProxyDev=%p\n", pProxyDev));
1674
1675 return RTPipeWrite(pDevLnx->hPipeWakeupW, "", 1, &cbIgnored);
1676}
1677
1678/**
1679 * The Linux USB Proxy Backend.
1680 */
1681const USBPROXYBACK g_USBProxyDeviceHost =
1682{
1683 /* pszName */
1684 "host",
1685 /* cbBackend */
1686 sizeof(USBPROXYDEVLNX),
1687 usbProxyLinuxOpen,
1688 usbProxyLinuxInit,
1689 usbProxyLinuxClose,
1690 usbProxyLinuxReset,
1691 usbProxyLinuxSetConfig,
1692 usbProxyLinuxClaimInterface,
1693 usbProxyLinuxReleaseInterface,
1694 usbProxyLinuxSetInterface,
1695 usbProxyLinuxClearHaltedEp,
1696 usbProxyLinuxUrbQueue,
1697 usbProxyLinuxUrbCancel,
1698 usbProxyLinuxUrbReap,
1699 usbProxyLinuxWakeup,
1700 0
1701};
1702
1703
1704/*
1705 * Local Variables:
1706 * mode: c
1707 * c-file-style: "bsd"
1708 * c-basic-offset: 4
1709 * End:
1710 */
1711
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