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"); 88c13fd8d4SAndrew Thompson 89c13fd8d4SAndrew Thompson TUNABLE_INT("hw.usb.dev.debug", &usb_fifo_debug); 9002ac6454SAndrew Thompson #endif 9102ac6454SAndrew Thompson 9202ac6454SAndrew Thompson #if ((__FreeBSD_version >= 700001) || (__FreeBSD_version == 0) || \ 9302ac6454SAndrew Thompson ((__FreeBSD_version >= 600034) && (__FreeBSD_version < 700000))) 9402ac6454SAndrew Thompson #define USB_UCRED struct ucred *ucred, 9502ac6454SAndrew Thompson #else 9602ac6454SAndrew Thompson #define USB_UCRED 9702ac6454SAndrew Thompson #endif 9802ac6454SAndrew Thompson 9902ac6454SAndrew Thompson /* prototypes */ 10002ac6454SAndrew Thompson 101a593f6b8SAndrew Thompson static int usb_fifo_open(struct usb_cdev_privdata *, 102760bc48eSAndrew Thompson struct usb_fifo *, int); 103a593f6b8SAndrew Thompson static void usb_fifo_close(struct usb_fifo *, int); 104a593f6b8SAndrew Thompson static void usb_dev_init(void *); 105a593f6b8SAndrew Thompson static void usb_dev_init_post(void *); 106a593f6b8SAndrew Thompson static void usb_dev_uninit(void *); 107a593f6b8SAndrew Thompson static int usb_fifo_uiomove(struct usb_fifo *, void *, int, 10802ac6454SAndrew Thompson struct uio *); 109a593f6b8SAndrew Thompson static void usb_fifo_check_methods(struct usb_fifo_methods *); 110a593f6b8SAndrew Thompson static struct usb_fifo *usb_fifo_alloc(void); 111a593f6b8SAndrew Thompson static struct usb_endpoint *usb_dev_get_ep(struct usb_device *, uint8_t, 112f5f145baSAndrew Thompson uint8_t); 113a593f6b8SAndrew Thompson static void usb_loc_fill(struct usb_fs_privdata *, 114760bc48eSAndrew Thompson struct usb_cdev_privdata *); 115a593f6b8SAndrew Thompson static void usb_close(void *); 116a593f6b8SAndrew Thompson static usb_error_t usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *, int); 117a593f6b8SAndrew Thompson static usb_error_t usb_usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *); 118a593f6b8SAndrew Thompson static void usb_unref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *); 11902ac6454SAndrew Thompson 120a593f6b8SAndrew Thompson static d_open_t usb_open; 121a593f6b8SAndrew Thompson static d_ioctl_t usb_ioctl; 122a593f6b8SAndrew Thompson static d_read_t usb_read; 123a593f6b8SAndrew Thompson static d_write_t usb_write; 124a593f6b8SAndrew Thompson static d_poll_t usb_poll; 12502ac6454SAndrew Thompson 126a593f6b8SAndrew Thompson static d_ioctl_t usb_static_ioctl; 12702ac6454SAndrew Thompson 128a593f6b8SAndrew Thompson static usb_fifo_open_t usb_fifo_dummy_open; 129a593f6b8SAndrew Thompson static usb_fifo_close_t usb_fifo_dummy_close; 130a593f6b8SAndrew Thompson static usb_fifo_ioctl_t usb_fifo_dummy_ioctl; 131a593f6b8SAndrew Thompson static usb_fifo_cmd_t usb_fifo_dummy_cmd; 13202ac6454SAndrew Thompson 133ee3e3ff5SAndrew Thompson /* character device structure used for devices (/dev/ugenX.Y and /dev/uXXX) */ 134a593f6b8SAndrew Thompson struct cdevsw usb_devsw = { 13502ac6454SAndrew Thompson .d_version = D_VERSION, 136a593f6b8SAndrew Thompson .d_open = usb_open, 137a593f6b8SAndrew Thompson .d_ioctl = usb_ioctl, 138ee3e3ff5SAndrew Thompson .d_name = "usbdev", 13902ac6454SAndrew Thompson .d_flags = D_TRACKCLOSE, 140a593f6b8SAndrew Thompson .d_read = usb_read, 141a593f6b8SAndrew Thompson .d_write = usb_write, 142a593f6b8SAndrew Thompson .d_poll = usb_poll 14302ac6454SAndrew Thompson }; 14402ac6454SAndrew Thompson 145a593f6b8SAndrew Thompson static struct cdev* usb_dev = NULL; 146ee3e3ff5SAndrew Thompson 147ee3e3ff5SAndrew Thompson /* character device structure used for /dev/usb */ 148a593f6b8SAndrew Thompson static struct cdevsw usb_static_devsw = { 149ee3e3ff5SAndrew Thompson .d_version = D_VERSION, 150a593f6b8SAndrew Thompson .d_ioctl = usb_static_ioctl, 151ee3e3ff5SAndrew Thompson .d_name = "usb" 15202ac6454SAndrew Thompson }; 15302ac6454SAndrew Thompson 154a593f6b8SAndrew Thompson static TAILQ_HEAD(, usb_symlink) usb_sym_head; 155a593f6b8SAndrew Thompson static struct sx usb_sym_lock; 15602ac6454SAndrew Thompson 157a593f6b8SAndrew Thompson struct mtx usb_ref_lock; 15802ac6454SAndrew Thompson 15902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 160a593f6b8SAndrew Thompson * usb_loc_fill 16102ac6454SAndrew Thompson * 162760bc48eSAndrew Thompson * This is used to fill out a usb_cdev_privdata structure based on the 163760bc48eSAndrew Thompson * device's address as contained in usb_fs_privdata. 16402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 165ee3e3ff5SAndrew Thompson static void 166a593f6b8SAndrew Thompson usb_loc_fill(struct usb_fs_privdata* pd, struct usb_cdev_privdata *cpd) 16702ac6454SAndrew Thompson { 168ee3e3ff5SAndrew Thompson cpd->bus_index = pd->bus_index; 169ee3e3ff5SAndrew Thompson cpd->dev_index = pd->dev_index; 170ee3e3ff5SAndrew Thompson cpd->ep_addr = pd->ep_addr; 171ee3e3ff5SAndrew Thompson cpd->fifo_index = pd->fifo_index; 17202ac6454SAndrew Thompson } 17302ac6454SAndrew Thompson 17402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 175a593f6b8SAndrew Thompson * usb_ref_device 17602ac6454SAndrew Thompson * 17702ac6454SAndrew Thompson * This function is used to atomically refer an USB device by its 17802ac6454SAndrew Thompson * device location. If this function returns success the USB device 17902ac6454SAndrew Thompson * will not dissappear until the USB device is unreferenced. 18002ac6454SAndrew Thompson * 18102ac6454SAndrew Thompson * Return values: 18202ac6454SAndrew Thompson * 0: Success, refcount incremented on the given USB device. 18302ac6454SAndrew Thompson * Else: Failure. 18402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 185e0a69b51SAndrew Thompson usb_error_t 186a593f6b8SAndrew Thompson usb_ref_device(struct usb_cdev_privdata *cpd, 187e13e19faSAndrew Thompson struct usb_cdev_refdata *crd, int need_uref) 18802ac6454SAndrew Thompson { 189760bc48eSAndrew Thompson struct usb_fifo **ppf; 190760bc48eSAndrew Thompson struct usb_fifo *f; 19102ac6454SAndrew Thompson 192e13e19faSAndrew Thompson DPRINTFN(2, "cpd=%p need uref=%d\n", cpd, need_uref); 193e13e19faSAndrew Thompson 194e13e19faSAndrew Thompson /* clear all refs */ 195e13e19faSAndrew Thompson memset(crd, 0, sizeof(*crd)); 19602ac6454SAndrew Thompson 197a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 198a593f6b8SAndrew Thompson cpd->bus = devclass_get_softc(usb_devclass_ptr, cpd->bus_index); 199ee3e3ff5SAndrew Thompson if (cpd->bus == NULL) { 200ee3e3ff5SAndrew Thompson DPRINTFN(2, "no bus at %u\n", cpd->bus_index); 20102ac6454SAndrew Thompson goto error; 20202ac6454SAndrew Thompson } 203ee3e3ff5SAndrew Thompson cpd->udev = cpd->bus->devices[cpd->dev_index]; 204ee3e3ff5SAndrew Thompson if (cpd->udev == NULL) { 205ee3e3ff5SAndrew Thompson DPRINTFN(2, "no device at %u\n", cpd->dev_index); 20602ac6454SAndrew Thompson goto error; 20702ac6454SAndrew Thompson } 208ee3e3ff5SAndrew Thompson if (cpd->udev->refcount == USB_DEV_REF_MAX) { 20902ac6454SAndrew Thompson DPRINTFN(2, "no dev ref\n"); 21002ac6454SAndrew Thompson goto error; 21102ac6454SAndrew Thompson } 2120ed53d45SAndrew Thompson if (need_uref) { 2130ed53d45SAndrew Thompson DPRINTFN(2, "ref udev - needed\n"); 2140ed53d45SAndrew Thompson cpd->udev->refcount++; 2150ed53d45SAndrew Thompson 216a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 2170ed53d45SAndrew Thompson 2180ed53d45SAndrew Thompson /* 2190ed53d45SAndrew Thompson * We need to grab the sx-lock before grabbing the 2200ed53d45SAndrew Thompson * FIFO refs to avoid deadlock at detach! 2210ed53d45SAndrew Thompson */ 222cb18f7d1SAlfred Perlstein usbd_enum_lock(cpd->udev); 2230ed53d45SAndrew Thompson 224a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 225a488edb5SAndrew Thompson 226a488edb5SAndrew Thompson /* 227a488edb5SAndrew Thompson * Set "is_uref" after grabbing the default SX lock 228a488edb5SAndrew Thompson */ 229e13e19faSAndrew Thompson crd->is_uref = 1; 2300ed53d45SAndrew Thompson } 2310ed53d45SAndrew Thompson 23202ac6454SAndrew Thompson /* check if we are doing an open */ 233ee3e3ff5SAndrew Thompson if (cpd->fflags == 0) { 234e13e19faSAndrew Thompson /* use zero defaults */ 23502ac6454SAndrew Thompson } else { 23602ac6454SAndrew Thompson /* check for write */ 237ee3e3ff5SAndrew Thompson if (cpd->fflags & FWRITE) { 238ee3e3ff5SAndrew Thompson ppf = cpd->udev->fifo; 239ee3e3ff5SAndrew Thompson f = ppf[cpd->fifo_index + USB_FIFO_TX]; 240e13e19faSAndrew Thompson crd->txfifo = f; 241e13e19faSAndrew Thompson crd->is_write = 1; /* ref */ 242ee3e3ff5SAndrew Thompson if (f == NULL || f->refcount == USB_FIFO_REF_MAX) 24302ac6454SAndrew Thompson goto error; 2447214348fSAndrew Thompson if (f->curr_cpd != cpd) 2457214348fSAndrew Thompson goto error; 24602ac6454SAndrew Thompson /* check if USB-FS is active */ 24702ac6454SAndrew Thompson if (f->fs_ep_max != 0) { 248e13e19faSAndrew Thompson crd->is_usbfs = 1; 24902ac6454SAndrew Thompson } 25002ac6454SAndrew Thompson } 25102ac6454SAndrew Thompson 25202ac6454SAndrew Thompson /* check for read */ 253ee3e3ff5SAndrew Thompson if (cpd->fflags & FREAD) { 254ee3e3ff5SAndrew Thompson ppf = cpd->udev->fifo; 255ee3e3ff5SAndrew Thompson f = ppf[cpd->fifo_index + USB_FIFO_RX]; 256e13e19faSAndrew Thompson crd->rxfifo = f; 257e13e19faSAndrew Thompson crd->is_read = 1; /* ref */ 258ee3e3ff5SAndrew Thompson if (f == NULL || f->refcount == USB_FIFO_REF_MAX) 25902ac6454SAndrew Thompson goto error; 2607214348fSAndrew Thompson if (f->curr_cpd != cpd) 2617214348fSAndrew Thompson goto error; 26202ac6454SAndrew Thompson /* check if USB-FS is active */ 26302ac6454SAndrew Thompson if (f->fs_ep_max != 0) { 264e13e19faSAndrew Thompson crd->is_usbfs = 1; 26502ac6454SAndrew Thompson } 26602ac6454SAndrew Thompson } 26702ac6454SAndrew Thompson } 26802ac6454SAndrew Thompson 26902ac6454SAndrew Thompson /* when everything is OK we increment the refcounts */ 270e13e19faSAndrew Thompson if (crd->is_write) { 27102ac6454SAndrew Thompson DPRINTFN(2, "ref write\n"); 272e13e19faSAndrew Thompson crd->txfifo->refcount++; 27302ac6454SAndrew Thompson } 274e13e19faSAndrew Thompson if (crd->is_read) { 27502ac6454SAndrew Thompson DPRINTFN(2, "ref read\n"); 276e13e19faSAndrew Thompson crd->rxfifo->refcount++; 27702ac6454SAndrew Thompson } 278a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 27902ac6454SAndrew Thompson 28002ac6454SAndrew Thompson return (0); 28102ac6454SAndrew Thompson 28202ac6454SAndrew Thompson error: 283e13e19faSAndrew Thompson if (crd->is_uref) { 284cb18f7d1SAlfred Perlstein usbd_enum_unlock(cpd->udev); 285cb18f7d1SAlfred Perlstein 2860ed53d45SAndrew Thompson if (--(cpd->udev->refcount) == 0) { 287*91cd9240SAndrew Thompson cv_signal(&cpd->udev->ref_cv); 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 337cb18f7d1SAlfred Perlstein if (crd->is_uref) 338cb18f7d1SAlfred Perlstein usbd_enum_unlock(cpd->udev); 339cb18f7d1SAlfred Perlstein 340a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 341e13e19faSAndrew Thompson if (crd->is_read) { 342e13e19faSAndrew Thompson if (--(crd->rxfifo->refcount) == 0) { 3438437751dSAndrew Thompson cv_signal(&crd->rxfifo->cv_drain); 34402ac6454SAndrew Thompson } 345e13e19faSAndrew Thompson crd->is_read = 0; 34602ac6454SAndrew Thompson } 347e13e19faSAndrew Thompson if (crd->is_write) { 348e13e19faSAndrew Thompson if (--(crd->txfifo->refcount) == 0) { 3498437751dSAndrew Thompson cv_signal(&crd->txfifo->cv_drain); 35002ac6454SAndrew Thompson } 351e13e19faSAndrew Thompson crd->is_write = 0; 35202ac6454SAndrew Thompson } 353e13e19faSAndrew Thompson if (crd->is_uref) { 354ee3e3ff5SAndrew Thompson if (--(cpd->udev->refcount) == 0) { 355*91cd9240SAndrew Thompson cv_signal(&cpd->udev->ref_cv); 35602ac6454SAndrew Thompson } 357e13e19faSAndrew Thompson crd->is_uref = 0; 35802ac6454SAndrew Thompson } 359a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 36002ac6454SAndrew Thompson } 36102ac6454SAndrew Thompson 362760bc48eSAndrew Thompson static struct usb_fifo * 363a593f6b8SAndrew Thompson usb_fifo_alloc(void) 36402ac6454SAndrew Thompson { 365760bc48eSAndrew Thompson struct usb_fifo *f; 36602ac6454SAndrew Thompson 36702ac6454SAndrew Thompson f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO); 36802ac6454SAndrew Thompson if (f) { 3698437751dSAndrew Thompson cv_init(&f->cv_io, "FIFO-IO"); 3708437751dSAndrew Thompson cv_init(&f->cv_drain, "FIFO-DRAIN"); 37102ac6454SAndrew Thompson f->refcount = 1; 37202ac6454SAndrew Thompson } 37302ac6454SAndrew Thompson return (f); 37402ac6454SAndrew Thompson } 37502ac6454SAndrew Thompson 37602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 377a593f6b8SAndrew Thompson * usb_fifo_create 37802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 37902ac6454SAndrew Thompson static int 380a593f6b8SAndrew Thompson usb_fifo_create(struct usb_cdev_privdata *cpd, 381e13e19faSAndrew Thompson struct usb_cdev_refdata *crd) 38202ac6454SAndrew Thompson { 383760bc48eSAndrew Thompson struct usb_device *udev = cpd->udev; 384760bc48eSAndrew Thompson struct usb_fifo *f; 385ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 38602ac6454SAndrew Thompson uint8_t n; 38702ac6454SAndrew Thompson uint8_t is_tx; 38802ac6454SAndrew Thompson uint8_t is_rx; 38902ac6454SAndrew Thompson uint8_t no_null; 39002ac6454SAndrew Thompson uint8_t is_busy; 391ae60fdfbSAndrew Thompson int e = cpd->ep_addr; 39202ac6454SAndrew Thompson 393ee3e3ff5SAndrew Thompson is_tx = (cpd->fflags & FWRITE) ? 1 : 0; 394ee3e3ff5SAndrew Thompson is_rx = (cpd->fflags & FREAD) ? 1 : 0; 39502ac6454SAndrew Thompson no_null = 1; 39602ac6454SAndrew Thompson is_busy = 0; 39702ac6454SAndrew Thompson 398ee3e3ff5SAndrew Thompson /* Preallocated FIFO */ 399ae60fdfbSAndrew Thompson if (e < 0) { 400ee3e3ff5SAndrew Thompson DPRINTFN(5, "Preallocated FIFO\n"); 401ee3e3ff5SAndrew Thompson if (is_tx) { 402ee3e3ff5SAndrew Thompson f = udev->fifo[cpd->fifo_index + USB_FIFO_TX]; 403ee3e3ff5SAndrew Thompson if (f == NULL) 404ee3e3ff5SAndrew Thompson return (EINVAL); 405e13e19faSAndrew Thompson crd->txfifo = f; 406ee3e3ff5SAndrew Thompson } 407ee3e3ff5SAndrew Thompson if (is_rx) { 408ee3e3ff5SAndrew Thompson f = udev->fifo[cpd->fifo_index + USB_FIFO_RX]; 409ee3e3ff5SAndrew Thompson if (f == NULL) 410ee3e3ff5SAndrew Thompson return (EINVAL); 411e13e19faSAndrew Thompson crd->rxfifo = f; 412ee3e3ff5SAndrew Thompson } 413ee3e3ff5SAndrew Thompson return (0); 414ee3e3ff5SAndrew Thompson } 41502ac6454SAndrew Thompson 416ae60fdfbSAndrew Thompson KASSERT(e >= 0 && e <= 15, ("endpoint %d out of range", e)); 417ee3e3ff5SAndrew Thompson 418ee3e3ff5SAndrew Thompson /* search for a free FIFO slot */ 419ae60fdfbSAndrew Thompson DPRINTFN(5, "Endpoint device, searching for 0x%02x\n", e); 42002ac6454SAndrew Thompson for (n = 0;; n += 2) { 42102ac6454SAndrew Thompson 42202ac6454SAndrew Thompson if (n == USB_FIFO_MAX) { 42302ac6454SAndrew Thompson if (no_null) { 42402ac6454SAndrew Thompson no_null = 0; 42502ac6454SAndrew Thompson n = 0; 42602ac6454SAndrew Thompson } else { 42702ac6454SAndrew Thompson /* end of FIFOs reached */ 428ee3e3ff5SAndrew Thompson DPRINTFN(5, "out of FIFOs\n"); 42902ac6454SAndrew Thompson return (ENOMEM); 43002ac6454SAndrew Thompson } 43102ac6454SAndrew Thompson } 43202ac6454SAndrew Thompson /* Check for TX FIFO */ 43302ac6454SAndrew Thompson if (is_tx) { 43402ac6454SAndrew Thompson f = udev->fifo[n + USB_FIFO_TX]; 43502ac6454SAndrew Thompson if (f != NULL) { 436ae60fdfbSAndrew Thompson if (f->dev_ep_index != e) { 43702ac6454SAndrew Thompson /* wrong endpoint index */ 43802ac6454SAndrew Thompson continue; 43902ac6454SAndrew Thompson } 4407214348fSAndrew Thompson if (f->curr_cpd != NULL) { 44102ac6454SAndrew Thompson /* FIFO is opened */ 44202ac6454SAndrew Thompson is_busy = 1; 44302ac6454SAndrew Thompson continue; 44402ac6454SAndrew Thompson } 44502ac6454SAndrew Thompson } else if (no_null) { 44602ac6454SAndrew Thompson continue; 44702ac6454SAndrew Thompson } 44802ac6454SAndrew Thompson } 44902ac6454SAndrew Thompson /* Check for RX FIFO */ 45002ac6454SAndrew Thompson if (is_rx) { 45102ac6454SAndrew Thompson f = udev->fifo[n + USB_FIFO_RX]; 45202ac6454SAndrew Thompson if (f != NULL) { 453ae60fdfbSAndrew Thompson if (f->dev_ep_index != e) { 45402ac6454SAndrew Thompson /* wrong endpoint index */ 45502ac6454SAndrew Thompson continue; 45602ac6454SAndrew Thompson } 4577214348fSAndrew Thompson if (f->curr_cpd != NULL) { 45802ac6454SAndrew Thompson /* FIFO is opened */ 45902ac6454SAndrew Thompson is_busy = 1; 46002ac6454SAndrew Thompson continue; 46102ac6454SAndrew Thompson } 46202ac6454SAndrew Thompson } else if (no_null) { 46302ac6454SAndrew Thompson continue; 46402ac6454SAndrew Thompson } 46502ac6454SAndrew Thompson } 46602ac6454SAndrew Thompson break; 46702ac6454SAndrew Thompson } 46802ac6454SAndrew Thompson 46902ac6454SAndrew Thompson if (no_null == 0) { 470ae60fdfbSAndrew Thompson if (e >= (USB_EP_MAX / 2)) { 47102ac6454SAndrew Thompson /* we don't create any endpoints in this range */ 4727214348fSAndrew Thompson DPRINTFN(5, "ep out of range\n"); 47302ac6454SAndrew Thompson return (is_busy ? EBUSY : EINVAL); 47402ac6454SAndrew Thompson } 47502ac6454SAndrew Thompson } 4767214348fSAndrew Thompson 477ae60fdfbSAndrew Thompson if ((e != 0) && is_busy) { 4787214348fSAndrew Thompson /* 4797214348fSAndrew Thompson * Only the default control endpoint is allowed to be 4807214348fSAndrew Thompson * opened multiple times! 4817214348fSAndrew Thompson */ 4827214348fSAndrew Thompson DPRINTFN(5, "busy\n"); 4837214348fSAndrew Thompson return (EBUSY); 4847214348fSAndrew Thompson } 4857214348fSAndrew Thompson 48602ac6454SAndrew Thompson /* Check TX FIFO */ 48702ac6454SAndrew Thompson if (is_tx && 48802ac6454SAndrew Thompson (udev->fifo[n + USB_FIFO_TX] == NULL)) { 489a593f6b8SAndrew Thompson ep = usb_dev_get_ep(udev, e, USB_FIFO_TX); 490ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_TX); 491ae60fdfbSAndrew Thompson if (ep == NULL) { 492ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint returned NULL\n"); 49302ac6454SAndrew Thompson return (EINVAL); 49402ac6454SAndrew Thompson } 495a593f6b8SAndrew Thompson f = usb_fifo_alloc(); 49602ac6454SAndrew Thompson if (f == NULL) { 497ee3e3ff5SAndrew Thompson DPRINTFN(5, "could not alloc tx fifo\n"); 49802ac6454SAndrew Thompson return (ENOMEM); 49902ac6454SAndrew Thompson } 50002ac6454SAndrew Thompson /* update some fields */ 50102ac6454SAndrew Thompson f->fifo_index = n + USB_FIFO_TX; 502ae60fdfbSAndrew Thompson f->dev_ep_index = e; 503*91cd9240SAndrew Thompson f->priv_mtx = &udev->device_mtx; 504ae60fdfbSAndrew Thompson f->priv_sc0 = ep; 505a593f6b8SAndrew Thompson f->methods = &usb_ugen_methods; 506ae60fdfbSAndrew Thompson f->iface_index = ep->iface_index; 50702ac6454SAndrew Thompson f->udev = udev; 508a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 50902ac6454SAndrew Thompson udev->fifo[n + USB_FIFO_TX] = f; 510a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 51102ac6454SAndrew Thompson } 51202ac6454SAndrew Thompson /* Check RX FIFO */ 51302ac6454SAndrew Thompson if (is_rx && 51402ac6454SAndrew Thompson (udev->fifo[n + USB_FIFO_RX] == NULL)) { 51502ac6454SAndrew Thompson 516a593f6b8SAndrew Thompson ep = usb_dev_get_ep(udev, e, USB_FIFO_RX); 517ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_RX); 518ae60fdfbSAndrew Thompson if (ep == NULL) { 519ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint returned NULL\n"); 52002ac6454SAndrew Thompson return (EINVAL); 52102ac6454SAndrew Thompson } 522a593f6b8SAndrew Thompson f = usb_fifo_alloc(); 52302ac6454SAndrew Thompson if (f == NULL) { 524ee3e3ff5SAndrew Thompson DPRINTFN(5, "could not alloc rx fifo\n"); 52502ac6454SAndrew Thompson return (ENOMEM); 52602ac6454SAndrew Thompson } 52702ac6454SAndrew Thompson /* update some fields */ 52802ac6454SAndrew Thompson f->fifo_index = n + USB_FIFO_RX; 529ae60fdfbSAndrew Thompson f->dev_ep_index = e; 530*91cd9240SAndrew Thompson f->priv_mtx = &udev->device_mtx; 531ae60fdfbSAndrew Thompson f->priv_sc0 = ep; 532a593f6b8SAndrew Thompson f->methods = &usb_ugen_methods; 533ae60fdfbSAndrew Thompson f->iface_index = ep->iface_index; 53402ac6454SAndrew Thompson f->udev = udev; 535a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 53602ac6454SAndrew Thompson udev->fifo[n + USB_FIFO_RX] = f; 537a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 53802ac6454SAndrew Thompson } 53902ac6454SAndrew Thompson if (is_tx) { 540e13e19faSAndrew Thompson crd->txfifo = udev->fifo[n + USB_FIFO_TX]; 54102ac6454SAndrew Thompson } 54202ac6454SAndrew Thompson if (is_rx) { 543e13e19faSAndrew Thompson crd->rxfifo = udev->fifo[n + USB_FIFO_RX]; 54402ac6454SAndrew Thompson } 545ee3e3ff5SAndrew Thompson /* fill out fifo index */ 546ee3e3ff5SAndrew Thompson DPRINTFN(5, "fifo index = %d\n", n); 547ee3e3ff5SAndrew Thompson cpd->fifo_index = n; 54802ac6454SAndrew Thompson 54902ac6454SAndrew Thompson /* complete */ 55002ac6454SAndrew Thompson 55102ac6454SAndrew Thompson return (0); 55202ac6454SAndrew Thompson } 55302ac6454SAndrew Thompson 55402ac6454SAndrew Thompson void 555a593f6b8SAndrew Thompson usb_fifo_free(struct usb_fifo *f) 55602ac6454SAndrew Thompson { 55702ac6454SAndrew Thompson uint8_t n; 55802ac6454SAndrew Thompson 55902ac6454SAndrew Thompson if (f == NULL) { 56002ac6454SAndrew Thompson /* be NULL safe */ 56102ac6454SAndrew Thompson return; 56202ac6454SAndrew Thompson } 56302ac6454SAndrew Thompson /* destroy symlink devices, if any */ 56402ac6454SAndrew Thompson for (n = 0; n != 2; n++) { 56502ac6454SAndrew Thompson if (f->symlink[n]) { 566a593f6b8SAndrew Thompson usb_free_symlink(f->symlink[n]); 56702ac6454SAndrew Thompson f->symlink[n] = NULL; 56802ac6454SAndrew Thompson } 56902ac6454SAndrew Thompson } 570a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 57102ac6454SAndrew Thompson 57202ac6454SAndrew Thompson /* delink ourselves to stop calls from userland */ 57302ac6454SAndrew Thompson if ((f->fifo_index < USB_FIFO_MAX) && 57402ac6454SAndrew Thompson (f->udev != NULL) && 57502ac6454SAndrew Thompson (f->udev->fifo[f->fifo_index] == f)) { 57602ac6454SAndrew Thompson f->udev->fifo[f->fifo_index] = NULL; 57702ac6454SAndrew Thompson } else { 578767cb2e2SAndrew Thompson DPRINTFN(0, "USB FIFO %p has not been linked\n", f); 57902ac6454SAndrew Thompson } 58002ac6454SAndrew Thompson 58102ac6454SAndrew Thompson /* decrease refcount */ 58202ac6454SAndrew Thompson f->refcount--; 58302ac6454SAndrew Thompson /* prevent any write flush */ 58402ac6454SAndrew Thompson f->flag_iserror = 1; 58502ac6454SAndrew Thompson /* need to wait until all callers have exited */ 58602ac6454SAndrew Thompson while (f->refcount != 0) { 587a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); /* avoid LOR */ 58802ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 58902ac6454SAndrew Thompson /* get I/O thread out of any sleep state */ 59002ac6454SAndrew Thompson if (f->flag_sleeping) { 59102ac6454SAndrew Thompson f->flag_sleeping = 0; 5928437751dSAndrew Thompson cv_broadcast(&f->cv_io); 59302ac6454SAndrew Thompson } 59402ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 595a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 59602ac6454SAndrew Thompson 59702ac6454SAndrew Thompson /* wait for sync */ 598a593f6b8SAndrew Thompson cv_wait(&f->cv_drain, &usb_ref_lock); 59902ac6454SAndrew Thompson } 600a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 60102ac6454SAndrew Thompson 60202ac6454SAndrew Thompson /* take care of closing the device here, if any */ 603a593f6b8SAndrew Thompson usb_fifo_close(f, 0); 60402ac6454SAndrew Thompson 6058437751dSAndrew Thompson cv_destroy(&f->cv_io); 6068437751dSAndrew Thompson cv_destroy(&f->cv_drain); 60702ac6454SAndrew Thompson 60802ac6454SAndrew Thompson free(f, M_USBDEV); 60902ac6454SAndrew Thompson } 61002ac6454SAndrew Thompson 611ae60fdfbSAndrew Thompson static struct usb_endpoint * 612a593f6b8SAndrew Thompson usb_dev_get_ep(struct usb_device *udev, uint8_t ep_index, uint8_t dir) 61302ac6454SAndrew Thompson { 614ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 61502ac6454SAndrew Thompson uint8_t ep_dir; 61602ac6454SAndrew Thompson 61702ac6454SAndrew Thompson if (ep_index == 0) { 618ae60fdfbSAndrew Thompson ep = &udev->default_ep; 61902ac6454SAndrew Thompson } else { 62002ac6454SAndrew Thompson if (dir == USB_FIFO_RX) { 621f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_HOST) { 62202ac6454SAndrew Thompson ep_dir = UE_DIR_IN; 62302ac6454SAndrew Thompson } else { 62402ac6454SAndrew Thompson ep_dir = UE_DIR_OUT; 62502ac6454SAndrew Thompson } 62602ac6454SAndrew Thompson } else { 627f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_HOST) { 62802ac6454SAndrew Thompson ep_dir = UE_DIR_OUT; 62902ac6454SAndrew Thompson } else { 63002ac6454SAndrew Thompson ep_dir = UE_DIR_IN; 63102ac6454SAndrew Thompson } 63202ac6454SAndrew Thompson } 633a593f6b8SAndrew Thompson ep = usbd_get_ep_by_addr(udev, ep_index | ep_dir); 63402ac6454SAndrew Thompson } 63502ac6454SAndrew Thompson 636ae60fdfbSAndrew Thompson if (ep == NULL) { 637ae60fdfbSAndrew Thompson /* if the endpoint does not exist then return */ 63802ac6454SAndrew Thompson return (NULL); 63902ac6454SAndrew Thompson } 640ae60fdfbSAndrew Thompson if (ep->edesc == NULL) { 641ae60fdfbSAndrew Thompson /* invalid endpoint */ 64202ac6454SAndrew Thompson return (NULL); 64302ac6454SAndrew Thompson } 644ae60fdfbSAndrew Thompson return (ep); /* success */ 64502ac6454SAndrew Thompson } 64602ac6454SAndrew Thompson 64702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 648a593f6b8SAndrew Thompson * usb_fifo_open 64902ac6454SAndrew Thompson * 65002ac6454SAndrew Thompson * Returns: 65102ac6454SAndrew Thompson * 0: Success 65202ac6454SAndrew Thompson * Else: Failure 65302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 65402ac6454SAndrew Thompson static int 655a593f6b8SAndrew Thompson usb_fifo_open(struct usb_cdev_privdata *cpd, 656760bc48eSAndrew Thompson struct usb_fifo *f, int fflags) 65702ac6454SAndrew Thompson { 65802ac6454SAndrew Thompson int err; 65902ac6454SAndrew Thompson 66002ac6454SAndrew Thompson if (f == NULL) { 66102ac6454SAndrew Thompson /* no FIFO there */ 66202ac6454SAndrew Thompson DPRINTFN(2, "no FIFO\n"); 66302ac6454SAndrew Thompson return (ENXIO); 66402ac6454SAndrew Thompson } 66502ac6454SAndrew Thompson /* remove FWRITE and FREAD flags */ 66602ac6454SAndrew Thompson fflags &= ~(FWRITE | FREAD); 66702ac6454SAndrew Thompson 66802ac6454SAndrew Thompson /* set correct file flags */ 66902ac6454SAndrew Thompson if ((f->fifo_index & 1) == USB_FIFO_TX) { 67002ac6454SAndrew Thompson fflags |= FWRITE; 67102ac6454SAndrew Thompson } else { 67202ac6454SAndrew Thompson fflags |= FREAD; 67302ac6454SAndrew Thompson } 67402ac6454SAndrew Thompson 67502ac6454SAndrew Thompson /* check if we are already opened */ 67602ac6454SAndrew Thompson /* we don't need any locks when checking this variable */ 6777214348fSAndrew Thompson if (f->curr_cpd != NULL) { 67802ac6454SAndrew Thompson err = EBUSY; 67902ac6454SAndrew Thompson goto done; 68002ac6454SAndrew Thompson } 681ee3e3ff5SAndrew Thompson 6827214348fSAndrew Thompson /* reset short flag before open */ 6837214348fSAndrew Thompson f->flag_short = 0; 6847214348fSAndrew Thompson 68502ac6454SAndrew Thompson /* call open method */ 686ee3e3ff5SAndrew Thompson err = (f->methods->f_open) (f, fflags); 68702ac6454SAndrew Thompson if (err) { 68802ac6454SAndrew Thompson goto done; 68902ac6454SAndrew Thompson } 69002ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 69102ac6454SAndrew Thompson 69202ac6454SAndrew Thompson /* reset sleep flag */ 69302ac6454SAndrew Thompson f->flag_sleeping = 0; 69402ac6454SAndrew Thompson 69502ac6454SAndrew Thompson /* reset error flag */ 69602ac6454SAndrew Thompson f->flag_iserror = 0; 69702ac6454SAndrew Thompson 69802ac6454SAndrew Thompson /* reset complete flag */ 69902ac6454SAndrew Thompson f->flag_iscomplete = 0; 70002ac6454SAndrew Thompson 70102ac6454SAndrew Thompson /* reset select flag */ 70202ac6454SAndrew Thompson f->flag_isselect = 0; 70302ac6454SAndrew Thompson 70402ac6454SAndrew Thompson /* reset flushing flag */ 70502ac6454SAndrew Thompson f->flag_flushing = 0; 70602ac6454SAndrew Thompson 70702ac6454SAndrew Thompson /* reset ASYNC proc flag */ 70802ac6454SAndrew Thompson f->async_p = NULL; 70902ac6454SAndrew Thompson 710a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 7117214348fSAndrew Thompson /* flag the fifo as opened to prevent others */ 7127214348fSAndrew Thompson f->curr_cpd = cpd; 713a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 71402ac6454SAndrew Thompson 71502ac6454SAndrew Thompson /* reset queue */ 716a593f6b8SAndrew Thompson usb_fifo_reset(f); 71702ac6454SAndrew Thompson 71802ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 71902ac6454SAndrew Thompson done: 72002ac6454SAndrew Thompson return (err); 72102ac6454SAndrew Thompson } 72202ac6454SAndrew Thompson 72302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 724a593f6b8SAndrew Thompson * usb_fifo_reset 72502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 72602ac6454SAndrew Thompson void 727a593f6b8SAndrew Thompson usb_fifo_reset(struct usb_fifo *f) 72802ac6454SAndrew Thompson { 729760bc48eSAndrew Thompson struct usb_mbuf *m; 73002ac6454SAndrew Thompson 73102ac6454SAndrew Thompson if (f == NULL) { 73202ac6454SAndrew Thompson return; 73302ac6454SAndrew Thompson } 73402ac6454SAndrew Thompson while (1) { 73502ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 73602ac6454SAndrew Thompson if (m) { 73702ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 73802ac6454SAndrew Thompson } else { 73902ac6454SAndrew Thompson break; 74002ac6454SAndrew Thompson } 74102ac6454SAndrew Thompson } 742bd73b187SAlfred Perlstein /* reset have fragment flag */ 743bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 74402ac6454SAndrew Thompson } 74502ac6454SAndrew Thompson 74602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 747a593f6b8SAndrew Thompson * usb_fifo_close 74802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 74902ac6454SAndrew Thompson static void 750a593f6b8SAndrew Thompson usb_fifo_close(struct usb_fifo *f, int fflags) 75102ac6454SAndrew Thompson { 75202ac6454SAndrew Thompson int err; 75302ac6454SAndrew Thompson 75402ac6454SAndrew Thompson /* check if we are not opened */ 7557214348fSAndrew Thompson if (f->curr_cpd == NULL) { 75602ac6454SAndrew Thompson /* nothing to do - already closed */ 75702ac6454SAndrew Thompson return; 75802ac6454SAndrew Thompson } 75902ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 76002ac6454SAndrew Thompson 7617214348fSAndrew Thompson /* clear current cdev private data pointer */ 7627214348fSAndrew Thompson f->curr_cpd = NULL; 76302ac6454SAndrew Thompson 76402ac6454SAndrew Thompson /* check if we are selected */ 76502ac6454SAndrew Thompson if (f->flag_isselect) { 76602ac6454SAndrew Thompson selwakeup(&f->selinfo); 76702ac6454SAndrew Thompson f->flag_isselect = 0; 76802ac6454SAndrew Thompson } 76902ac6454SAndrew Thompson /* check if a thread wants SIGIO */ 77002ac6454SAndrew Thompson if (f->async_p != NULL) { 77102ac6454SAndrew Thompson PROC_LOCK(f->async_p); 77202ac6454SAndrew Thompson psignal(f->async_p, SIGIO); 77302ac6454SAndrew Thompson PROC_UNLOCK(f->async_p); 77402ac6454SAndrew Thompson f->async_p = NULL; 77502ac6454SAndrew Thompson } 77602ac6454SAndrew Thompson /* remove FWRITE and FREAD flags */ 77702ac6454SAndrew Thompson fflags &= ~(FWRITE | FREAD); 77802ac6454SAndrew Thompson 77902ac6454SAndrew Thompson /* flush written data, if any */ 78002ac6454SAndrew Thompson if ((f->fifo_index & 1) == USB_FIFO_TX) { 78102ac6454SAndrew Thompson 78202ac6454SAndrew Thompson if (!f->flag_iserror) { 78302ac6454SAndrew Thompson 78402ac6454SAndrew Thompson /* set flushing flag */ 78502ac6454SAndrew Thompson f->flag_flushing = 1; 78602ac6454SAndrew Thompson 787bd73b187SAlfred Perlstein /* get the last packet in */ 788bd73b187SAlfred Perlstein if (f->flag_have_fragment) { 789bd73b187SAlfred Perlstein struct usb_mbuf *m; 790bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 791bd73b187SAlfred Perlstein USB_IF_DEQUEUE(&f->free_q, m); 792bd73b187SAlfred Perlstein if (m) { 793bd73b187SAlfred Perlstein USB_IF_ENQUEUE(&f->used_q, m); 794bd73b187SAlfred Perlstein } 795bd73b187SAlfred Perlstein } 796bd73b187SAlfred Perlstein 79702ac6454SAndrew Thompson /* start write transfer, if not already started */ 79802ac6454SAndrew Thompson (f->methods->f_start_write) (f); 79902ac6454SAndrew Thompson 80002ac6454SAndrew Thompson /* check if flushed already */ 80102ac6454SAndrew Thompson while (f->flag_flushing && 80202ac6454SAndrew Thompson (!f->flag_iserror)) { 80302ac6454SAndrew Thompson /* wait until all data has been written */ 80402ac6454SAndrew Thompson f->flag_sleeping = 1; 8058437751dSAndrew Thompson err = cv_wait_sig(&f->cv_io, f->priv_mtx); 80602ac6454SAndrew Thompson if (err) { 80702ac6454SAndrew Thompson DPRINTF("signal received\n"); 80802ac6454SAndrew Thompson break; 80902ac6454SAndrew Thompson } 81002ac6454SAndrew Thompson } 81102ac6454SAndrew Thompson } 81202ac6454SAndrew Thompson fflags |= FWRITE; 81302ac6454SAndrew Thompson 81402ac6454SAndrew Thompson /* stop write transfer, if not already stopped */ 81502ac6454SAndrew Thompson (f->methods->f_stop_write) (f); 81602ac6454SAndrew Thompson } else { 81702ac6454SAndrew Thompson fflags |= FREAD; 81802ac6454SAndrew Thompson 81902ac6454SAndrew Thompson /* stop write transfer, if not already stopped */ 82002ac6454SAndrew Thompson (f->methods->f_stop_read) (f); 82102ac6454SAndrew Thompson } 82202ac6454SAndrew Thompson 82302ac6454SAndrew Thompson /* check if we are sleeping */ 82402ac6454SAndrew Thompson if (f->flag_sleeping) { 82502ac6454SAndrew Thompson DPRINTFN(2, "Sleeping at close!\n"); 82602ac6454SAndrew Thompson } 82702ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 82802ac6454SAndrew Thompson 82902ac6454SAndrew Thompson /* call close method */ 830ee3e3ff5SAndrew Thompson (f->methods->f_close) (f, fflags); 83102ac6454SAndrew Thompson 83202ac6454SAndrew Thompson DPRINTF("closed\n"); 83302ac6454SAndrew Thompson } 83402ac6454SAndrew Thompson 83502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 836a593f6b8SAndrew Thompson * usb_open - cdev callback 83702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 83802ac6454SAndrew Thompson static int 839a593f6b8SAndrew Thompson usb_open(struct cdev *dev, int fflags, int devtype, struct thread *td) 84002ac6454SAndrew Thompson { 841760bc48eSAndrew Thompson struct usb_fs_privdata* pd = (struct usb_fs_privdata*)dev->si_drv1; 842e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 843760bc48eSAndrew Thompson struct usb_cdev_privdata *cpd; 844ee3e3ff5SAndrew Thompson int err, ep; 84502ac6454SAndrew Thompson 846cba30496SAndrew Thompson DPRINTFN(2, "%s fflags=0x%08x\n", dev->si_name, fflags); 84702ac6454SAndrew Thompson 848ee3e3ff5SAndrew Thompson KASSERT(fflags & (FREAD|FWRITE), ("invalid open flags")); 849ee3e3ff5SAndrew Thompson if (((fflags & FREAD) && !(pd->mode & FREAD)) || 850ee3e3ff5SAndrew Thompson ((fflags & FWRITE) && !(pd->mode & FWRITE))) { 851ee3e3ff5SAndrew Thompson DPRINTFN(2, "access mode not supported\n"); 85202ac6454SAndrew Thompson return (EPERM); 85302ac6454SAndrew Thompson } 854ee3e3ff5SAndrew Thompson 855ee3e3ff5SAndrew Thompson cpd = malloc(sizeof(*cpd), M_USBDEV, M_WAITOK | M_ZERO); 856ee3e3ff5SAndrew Thompson ep = cpd->ep_addr = pd->ep_addr; 857ee3e3ff5SAndrew Thompson 858a593f6b8SAndrew Thompson usb_loc_fill(pd, cpd); 859a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 1); 86002ac6454SAndrew Thompson if (err) { 86102ac6454SAndrew Thompson DPRINTFN(2, "cannot ref device\n"); 862ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 86302ac6454SAndrew Thompson return (ENXIO); 86402ac6454SAndrew Thompson } 865ee3e3ff5SAndrew Thompson cpd->fflags = fflags; /* access mode for open lifetime */ 86602ac6454SAndrew Thompson 86702ac6454SAndrew Thompson /* create FIFOs, if any */ 868a593f6b8SAndrew Thompson err = usb_fifo_create(cpd, &refs); 86902ac6454SAndrew Thompson /* check for error */ 87002ac6454SAndrew Thompson if (err) { 871ee3e3ff5SAndrew Thompson DPRINTFN(2, "cannot create fifo\n"); 872a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 873ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 87402ac6454SAndrew Thompson return (err); 87502ac6454SAndrew Thompson } 87602ac6454SAndrew Thompson if (fflags & FREAD) { 877a593f6b8SAndrew Thompson err = usb_fifo_open(cpd, refs.rxfifo, fflags); 87802ac6454SAndrew Thompson if (err) { 87902ac6454SAndrew Thompson DPRINTFN(2, "read open failed\n"); 880a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 881ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 88202ac6454SAndrew Thompson return (err); 88302ac6454SAndrew Thompson } 88402ac6454SAndrew Thompson } 88502ac6454SAndrew Thompson if (fflags & FWRITE) { 886a593f6b8SAndrew Thompson err = usb_fifo_open(cpd, refs.txfifo, fflags); 88702ac6454SAndrew Thompson if (err) { 88802ac6454SAndrew Thompson DPRINTFN(2, "write open failed\n"); 88902ac6454SAndrew Thompson if (fflags & FREAD) { 890a593f6b8SAndrew Thompson usb_fifo_close(refs.rxfifo, fflags); 89102ac6454SAndrew Thompson } 892a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 893ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 89402ac6454SAndrew Thompson return (err); 89502ac6454SAndrew Thompson } 89602ac6454SAndrew Thompson } 897a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 898a593f6b8SAndrew Thompson devfs_set_cdevpriv(cpd, usb_close); 89902ac6454SAndrew Thompson 900ee3e3ff5SAndrew Thompson return (0); 90102ac6454SAndrew Thompson } 90202ac6454SAndrew Thompson 90302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 904a593f6b8SAndrew Thompson * usb_close - cdev callback 90502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 90602ac6454SAndrew Thompson static void 907a593f6b8SAndrew Thompson usb_close(void *arg) 90802ac6454SAndrew Thompson { 909e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 910760bc48eSAndrew Thompson struct usb_cdev_privdata *cpd = arg; 911ee3e3ff5SAndrew Thompson int err; 91202ac6454SAndrew Thompson 9137214348fSAndrew Thompson DPRINTFN(2, "cpd=%p\n", cpd); 914ee3e3ff5SAndrew Thompson 915a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 1); 916ee3e3ff5SAndrew Thompson if (err) { 917ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 91802ac6454SAndrew Thompson return; 91902ac6454SAndrew Thompson } 920ee3e3ff5SAndrew Thompson if (cpd->fflags & FREAD) { 921a593f6b8SAndrew Thompson usb_fifo_close(refs.rxfifo, cpd->fflags); 92202ac6454SAndrew Thompson } 923ee3e3ff5SAndrew Thompson if (cpd->fflags & FWRITE) { 924a593f6b8SAndrew Thompson usb_fifo_close(refs.txfifo, cpd->fflags); 92502ac6454SAndrew Thompson } 926ee3e3ff5SAndrew Thompson 927a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 928ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 92902ac6454SAndrew Thompson return; 93002ac6454SAndrew Thompson } 93102ac6454SAndrew Thompson 93202ac6454SAndrew Thompson static void 933a593f6b8SAndrew Thompson usb_dev_init(void *arg) 93402ac6454SAndrew Thompson { 935a593f6b8SAndrew Thompson mtx_init(&usb_ref_lock, "USB ref mutex", NULL, MTX_DEF); 936a593f6b8SAndrew Thompson sx_init(&usb_sym_lock, "USB sym mutex"); 937a593f6b8SAndrew Thompson TAILQ_INIT(&usb_sym_head); 93802ac6454SAndrew Thompson 93902ac6454SAndrew Thompson /* check the UGEN methods */ 940a593f6b8SAndrew Thompson usb_fifo_check_methods(&usb_ugen_methods); 94102ac6454SAndrew Thompson } 94202ac6454SAndrew Thompson 943a593f6b8SAndrew Thompson SYSINIT(usb_dev_init, SI_SUB_KLD, SI_ORDER_FIRST, usb_dev_init, NULL); 94402ac6454SAndrew Thompson 94502ac6454SAndrew Thompson static void 946a593f6b8SAndrew Thompson usb_dev_init_post(void *arg) 94702ac6454SAndrew Thompson { 94802ac6454SAndrew Thompson /* 949ee3e3ff5SAndrew Thompson * Create /dev/usb - this is needed for usbconfig(8), which 950ee3e3ff5SAndrew Thompson * needs a well-known device name to access. 95102ac6454SAndrew Thompson */ 952a593f6b8SAndrew Thompson usb_dev = make_dev(&usb_static_devsw, 0, UID_ROOT, GID_OPERATOR, 953ee3e3ff5SAndrew Thompson 0644, USB_DEVICE_NAME); 954a593f6b8SAndrew Thompson if (usb_dev == NULL) { 955767cb2e2SAndrew Thompson DPRINTFN(0, "Could not create usb bus device\n"); 95602ac6454SAndrew Thompson } 95702ac6454SAndrew Thompson } 95802ac6454SAndrew Thompson 959a593f6b8SAndrew Thompson SYSINIT(usb_dev_init_post, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, usb_dev_init_post, NULL); 96002ac6454SAndrew Thompson 96102ac6454SAndrew Thompson static void 962a593f6b8SAndrew Thompson usb_dev_uninit(void *arg) 96302ac6454SAndrew Thompson { 964a593f6b8SAndrew Thompson if (usb_dev != NULL) { 965a593f6b8SAndrew Thompson destroy_dev(usb_dev); 966a593f6b8SAndrew Thompson usb_dev = NULL; 967ee3e3ff5SAndrew Thompson 96802ac6454SAndrew Thompson } 969a593f6b8SAndrew Thompson mtx_destroy(&usb_ref_lock); 970a593f6b8SAndrew Thompson sx_destroy(&usb_sym_lock); 97102ac6454SAndrew Thompson } 97202ac6454SAndrew Thompson 973a593f6b8SAndrew Thompson SYSUNINIT(usb_dev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb_dev_uninit, NULL); 97402ac6454SAndrew Thompson 97502ac6454SAndrew Thompson static int 976a593f6b8SAndrew Thompson usb_ioctl_f_sub(struct usb_fifo *f, u_long cmd, void *addr, 97702ac6454SAndrew Thompson struct thread *td) 97802ac6454SAndrew Thompson { 97902ac6454SAndrew Thompson int error = 0; 98002ac6454SAndrew Thompson 98102ac6454SAndrew Thompson switch (cmd) { 98202ac6454SAndrew Thompson case FIODTYPE: 98302ac6454SAndrew Thompson *(int *)addr = 0; /* character device */ 98402ac6454SAndrew Thompson break; 98502ac6454SAndrew Thompson 98602ac6454SAndrew Thompson case FIONBIO: 98702ac6454SAndrew Thompson /* handled by upper FS layer */ 98802ac6454SAndrew Thompson break; 98902ac6454SAndrew Thompson 99002ac6454SAndrew Thompson case FIOASYNC: 99102ac6454SAndrew Thompson if (*(int *)addr) { 99202ac6454SAndrew Thompson if (f->async_p != NULL) { 99302ac6454SAndrew Thompson error = EBUSY; 99402ac6454SAndrew Thompson break; 99502ac6454SAndrew Thompson } 99602ac6454SAndrew Thompson f->async_p = USB_TD_GET_PROC(td); 99702ac6454SAndrew Thompson } else { 99802ac6454SAndrew Thompson f->async_p = NULL; 99902ac6454SAndrew Thompson } 100002ac6454SAndrew Thompson break; 100102ac6454SAndrew Thompson 100202ac6454SAndrew Thompson /* XXX this is not the most general solution */ 100302ac6454SAndrew Thompson case TIOCSPGRP: 100402ac6454SAndrew Thompson if (f->async_p == NULL) { 100502ac6454SAndrew Thompson error = EINVAL; 100602ac6454SAndrew Thompson break; 100702ac6454SAndrew Thompson } 100802ac6454SAndrew Thompson if (*(int *)addr != USB_PROC_GET_GID(f->async_p)) { 100902ac6454SAndrew Thompson error = EPERM; 101002ac6454SAndrew Thompson break; 101102ac6454SAndrew Thompson } 101202ac6454SAndrew Thompson break; 101302ac6454SAndrew Thompson default: 101402ac6454SAndrew Thompson return (ENOIOCTL); 101502ac6454SAndrew Thompson } 1016cba30496SAndrew Thompson DPRINTFN(3, "cmd 0x%lx = %d\n", cmd, error); 101702ac6454SAndrew Thompson return (error); 101802ac6454SAndrew Thompson } 101902ac6454SAndrew Thompson 1020ee3e3ff5SAndrew Thompson /*------------------------------------------------------------------------* 1021a593f6b8SAndrew Thompson * usb_ioctl - cdev callback 1022ee3e3ff5SAndrew Thompson *------------------------------------------------------------------------*/ 102302ac6454SAndrew Thompson static int 1024a593f6b8SAndrew Thompson usb_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int fflag, struct thread* td) 102502ac6454SAndrew Thompson { 1026e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1027760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1028760bc48eSAndrew Thompson struct usb_fifo *f; 102902ac6454SAndrew Thompson int fflags; 103002ac6454SAndrew Thompson int err; 103102ac6454SAndrew Thompson 1032cba30496SAndrew Thompson DPRINTFN(2, "cmd=0x%lx\n", cmd); 1033cba30496SAndrew Thompson 1034ee3e3ff5SAndrew Thompson err = devfs_get_cdevpriv((void **)&cpd); 1035ee3e3ff5SAndrew Thompson if (err != 0) 1036ee3e3ff5SAndrew Thompson return (err); 1037ee3e3ff5SAndrew Thompson 1038f5f145baSAndrew Thompson /* 1039e13e19faSAndrew Thompson * Performance optimisation: We try to check for IOCTL's that 1040f5f145baSAndrew Thompson * don't need the USB reference first. Then we grab the USB 1041f5f145baSAndrew Thompson * reference if we need it! 1042f5f145baSAndrew Thompson */ 1043a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 1044cb18f7d1SAlfred Perlstein if (err) 104502ac6454SAndrew Thompson return (ENXIO); 1046cb18f7d1SAlfred Perlstein 1047ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 104802ac6454SAndrew Thompson 104902ac6454SAndrew Thompson f = NULL; /* set default value */ 105002ac6454SAndrew Thompson err = ENOIOCTL; /* set default value */ 105102ac6454SAndrew Thompson 105202ac6454SAndrew Thompson if (fflags & FWRITE) { 1053e13e19faSAndrew Thompson f = refs.txfifo; 1054a593f6b8SAndrew Thompson err = usb_ioctl_f_sub(f, cmd, addr, td); 105502ac6454SAndrew Thompson } 105602ac6454SAndrew Thompson if (fflags & FREAD) { 1057e13e19faSAndrew Thompson f = refs.rxfifo; 1058a593f6b8SAndrew Thompson err = usb_ioctl_f_sub(f, cmd, addr, td); 105902ac6454SAndrew Thompson } 1060ee3e3ff5SAndrew Thompson KASSERT(f != NULL, ("fifo not found")); 106102ac6454SAndrew Thompson if (err == ENOIOCTL) { 1062ee3e3ff5SAndrew Thompson err = (f->methods->f_ioctl) (f, cmd, addr, fflags); 1063cba30496SAndrew Thompson DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err); 106402ac6454SAndrew Thompson if (err == ENOIOCTL) { 1065a593f6b8SAndrew Thompson if (usb_usb_ref_device(cpd, &refs)) { 106602ac6454SAndrew Thompson err = ENXIO; 106702ac6454SAndrew Thompson goto done; 106802ac6454SAndrew Thompson } 1069ee3e3ff5SAndrew Thompson err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags); 1070cba30496SAndrew Thompson DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err); 107102ac6454SAndrew Thompson } 107202ac6454SAndrew Thompson } 107302ac6454SAndrew Thompson if (err == ENOIOCTL) { 107402ac6454SAndrew Thompson err = ENOTTY; 107502ac6454SAndrew Thompson } 107602ac6454SAndrew Thompson done: 1077a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 107802ac6454SAndrew Thompson return (err); 107902ac6454SAndrew Thompson } 108002ac6454SAndrew Thompson 108102ac6454SAndrew Thompson /* ARGSUSED */ 108202ac6454SAndrew Thompson static int 1083a593f6b8SAndrew Thompson usb_poll(struct cdev* dev, int events, struct thread* td) 108402ac6454SAndrew Thompson { 1085e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1086760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1087760bc48eSAndrew Thompson struct usb_fifo *f; 1088760bc48eSAndrew Thompson struct usb_mbuf *m; 10898a93629eSAndrew Thompson int fflags, revents; 109002ac6454SAndrew Thompson 10918a93629eSAndrew Thompson if (devfs_get_cdevpriv((void **)&cpd) != 0 || 1092a593f6b8SAndrew Thompson usb_ref_device(cpd, &refs, 0) != 0) 10938a93629eSAndrew Thompson return (events & 10948a93629eSAndrew Thompson (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); 1095ee3e3ff5SAndrew Thompson 1096ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 109702ac6454SAndrew Thompson 109802ac6454SAndrew Thompson /* Figure out who needs service */ 1099ee3e3ff5SAndrew Thompson revents = 0; 110002ac6454SAndrew Thompson if ((events & (POLLOUT | POLLWRNORM)) && 110102ac6454SAndrew Thompson (fflags & FWRITE)) { 110202ac6454SAndrew Thompson 1103e13e19faSAndrew Thompson f = refs.txfifo; 110402ac6454SAndrew Thompson 110502ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 110602ac6454SAndrew Thompson 1107e13e19faSAndrew Thompson if (!refs.is_usbfs) { 110802ac6454SAndrew Thompson if (f->flag_iserror) { 110902ac6454SAndrew Thompson /* we got an error */ 111002ac6454SAndrew Thompson m = (void *)1; 111102ac6454SAndrew Thompson } else { 111202ac6454SAndrew Thompson if (f->queue_data == NULL) { 111302ac6454SAndrew Thompson /* 111402ac6454SAndrew Thompson * start write transfer, if not 111502ac6454SAndrew Thompson * already started 111602ac6454SAndrew Thompson */ 111702ac6454SAndrew Thompson (f->methods->f_start_write) (f); 111802ac6454SAndrew Thompson } 111902ac6454SAndrew Thompson /* check if any packets are available */ 112002ac6454SAndrew Thompson USB_IF_POLL(&f->free_q, m); 112102ac6454SAndrew Thompson } 112202ac6454SAndrew Thompson } else { 112302ac6454SAndrew Thompson if (f->flag_iscomplete) { 112402ac6454SAndrew Thompson m = (void *)1; 112502ac6454SAndrew Thompson } else { 112602ac6454SAndrew Thompson m = NULL; 112702ac6454SAndrew Thompson } 112802ac6454SAndrew Thompson } 112902ac6454SAndrew Thompson 113002ac6454SAndrew Thompson if (m) { 113102ac6454SAndrew Thompson revents |= events & (POLLOUT | POLLWRNORM); 113202ac6454SAndrew Thompson } else { 113302ac6454SAndrew Thompson f->flag_isselect = 1; 113402ac6454SAndrew Thompson selrecord(td, &f->selinfo); 113502ac6454SAndrew Thompson } 113602ac6454SAndrew Thompson 113702ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 113802ac6454SAndrew Thompson } 113902ac6454SAndrew Thompson if ((events & (POLLIN | POLLRDNORM)) && 114002ac6454SAndrew Thompson (fflags & FREAD)) { 114102ac6454SAndrew Thompson 1142e13e19faSAndrew Thompson f = refs.rxfifo; 114302ac6454SAndrew Thompson 114402ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 114502ac6454SAndrew Thompson 1146e13e19faSAndrew Thompson if (!refs.is_usbfs) { 114702ac6454SAndrew Thompson if (f->flag_iserror) { 114802ac6454SAndrew Thompson /* we have and error */ 114902ac6454SAndrew Thompson m = (void *)1; 115002ac6454SAndrew Thompson } else { 115102ac6454SAndrew Thompson if (f->queue_data == NULL) { 115202ac6454SAndrew Thompson /* 115302ac6454SAndrew Thompson * start read transfer, if not 115402ac6454SAndrew Thompson * already started 115502ac6454SAndrew Thompson */ 115602ac6454SAndrew Thompson (f->methods->f_start_read) (f); 115702ac6454SAndrew Thompson } 115802ac6454SAndrew Thompson /* check if any packets are available */ 115902ac6454SAndrew Thompson USB_IF_POLL(&f->used_q, m); 116002ac6454SAndrew Thompson } 116102ac6454SAndrew Thompson } else { 116202ac6454SAndrew Thompson if (f->flag_iscomplete) { 116302ac6454SAndrew Thompson m = (void *)1; 116402ac6454SAndrew Thompson } else { 116502ac6454SAndrew Thompson m = NULL; 116602ac6454SAndrew Thompson } 116702ac6454SAndrew Thompson } 116802ac6454SAndrew Thompson 116902ac6454SAndrew Thompson if (m) { 117002ac6454SAndrew Thompson revents |= events & (POLLIN | POLLRDNORM); 117102ac6454SAndrew Thompson } else { 117202ac6454SAndrew Thompson f->flag_isselect = 1; 117302ac6454SAndrew Thompson selrecord(td, &f->selinfo); 117402ac6454SAndrew Thompson 1175e13e19faSAndrew Thompson if (!refs.is_usbfs) { 117602ac6454SAndrew Thompson /* start reading data */ 117702ac6454SAndrew Thompson (f->methods->f_start_read) (f); 117802ac6454SAndrew Thompson } 117902ac6454SAndrew Thompson } 118002ac6454SAndrew Thompson 118102ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 118202ac6454SAndrew Thompson } 1183a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 118402ac6454SAndrew Thompson return (revents); 118502ac6454SAndrew Thompson } 118602ac6454SAndrew Thompson 118702ac6454SAndrew Thompson static int 1188a593f6b8SAndrew Thompson usb_read(struct cdev *dev, struct uio *uio, int ioflag) 118902ac6454SAndrew Thompson { 1190e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1191760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1192760bc48eSAndrew Thompson struct usb_fifo *f; 1193760bc48eSAndrew Thompson struct usb_mbuf *m; 119402ac6454SAndrew Thompson int fflags; 119502ac6454SAndrew Thompson int resid; 119602ac6454SAndrew Thompson int io_len; 119702ac6454SAndrew Thompson int err; 119802ac6454SAndrew Thompson uint8_t tr_data = 0; 119902ac6454SAndrew Thompson 1200ee3e3ff5SAndrew Thompson err = devfs_get_cdevpriv((void **)&cpd); 1201ee3e3ff5SAndrew Thompson if (err != 0) 1202ee3e3ff5SAndrew Thompson return (err); 120302ac6454SAndrew Thompson 1204a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 120502ac6454SAndrew Thompson if (err) { 120602ac6454SAndrew Thompson return (ENXIO); 120702ac6454SAndrew Thompson } 1208ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 1209ee3e3ff5SAndrew Thompson 1210e13e19faSAndrew Thompson f = refs.rxfifo; 121102ac6454SAndrew Thompson if (f == NULL) { 121202ac6454SAndrew Thompson /* should not happen */ 1213a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 121402ac6454SAndrew Thompson return (EPERM); 121502ac6454SAndrew Thompson } 121602ac6454SAndrew Thompson 1217ee3e3ff5SAndrew Thompson resid = uio->uio_resid; 121802ac6454SAndrew Thompson 121902ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 122002ac6454SAndrew Thompson 122102ac6454SAndrew Thompson /* check for permanent read error */ 122202ac6454SAndrew Thompson if (f->flag_iserror) { 122302ac6454SAndrew Thompson err = EIO; 122402ac6454SAndrew Thompson goto done; 122502ac6454SAndrew Thompson } 122602ac6454SAndrew Thompson /* check if USB-FS interface is active */ 1227e13e19faSAndrew Thompson if (refs.is_usbfs) { 122802ac6454SAndrew Thompson /* 122902ac6454SAndrew Thompson * The queue is used for events that should be 123002ac6454SAndrew Thompson * retrieved using the "USB_FS_COMPLETE" ioctl. 123102ac6454SAndrew Thompson */ 123202ac6454SAndrew Thompson err = EINVAL; 123302ac6454SAndrew Thompson goto done; 123402ac6454SAndrew Thompson } 123502ac6454SAndrew Thompson while (uio->uio_resid > 0) { 123602ac6454SAndrew Thompson 123702ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 123802ac6454SAndrew Thompson 123902ac6454SAndrew Thompson if (m == NULL) { 124002ac6454SAndrew Thompson 124102ac6454SAndrew Thompson /* start read transfer, if not already started */ 124202ac6454SAndrew Thompson 124302ac6454SAndrew Thompson (f->methods->f_start_read) (f); 124402ac6454SAndrew Thompson 124507532e49SAndrew Thompson if (ioflag & IO_NDELAY) { 124602ac6454SAndrew Thompson if (tr_data) { 124702ac6454SAndrew Thompson /* return length before error */ 124802ac6454SAndrew Thompson break; 124902ac6454SAndrew Thompson } 125002ac6454SAndrew Thompson err = EWOULDBLOCK; 125102ac6454SAndrew Thompson break; 125202ac6454SAndrew Thompson } 125302ac6454SAndrew Thompson DPRINTF("sleeping\n"); 125402ac6454SAndrew Thompson 1255a593f6b8SAndrew Thompson err = usb_fifo_wait(f); 125602ac6454SAndrew Thompson if (err) { 125702ac6454SAndrew Thompson break; 125802ac6454SAndrew Thompson } 125902ac6454SAndrew Thompson continue; 126002ac6454SAndrew Thompson } 126102ac6454SAndrew Thompson if (f->methods->f_filter_read) { 126202ac6454SAndrew Thompson /* 126302ac6454SAndrew Thompson * Sometimes it is convenient to process data at the 126402ac6454SAndrew Thompson * expense of a userland process instead of a kernel 126502ac6454SAndrew Thompson * process. 126602ac6454SAndrew Thompson */ 126702ac6454SAndrew Thompson (f->methods->f_filter_read) (f, m); 126802ac6454SAndrew Thompson } 126902ac6454SAndrew Thompson tr_data = 1; 127002ac6454SAndrew Thompson 127102ac6454SAndrew Thompson io_len = MIN(m->cur_data_len, uio->uio_resid); 127202ac6454SAndrew Thompson 127302ac6454SAndrew Thompson DPRINTFN(2, "transfer %d bytes from %p\n", 127402ac6454SAndrew Thompson io_len, m->cur_data_ptr); 127502ac6454SAndrew Thompson 1276a593f6b8SAndrew Thompson err = usb_fifo_uiomove(f, 127702ac6454SAndrew Thompson m->cur_data_ptr, io_len, uio); 127802ac6454SAndrew Thompson 127902ac6454SAndrew Thompson m->cur_data_len -= io_len; 128002ac6454SAndrew Thompson m->cur_data_ptr += io_len; 128102ac6454SAndrew Thompson 128202ac6454SAndrew Thompson if (m->cur_data_len == 0) { 128302ac6454SAndrew Thompson 128402ac6454SAndrew Thompson uint8_t last_packet; 128502ac6454SAndrew Thompson 128602ac6454SAndrew Thompson last_packet = m->last_packet; 128702ac6454SAndrew Thompson 128802ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 128902ac6454SAndrew Thompson 129002ac6454SAndrew Thompson if (last_packet) { 129102ac6454SAndrew Thompson /* keep framing */ 129202ac6454SAndrew Thompson break; 129302ac6454SAndrew Thompson } 129402ac6454SAndrew Thompson } else { 129502ac6454SAndrew Thompson USB_IF_PREPEND(&f->used_q, m); 129602ac6454SAndrew Thompson } 129702ac6454SAndrew Thompson 129802ac6454SAndrew Thompson if (err) { 129902ac6454SAndrew Thompson break; 130002ac6454SAndrew Thompson } 130102ac6454SAndrew Thompson } 130202ac6454SAndrew Thompson done: 130302ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 130402ac6454SAndrew Thompson 1305a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 130602ac6454SAndrew Thompson 130702ac6454SAndrew Thompson return (err); 130802ac6454SAndrew Thompson } 130902ac6454SAndrew Thompson 131002ac6454SAndrew Thompson static int 1311a593f6b8SAndrew Thompson usb_write(struct cdev *dev, struct uio *uio, int ioflag) 131202ac6454SAndrew Thompson { 1313e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1314760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1315760bc48eSAndrew Thompson struct usb_fifo *f; 1316760bc48eSAndrew Thompson struct usb_mbuf *m; 1317bd73b187SAlfred Perlstein uint8_t *pdata; 131802ac6454SAndrew Thompson int fflags; 131902ac6454SAndrew Thompson int resid; 132002ac6454SAndrew Thompson int io_len; 132102ac6454SAndrew Thompson int err; 132202ac6454SAndrew Thompson uint8_t tr_data = 0; 132302ac6454SAndrew Thompson 132402ac6454SAndrew Thompson DPRINTFN(2, "\n"); 132502ac6454SAndrew Thompson 1326ee3e3ff5SAndrew Thompson err = devfs_get_cdevpriv((void **)&cpd); 1327ee3e3ff5SAndrew Thompson if (err != 0) 1328ee3e3ff5SAndrew Thompson return (err); 132902ac6454SAndrew Thompson 1330a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 133102ac6454SAndrew Thompson if (err) { 133202ac6454SAndrew Thompson return (ENXIO); 133302ac6454SAndrew Thompson } 1334ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 1335ee3e3ff5SAndrew Thompson 1336e13e19faSAndrew Thompson f = refs.txfifo; 133702ac6454SAndrew Thompson if (f == NULL) { 133802ac6454SAndrew Thompson /* should not happen */ 1339a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 134002ac6454SAndrew Thompson return (EPERM); 134102ac6454SAndrew Thompson } 134202ac6454SAndrew Thompson resid = uio->uio_resid; 134302ac6454SAndrew Thompson 134402ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 134502ac6454SAndrew Thompson 134602ac6454SAndrew Thompson /* check for permanent write error */ 134702ac6454SAndrew Thompson if (f->flag_iserror) { 134802ac6454SAndrew Thompson err = EIO; 134902ac6454SAndrew Thompson goto done; 135002ac6454SAndrew Thompson } 135102ac6454SAndrew Thompson /* check if USB-FS interface is active */ 1352e13e19faSAndrew Thompson if (refs.is_usbfs) { 135302ac6454SAndrew Thompson /* 135402ac6454SAndrew Thompson * The queue is used for events that should be 135502ac6454SAndrew Thompson * retrieved using the "USB_FS_COMPLETE" ioctl. 135602ac6454SAndrew Thompson */ 135702ac6454SAndrew Thompson err = EINVAL; 135802ac6454SAndrew Thompson goto done; 135902ac6454SAndrew Thompson } 136002ac6454SAndrew Thompson if (f->queue_data == NULL) { 136102ac6454SAndrew Thompson /* start write transfer, if not already started */ 136202ac6454SAndrew Thompson (f->methods->f_start_write) (f); 136302ac6454SAndrew Thompson } 136402ac6454SAndrew Thompson /* we allow writing zero length data */ 136502ac6454SAndrew Thompson do { 136602ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 136702ac6454SAndrew Thompson 136802ac6454SAndrew Thompson if (m == NULL) { 136902ac6454SAndrew Thompson 137007532e49SAndrew Thompson if (ioflag & IO_NDELAY) { 137102ac6454SAndrew Thompson if (tr_data) { 137202ac6454SAndrew Thompson /* return length before error */ 137302ac6454SAndrew Thompson break; 137402ac6454SAndrew Thompson } 137502ac6454SAndrew Thompson err = EWOULDBLOCK; 137602ac6454SAndrew Thompson break; 137702ac6454SAndrew Thompson } 137802ac6454SAndrew Thompson DPRINTF("sleeping\n"); 137902ac6454SAndrew Thompson 1380a593f6b8SAndrew Thompson err = usb_fifo_wait(f); 138102ac6454SAndrew Thompson if (err) { 138202ac6454SAndrew Thompson break; 138302ac6454SAndrew Thompson } 138402ac6454SAndrew Thompson continue; 138502ac6454SAndrew Thompson } 138602ac6454SAndrew Thompson tr_data = 1; 138702ac6454SAndrew Thompson 1388bd73b187SAlfred Perlstein if (f->flag_have_fragment == 0) { 138902ac6454SAndrew Thompson USB_MBUF_RESET(m); 1390bd73b187SAlfred Perlstein io_len = m->cur_data_len; 1391bd73b187SAlfred Perlstein pdata = m->cur_data_ptr; 1392bd73b187SAlfred Perlstein if (io_len > uio->uio_resid) 1393bd73b187SAlfred Perlstein io_len = uio->uio_resid; 139402ac6454SAndrew Thompson m->cur_data_len = io_len; 1395bd73b187SAlfred Perlstein } else { 1396bd73b187SAlfred Perlstein io_len = m->max_data_len - m->cur_data_len; 1397bd73b187SAlfred Perlstein pdata = m->cur_data_ptr + m->cur_data_len; 1398bd73b187SAlfred Perlstein if (io_len > uio->uio_resid) 1399bd73b187SAlfred Perlstein io_len = uio->uio_resid; 1400bd73b187SAlfred Perlstein m->cur_data_len += io_len; 1401bd73b187SAlfred Perlstein } 140202ac6454SAndrew Thompson 140302ac6454SAndrew Thompson DPRINTFN(2, "transfer %d bytes to %p\n", 1404bd73b187SAlfred Perlstein io_len, pdata); 140502ac6454SAndrew Thompson 1406bd73b187SAlfred Perlstein err = usb_fifo_uiomove(f, pdata, io_len, uio); 140702ac6454SAndrew Thompson 140802ac6454SAndrew Thompson if (err) { 1409bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 141002ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 141102ac6454SAndrew Thompson break; 141202ac6454SAndrew Thompson } 1413bd73b187SAlfred Perlstein 1414bd73b187SAlfred Perlstein /* check if the buffer is ready to be transmitted */ 1415bd73b187SAlfred Perlstein 1416bd73b187SAlfred Perlstein if ((f->flag_write_defrag == 0) || 1417bd73b187SAlfred Perlstein (m->cur_data_len == m->max_data_len)) { 1418bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 1419bd73b187SAlfred Perlstein 142002ac6454SAndrew Thompson /* 1421bd73b187SAlfred Perlstein * Check for write filter: 1422bd73b187SAlfred Perlstein * 1423bd73b187SAlfred Perlstein * Sometimes it is convenient to process data 1424bd73b187SAlfred Perlstein * at the expense of a userland process 1425bd73b187SAlfred Perlstein * instead of a kernel process. 142602ac6454SAndrew Thompson */ 1427bd73b187SAlfred Perlstein if (f->methods->f_filter_write) { 142802ac6454SAndrew Thompson (f->methods->f_filter_write) (f, m); 142902ac6454SAndrew Thompson } 1430bd73b187SAlfred Perlstein 1431bd73b187SAlfred Perlstein /* Put USB mbuf in the used queue */ 143202ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 143302ac6454SAndrew Thompson 1434bd73b187SAlfred Perlstein /* Start writing data, if not already started */ 143502ac6454SAndrew Thompson (f->methods->f_start_write) (f); 1436bd73b187SAlfred Perlstein } else { 1437bd73b187SAlfred Perlstein /* Wait for more data or close */ 1438bd73b187SAlfred Perlstein f->flag_have_fragment = 1; 1439bd73b187SAlfred Perlstein USB_IF_PREPEND(&f->free_q, m); 1440bd73b187SAlfred Perlstein } 144102ac6454SAndrew Thompson 144202ac6454SAndrew Thompson } while (uio->uio_resid > 0); 144302ac6454SAndrew Thompson done: 144402ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 144502ac6454SAndrew Thompson 1446a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 144702ac6454SAndrew Thompson 1448ee3e3ff5SAndrew Thompson return (err); 1449ee3e3ff5SAndrew Thompson } 145002ac6454SAndrew Thompson 1451ee3e3ff5SAndrew Thompson int 1452a593f6b8SAndrew Thompson usb_static_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 1453ee3e3ff5SAndrew Thompson struct thread *td) 1454ee3e3ff5SAndrew Thompson { 1455ee3e3ff5SAndrew Thompson union { 1456760bc48eSAndrew Thompson struct usb_read_dir *urd; 1457ee3e3ff5SAndrew Thompson void* data; 1458ee3e3ff5SAndrew Thompson } u; 1459ee3e3ff5SAndrew Thompson int err = ENOTTY; 1460ee3e3ff5SAndrew Thompson 1461ee3e3ff5SAndrew Thompson u.data = data; 1462ee3e3ff5SAndrew Thompson switch (cmd) { 1463ee3e3ff5SAndrew Thompson case USB_READ_DIR: 1464a593f6b8SAndrew Thompson err = usb_read_symlink(u.urd->urd_data, 1465ee3e3ff5SAndrew Thompson u.urd->urd_startentry, u.urd->urd_maxlen); 1466ee3e3ff5SAndrew Thompson break; 1467ee3e3ff5SAndrew Thompson case USB_DEV_QUIRK_GET: 1468ee3e3ff5SAndrew Thompson case USB_QUIRK_NAME_GET: 1469ee3e3ff5SAndrew Thompson case USB_DEV_QUIRK_ADD: 1470ee3e3ff5SAndrew Thompson case USB_DEV_QUIRK_REMOVE: 1471a593f6b8SAndrew Thompson err = usb_quirk_ioctl_p(cmd, data, fflag, td); 1472ee3e3ff5SAndrew Thompson break; 1473ee3e3ff5SAndrew Thompson case USB_GET_TEMPLATE: 1474a593f6b8SAndrew Thompson *(int *)data = usb_template; 1475ee3e3ff5SAndrew Thompson break; 1476ee3e3ff5SAndrew Thompson case USB_SET_TEMPLATE: 147750230f98SAndrew Thompson err = priv_check(curthread, PRIV_DRIVER); 1478ee3e3ff5SAndrew Thompson if (err) 1479ee3e3ff5SAndrew Thompson break; 1480a593f6b8SAndrew Thompson usb_template = *(int *)data; 1481ee3e3ff5SAndrew Thompson break; 1482ee3e3ff5SAndrew Thompson } 148302ac6454SAndrew Thompson return (err); 148402ac6454SAndrew Thompson } 148502ac6454SAndrew Thompson 148602ac6454SAndrew Thompson static int 1487a593f6b8SAndrew Thompson usb_fifo_uiomove(struct usb_fifo *f, void *cp, 148802ac6454SAndrew Thompson int n, struct uio *uio) 148902ac6454SAndrew Thompson { 149002ac6454SAndrew Thompson int error; 149102ac6454SAndrew Thompson 149202ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 149302ac6454SAndrew Thompson 149402ac6454SAndrew Thompson /* 149502ac6454SAndrew Thompson * "uiomove()" can sleep so one needs to make a wrapper, 149602ac6454SAndrew Thompson * exiting the mutex and checking things: 149702ac6454SAndrew Thompson */ 149802ac6454SAndrew Thompson error = uiomove(cp, n, uio); 149902ac6454SAndrew Thompson 150002ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 150102ac6454SAndrew Thompson 150202ac6454SAndrew Thompson return (error); 150302ac6454SAndrew Thompson } 150402ac6454SAndrew Thompson 150502ac6454SAndrew Thompson int 1506a593f6b8SAndrew Thompson usb_fifo_wait(struct usb_fifo *f) 150702ac6454SAndrew Thompson { 150802ac6454SAndrew Thompson int err; 150902ac6454SAndrew Thompson 151002ac6454SAndrew Thompson mtx_assert(f->priv_mtx, MA_OWNED); 151102ac6454SAndrew Thompson 151202ac6454SAndrew Thompson if (f->flag_iserror) { 151302ac6454SAndrew Thompson /* we are gone */ 151402ac6454SAndrew Thompson return (EIO); 151502ac6454SAndrew Thompson } 151602ac6454SAndrew Thompson f->flag_sleeping = 1; 151702ac6454SAndrew Thompson 15188437751dSAndrew Thompson err = cv_wait_sig(&f->cv_io, f->priv_mtx); 151902ac6454SAndrew Thompson 152002ac6454SAndrew Thompson if (f->flag_iserror) { 152102ac6454SAndrew Thompson /* we are gone */ 152202ac6454SAndrew Thompson err = EIO; 152302ac6454SAndrew Thompson } 152402ac6454SAndrew Thompson return (err); 152502ac6454SAndrew Thompson } 152602ac6454SAndrew Thompson 152702ac6454SAndrew Thompson void 1528a593f6b8SAndrew Thompson usb_fifo_signal(struct usb_fifo *f) 152902ac6454SAndrew Thompson { 153002ac6454SAndrew Thompson if (f->flag_sleeping) { 153102ac6454SAndrew Thompson f->flag_sleeping = 0; 15328437751dSAndrew Thompson cv_broadcast(&f->cv_io); 153302ac6454SAndrew Thompson } 153402ac6454SAndrew Thompson } 153502ac6454SAndrew Thompson 153602ac6454SAndrew Thompson void 1537a593f6b8SAndrew Thompson usb_fifo_wakeup(struct usb_fifo *f) 153802ac6454SAndrew Thompson { 1539a593f6b8SAndrew Thompson usb_fifo_signal(f); 154002ac6454SAndrew Thompson 154102ac6454SAndrew Thompson if (f->flag_isselect) { 154202ac6454SAndrew Thompson selwakeup(&f->selinfo); 154302ac6454SAndrew Thompson f->flag_isselect = 0; 154402ac6454SAndrew Thompson } 154502ac6454SAndrew Thompson if (f->async_p != NULL) { 154602ac6454SAndrew Thompson PROC_LOCK(f->async_p); 154702ac6454SAndrew Thompson psignal(f->async_p, SIGIO); 154802ac6454SAndrew Thompson PROC_UNLOCK(f->async_p); 154902ac6454SAndrew Thompson } 155002ac6454SAndrew Thompson } 155102ac6454SAndrew Thompson 155202ac6454SAndrew Thompson static int 1553a593f6b8SAndrew Thompson usb_fifo_dummy_open(struct usb_fifo *fifo, int fflags) 155402ac6454SAndrew Thompson { 155502ac6454SAndrew Thompson return (0); 155602ac6454SAndrew Thompson } 155702ac6454SAndrew Thompson 155802ac6454SAndrew Thompson static void 1559a593f6b8SAndrew Thompson usb_fifo_dummy_close(struct usb_fifo *fifo, int fflags) 156002ac6454SAndrew Thompson { 156102ac6454SAndrew Thompson return; 156202ac6454SAndrew Thompson } 156302ac6454SAndrew Thompson 156402ac6454SAndrew Thompson static int 1565a593f6b8SAndrew Thompson usb_fifo_dummy_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) 156602ac6454SAndrew Thompson { 156702ac6454SAndrew Thompson return (ENOIOCTL); 156802ac6454SAndrew Thompson } 156902ac6454SAndrew Thompson 157002ac6454SAndrew Thompson static void 1571a593f6b8SAndrew Thompson usb_fifo_dummy_cmd(struct usb_fifo *fifo) 157202ac6454SAndrew Thompson { 157302ac6454SAndrew Thompson fifo->flag_flushing = 0; /* not flushing */ 157402ac6454SAndrew Thompson } 157502ac6454SAndrew Thompson 157602ac6454SAndrew Thompson static void 1577a593f6b8SAndrew Thompson usb_fifo_check_methods(struct usb_fifo_methods *pm) 157802ac6454SAndrew Thompson { 157902ac6454SAndrew Thompson /* check that all callback functions are OK */ 158002ac6454SAndrew Thompson 158102ac6454SAndrew Thompson if (pm->f_open == NULL) 1582a593f6b8SAndrew Thompson pm->f_open = &usb_fifo_dummy_open; 158302ac6454SAndrew Thompson 158402ac6454SAndrew Thompson if (pm->f_close == NULL) 1585a593f6b8SAndrew Thompson pm->f_close = &usb_fifo_dummy_close; 158602ac6454SAndrew Thompson 158702ac6454SAndrew Thompson if (pm->f_ioctl == NULL) 1588a593f6b8SAndrew Thompson pm->f_ioctl = &usb_fifo_dummy_ioctl; 158902ac6454SAndrew Thompson 159002ac6454SAndrew Thompson if (pm->f_ioctl_post == NULL) 1591a593f6b8SAndrew Thompson pm->f_ioctl_post = &usb_fifo_dummy_ioctl; 159202ac6454SAndrew Thompson 159302ac6454SAndrew Thompson if (pm->f_start_read == NULL) 1594a593f6b8SAndrew Thompson pm->f_start_read = &usb_fifo_dummy_cmd; 159502ac6454SAndrew Thompson 159602ac6454SAndrew Thompson if (pm->f_stop_read == NULL) 1597a593f6b8SAndrew Thompson pm->f_stop_read = &usb_fifo_dummy_cmd; 159802ac6454SAndrew Thompson 159902ac6454SAndrew Thompson if (pm->f_start_write == NULL) 1600a593f6b8SAndrew Thompson pm->f_start_write = &usb_fifo_dummy_cmd; 160102ac6454SAndrew Thompson 160202ac6454SAndrew Thompson if (pm->f_stop_write == NULL) 1603a593f6b8SAndrew Thompson pm->f_stop_write = &usb_fifo_dummy_cmd; 160402ac6454SAndrew Thompson } 160502ac6454SAndrew Thompson 160602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1607a593f6b8SAndrew Thompson * usb_fifo_attach 160802ac6454SAndrew Thompson * 160902ac6454SAndrew Thompson * The following function will create a duplex FIFO. 161002ac6454SAndrew Thompson * 161102ac6454SAndrew Thompson * Return values: 161202ac6454SAndrew Thompson * 0: Success. 161302ac6454SAndrew Thompson * Else: Failure. 161402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 161502ac6454SAndrew Thompson int 1616a593f6b8SAndrew Thompson usb_fifo_attach(struct usb_device *udev, void *priv_sc, 1617760bc48eSAndrew Thompson struct mtx *priv_mtx, struct usb_fifo_methods *pm, 1618760bc48eSAndrew Thompson struct usb_fifo_sc *f_sc, uint16_t unit, uint16_t subunit, 1619ee3e3ff5SAndrew Thompson uint8_t iface_index, uid_t uid, gid_t gid, int mode) 162002ac6454SAndrew Thompson { 1621760bc48eSAndrew Thompson struct usb_fifo *f_tx; 1622760bc48eSAndrew Thompson struct usb_fifo *f_rx; 1623ee3e3ff5SAndrew Thompson char devname[32]; 162402ac6454SAndrew Thompson uint8_t n; 1625760bc48eSAndrew Thompson struct usb_fs_privdata* pd; 162602ac6454SAndrew Thompson 162702ac6454SAndrew Thompson f_sc->fp[USB_FIFO_TX] = NULL; 162802ac6454SAndrew Thompson f_sc->fp[USB_FIFO_RX] = NULL; 162902ac6454SAndrew Thompson 163002ac6454SAndrew Thompson if (pm == NULL) 163102ac6454SAndrew Thompson return (EINVAL); 163202ac6454SAndrew Thompson 163302ac6454SAndrew Thompson /* check the methods */ 1634a593f6b8SAndrew Thompson usb_fifo_check_methods(pm); 163502ac6454SAndrew Thompson 163602ac6454SAndrew Thompson if (priv_mtx == NULL) 163702ac6454SAndrew Thompson priv_mtx = &Giant; 163802ac6454SAndrew Thompson 163902ac6454SAndrew Thompson /* search for a free FIFO slot */ 164002ac6454SAndrew Thompson for (n = 0;; n += 2) { 164102ac6454SAndrew Thompson 164202ac6454SAndrew Thompson if (n == USB_FIFO_MAX) { 164302ac6454SAndrew Thompson /* end of FIFOs reached */ 164402ac6454SAndrew Thompson return (ENOMEM); 164502ac6454SAndrew Thompson } 164602ac6454SAndrew Thompson /* Check for TX FIFO */ 164702ac6454SAndrew Thompson if (udev->fifo[n + USB_FIFO_TX] != NULL) { 164802ac6454SAndrew Thompson continue; 164902ac6454SAndrew Thompson } 165002ac6454SAndrew Thompson /* Check for RX FIFO */ 165102ac6454SAndrew Thompson if (udev->fifo[n + USB_FIFO_RX] != NULL) { 165202ac6454SAndrew Thompson continue; 165302ac6454SAndrew Thompson } 165402ac6454SAndrew Thompson break; 165502ac6454SAndrew Thompson } 165602ac6454SAndrew Thompson 1657a593f6b8SAndrew Thompson f_tx = usb_fifo_alloc(); 1658a593f6b8SAndrew Thompson f_rx = usb_fifo_alloc(); 165902ac6454SAndrew Thompson 166002ac6454SAndrew Thompson if ((f_tx == NULL) || (f_rx == NULL)) { 1661a593f6b8SAndrew Thompson usb_fifo_free(f_tx); 1662a593f6b8SAndrew Thompson usb_fifo_free(f_rx); 166302ac6454SAndrew Thompson return (ENOMEM); 166402ac6454SAndrew Thompson } 166502ac6454SAndrew Thompson /* initialise FIFO structures */ 166602ac6454SAndrew Thompson 166702ac6454SAndrew Thompson f_tx->fifo_index = n + USB_FIFO_TX; 16682989a677SAndrew Thompson f_tx->dev_ep_index = -1; 166902ac6454SAndrew Thompson f_tx->priv_mtx = priv_mtx; 167002ac6454SAndrew Thompson f_tx->priv_sc0 = priv_sc; 167102ac6454SAndrew Thompson f_tx->methods = pm; 167202ac6454SAndrew Thompson f_tx->iface_index = iface_index; 167302ac6454SAndrew Thompson f_tx->udev = udev; 167402ac6454SAndrew Thompson 167502ac6454SAndrew Thompson f_rx->fifo_index = n + USB_FIFO_RX; 16762989a677SAndrew Thompson f_rx->dev_ep_index = -1; 167702ac6454SAndrew Thompson f_rx->priv_mtx = priv_mtx; 167802ac6454SAndrew Thompson f_rx->priv_sc0 = priv_sc; 167902ac6454SAndrew Thompson f_rx->methods = pm; 168002ac6454SAndrew Thompson f_rx->iface_index = iface_index; 168102ac6454SAndrew Thompson f_rx->udev = udev; 168202ac6454SAndrew Thompson 168302ac6454SAndrew Thompson f_sc->fp[USB_FIFO_TX] = f_tx; 168402ac6454SAndrew Thompson f_sc->fp[USB_FIFO_RX] = f_rx; 168502ac6454SAndrew Thompson 1686a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 168702ac6454SAndrew Thompson udev->fifo[f_tx->fifo_index] = f_tx; 168802ac6454SAndrew Thompson udev->fifo[f_rx->fifo_index] = f_rx; 1689a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 169002ac6454SAndrew Thompson 169102ac6454SAndrew Thompson for (n = 0; n != 4; n++) { 169202ac6454SAndrew Thompson 169302ac6454SAndrew Thompson if (pm->basename[n] == NULL) { 169402ac6454SAndrew Thompson continue; 169502ac6454SAndrew Thompson } 169602ac6454SAndrew Thompson if (subunit == 0xFFFF) { 1697ee3e3ff5SAndrew Thompson if (snprintf(devname, sizeof(devname), 169802ac6454SAndrew Thompson "%s%u%s", pm->basename[n], 169902ac6454SAndrew Thompson unit, pm->postfix[n] ? 170002ac6454SAndrew Thompson pm->postfix[n] : "")) { 170102ac6454SAndrew Thompson /* ignore */ 170202ac6454SAndrew Thompson } 170302ac6454SAndrew Thompson } else { 1704ee3e3ff5SAndrew Thompson if (snprintf(devname, sizeof(devname), 170502ac6454SAndrew Thompson "%s%u.%u%s", pm->basename[n], 170602ac6454SAndrew Thompson unit, subunit, pm->postfix[n] ? 170702ac6454SAndrew Thompson pm->postfix[n] : "")) { 170802ac6454SAndrew Thompson /* ignore */ 170902ac6454SAndrew Thompson } 171002ac6454SAndrew Thompson } 171102ac6454SAndrew Thompson 171202ac6454SAndrew Thompson /* 171302ac6454SAndrew Thompson * Distribute the symbolic links into two FIFO structures: 171402ac6454SAndrew Thompson */ 171502ac6454SAndrew Thompson if (n & 1) { 171602ac6454SAndrew Thompson f_rx->symlink[n / 2] = 1717a593f6b8SAndrew Thompson usb_alloc_symlink(devname); 171802ac6454SAndrew Thompson } else { 171902ac6454SAndrew Thompson f_tx->symlink[n / 2] = 1720a593f6b8SAndrew Thompson usb_alloc_symlink(devname); 172102ac6454SAndrew Thompson } 1722ee3e3ff5SAndrew Thompson 1723ee3e3ff5SAndrew Thompson /* 1724ee3e3ff5SAndrew Thompson * Initialize device private data - this is used to find the 1725ee3e3ff5SAndrew Thompson * actual USB device itself. 1726ee3e3ff5SAndrew Thompson */ 1727760bc48eSAndrew Thompson pd = malloc(sizeof(struct usb_fs_privdata), M_USBDEV, M_WAITOK | M_ZERO); 1728ee3e3ff5SAndrew Thompson pd->bus_index = device_get_unit(udev->bus->bdev); 1729ee3e3ff5SAndrew Thompson pd->dev_index = udev->device_index; 1730ee3e3ff5SAndrew Thompson pd->ep_addr = -1; /* not an endpoint */ 17317214348fSAndrew Thompson pd->fifo_index = f_tx->fifo_index & f_rx->fifo_index; 1732ee3e3ff5SAndrew Thompson pd->mode = FREAD|FWRITE; 1733ee3e3ff5SAndrew Thompson 1734ee3e3ff5SAndrew Thompson /* Now, create the device itself */ 1735a593f6b8SAndrew Thompson f_sc->dev = make_dev(&usb_devsw, 0, uid, gid, mode, 1736ee3e3ff5SAndrew Thompson devname); 17377214348fSAndrew Thompson /* XXX setting si_drv1 and creating the device is not atomic! */ 1738ee3e3ff5SAndrew Thompson f_sc->dev->si_drv1 = pd; 173902ac6454SAndrew Thompson } 174002ac6454SAndrew Thompson 174102ac6454SAndrew Thompson DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx); 174202ac6454SAndrew Thompson return (0); 174302ac6454SAndrew Thompson } 174402ac6454SAndrew Thompson 174502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1746a593f6b8SAndrew Thompson * usb_fifo_alloc_buffer 174702ac6454SAndrew Thompson * 174802ac6454SAndrew Thompson * Return values: 174902ac6454SAndrew Thompson * 0: Success 175002ac6454SAndrew Thompson * Else failure 175102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 175202ac6454SAndrew Thompson int 1753a593f6b8SAndrew Thompson usb_fifo_alloc_buffer(struct usb_fifo *f, usb_size_t bufsize, 175402ac6454SAndrew Thompson uint16_t nbuf) 175502ac6454SAndrew Thompson { 1756a593f6b8SAndrew Thompson usb_fifo_free_buffer(f); 175702ac6454SAndrew Thompson 175802ac6454SAndrew Thompson /* allocate an endpoint */ 175902ac6454SAndrew Thompson f->free_q.ifq_maxlen = nbuf; 176002ac6454SAndrew Thompson f->used_q.ifq_maxlen = nbuf; 176102ac6454SAndrew Thompson 1762a593f6b8SAndrew Thompson f->queue_data = usb_alloc_mbufs( 176302ac6454SAndrew Thompson M_USBDEV, &f->free_q, bufsize, nbuf); 176402ac6454SAndrew Thompson 176502ac6454SAndrew Thompson if ((f->queue_data == NULL) && bufsize && nbuf) { 176602ac6454SAndrew Thompson return (ENOMEM); 176702ac6454SAndrew Thompson } 176802ac6454SAndrew Thompson return (0); /* success */ 176902ac6454SAndrew Thompson } 177002ac6454SAndrew Thompson 177102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1772a593f6b8SAndrew Thompson * usb_fifo_free_buffer 177302ac6454SAndrew Thompson * 177402ac6454SAndrew Thompson * This function will free the buffers associated with a FIFO. This 177502ac6454SAndrew Thompson * function can be called multiple times in a row. 177602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 177702ac6454SAndrew Thompson void 1778a593f6b8SAndrew Thompson usb_fifo_free_buffer(struct usb_fifo *f) 177902ac6454SAndrew Thompson { 178002ac6454SAndrew Thompson if (f->queue_data) { 178102ac6454SAndrew Thompson /* free old buffer */ 178202ac6454SAndrew Thompson free(f->queue_data, M_USBDEV); 178302ac6454SAndrew Thompson f->queue_data = NULL; 178402ac6454SAndrew Thompson } 178502ac6454SAndrew Thompson /* reset queues */ 178602ac6454SAndrew Thompson 178702ac6454SAndrew Thompson bzero(&f->free_q, sizeof(f->free_q)); 178802ac6454SAndrew Thompson bzero(&f->used_q, sizeof(f->used_q)); 178902ac6454SAndrew Thompson } 179002ac6454SAndrew Thompson 1791ee3e3ff5SAndrew Thompson static void 1792a593f6b8SAndrew Thompson usb_fifo_cleanup(void* ptr) 1793ee3e3ff5SAndrew Thompson { 1794ee3e3ff5SAndrew Thompson free(ptr, M_USBDEV); 1795ee3e3ff5SAndrew Thompson } 1796ee3e3ff5SAndrew Thompson 179702ac6454SAndrew Thompson void 1798a593f6b8SAndrew Thompson usb_fifo_detach(struct usb_fifo_sc *f_sc) 179902ac6454SAndrew Thompson { 180002ac6454SAndrew Thompson if (f_sc == NULL) { 180102ac6454SAndrew Thompson return; 180202ac6454SAndrew Thompson } 1803a593f6b8SAndrew Thompson usb_fifo_free(f_sc->fp[USB_FIFO_TX]); 1804a593f6b8SAndrew Thompson usb_fifo_free(f_sc->fp[USB_FIFO_RX]); 180502ac6454SAndrew Thompson 180602ac6454SAndrew Thompson f_sc->fp[USB_FIFO_TX] = NULL; 180702ac6454SAndrew Thompson f_sc->fp[USB_FIFO_RX] = NULL; 180802ac6454SAndrew Thompson 1809ee3e3ff5SAndrew Thompson if (f_sc->dev != NULL) { 18107214348fSAndrew Thompson destroy_dev_sched_cb(f_sc->dev, 1811a593f6b8SAndrew Thompson usb_fifo_cleanup, f_sc->dev->si_drv1); 18127214348fSAndrew Thompson f_sc->dev = NULL; 1813ee3e3ff5SAndrew Thompson } 1814ee3e3ff5SAndrew Thompson 181502ac6454SAndrew Thompson DPRINTFN(2, "detached %p\n", f_sc); 181602ac6454SAndrew Thompson } 181702ac6454SAndrew Thompson 1818f9cb546cSAndrew Thompson usb_size_t 1819a593f6b8SAndrew Thompson usb_fifo_put_bytes_max(struct usb_fifo *f) 182002ac6454SAndrew Thompson { 1821760bc48eSAndrew Thompson struct usb_mbuf *m; 1822f9cb546cSAndrew Thompson usb_size_t len; 182302ac6454SAndrew Thompson 182402ac6454SAndrew Thompson USB_IF_POLL(&f->free_q, m); 182502ac6454SAndrew Thompson 182602ac6454SAndrew Thompson if (m) { 182702ac6454SAndrew Thompson len = m->max_data_len; 182802ac6454SAndrew Thompson } else { 182902ac6454SAndrew Thompson len = 0; 183002ac6454SAndrew Thompson } 183102ac6454SAndrew Thompson return (len); 183202ac6454SAndrew Thompson } 183302ac6454SAndrew Thompson 183402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1835a593f6b8SAndrew Thompson * usb_fifo_put_data 183602ac6454SAndrew Thompson * 183702ac6454SAndrew Thompson * what: 183802ac6454SAndrew Thompson * 0 - normal operation 183902ac6454SAndrew Thompson * 1 - set last packet flag to enforce framing 184002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 184102ac6454SAndrew Thompson void 1842a593f6b8SAndrew Thompson usb_fifo_put_data(struct usb_fifo *f, struct usb_page_cache *pc, 1843e0a69b51SAndrew Thompson usb_frlength_t offset, usb_frlength_t len, uint8_t what) 184402ac6454SAndrew Thompson { 1845760bc48eSAndrew Thompson struct usb_mbuf *m; 1846e0a69b51SAndrew Thompson usb_frlength_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 1857a593f6b8SAndrew Thompson usbd_copy_out(pc, offset, m->cur_data_ptr, io_len); 185802ac6454SAndrew Thompson 185902ac6454SAndrew Thompson m->cur_data_len = io_len; 186002ac6454SAndrew Thompson offset += 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 void 1880a593f6b8SAndrew Thompson usb_fifo_put_data_linear(struct usb_fifo *f, void *ptr, 1881f9cb546cSAndrew Thompson usb_size_t len, uint8_t what) 188202ac6454SAndrew Thompson { 1883760bc48eSAndrew Thompson struct usb_mbuf *m; 1884f9cb546cSAndrew Thompson usb_size_t io_len; 188502ac6454SAndrew Thompson 188602ac6454SAndrew Thompson while (len || (what == 1)) { 188702ac6454SAndrew Thompson 188802ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 188902ac6454SAndrew Thompson 189002ac6454SAndrew Thompson if (m) { 189102ac6454SAndrew Thompson USB_MBUF_RESET(m); 189202ac6454SAndrew Thompson 189302ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 189402ac6454SAndrew Thompson 189502ac6454SAndrew Thompson bcopy(ptr, m->cur_data_ptr, io_len); 189602ac6454SAndrew Thompson 189702ac6454SAndrew Thompson m->cur_data_len = io_len; 189802ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, io_len); 189902ac6454SAndrew Thompson len -= io_len; 190002ac6454SAndrew Thompson 190102ac6454SAndrew Thompson if ((len == 0) && (what == 1)) { 190202ac6454SAndrew Thompson m->last_packet = 1; 190302ac6454SAndrew Thompson } 190402ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 190502ac6454SAndrew Thompson 1906a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 190702ac6454SAndrew Thompson 190802ac6454SAndrew Thompson if ((len == 0) || (what == 1)) { 190902ac6454SAndrew Thompson break; 191002ac6454SAndrew Thompson } 191102ac6454SAndrew Thompson } else { 191202ac6454SAndrew Thompson break; 191302ac6454SAndrew Thompson } 191402ac6454SAndrew Thompson } 191502ac6454SAndrew Thompson } 191602ac6454SAndrew Thompson 191702ac6454SAndrew Thompson uint8_t 1918a593f6b8SAndrew Thompson usb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len) 191902ac6454SAndrew Thompson { 1920760bc48eSAndrew Thompson struct usb_mbuf *m; 192102ac6454SAndrew Thompson 192202ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 192302ac6454SAndrew Thompson 192402ac6454SAndrew Thompson if (m) { 192502ac6454SAndrew Thompson m->cur_data_len = len; 192602ac6454SAndrew Thompson m->cur_data_ptr = ptr; 192702ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 1928a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 192902ac6454SAndrew Thompson return (1); 193002ac6454SAndrew Thompson } 193102ac6454SAndrew Thompson return (0); 193202ac6454SAndrew Thompson } 193302ac6454SAndrew Thompson 193402ac6454SAndrew Thompson void 1935a593f6b8SAndrew Thompson usb_fifo_put_data_error(struct usb_fifo *f) 193602ac6454SAndrew Thompson { 193702ac6454SAndrew Thompson f->flag_iserror = 1; 1938a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 193902ac6454SAndrew Thompson } 194002ac6454SAndrew Thompson 194102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1942a593f6b8SAndrew Thompson * usb_fifo_get_data 194302ac6454SAndrew Thompson * 194402ac6454SAndrew Thompson * what: 194502ac6454SAndrew Thompson * 0 - normal operation 1946760bc48eSAndrew Thompson * 1 - only get one "usb_mbuf" 194702ac6454SAndrew Thompson * 194802ac6454SAndrew Thompson * returns: 194902ac6454SAndrew Thompson * 0 - no more data 195002ac6454SAndrew Thompson * 1 - data in buffer 195102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 195202ac6454SAndrew Thompson uint8_t 1953a593f6b8SAndrew Thompson usb_fifo_get_data(struct usb_fifo *f, struct usb_page_cache *pc, 1954e0a69b51SAndrew Thompson usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen, 195502ac6454SAndrew Thompson uint8_t what) 195602ac6454SAndrew Thompson { 1957760bc48eSAndrew Thompson struct usb_mbuf *m; 1958e0a69b51SAndrew Thompson usb_frlength_t io_len; 195902ac6454SAndrew Thompson uint8_t tr_data = 0; 196002ac6454SAndrew Thompson 196102ac6454SAndrew Thompson actlen[0] = 0; 196202ac6454SAndrew Thompson 196302ac6454SAndrew Thompson while (1) { 196402ac6454SAndrew Thompson 196502ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 196602ac6454SAndrew Thompson 196702ac6454SAndrew Thompson if (m) { 196802ac6454SAndrew Thompson 196902ac6454SAndrew Thompson tr_data = 1; 197002ac6454SAndrew Thompson 197102ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 197202ac6454SAndrew Thompson 1973a593f6b8SAndrew Thompson usbd_copy_in(pc, offset, m->cur_data_ptr, io_len); 197402ac6454SAndrew Thompson 197502ac6454SAndrew Thompson len -= io_len; 197602ac6454SAndrew Thompson offset += io_len; 197702ac6454SAndrew Thompson actlen[0] += io_len; 197802ac6454SAndrew Thompson m->cur_data_ptr += io_len; 197902ac6454SAndrew Thompson m->cur_data_len -= io_len; 198002ac6454SAndrew Thompson 198102ac6454SAndrew Thompson if ((m->cur_data_len == 0) || (what == 1)) { 198202ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 198302ac6454SAndrew Thompson 1984a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 198502ac6454SAndrew Thompson 198602ac6454SAndrew Thompson if (what == 1) { 198702ac6454SAndrew Thompson break; 198802ac6454SAndrew Thompson } 198902ac6454SAndrew Thompson } else { 199002ac6454SAndrew Thompson USB_IF_PREPEND(&f->used_q, m); 199102ac6454SAndrew Thompson } 199202ac6454SAndrew Thompson } else { 199302ac6454SAndrew Thompson 199402ac6454SAndrew Thompson if (tr_data) { 199502ac6454SAndrew Thompson /* wait for data to be written out */ 199602ac6454SAndrew Thompson break; 199702ac6454SAndrew Thompson } 199802ac6454SAndrew Thompson if (f->flag_flushing) { 19997214348fSAndrew Thompson /* check if we should send a short packet */ 20007214348fSAndrew Thompson if (f->flag_short != 0) { 20017214348fSAndrew Thompson f->flag_short = 0; 20027214348fSAndrew Thompson tr_data = 1; 20037214348fSAndrew Thompson break; 20047214348fSAndrew Thompson } 20057214348fSAndrew Thompson /* flushing complete */ 200602ac6454SAndrew Thompson f->flag_flushing = 0; 2007a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 200802ac6454SAndrew Thompson } 200902ac6454SAndrew Thompson break; 201002ac6454SAndrew Thompson } 201102ac6454SAndrew Thompson if (len == 0) { 201202ac6454SAndrew Thompson break; 201302ac6454SAndrew Thompson } 201402ac6454SAndrew Thompson } 201502ac6454SAndrew Thompson return (tr_data); 201602ac6454SAndrew Thompson } 201702ac6454SAndrew Thompson 201802ac6454SAndrew Thompson uint8_t 2019a593f6b8SAndrew Thompson usb_fifo_get_data_linear(struct usb_fifo *f, void *ptr, 2020f9cb546cSAndrew Thompson usb_size_t len, usb_size_t *actlen, uint8_t what) 202102ac6454SAndrew Thompson { 2022760bc48eSAndrew Thompson struct usb_mbuf *m; 2023f9cb546cSAndrew Thompson usb_size_t io_len; 202402ac6454SAndrew Thompson uint8_t tr_data = 0; 202502ac6454SAndrew Thompson 202602ac6454SAndrew Thompson actlen[0] = 0; 202702ac6454SAndrew Thompson 202802ac6454SAndrew Thompson while (1) { 202902ac6454SAndrew Thompson 203002ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 203102ac6454SAndrew Thompson 203202ac6454SAndrew Thompson if (m) { 203302ac6454SAndrew Thompson 203402ac6454SAndrew Thompson tr_data = 1; 203502ac6454SAndrew Thompson 203602ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 203702ac6454SAndrew Thompson 203802ac6454SAndrew Thompson bcopy(m->cur_data_ptr, ptr, io_len); 203902ac6454SAndrew Thompson 204002ac6454SAndrew Thompson len -= io_len; 204102ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, io_len); 204202ac6454SAndrew Thompson actlen[0] += io_len; 204302ac6454SAndrew Thompson m->cur_data_ptr += io_len; 204402ac6454SAndrew Thompson m->cur_data_len -= io_len; 204502ac6454SAndrew Thompson 204602ac6454SAndrew Thompson if ((m->cur_data_len == 0) || (what == 1)) { 204702ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 204802ac6454SAndrew Thompson 2049a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 205002ac6454SAndrew Thompson 205102ac6454SAndrew Thompson if (what == 1) { 205202ac6454SAndrew Thompson break; 205302ac6454SAndrew Thompson } 205402ac6454SAndrew Thompson } else { 205502ac6454SAndrew Thompson USB_IF_PREPEND(&f->used_q, m); 205602ac6454SAndrew Thompson } 205702ac6454SAndrew Thompson } else { 205802ac6454SAndrew Thompson 205902ac6454SAndrew Thompson if (tr_data) { 206002ac6454SAndrew Thompson /* wait for data to be written out */ 206102ac6454SAndrew Thompson break; 206202ac6454SAndrew Thompson } 206302ac6454SAndrew Thompson if (f->flag_flushing) { 20647214348fSAndrew Thompson /* check if we should send a short packet */ 20657214348fSAndrew Thompson if (f->flag_short != 0) { 20667214348fSAndrew Thompson f->flag_short = 0; 20677214348fSAndrew Thompson tr_data = 1; 20687214348fSAndrew Thompson break; 20697214348fSAndrew Thompson } 20707214348fSAndrew Thompson /* flushing complete */ 207102ac6454SAndrew Thompson f->flag_flushing = 0; 2072a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 207302ac6454SAndrew Thompson } 207402ac6454SAndrew Thompson break; 207502ac6454SAndrew Thompson } 207602ac6454SAndrew Thompson if (len == 0) { 207702ac6454SAndrew Thompson break; 207802ac6454SAndrew Thompson } 207902ac6454SAndrew Thompson } 208002ac6454SAndrew Thompson return (tr_data); 208102ac6454SAndrew Thompson } 208202ac6454SAndrew Thompson 208302ac6454SAndrew Thompson uint8_t 2084a593f6b8SAndrew Thompson usb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen) 208502ac6454SAndrew Thompson { 2086760bc48eSAndrew Thompson struct usb_mbuf *m; 208702ac6454SAndrew Thompson 208802ac6454SAndrew Thompson USB_IF_POLL(&f->used_q, m); 208902ac6454SAndrew Thompson 209002ac6454SAndrew Thompson if (m) { 209102ac6454SAndrew Thompson *plen = m->cur_data_len; 209202ac6454SAndrew Thompson *pptr = m->cur_data_ptr; 209302ac6454SAndrew Thompson 209402ac6454SAndrew Thompson return (1); 209502ac6454SAndrew Thompson } 209602ac6454SAndrew Thompson return (0); 209702ac6454SAndrew Thompson } 209802ac6454SAndrew Thompson 209902ac6454SAndrew Thompson void 2100a593f6b8SAndrew Thompson usb_fifo_get_data_error(struct usb_fifo *f) 210102ac6454SAndrew Thompson { 210202ac6454SAndrew Thompson f->flag_iserror = 1; 2103a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 210402ac6454SAndrew Thompson } 210502ac6454SAndrew Thompson 210602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2107a593f6b8SAndrew Thompson * usb_alloc_symlink 210802ac6454SAndrew Thompson * 210902ac6454SAndrew Thompson * Return values: 211002ac6454SAndrew Thompson * NULL: Failure 211102ac6454SAndrew Thompson * Else: Pointer to symlink entry 211202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 2113760bc48eSAndrew Thompson struct usb_symlink * 2114a593f6b8SAndrew Thompson usb_alloc_symlink(const char *target) 211502ac6454SAndrew Thompson { 2116760bc48eSAndrew Thompson struct usb_symlink *ps; 211702ac6454SAndrew Thompson 211802ac6454SAndrew Thompson ps = malloc(sizeof(*ps), M_USBDEV, M_WAITOK); 211902ac6454SAndrew Thompson if (ps == NULL) { 212002ac6454SAndrew Thompson return (ps); 212102ac6454SAndrew Thompson } 2122ee3e3ff5SAndrew Thompson /* XXX no longer needed */ 2123ee3e3ff5SAndrew Thompson strlcpy(ps->src_path, target, sizeof(ps->src_path)); 2124ee3e3ff5SAndrew Thompson ps->src_len = strlen(ps->src_path); 212502ac6454SAndrew Thompson strlcpy(ps->dst_path, target, sizeof(ps->dst_path)); 212602ac6454SAndrew Thompson ps->dst_len = strlen(ps->dst_path); 212702ac6454SAndrew Thompson 2128a593f6b8SAndrew Thompson sx_xlock(&usb_sym_lock); 2129a593f6b8SAndrew Thompson TAILQ_INSERT_TAIL(&usb_sym_head, ps, sym_entry); 2130a593f6b8SAndrew Thompson sx_unlock(&usb_sym_lock); 213102ac6454SAndrew Thompson return (ps); 213202ac6454SAndrew Thompson } 213302ac6454SAndrew Thompson 213402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2135a593f6b8SAndrew Thompson * usb_free_symlink 213602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 213702ac6454SAndrew Thompson void 2138a593f6b8SAndrew Thompson usb_free_symlink(struct usb_symlink *ps) 213902ac6454SAndrew Thompson { 214002ac6454SAndrew Thompson if (ps == NULL) { 214102ac6454SAndrew Thompson return; 214202ac6454SAndrew Thompson } 2143a593f6b8SAndrew Thompson sx_xlock(&usb_sym_lock); 2144a593f6b8SAndrew Thompson TAILQ_REMOVE(&usb_sym_head, ps, sym_entry); 2145a593f6b8SAndrew Thompson sx_unlock(&usb_sym_lock); 214602ac6454SAndrew Thompson 214702ac6454SAndrew Thompson free(ps, M_USBDEV); 214802ac6454SAndrew Thompson } 214902ac6454SAndrew Thompson 215002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2151a593f6b8SAndrew Thompson * usb_read_symlink 215202ac6454SAndrew Thompson * 215302ac6454SAndrew Thompson * Return value: 215402ac6454SAndrew Thompson * 0: Success 215502ac6454SAndrew Thompson * Else: Failure 215602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 215702ac6454SAndrew Thompson int 2158a593f6b8SAndrew Thompson usb_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len) 215902ac6454SAndrew Thompson { 2160760bc48eSAndrew Thompson struct usb_symlink *ps; 216102ac6454SAndrew Thompson uint32_t temp; 216202ac6454SAndrew Thompson uint32_t delta = 0; 216302ac6454SAndrew Thompson uint8_t len; 216402ac6454SAndrew Thompson int error = 0; 216502ac6454SAndrew Thompson 2166a593f6b8SAndrew Thompson sx_xlock(&usb_sym_lock); 216702ac6454SAndrew Thompson 2168a593f6b8SAndrew Thompson TAILQ_FOREACH(ps, &usb_sym_head, sym_entry) { 216902ac6454SAndrew Thompson 217002ac6454SAndrew Thompson /* 217102ac6454SAndrew Thompson * Compute total length of source and destination symlink 217202ac6454SAndrew Thompson * strings pluss one length byte and two NUL bytes: 217302ac6454SAndrew Thompson */ 217402ac6454SAndrew Thompson temp = ps->src_len + ps->dst_len + 3; 217502ac6454SAndrew Thompson 217602ac6454SAndrew Thompson if (temp > 255) { 217702ac6454SAndrew Thompson /* 217802ac6454SAndrew Thompson * Skip entry because this length cannot fit 217902ac6454SAndrew Thompson * into one byte: 218002ac6454SAndrew Thompson */ 218102ac6454SAndrew Thompson continue; 218202ac6454SAndrew Thompson } 218302ac6454SAndrew Thompson if (startentry != 0) { 218402ac6454SAndrew Thompson /* decrement read offset */ 218502ac6454SAndrew Thompson startentry--; 218602ac6454SAndrew Thompson continue; 218702ac6454SAndrew Thompson } 218802ac6454SAndrew Thompson if (temp > user_len) { 218902ac6454SAndrew Thompson /* out of buffer space */ 219002ac6454SAndrew Thompson break; 219102ac6454SAndrew Thompson } 219202ac6454SAndrew Thompson len = temp; 219302ac6454SAndrew Thompson 219402ac6454SAndrew Thompson /* copy out total length */ 219502ac6454SAndrew Thompson 219602ac6454SAndrew Thompson error = copyout(&len, 219702ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 219802ac6454SAndrew Thompson if (error) { 219902ac6454SAndrew Thompson break; 220002ac6454SAndrew Thompson } 220102ac6454SAndrew Thompson delta += 1; 220202ac6454SAndrew Thompson 220302ac6454SAndrew Thompson /* copy out source string */ 220402ac6454SAndrew Thompson 220502ac6454SAndrew Thompson error = copyout(ps->src_path, 220602ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), ps->src_len); 220702ac6454SAndrew Thompson if (error) { 220802ac6454SAndrew Thompson break; 220902ac6454SAndrew Thompson } 221002ac6454SAndrew Thompson len = 0; 221102ac6454SAndrew Thompson delta += ps->src_len; 221202ac6454SAndrew Thompson error = copyout(&len, 221302ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 221402ac6454SAndrew Thompson if (error) { 221502ac6454SAndrew Thompson break; 221602ac6454SAndrew Thompson } 221702ac6454SAndrew Thompson delta += 1; 221802ac6454SAndrew Thompson 221902ac6454SAndrew Thompson /* copy out destination string */ 222002ac6454SAndrew Thompson 222102ac6454SAndrew Thompson error = copyout(ps->dst_path, 222202ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), ps->dst_len); 222302ac6454SAndrew Thompson if (error) { 222402ac6454SAndrew Thompson break; 222502ac6454SAndrew Thompson } 222602ac6454SAndrew Thompson len = 0; 222702ac6454SAndrew Thompson delta += ps->dst_len; 222802ac6454SAndrew Thompson error = copyout(&len, 222902ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 223002ac6454SAndrew Thompson if (error) { 223102ac6454SAndrew Thompson break; 223202ac6454SAndrew Thompson } 223302ac6454SAndrew Thompson delta += 1; 223402ac6454SAndrew Thompson 223502ac6454SAndrew Thompson user_len -= temp; 223602ac6454SAndrew Thompson } 223702ac6454SAndrew Thompson 223802ac6454SAndrew Thompson /* a zero length entry indicates the end */ 223902ac6454SAndrew Thompson 224002ac6454SAndrew Thompson if ((user_len != 0) && (error == 0)) { 224102ac6454SAndrew Thompson 224202ac6454SAndrew Thompson len = 0; 224302ac6454SAndrew Thompson 224402ac6454SAndrew Thompson error = copyout(&len, 224502ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 224602ac6454SAndrew Thompson } 2247a593f6b8SAndrew Thompson sx_unlock(&usb_sym_lock); 224802ac6454SAndrew Thompson return (error); 224902ac6454SAndrew Thompson } 22507214348fSAndrew Thompson 22517214348fSAndrew Thompson void 2252a593f6b8SAndrew Thompson usb_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff) 22537214348fSAndrew Thompson { 22547214348fSAndrew Thompson if (f == NULL) 22557214348fSAndrew Thompson return; 22567214348fSAndrew Thompson 22577214348fSAndrew Thompson /* send a Zero Length Packet, ZLP, before close */ 22587214348fSAndrew Thompson f->flag_short = onoff; 22597214348fSAndrew Thompson } 2260ed6d949aSAndrew Thompson 2261bd73b187SAlfred Perlstein void 2262bd73b187SAlfred Perlstein usb_fifo_set_write_defrag(struct usb_fifo *f, uint8_t onoff) 2263bd73b187SAlfred Perlstein { 2264bd73b187SAlfred Perlstein if (f == NULL) 2265bd73b187SAlfred Perlstein return; 2266bd73b187SAlfred Perlstein 2267bd73b187SAlfred Perlstein /* defrag written data */ 2268bd73b187SAlfred Perlstein f->flag_write_defrag = onoff; 2269bd73b187SAlfred Perlstein /* reset defrag state */ 2270bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 2271bd73b187SAlfred Perlstein } 2272bd73b187SAlfred Perlstein 2273ed6d949aSAndrew Thompson void * 2274ed6d949aSAndrew Thompson usb_fifo_softc(struct usb_fifo *f) 2275ed6d949aSAndrew Thompson { 2276ed6d949aSAndrew Thompson return (f->priv_sc0); 2277ed6d949aSAndrew Thompson } 22788755859aSAndrew Thompson #endif /* USB_HAVE_UGEN */ 2279