diff --git a/sys/dev/usb/usb_dev.h b/sys/dev/usb/usb_dev.h --- a/sys/dev/usb/usb_dev.h +++ b/sys/dev/usb/usb_dev.h @@ -2,7 +2,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2008-2023 Hans Petter Selasky * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -110,13 +110,15 @@ struct selinfo selinfo; struct cv cv_io; struct cv cv_drain; + struct sx fs_fastpath_lock; struct usb_fifo_methods *methods; struct usb_symlink *symlink[2];/* our symlinks */ struct proc *async_p; /* process that wants SIGIO */ struct usb_fs_endpoint *fs_ep_ptr; struct usb_device *udev; +#define USB_FS_XFER_MAX 126 struct usb_xfer *xfer[2]; - struct usb_xfer **fs_xfer; + struct usb_xfer *fs_xfer[USB_FS_XFER_MAX]; struct mtx *priv_mtx; /* client data */ /* set if FIFO is opened by a FILE: */ struct usb_cdev_privdata *curr_cpd; diff --git a/sys/dev/usb/usb_dev.c b/sys/dev/usb/usb_dev.c --- a/sys/dev/usb/usb_dev.c +++ b/sys/dev/usb/usb_dev.c @@ -2,7 +2,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2006-2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2006-2023 Hans Petter Selasky * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -386,6 +386,7 @@ f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO); cv_init(&f->cv_io, "FIFO-IO"); cv_init(&f->cv_drain, "FIFO-DRAIN"); + sx_init(&f->fs_fastpath_lock, "FIFO-FP"); f->priv_mtx = mtx; f->refcount = 1; knlist_init_mtx(&f->selinfo.si_note, mtx); @@ -626,6 +627,7 @@ cv_destroy(&f->cv_io); cv_destroy(&f->cv_drain); + sx_destroy(&f->fs_fastpath_lock); knlist_clear(&f->selinfo.si_note, 0); seldrain(&f->selinfo); diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c --- a/sys/dev/usb/usb_device.c +++ b/sys/dev/usb/usb_device.c @@ -2,7 +2,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2008-2020 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2008-2023 Hans Petter Selasky * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -2829,7 +2829,7 @@ continue; } if ((f->dev_ep_index == 0) && - (f->fs_xfer == NULL)) { + (f->fs_ep_max == 0)) { /* no need to free this FIFO */ continue; } @@ -2837,7 +2837,7 @@ if ((f->methods == &usb_ugen_methods) && (f->dev_ep_index == 0) && (!(flag & USB_UNCFG_FLAG_FREE_EP0)) && - (f->fs_xfer == NULL)) { + (f->fs_ep_max == 0)) { /* no need to free this FIFO */ continue; } diff --git a/sys/dev/usb/usb_generic.c b/sys/dev/usb/usb_generic.c --- a/sys/dev/usb/usb_generic.c +++ b/sys/dev/usb/usb_generic.c @@ -2,7 +2,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2008-2022 Hans Petter Selasky + * Copyright (c) 2008-2023 Hans Petter Selasky * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -120,6 +120,7 @@ static uint8_t ugen_fs_get_complete(struct usb_fifo *, uint8_t *); static int ugen_fs_uninit(struct usb_fifo *f); static int ugen_fs_copyin(struct usb_fifo *, uint8_t, struct usb_fs_endpoint*); +static uint8_t ugen_fifo_in_use(struct usb_fifo *, int fflags); /* structures */ @@ -959,22 +960,231 @@ return (0); } +static int +ugen_fs_init(struct usb_fifo *f, + struct usb_fs_endpoint *fs_ep_ptr, usb_size_t fs_ep_sz, + int fflags, uint8_t ep_index_max) +{ + int error; + + /* verify input parameters */ + if (fs_ep_ptr == NULL || ep_index_max > USB_FS_XFER_MAX) + return (EINVAL); + + if (f->fs_ep_max != 0) + return (EBUSY); + + if (f->dev_ep_index != 0 || ep_index_max == 0) + return (EINVAL); + + if (ugen_fifo_in_use(f, fflags)) + return (EBUSY); + + error = usb_fifo_alloc_buffer(f, 1, ep_index_max); + if (error == 0) { + mtx_lock(f->priv_mtx); + f->fs_ep_max = ep_index_max; + f->fs_ep_ptr = fs_ep_ptr; + f->fs_ep_sz = fs_ep_sz; + mtx_unlock(f->priv_mtx); + } + return (error); +} + int ugen_fs_uninit(struct usb_fifo *f) { - if (f->fs_xfer == NULL) { + if (f->fs_ep_max == 0) return (EINVAL); - } - usbd_transfer_unsetup(f->fs_xfer, f->fs_ep_max); - free(f->fs_xfer, M_USB); - f->fs_xfer = NULL; + + /* + * Prevent calls into the fast-path code, by setting fs_ep_max + * to zero: + */ + sx_xlock(&f->fs_fastpath_lock); + mtx_lock(f->priv_mtx); f->fs_ep_max = 0; + mtx_unlock(f->priv_mtx); + sx_xunlock(&f->fs_fastpath_lock); + + usbd_transfer_unsetup(f->fs_xfer, USB_FS_XFER_MAX); + + mtx_lock(f->priv_mtx); f->fs_ep_ptr = NULL; f->flag_iscomplete = 0; + mtx_unlock(f->priv_mtx); + usb_fifo_free_buffer(f); return (0); } +static int +usb_fs_open(struct usb_fifo *f, struct usb_fs_open *popen, + int fflags, usb_stream_t stream_id) +{ + struct usb_config usb_config[1] = {}; + struct usb_endpoint *ep; + struct usb_endpoint_descriptor *ed; + uint8_t iface_index; + uint8_t pre_scale; + uint8_t isread; + int error; + + if (popen->ep_index >= f->fs_ep_max) + return (EINVAL); + + if (f->fs_xfer[popen->ep_index] != NULL) + return (EBUSY); + + if (popen->max_bufsize > USB_FS_MAX_BUFSIZE) + popen->max_bufsize = USB_FS_MAX_BUFSIZE; + + if (popen->max_frames & USB_FS_MAX_FRAMES_PRE_SCALE) { + pre_scale = 1; + popen->max_frames &= ~USB_FS_MAX_FRAMES_PRE_SCALE; + } else { + pre_scale = 0; + } + + if (popen->max_frames > USB_FS_MAX_FRAMES) + popen->max_frames = USB_FS_MAX_FRAMES; + + if (popen->max_frames == 0) + return (EINVAL); + + ep = usbd_get_ep_by_addr(f->udev, popen->ep_no); + if (ep == NULL) + return (EINVAL); + + ed = ep->edesc; + if (ed == NULL) + return (ENXIO); + + iface_index = ep->iface_index; + + usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; + usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; + usb_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN); + usb_config[0].interval = USB_DEFAULT_INTERVAL; + usb_config[0].flags.proxy_buffer = 1; + if (pre_scale != 0) + usb_config[0].flags.pre_scale_frames = 1; + + usb_config[0].callback = &ugen_ctrl_fs_callback; + usb_config[0].timeout = 0; /* no timeout */ + usb_config[0].frames = popen->max_frames; + usb_config[0].bufsize = popen->max_bufsize; + usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ + usb_config[0].stream_id = stream_id; + + if (usb_config[0].type == UE_CONTROL) { + if (f->udev->flags.usb_mode != USB_MODE_HOST) + return (EINVAL); + } else { + isread = ((usb_config[0].endpoint & + (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN); + + if (f->udev->flags.usb_mode != USB_MODE_HOST) + isread = !isread; + + /* check permissions */ + if (isread) { + if (!(fflags & FREAD)) + return (EPERM); + } else { + if (!(fflags & FWRITE)) + return (EPERM); + } + } + error = usbd_transfer_setup(f->udev, &iface_index, + f->fs_xfer + popen->ep_index, usb_config, 1, + f, f->priv_mtx); + if (error == 0) { + /* update maximums */ + popen->max_packet_length = + f->fs_xfer[popen->ep_index]->max_frame_size; + popen->max_bufsize = + f->fs_xfer[popen->ep_index]->max_data_length; + /* update number of frames */ + popen->max_frames = + f->fs_xfer[popen->ep_index]->nframes; + /* store index of endpoint */ + f->fs_xfer[popen->ep_index]->priv_fifo = + ((uint8_t *)0) + popen->ep_index; + } else { + error = ENOMEM; + } + return (error); +} + +static int +usb_fs_close(struct usb_fifo *f, struct usb_fs_close *pclose) +{ + struct usb_xfer *xfer; + + if (pclose->ep_index >= f->fs_ep_max) + return (EINVAL); + + /* + * Prevent calls into the fast-path code, by setting the + * fs_xfer[] in question to NULL: + */ + sx_xlock(&f->fs_fastpath_lock); + mtx_lock(f->priv_mtx); + xfer = f->fs_xfer[pclose->ep_index]; + f->fs_xfer[pclose->ep_index] = NULL; + mtx_unlock(f->priv_mtx); + sx_xunlock(&f->fs_fastpath_lock); + + if (xfer == NULL) + return (EINVAL); + + usbd_transfer_unsetup(&xfer, 1); + return (0); +} + +static int +usb_fs_clear_stall_sync(struct usb_fifo *f, struct usb_fs_clear_stall_sync *pstall) +{ + struct usb_device_request req; + struct usb_endpoint *ep; + int error; + + if (pstall->ep_index >= f->fs_ep_max) + return (EINVAL); + + if (f->fs_xfer[pstall->ep_index] == NULL) + return (EINVAL); + + if (f->udev->flags.usb_mode != USB_MODE_HOST) + return (EINVAL); + + mtx_lock(f->priv_mtx); + error = usbd_transfer_pending(f->fs_xfer[pstall->ep_index]); + mtx_unlock(f->priv_mtx); + + if (error) + return (EBUSY); + + ep = f->fs_xfer[pstall->ep_index]->endpoint; + + /* setup a clear-stall packet */ + req.bmRequestType = UT_WRITE_ENDPOINT; + req.bRequest = UR_CLEAR_FEATURE; + USETW(req.wValue, UF_ENDPOINT_HALT); + req.wIndex[0] = ep->edesc->bEndpointAddress; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + error = usbd_do_request(f->udev, NULL, &req, NULL); + if (error == 0) { + usbd_clear_data_toggle(f->udev, ep); + } else { + error = ENXIO; + } + return (error); +} + static uint8_t ugen_fs_get_complete(struct usb_fifo *f, uint8_t *pindex) { @@ -1066,14 +1276,16 @@ int error; uint8_t isread; + mtx_lock(f->priv_mtx); if (ep_index >= f->fs_ep_max) { + mtx_unlock(f->priv_mtx); return (EINVAL); } xfer = f->fs_xfer[ep_index]; if (xfer == NULL) { + mtx_unlock(f->priv_mtx); return (EINVAL); } - mtx_lock(f->priv_mtx); if (usbd_transfer_pending(xfer)) { mtx_unlock(f->priv_mtx); return (EBUSY); /* should not happen */ @@ -1346,14 +1558,16 @@ int error; uint8_t isread; - if (ep_index >= f->fs_ep_max) + mtx_lock(f->priv_mtx); + if (ep_index >= f->fs_ep_max) { + mtx_unlock(f->priv_mtx); return (EINVAL); - + } xfer = f->fs_xfer[ep_index]; - if (xfer == NULL) + if (xfer == NULL) { + mtx_unlock(f->priv_mtx); return (EINVAL); - - mtx_lock(f->priv_mtx); + } if (!xfer->flags_int.transferring && !xfer->flags_int.started) { mtx_unlock(f->priv_mtx); @@ -1488,26 +1702,15 @@ static int ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) { - struct usb_config usb_config[1]; - struct usb_device_request req; union { struct usb_fs_complete *pcomp; struct usb_fs_start *pstart; struct usb_fs_stop *pstop; - struct usb_fs_open *popen; - struct usb_fs_open_stream *popen_stream; - struct usb_fs_close *pclose; - struct usb_fs_clear_stall_sync *pstall; void *addr; } u; - struct usb_endpoint *ep; - struct usb_endpoint_descriptor *ed; struct usb_xfer *xfer; - int error = 0; - uint8_t iface_index; - uint8_t isread; + int error; uint8_t ep_index; - uint8_t pre_scale; u.addr = addr; @@ -1515,202 +1718,58 @@ switch (cmd) { case USB_FS_COMPLETE: + sx_slock(&f->fs_fastpath_lock); mtx_lock(f->priv_mtx); error = ugen_fs_get_complete(f, &ep_index); mtx_unlock(f->priv_mtx); - if (error) { + if (error != 0) { error = EBUSY; - break; + } else { + u.pcomp->ep_index = ep_index; + error = ugen_fs_copy_out(f, u.pcomp->ep_index); } - u.pcomp->ep_index = ep_index; - error = ugen_fs_copy_out(f, u.pcomp->ep_index); + sx_sunlock(&f->fs_fastpath_lock); break; case USB_FS_START: + sx_slock(&f->fs_fastpath_lock); error = ugen_fs_copy_in(f, u.pstart->ep_index); - if (error) - break; - mtx_lock(f->priv_mtx); - xfer = f->fs_xfer[u.pstart->ep_index]; - usbd_transfer_start(xfer); - mtx_unlock(f->priv_mtx); + if (error == 0) { + mtx_lock(f->priv_mtx); + xfer = f->fs_xfer[u.pstart->ep_index]; + usbd_transfer_start(xfer); + mtx_unlock(f->priv_mtx); + } + sx_sunlock(&f->fs_fastpath_lock); break; case USB_FS_STOP: - if (u.pstop->ep_index >= f->fs_ep_max) { - error = EINVAL; - break; - } + sx_slock(&f->fs_fastpath_lock); mtx_lock(f->priv_mtx); - xfer = f->fs_xfer[u.pstart->ep_index]; - if (usbd_transfer_pending(xfer)) { - usbd_transfer_stop(xfer); - - /* - * Check if the USB transfer was stopped - * before it was even started and fake a - * cancel event. - */ - if (!xfer->flags_int.transferring && - !xfer->flags_int.started) { - DPRINTF("Issuing fake completion event\n"); - ugen_fs_set_complete(xfer->priv_sc, - USB_P2U(xfer->priv_fifo)); - } - } - mtx_unlock(f->priv_mtx); - break; - - case USB_FS_OPEN: - case USB_FS_OPEN_STREAM: - if (u.popen->ep_index >= f->fs_ep_max) { - error = EINVAL; - break; - } - if (f->fs_xfer[u.popen->ep_index] != NULL) { - error = EBUSY; - break; - } - if (u.popen->max_bufsize > USB_FS_MAX_BUFSIZE) { - u.popen->max_bufsize = USB_FS_MAX_BUFSIZE; - } - if (u.popen->max_frames & USB_FS_MAX_FRAMES_PRE_SCALE) { - pre_scale = 1; - u.popen->max_frames &= ~USB_FS_MAX_FRAMES_PRE_SCALE; - } else { - pre_scale = 0; - } - if (u.popen->max_frames > USB_FS_MAX_FRAMES) { - u.popen->max_frames = USB_FS_MAX_FRAMES; - break; - } - if (u.popen->max_frames == 0) { - error = EINVAL; - break; - } - ep = usbd_get_ep_by_addr(f->udev, u.popen->ep_no); - if (ep == NULL) { + if (u.pstop->ep_index >= f->fs_ep_max) { error = EINVAL; - break; - } - ed = ep->edesc; - if (ed == NULL) { - error = ENXIO; - break; - } - iface_index = ep->iface_index; - - memset(usb_config, 0, sizeof(usb_config)); - - usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; - usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; - usb_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN); - usb_config[0].interval = USB_DEFAULT_INTERVAL; - usb_config[0].flags.proxy_buffer = 1; - if (pre_scale != 0) - usb_config[0].flags.pre_scale_frames = 1; - usb_config[0].callback = &ugen_ctrl_fs_callback; - usb_config[0].timeout = 0; /* no timeout */ - usb_config[0].frames = u.popen->max_frames; - usb_config[0].bufsize = u.popen->max_bufsize; - usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ - if (cmd == USB_FS_OPEN_STREAM) - usb_config[0].stream_id = u.popen_stream->stream_id; - - if (usb_config[0].type == UE_CONTROL) { - if (f->udev->flags.usb_mode != USB_MODE_HOST) { - error = EINVAL; - break; - } } else { - isread = ((usb_config[0].endpoint & - (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN); + error = 0; + xfer = f->fs_xfer[u.pstart->ep_index]; + if (usbd_transfer_pending(xfer)) { + usbd_transfer_stop(xfer); - if (f->udev->flags.usb_mode != USB_MODE_HOST) { - isread = !isread; - } - /* check permissions */ - if (isread) { - if (!(fflags & FREAD)) { - error = EPERM; - break; - } - } else { - if (!(fflags & FWRITE)) { - error = EPERM; - break; + /* + * Check if the USB transfer was + * stopped before it was even started + * and fake a cancel event. + */ + if (!xfer->flags_int.transferring && + !xfer->flags_int.started) { + DPRINTF("Issuing fake completion event\n"); + ugen_fs_set_complete(xfer->priv_sc, + USB_P2U(xfer->priv_fifo)); } } } - error = usbd_transfer_setup(f->udev, &iface_index, - f->fs_xfer + u.popen->ep_index, usb_config, 1, - f, f->priv_mtx); - if (error == 0) { - /* update maximums */ - u.popen->max_packet_length = - f->fs_xfer[u.popen->ep_index]->max_frame_size; - u.popen->max_bufsize = - f->fs_xfer[u.popen->ep_index]->max_data_length; - /* update number of frames */ - u.popen->max_frames = - f->fs_xfer[u.popen->ep_index]->nframes; - /* store index of endpoint */ - f->fs_xfer[u.popen->ep_index]->priv_fifo = - ((uint8_t *)0) + u.popen->ep_index; - } else { - error = ENOMEM; - } - break; - - case USB_FS_CLOSE: - if (u.pclose->ep_index >= f->fs_ep_max) { - error = EINVAL; - break; - } - if (f->fs_xfer[u.pclose->ep_index] == NULL) { - error = EINVAL; - break; - } - usbd_transfer_unsetup(f->fs_xfer + u.pclose->ep_index, 1); - break; - - case USB_FS_CLEAR_STALL_SYNC: - if (u.pstall->ep_index >= f->fs_ep_max) { - error = EINVAL; - break; - } - if (f->fs_xfer[u.pstall->ep_index] == NULL) { - error = EINVAL; - break; - } - if (f->udev->flags.usb_mode != USB_MODE_HOST) { - error = EINVAL; - break; - } - mtx_lock(f->priv_mtx); - error = usbd_transfer_pending(f->fs_xfer[u.pstall->ep_index]); mtx_unlock(f->priv_mtx); - - if (error) { - return (EBUSY); - } - ep = f->fs_xfer[u.pstall->ep_index]->endpoint; - - /* setup a clear-stall packet */ - req.bmRequestType = UT_WRITE_ENDPOINT; - req.bRequest = UR_CLEAR_FEATURE; - USETW(req.wValue, UF_ENDPOINT_HALT); - req.wIndex[0] = ep->edesc->bEndpointAddress; - req.wIndex[1] = 0; - USETW(req.wLength, 0); - - error = usbd_do_request(f->udev, NULL, &req, NULL); - if (error == 0) { - usbd_clear_data_toggle(f->udev, ep); - } else { - error = ENXIO; - } + sx_sunlock(&f->fs_fastpath_lock); break; default: @@ -2161,9 +2220,6 @@ static int ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags) { -#ifdef COMPAT_FREEBSD32 - struct usb_fs_init local_pinit; -#endif union { struct usb_interface_descriptor *idesc; struct usb_alt_interface *ai; @@ -2175,6 +2231,10 @@ struct usb_fs_init32 *pinit32; #endif struct usb_fs_uninit *puninit; + struct usb_fs_open *popen; + struct usb_fs_open_stream *popen_stream; + struct usb_fs_close *pclose; + struct usb_fs_clear_stall_sync *pstall; struct usb_device_port_path *dpp; uint32_t *ptime; void *addr; @@ -2183,7 +2243,6 @@ struct usb_device_descriptor *dtemp; struct usb_config_descriptor *ctemp; struct usb_interface *iface; - size_t usb_fs_endpoint_sz = sizeof(struct usb_fs_endpoint); int error = 0; uint8_t n; @@ -2191,18 +2250,6 @@ DPRINTFN(6, "cmd=0x%08lx\n", cmd); -#ifdef COMPAT_FREEBSD32 - switch (cmd) { - case USB_FS_INIT32: - PTRIN_CP(*u.pinit32, local_pinit, pEndpoints); - CP(*u.pinit32, local_pinit, ep_index_max); - u.addr = &local_pinit; - cmd = _IOC_NEWTYPE(USB_FS_INIT, struct usb_fs_init); - usb_fs_endpoint_sz = sizeof(struct usb_fs_endpoint32); - break; - } -#endif - switch (cmd) { case USB_DISCOVER: usb_needs_explore_all(); @@ -2397,42 +2444,17 @@ break; case USB_FS_INIT: - /* verify input parameters */ - if (u.pinit->pEndpoints == NULL) { - error = EINVAL; - break; - } - if (u.pinit->ep_index_max > 127) { - error = EINVAL; - break; - } - if (u.pinit->ep_index_max == 0) { - error = EINVAL; - break; - } - if (f->fs_xfer != NULL) { - error = EBUSY; - break; - } - if (f->dev_ep_index != 0) { - error = EINVAL; - break; - } - if (ugen_fifo_in_use(f, fflags)) { - error = EBUSY; - break; - } - error = usb_fifo_alloc_buffer(f, 1, u.pinit->ep_index_max); - if (error) { - break; - } - f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) * - u.pinit->ep_index_max, M_USB, M_WAITOK | M_ZERO); - f->fs_ep_max = u.pinit->ep_index_max; - f->fs_ep_ptr = u.pinit->pEndpoints; - f->fs_ep_sz = usb_fs_endpoint_sz; + error = ugen_fs_init(f, u.pinit->pEndpoints, + sizeof(struct usb_fs_endpoint), fflags, + u.pinit->ep_index_max); break; - +#ifdef COMPAT_FREEBSD32 + case USB_FS_INIT32: + error = ugen_fs_init(f, PTRIN(u.pinit32->pEndpoints), + sizeof(struct usb_fs_endpoint32), fflags, + u.pinit32->ep_index_max); + break; +#endif case USB_FS_UNINIT: if (u.puninit->dummy != 0) { error = EINVAL; @@ -2441,6 +2463,20 @@ error = ugen_fs_uninit(f); break; + case USB_FS_OPEN: + case USB_FS_OPEN_STREAM: + error = usb_fs_open(f, u.popen, fflags, + (cmd == USB_FS_OPEN_STREAM) ? u.popen_stream->stream_id : 0); + break; + + case USB_FS_CLOSE: + error = usb_fs_close(f, u.pclose); + break; + + case USB_FS_CLEAR_STALL_SYNC: + error = usb_fs_clear_stall_sync(f, u.pstall); + break; + default: mtx_lock(f->priv_mtx); error = ugen_iface_ioctl(f, cmd, addr, fflags);