VirtualBox

source: vbox/trunk/src/VBox/Devices/USB/freebsd/USBProxyDevice-freebsd.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.5 KB
Line 
1/* $Id: USBProxyDevice-freebsd.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * USB device proxy - the FreeBSD backend.
4 */
5
6/*
7 * Includes contributions from Hans Petter Selasky
8 *
9 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
10 *
11 * This file is part of VirtualBox base platform packages, as
12 * available from https://www.virtualbox.org.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation, in version 3 of the
17 * License.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see <https://www.gnu.org/licenses>.
26 *
27 * SPDX-License-Identifier: GPL-3.0-only
28 */
29
30
31/*********************************************************************************************************************************
32* Header Files *
33*********************************************************************************************************************************/
34#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
35#ifdef VBOX
36# include <iprt/stdint.h>
37#endif
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <sys/ioctl.h>
41#include <sys/poll.h>
42#include <stdint.h>
43#include <stdio.h>
44#include <string.h>
45#include <stdlib.h>
46#include <limits.h>
47#include <unistd.h>
48#include <fcntl.h>
49#include <errno.h>
50#include <dev/usb/usb.h>
51#include <dev/usb/usbdi.h>
52#include <dev/usb/usb_ioctl.h>
53
54#include <VBox/vmm/pdm.h>
55#include <VBox/err.h>
56#include <VBox/log.h>
57#include <VBox/vusb.h>
58#include <iprt/assert.h>
59#include <iprt/stream.h>
60#include <iprt/alloc.h>
61#include <iprt/thread.h>
62#include <iprt/time.h>
63#include <iprt/asm.h>
64#include <iprt/string.h>
65#include <iprt/file.h>
66#include <iprt/pipe.h>
67#include "../USBProxyDevice.h"
68
69/** Maximum endpoints supported. */
70#define USBFBSD_MAXENDPOINTS 127
71#define USBFBSD_MAXFRAMES 56
72
73/** This really needs to be defined in vusb.h! */
74#ifndef VUSB_DIR_TO_DEV
75# define VUSB_DIR_TO_DEV 0x00
76#endif
77
78
79/*********************************************************************************************************************************
80* Structures and Typedefs *
81*********************************************************************************************************************************/
82typedef struct USBENDPOINTFBSD
83{
84 /** Flag whether it is opened. */
85 bool fOpen;
86 /** Flag whether it is cancelling. */
87 bool fCancelling;
88 /** Buffer pointers. */
89 void *apvData[USBFBSD_MAXFRAMES];
90 /** Buffer lengths. */
91 uint32_t acbData[USBFBSD_MAXFRAMES];
92 /** Initial buffer length. */
93 uint32_t cbData0;
94 /** Pointer to the URB. */
95 PVUSBURB pUrb;
96 /** Copy of endpoint number. */
97 unsigned iEpNum;
98 /** Maximum transfer length. */
99 unsigned cMaxIo;
100 /** Maximum frame count. */
101 unsigned cMaxFrames;
102} USBENDPOINTFBSD, *PUSBENDPOINTFBSD;
103
104/**
105 * Data for the FreeBSD usb proxy backend.
106 */
107typedef struct USBPROXYDEVFBSD
108{
109 /** The open file. */
110 RTFILE hFile;
111 /** Flag whether an URB is cancelling. */
112 bool fCancelling;
113 /** Flag whether initialised or not */
114 bool fInit;
115 /** Pipe handle for waking up - writing end. */
116 RTPIPE hPipeWakeupW;
117 /** Pipe handle for waking up - reading end. */
118 RTPIPE hPipeWakeupR;
119 /** Software endpoint structures */
120 USBENDPOINTFBSD aSwEndpoint[USBFBSD_MAXENDPOINTS];
121 /** Kernel endpoint structures */
122 struct usb_fs_endpoint aHwEndpoint[USBFBSD_MAXENDPOINTS];
123} USBPROXYDEVFBSD, *PUSBPROXYDEVFBSD;
124
125
126/*********************************************************************************************************************************
127* Internal Functions *
128*********************************************************************************************************************************/
129static int usbProxyFreeBSDEndpointClose(PUSBPROXYDEV pProxyDev, int Endpoint);
130
131/**
132 * Wrapper for the ioctl call.
133 *
134 * This wrapper will repeat the call if we get an EINTR or EAGAIN. It can also
135 * handle ENODEV (detached device) errors.
136 *
137 * @returns whatever ioctl returns.
138 * @param pProxyDev The proxy device.
139 * @param iCmd The ioctl command / function.
140 * @param pvArg The ioctl argument / data.
141 * @param fHandleNoDev Whether to handle ENXIO.
142 * @internal
143 */
144static int usbProxyFreeBSDDoIoCtl(PUSBPROXYDEV pProxyDev, unsigned long iCmd,
145 void *pvArg, bool fHandleNoDev)
146{
147 int rc = VINF_SUCCESS;
148 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
149
150 LogFlow(("usbProxyFreeBSDDoIoCtl: iCmd=%#x\n", iCmd));
151
152 do
153 {
154 rc = ioctl(RTFileToNative(pDevFBSD->hFile), iCmd, pvArg);
155 if (rc >= 0)
156 return VINF_SUCCESS;
157 } while (errno == EINTR);
158
159 if (errno == ENXIO && fHandleNoDev)
160 {
161 Log(("usbProxyFreeBSDDoIoCtl: ENXIO -> unplugged. pProxyDev=%s\n",
162 pProxyDev->pUsbIns->pszName));
163 errno = ENODEV;
164 }
165 else if (errno != EAGAIN)
166 {
167 LogFlow(("usbProxyFreeBSDDoIoCtl: Returned %d. pProxyDev=%s\n",
168 errno, pProxyDev->pUsbIns->pszName));
169 }
170 return RTErrConvertFromErrno(errno);
171}
172
173/**
174 * Init USB subsystem.
175 */
176static int usbProxyFreeBSDFsInit(PUSBPROXYDEV pProxyDev)
177{
178 struct usb_fs_init UsbFsInit;
179 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
180 int rc;
181
182 LogFlow(("usbProxyFreeBSDFsInit: pProxyDev=%p\n", (void *)pProxyDev));
183
184 /* Sanity check */
185 AssertPtrReturn(pDevFBSD, VERR_INVALID_PARAMETER);
186
187 if (pDevFBSD->fInit == true)
188 return VINF_SUCCESS;
189
190 /* Zero default */
191 memset(&UsbFsInit, 0, sizeof(UsbFsInit));
192
193 UsbFsInit.pEndpoints = pDevFBSD->aHwEndpoint;
194 UsbFsInit.ep_index_max = USBFBSD_MAXENDPOINTS;
195
196 /* Init USB subsystem */
197 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_INIT, &UsbFsInit, false);
198 if (RT_SUCCESS(rc))
199 pDevFBSD->fInit = true;
200
201 return rc;
202}
203
204/**
205 * Uninit USB subsystem.
206 */
207static int usbProxyFreeBSDFsUnInit(PUSBPROXYDEV pProxyDev)
208{
209 struct usb_fs_uninit UsbFsUninit;
210 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
211 int rc;
212
213 LogFlow(("usbProxyFreeBSDFsUnInit: ProxyDev=%p\n", (void *)pProxyDev));
214
215 /* Sanity check */
216 AssertPtrReturn(pDevFBSD, VERR_INVALID_PARAMETER);
217
218 if (pDevFBSD->fInit != true)
219 return VINF_SUCCESS;
220
221 /* Close any open endpoints. */
222 for (unsigned n = 0; n != USBFBSD_MAXENDPOINTS; n++)
223 usbProxyFreeBSDEndpointClose(pProxyDev, n);
224
225 /* Zero default */
226 memset(&UsbFsUninit, 0, sizeof(UsbFsUninit));
227
228 /* Uninit USB subsystem */
229 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_UNINIT, &UsbFsUninit, false);
230 if (RT_SUCCESS(rc))
231 pDevFBSD->fInit = false;
232
233 return rc;
234}
235
236/**
237 * Setup a USB request packet.
238 */
239static void usbProxyFreeBSDSetupReq(struct usb_device_request *pSetupData,
240 uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue,
241 uint16_t wIndex, uint16_t wLength)
242{
243 LogFlow(("usbProxyFreeBSDSetupReq: pSetupData=%p bmRequestType=%x "
244 "bRequest=%x wValue=%x wIndex=%x wLength=%x\n", (void *)pSetupData,
245 bmRequestType, bRequest, wValue, wIndex, wLength));
246
247 pSetupData->bmRequestType = bmRequestType;
248 pSetupData->bRequest = bRequest;
249
250 /* Handle endianess here. Currently no swapping is needed. */
251 pSetupData->wValue[0] = wValue & 0xff;
252 pSetupData->wValue[1] = (wValue >> 8) & 0xff;
253 pSetupData->wIndex[0] = wIndex & 0xff;
254 pSetupData->wIndex[1] = (wIndex >> 8) & 0xff;
255 pSetupData->wLength[0] = wLength & 0xff;
256 pSetupData->wLength[1] = (wLength >> 8) & 0xff;
257}
258
259static int usbProxyFreeBSDEndpointOpen(PUSBPROXYDEV pProxyDev, int Endpoint, bool fIsoc, int index)
260{
261 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
262 PUSBENDPOINTFBSD pEndpointFBSD = NULL; /* shut up gcc */
263 struct usb_fs_endpoint *pXferEndpoint;
264 struct usb_fs_open UsbFsOpen;
265 int rc;
266
267 LogFlow(("usbProxyFreeBSDEndpointOpen: pProxyDev=%p Endpoint=%d\n",
268 (void *)pProxyDev, Endpoint));
269
270 for (; index < USBFBSD_MAXENDPOINTS; index++)
271 {
272 pEndpointFBSD = &pDevFBSD->aSwEndpoint[index];
273 if (pEndpointFBSD->fCancelling)
274 continue;
275 if ( pEndpointFBSD->fOpen
276 && !pEndpointFBSD->pUrb
277 && (int)pEndpointFBSD->iEpNum == Endpoint)
278 return index;
279 }
280
281 if (index == USBFBSD_MAXENDPOINTS)
282 {
283 for (index = 0; index != USBFBSD_MAXENDPOINTS; index++)
284 {
285 pEndpointFBSD = &pDevFBSD->aSwEndpoint[index];
286 if (pEndpointFBSD->fCancelling)
287 continue;
288 if (!pEndpointFBSD->fOpen)
289 break;
290 }
291 if (index == USBFBSD_MAXENDPOINTS)
292 return -1;
293 }
294 /* set ppBuffer and pLength */
295
296 pXferEndpoint = &pDevFBSD->aHwEndpoint[index];
297 pXferEndpoint->ppBuffer = &pEndpointFBSD->apvData[0];
298 pXferEndpoint->pLength = &pEndpointFBSD->acbData[0];
299
300 LogFlow(("usbProxyFreeBSDEndpointOpen: ep_index=%d ep_num=%d\n",
301 index, Endpoint));
302
303 memset(&UsbFsOpen, 0, sizeof(UsbFsOpen));
304
305 UsbFsOpen.ep_index = index;
306 UsbFsOpen.ep_no = Endpoint;
307 UsbFsOpen.max_bufsize = 256 * 1024;
308 /* Hardcoded assumption about the URBs we get. */
309
310 UsbFsOpen.max_frames = fIsoc ? USBFBSD_MAXFRAMES : 2;
311
312 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_OPEN, &UsbFsOpen, true);
313 if (RT_FAILURE(rc))
314 {
315 if (rc == VERR_RESOURCE_BUSY)
316 LogFlow(("usbProxyFreeBSDEndpointOpen: EBUSY\n"));
317
318 return -1;
319 }
320 pEndpointFBSD->fOpen = true;
321 pEndpointFBSD->pUrb = NULL;
322 pEndpointFBSD->iEpNum = Endpoint;
323 pEndpointFBSD->cMaxIo = UsbFsOpen.max_bufsize;
324 pEndpointFBSD->cMaxFrames = UsbFsOpen.max_frames;
325
326 return index;
327}
328
329/**
330 * Close an endpoint.
331 *
332 * @returns VBox status code.
333 */
334static int usbProxyFreeBSDEndpointClose(PUSBPROXYDEV pProxyDev, int Endpoint)
335{
336 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
337 PUSBENDPOINTFBSD pEndpointFBSD = &pDevFBSD->aSwEndpoint[Endpoint];
338 struct usb_fs_close UsbFsClose;
339 int rc = VINF_SUCCESS;
340
341 LogFlow(("usbProxyFreeBSDEndpointClose: pProxyDev=%p Endpoint=%d\n",
342 (void *)pProxyDev, Endpoint));
343
344 /* check for cancelling */
345 if (pEndpointFBSD->pUrb != NULL)
346 {
347 pEndpointFBSD->fCancelling = true;
348 pDevFBSD->fCancelling = true;
349 }
350
351 /* check for opened */
352 if (pEndpointFBSD->fOpen)
353 {
354 pEndpointFBSD->fOpen = false;
355
356 /* Zero default */
357 memset(&UsbFsClose, 0, sizeof(UsbFsClose));
358
359 /* Set endpoint index */
360 UsbFsClose.ep_index = Endpoint;
361
362 /* Close endpoint */
363 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_CLOSE, &UsbFsClose, true);
364 }
365 return rc;
366}
367
368/**
369 * Opens the device file.
370 *
371 * @returns VBox status code.
372 * @param pProxyDev The device instance.
373 * @param pszAddress If we are using usbfs, this is the path to the
374 * device. If we are using sysfs, this is a string of
375 * the form "sysfs:<sysfs path>//device:<device node>".
376 * In the second case, the two paths are guaranteed
377 * not to contain the substring "//".
378 */
379static DECLCALLBACK(int) usbProxyFreeBSDOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress)
380{
381 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
382 int rc;
383
384 LogFlow(("usbProxyFreeBSDOpen: pProxyDev=%p pszAddress=%s\n", pProxyDev, pszAddress));
385
386 /*
387 * Try open the device node.
388 */
389 RTFILE hFile;
390 rc = RTFileOpen(&hFile, pszAddress, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
391 if (RT_SUCCESS(rc))
392 {
393 /*
394 * Initialize the FreeBSD backend data.
395 */
396 pDevFBSD->hFile = hFile;
397 rc = usbProxyFreeBSDFsInit(pProxyDev);
398 if (RT_SUCCESS(rc))
399 {
400 /*
401 * Create wakeup pipe.
402 */
403 rc = RTPipeCreate(&pDevFBSD->hPipeWakeupR, &pDevFBSD->hPipeWakeupW, 0);
404 if (RT_SUCCESS(rc))
405 {
406 LogFlow(("usbProxyFreeBSDOpen(%p, %s): returns successfully hFile=%RTfile iActiveCfg=%d\n",
407 pProxyDev, pszAddress, pDevFBSD->hFile, pProxyDev->iActiveCfg));
408
409 return VINF_SUCCESS;
410 }
411 }
412
413 RTFileClose(hFile);
414 }
415 else if (rc == VERR_ACCESS_DENIED)
416 rc = VERR_VUSB_USBFS_PERMISSION;
417
418 Log(("usbProxyFreeBSDOpen(%p, %s) failed, rc=%d!\n",
419 pProxyDev, pszAddress, rc));
420
421 return rc;
422}
423
424
425/**
426 * Claims all the interfaces and figures out the
427 * current configuration.
428 *
429 * @returns VINF_SUCCESS.
430 * @param pProxyDev The proxy device.
431 */
432static DECLCALLBACK(int) usbProxyFreeBSDInit(PUSBPROXYDEV pProxyDev)
433{
434 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
435 int rc;
436
437 LogFlow(("usbProxyFreeBSDInit: pProxyDev=%s\n",
438 pProxyDev->pUsbIns->pszName));
439
440 /* Retrieve current active configuration. */
441 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_GET_CONFIG,
442 &pProxyDev->iActiveCfg, true);
443 if (RT_FAILURE(rc) || pProxyDev->iActiveCfg == 255)
444 {
445 pProxyDev->cIgnoreSetConfigs = 0;
446 pProxyDev->iActiveCfg = -1;
447 }
448 else
449 {
450 pProxyDev->cIgnoreSetConfigs = 1;
451 pProxyDev->iActiveCfg++;
452 }
453
454 Log(("usbProxyFreeBSDInit: iActiveCfg=%d\n", pProxyDev->iActiveCfg));
455
456 return rc;
457}
458
459/**
460 * Closes the proxy device.
461 */
462static DECLCALLBACK(void) usbProxyFreeBSDClose(PUSBPROXYDEV pProxyDev)
463{
464 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
465
466 LogFlow(("usbProxyFreeBSDClose: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName));
467
468 /* sanity check */
469 AssertPtrReturnVoid(pDevFBSD);
470
471 usbProxyFreeBSDFsUnInit(pProxyDev);
472
473 RTPipeClose(pDevFBSD->hPipeWakeupR);
474 RTPipeClose(pDevFBSD->hPipeWakeupW);
475
476 RTFileClose(pDevFBSD->hFile);
477 pDevFBSD->hFile = NIL_RTFILE;
478
479 LogFlow(("usbProxyFreeBSDClose: returns\n"));
480}
481
482/**
483 * Reset a device.
484 *
485 * @returns VBox status code.
486 * @param pDev The device to reset.
487 */
488static DECLCALLBACK(int) usbProxyFreeBSDReset(PUSBPROXYDEV pProxyDev, bool fResetOnFreeBSD)
489{
490 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
491 int iParm;
492 int rc = VINF_SUCCESS;
493
494 LogFlow(("usbProxyFreeBSDReset: pProxyDev=%s\n",
495 pProxyDev->pUsbIns->pszName));
496
497 if (!fResetOnFreeBSD)
498 goto done;
499
500 /* We need to release kernel ressources first. */
501 rc = usbProxyFreeBSDFsUnInit(pProxyDev);
502 if (RT_FAILURE(rc))
503 goto done;
504
505 /* Resetting is only possible as super-user, ignore any failures: */
506 iParm = 0;
507 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_DEVICEENUMERATE, &iParm, true);
508 if (RT_FAILURE(rc))
509 {
510 /* Set the config instead of bus reset */
511 iParm = 255;
512 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iParm, true);
513 if (RT_SUCCESS(rc))
514 {
515 iParm = 0;
516 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iParm, true);
517 }
518 }
519 usleep(10000); /* nice it! */
520
521 /* Allocate kernel ressources again. */
522 rc = usbProxyFreeBSDFsInit(pProxyDev);
523 if (RT_FAILURE(rc))
524 goto done;
525
526 /* Retrieve current active configuration. */
527 rc = usbProxyFreeBSDInit(pProxyDev);
528
529done:
530 pProxyDev->cIgnoreSetConfigs = 2;
531
532 return rc;
533}
534
535/**
536 * SET_CONFIGURATION.
537 *
538 * The caller makes sure that it's not called first time after open or reset
539 * with the active interface.
540 *
541 * @returns success indicator.
542 * @param pProxyDev The device instance data.
543 * @param iCfg The configuration to set.
544 */
545static DECLCALLBACK(int) usbProxyFreeBSDSetConfig(PUSBPROXYDEV pProxyDev, int iCfg)
546{
547 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
548 int iCfgIndex;
549 int rc;
550
551 LogFlow(("usbProxyFreeBSDSetConfig: pProxyDev=%s cfg=%x\n",
552 pProxyDev->pUsbIns->pszName, iCfg));
553
554 /* We need to release kernel ressources first. */
555 rc = usbProxyFreeBSDFsUnInit(pProxyDev);
556 if (RT_FAILURE(rc))
557 {
558 LogFlow(("usbProxyFreeBSDSetInterface: Freeing kernel resources "
559 "failed failed rc=%d\n", rc));
560 return rc;
561 }
562
563 if (iCfg == 0)
564 {
565 /* Unconfigure */
566 iCfgIndex = 255;
567 }
568 else
569 {
570 /* Get the configuration index matching the value. */
571 for (iCfgIndex = 0; iCfgIndex < pProxyDev->DevDesc.bNumConfigurations; iCfgIndex++)
572 {
573 if (pProxyDev->paCfgDescs[iCfgIndex].Core.bConfigurationValue == iCfg)
574 break;
575 }
576
577 if (iCfgIndex == pProxyDev->DevDesc.bNumConfigurations)
578 {
579 LogFlow(("usbProxyFreeBSDSetConfig: configuration "
580 "%d not found\n", iCfg));
581 return VERR_NOT_FOUND;
582 }
583 }
584
585 /* Set the config */
586 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iCfgIndex, true);
587 if (RT_FAILURE(rc))
588 return rc;
589
590 /* Allocate kernel ressources again. */
591 return usbProxyFreeBSDFsInit(pProxyDev);
592}
593
594/**
595 * Claims an interface.
596 * @returns success indicator.
597 */
598static DECLCALLBACK(int) usbProxyFreeBSDClaimInterface(PUSBPROXYDEV pProxyDev, int iIf)
599{
600 int rc;
601
602 LogFlow(("usbProxyFreeBSDClaimInterface: pProxyDev=%s "
603 "ifnum=%x\n", pProxyDev->pUsbIns->pszName, iIf));
604
605 /*
606 * Try to detach kernel driver on this interface, ignore any
607 * failures
608 */
609 usbProxyFreeBSDDoIoCtl(pProxyDev, USB_IFACE_DRIVER_DETACH, &iIf, true);
610
611 /* Try to claim interface */
612 return usbProxyFreeBSDDoIoCtl(pProxyDev, USB_CLAIM_INTERFACE, &iIf, true);
613}
614
615/**
616 * Releases an interface.
617 * @returns success indicator.
618 */
619static DECLCALLBACK(int) usbProxyFreeBSDReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf)
620{
621 int rc;
622
623 LogFlow(("usbProxyFreeBSDReleaseInterface: pProxyDev=%s "
624 "ifnum=%x\n", pProxyDev->pUsbIns->pszName, iIf));
625
626 return usbProxyFreeBSDDoIoCtl(pProxyDev, USB_RELEASE_INTERFACE, &iIf, true);
627}
628
629/**
630 * SET_INTERFACE.
631 *
632 * @returns success indicator.
633 */
634static DECLCALLBACK(int) usbProxyFreeBSDSetInterface(PUSBPROXYDEV pProxyDev, int iIf, int iAlt)
635{
636 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
637 struct usb_alt_interface UsbIntAlt;
638 int rc;
639
640 LogFlow(("usbProxyFreeBSDSetInterface: pProxyDev=%p iIf=%x iAlt=%x\n",
641 pProxyDev, iIf, iAlt));
642
643 /* We need to release kernel ressources first. */
644 rc = usbProxyFreeBSDFsUnInit(pProxyDev);
645 if (RT_FAILURE(rc))
646 {
647 LogFlow(("usbProxyFreeBSDSetInterface: Freeing kernel resources "
648 "failed failed rc=%d\n", rc));
649 return rc;
650 }
651 memset(&UsbIntAlt, 0, sizeof(UsbIntAlt));
652 UsbIntAlt.uai_interface_index = iIf;
653 UsbIntAlt.uai_alt_index = iAlt;
654
655 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_ALTINTERFACE, &UsbIntAlt, true);
656 if (RT_FAILURE(rc))
657 {
658 LogFlow(("usbProxyFreeBSDSetInterface: Setting interface %d %d "
659 "failed rc=%d\n", iIf, iAlt, rc));
660 return rc;
661 }
662
663 return usbProxyFreeBSDFsInit(pProxyDev);
664}
665
666/**
667 * Clears the halted endpoint 'ep_num'.
668 */
669static DECLCALLBACK(int) usbProxyFreeBSDClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int ep_num)
670{
671 LogFlow(("usbProxyFreeBSDClearHaltedEp: pProxyDev=%s ep_num=%u\n",
672 pProxyDev->pUsbIns->pszName, ep_num));
673
674 /*
675 * Clearing the zero control pipe doesn't make sense.
676 * Just ignore it.
677 */
678 if ((ep_num & 0xF) == 0)
679 return VINF_SUCCESS;
680
681 struct usb_ctl_request Req;
682 memset(&Req, 0, sizeof(Req));
683 usbProxyFreeBSDSetupReq(&Req.ucr_request,
684 VUSB_DIR_TO_DEV | VUSB_TO_ENDPOINT,
685 VUSB_REQ_CLEAR_FEATURE, 0, ep_num, 0);
686
687 int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_DO_REQUEST, &Req, true);
688
689 LogFlow(("usbProxyFreeBSDClearHaltedEp: rc=%Rrc\n", rc));
690 return rc;
691}
692
693/**
694 * @interface_method_impl{USBPROXYBACK,pfnUrbQueue}
695 */
696static DECLCALLBACK(int) usbProxyFreeBSDUrbQueue(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb)
697{
698 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
699 PUSBENDPOINTFBSD pEndpointFBSD;
700 struct usb_fs_endpoint *pXferEndpoint;
701 struct usb_fs_start UsbFsStart;
702 unsigned cFrames;
703 uint8_t *pbData;
704 int index;
705 int ep_num;
706 int rc;
707
708 LogFlow(("usbProxyFreeBSDUrbQueue: pUrb=%p EndPt=%u Dir=%u\n",
709 pUrb, (unsigned)pUrb->EndPt, (unsigned)pUrb->enmDir));
710
711 ep_num = pUrb->EndPt;
712 if ((pUrb->enmType != VUSBXFERTYPE_MSG) && (pUrb->enmDir == VUSBDIRECTION_IN)) {
713 /* set IN-direction bit */
714 ep_num |= 0x80;
715 }
716
717 index = 0;
718
719retry:
720
721 index = usbProxyFreeBSDEndpointOpen(pProxyDev, ep_num,
722 (pUrb->enmType == VUSBXFERTYPE_ISOC),
723 index);
724
725 if (index < 0)
726 return VERR_INVALID_PARAMETER;
727
728 pEndpointFBSD = &pDevFBSD->aSwEndpoint[index];
729 pXferEndpoint = &pDevFBSD->aHwEndpoint[index];
730
731 pbData = pUrb->abData;
732
733 switch (pUrb->enmType)
734 {
735 case VUSBXFERTYPE_MSG:
736 {
737 pEndpointFBSD->apvData[0] = pbData;
738 pEndpointFBSD->acbData[0] = 8;
739
740 /* check wLength */
741 if (pbData[6] || pbData[7])
742 {
743 pEndpointFBSD->apvData[1] = pbData + 8;
744 pEndpointFBSD->acbData[1] = pbData[6] | (pbData[7] << 8);
745 cFrames = 2;
746 }
747 else
748 {
749 pEndpointFBSD->apvData[1] = NULL;
750 pEndpointFBSD->acbData[1] = 0;
751 cFrames = 1;
752 }
753
754 LogFlow(("usbProxyFreeBSDUrbQueue: pUrb->cbData=%u, 0x%02x, "
755 "0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
756 pUrb->cbData, pbData[0], pbData[1], pbData[2], pbData[3],
757 pbData[4], pbData[5], pbData[6], pbData[7]));
758
759 pXferEndpoint->timeout = USB_FS_TIMEOUT_NONE;
760 pXferEndpoint->flags = USB_FS_FLAG_MULTI_SHORT_OK;
761 break;
762 }
763 case VUSBXFERTYPE_ISOC:
764 {
765 unsigned i;
766
767 for (i = 0; i < pUrb->cIsocPkts; i++)
768 {
769 if (i >= pEndpointFBSD->cMaxFrames)
770 break;
771 pEndpointFBSD->apvData[i] = pbData + pUrb->aIsocPkts[i].off;
772 pEndpointFBSD->acbData[i] = pUrb->aIsocPkts[i].cb;
773 }
774 /* Timeout handling will be done during reap. */
775 pXferEndpoint->timeout = USB_FS_TIMEOUT_NONE;
776 pXferEndpoint->flags = USB_FS_FLAG_MULTI_SHORT_OK;
777 cFrames = i;
778 break;
779 }
780 default:
781 {
782 pEndpointFBSD->apvData[0] = pbData;
783 pEndpointFBSD->cbData0 = pUrb->cbData;
784
785 /* XXX maybe we have to loop */
786 if (pUrb->cbData > pEndpointFBSD->cMaxIo)
787 pEndpointFBSD->acbData[0] = pEndpointFBSD->cMaxIo;
788 else
789 pEndpointFBSD->acbData[0] = pUrb->cbData;
790
791 /* Timeout handling will be done during reap. */
792 pXferEndpoint->timeout = USB_FS_TIMEOUT_NONE;
793 pXferEndpoint->flags = pUrb->fShortNotOk ? 0 : USB_FS_FLAG_MULTI_SHORT_OK;
794 cFrames = 1;
795 break;
796 }
797 }
798
799 /* store number of frames */
800 pXferEndpoint->nFrames = cFrames;
801
802 /* zero-default */
803 memset(&UsbFsStart, 0, sizeof(UsbFsStart));
804
805 /* Start the transfer */
806 UsbFsStart.ep_index = index;
807
808 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_START, &UsbFsStart, true);
809
810 LogFlow(("usbProxyFreeBSDUrbQueue: USB_FS_START returned rc=%d "
811 "len[0]=%u len[1]=%u cbData=%u index=%u ep_num=%u\n", rc,
812 (unsigned)pEndpointFBSD->acbData[0],
813 (unsigned)pEndpointFBSD->acbData[1],
814 (unsigned)pUrb->cbData,
815 (unsigned)index, (unsigned)ep_num));
816
817 if (RT_FAILURE(rc))
818 {
819 if (rc == VERR_RESOURCE_BUSY)
820 {
821 index++;
822 goto retry;
823 }
824 return rc;
825 }
826 pUrb->Dev.pvPrivate = (void *)(long)(index + 1);
827 pEndpointFBSD->pUrb = pUrb;
828
829 return rc;
830}
831
832/**
833 * Reap URBs in-flight on a device.
834 *
835 * @returns Pointer to a completed URB.
836 * @returns NULL if no URB was completed.
837 * @param pProxyDev The device.
838 * @param cMillies Number of milliseconds to wait. Use 0 to not wait at all.
839 */
840static DECLCALLBACK(PVUSBURB) usbProxyFreeBSDUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies)
841{
842 struct usb_fs_endpoint *pXferEndpoint;
843 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
844 PUSBENDPOINTFBSD pEndpointFBSD;
845 PVUSBURB pUrb;
846 struct usb_fs_complete UsbFsComplete;
847 struct pollfd pfd[2];
848 int rc;
849
850 LogFlow(("usbProxyFreeBSDUrbReap: pProxyDev=%p, cMillies=%u\n",
851 pProxyDev, cMillies));
852
853repeat:
854
855 pUrb = NULL;
856
857 /* check for cancelled transfers */
858 if (pDevFBSD->fCancelling)
859 {
860 for (unsigned n = 0; n < USBFBSD_MAXENDPOINTS; n++)
861 {
862 pEndpointFBSD = &pDevFBSD->aSwEndpoint[n];
863 if (pEndpointFBSD->fCancelling)
864 {
865 pEndpointFBSD->fCancelling = false;
866 pUrb = pEndpointFBSD->pUrb;
867 pEndpointFBSD->pUrb = NULL;
868
869 if (pUrb != NULL)
870 break;
871 }
872 }
873
874 if (pUrb != NULL)
875 {
876 pUrb->enmStatus = VUSBSTATUS_INVALID;
877 pUrb->Dev.pvPrivate = NULL;
878
879 switch (pUrb->enmType)
880 {
881 case VUSBXFERTYPE_MSG:
882 pUrb->cbData = 0;
883 break;
884 case VUSBXFERTYPE_ISOC:
885 pUrb->cbData = 0;
886 for (int n = 0; n < (int)pUrb->cIsocPkts; n++)
887 pUrb->aIsocPkts[n].cb = 0;
888 break;
889 default:
890 pUrb->cbData = 0;
891 break;
892 }
893 return pUrb;
894 }
895 pDevFBSD->fCancelling = false;
896 }
897 /* Zero default */
898
899 memset(&UsbFsComplete, 0, sizeof(UsbFsComplete));
900
901 /* Check if any endpoints are complete */
902 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_COMPLETE, &UsbFsComplete, true);
903 if (RT_SUCCESS(rc))
904 {
905 pXferEndpoint = &pDevFBSD->aHwEndpoint[UsbFsComplete.ep_index];
906 pEndpointFBSD = &pDevFBSD->aSwEndpoint[UsbFsComplete.ep_index];
907
908 LogFlow(("usbProxyFreeBSDUrbReap: Reaped "
909 "URB %#p\n", pEndpointFBSD->pUrb));
910
911 if (pXferEndpoint->status == USB_ERR_CANCELLED)
912 goto repeat;
913
914 pUrb = pEndpointFBSD->pUrb;
915 pEndpointFBSD->pUrb = NULL;
916 if (pUrb == NULL)
917 goto repeat;
918
919 switch (pXferEndpoint->status)
920 {
921 case USB_ERR_NORMAL_COMPLETION:
922 pUrb->enmStatus = VUSBSTATUS_OK;
923 break;
924 case USB_ERR_STALLED:
925 pUrb->enmStatus = VUSBSTATUS_STALL;
926 break;
927 default:
928 pUrb->enmStatus = VUSBSTATUS_INVALID;
929 break;
930 }
931
932 pUrb->Dev.pvPrivate = NULL;
933
934 switch (pUrb->enmType)
935 {
936 case VUSBXFERTYPE_MSG:
937 pUrb->cbData = pEndpointFBSD->acbData[0] + pEndpointFBSD->acbData[1];
938 break;
939 case VUSBXFERTYPE_ISOC:
940 {
941 int n;
942
943 if (pUrb->enmDir == VUSBDIRECTION_OUT)
944 break;
945 pUrb->cbData = 0;
946 for (n = 0; n < (int)pUrb->cIsocPkts; n++)
947 {
948 if (n >= (int)pEndpointFBSD->cMaxFrames)
949 break;
950 pUrb->cbData += pEndpointFBSD->acbData[n];
951 pUrb->aIsocPkts[n].cb = pEndpointFBSD->acbData[n];
952 }
953 for (; n < (int)pUrb->cIsocPkts; n++)
954 pUrb->aIsocPkts[n].cb = 0;
955
956 break;
957 }
958 default:
959 pUrb->cbData = pEndpointFBSD->acbData[0];
960 break;
961 }
962
963 LogFlow(("usbProxyFreeBSDUrbReap: Status=%d epindex=%u "
964 "len[0]=%d len[1]=%d\n",
965 (int)pXferEndpoint->status,
966 (unsigned)UsbFsComplete.ep_index,
967 (unsigned)pEndpointFBSD->acbData[0],
968 (unsigned)pEndpointFBSD->acbData[1]));
969
970 }
971 else if (cMillies != 0 && rc == VERR_RESOURCE_BUSY)
972 {
973 for (;;)
974 {
975 pfd[0].fd = RTFileToNative(pDevFBSD->hFile);
976 pfd[0].events = POLLIN | POLLRDNORM;
977 pfd[0].revents = 0;
978
979 pfd[1].fd = RTPipeToNative(pDevFBSD->hPipeWakeupR);
980 pfd[1].events = POLLIN | POLLRDNORM;
981 pfd[1].revents = 0;
982
983 rc = poll(pfd, 2, (cMillies == RT_INDEFINITE_WAIT) ? INFTIM : cMillies);
984 if (rc > 0)
985 {
986 if (pfd[1].revents & POLLIN)
987 {
988 /* Got woken up, drain pipe. */
989 uint8_t bRead;
990 size_t cbIgnored = 0;
991 RTPipeRead(pDevFBSD->hPipeWakeupR, &bRead, 1, &cbIgnored);
992 /* Make sure we return from this function */
993 cMillies = 0;
994 }
995 break;
996 }
997 if (rc == 0)
998 return NULL;
999 if (errno != EAGAIN)
1000 return NULL;
1001 }
1002 goto repeat;
1003 }
1004 return pUrb;
1005}
1006
1007/**
1008 * Cancels the URB.
1009 * The URB requires reaping, so we don't change its state.
1010 */
1011static DECLCALLBACK(int) usbProxyFreeBSDUrbCancel(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb)
1012{
1013 int index;
1014
1015 index = (int)(long)pUrb->Dev.pvPrivate - 1;
1016
1017 if (index < 0 || index >= USBFBSD_MAXENDPOINTS)
1018 return VINF_SUCCESS; /* invalid index, pretend success. */
1019
1020 LogFlow(("usbProxyFreeBSDUrbCancel: epindex=%u\n", (unsigned)index));
1021 return usbProxyFreeBSDEndpointClose(pProxyDev, index);
1022}
1023
1024static DECLCALLBACK(int) usbProxyFreeBSDWakeup(PUSBPROXYDEV pProxyDev)
1025{
1026 PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
1027 size_t cbIgnored;
1028
1029 LogFlowFunc(("pProxyDev=%p\n", pProxyDev));
1030
1031 return RTPipeWrite(pDevFBSD->hPipeWakeupW, "", 1, &cbIgnored);
1032}
1033
1034/**
1035 * The FreeBSD USB Proxy Backend.
1036 */
1037extern const USBPROXYBACK g_USBProxyDeviceHost =
1038{
1039 /* pszName */
1040 "host",
1041 /* cbBackend */
1042 sizeof(USBPROXYDEVFBSD),
1043 usbProxyFreeBSDOpen,
1044 usbProxyFreeBSDInit,
1045 usbProxyFreeBSDClose,
1046 usbProxyFreeBSDReset,
1047 usbProxyFreeBSDSetConfig,
1048 usbProxyFreeBSDClaimInterface,
1049 usbProxyFreeBSDReleaseInterface,
1050 usbProxyFreeBSDSetInterface,
1051 usbProxyFreeBSDClearHaltedEp,
1052 usbProxyFreeBSDUrbQueue,
1053 usbProxyFreeBSDUrbCancel,
1054 usbProxyFreeBSDUrbReap,
1055 usbProxyFreeBSDWakeup,
1056 0
1057};
1058
1059/*
1060 * Local Variables:
1061 * mode: c
1062 * c-file-style: "bsd"
1063 * c-basic-offset: 4
1064 * tab-width: 4
1065 * indent-tabs-mode: s
1066 * End:
1067 */
1068
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use