102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 302ac6454SAndrew Thompson * Copyright (c) 2006-2008 Hans Petter Selasky. All rights reserved. 402ac6454SAndrew Thompson * 502ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 602ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 702ac6454SAndrew Thompson * are met: 802ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 902ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1002ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1202ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1302ac6454SAndrew Thompson * 1402ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1502ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1602ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1702ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1802ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1902ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2002ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2102ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2202ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2302ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2402ac6454SAndrew Thompson * SUCH DAMAGE. 2502ac6454SAndrew Thompson * 2602ac6454SAndrew Thompson * 27a593f6b8SAndrew Thompson * usb_dev.c - An abstraction layer for creating devices under /dev/... 2802ac6454SAndrew Thompson */ 2902ac6454SAndrew Thompson 30ed6d949aSAndrew Thompson #include <sys/stdint.h> 31ed6d949aSAndrew Thompson #include <sys/stddef.h> 32ed6d949aSAndrew Thompson #include <sys/param.h> 33ed6d949aSAndrew Thompson #include <sys/queue.h> 34ed6d949aSAndrew Thompson #include <sys/types.h> 35ed6d949aSAndrew Thompson #include <sys/systm.h> 36ed6d949aSAndrew Thompson #include <sys/kernel.h> 37ed6d949aSAndrew Thompson #include <sys/bus.h> 38ed6d949aSAndrew Thompson #include <sys/linker_set.h> 39ed6d949aSAndrew Thompson #include <sys/module.h> 40ed6d949aSAndrew Thompson #include <sys/lock.h> 41ed6d949aSAndrew Thompson #include <sys/mutex.h> 42ed6d949aSAndrew Thompson #include <sys/condvar.h> 43ed6d949aSAndrew Thompson #include <sys/sysctl.h> 44ed6d949aSAndrew Thompson #include <sys/sx.h> 45ed6d949aSAndrew Thompson #include <sys/unistd.h> 46ed6d949aSAndrew Thompson #include <sys/callout.h> 47ed6d949aSAndrew Thompson #include <sys/malloc.h> 48ed6d949aSAndrew Thompson #include <sys/priv.h> 49ed6d949aSAndrew Thompson #include <sys/vnode.h> 50ed6d949aSAndrew Thompson #include <sys/conf.h> 51ed6d949aSAndrew Thompson #include <sys/fcntl.h> 52ed6d949aSAndrew Thompson 5302ac6454SAndrew Thompson #include <dev/usb/usb.h> 5402ac6454SAndrew Thompson #include <dev/usb/usb_ioctl.h> 55ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 56ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 5702ac6454SAndrew Thompson 58a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_fifo_debug 5902ac6454SAndrew Thompson 6002ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 6102ac6454SAndrew Thompson #include <dev/usb/usb_dev.h> 62ed6d949aSAndrew Thompson #include <dev/usb/usb_mbuf.h> 6302ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 6402ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 6502ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6602ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 6702ac6454SAndrew Thompson #include <dev/usb/usb_generic.h> 6802ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 6902ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 7002ac6454SAndrew Thompson 7102ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 7202ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 7302ac6454SAndrew Thompson 7402ac6454SAndrew Thompson #include <sys/filio.h> 7502ac6454SAndrew Thompson #include <sys/ttycom.h> 7602ac6454SAndrew Thompson #include <sys/syscallsubr.h> 7702ac6454SAndrew Thompson 7802ac6454SAndrew Thompson #include <machine/stdarg.h> 7902ac6454SAndrew Thompson 808755859aSAndrew Thompson #if USB_HAVE_UGEN 818755859aSAndrew Thompson 82ed6d949aSAndrew Thompson #ifdef USB_DEBUG 83a593f6b8SAndrew Thompson static int usb_fifo_debug = 0; 8402ac6454SAndrew Thompson 859360ae40SAndrew Thompson SYSCTL_NODE(_hw_usb, OID_AUTO, dev, CTLFLAG_RW, 0, "USB device"); 869360ae40SAndrew Thompson SYSCTL_INT(_hw_usb_dev, OID_AUTO, debug, CTLFLAG_RW, 87a593f6b8SAndrew Thompson &usb_fifo_debug, 0, "Debug Level"); 8802ac6454SAndrew Thompson #endif 8902ac6454SAndrew Thompson 9002ac6454SAndrew Thompson #if ((__FreeBSD_version >= 700001) || (__FreeBSD_version == 0) || \ 9102ac6454SAndrew Thompson ((__FreeBSD_version >= 600034) && (__FreeBSD_version < 700000))) 9202ac6454SAndrew Thompson #define USB_UCRED struct ucred *ucred, 9302ac6454SAndrew Thompson #else 9402ac6454SAndrew Thompson #define USB_UCRED 9502ac6454SAndrew Thompson #endif 9602ac6454SAndrew Thompson 9702ac6454SAndrew Thompson /* prototypes */ 9802ac6454SAndrew Thompson 99a593f6b8SAndrew Thompson static int usb_fifo_open(struct usb_cdev_privdata *, 100760bc48eSAndrew Thompson struct usb_fifo *, int); 101a593f6b8SAndrew Thompson static void usb_fifo_close(struct usb_fifo *, int); 102a593f6b8SAndrew Thompson static void usb_dev_init(void *); 103a593f6b8SAndrew Thompson static void usb_dev_init_post(void *); 104a593f6b8SAndrew Thompson static void usb_dev_uninit(void *); 105a593f6b8SAndrew Thompson static int usb_fifo_uiomove(struct usb_fifo *, void *, int, 10602ac6454SAndrew Thompson struct uio *); 107a593f6b8SAndrew Thompson static void usb_fifo_check_methods(struct usb_fifo_methods *); 108a593f6b8SAndrew Thompson static struct usb_fifo *usb_fifo_alloc(void); 109a593f6b8SAndrew Thompson static struct usb_endpoint *usb_dev_get_ep(struct usb_device *, uint8_t, 110f5f145baSAndrew Thompson uint8_t); 111a593f6b8SAndrew Thompson static void usb_loc_fill(struct usb_fs_privdata *, 112760bc48eSAndrew Thompson struct usb_cdev_privdata *); 113a593f6b8SAndrew Thompson static void usb_close(void *); 114a593f6b8SAndrew Thompson static usb_error_t usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *, int); 115a593f6b8SAndrew Thompson static usb_error_t usb_usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *); 116a593f6b8SAndrew Thompson static void usb_unref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *); 11702ac6454SAndrew Thompson 118a593f6b8SAndrew Thompson static d_open_t usb_open; 119a593f6b8SAndrew Thompson static d_ioctl_t usb_ioctl; 120a593f6b8SAndrew Thompson static d_read_t usb_read; 121a593f6b8SAndrew Thompson static d_write_t usb_write; 122a593f6b8SAndrew Thompson static d_poll_t usb_poll; 12302ac6454SAndrew Thompson 124a593f6b8SAndrew Thompson static d_ioctl_t usb_static_ioctl; 12502ac6454SAndrew Thompson 126a593f6b8SAndrew Thompson static usb_fifo_open_t usb_fifo_dummy_open; 127a593f6b8SAndrew Thompson static usb_fifo_close_t usb_fifo_dummy_close; 128a593f6b8SAndrew Thompson static usb_fifo_ioctl_t usb_fifo_dummy_ioctl; 129a593f6b8SAndrew Thompson static usb_fifo_cmd_t usb_fifo_dummy_cmd; 13002ac6454SAndrew Thompson 131ee3e3ff5SAndrew Thompson /* character device structure used for devices (/dev/ugenX.Y and /dev/uXXX) */ 132a593f6b8SAndrew Thompson struct cdevsw usb_devsw = { 13302ac6454SAndrew Thompson .d_version = D_VERSION, 134a593f6b8SAndrew Thompson .d_open = usb_open, 135a593f6b8SAndrew Thompson .d_ioctl = usb_ioctl, 136ee3e3ff5SAndrew Thompson .d_name = "usbdev", 13702ac6454SAndrew Thompson .d_flags = D_TRACKCLOSE, 138a593f6b8SAndrew Thompson .d_read = usb_read, 139a593f6b8SAndrew Thompson .d_write = usb_write, 140a593f6b8SAndrew Thompson .d_poll = usb_poll 14102ac6454SAndrew Thompson }; 14202ac6454SAndrew Thompson 143a593f6b8SAndrew Thompson static struct cdev* usb_dev = NULL; 144ee3e3ff5SAndrew Thompson 145ee3e3ff5SAndrew Thompson /* character device structure used for /dev/usb */ 146a593f6b8SAndrew Thompson static struct cdevsw usb_static_devsw = { 147ee3e3ff5SAndrew Thompson .d_version = D_VERSION, 148a593f6b8SAndrew Thompson .d_ioctl = usb_static_ioctl, 149ee3e3ff5SAndrew Thompson .d_name = "usb" 15002ac6454SAndrew Thompson }; 15102ac6454SAndrew Thompson 152a593f6b8SAndrew Thompson static TAILQ_HEAD(, usb_symlink) usb_sym_head; 153a593f6b8SAndrew Thompson static struct sx usb_sym_lock; 15402ac6454SAndrew Thompson 155a593f6b8SAndrew Thompson struct mtx usb_ref_lock; 15602ac6454SAndrew Thompson 15702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 158a593f6b8SAndrew Thompson * usb_loc_fill 15902ac6454SAndrew Thompson * 160760bc48eSAndrew Thompson * This is used to fill out a usb_cdev_privdata structure based on the 161760bc48eSAndrew Thompson * device's address as contained in usb_fs_privdata. 16202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 163ee3e3ff5SAndrew Thompson static void 164a593f6b8SAndrew Thompson usb_loc_fill(struct usb_fs_privdata* pd, struct usb_cdev_privdata *cpd) 16502ac6454SAndrew Thompson { 166ee3e3ff5SAndrew Thompson cpd->bus_index = pd->bus_index; 167ee3e3ff5SAndrew Thompson cpd->dev_index = pd->dev_index; 168ee3e3ff5SAndrew Thompson cpd->ep_addr = pd->ep_addr; 169ee3e3ff5SAndrew Thompson cpd->fifo_index = pd->fifo_index; 17002ac6454SAndrew Thompson } 17102ac6454SAndrew Thompson 17202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 173a593f6b8SAndrew Thompson * usb_ref_device 17402ac6454SAndrew Thompson * 17502ac6454SAndrew Thompson * This function is used to atomically refer an USB device by its 17602ac6454SAndrew Thompson * device location. If this function returns success the USB device 17702ac6454SAndrew Thompson * will not dissappear until the USB device is unreferenced. 17802ac6454SAndrew Thompson * 17902ac6454SAndrew Thompson * Return values: 18002ac6454SAndrew Thompson * 0: Success, refcount incremented on the given USB device. 18102ac6454SAndrew Thompson * Else: Failure. 18202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 183e0a69b51SAndrew Thompson usb_error_t 184a593f6b8SAndrew Thompson usb_ref_device(struct usb_cdev_privdata *cpd, 185e13e19faSAndrew Thompson struct usb_cdev_refdata *crd, int need_uref) 18602ac6454SAndrew Thompson { 187760bc48eSAndrew Thompson struct usb_fifo **ppf; 188760bc48eSAndrew Thompson struct usb_fifo *f; 18902ac6454SAndrew Thompson 190e13e19faSAndrew Thompson DPRINTFN(2, "cpd=%p need uref=%d\n", cpd, need_uref); 191e13e19faSAndrew Thompson 192e13e19faSAndrew Thompson /* clear all refs */ 193e13e19faSAndrew Thompson memset(crd, 0, sizeof(*crd)); 19402ac6454SAndrew Thompson 195a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 196a593f6b8SAndrew Thompson cpd->bus = devclass_get_softc(usb_devclass_ptr, cpd->bus_index); 197ee3e3ff5SAndrew Thompson if (cpd->bus == NULL) { 198ee3e3ff5SAndrew Thompson DPRINTFN(2, "no bus at %u\n", cpd->bus_index); 19902ac6454SAndrew Thompson goto error; 20002ac6454SAndrew Thompson } 201ee3e3ff5SAndrew Thompson cpd->udev = cpd->bus->devices[cpd->dev_index]; 202ee3e3ff5SAndrew Thompson if (cpd->udev == NULL) { 203ee3e3ff5SAndrew Thompson DPRINTFN(2, "no device at %u\n", cpd->dev_index); 20402ac6454SAndrew Thompson goto error; 20502ac6454SAndrew Thompson } 206ee3e3ff5SAndrew Thompson if (cpd->udev->refcount == USB_DEV_REF_MAX) { 20702ac6454SAndrew Thompson DPRINTFN(2, "no dev ref\n"); 20802ac6454SAndrew Thompson goto error; 20902ac6454SAndrew Thompson } 2100ed53d45SAndrew Thompson if (need_uref) { 2110ed53d45SAndrew Thompson DPRINTFN(2, "ref udev - needed\n"); 2120ed53d45SAndrew Thompson cpd->udev->refcount++; 2130ed53d45SAndrew Thompson 214a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 2150ed53d45SAndrew Thompson 2160ed53d45SAndrew Thompson /* 2170ed53d45SAndrew Thompson * We need to grab the sx-lock before grabbing the 2180ed53d45SAndrew Thompson * FIFO refs to avoid deadlock at detach! 2190ed53d45SAndrew Thompson */ 2200ed53d45SAndrew Thompson sx_xlock(cpd->udev->default_sx + 1); 2210ed53d45SAndrew Thompson 222a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 223a488edb5SAndrew Thompson 224a488edb5SAndrew Thompson /* 225a488edb5SAndrew Thompson * Set "is_uref" after grabbing the default SX lock 226a488edb5SAndrew Thompson */ 227e13e19faSAndrew Thompson crd->is_uref = 1; 2280ed53d45SAndrew Thompson } 2290ed53d45SAndrew Thompson 23002ac6454SAndrew Thompson /* check if we are doing an open */ 231ee3e3ff5SAndrew Thompson if (cpd->fflags == 0) { 232e13e19faSAndrew Thompson /* use zero defaults */ 23302ac6454SAndrew Thompson } else { 23402ac6454SAndrew Thompson /* check for write */ 235ee3e3ff5SAndrew Thompson if (cpd->fflags & FWRITE) { 236ee3e3ff5SAndrew Thompson ppf = cpd->udev->fifo; 237ee3e3ff5SAndrew Thompson f = ppf[cpd->fifo_index + USB_FIFO_TX]; 238e13e19faSAndrew Thompson crd->txfifo = f; 239e13e19faSAndrew Thompson crd->is_write = 1; /* ref */ 240ee3e3ff5SAndrew Thompson if (f == NULL || f->refcount == USB_FIFO_REF_MAX) 24102ac6454SAndrew Thompson goto error; 2427214348fSAndrew Thompson if (f->curr_cpd != cpd) 2437214348fSAndrew Thompson goto error; 24402ac6454SAndrew Thompson /* check if USB-FS is active */ 24502ac6454SAndrew Thompson if (f->fs_ep_max != 0) { 246e13e19faSAndrew Thompson crd->is_usbfs = 1; 24702ac6454SAndrew Thompson } 24802ac6454SAndrew Thompson } 24902ac6454SAndrew Thompson 25002ac6454SAndrew Thompson /* check for read */ 251ee3e3ff5SAndrew Thompson if (cpd->fflags & FREAD) { 252ee3e3ff5SAndrew Thompson ppf = cpd->udev->fifo; 253ee3e3ff5SAndrew Thompson f = ppf[cpd->fifo_index + USB_FIFO_RX]; 254e13e19faSAndrew Thompson crd->rxfifo = f; 255e13e19faSAndrew Thompson crd->is_read = 1; /* ref */ 256ee3e3ff5SAndrew Thompson if (f == NULL || f->refcount == USB_FIFO_REF_MAX) 25702ac6454SAndrew Thompson goto error; 2587214348fSAndrew Thompson if (f->curr_cpd != cpd) 2597214348fSAndrew Thompson goto error; 26002ac6454SAndrew Thompson /* check if USB-FS is active */ 26102ac6454SAndrew Thompson if (f->fs_ep_max != 0) { 262e13e19faSAndrew Thompson crd->is_usbfs = 1; 26302ac6454SAndrew Thompson } 26402ac6454SAndrew Thompson } 26502ac6454SAndrew Thompson } 26602ac6454SAndrew Thompson 26702ac6454SAndrew Thompson /* when everything is OK we increment the refcounts */ 268e13e19faSAndrew Thompson if (crd->is_write) { 26902ac6454SAndrew Thompson DPRINTFN(2, "ref write\n"); 270e13e19faSAndrew Thompson crd->txfifo->refcount++; 27102ac6454SAndrew Thompson } 272e13e19faSAndrew Thompson if (crd->is_read) { 27302ac6454SAndrew Thompson DPRINTFN(2, "ref read\n"); 274e13e19faSAndrew Thompson crd->rxfifo->refcount++; 27502ac6454SAndrew Thompson } 276a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 27702ac6454SAndrew Thompson 278e13e19faSAndrew Thompson if (crd->is_uref) { 27902ac6454SAndrew Thompson mtx_lock(&Giant); /* XXX */ 28002ac6454SAndrew Thompson } 28102ac6454SAndrew Thompson return (0); 28202ac6454SAndrew Thompson 28302ac6454SAndrew Thompson error: 284e13e19faSAndrew Thompson if (crd->is_uref) { 2850ed53d45SAndrew Thompson sx_unlock(cpd->udev->default_sx + 1); 2860ed53d45SAndrew Thompson if (--(cpd->udev->refcount) == 0) { 2878437751dSAndrew Thompson cv_signal(cpd->udev->default_cv + 1); 2880ed53d45SAndrew Thompson } 2890ed53d45SAndrew Thompson } 290a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 29102ac6454SAndrew Thompson DPRINTFN(2, "fail\n"); 29202ac6454SAndrew Thompson return (USB_ERR_INVAL); 29302ac6454SAndrew Thompson } 29402ac6454SAndrew Thompson 29502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 296a593f6b8SAndrew Thompson * usb_usb_ref_device 29702ac6454SAndrew Thompson * 29802ac6454SAndrew Thompson * This function is used to upgrade an USB reference to include the 29902ac6454SAndrew Thompson * USB device reference on a USB location. 30002ac6454SAndrew Thompson * 30102ac6454SAndrew Thompson * Return values: 30202ac6454SAndrew Thompson * 0: Success, refcount incremented on the given USB device. 30302ac6454SAndrew Thompson * Else: Failure. 30402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 305e0a69b51SAndrew Thompson static usb_error_t 306a593f6b8SAndrew Thompson usb_usb_ref_device(struct usb_cdev_privdata *cpd, 307e13e19faSAndrew Thompson struct usb_cdev_refdata *crd) 30802ac6454SAndrew Thompson { 30902ac6454SAndrew Thompson /* 31002ac6454SAndrew Thompson * Check if we already got an USB reference on this location: 31102ac6454SAndrew Thompson */ 312e13e19faSAndrew Thompson if (crd->is_uref) 31302ac6454SAndrew Thompson return (0); /* success */ 31402ac6454SAndrew Thompson 31502ac6454SAndrew Thompson /* 3160ed53d45SAndrew Thompson * To avoid deadlock at detach we need to drop the FIFO ref 3170ed53d45SAndrew Thompson * and re-acquire a new ref! 31802ac6454SAndrew Thompson */ 319a593f6b8SAndrew Thompson usb_unref_device(cpd, crd); 32002ac6454SAndrew Thompson 321a593f6b8SAndrew Thompson return (usb_ref_device(cpd, crd, 1 /* need uref */)); 32202ac6454SAndrew Thompson } 32302ac6454SAndrew Thompson 32402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 325a593f6b8SAndrew Thompson * usb_unref_device 32602ac6454SAndrew Thompson * 32702ac6454SAndrew Thompson * This function will release the reference count by one unit for the 32802ac6454SAndrew Thompson * given USB device. 32902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 33002ac6454SAndrew Thompson void 331a593f6b8SAndrew Thompson usb_unref_device(struct usb_cdev_privdata *cpd, 332e13e19faSAndrew Thompson struct usb_cdev_refdata *crd) 33302ac6454SAndrew Thompson { 334a488edb5SAndrew Thompson 335e13e19faSAndrew Thompson DPRINTFN(2, "cpd=%p is_uref=%d\n", cpd, crd->is_uref); 336a488edb5SAndrew Thompson 337e13e19faSAndrew Thompson if (crd->is_uref) { 33802ac6454SAndrew Thompson mtx_unlock(&Giant); /* XXX */ 339ee3e3ff5SAndrew Thompson sx_unlock(cpd->udev->default_sx + 1); 34002ac6454SAndrew Thompson } 341a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 342e13e19faSAndrew Thompson if (crd->is_read) { 343e13e19faSAndrew Thompson if (--(crd->rxfifo->refcount) == 0) { 3448437751dSAndrew Thompson cv_signal(&crd->rxfifo->cv_drain); 34502ac6454SAndrew Thompson } 346e13e19faSAndrew Thompson crd->is_read = 0; 34702ac6454SAndrew Thompson } 348e13e19faSAndrew Thompson if (crd->is_write) { 349e13e19faSAndrew Thompson if (--(crd->txfifo->refcount) == 0) { 3508437751dSAndrew Thompson cv_signal(&crd->txfifo->cv_drain); 35102ac6454SAndrew Thompson } 352e13e19faSAndrew Thompson crd->is_write = 0; 35302ac6454SAndrew Thompson } 354e13e19faSAndrew Thompson if (crd->is_uref) { 355ee3e3ff5SAndrew Thompson if (--(cpd->udev->refcount) == 0) { 3568437751dSAndrew Thompson cv_signal(cpd->udev->default_cv + 1); 35702ac6454SAndrew Thompson } 358e13e19faSAndrew Thompson crd->is_uref = 0; 35902ac6454SAndrew Thompson } 360a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 36102ac6454SAndrew Thompson } 36202ac6454SAndrew Thompson 363760bc48eSAndrew Thompson static struct usb_fifo * 364a593f6b8SAndrew Thompson usb_fifo_alloc(void) 36502ac6454SAndrew Thompson { 366760bc48eSAndrew Thompson struct usb_fifo *f; 36702ac6454SAndrew Thompson 36802ac6454SAndrew Thompson f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO); 36902ac6454SAndrew Thompson if (f) { 3708437751dSAndrew Thompson cv_init(&f->cv_io, "FIFO-IO"); 3718437751dSAndrew Thompson cv_init(&f->cv_drain, "FIFO-DRAIN"); 37202ac6454SAndrew Thompson f->refcount = 1; 37302ac6454SAndrew Thompson } 37402ac6454SAndrew Thompson return (f); 37502ac6454SAndrew Thompson } 37602ac6454SAndrew Thompson 37702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 378a593f6b8SAndrew Thompson * usb_fifo_create 37902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 38002ac6454SAndrew Thompson static int 381a593f6b8SAndrew Thompson usb_fifo_create(struct usb_cdev_privdata *cpd, 382e13e19faSAndrew Thompson struct usb_cdev_refdata *crd) 38302ac6454SAndrew Thompson { 384760bc48eSAndrew Thompson struct usb_device *udev = cpd->udev; 385760bc48eSAndrew Thompson struct usb_fifo *f; 386ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 38702ac6454SAndrew Thompson uint8_t n; 38802ac6454SAndrew Thompson uint8_t is_tx; 38902ac6454SAndrew Thompson uint8_t is_rx; 39002ac6454SAndrew Thompson uint8_t no_null; 39102ac6454SAndrew Thompson uint8_t is_busy; 392ae60fdfbSAndrew Thompson int e = cpd->ep_addr; 39302ac6454SAndrew Thompson 394ee3e3ff5SAndrew Thompson is_tx = (cpd->fflags & FWRITE) ? 1 : 0; 395ee3e3ff5SAndrew Thompson is_rx = (cpd->fflags & FREAD) ? 1 : 0; 39602ac6454SAndrew Thompson no_null = 1; 39702ac6454SAndrew Thompson is_busy = 0; 39802ac6454SAndrew Thompson 399ee3e3ff5SAndrew Thompson /* Preallocated FIFO */ 400ae60fdfbSAndrew Thompson if (e < 0) { 401ee3e3ff5SAndrew Thompson DPRINTFN(5, "Preallocated FIFO\n"); 402ee3e3ff5SAndrew Thompson if (is_tx) { 403ee3e3ff5SAndrew Thompson f = udev->fifo[cpd->fifo_index + USB_FIFO_TX]; 404ee3e3ff5SAndrew Thompson if (f == NULL) 405ee3e3ff5SAndrew Thompson return (EINVAL); 406e13e19faSAndrew Thompson crd->txfifo = f; 407ee3e3ff5SAndrew Thompson } 408ee3e3ff5SAndrew Thompson if (is_rx) { 409ee3e3ff5SAndrew Thompson f = udev->fifo[cpd->fifo_index + USB_FIFO_RX]; 410ee3e3ff5SAndrew Thompson if (f == NULL) 411ee3e3ff5SAndrew Thompson return (EINVAL); 412e13e19faSAndrew Thompson crd->rxfifo = f; 413ee3e3ff5SAndrew Thompson } 414ee3e3ff5SAndrew Thompson return (0); 415ee3e3ff5SAndrew Thompson } 41602ac6454SAndrew Thompson 417ae60fdfbSAndrew Thompson KASSERT(e >= 0 && e <= 15, ("endpoint %d out of range", e)); 418ee3e3ff5SAndrew Thompson 419ee3e3ff5SAndrew Thompson /* search for a free FIFO slot */ 420ae60fdfbSAndrew Thompson DPRINTFN(5, "Endpoint device, searching for 0x%02x\n", e); 42102ac6454SAndrew Thompson for (n = 0;; n += 2) { 42202ac6454SAndrew Thompson 42302ac6454SAndrew Thompson if (n == USB_FIFO_MAX) { 42402ac6454SAndrew Thompson if (no_null) { 42502ac6454SAndrew Thompson no_null = 0; 42602ac6454SAndrew Thompson n = 0; 42702ac6454SAndrew Thompson } else { 42802ac6454SAndrew Thompson /* end of FIFOs reached */ 429ee3e3ff5SAndrew Thompson DPRINTFN(5, "out of FIFOs\n"); 43002ac6454SAndrew Thompson return (ENOMEM); 43102ac6454SAndrew Thompson } 43202ac6454SAndrew Thompson } 43302ac6454SAndrew Thompson /* Check for TX FIFO */ 43402ac6454SAndrew Thompson if (is_tx) { 43502ac6454SAndrew Thompson f = udev->fifo[n + USB_FIFO_TX]; 43602ac6454SAndrew Thompson if (f != NULL) { 437ae60fdfbSAndrew Thompson if (f->dev_ep_index != e) { 43802ac6454SAndrew Thompson /* wrong endpoint index */ 43902ac6454SAndrew Thompson continue; 44002ac6454SAndrew Thompson } 4417214348fSAndrew Thompson if (f->curr_cpd != NULL) { 44202ac6454SAndrew Thompson /* FIFO is opened */ 44302ac6454SAndrew Thompson is_busy = 1; 44402ac6454SAndrew Thompson continue; 44502ac6454SAndrew Thompson } 44602ac6454SAndrew Thompson } else if (no_null) { 44702ac6454SAndrew Thompson continue; 44802ac6454SAndrew Thompson } 44902ac6454SAndrew Thompson } 45002ac6454SAndrew Thompson /* Check for RX FIFO */ 45102ac6454SAndrew Thompson if (is_rx) { 45202ac6454SAndrew Thompson f = udev->fifo[n + USB_FIFO_RX]; 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 break; 46802ac6454SAndrew Thompson } 46902ac6454SAndrew Thompson 47002ac6454SAndrew Thompson if (no_null == 0) { 471ae60fdfbSAndrew Thompson if (e >= (USB_EP_MAX / 2)) { 47202ac6454SAndrew Thompson /* we don't create any endpoints in this range */ 4737214348fSAndrew Thompson DPRINTFN(5, "ep out of range\n"); 47402ac6454SAndrew Thompson return (is_busy ? EBUSY : EINVAL); 47502ac6454SAndrew Thompson } 47602ac6454SAndrew Thompson } 4777214348fSAndrew Thompson 478ae60fdfbSAndrew Thompson if ((e != 0) && is_busy) { 4797214348fSAndrew Thompson /* 4807214348fSAndrew Thompson * Only the default control endpoint is allowed to be 4817214348fSAndrew Thompson * opened multiple times! 4827214348fSAndrew Thompson */ 4837214348fSAndrew Thompson DPRINTFN(5, "busy\n"); 4847214348fSAndrew Thompson return (EBUSY); 4857214348fSAndrew Thompson } 4867214348fSAndrew Thompson 48702ac6454SAndrew Thompson /* Check TX FIFO */ 48802ac6454SAndrew Thompson if (is_tx && 48902ac6454SAndrew Thompson (udev->fifo[n + USB_FIFO_TX] == NULL)) { 490a593f6b8SAndrew Thompson ep = usb_dev_get_ep(udev, e, USB_FIFO_TX); 491ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_TX); 492ae60fdfbSAndrew Thompson if (ep == NULL) { 493ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint returned NULL\n"); 49402ac6454SAndrew Thompson return (EINVAL); 49502ac6454SAndrew Thompson } 496a593f6b8SAndrew Thompson f = usb_fifo_alloc(); 49702ac6454SAndrew Thompson if (f == NULL) { 498ee3e3ff5SAndrew Thompson DPRINTFN(5, "could not alloc tx fifo\n"); 49902ac6454SAndrew Thompson return (ENOMEM); 50002ac6454SAndrew Thompson } 50102ac6454SAndrew Thompson /* update some fields */ 50202ac6454SAndrew Thompson f->fifo_index = n + USB_FIFO_TX; 503ae60fdfbSAndrew Thompson f->dev_ep_index = e; 50402ac6454SAndrew Thompson f->priv_mtx = udev->default_mtx; 505ae60fdfbSAndrew Thompson f->priv_sc0 = ep; 506a593f6b8SAndrew Thompson f->methods = &usb_ugen_methods; 507ae60fdfbSAndrew Thompson f->iface_index = ep->iface_index; 50802ac6454SAndrew Thompson f->udev = udev; 509a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 51002ac6454SAndrew Thompson udev->fifo[n + USB_FIFO_TX] = f; 511a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 51202ac6454SAndrew Thompson } 51302ac6454SAndrew Thompson /* Check RX FIFO */ 51402ac6454SAndrew Thompson if (is_rx && 51502ac6454SAndrew Thompson (udev->fifo[n + USB_FIFO_RX] == NULL)) { 51602ac6454SAndrew Thompson 517a593f6b8SAndrew Thompson ep = usb_dev_get_ep(udev, e, USB_FIFO_RX); 518ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_RX); 519ae60fdfbSAndrew Thompson if (ep == NULL) { 520ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint returned NULL\n"); 52102ac6454SAndrew Thompson return (EINVAL); 52202ac6454SAndrew Thompson } 523a593f6b8SAndrew Thompson f = usb_fifo_alloc(); 52402ac6454SAndrew Thompson if (f == NULL) { 525ee3e3ff5SAndrew Thompson DPRINTFN(5, "could not alloc rx fifo\n"); 52602ac6454SAndrew Thompson return (ENOMEM); 52702ac6454SAndrew Thompson } 52802ac6454SAndrew Thompson /* update some fields */ 52902ac6454SAndrew Thompson f->fifo_index = n + USB_FIFO_RX; 530ae60fdfbSAndrew Thompson f->dev_ep_index = e; 53102ac6454SAndrew Thompson f->priv_mtx = udev->default_mtx; 532ae60fdfbSAndrew Thompson f->priv_sc0 = ep; 533a593f6b8SAndrew Thompson f->methods = &usb_ugen_methods; 534ae60fdfbSAndrew Thompson f->iface_index = ep->iface_index; 53502ac6454SAndrew Thompson f->udev = udev; 536a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 53702ac6454SAndrew Thompson udev->fifo[n + USB_FIFO_RX] = f; 538a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 53902ac6454SAndrew Thompson } 54002ac6454SAndrew Thompson if (is_tx) { 541e13e19faSAndrew Thompson crd->txfifo = udev->fifo[n + USB_FIFO_TX]; 54202ac6454SAndrew Thompson } 54302ac6454SAndrew Thompson if (is_rx) { 544e13e19faSAndrew Thompson crd->rxfifo = udev->fifo[n + USB_FIFO_RX]; 54502ac6454SAndrew Thompson } 546ee3e3ff5SAndrew Thompson /* fill out fifo index */ 547ee3e3ff5SAndrew Thompson DPRINTFN(5, "fifo index = %d\n", n); 548ee3e3ff5SAndrew Thompson cpd->fifo_index = n; 54902ac6454SAndrew Thompson 55002ac6454SAndrew Thompson /* complete */ 55102ac6454SAndrew Thompson 55202ac6454SAndrew Thompson return (0); 55302ac6454SAndrew Thompson } 55402ac6454SAndrew Thompson 55502ac6454SAndrew Thompson void 556a593f6b8SAndrew Thompson usb_fifo_free(struct usb_fifo *f) 55702ac6454SAndrew Thompson { 55802ac6454SAndrew Thompson uint8_t n; 55902ac6454SAndrew Thompson 56002ac6454SAndrew Thompson if (f == NULL) { 56102ac6454SAndrew Thompson /* be NULL safe */ 56202ac6454SAndrew Thompson return; 56302ac6454SAndrew Thompson } 56402ac6454SAndrew Thompson /* destroy symlink devices, if any */ 56502ac6454SAndrew Thompson for (n = 0; n != 2; n++) { 56602ac6454SAndrew Thompson if (f->symlink[n]) { 567a593f6b8SAndrew Thompson usb_free_symlink(f->symlink[n]); 56802ac6454SAndrew Thompson f->symlink[n] = NULL; 56902ac6454SAndrew Thompson } 57002ac6454SAndrew Thompson } 571a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 57202ac6454SAndrew Thompson 57302ac6454SAndrew Thompson /* delink ourselves to stop calls from userland */ 57402ac6454SAndrew Thompson if ((f->fifo_index < USB_FIFO_MAX) && 57502ac6454SAndrew Thompson (f->udev != NULL) && 57602ac6454SAndrew Thompson (f->udev->fifo[f->fifo_index] == f)) { 57702ac6454SAndrew Thompson f->udev->fifo[f->fifo_index] = NULL; 57802ac6454SAndrew Thompson } else { 57902ac6454SAndrew Thompson DPRINTFN(0, "USB FIFO %p has not been linked!\n", f); 58002ac6454SAndrew Thompson } 58102ac6454SAndrew Thompson 58202ac6454SAndrew Thompson /* decrease refcount */ 58302ac6454SAndrew Thompson f->refcount--; 58402ac6454SAndrew Thompson /* prevent any write flush */ 58502ac6454SAndrew Thompson f->flag_iserror = 1; 58602ac6454SAndrew Thompson /* need to wait until all callers have exited */ 58702ac6454SAndrew Thompson while (f->refcount != 0) { 588a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); /* avoid LOR */ 58902ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 59002ac6454SAndrew Thompson /* get I/O thread out of any sleep state */ 59102ac6454SAndrew Thompson if (f->flag_sleeping) { 59202ac6454SAndrew Thompson f->flag_sleeping = 0; 5938437751dSAndrew Thompson cv_broadcast(&f->cv_io); 59402ac6454SAndrew Thompson } 59502ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 596a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 59702ac6454SAndrew Thompson 59802ac6454SAndrew Thompson /* wait for sync */ 599a593f6b8SAndrew Thompson cv_wait(&f->cv_drain, &usb_ref_lock); 60002ac6454SAndrew Thompson } 601a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 60202ac6454SAndrew Thompson 60302ac6454SAndrew Thompson /* take care of closing the device here, if any */ 604a593f6b8SAndrew Thompson usb_fifo_close(f, 0); 60502ac6454SAndrew Thompson 6068437751dSAndrew Thompson cv_destroy(&f->cv_io); 6078437751dSAndrew Thompson cv_destroy(&f->cv_drain); 60802ac6454SAndrew Thompson 60902ac6454SAndrew Thompson free(f, M_USBDEV); 61002ac6454SAndrew Thompson } 61102ac6454SAndrew Thompson 612ae60fdfbSAndrew Thompson static struct usb_endpoint * 613a593f6b8SAndrew Thompson usb_dev_get_ep(struct usb_device *udev, uint8_t ep_index, uint8_t dir) 61402ac6454SAndrew Thompson { 615ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 61602ac6454SAndrew Thompson uint8_t ep_dir; 61702ac6454SAndrew Thompson 61802ac6454SAndrew Thompson if (ep_index == 0) { 619ae60fdfbSAndrew Thompson ep = &udev->default_ep; 62002ac6454SAndrew Thompson } else { 62102ac6454SAndrew Thompson if (dir == USB_FIFO_RX) { 622f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_HOST) { 62302ac6454SAndrew Thompson ep_dir = UE_DIR_IN; 62402ac6454SAndrew Thompson } else { 62502ac6454SAndrew Thompson ep_dir = UE_DIR_OUT; 62602ac6454SAndrew Thompson } 62702ac6454SAndrew Thompson } else { 628f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_HOST) { 62902ac6454SAndrew Thompson ep_dir = UE_DIR_OUT; 63002ac6454SAndrew Thompson } else { 63102ac6454SAndrew Thompson ep_dir = UE_DIR_IN; 63202ac6454SAndrew Thompson } 63302ac6454SAndrew Thompson } 634a593f6b8SAndrew Thompson ep = usbd_get_ep_by_addr(udev, ep_index | ep_dir); 63502ac6454SAndrew Thompson } 63602ac6454SAndrew Thompson 637ae60fdfbSAndrew Thompson if (ep == NULL) { 638ae60fdfbSAndrew Thompson /* if the endpoint does not exist then return */ 63902ac6454SAndrew Thompson return (NULL); 64002ac6454SAndrew Thompson } 641ae60fdfbSAndrew Thompson if (ep->edesc == NULL) { 642ae60fdfbSAndrew Thompson /* invalid endpoint */ 64302ac6454SAndrew Thompson return (NULL); 64402ac6454SAndrew Thompson } 645ae60fdfbSAndrew Thompson return (ep); /* success */ 64602ac6454SAndrew Thompson } 64702ac6454SAndrew Thompson 64802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 649a593f6b8SAndrew Thompson * usb_fifo_open 65002ac6454SAndrew Thompson * 65102ac6454SAndrew Thompson * Returns: 65202ac6454SAndrew Thompson * 0: Success 65302ac6454SAndrew Thompson * Else: Failure 65402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 65502ac6454SAndrew Thompson static int 656a593f6b8SAndrew Thompson usb_fifo_open(struct usb_cdev_privdata *cpd, 657760bc48eSAndrew Thompson struct usb_fifo *f, int fflags) 65802ac6454SAndrew Thompson { 65902ac6454SAndrew Thompson int err; 66002ac6454SAndrew Thompson 66102ac6454SAndrew Thompson if (f == NULL) { 66202ac6454SAndrew Thompson /* no FIFO there */ 66302ac6454SAndrew Thompson DPRINTFN(2, "no FIFO\n"); 66402ac6454SAndrew Thompson return (ENXIO); 66502ac6454SAndrew Thompson } 66602ac6454SAndrew Thompson /* remove FWRITE and FREAD flags */ 66702ac6454SAndrew Thompson fflags &= ~(FWRITE | FREAD); 66802ac6454SAndrew Thompson 66902ac6454SAndrew Thompson /* set correct file flags */ 67002ac6454SAndrew Thompson if ((f->fifo_index & 1) == USB_FIFO_TX) { 67102ac6454SAndrew Thompson fflags |= FWRITE; 67202ac6454SAndrew Thompson } else { 67302ac6454SAndrew Thompson fflags |= FREAD; 67402ac6454SAndrew Thompson } 67502ac6454SAndrew Thompson 67602ac6454SAndrew Thompson /* check if we are already opened */ 67702ac6454SAndrew Thompson /* we don't need any locks when checking this variable */ 6787214348fSAndrew Thompson if (f->curr_cpd != NULL) { 67902ac6454SAndrew Thompson err = EBUSY; 68002ac6454SAndrew Thompson goto done; 68102ac6454SAndrew Thompson } 682ee3e3ff5SAndrew Thompson 6837214348fSAndrew Thompson /* reset short flag before open */ 6847214348fSAndrew Thompson f->flag_short = 0; 6857214348fSAndrew Thompson 68602ac6454SAndrew Thompson /* call open method */ 687ee3e3ff5SAndrew Thompson err = (f->methods->f_open) (f, fflags); 68802ac6454SAndrew Thompson if (err) { 68902ac6454SAndrew Thompson goto done; 69002ac6454SAndrew Thompson } 69102ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 69202ac6454SAndrew Thompson 69302ac6454SAndrew Thompson /* reset sleep flag */ 69402ac6454SAndrew Thompson f->flag_sleeping = 0; 69502ac6454SAndrew Thompson 69602ac6454SAndrew Thompson /* reset error flag */ 69702ac6454SAndrew Thompson f->flag_iserror = 0; 69802ac6454SAndrew Thompson 69902ac6454SAndrew Thompson /* reset complete flag */ 70002ac6454SAndrew Thompson f->flag_iscomplete = 0; 70102ac6454SAndrew Thompson 70202ac6454SAndrew Thompson /* reset select flag */ 70302ac6454SAndrew Thompson f->flag_isselect = 0; 70402ac6454SAndrew Thompson 70502ac6454SAndrew Thompson /* reset flushing flag */ 70602ac6454SAndrew Thompson f->flag_flushing = 0; 70702ac6454SAndrew Thompson 70802ac6454SAndrew Thompson /* reset ASYNC proc flag */ 70902ac6454SAndrew Thompson f->async_p = NULL; 71002ac6454SAndrew Thompson 711a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 7127214348fSAndrew Thompson /* flag the fifo as opened to prevent others */ 7137214348fSAndrew Thompson f->curr_cpd = cpd; 714a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 71502ac6454SAndrew Thompson 71602ac6454SAndrew Thompson /* reset queue */ 717a593f6b8SAndrew Thompson usb_fifo_reset(f); 71802ac6454SAndrew Thompson 71902ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 72002ac6454SAndrew Thompson done: 72102ac6454SAndrew Thompson return (err); 72202ac6454SAndrew Thompson } 72302ac6454SAndrew Thompson 72402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 725a593f6b8SAndrew Thompson * usb_fifo_reset 72602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 72702ac6454SAndrew Thompson void 728a593f6b8SAndrew Thompson usb_fifo_reset(struct usb_fifo *f) 72902ac6454SAndrew Thompson { 730760bc48eSAndrew Thompson struct usb_mbuf *m; 73102ac6454SAndrew Thompson 73202ac6454SAndrew Thompson if (f == NULL) { 73302ac6454SAndrew Thompson return; 73402ac6454SAndrew Thompson } 73502ac6454SAndrew Thompson while (1) { 73602ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 73702ac6454SAndrew Thompson if (m) { 73802ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 73902ac6454SAndrew Thompson } else { 74002ac6454SAndrew Thompson break; 74102ac6454SAndrew Thompson } 74202ac6454SAndrew Thompson } 74302ac6454SAndrew Thompson } 74402ac6454SAndrew Thompson 74502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 746a593f6b8SAndrew Thompson * usb_fifo_close 74702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 74802ac6454SAndrew Thompson static void 749a593f6b8SAndrew Thompson usb_fifo_close(struct usb_fifo *f, int fflags) 75002ac6454SAndrew Thompson { 75102ac6454SAndrew Thompson int err; 75202ac6454SAndrew Thompson 75302ac6454SAndrew Thompson /* check if we are not opened */ 7547214348fSAndrew Thompson if (f->curr_cpd == NULL) { 75502ac6454SAndrew Thompson /* nothing to do - already closed */ 75602ac6454SAndrew Thompson return; 75702ac6454SAndrew Thompson } 75802ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 75902ac6454SAndrew Thompson 7607214348fSAndrew Thompson /* clear current cdev private data pointer */ 7617214348fSAndrew Thompson f->curr_cpd = NULL; 76202ac6454SAndrew Thompson 76302ac6454SAndrew Thompson /* check if we are selected */ 76402ac6454SAndrew Thompson if (f->flag_isselect) { 76502ac6454SAndrew Thompson selwakeup(&f->selinfo); 76602ac6454SAndrew Thompson f->flag_isselect = 0; 76702ac6454SAndrew Thompson } 76802ac6454SAndrew Thompson /* check if a thread wants SIGIO */ 76902ac6454SAndrew Thompson if (f->async_p != NULL) { 77002ac6454SAndrew Thompson PROC_LOCK(f->async_p); 77102ac6454SAndrew Thompson psignal(f->async_p, SIGIO); 77202ac6454SAndrew Thompson PROC_UNLOCK(f->async_p); 77302ac6454SAndrew Thompson f->async_p = NULL; 77402ac6454SAndrew Thompson } 77502ac6454SAndrew Thompson /* remove FWRITE and FREAD flags */ 77602ac6454SAndrew Thompson fflags &= ~(FWRITE | FREAD); 77702ac6454SAndrew Thompson 77802ac6454SAndrew Thompson /* flush written data, if any */ 77902ac6454SAndrew Thompson if ((f->fifo_index & 1) == USB_FIFO_TX) { 78002ac6454SAndrew Thompson 78102ac6454SAndrew Thompson if (!f->flag_iserror) { 78202ac6454SAndrew Thompson 78302ac6454SAndrew Thompson /* set flushing flag */ 78402ac6454SAndrew Thompson f->flag_flushing = 1; 78502ac6454SAndrew Thompson 78602ac6454SAndrew Thompson /* start write transfer, if not already started */ 78702ac6454SAndrew Thompson (f->methods->f_start_write) (f); 78802ac6454SAndrew Thompson 78902ac6454SAndrew Thompson /* check if flushed already */ 79002ac6454SAndrew Thompson while (f->flag_flushing && 79102ac6454SAndrew Thompson (!f->flag_iserror)) { 79202ac6454SAndrew Thompson /* wait until all data has been written */ 79302ac6454SAndrew Thompson f->flag_sleeping = 1; 7948437751dSAndrew Thompson err = cv_wait_sig(&f->cv_io, f->priv_mtx); 79502ac6454SAndrew Thompson if (err) { 79602ac6454SAndrew Thompson DPRINTF("signal received\n"); 79702ac6454SAndrew Thompson break; 79802ac6454SAndrew Thompson } 79902ac6454SAndrew Thompson } 80002ac6454SAndrew Thompson } 80102ac6454SAndrew Thompson fflags |= FWRITE; 80202ac6454SAndrew Thompson 80302ac6454SAndrew Thompson /* stop write transfer, if not already stopped */ 80402ac6454SAndrew Thompson (f->methods->f_stop_write) (f); 80502ac6454SAndrew Thompson } else { 80602ac6454SAndrew Thompson fflags |= FREAD; 80702ac6454SAndrew Thompson 80802ac6454SAndrew Thompson /* stop write transfer, if not already stopped */ 80902ac6454SAndrew Thompson (f->methods->f_stop_read) (f); 81002ac6454SAndrew Thompson } 81102ac6454SAndrew Thompson 81202ac6454SAndrew Thompson /* check if we are sleeping */ 81302ac6454SAndrew Thompson if (f->flag_sleeping) { 81402ac6454SAndrew Thompson DPRINTFN(2, "Sleeping at close!\n"); 81502ac6454SAndrew Thompson } 81602ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 81702ac6454SAndrew Thompson 81802ac6454SAndrew Thompson /* call close method */ 819ee3e3ff5SAndrew Thompson (f->methods->f_close) (f, fflags); 82002ac6454SAndrew Thompson 82102ac6454SAndrew Thompson DPRINTF("closed\n"); 82202ac6454SAndrew Thompson } 82302ac6454SAndrew Thompson 82402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 825a593f6b8SAndrew Thompson * usb_open - cdev callback 82602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 82702ac6454SAndrew Thompson static int 828a593f6b8SAndrew Thompson usb_open(struct cdev *dev, int fflags, int devtype, struct thread *td) 82902ac6454SAndrew Thompson { 830760bc48eSAndrew Thompson struct usb_fs_privdata* pd = (struct usb_fs_privdata*)dev->si_drv1; 831e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 832760bc48eSAndrew Thompson struct usb_cdev_privdata *cpd; 833ee3e3ff5SAndrew Thompson int err, ep; 83402ac6454SAndrew Thompson 835cba30496SAndrew Thompson DPRINTFN(2, "%s fflags=0x%08x\n", dev->si_name, fflags); 83602ac6454SAndrew Thompson 837ee3e3ff5SAndrew Thompson KASSERT(fflags & (FREAD|FWRITE), ("invalid open flags")); 838ee3e3ff5SAndrew Thompson if (((fflags & FREAD) && !(pd->mode & FREAD)) || 839ee3e3ff5SAndrew Thompson ((fflags & FWRITE) && !(pd->mode & FWRITE))) { 840ee3e3ff5SAndrew Thompson DPRINTFN(2, "access mode not supported\n"); 84102ac6454SAndrew Thompson return (EPERM); 84202ac6454SAndrew Thompson } 843ee3e3ff5SAndrew Thompson 844ee3e3ff5SAndrew Thompson cpd = malloc(sizeof(*cpd), M_USBDEV, M_WAITOK | M_ZERO); 845ee3e3ff5SAndrew Thompson ep = cpd->ep_addr = pd->ep_addr; 846ee3e3ff5SAndrew Thompson 847a593f6b8SAndrew Thompson usb_loc_fill(pd, cpd); 848a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 1); 84902ac6454SAndrew Thompson if (err) { 85002ac6454SAndrew Thompson DPRINTFN(2, "cannot ref device\n"); 851ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 85202ac6454SAndrew Thompson return (ENXIO); 85302ac6454SAndrew Thompson } 854ee3e3ff5SAndrew Thompson cpd->fflags = fflags; /* access mode for open lifetime */ 85502ac6454SAndrew Thompson 85602ac6454SAndrew Thompson /* create FIFOs, if any */ 857a593f6b8SAndrew Thompson err = usb_fifo_create(cpd, &refs); 85802ac6454SAndrew Thompson /* check for error */ 85902ac6454SAndrew Thompson if (err) { 860ee3e3ff5SAndrew Thompson DPRINTFN(2, "cannot create fifo\n"); 861a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 862ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 86302ac6454SAndrew Thompson return (err); 86402ac6454SAndrew Thompson } 86502ac6454SAndrew Thompson if (fflags & FREAD) { 866a593f6b8SAndrew Thompson err = usb_fifo_open(cpd, refs.rxfifo, fflags); 86702ac6454SAndrew Thompson if (err) { 86802ac6454SAndrew Thompson DPRINTFN(2, "read open failed\n"); 869a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 870ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 87102ac6454SAndrew Thompson return (err); 87202ac6454SAndrew Thompson } 87302ac6454SAndrew Thompson } 87402ac6454SAndrew Thompson if (fflags & FWRITE) { 875a593f6b8SAndrew Thompson err = usb_fifo_open(cpd, refs.txfifo, fflags); 87602ac6454SAndrew Thompson if (err) { 87702ac6454SAndrew Thompson DPRINTFN(2, "write open failed\n"); 87802ac6454SAndrew Thompson if (fflags & FREAD) { 879a593f6b8SAndrew Thompson usb_fifo_close(refs.rxfifo, fflags); 88002ac6454SAndrew Thompson } 881a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 882ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 88302ac6454SAndrew Thompson return (err); 88402ac6454SAndrew Thompson } 88502ac6454SAndrew Thompson } 886a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 887a593f6b8SAndrew Thompson devfs_set_cdevpriv(cpd, usb_close); 88802ac6454SAndrew Thompson 889ee3e3ff5SAndrew Thompson return (0); 89002ac6454SAndrew Thompson } 89102ac6454SAndrew Thompson 89202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 893a593f6b8SAndrew Thompson * usb_close - cdev callback 89402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 89502ac6454SAndrew Thompson static void 896a593f6b8SAndrew Thompson usb_close(void *arg) 89702ac6454SAndrew Thompson { 898e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 899760bc48eSAndrew Thompson struct usb_cdev_privdata *cpd = arg; 900ee3e3ff5SAndrew Thompson int err; 90102ac6454SAndrew Thompson 9027214348fSAndrew Thompson DPRINTFN(2, "cpd=%p\n", cpd); 903ee3e3ff5SAndrew Thompson 904a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 1); 905ee3e3ff5SAndrew Thompson if (err) { 906ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 90702ac6454SAndrew Thompson return; 90802ac6454SAndrew Thompson } 909ee3e3ff5SAndrew Thompson if (cpd->fflags & FREAD) { 910a593f6b8SAndrew Thompson usb_fifo_close(refs.rxfifo, cpd->fflags); 91102ac6454SAndrew Thompson } 912ee3e3ff5SAndrew Thompson if (cpd->fflags & FWRITE) { 913a593f6b8SAndrew Thompson usb_fifo_close(refs.txfifo, cpd->fflags); 91402ac6454SAndrew Thompson } 915ee3e3ff5SAndrew Thompson 916a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 917ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 91802ac6454SAndrew Thompson return; 91902ac6454SAndrew Thompson } 92002ac6454SAndrew Thompson 92102ac6454SAndrew Thompson static void 922a593f6b8SAndrew Thompson usb_dev_init(void *arg) 92302ac6454SAndrew Thompson { 924a593f6b8SAndrew Thompson mtx_init(&usb_ref_lock, "USB ref mutex", NULL, MTX_DEF); 925a593f6b8SAndrew Thompson sx_init(&usb_sym_lock, "USB sym mutex"); 926a593f6b8SAndrew Thompson TAILQ_INIT(&usb_sym_head); 92702ac6454SAndrew Thompson 92802ac6454SAndrew Thompson /* check the UGEN methods */ 929a593f6b8SAndrew Thompson usb_fifo_check_methods(&usb_ugen_methods); 93002ac6454SAndrew Thompson } 93102ac6454SAndrew Thompson 932a593f6b8SAndrew Thompson SYSINIT(usb_dev_init, SI_SUB_KLD, SI_ORDER_FIRST, usb_dev_init, NULL); 93302ac6454SAndrew Thompson 93402ac6454SAndrew Thompson static void 935a593f6b8SAndrew Thompson usb_dev_init_post(void *arg) 93602ac6454SAndrew Thompson { 93702ac6454SAndrew Thompson /* 938ee3e3ff5SAndrew Thompson * Create /dev/usb - this is needed for usbconfig(8), which 939ee3e3ff5SAndrew Thompson * needs a well-known device name to access. 94002ac6454SAndrew Thompson */ 941a593f6b8SAndrew Thompson usb_dev = make_dev(&usb_static_devsw, 0, UID_ROOT, GID_OPERATOR, 942ee3e3ff5SAndrew Thompson 0644, USB_DEVICE_NAME); 943a593f6b8SAndrew Thompson if (usb_dev == NULL) { 94402ac6454SAndrew Thompson DPRINTFN(0, "Could not create usb bus device!\n"); 94502ac6454SAndrew Thompson } 94602ac6454SAndrew Thompson } 94702ac6454SAndrew Thompson 948a593f6b8SAndrew Thompson SYSINIT(usb_dev_init_post, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, usb_dev_init_post, NULL); 94902ac6454SAndrew Thompson 95002ac6454SAndrew Thompson static void 951a593f6b8SAndrew Thompson usb_dev_uninit(void *arg) 95202ac6454SAndrew Thompson { 953a593f6b8SAndrew Thompson if (usb_dev != NULL) { 954a593f6b8SAndrew Thompson destroy_dev(usb_dev); 955a593f6b8SAndrew Thompson usb_dev = NULL; 956ee3e3ff5SAndrew Thompson 95702ac6454SAndrew Thompson } 958a593f6b8SAndrew Thompson mtx_destroy(&usb_ref_lock); 959a593f6b8SAndrew Thompson sx_destroy(&usb_sym_lock); 96002ac6454SAndrew Thompson } 96102ac6454SAndrew Thompson 962a593f6b8SAndrew Thompson SYSUNINIT(usb_dev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb_dev_uninit, NULL); 96302ac6454SAndrew Thompson 96402ac6454SAndrew Thompson static int 965a593f6b8SAndrew Thompson usb_ioctl_f_sub(struct usb_fifo *f, u_long cmd, void *addr, 96602ac6454SAndrew Thompson struct thread *td) 96702ac6454SAndrew Thompson { 96802ac6454SAndrew Thompson int error = 0; 96902ac6454SAndrew Thompson 97002ac6454SAndrew Thompson switch (cmd) { 97102ac6454SAndrew Thompson case FIODTYPE: 97202ac6454SAndrew Thompson *(int *)addr = 0; /* character device */ 97302ac6454SAndrew Thompson break; 97402ac6454SAndrew Thompson 97502ac6454SAndrew Thompson case FIONBIO: 97602ac6454SAndrew Thompson /* handled by upper FS layer */ 97702ac6454SAndrew Thompson break; 97802ac6454SAndrew Thompson 97902ac6454SAndrew Thompson case FIOASYNC: 98002ac6454SAndrew Thompson if (*(int *)addr) { 98102ac6454SAndrew Thompson if (f->async_p != NULL) { 98202ac6454SAndrew Thompson error = EBUSY; 98302ac6454SAndrew Thompson break; 98402ac6454SAndrew Thompson } 98502ac6454SAndrew Thompson f->async_p = USB_TD_GET_PROC(td); 98602ac6454SAndrew Thompson } else { 98702ac6454SAndrew Thompson f->async_p = NULL; 98802ac6454SAndrew Thompson } 98902ac6454SAndrew Thompson break; 99002ac6454SAndrew Thompson 99102ac6454SAndrew Thompson /* XXX this is not the most general solution */ 99202ac6454SAndrew Thompson case TIOCSPGRP: 99302ac6454SAndrew Thompson if (f->async_p == NULL) { 99402ac6454SAndrew Thompson error = EINVAL; 99502ac6454SAndrew Thompson break; 99602ac6454SAndrew Thompson } 99702ac6454SAndrew Thompson if (*(int *)addr != USB_PROC_GET_GID(f->async_p)) { 99802ac6454SAndrew Thompson error = EPERM; 99902ac6454SAndrew Thompson break; 100002ac6454SAndrew Thompson } 100102ac6454SAndrew Thompson break; 100202ac6454SAndrew Thompson default: 100302ac6454SAndrew Thompson return (ENOIOCTL); 100402ac6454SAndrew Thompson } 1005cba30496SAndrew Thompson DPRINTFN(3, "cmd 0x%lx = %d\n", cmd, error); 100602ac6454SAndrew Thompson return (error); 100702ac6454SAndrew Thompson } 100802ac6454SAndrew Thompson 1009ee3e3ff5SAndrew Thompson /*------------------------------------------------------------------------* 1010a593f6b8SAndrew Thompson * usb_ioctl - cdev callback 1011ee3e3ff5SAndrew Thompson *------------------------------------------------------------------------*/ 101202ac6454SAndrew Thompson static int 1013a593f6b8SAndrew Thompson usb_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int fflag, struct thread* td) 101402ac6454SAndrew Thompson { 1015e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1016760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1017760bc48eSAndrew Thompson struct usb_fifo *f; 101802ac6454SAndrew Thompson int fflags; 101902ac6454SAndrew Thompson int err; 102002ac6454SAndrew Thompson 1021cba30496SAndrew Thompson DPRINTFN(2, "cmd=0x%lx\n", cmd); 1022cba30496SAndrew Thompson 1023ee3e3ff5SAndrew Thompson err = devfs_get_cdevpriv((void **)&cpd); 1024ee3e3ff5SAndrew Thompson if (err != 0) 1025ee3e3ff5SAndrew Thompson return (err); 1026ee3e3ff5SAndrew Thompson 1027f5f145baSAndrew Thompson /* 1028e13e19faSAndrew Thompson * Performance optimisation: We try to check for IOCTL's that 1029f5f145baSAndrew Thompson * don't need the USB reference first. Then we grab the USB 1030f5f145baSAndrew Thompson * reference if we need it! 1031f5f145baSAndrew Thompson */ 1032a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 103302ac6454SAndrew Thompson if (err) { 103402ac6454SAndrew Thompson return (ENXIO); 103502ac6454SAndrew Thompson } 1036ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 103702ac6454SAndrew Thompson 103802ac6454SAndrew Thompson f = NULL; /* set default value */ 103902ac6454SAndrew Thompson err = ENOIOCTL; /* set default value */ 104002ac6454SAndrew Thompson 104102ac6454SAndrew Thompson if (fflags & FWRITE) { 1042e13e19faSAndrew Thompson f = refs.txfifo; 1043a593f6b8SAndrew Thompson err = usb_ioctl_f_sub(f, cmd, addr, td); 104402ac6454SAndrew Thompson } 104502ac6454SAndrew Thompson if (fflags & FREAD) { 1046e13e19faSAndrew Thompson f = refs.rxfifo; 1047a593f6b8SAndrew Thompson err = usb_ioctl_f_sub(f, cmd, addr, td); 104802ac6454SAndrew Thompson } 1049ee3e3ff5SAndrew Thompson KASSERT(f != NULL, ("fifo not found")); 105002ac6454SAndrew Thompson if (err == ENOIOCTL) { 1051ee3e3ff5SAndrew Thompson err = (f->methods->f_ioctl) (f, cmd, addr, fflags); 1052cba30496SAndrew Thompson DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err); 105302ac6454SAndrew Thompson if (err == ENOIOCTL) { 1054a593f6b8SAndrew Thompson if (usb_usb_ref_device(cpd, &refs)) { 105502ac6454SAndrew Thompson err = ENXIO; 105602ac6454SAndrew Thompson goto done; 105702ac6454SAndrew Thompson } 1058ee3e3ff5SAndrew Thompson err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags); 1059cba30496SAndrew Thompson DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err); 106002ac6454SAndrew Thompson } 106102ac6454SAndrew Thompson } 106202ac6454SAndrew Thompson if (err == ENOIOCTL) { 106302ac6454SAndrew Thompson err = ENOTTY; 106402ac6454SAndrew Thompson } 106502ac6454SAndrew Thompson done: 1066a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 106702ac6454SAndrew Thompson return (err); 106802ac6454SAndrew Thompson } 106902ac6454SAndrew Thompson 107002ac6454SAndrew Thompson /* ARGSUSED */ 107102ac6454SAndrew Thompson static int 1072a593f6b8SAndrew Thompson usb_poll(struct cdev* dev, int events, struct thread* td) 107302ac6454SAndrew Thompson { 1074e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1075760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1076760bc48eSAndrew Thompson struct usb_fifo *f; 1077760bc48eSAndrew Thompson struct usb_mbuf *m; 10788a93629eSAndrew Thompson int fflags, revents; 107902ac6454SAndrew Thompson 10808a93629eSAndrew Thompson if (devfs_get_cdevpriv((void **)&cpd) != 0 || 1081a593f6b8SAndrew Thompson usb_ref_device(cpd, &refs, 0) != 0) 10828a93629eSAndrew Thompson return (events & 10838a93629eSAndrew Thompson (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); 1084ee3e3ff5SAndrew Thompson 1085ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 108602ac6454SAndrew Thompson 108702ac6454SAndrew Thompson /* Figure out who needs service */ 1088ee3e3ff5SAndrew Thompson revents = 0; 108902ac6454SAndrew Thompson if ((events & (POLLOUT | POLLWRNORM)) && 109002ac6454SAndrew Thompson (fflags & FWRITE)) { 109102ac6454SAndrew Thompson 1092e13e19faSAndrew Thompson f = refs.txfifo; 109302ac6454SAndrew Thompson 109402ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 109502ac6454SAndrew Thompson 1096e13e19faSAndrew Thompson if (!refs.is_usbfs) { 109702ac6454SAndrew Thompson if (f->flag_iserror) { 109802ac6454SAndrew Thompson /* we got an error */ 109902ac6454SAndrew Thompson m = (void *)1; 110002ac6454SAndrew Thompson } else { 110102ac6454SAndrew Thompson if (f->queue_data == NULL) { 110202ac6454SAndrew Thompson /* 110302ac6454SAndrew Thompson * start write transfer, if not 110402ac6454SAndrew Thompson * already started 110502ac6454SAndrew Thompson */ 110602ac6454SAndrew Thompson (f->methods->f_start_write) (f); 110702ac6454SAndrew Thompson } 110802ac6454SAndrew Thompson /* check if any packets are available */ 110902ac6454SAndrew Thompson USB_IF_POLL(&f->free_q, m); 111002ac6454SAndrew Thompson } 111102ac6454SAndrew Thompson } else { 111202ac6454SAndrew Thompson if (f->flag_iscomplete) { 111302ac6454SAndrew Thompson m = (void *)1; 111402ac6454SAndrew Thompson } else { 111502ac6454SAndrew Thompson m = NULL; 111602ac6454SAndrew Thompson } 111702ac6454SAndrew Thompson } 111802ac6454SAndrew Thompson 111902ac6454SAndrew Thompson if (m) { 112002ac6454SAndrew Thompson revents |= events & (POLLOUT | POLLWRNORM); 112102ac6454SAndrew Thompson } else { 112202ac6454SAndrew Thompson f->flag_isselect = 1; 112302ac6454SAndrew Thompson selrecord(td, &f->selinfo); 112402ac6454SAndrew Thompson } 112502ac6454SAndrew Thompson 112602ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 112702ac6454SAndrew Thompson } 112802ac6454SAndrew Thompson if ((events & (POLLIN | POLLRDNORM)) && 112902ac6454SAndrew Thompson (fflags & FREAD)) { 113002ac6454SAndrew Thompson 1131e13e19faSAndrew Thompson f = refs.rxfifo; 113202ac6454SAndrew Thompson 113302ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 113402ac6454SAndrew Thompson 1135e13e19faSAndrew Thompson if (!refs.is_usbfs) { 113602ac6454SAndrew Thompson if (f->flag_iserror) { 113702ac6454SAndrew Thompson /* we have and error */ 113802ac6454SAndrew Thompson m = (void *)1; 113902ac6454SAndrew Thompson } else { 114002ac6454SAndrew Thompson if (f->queue_data == NULL) { 114102ac6454SAndrew Thompson /* 114202ac6454SAndrew Thompson * start read transfer, if not 114302ac6454SAndrew Thompson * already started 114402ac6454SAndrew Thompson */ 114502ac6454SAndrew Thompson (f->methods->f_start_read) (f); 114602ac6454SAndrew Thompson } 114702ac6454SAndrew Thompson /* check if any packets are available */ 114802ac6454SAndrew Thompson USB_IF_POLL(&f->used_q, m); 114902ac6454SAndrew Thompson } 115002ac6454SAndrew Thompson } else { 115102ac6454SAndrew Thompson if (f->flag_iscomplete) { 115202ac6454SAndrew Thompson m = (void *)1; 115302ac6454SAndrew Thompson } else { 115402ac6454SAndrew Thompson m = NULL; 115502ac6454SAndrew Thompson } 115602ac6454SAndrew Thompson } 115702ac6454SAndrew Thompson 115802ac6454SAndrew Thompson if (m) { 115902ac6454SAndrew Thompson revents |= events & (POLLIN | POLLRDNORM); 116002ac6454SAndrew Thompson } else { 116102ac6454SAndrew Thompson f->flag_isselect = 1; 116202ac6454SAndrew Thompson selrecord(td, &f->selinfo); 116302ac6454SAndrew Thompson 1164e13e19faSAndrew Thompson if (!refs.is_usbfs) { 116502ac6454SAndrew Thompson /* start reading data */ 116602ac6454SAndrew Thompson (f->methods->f_start_read) (f); 116702ac6454SAndrew Thompson } 116802ac6454SAndrew Thompson } 116902ac6454SAndrew Thompson 117002ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 117102ac6454SAndrew Thompson } 1172a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 117302ac6454SAndrew Thompson return (revents); 117402ac6454SAndrew Thompson } 117502ac6454SAndrew Thompson 117602ac6454SAndrew Thompson static int 1177a593f6b8SAndrew Thompson usb_read(struct cdev *dev, struct uio *uio, int ioflag) 117802ac6454SAndrew Thompson { 1179e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1180760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1181760bc48eSAndrew Thompson struct usb_fifo *f; 1182760bc48eSAndrew Thompson struct usb_mbuf *m; 118302ac6454SAndrew Thompson int fflags; 118402ac6454SAndrew Thompson int resid; 118502ac6454SAndrew Thompson int io_len; 118602ac6454SAndrew Thompson int err; 118702ac6454SAndrew Thompson uint8_t tr_data = 0; 118802ac6454SAndrew Thompson 1189ee3e3ff5SAndrew Thompson err = devfs_get_cdevpriv((void **)&cpd); 1190ee3e3ff5SAndrew Thompson if (err != 0) 1191ee3e3ff5SAndrew Thompson return (err); 119202ac6454SAndrew Thompson 1193a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 119402ac6454SAndrew Thompson if (err) { 119502ac6454SAndrew Thompson return (ENXIO); 119602ac6454SAndrew Thompson } 1197ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 1198ee3e3ff5SAndrew Thompson 1199e13e19faSAndrew Thompson f = refs.rxfifo; 120002ac6454SAndrew Thompson if (f == NULL) { 120102ac6454SAndrew Thompson /* should not happen */ 1202a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 120302ac6454SAndrew Thompson return (EPERM); 120402ac6454SAndrew Thompson } 120502ac6454SAndrew Thompson 1206ee3e3ff5SAndrew Thompson resid = uio->uio_resid; 120702ac6454SAndrew Thompson 120802ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 120902ac6454SAndrew Thompson 121002ac6454SAndrew Thompson /* check for permanent read error */ 121102ac6454SAndrew Thompson if (f->flag_iserror) { 121202ac6454SAndrew Thompson err = EIO; 121302ac6454SAndrew Thompson goto done; 121402ac6454SAndrew Thompson } 121502ac6454SAndrew Thompson /* check if USB-FS interface is active */ 1216e13e19faSAndrew Thompson if (refs.is_usbfs) { 121702ac6454SAndrew Thompson /* 121802ac6454SAndrew Thompson * The queue is used for events that should be 121902ac6454SAndrew Thompson * retrieved using the "USB_FS_COMPLETE" ioctl. 122002ac6454SAndrew Thompson */ 122102ac6454SAndrew Thompson err = EINVAL; 122202ac6454SAndrew Thompson goto done; 122302ac6454SAndrew Thompson } 122402ac6454SAndrew Thompson while (uio->uio_resid > 0) { 122502ac6454SAndrew Thompson 122602ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 122702ac6454SAndrew Thompson 122802ac6454SAndrew Thompson if (m == NULL) { 122902ac6454SAndrew Thompson 123002ac6454SAndrew Thompson /* start read transfer, if not already started */ 123102ac6454SAndrew Thompson 123202ac6454SAndrew Thompson (f->methods->f_start_read) (f); 123302ac6454SAndrew Thompson 123407532e49SAndrew Thompson if (ioflag & IO_NDELAY) { 123502ac6454SAndrew Thompson if (tr_data) { 123602ac6454SAndrew Thompson /* return length before error */ 123702ac6454SAndrew Thompson break; 123802ac6454SAndrew Thompson } 123902ac6454SAndrew Thompson err = EWOULDBLOCK; 124002ac6454SAndrew Thompson break; 124102ac6454SAndrew Thompson } 124202ac6454SAndrew Thompson DPRINTF("sleeping\n"); 124302ac6454SAndrew Thompson 1244a593f6b8SAndrew Thompson err = usb_fifo_wait(f); 124502ac6454SAndrew Thompson if (err) { 124602ac6454SAndrew Thompson break; 124702ac6454SAndrew Thompson } 124802ac6454SAndrew Thompson continue; 124902ac6454SAndrew Thompson } 125002ac6454SAndrew Thompson if (f->methods->f_filter_read) { 125102ac6454SAndrew Thompson /* 125202ac6454SAndrew Thompson * Sometimes it is convenient to process data at the 125302ac6454SAndrew Thompson * expense of a userland process instead of a kernel 125402ac6454SAndrew Thompson * process. 125502ac6454SAndrew Thompson */ 125602ac6454SAndrew Thompson (f->methods->f_filter_read) (f, m); 125702ac6454SAndrew Thompson } 125802ac6454SAndrew Thompson tr_data = 1; 125902ac6454SAndrew Thompson 126002ac6454SAndrew Thompson io_len = MIN(m->cur_data_len, uio->uio_resid); 126102ac6454SAndrew Thompson 126202ac6454SAndrew Thompson DPRINTFN(2, "transfer %d bytes from %p\n", 126302ac6454SAndrew Thompson io_len, m->cur_data_ptr); 126402ac6454SAndrew Thompson 1265a593f6b8SAndrew Thompson err = usb_fifo_uiomove(f, 126602ac6454SAndrew Thompson m->cur_data_ptr, io_len, uio); 126702ac6454SAndrew Thompson 126802ac6454SAndrew Thompson m->cur_data_len -= io_len; 126902ac6454SAndrew Thompson m->cur_data_ptr += io_len; 127002ac6454SAndrew Thompson 127102ac6454SAndrew Thompson if (m->cur_data_len == 0) { 127202ac6454SAndrew Thompson 127302ac6454SAndrew Thompson uint8_t last_packet; 127402ac6454SAndrew Thompson 127502ac6454SAndrew Thompson last_packet = m->last_packet; 127602ac6454SAndrew Thompson 127702ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 127802ac6454SAndrew Thompson 127902ac6454SAndrew Thompson if (last_packet) { 128002ac6454SAndrew Thompson /* keep framing */ 128102ac6454SAndrew Thompson break; 128202ac6454SAndrew Thompson } 128302ac6454SAndrew Thompson } else { 128402ac6454SAndrew Thompson USB_IF_PREPEND(&f->used_q, m); 128502ac6454SAndrew Thompson } 128602ac6454SAndrew Thompson 128702ac6454SAndrew Thompson if (err) { 128802ac6454SAndrew Thompson break; 128902ac6454SAndrew Thompson } 129002ac6454SAndrew Thompson } 129102ac6454SAndrew Thompson done: 129202ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 129302ac6454SAndrew Thompson 1294a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 129502ac6454SAndrew Thompson 129602ac6454SAndrew Thompson return (err); 129702ac6454SAndrew Thompson } 129802ac6454SAndrew Thompson 129902ac6454SAndrew Thompson static int 1300a593f6b8SAndrew Thompson usb_write(struct cdev *dev, struct uio *uio, int ioflag) 130102ac6454SAndrew Thompson { 1302e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1303760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1304760bc48eSAndrew Thompson struct usb_fifo *f; 1305760bc48eSAndrew Thompson struct usb_mbuf *m; 130602ac6454SAndrew Thompson int fflags; 130702ac6454SAndrew Thompson int resid; 130802ac6454SAndrew Thompson int io_len; 130902ac6454SAndrew Thompson int err; 131002ac6454SAndrew Thompson uint8_t tr_data = 0; 131102ac6454SAndrew Thompson 131202ac6454SAndrew Thompson DPRINTFN(2, "\n"); 131302ac6454SAndrew Thompson 1314ee3e3ff5SAndrew Thompson err = devfs_get_cdevpriv((void **)&cpd); 1315ee3e3ff5SAndrew Thompson if (err != 0) 1316ee3e3ff5SAndrew Thompson return (err); 131702ac6454SAndrew Thompson 1318a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 131902ac6454SAndrew Thompson if (err) { 132002ac6454SAndrew Thompson return (ENXIO); 132102ac6454SAndrew Thompson } 1322ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 1323ee3e3ff5SAndrew Thompson 1324e13e19faSAndrew Thompson f = refs.txfifo; 132502ac6454SAndrew Thompson if (f == NULL) { 132602ac6454SAndrew Thompson /* should not happen */ 1327a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 132802ac6454SAndrew Thompson return (EPERM); 132902ac6454SAndrew Thompson } 133002ac6454SAndrew Thompson resid = uio->uio_resid; 133102ac6454SAndrew Thompson 133202ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 133302ac6454SAndrew Thompson 133402ac6454SAndrew Thompson /* check for permanent write error */ 133502ac6454SAndrew Thompson if (f->flag_iserror) { 133602ac6454SAndrew Thompson err = EIO; 133702ac6454SAndrew Thompson goto done; 133802ac6454SAndrew Thompson } 133902ac6454SAndrew Thompson /* check if USB-FS interface is active */ 1340e13e19faSAndrew Thompson if (refs.is_usbfs) { 134102ac6454SAndrew Thompson /* 134202ac6454SAndrew Thompson * The queue is used for events that should be 134302ac6454SAndrew Thompson * retrieved using the "USB_FS_COMPLETE" ioctl. 134402ac6454SAndrew Thompson */ 134502ac6454SAndrew Thompson err = EINVAL; 134602ac6454SAndrew Thompson goto done; 134702ac6454SAndrew Thompson } 134802ac6454SAndrew Thompson if (f->queue_data == NULL) { 134902ac6454SAndrew Thompson /* start write transfer, if not already started */ 135002ac6454SAndrew Thompson (f->methods->f_start_write) (f); 135102ac6454SAndrew Thompson } 135202ac6454SAndrew Thompson /* we allow writing zero length data */ 135302ac6454SAndrew Thompson do { 135402ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 135502ac6454SAndrew Thompson 135602ac6454SAndrew Thompson if (m == NULL) { 135702ac6454SAndrew Thompson 135807532e49SAndrew Thompson if (ioflag & IO_NDELAY) { 135902ac6454SAndrew Thompson if (tr_data) { 136002ac6454SAndrew Thompson /* return length before error */ 136102ac6454SAndrew Thompson break; 136202ac6454SAndrew Thompson } 136302ac6454SAndrew Thompson err = EWOULDBLOCK; 136402ac6454SAndrew Thompson break; 136502ac6454SAndrew Thompson } 136602ac6454SAndrew Thompson DPRINTF("sleeping\n"); 136702ac6454SAndrew Thompson 1368a593f6b8SAndrew Thompson err = usb_fifo_wait(f); 136902ac6454SAndrew Thompson if (err) { 137002ac6454SAndrew Thompson break; 137102ac6454SAndrew Thompson } 137202ac6454SAndrew Thompson continue; 137302ac6454SAndrew Thompson } 137402ac6454SAndrew Thompson tr_data = 1; 137502ac6454SAndrew Thompson 137602ac6454SAndrew Thompson USB_MBUF_RESET(m); 137702ac6454SAndrew Thompson 137802ac6454SAndrew Thompson io_len = MIN(m->cur_data_len, uio->uio_resid); 137902ac6454SAndrew Thompson 138002ac6454SAndrew Thompson m->cur_data_len = io_len; 138102ac6454SAndrew Thompson 138202ac6454SAndrew Thompson DPRINTFN(2, "transfer %d bytes to %p\n", 138302ac6454SAndrew Thompson io_len, m->cur_data_ptr); 138402ac6454SAndrew Thompson 1385a593f6b8SAndrew Thompson err = usb_fifo_uiomove(f, 138602ac6454SAndrew Thompson m->cur_data_ptr, io_len, uio); 138702ac6454SAndrew Thompson 138802ac6454SAndrew Thompson if (err) { 138902ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 139002ac6454SAndrew Thompson break; 139102ac6454SAndrew Thompson } 139202ac6454SAndrew Thompson if (f->methods->f_filter_write) { 139302ac6454SAndrew Thompson /* 139402ac6454SAndrew Thompson * Sometimes it is convenient to process data at the 139502ac6454SAndrew Thompson * expense of a userland process instead of a kernel 139602ac6454SAndrew Thompson * process. 139702ac6454SAndrew Thompson */ 139802ac6454SAndrew Thompson (f->methods->f_filter_write) (f, m); 139902ac6454SAndrew Thompson } 140002ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 140102ac6454SAndrew Thompson 140202ac6454SAndrew Thompson (f->methods->f_start_write) (f); 140302ac6454SAndrew Thompson 140402ac6454SAndrew Thompson } while (uio->uio_resid > 0); 140502ac6454SAndrew Thompson done: 140602ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 140702ac6454SAndrew Thompson 1408a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 140902ac6454SAndrew Thompson 1410ee3e3ff5SAndrew Thompson return (err); 1411ee3e3ff5SAndrew Thompson } 141202ac6454SAndrew Thompson 1413ee3e3ff5SAndrew Thompson int 1414a593f6b8SAndrew Thompson usb_static_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 1415ee3e3ff5SAndrew Thompson struct thread *td) 1416ee3e3ff5SAndrew Thompson { 1417ee3e3ff5SAndrew Thompson union { 1418760bc48eSAndrew Thompson struct usb_read_dir *urd; 1419ee3e3ff5SAndrew Thompson void* data; 1420ee3e3ff5SAndrew Thompson } u; 1421ee3e3ff5SAndrew Thompson int err = ENOTTY; 1422ee3e3ff5SAndrew Thompson 1423ee3e3ff5SAndrew Thompson u.data = data; 1424ee3e3ff5SAndrew Thompson switch (cmd) { 1425ee3e3ff5SAndrew Thompson case USB_READ_DIR: 1426a593f6b8SAndrew Thompson err = usb_read_symlink(u.urd->urd_data, 1427ee3e3ff5SAndrew Thompson u.urd->urd_startentry, u.urd->urd_maxlen); 1428ee3e3ff5SAndrew Thompson break; 1429ee3e3ff5SAndrew Thompson case USB_DEV_QUIRK_GET: 1430ee3e3ff5SAndrew Thompson case USB_QUIRK_NAME_GET: 1431ee3e3ff5SAndrew Thompson case USB_DEV_QUIRK_ADD: 1432ee3e3ff5SAndrew Thompson case USB_DEV_QUIRK_REMOVE: 1433a593f6b8SAndrew Thompson err = usb_quirk_ioctl_p(cmd, data, fflag, td); 1434ee3e3ff5SAndrew Thompson break; 1435ee3e3ff5SAndrew Thompson case USB_GET_TEMPLATE: 1436a593f6b8SAndrew Thompson *(int *)data = usb_template; 1437ee3e3ff5SAndrew Thompson break; 1438ee3e3ff5SAndrew Thompson case USB_SET_TEMPLATE: 143950230f98SAndrew Thompson err = priv_check(curthread, PRIV_DRIVER); 1440ee3e3ff5SAndrew Thompson if (err) 1441ee3e3ff5SAndrew Thompson break; 1442a593f6b8SAndrew Thompson usb_template = *(int *)data; 1443ee3e3ff5SAndrew Thompson break; 1444ee3e3ff5SAndrew Thompson } 144502ac6454SAndrew Thompson return (err); 144602ac6454SAndrew Thompson } 144702ac6454SAndrew Thompson 144802ac6454SAndrew Thompson static int 1449a593f6b8SAndrew Thompson usb_fifo_uiomove(struct usb_fifo *f, void *cp, 145002ac6454SAndrew Thompson int n, struct uio *uio) 145102ac6454SAndrew Thompson { 145202ac6454SAndrew Thompson int error; 145302ac6454SAndrew Thompson 145402ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 145502ac6454SAndrew Thompson 145602ac6454SAndrew Thompson /* 145702ac6454SAndrew Thompson * "uiomove()" can sleep so one needs to make a wrapper, 145802ac6454SAndrew Thompson * exiting the mutex and checking things: 145902ac6454SAndrew Thompson */ 146002ac6454SAndrew Thompson error = uiomove(cp, n, uio); 146102ac6454SAndrew Thompson 146202ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 146302ac6454SAndrew Thompson 146402ac6454SAndrew Thompson return (error); 146502ac6454SAndrew Thompson } 146602ac6454SAndrew Thompson 146702ac6454SAndrew Thompson int 1468a593f6b8SAndrew Thompson usb_fifo_wait(struct usb_fifo *f) 146902ac6454SAndrew Thompson { 147002ac6454SAndrew Thompson int err; 147102ac6454SAndrew Thompson 147202ac6454SAndrew Thompson mtx_assert(f->priv_mtx, MA_OWNED); 147302ac6454SAndrew Thompson 147402ac6454SAndrew Thompson if (f->flag_iserror) { 147502ac6454SAndrew Thompson /* we are gone */ 147602ac6454SAndrew Thompson return (EIO); 147702ac6454SAndrew Thompson } 147802ac6454SAndrew Thompson f->flag_sleeping = 1; 147902ac6454SAndrew Thompson 14808437751dSAndrew Thompson err = cv_wait_sig(&f->cv_io, f->priv_mtx); 148102ac6454SAndrew Thompson 148202ac6454SAndrew Thompson if (f->flag_iserror) { 148302ac6454SAndrew Thompson /* we are gone */ 148402ac6454SAndrew Thompson err = EIO; 148502ac6454SAndrew Thompson } 148602ac6454SAndrew Thompson return (err); 148702ac6454SAndrew Thompson } 148802ac6454SAndrew Thompson 148902ac6454SAndrew Thompson void 1490a593f6b8SAndrew Thompson usb_fifo_signal(struct usb_fifo *f) 149102ac6454SAndrew Thompson { 149202ac6454SAndrew Thompson if (f->flag_sleeping) { 149302ac6454SAndrew Thompson f->flag_sleeping = 0; 14948437751dSAndrew Thompson cv_broadcast(&f->cv_io); 149502ac6454SAndrew Thompson } 149602ac6454SAndrew Thompson } 149702ac6454SAndrew Thompson 149802ac6454SAndrew Thompson void 1499a593f6b8SAndrew Thompson usb_fifo_wakeup(struct usb_fifo *f) 150002ac6454SAndrew Thompson { 1501a593f6b8SAndrew Thompson usb_fifo_signal(f); 150202ac6454SAndrew Thompson 150302ac6454SAndrew Thompson if (f->flag_isselect) { 150402ac6454SAndrew Thompson selwakeup(&f->selinfo); 150502ac6454SAndrew Thompson f->flag_isselect = 0; 150602ac6454SAndrew Thompson } 150702ac6454SAndrew Thompson if (f->async_p != NULL) { 150802ac6454SAndrew Thompson PROC_LOCK(f->async_p); 150902ac6454SAndrew Thompson psignal(f->async_p, SIGIO); 151002ac6454SAndrew Thompson PROC_UNLOCK(f->async_p); 151102ac6454SAndrew Thompson } 151202ac6454SAndrew Thompson } 151302ac6454SAndrew Thompson 151402ac6454SAndrew Thompson static int 1515a593f6b8SAndrew Thompson usb_fifo_dummy_open(struct usb_fifo *fifo, int fflags) 151602ac6454SAndrew Thompson { 151702ac6454SAndrew Thompson return (0); 151802ac6454SAndrew Thompson } 151902ac6454SAndrew Thompson 152002ac6454SAndrew Thompson static void 1521a593f6b8SAndrew Thompson usb_fifo_dummy_close(struct usb_fifo *fifo, int fflags) 152202ac6454SAndrew Thompson { 152302ac6454SAndrew Thompson return; 152402ac6454SAndrew Thompson } 152502ac6454SAndrew Thompson 152602ac6454SAndrew Thompson static int 1527a593f6b8SAndrew Thompson usb_fifo_dummy_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) 152802ac6454SAndrew Thompson { 152902ac6454SAndrew Thompson return (ENOIOCTL); 153002ac6454SAndrew Thompson } 153102ac6454SAndrew Thompson 153202ac6454SAndrew Thompson static void 1533a593f6b8SAndrew Thompson usb_fifo_dummy_cmd(struct usb_fifo *fifo) 153402ac6454SAndrew Thompson { 153502ac6454SAndrew Thompson fifo->flag_flushing = 0; /* not flushing */ 153602ac6454SAndrew Thompson } 153702ac6454SAndrew Thompson 153802ac6454SAndrew Thompson static void 1539a593f6b8SAndrew Thompson usb_fifo_check_methods(struct usb_fifo_methods *pm) 154002ac6454SAndrew Thompson { 154102ac6454SAndrew Thompson /* check that all callback functions are OK */ 154202ac6454SAndrew Thompson 154302ac6454SAndrew Thompson if (pm->f_open == NULL) 1544a593f6b8SAndrew Thompson pm->f_open = &usb_fifo_dummy_open; 154502ac6454SAndrew Thompson 154602ac6454SAndrew Thompson if (pm->f_close == NULL) 1547a593f6b8SAndrew Thompson pm->f_close = &usb_fifo_dummy_close; 154802ac6454SAndrew Thompson 154902ac6454SAndrew Thompson if (pm->f_ioctl == NULL) 1550a593f6b8SAndrew Thompson pm->f_ioctl = &usb_fifo_dummy_ioctl; 155102ac6454SAndrew Thompson 155202ac6454SAndrew Thompson if (pm->f_ioctl_post == NULL) 1553a593f6b8SAndrew Thompson pm->f_ioctl_post = &usb_fifo_dummy_ioctl; 155402ac6454SAndrew Thompson 155502ac6454SAndrew Thompson if (pm->f_start_read == NULL) 1556a593f6b8SAndrew Thompson pm->f_start_read = &usb_fifo_dummy_cmd; 155702ac6454SAndrew Thompson 155802ac6454SAndrew Thompson if (pm->f_stop_read == NULL) 1559a593f6b8SAndrew Thompson pm->f_stop_read = &usb_fifo_dummy_cmd; 156002ac6454SAndrew Thompson 156102ac6454SAndrew Thompson if (pm->f_start_write == NULL) 1562a593f6b8SAndrew Thompson pm->f_start_write = &usb_fifo_dummy_cmd; 156302ac6454SAndrew Thompson 156402ac6454SAndrew Thompson if (pm->f_stop_write == NULL) 1565a593f6b8SAndrew Thompson pm->f_stop_write = &usb_fifo_dummy_cmd; 156602ac6454SAndrew Thompson } 156702ac6454SAndrew Thompson 156802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1569a593f6b8SAndrew Thompson * usb_fifo_attach 157002ac6454SAndrew Thompson * 157102ac6454SAndrew Thompson * The following function will create a duplex FIFO. 157202ac6454SAndrew Thompson * 157302ac6454SAndrew Thompson * Return values: 157402ac6454SAndrew Thompson * 0: Success. 157502ac6454SAndrew Thompson * Else: Failure. 157602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 157702ac6454SAndrew Thompson int 1578a593f6b8SAndrew Thompson usb_fifo_attach(struct usb_device *udev, void *priv_sc, 1579760bc48eSAndrew Thompson struct mtx *priv_mtx, struct usb_fifo_methods *pm, 1580760bc48eSAndrew Thompson struct usb_fifo_sc *f_sc, uint16_t unit, uint16_t subunit, 1581ee3e3ff5SAndrew Thompson uint8_t iface_index, uid_t uid, gid_t gid, int mode) 158202ac6454SAndrew Thompson { 1583760bc48eSAndrew Thompson struct usb_fifo *f_tx; 1584760bc48eSAndrew Thompson struct usb_fifo *f_rx; 1585ee3e3ff5SAndrew Thompson char devname[32]; 158602ac6454SAndrew Thompson uint8_t n; 1587760bc48eSAndrew Thompson struct usb_fs_privdata* pd; 158802ac6454SAndrew Thompson 158902ac6454SAndrew Thompson f_sc->fp[USB_FIFO_TX] = NULL; 159002ac6454SAndrew Thompson f_sc->fp[USB_FIFO_RX] = NULL; 159102ac6454SAndrew Thompson 159202ac6454SAndrew Thompson if (pm == NULL) 159302ac6454SAndrew Thompson return (EINVAL); 159402ac6454SAndrew Thompson 159502ac6454SAndrew Thompson /* check the methods */ 1596a593f6b8SAndrew Thompson usb_fifo_check_methods(pm); 159702ac6454SAndrew Thompson 159802ac6454SAndrew Thompson if (priv_mtx == NULL) 159902ac6454SAndrew Thompson priv_mtx = &Giant; 160002ac6454SAndrew Thompson 160102ac6454SAndrew Thompson /* search for a free FIFO slot */ 160202ac6454SAndrew Thompson for (n = 0;; n += 2) { 160302ac6454SAndrew Thompson 160402ac6454SAndrew Thompson if (n == USB_FIFO_MAX) { 160502ac6454SAndrew Thompson /* end of FIFOs reached */ 160602ac6454SAndrew Thompson return (ENOMEM); 160702ac6454SAndrew Thompson } 160802ac6454SAndrew Thompson /* Check for TX FIFO */ 160902ac6454SAndrew Thompson if (udev->fifo[n + USB_FIFO_TX] != NULL) { 161002ac6454SAndrew Thompson continue; 161102ac6454SAndrew Thompson } 161202ac6454SAndrew Thompson /* Check for RX FIFO */ 161302ac6454SAndrew Thompson if (udev->fifo[n + USB_FIFO_RX] != NULL) { 161402ac6454SAndrew Thompson continue; 161502ac6454SAndrew Thompson } 161602ac6454SAndrew Thompson break; 161702ac6454SAndrew Thompson } 161802ac6454SAndrew Thompson 1619a593f6b8SAndrew Thompson f_tx = usb_fifo_alloc(); 1620a593f6b8SAndrew Thompson f_rx = usb_fifo_alloc(); 162102ac6454SAndrew Thompson 162202ac6454SAndrew Thompson if ((f_tx == NULL) || (f_rx == NULL)) { 1623a593f6b8SAndrew Thompson usb_fifo_free(f_tx); 1624a593f6b8SAndrew Thompson usb_fifo_free(f_rx); 162502ac6454SAndrew Thompson return (ENOMEM); 162602ac6454SAndrew Thompson } 162702ac6454SAndrew Thompson /* initialise FIFO structures */ 162802ac6454SAndrew Thompson 162902ac6454SAndrew Thompson f_tx->fifo_index = n + USB_FIFO_TX; 16302989a677SAndrew Thompson f_tx->dev_ep_index = -1; 163102ac6454SAndrew Thompson f_tx->priv_mtx = priv_mtx; 163202ac6454SAndrew Thompson f_tx->priv_sc0 = priv_sc; 163302ac6454SAndrew Thompson f_tx->methods = pm; 163402ac6454SAndrew Thompson f_tx->iface_index = iface_index; 163502ac6454SAndrew Thompson f_tx->udev = udev; 163602ac6454SAndrew Thompson 163702ac6454SAndrew Thompson f_rx->fifo_index = n + USB_FIFO_RX; 16382989a677SAndrew Thompson f_rx->dev_ep_index = -1; 163902ac6454SAndrew Thompson f_rx->priv_mtx = priv_mtx; 164002ac6454SAndrew Thompson f_rx->priv_sc0 = priv_sc; 164102ac6454SAndrew Thompson f_rx->methods = pm; 164202ac6454SAndrew Thompson f_rx->iface_index = iface_index; 164302ac6454SAndrew Thompson f_rx->udev = udev; 164402ac6454SAndrew Thompson 164502ac6454SAndrew Thompson f_sc->fp[USB_FIFO_TX] = f_tx; 164602ac6454SAndrew Thompson f_sc->fp[USB_FIFO_RX] = f_rx; 164702ac6454SAndrew Thompson 1648a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 164902ac6454SAndrew Thompson udev->fifo[f_tx->fifo_index] = f_tx; 165002ac6454SAndrew Thompson udev->fifo[f_rx->fifo_index] = f_rx; 1651a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 165202ac6454SAndrew Thompson 165302ac6454SAndrew Thompson for (n = 0; n != 4; n++) { 165402ac6454SAndrew Thompson 165502ac6454SAndrew Thompson if (pm->basename[n] == NULL) { 165602ac6454SAndrew Thompson continue; 165702ac6454SAndrew Thompson } 165802ac6454SAndrew Thompson if (subunit == 0xFFFF) { 1659ee3e3ff5SAndrew Thompson if (snprintf(devname, sizeof(devname), 166002ac6454SAndrew Thompson "%s%u%s", pm->basename[n], 166102ac6454SAndrew Thompson unit, pm->postfix[n] ? 166202ac6454SAndrew Thompson pm->postfix[n] : "")) { 166302ac6454SAndrew Thompson /* ignore */ 166402ac6454SAndrew Thompson } 166502ac6454SAndrew Thompson } else { 1666ee3e3ff5SAndrew Thompson if (snprintf(devname, sizeof(devname), 166702ac6454SAndrew Thompson "%s%u.%u%s", pm->basename[n], 166802ac6454SAndrew Thompson unit, subunit, pm->postfix[n] ? 166902ac6454SAndrew Thompson pm->postfix[n] : "")) { 167002ac6454SAndrew Thompson /* ignore */ 167102ac6454SAndrew Thompson } 167202ac6454SAndrew Thompson } 167302ac6454SAndrew Thompson 167402ac6454SAndrew Thompson /* 167502ac6454SAndrew Thompson * Distribute the symbolic links into two FIFO structures: 167602ac6454SAndrew Thompson */ 167702ac6454SAndrew Thompson if (n & 1) { 167802ac6454SAndrew Thompson f_rx->symlink[n / 2] = 1679a593f6b8SAndrew Thompson usb_alloc_symlink(devname); 168002ac6454SAndrew Thompson } else { 168102ac6454SAndrew Thompson f_tx->symlink[n / 2] = 1682a593f6b8SAndrew Thompson usb_alloc_symlink(devname); 168302ac6454SAndrew Thompson } 1684ee3e3ff5SAndrew Thompson 1685ee3e3ff5SAndrew Thompson /* 1686ee3e3ff5SAndrew Thompson * Initialize device private data - this is used to find the 1687ee3e3ff5SAndrew Thompson * actual USB device itself. 1688ee3e3ff5SAndrew Thompson */ 1689760bc48eSAndrew Thompson pd = malloc(sizeof(struct usb_fs_privdata), M_USBDEV, M_WAITOK | M_ZERO); 1690ee3e3ff5SAndrew Thompson pd->bus_index = device_get_unit(udev->bus->bdev); 1691ee3e3ff5SAndrew Thompson pd->dev_index = udev->device_index; 1692ee3e3ff5SAndrew Thompson pd->ep_addr = -1; /* not an endpoint */ 16937214348fSAndrew Thompson pd->fifo_index = f_tx->fifo_index & f_rx->fifo_index; 1694ee3e3ff5SAndrew Thompson pd->mode = FREAD|FWRITE; 1695ee3e3ff5SAndrew Thompson 1696ee3e3ff5SAndrew Thompson /* Now, create the device itself */ 1697a593f6b8SAndrew Thompson f_sc->dev = make_dev(&usb_devsw, 0, uid, gid, mode, 1698ee3e3ff5SAndrew Thompson devname); 16997214348fSAndrew Thompson /* XXX setting si_drv1 and creating the device is not atomic! */ 1700ee3e3ff5SAndrew Thompson f_sc->dev->si_drv1 = pd; 170102ac6454SAndrew Thompson } 170202ac6454SAndrew Thompson 170302ac6454SAndrew Thompson DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx); 170402ac6454SAndrew Thompson return (0); 170502ac6454SAndrew Thompson } 170602ac6454SAndrew Thompson 170702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1708a593f6b8SAndrew Thompson * usb_fifo_alloc_buffer 170902ac6454SAndrew Thompson * 171002ac6454SAndrew Thompson * Return values: 171102ac6454SAndrew Thompson * 0: Success 171202ac6454SAndrew Thompson * Else failure 171302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 171402ac6454SAndrew Thompson int 1715a593f6b8SAndrew Thompson usb_fifo_alloc_buffer(struct usb_fifo *f, usb_size_t bufsize, 171602ac6454SAndrew Thompson uint16_t nbuf) 171702ac6454SAndrew Thompson { 1718a593f6b8SAndrew Thompson usb_fifo_free_buffer(f); 171902ac6454SAndrew Thompson 172002ac6454SAndrew Thompson /* allocate an endpoint */ 172102ac6454SAndrew Thompson f->free_q.ifq_maxlen = nbuf; 172202ac6454SAndrew Thompson f->used_q.ifq_maxlen = nbuf; 172302ac6454SAndrew Thompson 1724a593f6b8SAndrew Thompson f->queue_data = usb_alloc_mbufs( 172502ac6454SAndrew Thompson M_USBDEV, &f->free_q, bufsize, nbuf); 172602ac6454SAndrew Thompson 172702ac6454SAndrew Thompson if ((f->queue_data == NULL) && bufsize && nbuf) { 172802ac6454SAndrew Thompson return (ENOMEM); 172902ac6454SAndrew Thompson } 173002ac6454SAndrew Thompson return (0); /* success */ 173102ac6454SAndrew Thompson } 173202ac6454SAndrew Thompson 173302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1734a593f6b8SAndrew Thompson * usb_fifo_free_buffer 173502ac6454SAndrew Thompson * 173602ac6454SAndrew Thompson * This function will free the buffers associated with a FIFO. This 173702ac6454SAndrew Thompson * function can be called multiple times in a row. 173802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 173902ac6454SAndrew Thompson void 1740a593f6b8SAndrew Thompson usb_fifo_free_buffer(struct usb_fifo *f) 174102ac6454SAndrew Thompson { 174202ac6454SAndrew Thompson if (f->queue_data) { 174302ac6454SAndrew Thompson /* free old buffer */ 174402ac6454SAndrew Thompson free(f->queue_data, M_USBDEV); 174502ac6454SAndrew Thompson f->queue_data = NULL; 174602ac6454SAndrew Thompson } 174702ac6454SAndrew Thompson /* reset queues */ 174802ac6454SAndrew Thompson 174902ac6454SAndrew Thompson bzero(&f->free_q, sizeof(f->free_q)); 175002ac6454SAndrew Thompson bzero(&f->used_q, sizeof(f->used_q)); 175102ac6454SAndrew Thompson } 175202ac6454SAndrew Thompson 1753ee3e3ff5SAndrew Thompson static void 1754a593f6b8SAndrew Thompson usb_fifo_cleanup(void* ptr) 1755ee3e3ff5SAndrew Thompson { 1756ee3e3ff5SAndrew Thompson free(ptr, M_USBDEV); 1757ee3e3ff5SAndrew Thompson } 1758ee3e3ff5SAndrew Thompson 175902ac6454SAndrew Thompson void 1760a593f6b8SAndrew Thompson usb_fifo_detach(struct usb_fifo_sc *f_sc) 176102ac6454SAndrew Thompson { 176202ac6454SAndrew Thompson if (f_sc == NULL) { 176302ac6454SAndrew Thompson return; 176402ac6454SAndrew Thompson } 1765a593f6b8SAndrew Thompson usb_fifo_free(f_sc->fp[USB_FIFO_TX]); 1766a593f6b8SAndrew Thompson usb_fifo_free(f_sc->fp[USB_FIFO_RX]); 176702ac6454SAndrew Thompson 176802ac6454SAndrew Thompson f_sc->fp[USB_FIFO_TX] = NULL; 176902ac6454SAndrew Thompson f_sc->fp[USB_FIFO_RX] = NULL; 177002ac6454SAndrew Thompson 1771ee3e3ff5SAndrew Thompson if (f_sc->dev != NULL) { 17727214348fSAndrew Thompson destroy_dev_sched_cb(f_sc->dev, 1773a593f6b8SAndrew Thompson usb_fifo_cleanup, f_sc->dev->si_drv1); 17747214348fSAndrew Thompson f_sc->dev = NULL; 1775ee3e3ff5SAndrew Thompson } 1776ee3e3ff5SAndrew Thompson 177702ac6454SAndrew Thompson DPRINTFN(2, "detached %p\n", f_sc); 177802ac6454SAndrew Thompson } 177902ac6454SAndrew Thompson 1780f9cb546cSAndrew Thompson usb_size_t 1781a593f6b8SAndrew Thompson usb_fifo_put_bytes_max(struct usb_fifo *f) 178202ac6454SAndrew Thompson { 1783760bc48eSAndrew Thompson struct usb_mbuf *m; 1784f9cb546cSAndrew Thompson usb_size_t len; 178502ac6454SAndrew Thompson 178602ac6454SAndrew Thompson USB_IF_POLL(&f->free_q, m); 178702ac6454SAndrew Thompson 178802ac6454SAndrew Thompson if (m) { 178902ac6454SAndrew Thompson len = m->max_data_len; 179002ac6454SAndrew Thompson } else { 179102ac6454SAndrew Thompson len = 0; 179202ac6454SAndrew Thompson } 179302ac6454SAndrew Thompson return (len); 179402ac6454SAndrew Thompson } 179502ac6454SAndrew Thompson 179602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1797a593f6b8SAndrew Thompson * usb_fifo_put_data 179802ac6454SAndrew Thompson * 179902ac6454SAndrew Thompson * what: 180002ac6454SAndrew Thompson * 0 - normal operation 180102ac6454SAndrew Thompson * 1 - set last packet flag to enforce framing 180202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 180302ac6454SAndrew Thompson void 1804a593f6b8SAndrew Thompson usb_fifo_put_data(struct usb_fifo *f, struct usb_page_cache *pc, 1805e0a69b51SAndrew Thompson usb_frlength_t offset, usb_frlength_t len, uint8_t what) 180602ac6454SAndrew Thompson { 1807760bc48eSAndrew Thompson struct usb_mbuf *m; 1808e0a69b51SAndrew Thompson usb_frlength_t io_len; 180902ac6454SAndrew Thompson 181002ac6454SAndrew Thompson while (len || (what == 1)) { 181102ac6454SAndrew Thompson 181202ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 181302ac6454SAndrew Thompson 181402ac6454SAndrew Thompson if (m) { 181502ac6454SAndrew Thompson USB_MBUF_RESET(m); 181602ac6454SAndrew Thompson 181702ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 181802ac6454SAndrew Thompson 1819a593f6b8SAndrew Thompson usbd_copy_out(pc, offset, m->cur_data_ptr, io_len); 182002ac6454SAndrew Thompson 182102ac6454SAndrew Thompson m->cur_data_len = io_len; 182202ac6454SAndrew Thompson offset += io_len; 182302ac6454SAndrew Thompson len -= io_len; 182402ac6454SAndrew Thompson 182502ac6454SAndrew Thompson if ((len == 0) && (what == 1)) { 182602ac6454SAndrew Thompson m->last_packet = 1; 182702ac6454SAndrew Thompson } 182802ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 182902ac6454SAndrew Thompson 1830a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 183102ac6454SAndrew Thompson 183202ac6454SAndrew Thompson if ((len == 0) || (what == 1)) { 183302ac6454SAndrew Thompson break; 183402ac6454SAndrew Thompson } 183502ac6454SAndrew Thompson } else { 183602ac6454SAndrew Thompson break; 183702ac6454SAndrew Thompson } 183802ac6454SAndrew Thompson } 183902ac6454SAndrew Thompson } 184002ac6454SAndrew Thompson 184102ac6454SAndrew Thompson void 1842a593f6b8SAndrew Thompson usb_fifo_put_data_linear(struct usb_fifo *f, void *ptr, 1843f9cb546cSAndrew Thompson usb_size_t len, uint8_t what) 184402ac6454SAndrew Thompson { 1845760bc48eSAndrew Thompson struct usb_mbuf *m; 1846f9cb546cSAndrew Thompson usb_size_t io_len; 184702ac6454SAndrew Thompson 184802ac6454SAndrew Thompson while (len || (what == 1)) { 184902ac6454SAndrew Thompson 185002ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 185102ac6454SAndrew Thompson 185202ac6454SAndrew Thompson if (m) { 185302ac6454SAndrew Thompson USB_MBUF_RESET(m); 185402ac6454SAndrew Thompson 185502ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 185602ac6454SAndrew Thompson 185702ac6454SAndrew Thompson bcopy(ptr, m->cur_data_ptr, io_len); 185802ac6454SAndrew Thompson 185902ac6454SAndrew Thompson m->cur_data_len = io_len; 186002ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, io_len); 186102ac6454SAndrew Thompson len -= io_len; 186202ac6454SAndrew Thompson 186302ac6454SAndrew Thompson if ((len == 0) && (what == 1)) { 186402ac6454SAndrew Thompson m->last_packet = 1; 186502ac6454SAndrew Thompson } 186602ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 186702ac6454SAndrew Thompson 1868a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 186902ac6454SAndrew Thompson 187002ac6454SAndrew Thompson if ((len == 0) || (what == 1)) { 187102ac6454SAndrew Thompson break; 187202ac6454SAndrew Thompson } 187302ac6454SAndrew Thompson } else { 187402ac6454SAndrew Thompson break; 187502ac6454SAndrew Thompson } 187602ac6454SAndrew Thompson } 187702ac6454SAndrew Thompson } 187802ac6454SAndrew Thompson 187902ac6454SAndrew Thompson uint8_t 1880a593f6b8SAndrew Thompson usb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len) 188102ac6454SAndrew Thompson { 1882760bc48eSAndrew Thompson struct usb_mbuf *m; 188302ac6454SAndrew Thompson 188402ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 188502ac6454SAndrew Thompson 188602ac6454SAndrew Thompson if (m) { 188702ac6454SAndrew Thompson m->cur_data_len = len; 188802ac6454SAndrew Thompson m->cur_data_ptr = ptr; 188902ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 1890a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 189102ac6454SAndrew Thompson return (1); 189202ac6454SAndrew Thompson } 189302ac6454SAndrew Thompson return (0); 189402ac6454SAndrew Thompson } 189502ac6454SAndrew Thompson 189602ac6454SAndrew Thompson void 1897a593f6b8SAndrew Thompson usb_fifo_put_data_error(struct usb_fifo *f) 189802ac6454SAndrew Thompson { 189902ac6454SAndrew Thompson f->flag_iserror = 1; 1900a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 190102ac6454SAndrew Thompson } 190202ac6454SAndrew Thompson 190302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1904a593f6b8SAndrew Thompson * usb_fifo_get_data 190502ac6454SAndrew Thompson * 190602ac6454SAndrew Thompson * what: 190702ac6454SAndrew Thompson * 0 - normal operation 1908760bc48eSAndrew Thompson * 1 - only get one "usb_mbuf" 190902ac6454SAndrew Thompson * 191002ac6454SAndrew Thompson * returns: 191102ac6454SAndrew Thompson * 0 - no more data 191202ac6454SAndrew Thompson * 1 - data in buffer 191302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 191402ac6454SAndrew Thompson uint8_t 1915a593f6b8SAndrew Thompson usb_fifo_get_data(struct usb_fifo *f, struct usb_page_cache *pc, 1916e0a69b51SAndrew Thompson usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen, 191702ac6454SAndrew Thompson uint8_t what) 191802ac6454SAndrew Thompson { 1919760bc48eSAndrew Thompson struct usb_mbuf *m; 1920e0a69b51SAndrew Thompson usb_frlength_t io_len; 192102ac6454SAndrew Thompson uint8_t tr_data = 0; 192202ac6454SAndrew Thompson 192302ac6454SAndrew Thompson actlen[0] = 0; 192402ac6454SAndrew Thompson 192502ac6454SAndrew Thompson while (1) { 192602ac6454SAndrew Thompson 192702ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 192802ac6454SAndrew Thompson 192902ac6454SAndrew Thompson if (m) { 193002ac6454SAndrew Thompson 193102ac6454SAndrew Thompson tr_data = 1; 193202ac6454SAndrew Thompson 193302ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 193402ac6454SAndrew Thompson 1935a593f6b8SAndrew Thompson usbd_copy_in(pc, offset, m->cur_data_ptr, io_len); 193602ac6454SAndrew Thompson 193702ac6454SAndrew Thompson len -= io_len; 193802ac6454SAndrew Thompson offset += io_len; 193902ac6454SAndrew Thompson actlen[0] += io_len; 194002ac6454SAndrew Thompson m->cur_data_ptr += io_len; 194102ac6454SAndrew Thompson m->cur_data_len -= io_len; 194202ac6454SAndrew Thompson 194302ac6454SAndrew Thompson if ((m->cur_data_len == 0) || (what == 1)) { 194402ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 194502ac6454SAndrew Thompson 1946a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 194702ac6454SAndrew Thompson 194802ac6454SAndrew Thompson if (what == 1) { 194902ac6454SAndrew Thompson break; 195002ac6454SAndrew Thompson } 195102ac6454SAndrew Thompson } else { 195202ac6454SAndrew Thompson USB_IF_PREPEND(&f->used_q, m); 195302ac6454SAndrew Thompson } 195402ac6454SAndrew Thompson } else { 195502ac6454SAndrew Thompson 195602ac6454SAndrew Thompson if (tr_data) { 195702ac6454SAndrew Thompson /* wait for data to be written out */ 195802ac6454SAndrew Thompson break; 195902ac6454SAndrew Thompson } 196002ac6454SAndrew Thompson if (f->flag_flushing) { 19617214348fSAndrew Thompson /* check if we should send a short packet */ 19627214348fSAndrew Thompson if (f->flag_short != 0) { 19637214348fSAndrew Thompson f->flag_short = 0; 19647214348fSAndrew Thompson tr_data = 1; 19657214348fSAndrew Thompson break; 19667214348fSAndrew Thompson } 19677214348fSAndrew Thompson /* flushing complete */ 196802ac6454SAndrew Thompson f->flag_flushing = 0; 1969a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 197002ac6454SAndrew Thompson } 197102ac6454SAndrew Thompson break; 197202ac6454SAndrew Thompson } 197302ac6454SAndrew Thompson if (len == 0) { 197402ac6454SAndrew Thompson break; 197502ac6454SAndrew Thompson } 197602ac6454SAndrew Thompson } 197702ac6454SAndrew Thompson return (tr_data); 197802ac6454SAndrew Thompson } 197902ac6454SAndrew Thompson 198002ac6454SAndrew Thompson uint8_t 1981a593f6b8SAndrew Thompson usb_fifo_get_data_linear(struct usb_fifo *f, void *ptr, 1982f9cb546cSAndrew Thompson usb_size_t len, usb_size_t *actlen, uint8_t what) 198302ac6454SAndrew Thompson { 1984760bc48eSAndrew Thompson struct usb_mbuf *m; 1985f9cb546cSAndrew Thompson usb_size_t io_len; 198602ac6454SAndrew Thompson uint8_t tr_data = 0; 198702ac6454SAndrew Thompson 198802ac6454SAndrew Thompson actlen[0] = 0; 198902ac6454SAndrew Thompson 199002ac6454SAndrew Thompson while (1) { 199102ac6454SAndrew Thompson 199202ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 199302ac6454SAndrew Thompson 199402ac6454SAndrew Thompson if (m) { 199502ac6454SAndrew Thompson 199602ac6454SAndrew Thompson tr_data = 1; 199702ac6454SAndrew Thompson 199802ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 199902ac6454SAndrew Thompson 200002ac6454SAndrew Thompson bcopy(m->cur_data_ptr, ptr, io_len); 200102ac6454SAndrew Thompson 200202ac6454SAndrew Thompson len -= io_len; 200302ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, io_len); 200402ac6454SAndrew Thompson actlen[0] += io_len; 200502ac6454SAndrew Thompson m->cur_data_ptr += io_len; 200602ac6454SAndrew Thompson m->cur_data_len -= io_len; 200702ac6454SAndrew Thompson 200802ac6454SAndrew Thompson if ((m->cur_data_len == 0) || (what == 1)) { 200902ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 201002ac6454SAndrew Thompson 2011a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 201202ac6454SAndrew Thompson 201302ac6454SAndrew Thompson if (what == 1) { 201402ac6454SAndrew Thompson break; 201502ac6454SAndrew Thompson } 201602ac6454SAndrew Thompson } else { 201702ac6454SAndrew Thompson USB_IF_PREPEND(&f->used_q, m); 201802ac6454SAndrew Thompson } 201902ac6454SAndrew Thompson } else { 202002ac6454SAndrew Thompson 202102ac6454SAndrew Thompson if (tr_data) { 202202ac6454SAndrew Thompson /* wait for data to be written out */ 202302ac6454SAndrew Thompson break; 202402ac6454SAndrew Thompson } 202502ac6454SAndrew Thompson if (f->flag_flushing) { 20267214348fSAndrew Thompson /* check if we should send a short packet */ 20277214348fSAndrew Thompson if (f->flag_short != 0) { 20287214348fSAndrew Thompson f->flag_short = 0; 20297214348fSAndrew Thompson tr_data = 1; 20307214348fSAndrew Thompson break; 20317214348fSAndrew Thompson } 20327214348fSAndrew Thompson /* flushing complete */ 203302ac6454SAndrew Thompson f->flag_flushing = 0; 2034a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 203502ac6454SAndrew Thompson } 203602ac6454SAndrew Thompson break; 203702ac6454SAndrew Thompson } 203802ac6454SAndrew Thompson if (len == 0) { 203902ac6454SAndrew Thompson break; 204002ac6454SAndrew Thompson } 204102ac6454SAndrew Thompson } 204202ac6454SAndrew Thompson return (tr_data); 204302ac6454SAndrew Thompson } 204402ac6454SAndrew Thompson 204502ac6454SAndrew Thompson uint8_t 2046a593f6b8SAndrew Thompson usb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen) 204702ac6454SAndrew Thompson { 2048760bc48eSAndrew Thompson struct usb_mbuf *m; 204902ac6454SAndrew Thompson 205002ac6454SAndrew Thompson USB_IF_POLL(&f->used_q, m); 205102ac6454SAndrew Thompson 205202ac6454SAndrew Thompson if (m) { 205302ac6454SAndrew Thompson *plen = m->cur_data_len; 205402ac6454SAndrew Thompson *pptr = m->cur_data_ptr; 205502ac6454SAndrew Thompson 205602ac6454SAndrew Thompson return (1); 205702ac6454SAndrew Thompson } 205802ac6454SAndrew Thompson return (0); 205902ac6454SAndrew Thompson } 206002ac6454SAndrew Thompson 206102ac6454SAndrew Thompson void 2062a593f6b8SAndrew Thompson usb_fifo_get_data_error(struct usb_fifo *f) 206302ac6454SAndrew Thompson { 206402ac6454SAndrew Thompson f->flag_iserror = 1; 2065a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 206602ac6454SAndrew Thompson } 206702ac6454SAndrew Thompson 206802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2069a593f6b8SAndrew Thompson * usb_alloc_symlink 207002ac6454SAndrew Thompson * 207102ac6454SAndrew Thompson * Return values: 207202ac6454SAndrew Thompson * NULL: Failure 207302ac6454SAndrew Thompson * Else: Pointer to symlink entry 207402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 2075760bc48eSAndrew Thompson struct usb_symlink * 2076a593f6b8SAndrew Thompson usb_alloc_symlink(const char *target) 207702ac6454SAndrew Thompson { 2078760bc48eSAndrew Thompson struct usb_symlink *ps; 207902ac6454SAndrew Thompson 208002ac6454SAndrew Thompson ps = malloc(sizeof(*ps), M_USBDEV, M_WAITOK); 208102ac6454SAndrew Thompson if (ps == NULL) { 208202ac6454SAndrew Thompson return (ps); 208302ac6454SAndrew Thompson } 2084ee3e3ff5SAndrew Thompson /* XXX no longer needed */ 2085ee3e3ff5SAndrew Thompson strlcpy(ps->src_path, target, sizeof(ps->src_path)); 2086ee3e3ff5SAndrew Thompson ps->src_len = strlen(ps->src_path); 208702ac6454SAndrew Thompson strlcpy(ps->dst_path, target, sizeof(ps->dst_path)); 208802ac6454SAndrew Thompson ps->dst_len = strlen(ps->dst_path); 208902ac6454SAndrew Thompson 2090a593f6b8SAndrew Thompson sx_xlock(&usb_sym_lock); 2091a593f6b8SAndrew Thompson TAILQ_INSERT_TAIL(&usb_sym_head, ps, sym_entry); 2092a593f6b8SAndrew Thompson sx_unlock(&usb_sym_lock); 209302ac6454SAndrew Thompson return (ps); 209402ac6454SAndrew Thompson } 209502ac6454SAndrew Thompson 209602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2097a593f6b8SAndrew Thompson * usb_free_symlink 209802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 209902ac6454SAndrew Thompson void 2100a593f6b8SAndrew Thompson usb_free_symlink(struct usb_symlink *ps) 210102ac6454SAndrew Thompson { 210202ac6454SAndrew Thompson if (ps == NULL) { 210302ac6454SAndrew Thompson return; 210402ac6454SAndrew Thompson } 2105a593f6b8SAndrew Thompson sx_xlock(&usb_sym_lock); 2106a593f6b8SAndrew Thompson TAILQ_REMOVE(&usb_sym_head, ps, sym_entry); 2107a593f6b8SAndrew Thompson sx_unlock(&usb_sym_lock); 210802ac6454SAndrew Thompson 210902ac6454SAndrew Thompson free(ps, M_USBDEV); 211002ac6454SAndrew Thompson } 211102ac6454SAndrew Thompson 211202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2113a593f6b8SAndrew Thompson * usb_read_symlink 211402ac6454SAndrew Thompson * 211502ac6454SAndrew Thompson * Return value: 211602ac6454SAndrew Thompson * 0: Success 211702ac6454SAndrew Thompson * Else: Failure 211802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 211902ac6454SAndrew Thompson int 2120a593f6b8SAndrew Thompson usb_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len) 212102ac6454SAndrew Thompson { 2122760bc48eSAndrew Thompson struct usb_symlink *ps; 212302ac6454SAndrew Thompson uint32_t temp; 212402ac6454SAndrew Thompson uint32_t delta = 0; 212502ac6454SAndrew Thompson uint8_t len; 212602ac6454SAndrew Thompson int error = 0; 212702ac6454SAndrew Thompson 2128a593f6b8SAndrew Thompson sx_xlock(&usb_sym_lock); 212902ac6454SAndrew Thompson 2130a593f6b8SAndrew Thompson TAILQ_FOREACH(ps, &usb_sym_head, sym_entry) { 213102ac6454SAndrew Thompson 213202ac6454SAndrew Thompson /* 213302ac6454SAndrew Thompson * Compute total length of source and destination symlink 213402ac6454SAndrew Thompson * strings pluss one length byte and two NUL bytes: 213502ac6454SAndrew Thompson */ 213602ac6454SAndrew Thompson temp = ps->src_len + ps->dst_len + 3; 213702ac6454SAndrew Thompson 213802ac6454SAndrew Thompson if (temp > 255) { 213902ac6454SAndrew Thompson /* 214002ac6454SAndrew Thompson * Skip entry because this length cannot fit 214102ac6454SAndrew Thompson * into one byte: 214202ac6454SAndrew Thompson */ 214302ac6454SAndrew Thompson continue; 214402ac6454SAndrew Thompson } 214502ac6454SAndrew Thompson if (startentry != 0) { 214602ac6454SAndrew Thompson /* decrement read offset */ 214702ac6454SAndrew Thompson startentry--; 214802ac6454SAndrew Thompson continue; 214902ac6454SAndrew Thompson } 215002ac6454SAndrew Thompson if (temp > user_len) { 215102ac6454SAndrew Thompson /* out of buffer space */ 215202ac6454SAndrew Thompson break; 215302ac6454SAndrew Thompson } 215402ac6454SAndrew Thompson len = temp; 215502ac6454SAndrew Thompson 215602ac6454SAndrew Thompson /* copy out total length */ 215702ac6454SAndrew Thompson 215802ac6454SAndrew Thompson error = copyout(&len, 215902ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 216002ac6454SAndrew Thompson if (error) { 216102ac6454SAndrew Thompson break; 216202ac6454SAndrew Thompson } 216302ac6454SAndrew Thompson delta += 1; 216402ac6454SAndrew Thompson 216502ac6454SAndrew Thompson /* copy out source string */ 216602ac6454SAndrew Thompson 216702ac6454SAndrew Thompson error = copyout(ps->src_path, 216802ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), ps->src_len); 216902ac6454SAndrew Thompson if (error) { 217002ac6454SAndrew Thompson break; 217102ac6454SAndrew Thompson } 217202ac6454SAndrew Thompson len = 0; 217302ac6454SAndrew Thompson delta += ps->src_len; 217402ac6454SAndrew Thompson error = copyout(&len, 217502ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 217602ac6454SAndrew Thompson if (error) { 217702ac6454SAndrew Thompson break; 217802ac6454SAndrew Thompson } 217902ac6454SAndrew Thompson delta += 1; 218002ac6454SAndrew Thompson 218102ac6454SAndrew Thompson /* copy out destination string */ 218202ac6454SAndrew Thompson 218302ac6454SAndrew Thompson error = copyout(ps->dst_path, 218402ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), ps->dst_len); 218502ac6454SAndrew Thompson if (error) { 218602ac6454SAndrew Thompson break; 218702ac6454SAndrew Thompson } 218802ac6454SAndrew Thompson len = 0; 218902ac6454SAndrew Thompson delta += ps->dst_len; 219002ac6454SAndrew Thompson error = copyout(&len, 219102ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 219202ac6454SAndrew Thompson if (error) { 219302ac6454SAndrew Thompson break; 219402ac6454SAndrew Thompson } 219502ac6454SAndrew Thompson delta += 1; 219602ac6454SAndrew Thompson 219702ac6454SAndrew Thompson user_len -= temp; 219802ac6454SAndrew Thompson } 219902ac6454SAndrew Thompson 220002ac6454SAndrew Thompson /* a zero length entry indicates the end */ 220102ac6454SAndrew Thompson 220202ac6454SAndrew Thompson if ((user_len != 0) && (error == 0)) { 220302ac6454SAndrew Thompson 220402ac6454SAndrew Thompson len = 0; 220502ac6454SAndrew Thompson 220602ac6454SAndrew Thompson error = copyout(&len, 220702ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 220802ac6454SAndrew Thompson } 2209a593f6b8SAndrew Thompson sx_unlock(&usb_sym_lock); 221002ac6454SAndrew Thompson return (error); 221102ac6454SAndrew Thompson } 22127214348fSAndrew Thompson 22137214348fSAndrew Thompson void 2214a593f6b8SAndrew Thompson usb_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff) 22157214348fSAndrew Thompson { 22167214348fSAndrew Thompson if (f == NULL) 22177214348fSAndrew Thompson return; 22187214348fSAndrew Thompson 22197214348fSAndrew Thompson /* send a Zero Length Packet, ZLP, before close */ 22207214348fSAndrew Thompson f->flag_short = onoff; 22217214348fSAndrew Thompson } 2222ed6d949aSAndrew Thompson 2223ed6d949aSAndrew Thompson void * 2224ed6d949aSAndrew Thompson usb_fifo_softc(struct usb_fifo *f) 2225ed6d949aSAndrew Thompson { 2226ed6d949aSAndrew Thompson return (f->priv_sc0); 2227ed6d949aSAndrew Thompson } 22288755859aSAndrew Thompson #endif /* USB_HAVE_UGEN */ 2229