102ac6454SAndrew Thompson /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 49b077d72SHans Petter Selasky * Copyright (c) 2006-2023 Hans Petter Selasky 502ac6454SAndrew Thompson * 602ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 702ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 802ac6454SAndrew Thompson * are met: 902ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 1002ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1102ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1202ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1302ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1402ac6454SAndrew Thompson * 1502ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1602ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1702ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1802ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1902ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2002ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2102ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2202ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2302ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2402ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2502ac6454SAndrew Thompson * SUCH DAMAGE. 2602ac6454SAndrew Thompson * 2702ac6454SAndrew Thompson * 28a593f6b8SAndrew Thompson * usb_dev.c - An abstraction layer for creating devices under /dev/... 2902ac6454SAndrew Thompson */ 3002ac6454SAndrew Thompson 31d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 32d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 33d2b99310SHans Petter Selasky #else 3445b48cbcSBrooks Davis #ifdef COMPAT_FREEBSD32 3545b48cbcSBrooks Davis #include <sys/abi_compat.h> 3645b48cbcSBrooks Davis #endif 37ed6d949aSAndrew Thompson #include <sys/stdint.h> 38ed6d949aSAndrew Thompson #include <sys/stddef.h> 39ed6d949aSAndrew Thompson #include <sys/param.h> 40ed6d949aSAndrew Thompson #include <sys/queue.h> 41ed6d949aSAndrew Thompson #include <sys/types.h> 42ed6d949aSAndrew Thompson #include <sys/systm.h> 43ed6d949aSAndrew Thompson #include <sys/kernel.h> 44ed6d949aSAndrew Thompson #include <sys/bus.h> 45ed6d949aSAndrew Thompson #include <sys/module.h> 46ed6d949aSAndrew Thompson #include <sys/lock.h> 47ed6d949aSAndrew Thompson #include <sys/mutex.h> 48ed6d949aSAndrew Thompson #include <sys/condvar.h> 49ed6d949aSAndrew Thompson #include <sys/sysctl.h> 50ed6d949aSAndrew Thompson #include <sys/sx.h> 51ed6d949aSAndrew Thompson #include <sys/unistd.h> 52ed6d949aSAndrew Thompson #include <sys/callout.h> 53ed6d949aSAndrew Thompson #include <sys/malloc.h> 54ed6d949aSAndrew Thompson #include <sys/priv.h> 55ed6d949aSAndrew Thompson #include <sys/vnode.h> 56ed6d949aSAndrew Thompson #include <sys/conf.h> 57ed6d949aSAndrew Thompson #include <sys/fcntl.h> 58ed6d949aSAndrew Thompson 5902ac6454SAndrew Thompson #include <dev/usb/usb.h> 6002ac6454SAndrew Thompson #include <dev/usb/usb_ioctl.h> 61ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 62ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 6302ac6454SAndrew Thompson 64a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_fifo_debug 6502ac6454SAndrew Thompson 6602ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 6702ac6454SAndrew Thompson #include <dev/usb/usb_dev.h> 68ed6d949aSAndrew Thompson #include <dev/usb/usb_mbuf.h> 6902ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 7002ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 7102ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 7202ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 7302ac6454SAndrew Thompson #include <dev/usb/usb_generic.h> 7402ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 7502ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 7602ac6454SAndrew Thompson 7702ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 7802ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 7902ac6454SAndrew Thompson 8002ac6454SAndrew Thompson #include <sys/filio.h> 8102ac6454SAndrew Thompson #include <sys/ttycom.h> 8202ac6454SAndrew Thompson #include <sys/syscallsubr.h> 8302ac6454SAndrew Thompson 8402ac6454SAndrew Thompson #include <machine/stdarg.h> 85d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 8602ac6454SAndrew Thompson 878755859aSAndrew Thompson #if USB_HAVE_UGEN 888755859aSAndrew Thompson 89ed6d949aSAndrew Thompson #ifdef USB_DEBUG 90a593f6b8SAndrew Thompson static int usb_fifo_debug = 0; 9102ac6454SAndrew Thompson 92f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, dev, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 93f8d2b1f3SPawel Biernacki "USB device"); 94af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb_dev, OID_AUTO, debug, CTLFLAG_RWTUN, 95a593f6b8SAndrew Thompson &usb_fifo_debug, 0, "Debug Level"); 9602ac6454SAndrew Thompson #endif 9702ac6454SAndrew Thompson 9802ac6454SAndrew Thompson #define USB_UCRED struct ucred *ucred, 9902ac6454SAndrew Thompson 10002ac6454SAndrew Thompson /* prototypes */ 10102ac6454SAndrew Thompson 102a593f6b8SAndrew Thompson static int usb_fifo_open(struct usb_cdev_privdata *, 103760bc48eSAndrew Thompson struct usb_fifo *, int); 104a593f6b8SAndrew Thompson static void usb_fifo_close(struct usb_fifo *, int); 105a593f6b8SAndrew Thompson static void usb_dev_init(void *); 106a593f6b8SAndrew Thompson static void usb_dev_init_post(void *); 107a593f6b8SAndrew Thompson static void usb_dev_uninit(void *); 108a593f6b8SAndrew Thompson static int usb_fifo_uiomove(struct usb_fifo *, void *, int, 10902ac6454SAndrew Thompson struct uio *); 110a593f6b8SAndrew Thompson static void usb_fifo_check_methods(struct usb_fifo_methods *); 111e2723934SHans Petter Selasky static struct usb_fifo *usb_fifo_alloc(struct mtx *); 112a593f6b8SAndrew Thompson static struct usb_endpoint *usb_dev_get_ep(struct usb_device *, uint8_t, 113f5f145baSAndrew Thompson uint8_t); 114a593f6b8SAndrew Thompson static void usb_loc_fill(struct usb_fs_privdata *, 115760bc48eSAndrew Thompson struct usb_cdev_privdata *); 116a593f6b8SAndrew Thompson static void usb_close(void *); 117a593f6b8SAndrew Thompson static usb_error_t usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *, int); 118a593f6b8SAndrew Thompson static usb_error_t usb_usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *); 119a593f6b8SAndrew Thompson static void usb_unref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *); 12002ac6454SAndrew Thompson 121a593f6b8SAndrew Thompson static d_open_t usb_open; 122a593f6b8SAndrew Thompson static d_ioctl_t usb_ioctl; 123a593f6b8SAndrew Thompson static d_read_t usb_read; 124a593f6b8SAndrew Thompson static d_write_t usb_write; 125a593f6b8SAndrew Thompson static d_poll_t usb_poll; 1260b6d54d4SHans Petter Selasky static d_kqfilter_t usb_kqfilter; 12702ac6454SAndrew Thompson 128a593f6b8SAndrew Thompson static d_ioctl_t usb_static_ioctl; 12902ac6454SAndrew Thompson 130a593f6b8SAndrew Thompson static usb_fifo_open_t usb_fifo_dummy_open; 131a593f6b8SAndrew Thompson static usb_fifo_close_t usb_fifo_dummy_close; 132a593f6b8SAndrew Thompson static usb_fifo_ioctl_t usb_fifo_dummy_ioctl; 133a593f6b8SAndrew Thompson static usb_fifo_cmd_t usb_fifo_dummy_cmd; 13402ac6454SAndrew Thompson 135ee3e3ff5SAndrew Thompson /* character device structure used for devices (/dev/ugenX.Y and /dev/uXXX) */ 136a593f6b8SAndrew Thompson struct cdevsw usb_devsw = { 13702ac6454SAndrew Thompson .d_version = D_VERSION, 138a593f6b8SAndrew Thompson .d_open = usb_open, 139a593f6b8SAndrew Thompson .d_ioctl = usb_ioctl, 140ee3e3ff5SAndrew Thompson .d_name = "usbdev", 14102ac6454SAndrew Thompson .d_flags = D_TRACKCLOSE, 142a593f6b8SAndrew Thompson .d_read = usb_read, 143a593f6b8SAndrew Thompson .d_write = usb_write, 1440b6d54d4SHans Petter Selasky .d_poll = usb_poll, 1450b6d54d4SHans Petter Selasky .d_kqfilter = usb_kqfilter, 14602ac6454SAndrew Thompson }; 14702ac6454SAndrew Thompson 148a593f6b8SAndrew Thompson static struct cdev* usb_dev = NULL; 149ee3e3ff5SAndrew Thompson 150ee3e3ff5SAndrew Thompson /* character device structure used for /dev/usb */ 151a593f6b8SAndrew Thompson static struct cdevsw usb_static_devsw = { 152ee3e3ff5SAndrew Thompson .d_version = D_VERSION, 153a593f6b8SAndrew Thompson .d_ioctl = usb_static_ioctl, 154ee3e3ff5SAndrew Thompson .d_name = "usb" 15502ac6454SAndrew Thompson }; 15602ac6454SAndrew Thompson 157a593f6b8SAndrew Thompson static TAILQ_HEAD(, usb_symlink) usb_sym_head; 158a593f6b8SAndrew Thompson static struct sx usb_sym_lock; 15902ac6454SAndrew Thompson 160a593f6b8SAndrew Thompson struct mtx usb_ref_lock; 16102ac6454SAndrew Thompson 16202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 163a593f6b8SAndrew Thompson * usb_loc_fill 16402ac6454SAndrew Thompson * 165760bc48eSAndrew Thompson * This is used to fill out a usb_cdev_privdata structure based on the 166760bc48eSAndrew Thompson * device's address as contained in usb_fs_privdata. 16702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 168ee3e3ff5SAndrew Thompson static void 169a593f6b8SAndrew Thompson usb_loc_fill(struct usb_fs_privdata* pd, struct usb_cdev_privdata *cpd) 17002ac6454SAndrew Thompson { 171ee3e3ff5SAndrew Thompson cpd->bus_index = pd->bus_index; 172ee3e3ff5SAndrew Thompson cpd->dev_index = pd->dev_index; 173ee3e3ff5SAndrew Thompson cpd->ep_addr = pd->ep_addr; 174ee3e3ff5SAndrew Thompson cpd->fifo_index = pd->fifo_index; 17502ac6454SAndrew Thompson } 17602ac6454SAndrew Thompson 17702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 178a593f6b8SAndrew Thompson * usb_ref_device 17902ac6454SAndrew Thompson * 18002ac6454SAndrew Thompson * This function is used to atomically refer an USB device by its 18102ac6454SAndrew Thompson * device location. If this function returns success the USB device 18220733245SPedro F. Giffuni * will not disappear until the USB device is unreferenced. 18302ac6454SAndrew Thompson * 18402ac6454SAndrew Thompson * Return values: 18502ac6454SAndrew Thompson * 0: Success, refcount incremented on the given USB device. 18602ac6454SAndrew Thompson * Else: Failure. 18702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1888019e7e7SAndrew Thompson static usb_error_t 189a593f6b8SAndrew Thompson usb_ref_device(struct usb_cdev_privdata *cpd, 190e13e19faSAndrew Thompson struct usb_cdev_refdata *crd, int need_uref) 19102ac6454SAndrew Thompson { 192760bc48eSAndrew Thompson struct usb_fifo **ppf; 193760bc48eSAndrew Thompson struct usb_fifo *f; 19402ac6454SAndrew Thompson 195e13e19faSAndrew Thompson DPRINTFN(2, "cpd=%p need uref=%d\n", cpd, need_uref); 196e13e19faSAndrew Thompson 197e13e19faSAndrew Thompson /* clear all refs */ 198e13e19faSAndrew Thompson memset(crd, 0, sizeof(*crd)); 19902ac6454SAndrew Thompson 200a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 201a593f6b8SAndrew Thompson cpd->bus = devclass_get_softc(usb_devclass_ptr, cpd->bus_index); 202ee3e3ff5SAndrew Thompson if (cpd->bus == NULL) { 203ee3e3ff5SAndrew Thompson DPRINTFN(2, "no bus at %u\n", cpd->bus_index); 20402ac6454SAndrew Thompson goto error; 20502ac6454SAndrew Thompson } 206ee3e3ff5SAndrew Thompson cpd->udev = cpd->bus->devices[cpd->dev_index]; 207ee3e3ff5SAndrew Thompson if (cpd->udev == NULL) { 208ee3e3ff5SAndrew Thompson DPRINTFN(2, "no device at %u\n", cpd->dev_index); 20902ac6454SAndrew Thompson goto error; 21002ac6454SAndrew Thompson } 211f97da128SHans Petter Selasky if (cpd->udev->state == USB_STATE_DETACHED && 212f97da128SHans Petter Selasky (need_uref != 2)) { 213f97da128SHans Petter Selasky DPRINTFN(2, "device is detached\n"); 214f97da128SHans Petter Selasky goto error; 215f97da128SHans Petter Selasky } 216d008478eSHans Petter Selasky if (need_uref) { 217d008478eSHans Petter Selasky DPRINTFN(2, "ref udev - needed\n"); 218d008478eSHans Petter Selasky 219ee3e3ff5SAndrew Thompson if (cpd->udev->refcount == USB_DEV_REF_MAX) { 22002ac6454SAndrew Thompson DPRINTFN(2, "no dev ref\n"); 22102ac6454SAndrew Thompson goto error; 22202ac6454SAndrew Thompson } 2230ed53d45SAndrew Thompson cpd->udev->refcount++; 2240ed53d45SAndrew Thompson 225a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 2260ed53d45SAndrew Thompson 2270ed53d45SAndrew Thompson /* 228a18a7a41SHans Petter Selasky * We need to grab the enumeration SX-lock before 229a18a7a41SHans Petter Selasky * grabbing the FIFO refs to avoid deadlock at detach! 2300ed53d45SAndrew Thompson */ 23110aab8b6SHans Petter Selasky crd->do_unlock = usbd_enum_lock_sig(cpd->udev); 2320ed53d45SAndrew Thompson 233a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 234a488edb5SAndrew Thompson 235a488edb5SAndrew Thompson /* 236a488edb5SAndrew Thompson * Set "is_uref" after grabbing the default SX lock 237a488edb5SAndrew Thompson */ 238e13e19faSAndrew Thompson crd->is_uref = 1; 23910aab8b6SHans Petter Selasky 24010aab8b6SHans Petter Selasky /* check for signal */ 24110aab8b6SHans Petter Selasky if (crd->do_unlock > 1) { 24210aab8b6SHans Petter Selasky crd->do_unlock = 0; 24310aab8b6SHans Petter Selasky goto error; 24410aab8b6SHans Petter Selasky } 2450ed53d45SAndrew Thompson } 2460ed53d45SAndrew Thompson 24702ac6454SAndrew Thompson /* check if we are doing an open */ 248ee3e3ff5SAndrew Thompson if (cpd->fflags == 0) { 249e13e19faSAndrew Thompson /* use zero defaults */ 25002ac6454SAndrew Thompson } else { 25102ac6454SAndrew Thompson /* check for write */ 252ee3e3ff5SAndrew Thompson if (cpd->fflags & FWRITE) { 253ee3e3ff5SAndrew Thompson ppf = cpd->udev->fifo; 254ee3e3ff5SAndrew Thompson f = ppf[cpd->fifo_index + USB_FIFO_TX]; 255e13e19faSAndrew Thompson crd->txfifo = f; 256e13e19faSAndrew Thompson crd->is_write = 1; /* ref */ 257ee3e3ff5SAndrew Thompson if (f == NULL || f->refcount == USB_FIFO_REF_MAX) 25802ac6454SAndrew Thompson goto error; 2597214348fSAndrew Thompson if (f->curr_cpd != cpd) 2607214348fSAndrew Thompson goto error; 26102ac6454SAndrew Thompson /* check if USB-FS is active */ 26202ac6454SAndrew Thompson if (f->fs_ep_max != 0) { 263e13e19faSAndrew Thompson crd->is_usbfs = 1; 26402ac6454SAndrew Thompson } 26502ac6454SAndrew Thompson } 26602ac6454SAndrew Thompson 26702ac6454SAndrew Thompson /* check for read */ 268ee3e3ff5SAndrew Thompson if (cpd->fflags & FREAD) { 269ee3e3ff5SAndrew Thompson ppf = cpd->udev->fifo; 270ee3e3ff5SAndrew Thompson f = ppf[cpd->fifo_index + USB_FIFO_RX]; 271e13e19faSAndrew Thompson crd->rxfifo = f; 272e13e19faSAndrew Thompson crd->is_read = 1; /* ref */ 273ee3e3ff5SAndrew Thompson if (f == NULL || f->refcount == USB_FIFO_REF_MAX) 27402ac6454SAndrew Thompson goto error; 2757214348fSAndrew Thompson if (f->curr_cpd != cpd) 2767214348fSAndrew Thompson goto error; 27702ac6454SAndrew Thompson /* check if USB-FS is active */ 27802ac6454SAndrew Thompson if (f->fs_ep_max != 0) { 279e13e19faSAndrew Thompson crd->is_usbfs = 1; 28002ac6454SAndrew Thompson } 28102ac6454SAndrew Thompson } 28202ac6454SAndrew Thompson } 28302ac6454SAndrew Thompson 28402ac6454SAndrew Thompson /* when everything is OK we increment the refcounts */ 285e13e19faSAndrew Thompson if (crd->is_write) { 28602ac6454SAndrew Thompson DPRINTFN(2, "ref write\n"); 287e13e19faSAndrew Thompson crd->txfifo->refcount++; 28802ac6454SAndrew Thompson } 289e13e19faSAndrew Thompson if (crd->is_read) { 29002ac6454SAndrew Thompson DPRINTFN(2, "ref read\n"); 291e13e19faSAndrew Thompson crd->rxfifo->refcount++; 29202ac6454SAndrew Thompson } 293a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 29402ac6454SAndrew Thompson 29502ac6454SAndrew Thompson return (0); 29602ac6454SAndrew Thompson 29702ac6454SAndrew Thompson error: 298a18a7a41SHans Petter Selasky if (crd->do_unlock) 299cb18f7d1SAlfred Perlstein usbd_enum_unlock(cpd->udev); 300cb18f7d1SAlfred Perlstein 301a18a7a41SHans Petter Selasky if (crd->is_uref) { 302b78e84d1SHans Petter Selasky if (--(cpd->udev->refcount) == 0) 303d008478eSHans Petter Selasky cv_broadcast(&cpd->udev->ref_cv); 3040ed53d45SAndrew Thompson } 305a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 30602ac6454SAndrew Thompson DPRINTFN(2, "fail\n"); 307b06d477bSHans Petter Selasky 308b06d477bSHans Petter Selasky /* clear all refs */ 309b06d477bSHans Petter Selasky memset(crd, 0, sizeof(*crd)); 310b06d477bSHans Petter Selasky 31102ac6454SAndrew Thompson return (USB_ERR_INVAL); 31202ac6454SAndrew Thompson } 31302ac6454SAndrew Thompson 31402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 315a593f6b8SAndrew Thompson * usb_usb_ref_device 31602ac6454SAndrew Thompson * 31702ac6454SAndrew Thompson * This function is used to upgrade an USB reference to include the 31802ac6454SAndrew Thompson * USB device reference on a USB location. 31902ac6454SAndrew Thompson * 32002ac6454SAndrew Thompson * Return values: 32102ac6454SAndrew Thompson * 0: Success, refcount incremented on the given USB device. 32202ac6454SAndrew Thompson * Else: Failure. 32302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 324e0a69b51SAndrew Thompson static usb_error_t 325a593f6b8SAndrew Thompson usb_usb_ref_device(struct usb_cdev_privdata *cpd, 326e13e19faSAndrew Thompson struct usb_cdev_refdata *crd) 32702ac6454SAndrew Thompson { 32802ac6454SAndrew Thompson /* 32902ac6454SAndrew Thompson * Check if we already got an USB reference on this location: 33002ac6454SAndrew Thompson */ 331e13e19faSAndrew Thompson if (crd->is_uref) 33202ac6454SAndrew Thompson return (0); /* success */ 33302ac6454SAndrew Thompson 33402ac6454SAndrew Thompson /* 3350ed53d45SAndrew Thompson * To avoid deadlock at detach we need to drop the FIFO ref 3360ed53d45SAndrew Thompson * and re-acquire a new ref! 33702ac6454SAndrew Thompson */ 338a593f6b8SAndrew Thompson usb_unref_device(cpd, crd); 33902ac6454SAndrew Thompson 340a593f6b8SAndrew Thompson return (usb_ref_device(cpd, crd, 1 /* need uref */)); 34102ac6454SAndrew Thompson } 34202ac6454SAndrew Thompson 34302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 344a593f6b8SAndrew Thompson * usb_unref_device 34502ac6454SAndrew Thompson * 34602ac6454SAndrew Thompson * This function will release the reference count by one unit for the 34702ac6454SAndrew Thompson * given USB device. 34802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 3498019e7e7SAndrew Thompson static void 350a593f6b8SAndrew Thompson usb_unref_device(struct usb_cdev_privdata *cpd, 351e13e19faSAndrew Thompson struct usb_cdev_refdata *crd) 35202ac6454SAndrew Thompson { 353a488edb5SAndrew Thompson 354e13e19faSAndrew Thompson DPRINTFN(2, "cpd=%p is_uref=%d\n", cpd, crd->is_uref); 355a488edb5SAndrew Thompson 356a18a7a41SHans Petter Selasky if (crd->do_unlock) 357cb18f7d1SAlfred Perlstein usbd_enum_unlock(cpd->udev); 358cb18f7d1SAlfred Perlstein 359a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 360e13e19faSAndrew Thompson if (crd->is_read) { 361e13e19faSAndrew Thompson if (--(crd->rxfifo->refcount) == 0) { 3628437751dSAndrew Thompson cv_signal(&crd->rxfifo->cv_drain); 36302ac6454SAndrew Thompson } 364e13e19faSAndrew Thompson crd->is_read = 0; 36502ac6454SAndrew Thompson } 366e13e19faSAndrew Thompson if (crd->is_write) { 367e13e19faSAndrew Thompson if (--(crd->txfifo->refcount) == 0) { 3688437751dSAndrew Thompson cv_signal(&crd->txfifo->cv_drain); 36902ac6454SAndrew Thompson } 370e13e19faSAndrew Thompson crd->is_write = 0; 37102ac6454SAndrew Thompson } 372e13e19faSAndrew Thompson if (crd->is_uref) { 373e13e19faSAndrew Thompson crd->is_uref = 0; 374b78e84d1SHans Petter Selasky if (--(cpd->udev->refcount) == 0) 375d008478eSHans Petter Selasky cv_broadcast(&cpd->udev->ref_cv); 37602ac6454SAndrew Thompson } 377a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 37802ac6454SAndrew Thompson } 37902ac6454SAndrew Thompson 380760bc48eSAndrew Thompson static struct usb_fifo * 381e2723934SHans Petter Selasky usb_fifo_alloc(struct mtx *mtx) 38202ac6454SAndrew Thompson { 383760bc48eSAndrew Thompson struct usb_fifo *f; 38402ac6454SAndrew Thompson 38502ac6454SAndrew Thompson f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO); 3868437751dSAndrew Thompson cv_init(&f->cv_io, "FIFO-IO"); 3878437751dSAndrew Thompson cv_init(&f->cv_drain, "FIFO-DRAIN"); 3889b077d72SHans Petter Selasky sx_init(&f->fs_fastpath_lock, "FIFO-FP"); 389e2723934SHans Petter Selasky f->priv_mtx = mtx; 39002ac6454SAndrew Thompson f->refcount = 1; 391e2723934SHans Petter Selasky knlist_init_mtx(&f->selinfo.si_note, mtx); 39202ac6454SAndrew Thompson return (f); 39302ac6454SAndrew Thompson } 39402ac6454SAndrew Thompson 39502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 396a593f6b8SAndrew Thompson * usb_fifo_create 39702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 39802ac6454SAndrew Thompson static int 399a593f6b8SAndrew Thompson usb_fifo_create(struct usb_cdev_privdata *cpd, 400e13e19faSAndrew Thompson struct usb_cdev_refdata *crd) 40102ac6454SAndrew Thompson { 402760bc48eSAndrew Thompson struct usb_device *udev = cpd->udev; 403760bc48eSAndrew Thompson struct usb_fifo *f; 404ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 40502ac6454SAndrew Thompson uint8_t n; 40602ac6454SAndrew Thompson uint8_t is_tx; 40702ac6454SAndrew Thompson uint8_t is_rx; 40802ac6454SAndrew Thompson uint8_t no_null; 40902ac6454SAndrew Thompson uint8_t is_busy; 410ae60fdfbSAndrew Thompson int e = cpd->ep_addr; 41102ac6454SAndrew Thompson 412ee3e3ff5SAndrew Thompson is_tx = (cpd->fflags & FWRITE) ? 1 : 0; 413ee3e3ff5SAndrew Thompson is_rx = (cpd->fflags & FREAD) ? 1 : 0; 41402ac6454SAndrew Thompson no_null = 1; 41502ac6454SAndrew Thompson is_busy = 0; 41602ac6454SAndrew Thompson 417ee3e3ff5SAndrew Thompson /* Preallocated FIFO */ 418ae60fdfbSAndrew Thompson if (e < 0) { 419ee3e3ff5SAndrew Thompson DPRINTFN(5, "Preallocated FIFO\n"); 420ee3e3ff5SAndrew Thompson if (is_tx) { 421ee3e3ff5SAndrew Thompson f = udev->fifo[cpd->fifo_index + USB_FIFO_TX]; 422ee3e3ff5SAndrew Thompson if (f == NULL) 423ee3e3ff5SAndrew Thompson return (EINVAL); 424e13e19faSAndrew Thompson crd->txfifo = f; 425ee3e3ff5SAndrew Thompson } 426ee3e3ff5SAndrew Thompson if (is_rx) { 427ee3e3ff5SAndrew Thompson f = udev->fifo[cpd->fifo_index + USB_FIFO_RX]; 428ee3e3ff5SAndrew Thompson if (f == NULL) 429ee3e3ff5SAndrew Thompson return (EINVAL); 430e13e19faSAndrew Thompson crd->rxfifo = f; 431ee3e3ff5SAndrew Thompson } 432ee3e3ff5SAndrew Thompson return (0); 433ee3e3ff5SAndrew Thompson } 43402ac6454SAndrew Thompson 435ae60fdfbSAndrew Thompson KASSERT(e >= 0 && e <= 15, ("endpoint %d out of range", e)); 436ee3e3ff5SAndrew Thompson 437ee3e3ff5SAndrew Thompson /* search for a free FIFO slot */ 438ae60fdfbSAndrew Thompson DPRINTFN(5, "Endpoint device, searching for 0x%02x\n", e); 43902ac6454SAndrew Thompson for (n = 0;; n += 2) { 44002ac6454SAndrew Thompson if (n == USB_FIFO_MAX) { 44102ac6454SAndrew Thompson if (no_null) { 44202ac6454SAndrew Thompson no_null = 0; 44302ac6454SAndrew Thompson n = 0; 44402ac6454SAndrew Thompson } else { 44502ac6454SAndrew Thompson /* end of FIFOs reached */ 446ee3e3ff5SAndrew Thompson DPRINTFN(5, "out of FIFOs\n"); 44702ac6454SAndrew Thompson return (ENOMEM); 44802ac6454SAndrew Thompson } 44902ac6454SAndrew Thompson } 45002ac6454SAndrew Thompson /* Check for TX FIFO */ 45102ac6454SAndrew Thompson if (is_tx) { 45202ac6454SAndrew Thompson f = udev->fifo[n + USB_FIFO_TX]; 45302ac6454SAndrew Thompson if (f != NULL) { 454ae60fdfbSAndrew Thompson if (f->dev_ep_index != e) { 45502ac6454SAndrew Thompson /* wrong endpoint index */ 45602ac6454SAndrew Thompson continue; 45702ac6454SAndrew Thompson } 4587214348fSAndrew Thompson if (f->curr_cpd != NULL) { 45902ac6454SAndrew Thompson /* FIFO is opened */ 46002ac6454SAndrew Thompson is_busy = 1; 46102ac6454SAndrew Thompson continue; 46202ac6454SAndrew Thompson } 46302ac6454SAndrew Thompson } else if (no_null) { 46402ac6454SAndrew Thompson continue; 46502ac6454SAndrew Thompson } 46602ac6454SAndrew Thompson } 46702ac6454SAndrew Thompson /* Check for RX FIFO */ 46802ac6454SAndrew Thompson if (is_rx) { 46902ac6454SAndrew Thompson f = udev->fifo[n + USB_FIFO_RX]; 47002ac6454SAndrew Thompson if (f != NULL) { 471ae60fdfbSAndrew Thompson if (f->dev_ep_index != e) { 47202ac6454SAndrew Thompson /* wrong endpoint index */ 47302ac6454SAndrew Thompson continue; 47402ac6454SAndrew Thompson } 4757214348fSAndrew Thompson if (f->curr_cpd != NULL) { 47602ac6454SAndrew Thompson /* FIFO is opened */ 47702ac6454SAndrew Thompson is_busy = 1; 47802ac6454SAndrew Thompson continue; 47902ac6454SAndrew Thompson } 48002ac6454SAndrew Thompson } else if (no_null) { 48102ac6454SAndrew Thompson continue; 48202ac6454SAndrew Thompson } 48302ac6454SAndrew Thompson } 48402ac6454SAndrew Thompson break; 48502ac6454SAndrew Thompson } 48602ac6454SAndrew Thompson 48702ac6454SAndrew Thompson if (no_null == 0) { 488ae60fdfbSAndrew Thompson if (e >= (USB_EP_MAX / 2)) { 48902ac6454SAndrew Thompson /* we don't create any endpoints in this range */ 4907214348fSAndrew Thompson DPRINTFN(5, "ep out of range\n"); 49102ac6454SAndrew Thompson return (is_busy ? EBUSY : EINVAL); 49202ac6454SAndrew Thompson } 49302ac6454SAndrew Thompson } 4947214348fSAndrew Thompson 495ae60fdfbSAndrew Thompson if ((e != 0) && is_busy) { 4967214348fSAndrew Thompson /* 4977214348fSAndrew Thompson * Only the default control endpoint is allowed to be 4987214348fSAndrew Thompson * opened multiple times! 4997214348fSAndrew Thompson */ 5007214348fSAndrew Thompson DPRINTFN(5, "busy\n"); 5017214348fSAndrew Thompson return (EBUSY); 5027214348fSAndrew Thompson } 5037214348fSAndrew Thompson 50402ac6454SAndrew Thompson /* Check TX FIFO */ 50502ac6454SAndrew Thompson if (is_tx && 50602ac6454SAndrew Thompson (udev->fifo[n + USB_FIFO_TX] == NULL)) { 507a593f6b8SAndrew Thompson ep = usb_dev_get_ep(udev, e, USB_FIFO_TX); 508ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_TX); 509ae60fdfbSAndrew Thompson if (ep == NULL) { 510ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint returned NULL\n"); 51102ac6454SAndrew Thompson return (EINVAL); 51202ac6454SAndrew Thompson } 513e2723934SHans Petter Selasky f = usb_fifo_alloc(&udev->device_mtx); 51402ac6454SAndrew Thompson if (f == NULL) { 515ee3e3ff5SAndrew Thompson DPRINTFN(5, "could not alloc tx fifo\n"); 51602ac6454SAndrew Thompson return (ENOMEM); 51702ac6454SAndrew Thompson } 51802ac6454SAndrew Thompson /* update some fields */ 51902ac6454SAndrew Thompson f->fifo_index = n + USB_FIFO_TX; 520ae60fdfbSAndrew Thompson f->dev_ep_index = e; 521ae60fdfbSAndrew Thompson f->priv_sc0 = ep; 522a593f6b8SAndrew Thompson f->methods = &usb_ugen_methods; 523ae60fdfbSAndrew Thompson f->iface_index = ep->iface_index; 52402ac6454SAndrew Thompson f->udev = udev; 525a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 52602ac6454SAndrew Thompson udev->fifo[n + USB_FIFO_TX] = f; 527a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 52802ac6454SAndrew Thompson } 52902ac6454SAndrew Thompson /* Check RX FIFO */ 53002ac6454SAndrew Thompson if (is_rx && 53102ac6454SAndrew Thompson (udev->fifo[n + USB_FIFO_RX] == NULL)) { 532a593f6b8SAndrew Thompson ep = usb_dev_get_ep(udev, e, USB_FIFO_RX); 533ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_RX); 534ae60fdfbSAndrew Thompson if (ep == NULL) { 535ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint returned NULL\n"); 53602ac6454SAndrew Thompson return (EINVAL); 53702ac6454SAndrew Thompson } 538e2723934SHans Petter Selasky f = usb_fifo_alloc(&udev->device_mtx); 53902ac6454SAndrew Thompson if (f == NULL) { 540ee3e3ff5SAndrew Thompson DPRINTFN(5, "could not alloc rx fifo\n"); 54102ac6454SAndrew Thompson return (ENOMEM); 54202ac6454SAndrew Thompson } 54302ac6454SAndrew Thompson /* update some fields */ 54402ac6454SAndrew Thompson f->fifo_index = n + USB_FIFO_RX; 545ae60fdfbSAndrew Thompson f->dev_ep_index = e; 546ae60fdfbSAndrew Thompson f->priv_sc0 = ep; 547a593f6b8SAndrew Thompson f->methods = &usb_ugen_methods; 548ae60fdfbSAndrew Thompson f->iface_index = ep->iface_index; 54902ac6454SAndrew Thompson f->udev = udev; 550a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 55102ac6454SAndrew Thompson udev->fifo[n + USB_FIFO_RX] = f; 552a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 55302ac6454SAndrew Thompson } 55402ac6454SAndrew Thompson if (is_tx) { 555e13e19faSAndrew Thompson crd->txfifo = udev->fifo[n + USB_FIFO_TX]; 55602ac6454SAndrew Thompson } 55702ac6454SAndrew Thompson if (is_rx) { 558e13e19faSAndrew Thompson crd->rxfifo = udev->fifo[n + USB_FIFO_RX]; 55902ac6454SAndrew Thompson } 560ee3e3ff5SAndrew Thompson /* fill out fifo index */ 561ee3e3ff5SAndrew Thompson DPRINTFN(5, "fifo index = %d\n", n); 562ee3e3ff5SAndrew Thompson cpd->fifo_index = n; 56302ac6454SAndrew Thompson 56402ac6454SAndrew Thompson /* complete */ 56502ac6454SAndrew Thompson 56602ac6454SAndrew Thompson return (0); 56702ac6454SAndrew Thompson } 56802ac6454SAndrew Thompson 56902ac6454SAndrew Thompson void 570a593f6b8SAndrew Thompson usb_fifo_free(struct usb_fifo *f) 57102ac6454SAndrew Thompson { 57202ac6454SAndrew Thompson uint8_t n; 57302ac6454SAndrew Thompson 57402ac6454SAndrew Thompson if (f == NULL) { 57502ac6454SAndrew Thompson /* be NULL safe */ 57602ac6454SAndrew Thompson return; 57702ac6454SAndrew Thompson } 57802ac6454SAndrew Thompson /* destroy symlink devices, if any */ 57902ac6454SAndrew Thompson for (n = 0; n != 2; n++) { 58002ac6454SAndrew Thompson if (f->symlink[n]) { 581a593f6b8SAndrew Thompson usb_free_symlink(f->symlink[n]); 58202ac6454SAndrew Thompson f->symlink[n] = NULL; 58302ac6454SAndrew Thompson } 58402ac6454SAndrew Thompson } 585a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 58602ac6454SAndrew Thompson 58702ac6454SAndrew Thompson /* delink ourselves to stop calls from userland */ 58802ac6454SAndrew Thompson if ((f->fifo_index < USB_FIFO_MAX) && 58902ac6454SAndrew Thompson (f->udev != NULL) && 59002ac6454SAndrew Thompson (f->udev->fifo[f->fifo_index] == f)) { 59102ac6454SAndrew Thompson f->udev->fifo[f->fifo_index] = NULL; 59202ac6454SAndrew Thompson } else { 593767cb2e2SAndrew Thompson DPRINTFN(0, "USB FIFO %p has not been linked\n", f); 59402ac6454SAndrew Thompson } 59502ac6454SAndrew Thompson 59602ac6454SAndrew Thompson /* decrease refcount */ 59702ac6454SAndrew Thompson f->refcount--; 59802ac6454SAndrew Thompson /* need to wait until all callers have exited */ 59902ac6454SAndrew Thompson while (f->refcount != 0) { 600a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); /* avoid LOR */ 60102ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 602b78e84d1SHans Petter Selasky /* prevent write flush, if any */ 603b78e84d1SHans Petter Selasky f->flag_iserror = 1; 60402ac6454SAndrew Thompson /* get I/O thread out of any sleep state */ 60502ac6454SAndrew Thompson if (f->flag_sleeping) { 60602ac6454SAndrew Thompson f->flag_sleeping = 0; 6078437751dSAndrew Thompson cv_broadcast(&f->cv_io); 60802ac6454SAndrew Thompson } 60902ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 610a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 61102ac6454SAndrew Thompson 612c7d8c1c6SHans Petter Selasky /* 613c7d8c1c6SHans Petter Selasky * Check if the "f->refcount" variable reached zero 614c7d8c1c6SHans Petter Selasky * during the unlocked time before entering wait: 615c7d8c1c6SHans Petter Selasky */ 616c7d8c1c6SHans Petter Selasky if (f->refcount == 0) 617c7d8c1c6SHans Petter Selasky break; 618c7d8c1c6SHans Petter Selasky 61902ac6454SAndrew Thompson /* wait for sync */ 620a593f6b8SAndrew Thompson cv_wait(&f->cv_drain, &usb_ref_lock); 62102ac6454SAndrew Thompson } 622a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 62302ac6454SAndrew Thompson 62402ac6454SAndrew Thompson /* take care of closing the device here, if any */ 625a593f6b8SAndrew Thompson usb_fifo_close(f, 0); 62602ac6454SAndrew Thompson 6278437751dSAndrew Thompson cv_destroy(&f->cv_io); 6288437751dSAndrew Thompson cv_destroy(&f->cv_drain); 6299b077d72SHans Petter Selasky sx_destroy(&f->fs_fastpath_lock); 63002ac6454SAndrew Thompson 631a6609003SHans Petter Selasky knlist_clear(&f->selinfo.si_note, 0); 632a6609003SHans Petter Selasky seldrain(&f->selinfo); 633e2723934SHans Petter Selasky knlist_destroy(&f->selinfo.si_note); 634e2723934SHans Petter Selasky 63502ac6454SAndrew Thompson free(f, M_USBDEV); 63602ac6454SAndrew Thompson } 63702ac6454SAndrew Thompson 638ae60fdfbSAndrew Thompson static struct usb_endpoint * 639a593f6b8SAndrew Thompson usb_dev_get_ep(struct usb_device *udev, uint8_t ep_index, uint8_t dir) 64002ac6454SAndrew Thompson { 641ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 64202ac6454SAndrew Thompson uint8_t ep_dir; 64302ac6454SAndrew Thompson 64402ac6454SAndrew Thompson if (ep_index == 0) { 6455b3bb704SAndrew Thompson ep = &udev->ctrl_ep; 64602ac6454SAndrew Thompson } else { 64702ac6454SAndrew Thompson if (dir == USB_FIFO_RX) { 648f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_HOST) { 64902ac6454SAndrew Thompson ep_dir = UE_DIR_IN; 65002ac6454SAndrew Thompson } else { 65102ac6454SAndrew Thompson ep_dir = UE_DIR_OUT; 65202ac6454SAndrew Thompson } 65302ac6454SAndrew Thompson } else { 654f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_HOST) { 65502ac6454SAndrew Thompson ep_dir = UE_DIR_OUT; 65602ac6454SAndrew Thompson } else { 65702ac6454SAndrew Thompson ep_dir = UE_DIR_IN; 65802ac6454SAndrew Thompson } 65902ac6454SAndrew Thompson } 660a593f6b8SAndrew Thompson ep = usbd_get_ep_by_addr(udev, ep_index | ep_dir); 66102ac6454SAndrew Thompson } 66202ac6454SAndrew Thompson 663ae60fdfbSAndrew Thompson if (ep == NULL) { 664ae60fdfbSAndrew Thompson /* if the endpoint does not exist then return */ 66502ac6454SAndrew Thompson return (NULL); 66602ac6454SAndrew Thompson } 667ae60fdfbSAndrew Thompson if (ep->edesc == NULL) { 668ae60fdfbSAndrew Thompson /* invalid endpoint */ 66902ac6454SAndrew Thompson return (NULL); 67002ac6454SAndrew Thompson } 671ae60fdfbSAndrew Thompson return (ep); /* success */ 67202ac6454SAndrew Thompson } 67302ac6454SAndrew Thompson 67402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 675a593f6b8SAndrew Thompson * usb_fifo_open 67602ac6454SAndrew Thompson * 67702ac6454SAndrew Thompson * Returns: 67802ac6454SAndrew Thompson * 0: Success 67902ac6454SAndrew Thompson * Else: Failure 68002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 68102ac6454SAndrew Thompson static int 682a593f6b8SAndrew Thompson usb_fifo_open(struct usb_cdev_privdata *cpd, 683760bc48eSAndrew Thompson struct usb_fifo *f, int fflags) 68402ac6454SAndrew Thompson { 68502ac6454SAndrew Thompson int err; 68602ac6454SAndrew Thompson 68702ac6454SAndrew Thompson if (f == NULL) { 68802ac6454SAndrew Thompson /* no FIFO there */ 68902ac6454SAndrew Thompson DPRINTFN(2, "no FIFO\n"); 69002ac6454SAndrew Thompson return (ENXIO); 69102ac6454SAndrew Thompson } 69202ac6454SAndrew Thompson /* remove FWRITE and FREAD flags */ 69302ac6454SAndrew Thompson fflags &= ~(FWRITE | FREAD); 69402ac6454SAndrew Thompson 69502ac6454SAndrew Thompson /* set correct file flags */ 69602ac6454SAndrew Thompson if ((f->fifo_index & 1) == USB_FIFO_TX) { 69702ac6454SAndrew Thompson fflags |= FWRITE; 69802ac6454SAndrew Thompson } else { 69902ac6454SAndrew Thompson fflags |= FREAD; 70002ac6454SAndrew Thompson } 70102ac6454SAndrew Thompson 70202ac6454SAndrew Thompson /* check if we are already opened */ 70302ac6454SAndrew Thompson /* we don't need any locks when checking this variable */ 7047214348fSAndrew Thompson if (f->curr_cpd != NULL) { 70502ac6454SAndrew Thompson err = EBUSY; 70602ac6454SAndrew Thompson goto done; 70702ac6454SAndrew Thompson } 708ee3e3ff5SAndrew Thompson 7097214348fSAndrew Thompson /* reset short flag before open */ 7107214348fSAndrew Thompson f->flag_short = 0; 7117214348fSAndrew Thompson 71202ac6454SAndrew Thompson /* call open method */ 713ee3e3ff5SAndrew Thompson err = (f->methods->f_open) (f, fflags); 71402ac6454SAndrew Thompson if (err) { 71502ac6454SAndrew Thompson goto done; 71602ac6454SAndrew Thompson } 71702ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 71802ac6454SAndrew Thompson 71902ac6454SAndrew Thompson /* reset sleep flag */ 72002ac6454SAndrew Thompson f->flag_sleeping = 0; 72102ac6454SAndrew Thompson 72202ac6454SAndrew Thompson /* reset error flag */ 72302ac6454SAndrew Thompson f->flag_iserror = 0; 72402ac6454SAndrew Thompson 72502ac6454SAndrew Thompson /* reset complete flag */ 72602ac6454SAndrew Thompson f->flag_iscomplete = 0; 72702ac6454SAndrew Thompson 72802ac6454SAndrew Thompson /* reset select flag */ 72902ac6454SAndrew Thompson f->flag_isselect = 0; 73002ac6454SAndrew Thompson 73102ac6454SAndrew Thompson /* reset flushing flag */ 73202ac6454SAndrew Thompson f->flag_flushing = 0; 73302ac6454SAndrew Thompson 73402ac6454SAndrew Thompson /* reset ASYNC proc flag */ 73502ac6454SAndrew Thompson f->async_p = NULL; 73602ac6454SAndrew Thompson 737a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 7387214348fSAndrew Thompson /* flag the fifo as opened to prevent others */ 7397214348fSAndrew Thompson f->curr_cpd = cpd; 740a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 74102ac6454SAndrew Thompson 74202ac6454SAndrew Thompson /* reset queue */ 743a593f6b8SAndrew Thompson usb_fifo_reset(f); 74402ac6454SAndrew Thompson 74502ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 74602ac6454SAndrew Thompson done: 74702ac6454SAndrew Thompson return (err); 74802ac6454SAndrew Thompson } 74902ac6454SAndrew Thompson 75002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 751a593f6b8SAndrew Thompson * usb_fifo_reset 75202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 75302ac6454SAndrew Thompson void 754a593f6b8SAndrew Thompson usb_fifo_reset(struct usb_fifo *f) 75502ac6454SAndrew Thompson { 756760bc48eSAndrew Thompson struct usb_mbuf *m; 75702ac6454SAndrew Thompson 75802ac6454SAndrew Thompson if (f == NULL) { 75902ac6454SAndrew Thompson return; 76002ac6454SAndrew Thompson } 76102ac6454SAndrew Thompson while (1) { 76202ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 76302ac6454SAndrew Thompson if (m) { 76402ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 76502ac6454SAndrew Thompson } else { 76602ac6454SAndrew Thompson break; 76702ac6454SAndrew Thompson } 76802ac6454SAndrew Thompson } 769bd73b187SAlfred Perlstein /* reset have fragment flag */ 770bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 77102ac6454SAndrew Thompson } 77202ac6454SAndrew Thompson 77302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 774a593f6b8SAndrew Thompson * usb_fifo_close 77502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 77602ac6454SAndrew Thompson static void 777a593f6b8SAndrew Thompson usb_fifo_close(struct usb_fifo *f, int fflags) 77802ac6454SAndrew Thompson { 77902ac6454SAndrew Thompson int err; 78002ac6454SAndrew Thompson 78102ac6454SAndrew Thompson /* check if we are not opened */ 7827214348fSAndrew Thompson if (f->curr_cpd == NULL) { 78302ac6454SAndrew Thompson /* nothing to do - already closed */ 78402ac6454SAndrew Thompson return; 78502ac6454SAndrew Thompson } 78602ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 78702ac6454SAndrew Thompson 7887214348fSAndrew Thompson /* clear current cdev private data pointer */ 7890b6d54d4SHans Petter Selasky mtx_lock(&usb_ref_lock); 7907214348fSAndrew Thompson f->curr_cpd = NULL; 7910b6d54d4SHans Petter Selasky mtx_unlock(&usb_ref_lock); 7920b6d54d4SHans Petter Selasky 7930b6d54d4SHans Petter Selasky /* check if we are watched by kevent */ 7940b6d54d4SHans Petter Selasky KNOTE_LOCKED(&f->selinfo.si_note, 0); 79502ac6454SAndrew Thompson 79602ac6454SAndrew Thompson /* check if we are selected */ 79702ac6454SAndrew Thompson if (f->flag_isselect) { 79802ac6454SAndrew Thompson selwakeup(&f->selinfo); 79902ac6454SAndrew Thompson f->flag_isselect = 0; 80002ac6454SAndrew Thompson } 80102ac6454SAndrew Thompson /* check if a thread wants SIGIO */ 80202ac6454SAndrew Thompson if (f->async_p != NULL) { 80302ac6454SAndrew Thompson PROC_LOCK(f->async_p); 8048451d0ddSKip Macy kern_psignal(f->async_p, SIGIO); 80502ac6454SAndrew Thompson PROC_UNLOCK(f->async_p); 80602ac6454SAndrew Thompson f->async_p = NULL; 80702ac6454SAndrew Thompson } 80802ac6454SAndrew Thompson /* remove FWRITE and FREAD flags */ 80902ac6454SAndrew Thompson fflags &= ~(FWRITE | FREAD); 81002ac6454SAndrew Thompson 81102ac6454SAndrew Thompson /* flush written data, if any */ 81202ac6454SAndrew Thompson if ((f->fifo_index & 1) == USB_FIFO_TX) { 81302ac6454SAndrew Thompson if (!f->flag_iserror) { 81402ac6454SAndrew Thompson /* set flushing flag */ 81502ac6454SAndrew Thompson f->flag_flushing = 1; 81602ac6454SAndrew Thompson 817bd73b187SAlfred Perlstein /* get the last packet in */ 818bd73b187SAlfred Perlstein if (f->flag_have_fragment) { 819bd73b187SAlfred Perlstein struct usb_mbuf *m; 820bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 821bd73b187SAlfred Perlstein USB_IF_DEQUEUE(&f->free_q, m); 822bd73b187SAlfred Perlstein if (m) { 823bd73b187SAlfred Perlstein USB_IF_ENQUEUE(&f->used_q, m); 824bd73b187SAlfred Perlstein } 825bd73b187SAlfred Perlstein } 826bd73b187SAlfred Perlstein 82702ac6454SAndrew Thompson /* start write transfer, if not already started */ 82802ac6454SAndrew Thompson (f->methods->f_start_write) (f); 82902ac6454SAndrew Thompson 83002ac6454SAndrew Thompson /* check if flushed already */ 83102ac6454SAndrew Thompson while (f->flag_flushing && 83202ac6454SAndrew Thompson (!f->flag_iserror)) { 83302ac6454SAndrew Thompson /* wait until all data has been written */ 83402ac6454SAndrew Thompson f->flag_sleeping = 1; 835b6630e50SHans Petter Selasky err = cv_timedwait_sig(&f->cv_io, f->priv_mtx, 836b6630e50SHans Petter Selasky USB_MS_TO_TICKS(USB_DEFAULT_TIMEOUT)); 83702ac6454SAndrew Thompson if (err) { 83802ac6454SAndrew Thompson DPRINTF("signal received\n"); 83902ac6454SAndrew Thompson break; 84002ac6454SAndrew Thompson } 84102ac6454SAndrew Thompson } 84202ac6454SAndrew Thompson } 84302ac6454SAndrew Thompson fflags |= FWRITE; 84402ac6454SAndrew Thompson 84502ac6454SAndrew Thompson /* stop write transfer, if not already stopped */ 84602ac6454SAndrew Thompson (f->methods->f_stop_write) (f); 84702ac6454SAndrew Thompson } else { 84802ac6454SAndrew Thompson fflags |= FREAD; 84902ac6454SAndrew Thompson 85002ac6454SAndrew Thompson /* stop write transfer, if not already stopped */ 85102ac6454SAndrew Thompson (f->methods->f_stop_read) (f); 85202ac6454SAndrew Thompson } 85302ac6454SAndrew Thompson 85402ac6454SAndrew Thompson /* check if we are sleeping */ 85502ac6454SAndrew Thompson if (f->flag_sleeping) { 85602ac6454SAndrew Thompson DPRINTFN(2, "Sleeping at close!\n"); 85702ac6454SAndrew Thompson } 85802ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 85902ac6454SAndrew Thompson 86002ac6454SAndrew Thompson /* call close method */ 861ee3e3ff5SAndrew Thompson (f->methods->f_close) (f, fflags); 86202ac6454SAndrew Thompson 86302ac6454SAndrew Thompson DPRINTF("closed\n"); 86402ac6454SAndrew Thompson } 86502ac6454SAndrew Thompson 86602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 867a593f6b8SAndrew Thompson * usb_open - cdev callback 86802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 86902ac6454SAndrew Thompson static int 870a593f6b8SAndrew Thompson usb_open(struct cdev *dev, int fflags, int devtype, struct thread *td) 87102ac6454SAndrew Thompson { 872760bc48eSAndrew Thompson struct usb_fs_privdata* pd = (struct usb_fs_privdata*)dev->si_drv1; 873e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 874760bc48eSAndrew Thompson struct usb_cdev_privdata *cpd; 875151ba793SAlexander Kabaev int err; 87602ac6454SAndrew Thompson 8777870adb6SEd Schouten DPRINTFN(2, "%s fflags=0x%08x\n", devtoname(dev), fflags); 87802ac6454SAndrew Thompson 879ee3e3ff5SAndrew Thompson KASSERT(fflags & (FREAD|FWRITE), ("invalid open flags")); 880ee3e3ff5SAndrew Thompson if (((fflags & FREAD) && !(pd->mode & FREAD)) || 881ee3e3ff5SAndrew Thompson ((fflags & FWRITE) && !(pd->mode & FWRITE))) { 882ee3e3ff5SAndrew Thompson DPRINTFN(2, "access mode not supported\n"); 88302ac6454SAndrew Thompson return (EPERM); 88402ac6454SAndrew Thompson } 885ee3e3ff5SAndrew Thompson 886ee3e3ff5SAndrew Thompson cpd = malloc(sizeof(*cpd), M_USBDEV, M_WAITOK | M_ZERO); 887ee3e3ff5SAndrew Thompson 888a593f6b8SAndrew Thompson usb_loc_fill(pd, cpd); 889a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 1); 89002ac6454SAndrew Thompson if (err) { 89102ac6454SAndrew Thompson DPRINTFN(2, "cannot ref device\n"); 892ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 89302ac6454SAndrew Thompson return (ENXIO); 89402ac6454SAndrew Thompson } 895ee3e3ff5SAndrew Thompson cpd->fflags = fflags; /* access mode for open lifetime */ 89602ac6454SAndrew Thompson 89702ac6454SAndrew Thompson /* create FIFOs, if any */ 898a593f6b8SAndrew Thompson err = usb_fifo_create(cpd, &refs); 89902ac6454SAndrew Thompson /* check for error */ 90002ac6454SAndrew Thompson if (err) { 901ee3e3ff5SAndrew Thompson DPRINTFN(2, "cannot create fifo\n"); 902a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 903ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 90402ac6454SAndrew Thompson return (err); 90502ac6454SAndrew Thompson } 90602ac6454SAndrew Thompson if (fflags & FREAD) { 907a593f6b8SAndrew Thompson err = usb_fifo_open(cpd, refs.rxfifo, fflags); 90802ac6454SAndrew Thompson if (err) { 90902ac6454SAndrew Thompson DPRINTFN(2, "read open failed\n"); 910a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 911ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 91202ac6454SAndrew Thompson return (err); 91302ac6454SAndrew Thompson } 91402ac6454SAndrew Thompson } 91502ac6454SAndrew Thompson if (fflags & FWRITE) { 916a593f6b8SAndrew Thompson err = usb_fifo_open(cpd, refs.txfifo, fflags); 91702ac6454SAndrew Thompson if (err) { 91802ac6454SAndrew Thompson DPRINTFN(2, "write open failed\n"); 91902ac6454SAndrew Thompson if (fflags & FREAD) { 920a593f6b8SAndrew Thompson usb_fifo_close(refs.rxfifo, fflags); 92102ac6454SAndrew Thompson } 922a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 923ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 92402ac6454SAndrew Thompson return (err); 92502ac6454SAndrew Thompson } 92602ac6454SAndrew Thompson } 927a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 928a593f6b8SAndrew Thompson devfs_set_cdevpriv(cpd, usb_close); 92902ac6454SAndrew Thompson 930ee3e3ff5SAndrew Thompson return (0); 93102ac6454SAndrew Thompson } 93202ac6454SAndrew Thompson 93302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 934a593f6b8SAndrew Thompson * usb_close - cdev callback 93502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 93602ac6454SAndrew Thompson static void 937a593f6b8SAndrew Thompson usb_close(void *arg) 93802ac6454SAndrew Thompson { 939e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 940760bc48eSAndrew Thompson struct usb_cdev_privdata *cpd = arg; 941ee3e3ff5SAndrew Thompson int err; 94202ac6454SAndrew Thompson 9437214348fSAndrew Thompson DPRINTFN(2, "cpd=%p\n", cpd); 944ee3e3ff5SAndrew Thompson 945f97da128SHans Petter Selasky err = usb_ref_device(cpd, &refs, 946f97da128SHans Petter Selasky 2 /* uref and allow detached state */); 947f97da128SHans Petter Selasky if (err) { 9485c8c627bSHans Petter Selasky DPRINTFN(2, "Cannot grab USB reference when " 949f97da128SHans Petter Selasky "closing USB file handle\n"); 950fbaee0f1SHans Petter Selasky goto done; 95102ac6454SAndrew Thompson } 952ee3e3ff5SAndrew Thompson if (cpd->fflags & FREAD) { 953a593f6b8SAndrew Thompson usb_fifo_close(refs.rxfifo, cpd->fflags); 95402ac6454SAndrew Thompson } 955ee3e3ff5SAndrew Thompson if (cpd->fflags & FWRITE) { 956a593f6b8SAndrew Thompson usb_fifo_close(refs.txfifo, cpd->fflags); 95702ac6454SAndrew Thompson } 958a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 959fbaee0f1SHans Petter Selasky done: 960ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 96102ac6454SAndrew Thompson } 96202ac6454SAndrew Thompson 96302ac6454SAndrew Thompson static void 964a593f6b8SAndrew Thompson usb_dev_init(void *arg) 96502ac6454SAndrew Thompson { 966a593f6b8SAndrew Thompson mtx_init(&usb_ref_lock, "USB ref mutex", NULL, MTX_DEF); 967a593f6b8SAndrew Thompson sx_init(&usb_sym_lock, "USB sym mutex"); 968a593f6b8SAndrew Thompson TAILQ_INIT(&usb_sym_head); 96902ac6454SAndrew Thompson 97002ac6454SAndrew Thompson /* check the UGEN methods */ 971a593f6b8SAndrew Thompson usb_fifo_check_methods(&usb_ugen_methods); 97202ac6454SAndrew Thompson } 97302ac6454SAndrew Thompson 974a593f6b8SAndrew Thompson SYSINIT(usb_dev_init, SI_SUB_KLD, SI_ORDER_FIRST, usb_dev_init, NULL); 97502ac6454SAndrew Thompson 97602ac6454SAndrew Thompson static void 977a593f6b8SAndrew Thompson usb_dev_init_post(void *arg) 97802ac6454SAndrew Thompson { 97902ac6454SAndrew Thompson /* 980ee3e3ff5SAndrew Thompson * Create /dev/usb - this is needed for usbconfig(8), which 981ee3e3ff5SAndrew Thompson * needs a well-known device name to access. 98202ac6454SAndrew Thompson */ 983a593f6b8SAndrew Thompson usb_dev = make_dev(&usb_static_devsw, 0, UID_ROOT, GID_OPERATOR, 984ee3e3ff5SAndrew Thompson 0644, USB_DEVICE_NAME); 985a593f6b8SAndrew Thompson if (usb_dev == NULL) { 986767cb2e2SAndrew Thompson DPRINTFN(0, "Could not create usb bus device\n"); 98702ac6454SAndrew Thompson } 98802ac6454SAndrew Thompson } 98902ac6454SAndrew Thompson 990a593f6b8SAndrew Thompson SYSINIT(usb_dev_init_post, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, usb_dev_init_post, NULL); 99102ac6454SAndrew Thompson 99202ac6454SAndrew Thompson static void 993a593f6b8SAndrew Thompson usb_dev_uninit(void *arg) 99402ac6454SAndrew Thompson { 995a593f6b8SAndrew Thompson if (usb_dev != NULL) { 996a593f6b8SAndrew Thompson destroy_dev(usb_dev); 997a593f6b8SAndrew Thompson usb_dev = NULL; 99802ac6454SAndrew Thompson } 999a593f6b8SAndrew Thompson mtx_destroy(&usb_ref_lock); 1000a593f6b8SAndrew Thompson sx_destroy(&usb_sym_lock); 100102ac6454SAndrew Thompson } 100202ac6454SAndrew Thompson 1003a593f6b8SAndrew Thompson SYSUNINIT(usb_dev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb_dev_uninit, NULL); 100402ac6454SAndrew Thompson 100502ac6454SAndrew Thompson static int 1006a593f6b8SAndrew Thompson usb_ioctl_f_sub(struct usb_fifo *f, u_long cmd, void *addr, 100702ac6454SAndrew Thompson struct thread *td) 100802ac6454SAndrew Thompson { 100902ac6454SAndrew Thompson int error = 0; 101002ac6454SAndrew Thompson 101102ac6454SAndrew Thompson switch (cmd) { 101202ac6454SAndrew Thompson case FIODTYPE: 101302ac6454SAndrew Thompson *(int *)addr = 0; /* character device */ 101402ac6454SAndrew Thompson break; 101502ac6454SAndrew Thompson 101602ac6454SAndrew Thompson case FIONBIO: 101702ac6454SAndrew Thompson /* handled by upper FS layer */ 101802ac6454SAndrew Thompson break; 101902ac6454SAndrew Thompson 102002ac6454SAndrew Thompson case FIOASYNC: 102102ac6454SAndrew Thompson if (*(int *)addr) { 102202ac6454SAndrew Thompson if (f->async_p != NULL) { 102302ac6454SAndrew Thompson error = EBUSY; 102402ac6454SAndrew Thompson break; 102502ac6454SAndrew Thompson } 102602ac6454SAndrew Thompson f->async_p = USB_TD_GET_PROC(td); 102702ac6454SAndrew Thompson } else { 102802ac6454SAndrew Thompson f->async_p = NULL; 102902ac6454SAndrew Thompson } 103002ac6454SAndrew Thompson break; 103102ac6454SAndrew Thompson 103202ac6454SAndrew Thompson /* XXX this is not the most general solution */ 103302ac6454SAndrew Thompson case TIOCSPGRP: 103402ac6454SAndrew Thompson if (f->async_p == NULL) { 103502ac6454SAndrew Thompson error = EINVAL; 103602ac6454SAndrew Thompson break; 103702ac6454SAndrew Thompson } 103802ac6454SAndrew Thompson if (*(int *)addr != USB_PROC_GET_GID(f->async_p)) { 103902ac6454SAndrew Thompson error = EPERM; 104002ac6454SAndrew Thompson break; 104102ac6454SAndrew Thompson } 104202ac6454SAndrew Thompson break; 104302ac6454SAndrew Thompson default: 104402ac6454SAndrew Thompson return (ENOIOCTL); 104502ac6454SAndrew Thompson } 1046cba30496SAndrew Thompson DPRINTFN(3, "cmd 0x%lx = %d\n", cmd, error); 104702ac6454SAndrew Thompson return (error); 104802ac6454SAndrew Thompson } 104902ac6454SAndrew Thompson 1050ee3e3ff5SAndrew Thompson /*------------------------------------------------------------------------* 1051a593f6b8SAndrew Thompson * usb_ioctl - cdev callback 1052ee3e3ff5SAndrew Thompson *------------------------------------------------------------------------*/ 105302ac6454SAndrew Thompson static int 1054a593f6b8SAndrew Thompson usb_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int fflag, struct thread* td) 105502ac6454SAndrew Thompson { 1056e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1057760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1058760bc48eSAndrew Thompson struct usb_fifo *f; 105902ac6454SAndrew Thompson int fflags; 106002ac6454SAndrew Thompson int err; 106102ac6454SAndrew Thompson 1062cba30496SAndrew Thompson DPRINTFN(2, "cmd=0x%lx\n", cmd); 1063cba30496SAndrew Thompson 1064ee3e3ff5SAndrew Thompson err = devfs_get_cdevpriv((void **)&cpd); 1065ee3e3ff5SAndrew Thompson if (err != 0) 1066ee3e3ff5SAndrew Thompson return (err); 1067ee3e3ff5SAndrew Thompson 1068f5f145baSAndrew Thompson /* 1069e13e19faSAndrew Thompson * Performance optimisation: We try to check for IOCTL's that 1070f5f145baSAndrew Thompson * don't need the USB reference first. Then we grab the USB 1071f5f145baSAndrew Thompson * reference if we need it! 1072f5f145baSAndrew Thompson */ 1073a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 1074cb18f7d1SAlfred Perlstein if (err) 107502ac6454SAndrew Thompson return (ENXIO); 1076cb18f7d1SAlfred Perlstein 1077ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 107802ac6454SAndrew Thompson 107902ac6454SAndrew Thompson f = NULL; /* set default value */ 108002ac6454SAndrew Thompson err = ENOIOCTL; /* set default value */ 108102ac6454SAndrew Thompson 108202ac6454SAndrew Thompson if (fflags & FWRITE) { 1083e13e19faSAndrew Thompson f = refs.txfifo; 1084a593f6b8SAndrew Thompson err = usb_ioctl_f_sub(f, cmd, addr, td); 108502ac6454SAndrew Thompson } 108602ac6454SAndrew Thompson if (fflags & FREAD) { 1087e13e19faSAndrew Thompson f = refs.rxfifo; 1088a593f6b8SAndrew Thompson err = usb_ioctl_f_sub(f, cmd, addr, td); 108902ac6454SAndrew Thompson } 1090ee3e3ff5SAndrew Thompson KASSERT(f != NULL, ("fifo not found")); 10918f9750b7SHans Petter Selasky if (err != ENOIOCTL) 10928f9750b7SHans Petter Selasky goto done; 10938f9750b7SHans Petter Selasky 1094ee3e3ff5SAndrew Thompson err = (f->methods->f_ioctl) (f, cmd, addr, fflags); 10958f9750b7SHans Petter Selasky 1096cba30496SAndrew Thompson DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err); 10978f9750b7SHans Petter Selasky 10988f9750b7SHans Petter Selasky if (err != ENOIOCTL) 10998f9750b7SHans Petter Selasky goto done; 11008f9750b7SHans Petter Selasky 1101a593f6b8SAndrew Thompson if (usb_usb_ref_device(cpd, &refs)) { 1102b06d477bSHans Petter Selasky /* we lost the reference */ 1103b06d477bSHans Petter Selasky return (ENXIO); 110402ac6454SAndrew Thompson } 11058f9750b7SHans Petter Selasky 1106ee3e3ff5SAndrew Thompson err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags); 11078f9750b7SHans Petter Selasky 1108cba30496SAndrew Thompson DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err); 11098f9750b7SHans Petter Selasky 11108f9750b7SHans Petter Selasky if (err == ENOIOCTL) 111102ac6454SAndrew Thompson err = ENOTTY; 11128f9750b7SHans Petter Selasky 11138f9750b7SHans Petter Selasky if (err) 11148f9750b7SHans Petter Selasky goto done; 11158f9750b7SHans Petter Selasky 11168f9750b7SHans Petter Selasky /* Wait for re-enumeration, if any */ 11178f9750b7SHans Petter Selasky 1118d6f4a9f9SHans Petter Selasky while (f->udev->re_enumerate_wait != USB_RE_ENUM_DONE) { 11198f9750b7SHans Petter Selasky usb_unref_device(cpd, &refs); 11208f9750b7SHans Petter Selasky 11218f9750b7SHans Petter Selasky usb_pause_mtx(NULL, hz / 128); 11228f9750b7SHans Petter Selasky 1123d64e9217SHans Petter Selasky while (usb_ref_device(cpd, &refs, 1 /* need uref */)) { 1124d64e9217SHans Petter Selasky if (usb_ref_device(cpd, &refs, 0)) { 1125b06d477bSHans Petter Selasky /* device no longer exists */ 1126b06d477bSHans Petter Selasky return (ENXIO); 112702ac6454SAndrew Thompson } 1128d64e9217SHans Petter Selasky usb_unref_device(cpd, &refs); 1129d64e9217SHans Petter Selasky usb_pause_mtx(NULL, hz / 128); 1130d64e9217SHans Petter Selasky } 11318f9750b7SHans Petter Selasky } 11328f9750b7SHans Petter Selasky 113302ac6454SAndrew Thompson done: 1134a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 113502ac6454SAndrew Thompson return (err); 113602ac6454SAndrew Thompson } 113702ac6454SAndrew Thompson 11380b6d54d4SHans Petter Selasky static void 11390b6d54d4SHans Petter Selasky usb_filter_detach(struct knote *kn) 11400b6d54d4SHans Petter Selasky { 11410b6d54d4SHans Petter Selasky struct usb_fifo *f = kn->kn_hook; 11420b6d54d4SHans Petter Selasky knlist_remove(&f->selinfo.si_note, kn, 0); 11430b6d54d4SHans Petter Selasky } 11440b6d54d4SHans Petter Selasky 11450b6d54d4SHans Petter Selasky static int 11460b6d54d4SHans Petter Selasky usb_filter_write(struct knote *kn, long hint) 11470b6d54d4SHans Petter Selasky { 11480b6d54d4SHans Petter Selasky struct usb_cdev_privdata* cpd; 11490b6d54d4SHans Petter Selasky struct usb_fifo *f; 11500b6d54d4SHans Petter Selasky struct usb_mbuf *m; 11510b6d54d4SHans Petter Selasky 11520b6d54d4SHans Petter Selasky DPRINTFN(2, "\n"); 11530b6d54d4SHans Petter Selasky 11540b6d54d4SHans Petter Selasky f = kn->kn_hook; 11550b6d54d4SHans Petter Selasky 11560eb8d462SHans Petter Selasky USB_MTX_ASSERT(f->priv_mtx, MA_OWNED); 11570b6d54d4SHans Petter Selasky 11580b6d54d4SHans Petter Selasky cpd = f->curr_cpd; 11590b6d54d4SHans Petter Selasky if (cpd == NULL) { 11600b6d54d4SHans Petter Selasky m = (void *)1; 11610b6d54d4SHans Petter Selasky } else if (f->fs_ep_max == 0) { 11620b6d54d4SHans Petter Selasky if (f->flag_iserror) { 11630b6d54d4SHans Petter Selasky /* we got an error */ 11640b6d54d4SHans Petter Selasky m = (void *)1; 11650b6d54d4SHans Petter Selasky } else { 11660b6d54d4SHans Petter Selasky if (f->queue_data == NULL) { 11670b6d54d4SHans Petter Selasky /* 11680b6d54d4SHans Petter Selasky * start write transfer, if not 11690b6d54d4SHans Petter Selasky * already started 11700b6d54d4SHans Petter Selasky */ 11710b6d54d4SHans Petter Selasky (f->methods->f_start_write) (f); 11720b6d54d4SHans Petter Selasky } 11730b6d54d4SHans Petter Selasky /* check if any packets are available */ 11740b6d54d4SHans Petter Selasky USB_IF_POLL(&f->free_q, m); 11750b6d54d4SHans Petter Selasky } 11760b6d54d4SHans Petter Selasky } else { 11770b6d54d4SHans Petter Selasky if (f->flag_iscomplete) { 11780b6d54d4SHans Petter Selasky m = (void *)1; 11790b6d54d4SHans Petter Selasky } else { 11800b6d54d4SHans Petter Selasky m = NULL; 11810b6d54d4SHans Petter Selasky } 11820b6d54d4SHans Petter Selasky } 11830b6d54d4SHans Petter Selasky return (m ? 1 : 0); 11840b6d54d4SHans Petter Selasky } 11850b6d54d4SHans Petter Selasky 11860b6d54d4SHans Petter Selasky static int 11870b6d54d4SHans Petter Selasky usb_filter_read(struct knote *kn, long hint) 11880b6d54d4SHans Petter Selasky { 11890b6d54d4SHans Petter Selasky struct usb_cdev_privdata* cpd; 11900b6d54d4SHans Petter Selasky struct usb_fifo *f; 11910b6d54d4SHans Petter Selasky struct usb_mbuf *m; 11920b6d54d4SHans Petter Selasky 11930b6d54d4SHans Petter Selasky DPRINTFN(2, "\n"); 11940b6d54d4SHans Petter Selasky 11950b6d54d4SHans Petter Selasky f = kn->kn_hook; 11960b6d54d4SHans Petter Selasky 11970eb8d462SHans Petter Selasky USB_MTX_ASSERT(f->priv_mtx, MA_OWNED); 11980b6d54d4SHans Petter Selasky 11990b6d54d4SHans Petter Selasky cpd = f->curr_cpd; 12000b6d54d4SHans Petter Selasky if (cpd == NULL) { 12010b6d54d4SHans Petter Selasky m = (void *)1; 12020b6d54d4SHans Petter Selasky } else if (f->fs_ep_max == 0) { 12030b6d54d4SHans Petter Selasky if (f->flag_iserror) { 12040b6d54d4SHans Petter Selasky /* we have an error */ 12050b6d54d4SHans Petter Selasky m = (void *)1; 12060b6d54d4SHans Petter Selasky } else { 12070b6d54d4SHans Petter Selasky if (f->queue_data == NULL) { 12080b6d54d4SHans Petter Selasky /* 12090b6d54d4SHans Petter Selasky * start read transfer, if not 12100b6d54d4SHans Petter Selasky * already started 12110b6d54d4SHans Petter Selasky */ 12120b6d54d4SHans Petter Selasky (f->methods->f_start_read) (f); 12130b6d54d4SHans Petter Selasky } 12140b6d54d4SHans Petter Selasky /* check if any packets are available */ 12150b6d54d4SHans Petter Selasky USB_IF_POLL(&f->used_q, m); 12160b6d54d4SHans Petter Selasky 12170b6d54d4SHans Petter Selasky /* start reading data, if any */ 12180b6d54d4SHans Petter Selasky if (m == NULL) 12190b6d54d4SHans Petter Selasky (f->methods->f_start_read) (f); 12200b6d54d4SHans Petter Selasky } 12210b6d54d4SHans Petter Selasky } else { 12220b6d54d4SHans Petter Selasky if (f->flag_iscomplete) { 12230b6d54d4SHans Petter Selasky m = (void *)1; 12240b6d54d4SHans Petter Selasky } else { 12250b6d54d4SHans Petter Selasky m = NULL; 12260b6d54d4SHans Petter Selasky } 12270b6d54d4SHans Petter Selasky } 12280b6d54d4SHans Petter Selasky return (m ? 1 : 0); 12290b6d54d4SHans Petter Selasky } 12300b6d54d4SHans Petter Selasky 1231*ef9ffb85SMark Johnston static const struct filterops usb_filtops_write = { 12320b6d54d4SHans Petter Selasky .f_isfd = 1, 12330b6d54d4SHans Petter Selasky .f_detach = usb_filter_detach, 12340b6d54d4SHans Petter Selasky .f_event = usb_filter_write, 12350b6d54d4SHans Petter Selasky }; 12360b6d54d4SHans Petter Selasky 1237*ef9ffb85SMark Johnston static const struct filterops usb_filtops_read = { 12380b6d54d4SHans Petter Selasky .f_isfd = 1, 12390b6d54d4SHans Petter Selasky .f_detach = usb_filter_detach, 12400b6d54d4SHans Petter Selasky .f_event = usb_filter_read, 12410b6d54d4SHans Petter Selasky }; 12420b6d54d4SHans Petter Selasky 12430b6d54d4SHans Petter Selasky /* ARGSUSED */ 12440b6d54d4SHans Petter Selasky static int 12450b6d54d4SHans Petter Selasky usb_kqfilter(struct cdev* dev, struct knote *kn) 12460b6d54d4SHans Petter Selasky { 12470b6d54d4SHans Petter Selasky struct usb_cdev_refdata refs; 12480b6d54d4SHans Petter Selasky struct usb_cdev_privdata* cpd; 12490b6d54d4SHans Petter Selasky struct usb_fifo *f; 12500b6d54d4SHans Petter Selasky int fflags; 12510b6d54d4SHans Petter Selasky int err = EINVAL; 12520b6d54d4SHans Petter Selasky 12530b6d54d4SHans Petter Selasky DPRINTFN(2, "\n"); 12540b6d54d4SHans Petter Selasky 12550b6d54d4SHans Petter Selasky if (devfs_get_cdevpriv((void **)&cpd) != 0 || 12560b6d54d4SHans Petter Selasky usb_ref_device(cpd, &refs, 0) != 0) 12570b6d54d4SHans Petter Selasky return (ENXIO); 12580b6d54d4SHans Petter Selasky 12590b6d54d4SHans Petter Selasky fflags = cpd->fflags; 12600b6d54d4SHans Petter Selasky 12610b6d54d4SHans Petter Selasky /* Figure out who needs service */ 12620b6d54d4SHans Petter Selasky switch (kn->kn_filter) { 12630b6d54d4SHans Petter Selasky case EVFILT_WRITE: 12640b6d54d4SHans Petter Selasky if (fflags & FWRITE) { 12650b6d54d4SHans Petter Selasky f = refs.txfifo; 12660b6d54d4SHans Petter Selasky kn->kn_fop = &usb_filtops_write; 12670b6d54d4SHans Petter Selasky err = 0; 12680b6d54d4SHans Petter Selasky } 12690b6d54d4SHans Petter Selasky break; 12700b6d54d4SHans Petter Selasky case EVFILT_READ: 12710b6d54d4SHans Petter Selasky if (fflags & FREAD) { 12720b6d54d4SHans Petter Selasky f = refs.rxfifo; 12730b6d54d4SHans Petter Selasky kn->kn_fop = &usb_filtops_read; 12740b6d54d4SHans Petter Selasky err = 0; 12750b6d54d4SHans Petter Selasky } 12760b6d54d4SHans Petter Selasky break; 12770b6d54d4SHans Petter Selasky default: 12780b6d54d4SHans Petter Selasky err = EOPNOTSUPP; 12790b6d54d4SHans Petter Selasky break; 12800b6d54d4SHans Petter Selasky } 12810b6d54d4SHans Petter Selasky 12820b6d54d4SHans Petter Selasky if (err == 0) { 12830b6d54d4SHans Petter Selasky kn->kn_hook = f; 12840b6d54d4SHans Petter Selasky mtx_lock(f->priv_mtx); 12850b6d54d4SHans Petter Selasky knlist_add(&f->selinfo.si_note, kn, 1); 12860b6d54d4SHans Petter Selasky mtx_unlock(f->priv_mtx); 12870b6d54d4SHans Petter Selasky } 12880b6d54d4SHans Petter Selasky 12890b6d54d4SHans Petter Selasky usb_unref_device(cpd, &refs); 12900b6d54d4SHans Petter Selasky return (err); 12910b6d54d4SHans Petter Selasky } 12920b6d54d4SHans Petter Selasky 129302ac6454SAndrew Thompson /* ARGSUSED */ 129402ac6454SAndrew Thompson static int 1295a593f6b8SAndrew Thompson usb_poll(struct cdev* dev, int events, struct thread* td) 129602ac6454SAndrew Thompson { 1297e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1298760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1299760bc48eSAndrew Thompson struct usb_fifo *f; 1300760bc48eSAndrew Thompson struct usb_mbuf *m; 13018a93629eSAndrew Thompson int fflags, revents; 130202ac6454SAndrew Thompson 13038a93629eSAndrew Thompson if (devfs_get_cdevpriv((void **)&cpd) != 0 || 1304a593f6b8SAndrew Thompson usb_ref_device(cpd, &refs, 0) != 0) 13058a93629eSAndrew Thompson return (events & 13068a93629eSAndrew Thompson (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); 1307ee3e3ff5SAndrew Thompson 1308ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 130902ac6454SAndrew Thompson 131002ac6454SAndrew Thompson /* Figure out who needs service */ 1311ee3e3ff5SAndrew Thompson revents = 0; 131202ac6454SAndrew Thompson if ((events & (POLLOUT | POLLWRNORM)) && 131302ac6454SAndrew Thompson (fflags & FWRITE)) { 1314e13e19faSAndrew Thompson f = refs.txfifo; 131502ac6454SAndrew Thompson 131602ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 131702ac6454SAndrew Thompson 1318e13e19faSAndrew Thompson if (!refs.is_usbfs) { 131902ac6454SAndrew Thompson if (f->flag_iserror) { 132002ac6454SAndrew Thompson /* we got an error */ 132102ac6454SAndrew Thompson m = (void *)1; 132202ac6454SAndrew Thompson } else { 132302ac6454SAndrew Thompson if (f->queue_data == NULL) { 132402ac6454SAndrew Thompson /* 132502ac6454SAndrew Thompson * start write transfer, if not 132602ac6454SAndrew Thompson * already started 132702ac6454SAndrew Thompson */ 132802ac6454SAndrew Thompson (f->methods->f_start_write) (f); 132902ac6454SAndrew Thompson } 133002ac6454SAndrew Thompson /* check if any packets are available */ 133102ac6454SAndrew Thompson USB_IF_POLL(&f->free_q, m); 133202ac6454SAndrew Thompson } 133302ac6454SAndrew Thompson } else { 133402ac6454SAndrew Thompson if (f->flag_iscomplete) { 133502ac6454SAndrew Thompson m = (void *)1; 133602ac6454SAndrew Thompson } else { 133702ac6454SAndrew Thompson m = NULL; 133802ac6454SAndrew Thompson } 133902ac6454SAndrew Thompson } 134002ac6454SAndrew Thompson 134102ac6454SAndrew Thompson if (m) { 134202ac6454SAndrew Thompson revents |= events & (POLLOUT | POLLWRNORM); 134302ac6454SAndrew Thompson } else { 134402ac6454SAndrew Thompson f->flag_isselect = 1; 134502ac6454SAndrew Thompson selrecord(td, &f->selinfo); 134602ac6454SAndrew Thompson } 134702ac6454SAndrew Thompson 134802ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 134902ac6454SAndrew Thompson } 135002ac6454SAndrew Thompson if ((events & (POLLIN | POLLRDNORM)) && 135102ac6454SAndrew Thompson (fflags & FREAD)) { 1352e13e19faSAndrew Thompson f = refs.rxfifo; 135302ac6454SAndrew Thompson 135402ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 135502ac6454SAndrew Thompson 1356e13e19faSAndrew Thompson if (!refs.is_usbfs) { 135702ac6454SAndrew Thompson if (f->flag_iserror) { 13580b6d54d4SHans Petter Selasky /* we have an error */ 135902ac6454SAndrew Thompson m = (void *)1; 136002ac6454SAndrew Thompson } else { 136102ac6454SAndrew Thompson if (f->queue_data == NULL) { 136202ac6454SAndrew Thompson /* 136302ac6454SAndrew Thompson * start read transfer, if not 136402ac6454SAndrew Thompson * already started 136502ac6454SAndrew Thompson */ 136602ac6454SAndrew Thompson (f->methods->f_start_read) (f); 136702ac6454SAndrew Thompson } 136802ac6454SAndrew Thompson /* check if any packets are available */ 136902ac6454SAndrew Thompson USB_IF_POLL(&f->used_q, m); 137002ac6454SAndrew Thompson } 137102ac6454SAndrew Thompson } else { 137202ac6454SAndrew Thompson if (f->flag_iscomplete) { 137302ac6454SAndrew Thompson m = (void *)1; 137402ac6454SAndrew Thompson } else { 137502ac6454SAndrew Thompson m = NULL; 137602ac6454SAndrew Thompson } 137702ac6454SAndrew Thompson } 137802ac6454SAndrew Thompson 137902ac6454SAndrew Thompson if (m) { 138002ac6454SAndrew Thompson revents |= events & (POLLIN | POLLRDNORM); 138102ac6454SAndrew Thompson } else { 138202ac6454SAndrew Thompson f->flag_isselect = 1; 138302ac6454SAndrew Thompson selrecord(td, &f->selinfo); 138402ac6454SAndrew Thompson 1385e13e19faSAndrew Thompson if (!refs.is_usbfs) { 138602ac6454SAndrew Thompson /* start reading data */ 138702ac6454SAndrew Thompson (f->methods->f_start_read) (f); 138802ac6454SAndrew Thompson } 138902ac6454SAndrew Thompson } 139002ac6454SAndrew Thompson 139102ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 139202ac6454SAndrew Thompson } 1393a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 139402ac6454SAndrew Thompson return (revents); 139502ac6454SAndrew Thompson } 139602ac6454SAndrew Thompson 139702ac6454SAndrew Thompson static int 1398a593f6b8SAndrew Thompson usb_read(struct cdev *dev, struct uio *uio, int ioflag) 139902ac6454SAndrew Thompson { 1400e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1401760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1402760bc48eSAndrew Thompson struct usb_fifo *f; 1403760bc48eSAndrew Thompson struct usb_mbuf *m; 140402ac6454SAndrew Thompson int io_len; 140502ac6454SAndrew Thompson int err; 140602ac6454SAndrew Thompson uint8_t tr_data = 0; 140702ac6454SAndrew Thompson 1408ee3e3ff5SAndrew Thompson err = devfs_get_cdevpriv((void **)&cpd); 1409ee3e3ff5SAndrew Thompson if (err != 0) 1410ee3e3ff5SAndrew Thompson return (err); 141102ac6454SAndrew Thompson 1412a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 1413b06d477bSHans Petter Selasky if (err) 141402ac6454SAndrew Thompson return (ENXIO); 1415b06d477bSHans Petter Selasky 1416e13e19faSAndrew Thompson f = refs.rxfifo; 141702ac6454SAndrew Thompson if (f == NULL) { 141802ac6454SAndrew Thompson /* should not happen */ 1419a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 142002ac6454SAndrew Thompson return (EPERM); 142102ac6454SAndrew Thompson } 142202ac6454SAndrew Thompson 142302ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 142402ac6454SAndrew Thompson 142502ac6454SAndrew Thompson /* check for permanent read error */ 142602ac6454SAndrew Thompson if (f->flag_iserror) { 142702ac6454SAndrew Thompson err = EIO; 142802ac6454SAndrew Thompson goto done; 142902ac6454SAndrew Thompson } 143002ac6454SAndrew Thompson /* check if USB-FS interface is active */ 1431e13e19faSAndrew Thompson if (refs.is_usbfs) { 143202ac6454SAndrew Thompson /* 143302ac6454SAndrew Thompson * The queue is used for events that should be 143402ac6454SAndrew Thompson * retrieved using the "USB_FS_COMPLETE" ioctl. 143502ac6454SAndrew Thompson */ 143602ac6454SAndrew Thompson err = EINVAL; 143702ac6454SAndrew Thompson goto done; 143802ac6454SAndrew Thompson } 143902ac6454SAndrew Thompson while (uio->uio_resid > 0) { 144002ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 144102ac6454SAndrew Thompson 144202ac6454SAndrew Thompson if (m == NULL) { 144302ac6454SAndrew Thompson /* start read transfer, if not already started */ 144402ac6454SAndrew Thompson 144502ac6454SAndrew Thompson (f->methods->f_start_read) (f); 144602ac6454SAndrew Thompson 144707532e49SAndrew Thompson if (ioflag & IO_NDELAY) { 144802ac6454SAndrew Thompson if (tr_data) { 144902ac6454SAndrew Thompson /* return length before error */ 145002ac6454SAndrew Thompson break; 145102ac6454SAndrew Thompson } 145202ac6454SAndrew Thompson err = EWOULDBLOCK; 145302ac6454SAndrew Thompson break; 145402ac6454SAndrew Thompson } 145502ac6454SAndrew Thompson DPRINTF("sleeping\n"); 145602ac6454SAndrew Thompson 1457a593f6b8SAndrew Thompson err = usb_fifo_wait(f); 145802ac6454SAndrew Thompson if (err) { 145902ac6454SAndrew Thompson break; 146002ac6454SAndrew Thompson } 146102ac6454SAndrew Thompson continue; 146202ac6454SAndrew Thompson } 146302ac6454SAndrew Thompson if (f->methods->f_filter_read) { 146402ac6454SAndrew Thompson /* 146502ac6454SAndrew Thompson * Sometimes it is convenient to process data at the 146602ac6454SAndrew Thompson * expense of a userland process instead of a kernel 146702ac6454SAndrew Thompson * process. 146802ac6454SAndrew Thompson */ 146902ac6454SAndrew Thompson (f->methods->f_filter_read) (f, m); 147002ac6454SAndrew Thompson } 147102ac6454SAndrew Thompson tr_data = 1; 147202ac6454SAndrew Thompson 147302ac6454SAndrew Thompson io_len = MIN(m->cur_data_len, uio->uio_resid); 147402ac6454SAndrew Thompson 147502ac6454SAndrew Thompson DPRINTFN(2, "transfer %d bytes from %p\n", 147602ac6454SAndrew Thompson io_len, m->cur_data_ptr); 147702ac6454SAndrew Thompson 1478a593f6b8SAndrew Thompson err = usb_fifo_uiomove(f, 147902ac6454SAndrew Thompson m->cur_data_ptr, io_len, uio); 148002ac6454SAndrew Thompson 148102ac6454SAndrew Thompson m->cur_data_len -= io_len; 148202ac6454SAndrew Thompson m->cur_data_ptr += io_len; 148302ac6454SAndrew Thompson 148402ac6454SAndrew Thompson if (m->cur_data_len == 0) { 148502ac6454SAndrew Thompson uint8_t last_packet; 148602ac6454SAndrew Thompson 148702ac6454SAndrew Thompson last_packet = m->last_packet; 148802ac6454SAndrew Thompson 148902ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 149002ac6454SAndrew Thompson 149102ac6454SAndrew Thompson if (last_packet) { 149202ac6454SAndrew Thompson /* keep framing */ 149302ac6454SAndrew Thompson break; 149402ac6454SAndrew Thompson } 149502ac6454SAndrew Thompson } else { 149602ac6454SAndrew Thompson USB_IF_PREPEND(&f->used_q, m); 149702ac6454SAndrew Thompson } 149802ac6454SAndrew Thompson 149902ac6454SAndrew Thompson if (err) { 150002ac6454SAndrew Thompson break; 150102ac6454SAndrew Thompson } 150202ac6454SAndrew Thompson } 150302ac6454SAndrew Thompson done: 150402ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 150502ac6454SAndrew Thompson 1506a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 150702ac6454SAndrew Thompson 150802ac6454SAndrew Thompson return (err); 150902ac6454SAndrew Thompson } 151002ac6454SAndrew Thompson 151102ac6454SAndrew Thompson static int 1512a593f6b8SAndrew Thompson usb_write(struct cdev *dev, struct uio *uio, int ioflag) 151302ac6454SAndrew Thompson { 1514e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1515760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1516760bc48eSAndrew Thompson struct usb_fifo *f; 1517760bc48eSAndrew Thompson struct usb_mbuf *m; 1518bd73b187SAlfred Perlstein uint8_t *pdata; 151902ac6454SAndrew Thompson int io_len; 152002ac6454SAndrew Thompson int err; 152102ac6454SAndrew Thompson uint8_t tr_data = 0; 152202ac6454SAndrew Thompson 152302ac6454SAndrew Thompson DPRINTFN(2, "\n"); 152402ac6454SAndrew Thompson 1525ee3e3ff5SAndrew Thompson err = devfs_get_cdevpriv((void **)&cpd); 1526ee3e3ff5SAndrew Thompson if (err != 0) 1527ee3e3ff5SAndrew Thompson return (err); 152802ac6454SAndrew Thompson 1529a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 1530b06d477bSHans Petter Selasky if (err) 153102ac6454SAndrew Thompson return (ENXIO); 1532b06d477bSHans Petter Selasky 1533e13e19faSAndrew Thompson f = refs.txfifo; 153402ac6454SAndrew Thompson if (f == NULL) { 153502ac6454SAndrew Thompson /* should not happen */ 1536a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 153702ac6454SAndrew Thompson return (EPERM); 153802ac6454SAndrew Thompson } 153902ac6454SAndrew Thompson 154002ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 154102ac6454SAndrew Thompson 154202ac6454SAndrew Thompson /* check for permanent write error */ 154302ac6454SAndrew Thompson if (f->flag_iserror) { 154402ac6454SAndrew Thompson err = EIO; 154502ac6454SAndrew Thompson goto done; 154602ac6454SAndrew Thompson } 154702ac6454SAndrew Thompson /* check if USB-FS interface is active */ 1548e13e19faSAndrew Thompson if (refs.is_usbfs) { 154902ac6454SAndrew Thompson /* 155002ac6454SAndrew Thompson * The queue is used for events that should be 155102ac6454SAndrew Thompson * retrieved using the "USB_FS_COMPLETE" ioctl. 155202ac6454SAndrew Thompson */ 155302ac6454SAndrew Thompson err = EINVAL; 155402ac6454SAndrew Thompson goto done; 155502ac6454SAndrew Thompson } 155602ac6454SAndrew Thompson if (f->queue_data == NULL) { 155702ac6454SAndrew Thompson /* start write transfer, if not already started */ 155802ac6454SAndrew Thompson (f->methods->f_start_write) (f); 155902ac6454SAndrew Thompson } 156002ac6454SAndrew Thompson /* we allow writing zero length data */ 156102ac6454SAndrew Thompson do { 156202ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 156302ac6454SAndrew Thompson 156402ac6454SAndrew Thompson if (m == NULL) { 156507532e49SAndrew Thompson if (ioflag & IO_NDELAY) { 156602ac6454SAndrew Thompson if (tr_data) { 156702ac6454SAndrew Thompson /* return length before error */ 156802ac6454SAndrew Thompson break; 156902ac6454SAndrew Thompson } 157002ac6454SAndrew Thompson err = EWOULDBLOCK; 157102ac6454SAndrew Thompson break; 157202ac6454SAndrew Thompson } 157302ac6454SAndrew Thompson DPRINTF("sleeping\n"); 157402ac6454SAndrew Thompson 1575a593f6b8SAndrew Thompson err = usb_fifo_wait(f); 157602ac6454SAndrew Thompson if (err) { 157702ac6454SAndrew Thompson break; 157802ac6454SAndrew Thompson } 157902ac6454SAndrew Thompson continue; 158002ac6454SAndrew Thompson } 158102ac6454SAndrew Thompson tr_data = 1; 158202ac6454SAndrew Thompson 1583bd73b187SAlfred Perlstein if (f->flag_have_fragment == 0) { 158402ac6454SAndrew Thompson USB_MBUF_RESET(m); 1585bd73b187SAlfred Perlstein io_len = m->cur_data_len; 1586bd73b187SAlfred Perlstein pdata = m->cur_data_ptr; 1587bd73b187SAlfred Perlstein if (io_len > uio->uio_resid) 1588bd73b187SAlfred Perlstein io_len = uio->uio_resid; 158902ac6454SAndrew Thompson m->cur_data_len = io_len; 1590bd73b187SAlfred Perlstein } else { 1591bd73b187SAlfred Perlstein io_len = m->max_data_len - m->cur_data_len; 1592bd73b187SAlfred Perlstein pdata = m->cur_data_ptr + m->cur_data_len; 1593bd73b187SAlfred Perlstein if (io_len > uio->uio_resid) 1594bd73b187SAlfred Perlstein io_len = uio->uio_resid; 1595bd73b187SAlfred Perlstein m->cur_data_len += io_len; 1596bd73b187SAlfred Perlstein } 159702ac6454SAndrew Thompson 159802ac6454SAndrew Thompson DPRINTFN(2, "transfer %d bytes to %p\n", 1599bd73b187SAlfred Perlstein io_len, pdata); 160002ac6454SAndrew Thompson 1601bd73b187SAlfred Perlstein err = usb_fifo_uiomove(f, pdata, io_len, uio); 160202ac6454SAndrew Thompson 160302ac6454SAndrew Thompson if (err) { 1604bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 160502ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 160602ac6454SAndrew Thompson break; 160702ac6454SAndrew Thompson } 1608bd73b187SAlfred Perlstein 1609bd73b187SAlfred Perlstein /* check if the buffer is ready to be transmitted */ 1610bd73b187SAlfred Perlstein 1611bd73b187SAlfred Perlstein if ((f->flag_write_defrag == 0) || 1612bd73b187SAlfred Perlstein (m->cur_data_len == m->max_data_len)) { 1613bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 1614bd73b187SAlfred Perlstein 161502ac6454SAndrew Thompson /* 1616bd73b187SAlfred Perlstein * Check for write filter: 1617bd73b187SAlfred Perlstein * 1618bd73b187SAlfred Perlstein * Sometimes it is convenient to process data 1619bd73b187SAlfred Perlstein * at the expense of a userland process 1620bd73b187SAlfred Perlstein * instead of a kernel process. 162102ac6454SAndrew Thompson */ 1622bd73b187SAlfred Perlstein if (f->methods->f_filter_write) { 162302ac6454SAndrew Thompson (f->methods->f_filter_write) (f, m); 162402ac6454SAndrew Thompson } 1625bd73b187SAlfred Perlstein 1626bd73b187SAlfred Perlstein /* Put USB mbuf in the used queue */ 162702ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 162802ac6454SAndrew Thompson 1629bd73b187SAlfred Perlstein /* Start writing data, if not already started */ 163002ac6454SAndrew Thompson (f->methods->f_start_write) (f); 1631bd73b187SAlfred Perlstein } else { 1632bd73b187SAlfred Perlstein /* Wait for more data or close */ 1633bd73b187SAlfred Perlstein f->flag_have_fragment = 1; 1634bd73b187SAlfred Perlstein USB_IF_PREPEND(&f->free_q, m); 1635bd73b187SAlfred Perlstein } 163602ac6454SAndrew Thompson 163702ac6454SAndrew Thompson } while (uio->uio_resid > 0); 163802ac6454SAndrew Thompson done: 163902ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 164002ac6454SAndrew Thompson 1641a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 164202ac6454SAndrew Thompson 1643ee3e3ff5SAndrew Thompson return (err); 1644ee3e3ff5SAndrew Thompson } 164502ac6454SAndrew Thompson 1646ee3e3ff5SAndrew Thompson int 1647a593f6b8SAndrew Thompson usb_static_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 1648ee3e3ff5SAndrew Thompson struct thread *td) 1649ee3e3ff5SAndrew Thompson { 1650ee3e3ff5SAndrew Thompson union { 1651760bc48eSAndrew Thompson struct usb_read_dir *urd; 165245b48cbcSBrooks Davis #ifdef COMPAT_FREEBSD32 165345b48cbcSBrooks Davis struct usb_read_dir32 *urd32; 165445b48cbcSBrooks Davis #endif 1655ee3e3ff5SAndrew Thompson void* data; 1656ee3e3ff5SAndrew Thompson } u; 1657ce4092bdSHans Petter Selasky int err; 1658ee3e3ff5SAndrew Thompson 1659ee3e3ff5SAndrew Thompson u.data = data; 1660ee3e3ff5SAndrew Thompson switch (cmd) { 1661ee3e3ff5SAndrew Thompson case USB_READ_DIR: 1662a593f6b8SAndrew Thompson err = usb_read_symlink(u.urd->urd_data, 1663ee3e3ff5SAndrew Thompson u.urd->urd_startentry, u.urd->urd_maxlen); 1664ee3e3ff5SAndrew Thompson break; 166545b48cbcSBrooks Davis #ifdef COMPAT_FREEBSD32 166645b48cbcSBrooks Davis case USB_READ_DIR32: 166745b48cbcSBrooks Davis err = usb_read_symlink(PTRIN(u.urd32->urd_data), 166845b48cbcSBrooks Davis u.urd32->urd_startentry, u.urd32->urd_maxlen); 166945b48cbcSBrooks Davis break; 167045b48cbcSBrooks Davis #endif 1671ee3e3ff5SAndrew Thompson case USB_DEV_QUIRK_GET: 1672ee3e3ff5SAndrew Thompson case USB_QUIRK_NAME_GET: 1673ee3e3ff5SAndrew Thompson case USB_DEV_QUIRK_ADD: 1674ee3e3ff5SAndrew Thompson case USB_DEV_QUIRK_REMOVE: 1675a593f6b8SAndrew Thompson err = usb_quirk_ioctl_p(cmd, data, fflag, td); 1676ee3e3ff5SAndrew Thompson break; 1677ee3e3ff5SAndrew Thompson case USB_GET_TEMPLATE: 1678a593f6b8SAndrew Thompson *(int *)data = usb_template; 1679ce4092bdSHans Petter Selasky err = 0; 1680ee3e3ff5SAndrew Thompson break; 1681ee3e3ff5SAndrew Thompson case USB_SET_TEMPLATE: 168250230f98SAndrew Thompson err = priv_check(curthread, PRIV_DRIVER); 1683ee3e3ff5SAndrew Thompson if (err) 1684ee3e3ff5SAndrew Thompson break; 1685a593f6b8SAndrew Thompson usb_template = *(int *)data; 1686ee3e3ff5SAndrew Thompson break; 1687ce4092bdSHans Petter Selasky default: 1688ce4092bdSHans Petter Selasky err = ENOTTY; 1689ce4092bdSHans Petter Selasky break; 1690ee3e3ff5SAndrew Thompson } 169102ac6454SAndrew Thompson return (err); 169202ac6454SAndrew Thompson } 169302ac6454SAndrew Thompson 169402ac6454SAndrew Thompson static int 1695a593f6b8SAndrew Thompson usb_fifo_uiomove(struct usb_fifo *f, void *cp, 169602ac6454SAndrew Thompson int n, struct uio *uio) 169702ac6454SAndrew Thompson { 169802ac6454SAndrew Thompson int error; 169902ac6454SAndrew Thompson 170002ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 170102ac6454SAndrew Thompson 170202ac6454SAndrew Thompson /* 170302ac6454SAndrew Thompson * "uiomove()" can sleep so one needs to make a wrapper, 170402ac6454SAndrew Thompson * exiting the mutex and checking things: 170502ac6454SAndrew Thompson */ 170602ac6454SAndrew Thompson error = uiomove(cp, n, uio); 170702ac6454SAndrew Thompson 170802ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 170902ac6454SAndrew Thompson 171002ac6454SAndrew Thompson return (error); 171102ac6454SAndrew Thompson } 171202ac6454SAndrew Thompson 171302ac6454SAndrew Thompson int 1714a593f6b8SAndrew Thompson usb_fifo_wait(struct usb_fifo *f) 171502ac6454SAndrew Thompson { 171602ac6454SAndrew Thompson int err; 171702ac6454SAndrew Thompson 17180eb8d462SHans Petter Selasky USB_MTX_ASSERT(f->priv_mtx, MA_OWNED); 171902ac6454SAndrew Thompson 172002ac6454SAndrew Thompson if (f->flag_iserror) { 172102ac6454SAndrew Thompson /* we are gone */ 172202ac6454SAndrew Thompson return (EIO); 172302ac6454SAndrew Thompson } 172402ac6454SAndrew Thompson f->flag_sleeping = 1; 172502ac6454SAndrew Thompson 17268437751dSAndrew Thompson err = cv_wait_sig(&f->cv_io, f->priv_mtx); 172702ac6454SAndrew Thompson 172802ac6454SAndrew Thompson if (f->flag_iserror) { 172902ac6454SAndrew Thompson /* we are gone */ 173002ac6454SAndrew Thompson err = EIO; 173102ac6454SAndrew Thompson } 173202ac6454SAndrew Thompson return (err); 173302ac6454SAndrew Thompson } 173402ac6454SAndrew Thompson 173502ac6454SAndrew Thompson void 1736a593f6b8SAndrew Thompson usb_fifo_signal(struct usb_fifo *f) 173702ac6454SAndrew Thompson { 173802ac6454SAndrew Thompson if (f->flag_sleeping) { 173902ac6454SAndrew Thompson f->flag_sleeping = 0; 17408437751dSAndrew Thompson cv_broadcast(&f->cv_io); 174102ac6454SAndrew Thompson } 174202ac6454SAndrew Thompson } 174302ac6454SAndrew Thompson 174402ac6454SAndrew Thompson void 1745a593f6b8SAndrew Thompson usb_fifo_wakeup(struct usb_fifo *f) 174602ac6454SAndrew Thompson { 1747a593f6b8SAndrew Thompson usb_fifo_signal(f); 174802ac6454SAndrew Thompson 17490b6d54d4SHans Petter Selasky KNOTE_LOCKED(&f->selinfo.si_note, 0); 17500b6d54d4SHans Petter Selasky 175102ac6454SAndrew Thompson if (f->flag_isselect) { 175202ac6454SAndrew Thompson selwakeup(&f->selinfo); 175302ac6454SAndrew Thompson f->flag_isselect = 0; 175402ac6454SAndrew Thompson } 175502ac6454SAndrew Thompson if (f->async_p != NULL) { 175602ac6454SAndrew Thompson PROC_LOCK(f->async_p); 17578451d0ddSKip Macy kern_psignal(f->async_p, SIGIO); 175802ac6454SAndrew Thompson PROC_UNLOCK(f->async_p); 175902ac6454SAndrew Thompson } 176002ac6454SAndrew Thompson } 176102ac6454SAndrew Thompson 176202ac6454SAndrew Thompson static int 1763a593f6b8SAndrew Thompson usb_fifo_dummy_open(struct usb_fifo *fifo, int fflags) 176402ac6454SAndrew Thompson { 176502ac6454SAndrew Thompson return (0); 176602ac6454SAndrew Thompson } 176702ac6454SAndrew Thompson 176802ac6454SAndrew Thompson static void 1769a593f6b8SAndrew Thompson usb_fifo_dummy_close(struct usb_fifo *fifo, int fflags) 177002ac6454SAndrew Thompson { 177102ac6454SAndrew Thompson return; 177202ac6454SAndrew Thompson } 177302ac6454SAndrew Thompson 177402ac6454SAndrew Thompson static int 1775a593f6b8SAndrew Thompson usb_fifo_dummy_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) 177602ac6454SAndrew Thompson { 177702ac6454SAndrew Thompson return (ENOIOCTL); 177802ac6454SAndrew Thompson } 177902ac6454SAndrew Thompson 178002ac6454SAndrew Thompson static void 1781a593f6b8SAndrew Thompson usb_fifo_dummy_cmd(struct usb_fifo *fifo) 178202ac6454SAndrew Thompson { 178302ac6454SAndrew Thompson fifo->flag_flushing = 0; /* not flushing */ 178402ac6454SAndrew Thompson } 178502ac6454SAndrew Thompson 178602ac6454SAndrew Thompson static void 1787a593f6b8SAndrew Thompson usb_fifo_check_methods(struct usb_fifo_methods *pm) 178802ac6454SAndrew Thompson { 178902ac6454SAndrew Thompson /* check that all callback functions are OK */ 179002ac6454SAndrew Thompson 179102ac6454SAndrew Thompson if (pm->f_open == NULL) 1792a593f6b8SAndrew Thompson pm->f_open = &usb_fifo_dummy_open; 179302ac6454SAndrew Thompson 179402ac6454SAndrew Thompson if (pm->f_close == NULL) 1795a593f6b8SAndrew Thompson pm->f_close = &usb_fifo_dummy_close; 179602ac6454SAndrew Thompson 179702ac6454SAndrew Thompson if (pm->f_ioctl == NULL) 1798a593f6b8SAndrew Thompson pm->f_ioctl = &usb_fifo_dummy_ioctl; 179902ac6454SAndrew Thompson 180002ac6454SAndrew Thompson if (pm->f_ioctl_post == NULL) 1801a593f6b8SAndrew Thompson pm->f_ioctl_post = &usb_fifo_dummy_ioctl; 180202ac6454SAndrew Thompson 180302ac6454SAndrew Thompson if (pm->f_start_read == NULL) 1804a593f6b8SAndrew Thompson pm->f_start_read = &usb_fifo_dummy_cmd; 180502ac6454SAndrew Thompson 180602ac6454SAndrew Thompson if (pm->f_stop_read == NULL) 1807a593f6b8SAndrew Thompson pm->f_stop_read = &usb_fifo_dummy_cmd; 180802ac6454SAndrew Thompson 180902ac6454SAndrew Thompson if (pm->f_start_write == NULL) 1810a593f6b8SAndrew Thompson pm->f_start_write = &usb_fifo_dummy_cmd; 181102ac6454SAndrew Thompson 181202ac6454SAndrew Thompson if (pm->f_stop_write == NULL) 1813a593f6b8SAndrew Thompson pm->f_stop_write = &usb_fifo_dummy_cmd; 181402ac6454SAndrew Thompson } 181502ac6454SAndrew Thompson 181602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1817a593f6b8SAndrew Thompson * usb_fifo_attach 181802ac6454SAndrew Thompson * 181902ac6454SAndrew Thompson * The following function will create a duplex FIFO. 182002ac6454SAndrew Thompson * 182102ac6454SAndrew Thompson * Return values: 182202ac6454SAndrew Thompson * 0: Success. 182302ac6454SAndrew Thompson * Else: Failure. 182402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 182502ac6454SAndrew Thompson int 1826a593f6b8SAndrew Thompson usb_fifo_attach(struct usb_device *udev, void *priv_sc, 1827760bc48eSAndrew Thompson struct mtx *priv_mtx, struct usb_fifo_methods *pm, 18286d917491SHans Petter Selasky struct usb_fifo_sc *f_sc, uint16_t unit, int16_t subunit, 1829ee3e3ff5SAndrew Thompson uint8_t iface_index, uid_t uid, gid_t gid, int mode) 183002ac6454SAndrew Thompson { 1831760bc48eSAndrew Thompson struct usb_fifo *f_tx; 1832760bc48eSAndrew Thompson struct usb_fifo *f_rx; 1833ee3e3ff5SAndrew Thompson char devname[32]; 183402ac6454SAndrew Thompson uint8_t n; 183502ac6454SAndrew Thompson 183602ac6454SAndrew Thompson f_sc->fp[USB_FIFO_TX] = NULL; 183702ac6454SAndrew Thompson f_sc->fp[USB_FIFO_RX] = NULL; 183802ac6454SAndrew Thompson 183902ac6454SAndrew Thompson if (pm == NULL) 184002ac6454SAndrew Thompson return (EINVAL); 184102ac6454SAndrew Thompson 184202ac6454SAndrew Thompson /* check the methods */ 1843a593f6b8SAndrew Thompson usb_fifo_check_methods(pm); 184402ac6454SAndrew Thompson 184502ac6454SAndrew Thompson if (priv_mtx == NULL) 184602ac6454SAndrew Thompson priv_mtx = &Giant; 184702ac6454SAndrew Thompson 184802ac6454SAndrew Thompson /* search for a free FIFO slot */ 184902ac6454SAndrew Thompson for (n = 0;; n += 2) { 185002ac6454SAndrew Thompson if (n == USB_FIFO_MAX) { 185102ac6454SAndrew Thompson /* end of FIFOs reached */ 185202ac6454SAndrew Thompson return (ENOMEM); 185302ac6454SAndrew Thompson } 185402ac6454SAndrew Thompson /* Check for TX FIFO */ 185502ac6454SAndrew Thompson if (udev->fifo[n + USB_FIFO_TX] != NULL) { 185602ac6454SAndrew Thompson continue; 185702ac6454SAndrew Thompson } 185802ac6454SAndrew Thompson /* Check for RX FIFO */ 185902ac6454SAndrew Thompson if (udev->fifo[n + USB_FIFO_RX] != NULL) { 186002ac6454SAndrew Thompson continue; 186102ac6454SAndrew Thompson } 186202ac6454SAndrew Thompson break; 186302ac6454SAndrew Thompson } 186402ac6454SAndrew Thompson 1865e2723934SHans Petter Selasky f_tx = usb_fifo_alloc(priv_mtx); 1866e2723934SHans Petter Selasky f_rx = usb_fifo_alloc(priv_mtx); 186702ac6454SAndrew Thompson 186802ac6454SAndrew Thompson if ((f_tx == NULL) || (f_rx == NULL)) { 1869a593f6b8SAndrew Thompson usb_fifo_free(f_tx); 1870a593f6b8SAndrew Thompson usb_fifo_free(f_rx); 187102ac6454SAndrew Thompson return (ENOMEM); 187202ac6454SAndrew Thompson } 187302ac6454SAndrew Thompson /* initialise FIFO structures */ 187402ac6454SAndrew Thompson 187502ac6454SAndrew Thompson f_tx->fifo_index = n + USB_FIFO_TX; 18762989a677SAndrew Thompson f_tx->dev_ep_index = -1; 187702ac6454SAndrew Thompson f_tx->priv_sc0 = priv_sc; 187802ac6454SAndrew Thompson f_tx->methods = pm; 187902ac6454SAndrew Thompson f_tx->iface_index = iface_index; 188002ac6454SAndrew Thompson f_tx->udev = udev; 188102ac6454SAndrew Thompson 188202ac6454SAndrew Thompson f_rx->fifo_index = n + USB_FIFO_RX; 18832989a677SAndrew Thompson f_rx->dev_ep_index = -1; 188402ac6454SAndrew Thompson f_rx->priv_sc0 = priv_sc; 188502ac6454SAndrew Thompson f_rx->methods = pm; 188602ac6454SAndrew Thompson f_rx->iface_index = iface_index; 188702ac6454SAndrew Thompson f_rx->udev = udev; 188802ac6454SAndrew Thompson 188902ac6454SAndrew Thompson f_sc->fp[USB_FIFO_TX] = f_tx; 189002ac6454SAndrew Thompson f_sc->fp[USB_FIFO_RX] = f_rx; 189102ac6454SAndrew Thompson 1892a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 189302ac6454SAndrew Thompson udev->fifo[f_tx->fifo_index] = f_tx; 189402ac6454SAndrew Thompson udev->fifo[f_rx->fifo_index] = f_rx; 1895a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 189602ac6454SAndrew Thompson 189702ac6454SAndrew Thompson for (n = 0; n != 4; n++) { 189802ac6454SAndrew Thompson if (pm->basename[n] == NULL) { 189902ac6454SAndrew Thompson continue; 190002ac6454SAndrew Thompson } 19016d917491SHans Petter Selasky if (subunit < 0) { 1902ee3e3ff5SAndrew Thompson if (snprintf(devname, sizeof(devname), 190302ac6454SAndrew Thompson "%s%u%s", pm->basename[n], 190402ac6454SAndrew Thompson unit, pm->postfix[n] ? 190502ac6454SAndrew Thompson pm->postfix[n] : "")) { 190602ac6454SAndrew Thompson /* ignore */ 190702ac6454SAndrew Thompson } 190802ac6454SAndrew Thompson } else { 1909ee3e3ff5SAndrew Thompson if (snprintf(devname, sizeof(devname), 19106d917491SHans Petter Selasky "%s%u.%d%s", pm->basename[n], 191102ac6454SAndrew Thompson unit, subunit, pm->postfix[n] ? 191202ac6454SAndrew Thompson pm->postfix[n] : "")) { 191302ac6454SAndrew Thompson /* ignore */ 191402ac6454SAndrew Thompson } 191502ac6454SAndrew Thompson } 191602ac6454SAndrew Thompson 191702ac6454SAndrew Thompson /* 191802ac6454SAndrew Thompson * Distribute the symbolic links into two FIFO structures: 191902ac6454SAndrew Thompson */ 192002ac6454SAndrew Thompson if (n & 1) { 192102ac6454SAndrew Thompson f_rx->symlink[n / 2] = 1922a593f6b8SAndrew Thompson usb_alloc_symlink(devname); 192302ac6454SAndrew Thompson } else { 192402ac6454SAndrew Thompson f_tx->symlink[n / 2] = 1925a593f6b8SAndrew Thompson usb_alloc_symlink(devname); 192602ac6454SAndrew Thompson } 1927ee3e3ff5SAndrew Thompson 19282ffd5fdcSHans Petter Selasky /* Create the device */ 19292ffd5fdcSHans Petter Selasky f_sc->dev = usb_make_dev(udev, devname, -1, 19302ffd5fdcSHans Petter Selasky f_tx->fifo_index & f_rx->fifo_index, 19312ffd5fdcSHans Petter Selasky FREAD|FWRITE, uid, gid, mode); 193202ac6454SAndrew Thompson } 193302ac6454SAndrew Thompson 193402ac6454SAndrew Thompson DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx); 193502ac6454SAndrew Thompson return (0); 193602ac6454SAndrew Thompson } 193702ac6454SAndrew Thompson 193802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1939a593f6b8SAndrew Thompson * usb_fifo_alloc_buffer 194002ac6454SAndrew Thompson * 194102ac6454SAndrew Thompson * Return values: 194202ac6454SAndrew Thompson * 0: Success 194302ac6454SAndrew Thompson * Else failure 194402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 194502ac6454SAndrew Thompson int 1946a593f6b8SAndrew Thompson usb_fifo_alloc_buffer(struct usb_fifo *f, usb_size_t bufsize, 194702ac6454SAndrew Thompson uint16_t nbuf) 194802ac6454SAndrew Thompson { 1949aad0c65dSHans Petter Selasky struct usb_ifqueue temp_q = {}; 1950aad0c65dSHans Petter Selasky void *queue_data; 1951aad0c65dSHans Petter Selasky 1952a593f6b8SAndrew Thompson usb_fifo_free_buffer(f); 195302ac6454SAndrew Thompson 1954aad0c65dSHans Petter Selasky temp_q.ifq_maxlen = nbuf; 195502ac6454SAndrew Thompson 1956aad0c65dSHans Petter Selasky queue_data = usb_alloc_mbufs( 1957aad0c65dSHans Petter Selasky M_USBDEV, &temp_q, bufsize, nbuf); 195802ac6454SAndrew Thompson 1959aad0c65dSHans Petter Selasky if (queue_data == NULL && bufsize != 0 && nbuf != 0) 196002ac6454SAndrew Thompson return (ENOMEM); 1961aad0c65dSHans Petter Selasky 1962aad0c65dSHans Petter Selasky mtx_lock(f->priv_mtx); 1963aad0c65dSHans Petter Selasky 1964aad0c65dSHans Petter Selasky /* 1965aad0c65dSHans Petter Selasky * Setup queues and sizes under lock to avoid early use by 1966aad0c65dSHans Petter Selasky * concurrent FIFO access: 1967aad0c65dSHans Petter Selasky */ 1968aad0c65dSHans Petter Selasky f->free_q = temp_q; 1969aad0c65dSHans Petter Selasky f->used_q.ifq_maxlen = nbuf; 1970aad0c65dSHans Petter Selasky f->queue_data = queue_data; 1971aad0c65dSHans Petter Selasky mtx_unlock(f->priv_mtx); 1972aad0c65dSHans Petter Selasky 197302ac6454SAndrew Thompson return (0); /* success */ 197402ac6454SAndrew Thompson } 197502ac6454SAndrew Thompson 197602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1977a593f6b8SAndrew Thompson * usb_fifo_free_buffer 197802ac6454SAndrew Thompson * 197902ac6454SAndrew Thompson * This function will free the buffers associated with a FIFO. This 198002ac6454SAndrew Thompson * function can be called multiple times in a row. 198102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 198202ac6454SAndrew Thompson void 1983a593f6b8SAndrew Thompson usb_fifo_free_buffer(struct usb_fifo *f) 198402ac6454SAndrew Thompson { 1985aad0c65dSHans Petter Selasky void *queue_data; 198602ac6454SAndrew Thompson 1987aad0c65dSHans Petter Selasky mtx_lock(f->priv_mtx); 1988aad0c65dSHans Petter Selasky 1989aad0c65dSHans Petter Selasky /* Get and clear pointer to free, if any. */ 1990aad0c65dSHans Petter Selasky queue_data = f->queue_data; 1991aad0c65dSHans Petter Selasky f->queue_data = NULL; 1992aad0c65dSHans Petter Selasky 1993aad0c65dSHans Petter Selasky /* 1994aad0c65dSHans Petter Selasky * Reset queues under lock to avoid use of freed buffers by 1995aad0c65dSHans Petter Selasky * concurrent FIFO activity: 1996aad0c65dSHans Petter Selasky */ 1997271ae033SHans Petter Selasky memset(&f->free_q, 0, sizeof(f->free_q)); 1998271ae033SHans Petter Selasky memset(&f->used_q, 0, sizeof(f->used_q)); 1999aad0c65dSHans Petter Selasky mtx_unlock(f->priv_mtx); 2000aad0c65dSHans Petter Selasky 2001aad0c65dSHans Petter Selasky /* Free old buffer, if any. */ 2002aad0c65dSHans Petter Selasky free(queue_data, M_USBDEV); 200302ac6454SAndrew Thompson } 200402ac6454SAndrew Thompson 200502ac6454SAndrew Thompson void 2006a593f6b8SAndrew Thompson usb_fifo_detach(struct usb_fifo_sc *f_sc) 200702ac6454SAndrew Thompson { 200802ac6454SAndrew Thompson if (f_sc == NULL) { 200902ac6454SAndrew Thompson return; 201002ac6454SAndrew Thompson } 2011a593f6b8SAndrew Thompson usb_fifo_free(f_sc->fp[USB_FIFO_TX]); 2012a593f6b8SAndrew Thompson usb_fifo_free(f_sc->fp[USB_FIFO_RX]); 201302ac6454SAndrew Thompson 201402ac6454SAndrew Thompson f_sc->fp[USB_FIFO_TX] = NULL; 201502ac6454SAndrew Thompson f_sc->fp[USB_FIFO_RX] = NULL; 201602ac6454SAndrew Thompson 20172ffd5fdcSHans Petter Selasky usb_destroy_dev(f_sc->dev); 20182ffd5fdcSHans Petter Selasky 20197214348fSAndrew Thompson f_sc->dev = NULL; 2020ee3e3ff5SAndrew Thompson 202102ac6454SAndrew Thompson DPRINTFN(2, "detached %p\n", f_sc); 202202ac6454SAndrew Thompson } 202302ac6454SAndrew Thompson 2024f9cb546cSAndrew Thompson usb_size_t 2025a593f6b8SAndrew Thompson usb_fifo_put_bytes_max(struct usb_fifo *f) 202602ac6454SAndrew Thompson { 2027760bc48eSAndrew Thompson struct usb_mbuf *m; 2028f9cb546cSAndrew Thompson usb_size_t len; 202902ac6454SAndrew Thompson 203002ac6454SAndrew Thompson USB_IF_POLL(&f->free_q, m); 203102ac6454SAndrew Thompson 203202ac6454SAndrew Thompson if (m) { 203302ac6454SAndrew Thompson len = m->max_data_len; 203402ac6454SAndrew Thompson } else { 203502ac6454SAndrew Thompson len = 0; 203602ac6454SAndrew Thompson } 203702ac6454SAndrew Thompson return (len); 203802ac6454SAndrew Thompson } 203902ac6454SAndrew Thompson 204002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2041a593f6b8SAndrew Thompson * usb_fifo_put_data 204202ac6454SAndrew Thompson * 204302ac6454SAndrew Thompson * what: 204402ac6454SAndrew Thompson * 0 - normal operation 204502ac6454SAndrew Thompson * 1 - set last packet flag to enforce framing 204602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 204702ac6454SAndrew Thompson void 2048a593f6b8SAndrew Thompson usb_fifo_put_data(struct usb_fifo *f, struct usb_page_cache *pc, 2049e0a69b51SAndrew Thompson usb_frlength_t offset, usb_frlength_t len, uint8_t what) 205002ac6454SAndrew Thompson { 2051760bc48eSAndrew Thompson struct usb_mbuf *m; 2052e0a69b51SAndrew Thompson usb_frlength_t io_len; 205302ac6454SAndrew Thompson 205402ac6454SAndrew Thompson while (len || (what == 1)) { 205502ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 205602ac6454SAndrew Thompson 205702ac6454SAndrew Thompson if (m) { 205802ac6454SAndrew Thompson USB_MBUF_RESET(m); 205902ac6454SAndrew Thompson 206002ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 206102ac6454SAndrew Thompson 2062a593f6b8SAndrew Thompson usbd_copy_out(pc, offset, m->cur_data_ptr, io_len); 206302ac6454SAndrew Thompson 206402ac6454SAndrew Thompson m->cur_data_len = io_len; 206502ac6454SAndrew Thompson offset += io_len; 206602ac6454SAndrew Thompson len -= io_len; 206702ac6454SAndrew Thompson 206802ac6454SAndrew Thompson if ((len == 0) && (what == 1)) { 206902ac6454SAndrew Thompson m->last_packet = 1; 207002ac6454SAndrew Thompson } 207102ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 207202ac6454SAndrew Thompson 2073a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 207402ac6454SAndrew Thompson 207502ac6454SAndrew Thompson if ((len == 0) || (what == 1)) { 207602ac6454SAndrew Thompson break; 207702ac6454SAndrew Thompson } 207802ac6454SAndrew Thompson } else { 207902ac6454SAndrew Thompson break; 208002ac6454SAndrew Thompson } 208102ac6454SAndrew Thompson } 208202ac6454SAndrew Thompson } 208302ac6454SAndrew Thompson 208402ac6454SAndrew Thompson void 2085a593f6b8SAndrew Thompson usb_fifo_put_data_linear(struct usb_fifo *f, void *ptr, 2086f9cb546cSAndrew Thompson usb_size_t len, uint8_t what) 208702ac6454SAndrew Thompson { 2088760bc48eSAndrew Thompson struct usb_mbuf *m; 2089f9cb546cSAndrew Thompson usb_size_t io_len; 209002ac6454SAndrew Thompson 209102ac6454SAndrew Thompson while (len || (what == 1)) { 209202ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 209302ac6454SAndrew Thompson 209402ac6454SAndrew Thompson if (m) { 209502ac6454SAndrew Thompson USB_MBUF_RESET(m); 209602ac6454SAndrew Thompson 209702ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 209802ac6454SAndrew Thompson 2099271ae033SHans Petter Selasky memcpy(m->cur_data_ptr, ptr, io_len); 210002ac6454SAndrew Thompson 210102ac6454SAndrew Thompson m->cur_data_len = io_len; 210202ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, io_len); 210302ac6454SAndrew Thompson len -= io_len; 210402ac6454SAndrew Thompson 210502ac6454SAndrew Thompson if ((len == 0) && (what == 1)) { 210602ac6454SAndrew Thompson m->last_packet = 1; 210702ac6454SAndrew Thompson } 210802ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 210902ac6454SAndrew Thompson 2110a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 211102ac6454SAndrew Thompson 211202ac6454SAndrew Thompson if ((len == 0) || (what == 1)) { 211302ac6454SAndrew Thompson break; 211402ac6454SAndrew Thompson } 211502ac6454SAndrew Thompson } else { 211602ac6454SAndrew Thompson break; 211702ac6454SAndrew Thompson } 211802ac6454SAndrew Thompson } 211902ac6454SAndrew Thompson } 212002ac6454SAndrew Thompson 212102ac6454SAndrew Thompson uint8_t 2122a593f6b8SAndrew Thompson usb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len) 212302ac6454SAndrew Thompson { 2124760bc48eSAndrew Thompson struct usb_mbuf *m; 212502ac6454SAndrew Thompson 212602ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 212702ac6454SAndrew Thompson 212802ac6454SAndrew Thompson if (m) { 212902ac6454SAndrew Thompson m->cur_data_len = len; 213002ac6454SAndrew Thompson m->cur_data_ptr = ptr; 213102ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 2132a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 213302ac6454SAndrew Thompson return (1); 213402ac6454SAndrew Thompson } 213502ac6454SAndrew Thompson return (0); 213602ac6454SAndrew Thompson } 213702ac6454SAndrew Thompson 213802ac6454SAndrew Thompson void 2139a593f6b8SAndrew Thompson usb_fifo_put_data_error(struct usb_fifo *f) 214002ac6454SAndrew Thompson { 214102ac6454SAndrew Thompson f->flag_iserror = 1; 2142a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 214302ac6454SAndrew Thompson } 214402ac6454SAndrew Thompson 214502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2146a593f6b8SAndrew Thompson * usb_fifo_get_data 214702ac6454SAndrew Thompson * 214802ac6454SAndrew Thompson * what: 214902ac6454SAndrew Thompson * 0 - normal operation 2150760bc48eSAndrew Thompson * 1 - only get one "usb_mbuf" 215102ac6454SAndrew Thompson * 215202ac6454SAndrew Thompson * returns: 215302ac6454SAndrew Thompson * 0 - no more data 215402ac6454SAndrew Thompson * 1 - data in buffer 215502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 215602ac6454SAndrew Thompson uint8_t 2157a593f6b8SAndrew Thompson usb_fifo_get_data(struct usb_fifo *f, struct usb_page_cache *pc, 2158e0a69b51SAndrew Thompson usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen, 215902ac6454SAndrew Thompson uint8_t what) 216002ac6454SAndrew Thompson { 2161760bc48eSAndrew Thompson struct usb_mbuf *m; 2162e0a69b51SAndrew Thompson usb_frlength_t io_len; 216302ac6454SAndrew Thompson uint8_t tr_data = 0; 216402ac6454SAndrew Thompson 216502ac6454SAndrew Thompson actlen[0] = 0; 216602ac6454SAndrew Thompson 216702ac6454SAndrew Thompson while (1) { 216802ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 216902ac6454SAndrew Thompson 217002ac6454SAndrew Thompson if (m) { 217102ac6454SAndrew Thompson tr_data = 1; 217202ac6454SAndrew Thompson 217302ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 217402ac6454SAndrew Thompson 2175a593f6b8SAndrew Thompson usbd_copy_in(pc, offset, m->cur_data_ptr, io_len); 217602ac6454SAndrew Thompson 217702ac6454SAndrew Thompson len -= io_len; 217802ac6454SAndrew Thompson offset += io_len; 217902ac6454SAndrew Thompson actlen[0] += io_len; 218002ac6454SAndrew Thompson m->cur_data_ptr += io_len; 218102ac6454SAndrew Thompson m->cur_data_len -= io_len; 218202ac6454SAndrew Thompson 218302ac6454SAndrew Thompson if ((m->cur_data_len == 0) || (what == 1)) { 218402ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 218502ac6454SAndrew Thompson 2186a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 218702ac6454SAndrew Thompson 218802ac6454SAndrew Thompson if (what == 1) { 218902ac6454SAndrew Thompson break; 219002ac6454SAndrew Thompson } 219102ac6454SAndrew Thompson } else { 219202ac6454SAndrew Thompson USB_IF_PREPEND(&f->used_q, m); 219302ac6454SAndrew Thompson } 219402ac6454SAndrew Thompson } else { 219502ac6454SAndrew Thompson if (tr_data) { 219602ac6454SAndrew Thompson /* wait for data to be written out */ 219702ac6454SAndrew Thompson break; 219802ac6454SAndrew Thompson } 219902ac6454SAndrew Thompson if (f->flag_flushing) { 22007214348fSAndrew Thompson /* check if we should send a short packet */ 22017214348fSAndrew Thompson if (f->flag_short != 0) { 22027214348fSAndrew Thompson f->flag_short = 0; 22037214348fSAndrew Thompson tr_data = 1; 22047214348fSAndrew Thompson break; 22057214348fSAndrew Thompson } 22067214348fSAndrew Thompson /* flushing complete */ 220702ac6454SAndrew Thompson f->flag_flushing = 0; 2208a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 220902ac6454SAndrew Thompson } 221002ac6454SAndrew Thompson break; 221102ac6454SAndrew Thompson } 221202ac6454SAndrew Thompson if (len == 0) { 221302ac6454SAndrew Thompson break; 221402ac6454SAndrew Thompson } 221502ac6454SAndrew Thompson } 221602ac6454SAndrew Thompson return (tr_data); 221702ac6454SAndrew Thompson } 221802ac6454SAndrew Thompson 221902ac6454SAndrew Thompson uint8_t 2220a593f6b8SAndrew Thompson usb_fifo_get_data_linear(struct usb_fifo *f, void *ptr, 2221f9cb546cSAndrew Thompson usb_size_t len, usb_size_t *actlen, uint8_t what) 222202ac6454SAndrew Thompson { 2223760bc48eSAndrew Thompson struct usb_mbuf *m; 2224f9cb546cSAndrew Thompson usb_size_t io_len; 222502ac6454SAndrew Thompson uint8_t tr_data = 0; 222602ac6454SAndrew Thompson 222702ac6454SAndrew Thompson actlen[0] = 0; 222802ac6454SAndrew Thompson 222902ac6454SAndrew Thompson while (1) { 223002ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 223102ac6454SAndrew Thompson 223202ac6454SAndrew Thompson if (m) { 223302ac6454SAndrew Thompson tr_data = 1; 223402ac6454SAndrew Thompson 223502ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 223602ac6454SAndrew Thompson 2237271ae033SHans Petter Selasky memcpy(ptr, m->cur_data_ptr, io_len); 223802ac6454SAndrew Thompson 223902ac6454SAndrew Thompson len -= io_len; 224002ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, io_len); 224102ac6454SAndrew Thompson actlen[0] += io_len; 224202ac6454SAndrew Thompson m->cur_data_ptr += io_len; 224302ac6454SAndrew Thompson m->cur_data_len -= io_len; 224402ac6454SAndrew Thompson 224502ac6454SAndrew Thompson if ((m->cur_data_len == 0) || (what == 1)) { 224602ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 224702ac6454SAndrew Thompson 2248a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 224902ac6454SAndrew Thompson 225002ac6454SAndrew Thompson if (what == 1) { 225102ac6454SAndrew Thompson break; 225202ac6454SAndrew Thompson } 225302ac6454SAndrew Thompson } else { 225402ac6454SAndrew Thompson USB_IF_PREPEND(&f->used_q, m); 225502ac6454SAndrew Thompson } 225602ac6454SAndrew Thompson } else { 225702ac6454SAndrew Thompson if (tr_data) { 225802ac6454SAndrew Thompson /* wait for data to be written out */ 225902ac6454SAndrew Thompson break; 226002ac6454SAndrew Thompson } 226102ac6454SAndrew Thompson if (f->flag_flushing) { 22627214348fSAndrew Thompson /* check if we should send a short packet */ 22637214348fSAndrew Thompson if (f->flag_short != 0) { 22647214348fSAndrew Thompson f->flag_short = 0; 22657214348fSAndrew Thompson tr_data = 1; 22667214348fSAndrew Thompson break; 22677214348fSAndrew Thompson } 22687214348fSAndrew Thompson /* flushing complete */ 226902ac6454SAndrew Thompson f->flag_flushing = 0; 2270a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 227102ac6454SAndrew Thompson } 227202ac6454SAndrew Thompson break; 227302ac6454SAndrew Thompson } 227402ac6454SAndrew Thompson if (len == 0) { 227502ac6454SAndrew Thompson break; 227602ac6454SAndrew Thompson } 227702ac6454SAndrew Thompson } 227802ac6454SAndrew Thompson return (tr_data); 227902ac6454SAndrew Thompson } 228002ac6454SAndrew Thompson 228102ac6454SAndrew Thompson uint8_t 2282a593f6b8SAndrew Thompson usb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen) 228302ac6454SAndrew Thompson { 2284760bc48eSAndrew Thompson struct usb_mbuf *m; 228502ac6454SAndrew Thompson 228602ac6454SAndrew Thompson USB_IF_POLL(&f->used_q, m); 228702ac6454SAndrew Thompson 228802ac6454SAndrew Thompson if (m) { 228902ac6454SAndrew Thompson *plen = m->cur_data_len; 229002ac6454SAndrew Thompson *pptr = m->cur_data_ptr; 229102ac6454SAndrew Thompson 229202ac6454SAndrew Thompson return (1); 229302ac6454SAndrew Thompson } 229402ac6454SAndrew Thompson return (0); 229502ac6454SAndrew Thompson } 229602ac6454SAndrew Thompson 229702ac6454SAndrew Thompson void 2298a593f6b8SAndrew Thompson usb_fifo_get_data_error(struct usb_fifo *f) 229902ac6454SAndrew Thompson { 230002ac6454SAndrew Thompson f->flag_iserror = 1; 2301a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 230202ac6454SAndrew Thompson } 230302ac6454SAndrew Thompson 230402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2305a593f6b8SAndrew Thompson * usb_alloc_symlink 230602ac6454SAndrew Thompson * 230702ac6454SAndrew Thompson * Return values: 230802ac6454SAndrew Thompson * NULL: Failure 230902ac6454SAndrew Thompson * Else: Pointer to symlink entry 231002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 2311760bc48eSAndrew Thompson struct usb_symlink * 2312a593f6b8SAndrew Thompson usb_alloc_symlink(const char *target) 231302ac6454SAndrew Thompson { 2314760bc48eSAndrew Thompson struct usb_symlink *ps; 231502ac6454SAndrew Thompson 231602ac6454SAndrew Thompson ps = malloc(sizeof(*ps), M_USBDEV, M_WAITOK); 2317ee3e3ff5SAndrew Thompson /* XXX no longer needed */ 2318ee3e3ff5SAndrew Thompson strlcpy(ps->src_path, target, sizeof(ps->src_path)); 2319ee3e3ff5SAndrew Thompson ps->src_len = strlen(ps->src_path); 232002ac6454SAndrew Thompson strlcpy(ps->dst_path, target, sizeof(ps->dst_path)); 232102ac6454SAndrew Thompson ps->dst_len = strlen(ps->dst_path); 232202ac6454SAndrew Thompson 2323a593f6b8SAndrew Thompson sx_xlock(&usb_sym_lock); 2324a593f6b8SAndrew Thompson TAILQ_INSERT_TAIL(&usb_sym_head, ps, sym_entry); 2325a593f6b8SAndrew Thompson sx_unlock(&usb_sym_lock); 232602ac6454SAndrew Thompson return (ps); 232702ac6454SAndrew Thompson } 232802ac6454SAndrew Thompson 232902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2330a593f6b8SAndrew Thompson * usb_free_symlink 233102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 233202ac6454SAndrew Thompson void 2333a593f6b8SAndrew Thompson usb_free_symlink(struct usb_symlink *ps) 233402ac6454SAndrew Thompson { 233502ac6454SAndrew Thompson if (ps == NULL) { 233602ac6454SAndrew Thompson return; 233702ac6454SAndrew Thompson } 2338a593f6b8SAndrew Thompson sx_xlock(&usb_sym_lock); 2339a593f6b8SAndrew Thompson TAILQ_REMOVE(&usb_sym_head, ps, sym_entry); 2340a593f6b8SAndrew Thompson sx_unlock(&usb_sym_lock); 234102ac6454SAndrew Thompson 234202ac6454SAndrew Thompson free(ps, M_USBDEV); 234302ac6454SAndrew Thompson } 234402ac6454SAndrew Thompson 234502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2346a593f6b8SAndrew Thompson * usb_read_symlink 234702ac6454SAndrew Thompson * 234802ac6454SAndrew Thompson * Return value: 234902ac6454SAndrew Thompson * 0: Success 235002ac6454SAndrew Thompson * Else: Failure 235102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 235202ac6454SAndrew Thompson int 2353a593f6b8SAndrew Thompson usb_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len) 235402ac6454SAndrew Thompson { 2355760bc48eSAndrew Thompson struct usb_symlink *ps; 235602ac6454SAndrew Thompson uint32_t temp; 235702ac6454SAndrew Thompson uint32_t delta = 0; 235802ac6454SAndrew Thompson uint8_t len; 235902ac6454SAndrew Thompson int error = 0; 236002ac6454SAndrew Thompson 2361a593f6b8SAndrew Thompson sx_xlock(&usb_sym_lock); 236202ac6454SAndrew Thompson 2363a593f6b8SAndrew Thompson TAILQ_FOREACH(ps, &usb_sym_head, sym_entry) { 236402ac6454SAndrew Thompson /* 236502ac6454SAndrew Thompson * Compute total length of source and destination symlink 236602ac6454SAndrew Thompson * strings pluss one length byte and two NUL bytes: 236702ac6454SAndrew Thompson */ 236802ac6454SAndrew Thompson temp = ps->src_len + ps->dst_len + 3; 236902ac6454SAndrew Thompson 237002ac6454SAndrew Thompson if (temp > 255) { 237102ac6454SAndrew Thompson /* 237202ac6454SAndrew Thompson * Skip entry because this length cannot fit 237302ac6454SAndrew Thompson * into one byte: 237402ac6454SAndrew Thompson */ 237502ac6454SAndrew Thompson continue; 237602ac6454SAndrew Thompson } 237702ac6454SAndrew Thompson if (startentry != 0) { 237802ac6454SAndrew Thompson /* decrement read offset */ 237902ac6454SAndrew Thompson startentry--; 238002ac6454SAndrew Thompson continue; 238102ac6454SAndrew Thompson } 238202ac6454SAndrew Thompson if (temp > user_len) { 238302ac6454SAndrew Thompson /* out of buffer space */ 238402ac6454SAndrew Thompson break; 238502ac6454SAndrew Thompson } 238602ac6454SAndrew Thompson len = temp; 238702ac6454SAndrew Thompson 238802ac6454SAndrew Thompson /* copy out total length */ 238902ac6454SAndrew Thompson 239002ac6454SAndrew Thompson error = copyout(&len, 239102ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 239202ac6454SAndrew Thompson if (error) { 239302ac6454SAndrew Thompson break; 239402ac6454SAndrew Thompson } 239502ac6454SAndrew Thompson delta += 1; 239602ac6454SAndrew Thompson 239702ac6454SAndrew Thompson /* copy out source string */ 239802ac6454SAndrew Thompson 239902ac6454SAndrew Thompson error = copyout(ps->src_path, 240002ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), ps->src_len); 240102ac6454SAndrew Thompson if (error) { 240202ac6454SAndrew Thompson break; 240302ac6454SAndrew Thompson } 240402ac6454SAndrew Thompson len = 0; 240502ac6454SAndrew Thompson delta += ps->src_len; 240602ac6454SAndrew Thompson error = copyout(&len, 240702ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 240802ac6454SAndrew Thompson if (error) { 240902ac6454SAndrew Thompson break; 241002ac6454SAndrew Thompson } 241102ac6454SAndrew Thompson delta += 1; 241202ac6454SAndrew Thompson 241302ac6454SAndrew Thompson /* copy out destination string */ 241402ac6454SAndrew Thompson 241502ac6454SAndrew Thompson error = copyout(ps->dst_path, 241602ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), ps->dst_len); 241702ac6454SAndrew Thompson if (error) { 241802ac6454SAndrew Thompson break; 241902ac6454SAndrew Thompson } 242002ac6454SAndrew Thompson len = 0; 242102ac6454SAndrew Thompson delta += ps->dst_len; 242202ac6454SAndrew Thompson error = copyout(&len, 242302ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 242402ac6454SAndrew Thompson if (error) { 242502ac6454SAndrew Thompson break; 242602ac6454SAndrew Thompson } 242702ac6454SAndrew Thompson delta += 1; 242802ac6454SAndrew Thompson 242902ac6454SAndrew Thompson user_len -= temp; 243002ac6454SAndrew Thompson } 243102ac6454SAndrew Thompson 243202ac6454SAndrew Thompson /* a zero length entry indicates the end */ 243302ac6454SAndrew Thompson 243402ac6454SAndrew Thompson if ((user_len != 0) && (error == 0)) { 243502ac6454SAndrew Thompson len = 0; 243602ac6454SAndrew Thompson 243702ac6454SAndrew Thompson error = copyout(&len, 243802ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 243902ac6454SAndrew Thompson } 2440a593f6b8SAndrew Thompson sx_unlock(&usb_sym_lock); 244102ac6454SAndrew Thompson return (error); 244202ac6454SAndrew Thompson } 24437214348fSAndrew Thompson 24447214348fSAndrew Thompson void 2445a593f6b8SAndrew Thompson usb_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff) 24467214348fSAndrew Thompson { 24477214348fSAndrew Thompson if (f == NULL) 24487214348fSAndrew Thompson return; 24497214348fSAndrew Thompson 24507214348fSAndrew Thompson /* send a Zero Length Packet, ZLP, before close */ 24517214348fSAndrew Thompson f->flag_short = onoff; 24527214348fSAndrew Thompson } 2453ed6d949aSAndrew Thompson 2454bd73b187SAlfred Perlstein void 2455bd73b187SAlfred Perlstein usb_fifo_set_write_defrag(struct usb_fifo *f, uint8_t onoff) 2456bd73b187SAlfred Perlstein { 2457bd73b187SAlfred Perlstein if (f == NULL) 2458bd73b187SAlfred Perlstein return; 2459bd73b187SAlfred Perlstein 2460bd73b187SAlfred Perlstein /* defrag written data */ 2461bd73b187SAlfred Perlstein f->flag_write_defrag = onoff; 2462bd73b187SAlfred Perlstein /* reset defrag state */ 2463bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 2464bd73b187SAlfred Perlstein } 2465bd73b187SAlfred Perlstein 2466ed6d949aSAndrew Thompson void * 2467ed6d949aSAndrew Thompson usb_fifo_softc(struct usb_fifo *f) 2468ed6d949aSAndrew Thompson { 2469ed6d949aSAndrew Thompson return (f->priv_sc0); 2470ed6d949aSAndrew Thompson } 24718755859aSAndrew Thompson #endif /* USB_HAVE_UGEN */ 2472