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 30d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 31d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 32d2b99310SHans Petter Selasky #else 33ed6d949aSAndrew Thompson #include <sys/stdint.h> 34ed6d949aSAndrew Thompson #include <sys/stddef.h> 35ed6d949aSAndrew Thompson #include <sys/param.h> 36ed6d949aSAndrew Thompson #include <sys/queue.h> 37ed6d949aSAndrew Thompson #include <sys/types.h> 38ed6d949aSAndrew Thompson #include <sys/systm.h> 39ed6d949aSAndrew Thompson #include <sys/kernel.h> 40ed6d949aSAndrew Thompson #include <sys/bus.h> 41ed6d949aSAndrew Thompson #include <sys/module.h> 42ed6d949aSAndrew Thompson #include <sys/lock.h> 43ed6d949aSAndrew Thompson #include <sys/mutex.h> 44ed6d949aSAndrew Thompson #include <sys/condvar.h> 45ed6d949aSAndrew Thompson #include <sys/sysctl.h> 46ed6d949aSAndrew Thompson #include <sys/sx.h> 47ed6d949aSAndrew Thompson #include <sys/unistd.h> 48ed6d949aSAndrew Thompson #include <sys/callout.h> 49ed6d949aSAndrew Thompson #include <sys/malloc.h> 50ed6d949aSAndrew Thompson #include <sys/priv.h> 51ed6d949aSAndrew Thompson #include <sys/vnode.h> 52ed6d949aSAndrew Thompson #include <sys/conf.h> 53ed6d949aSAndrew Thompson #include <sys/fcntl.h> 54ed6d949aSAndrew Thompson 5502ac6454SAndrew Thompson #include <dev/usb/usb.h> 5602ac6454SAndrew Thompson #include <dev/usb/usb_ioctl.h> 57ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 58ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 5902ac6454SAndrew Thompson 60a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_fifo_debug 6102ac6454SAndrew Thompson 6202ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 6302ac6454SAndrew Thompson #include <dev/usb/usb_dev.h> 64ed6d949aSAndrew Thompson #include <dev/usb/usb_mbuf.h> 6502ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 6602ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 6702ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6802ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 6902ac6454SAndrew Thompson #include <dev/usb/usb_generic.h> 7002ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 7102ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 7202ac6454SAndrew Thompson 7302ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 7402ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 7502ac6454SAndrew Thompson 7602ac6454SAndrew Thompson #include <sys/filio.h> 7702ac6454SAndrew Thompson #include <sys/ttycom.h> 7802ac6454SAndrew Thompson #include <sys/syscallsubr.h> 7902ac6454SAndrew Thompson 8002ac6454SAndrew Thompson #include <machine/stdarg.h> 81d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 8202ac6454SAndrew Thompson 838755859aSAndrew Thompson #if USB_HAVE_UGEN 848755859aSAndrew Thompson 85ed6d949aSAndrew Thompson #ifdef USB_DEBUG 86a593f6b8SAndrew Thompson static int usb_fifo_debug = 0; 8702ac6454SAndrew Thompson 886472ac3dSEd Schouten static SYSCTL_NODE(_hw_usb, OID_AUTO, dev, CTLFLAG_RW, 0, "USB device"); 8983cadd7dSHans Petter Selasky SYSCTL_INT(_hw_usb_dev, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, 90a593f6b8SAndrew Thompson &usb_fifo_debug, 0, "Debug Level"); 91c13fd8d4SAndrew Thompson TUNABLE_INT("hw.usb.dev.debug", &usb_fifo_debug); 9202ac6454SAndrew Thompson #endif 9302ac6454SAndrew Thompson 9402ac6454SAndrew Thompson #if ((__FreeBSD_version >= 700001) || (__FreeBSD_version == 0) || \ 9502ac6454SAndrew Thompson ((__FreeBSD_version >= 600034) && (__FreeBSD_version < 700000))) 9602ac6454SAndrew Thompson #define USB_UCRED struct ucred *ucred, 9702ac6454SAndrew Thompson #else 9802ac6454SAndrew Thompson #define USB_UCRED 9902ac6454SAndrew Thompson #endif 10002ac6454SAndrew Thompson 10102ac6454SAndrew Thompson /* prototypes */ 10202ac6454SAndrew Thompson 103a593f6b8SAndrew Thompson static int usb_fifo_open(struct usb_cdev_privdata *, 104760bc48eSAndrew Thompson struct usb_fifo *, int); 105a593f6b8SAndrew Thompson static void usb_fifo_close(struct usb_fifo *, int); 106a593f6b8SAndrew Thompson static void usb_dev_init(void *); 107a593f6b8SAndrew Thompson static void usb_dev_init_post(void *); 108a593f6b8SAndrew Thompson static void usb_dev_uninit(void *); 109a593f6b8SAndrew Thompson static int usb_fifo_uiomove(struct usb_fifo *, void *, int, 11002ac6454SAndrew Thompson struct uio *); 111a593f6b8SAndrew Thompson static void usb_fifo_check_methods(struct usb_fifo_methods *); 112a593f6b8SAndrew Thompson static struct usb_fifo *usb_fifo_alloc(void); 113a593f6b8SAndrew Thompson static struct usb_endpoint *usb_dev_get_ep(struct usb_device *, uint8_t, 114f5f145baSAndrew Thompson uint8_t); 115a593f6b8SAndrew Thompson static void usb_loc_fill(struct usb_fs_privdata *, 116760bc48eSAndrew Thompson struct usb_cdev_privdata *); 117a593f6b8SAndrew Thompson static void usb_close(void *); 118a593f6b8SAndrew Thompson static usb_error_t usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *, int); 119a593f6b8SAndrew Thompson static usb_error_t usb_usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *); 120a593f6b8SAndrew Thompson static void usb_unref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *); 12102ac6454SAndrew Thompson 122a593f6b8SAndrew Thompson static d_open_t usb_open; 123a593f6b8SAndrew Thompson static d_ioctl_t usb_ioctl; 124a593f6b8SAndrew Thompson static d_read_t usb_read; 125a593f6b8SAndrew Thompson static d_write_t usb_write; 126a593f6b8SAndrew Thompson static d_poll_t usb_poll; 12702ac6454SAndrew Thompson 128a593f6b8SAndrew Thompson static d_ioctl_t usb_static_ioctl; 12902ac6454SAndrew Thompson 130a593f6b8SAndrew Thompson static usb_fifo_open_t usb_fifo_dummy_open; 131a593f6b8SAndrew Thompson static usb_fifo_close_t usb_fifo_dummy_close; 132a593f6b8SAndrew Thompson static usb_fifo_ioctl_t usb_fifo_dummy_ioctl; 133a593f6b8SAndrew Thompson static usb_fifo_cmd_t usb_fifo_dummy_cmd; 13402ac6454SAndrew Thompson 135ee3e3ff5SAndrew Thompson /* character device structure used for devices (/dev/ugenX.Y and /dev/uXXX) */ 136a593f6b8SAndrew Thompson struct cdevsw usb_devsw = { 13702ac6454SAndrew Thompson .d_version = D_VERSION, 138a593f6b8SAndrew Thompson .d_open = usb_open, 139a593f6b8SAndrew Thompson .d_ioctl = usb_ioctl, 140ee3e3ff5SAndrew Thompson .d_name = "usbdev", 14102ac6454SAndrew Thompson .d_flags = D_TRACKCLOSE, 142a593f6b8SAndrew Thompson .d_read = usb_read, 143a593f6b8SAndrew Thompson .d_write = usb_write, 144a593f6b8SAndrew Thompson .d_poll = usb_poll 14502ac6454SAndrew Thompson }; 14602ac6454SAndrew Thompson 147a593f6b8SAndrew Thompson static struct cdev* usb_dev = NULL; 148ee3e3ff5SAndrew Thompson 149ee3e3ff5SAndrew Thompson /* character device structure used for /dev/usb */ 150a593f6b8SAndrew Thompson static struct cdevsw usb_static_devsw = { 151ee3e3ff5SAndrew Thompson .d_version = D_VERSION, 152a593f6b8SAndrew Thompson .d_ioctl = usb_static_ioctl, 153ee3e3ff5SAndrew Thompson .d_name = "usb" 15402ac6454SAndrew Thompson }; 15502ac6454SAndrew Thompson 156a593f6b8SAndrew Thompson static TAILQ_HEAD(, usb_symlink) usb_sym_head; 157a593f6b8SAndrew Thompson static struct sx usb_sym_lock; 15802ac6454SAndrew Thompson 159a593f6b8SAndrew Thompson struct mtx usb_ref_lock; 16002ac6454SAndrew Thompson 16102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 162a593f6b8SAndrew Thompson * usb_loc_fill 16302ac6454SAndrew Thompson * 164760bc48eSAndrew Thompson * This is used to fill out a usb_cdev_privdata structure based on the 165760bc48eSAndrew Thompson * device's address as contained in usb_fs_privdata. 16602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 167ee3e3ff5SAndrew Thompson static void 168a593f6b8SAndrew Thompson usb_loc_fill(struct usb_fs_privdata* pd, struct usb_cdev_privdata *cpd) 16902ac6454SAndrew Thompson { 170ee3e3ff5SAndrew Thompson cpd->bus_index = pd->bus_index; 171ee3e3ff5SAndrew Thompson cpd->dev_index = pd->dev_index; 172ee3e3ff5SAndrew Thompson cpd->ep_addr = pd->ep_addr; 173ee3e3ff5SAndrew Thompson cpd->fifo_index = pd->fifo_index; 17402ac6454SAndrew Thompson } 17502ac6454SAndrew Thompson 17602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 177a593f6b8SAndrew Thompson * usb_ref_device 17802ac6454SAndrew Thompson * 17902ac6454SAndrew Thompson * This function is used to atomically refer an USB device by its 18002ac6454SAndrew Thompson * device location. If this function returns success the USB device 18102ac6454SAndrew Thompson * will not dissappear until the USB device is unreferenced. 18202ac6454SAndrew Thompson * 18302ac6454SAndrew Thompson * Return values: 18402ac6454SAndrew Thompson * 0: Success, refcount incremented on the given USB device. 18502ac6454SAndrew Thompson * Else: Failure. 18602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 1878019e7e7SAndrew Thompson static usb_error_t 188a593f6b8SAndrew Thompson usb_ref_device(struct usb_cdev_privdata *cpd, 189e13e19faSAndrew Thompson struct usb_cdev_refdata *crd, int need_uref) 19002ac6454SAndrew Thompson { 191760bc48eSAndrew Thompson struct usb_fifo **ppf; 192760bc48eSAndrew Thompson struct usb_fifo *f; 19302ac6454SAndrew Thompson 194e13e19faSAndrew Thompson DPRINTFN(2, "cpd=%p need uref=%d\n", cpd, need_uref); 195e13e19faSAndrew Thompson 196e13e19faSAndrew Thompson /* clear all refs */ 197e13e19faSAndrew Thompson memset(crd, 0, sizeof(*crd)); 19802ac6454SAndrew Thompson 199a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 200a593f6b8SAndrew Thompson cpd->bus = devclass_get_softc(usb_devclass_ptr, cpd->bus_index); 201ee3e3ff5SAndrew Thompson if (cpd->bus == NULL) { 202ee3e3ff5SAndrew Thompson DPRINTFN(2, "no bus at %u\n", cpd->bus_index); 20302ac6454SAndrew Thompson goto error; 20402ac6454SAndrew Thompson } 205ee3e3ff5SAndrew Thompson cpd->udev = cpd->bus->devices[cpd->dev_index]; 206ee3e3ff5SAndrew Thompson if (cpd->udev == NULL) { 207ee3e3ff5SAndrew Thompson DPRINTFN(2, "no device at %u\n", cpd->dev_index); 20802ac6454SAndrew Thompson goto error; 20902ac6454SAndrew Thompson } 210f97da128SHans Petter Selasky if (cpd->udev->state == USB_STATE_DETACHED && 211f97da128SHans Petter Selasky (need_uref != 2)) { 212f97da128SHans Petter Selasky DPRINTFN(2, "device is detached\n"); 213f97da128SHans Petter Selasky goto error; 214f97da128SHans Petter Selasky } 215ee3e3ff5SAndrew Thompson if (cpd->udev->refcount == USB_DEV_REF_MAX) { 21602ac6454SAndrew Thompson DPRINTFN(2, "no dev ref\n"); 21702ac6454SAndrew Thompson goto error; 21802ac6454SAndrew Thompson } 2190ed53d45SAndrew Thompson if (need_uref) { 2200ed53d45SAndrew Thompson DPRINTFN(2, "ref udev - needed\n"); 2210ed53d45SAndrew Thompson cpd->udev->refcount++; 2220ed53d45SAndrew Thompson 223a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 2240ed53d45SAndrew Thompson 2250ed53d45SAndrew Thompson /* 226a18a7a41SHans Petter Selasky * We need to grab the enumeration SX-lock before 227a18a7a41SHans Petter Selasky * grabbing the FIFO refs to avoid deadlock at detach! 2280ed53d45SAndrew Thompson */ 229a18a7a41SHans Petter Selasky crd->do_unlock = usbd_enum_lock(cpd->udev); 2300ed53d45SAndrew Thompson 231a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 232a488edb5SAndrew Thompson 233a488edb5SAndrew Thompson /* 234a488edb5SAndrew Thompson * Set "is_uref" after grabbing the default SX lock 235a488edb5SAndrew Thompson */ 236e13e19faSAndrew Thompson crd->is_uref = 1; 2370ed53d45SAndrew Thompson } 2380ed53d45SAndrew Thompson 23902ac6454SAndrew Thompson /* check if we are doing an open */ 240ee3e3ff5SAndrew Thompson if (cpd->fflags == 0) { 241e13e19faSAndrew Thompson /* use zero defaults */ 24202ac6454SAndrew Thompson } else { 24302ac6454SAndrew Thompson /* check for write */ 244ee3e3ff5SAndrew Thompson if (cpd->fflags & FWRITE) { 245ee3e3ff5SAndrew Thompson ppf = cpd->udev->fifo; 246ee3e3ff5SAndrew Thompson f = ppf[cpd->fifo_index + USB_FIFO_TX]; 247e13e19faSAndrew Thompson crd->txfifo = f; 248e13e19faSAndrew Thompson crd->is_write = 1; /* ref */ 249ee3e3ff5SAndrew Thompson if (f == NULL || f->refcount == USB_FIFO_REF_MAX) 25002ac6454SAndrew Thompson goto error; 2517214348fSAndrew Thompson if (f->curr_cpd != cpd) 2527214348fSAndrew Thompson goto error; 25302ac6454SAndrew Thompson /* check if USB-FS is active */ 25402ac6454SAndrew Thompson if (f->fs_ep_max != 0) { 255e13e19faSAndrew Thompson crd->is_usbfs = 1; 25602ac6454SAndrew Thompson } 25702ac6454SAndrew Thompson } 25802ac6454SAndrew Thompson 25902ac6454SAndrew Thompson /* check for read */ 260ee3e3ff5SAndrew Thompson if (cpd->fflags & FREAD) { 261ee3e3ff5SAndrew Thompson ppf = cpd->udev->fifo; 262ee3e3ff5SAndrew Thompson f = ppf[cpd->fifo_index + USB_FIFO_RX]; 263e13e19faSAndrew Thompson crd->rxfifo = f; 264e13e19faSAndrew Thompson crd->is_read = 1; /* ref */ 265ee3e3ff5SAndrew Thompson if (f == NULL || f->refcount == USB_FIFO_REF_MAX) 26602ac6454SAndrew Thompson goto error; 2677214348fSAndrew Thompson if (f->curr_cpd != cpd) 2687214348fSAndrew Thompson goto error; 26902ac6454SAndrew Thompson /* check if USB-FS is active */ 27002ac6454SAndrew Thompson if (f->fs_ep_max != 0) { 271e13e19faSAndrew Thompson crd->is_usbfs = 1; 27202ac6454SAndrew Thompson } 27302ac6454SAndrew Thompson } 27402ac6454SAndrew Thompson } 27502ac6454SAndrew Thompson 27602ac6454SAndrew Thompson /* when everything is OK we increment the refcounts */ 277e13e19faSAndrew Thompson if (crd->is_write) { 27802ac6454SAndrew Thompson DPRINTFN(2, "ref write\n"); 279e13e19faSAndrew Thompson crd->txfifo->refcount++; 28002ac6454SAndrew Thompson } 281e13e19faSAndrew Thompson if (crd->is_read) { 28202ac6454SAndrew Thompson DPRINTFN(2, "ref read\n"); 283e13e19faSAndrew Thompson crd->rxfifo->refcount++; 28402ac6454SAndrew Thompson } 285a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 28602ac6454SAndrew Thompson 28702ac6454SAndrew Thompson return (0); 28802ac6454SAndrew Thompson 28902ac6454SAndrew Thompson error: 290a18a7a41SHans Petter Selasky if (crd->do_unlock) 291cb18f7d1SAlfred Perlstein usbd_enum_unlock(cpd->udev); 292cb18f7d1SAlfred Perlstein 293a18a7a41SHans Petter Selasky if (crd->is_uref) { 2940ed53d45SAndrew Thompson if (--(cpd->udev->refcount) == 0) { 29591cd9240SAndrew Thompson cv_signal(&cpd->udev->ref_cv); 2960ed53d45SAndrew Thompson } 2970ed53d45SAndrew Thompson } 298a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 29902ac6454SAndrew Thompson DPRINTFN(2, "fail\n"); 30002ac6454SAndrew Thompson return (USB_ERR_INVAL); 30102ac6454SAndrew Thompson } 30202ac6454SAndrew Thompson 30302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 304a593f6b8SAndrew Thompson * usb_usb_ref_device 30502ac6454SAndrew Thompson * 30602ac6454SAndrew Thompson * This function is used to upgrade an USB reference to include the 30702ac6454SAndrew Thompson * USB device reference on a USB location. 30802ac6454SAndrew Thompson * 30902ac6454SAndrew Thompson * Return values: 31002ac6454SAndrew Thompson * 0: Success, refcount incremented on the given USB device. 31102ac6454SAndrew Thompson * Else: Failure. 31202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 313e0a69b51SAndrew Thompson static usb_error_t 314a593f6b8SAndrew Thompson usb_usb_ref_device(struct usb_cdev_privdata *cpd, 315e13e19faSAndrew Thompson struct usb_cdev_refdata *crd) 31602ac6454SAndrew Thompson { 31702ac6454SAndrew Thompson /* 31802ac6454SAndrew Thompson * Check if we already got an USB reference on this location: 31902ac6454SAndrew Thompson */ 320e13e19faSAndrew Thompson if (crd->is_uref) 32102ac6454SAndrew Thompson return (0); /* success */ 32202ac6454SAndrew Thompson 32302ac6454SAndrew Thompson /* 3240ed53d45SAndrew Thompson * To avoid deadlock at detach we need to drop the FIFO ref 3250ed53d45SAndrew Thompson * and re-acquire a new ref! 32602ac6454SAndrew Thompson */ 327a593f6b8SAndrew Thompson usb_unref_device(cpd, crd); 32802ac6454SAndrew Thompson 329a593f6b8SAndrew Thompson return (usb_ref_device(cpd, crd, 1 /* need uref */)); 33002ac6454SAndrew Thompson } 33102ac6454SAndrew Thompson 33202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 333a593f6b8SAndrew Thompson * usb_unref_device 33402ac6454SAndrew Thompson * 33502ac6454SAndrew Thompson * This function will release the reference count by one unit for the 33602ac6454SAndrew Thompson * given USB device. 33702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 3388019e7e7SAndrew Thompson static void 339a593f6b8SAndrew Thompson usb_unref_device(struct usb_cdev_privdata *cpd, 340e13e19faSAndrew Thompson struct usb_cdev_refdata *crd) 34102ac6454SAndrew Thompson { 342a488edb5SAndrew Thompson 343e13e19faSAndrew Thompson DPRINTFN(2, "cpd=%p is_uref=%d\n", cpd, crd->is_uref); 344a488edb5SAndrew Thompson 345a18a7a41SHans Petter Selasky if (crd->do_unlock) 346cb18f7d1SAlfred Perlstein usbd_enum_unlock(cpd->udev); 347cb18f7d1SAlfred Perlstein 348a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 349e13e19faSAndrew Thompson if (crd->is_read) { 350e13e19faSAndrew Thompson if (--(crd->rxfifo->refcount) == 0) { 3518437751dSAndrew Thompson cv_signal(&crd->rxfifo->cv_drain); 35202ac6454SAndrew Thompson } 353e13e19faSAndrew Thompson crd->is_read = 0; 35402ac6454SAndrew Thompson } 355e13e19faSAndrew Thompson if (crd->is_write) { 356e13e19faSAndrew Thompson if (--(crd->txfifo->refcount) == 0) { 3578437751dSAndrew Thompson cv_signal(&crd->txfifo->cv_drain); 35802ac6454SAndrew Thompson } 359e13e19faSAndrew Thompson crd->is_write = 0; 36002ac6454SAndrew Thompson } 361e13e19faSAndrew Thompson if (crd->is_uref) { 362ee3e3ff5SAndrew Thompson if (--(cpd->udev->refcount) == 0) { 36391cd9240SAndrew Thompson cv_signal(&cpd->udev->ref_cv); 36402ac6454SAndrew Thompson } 365e13e19faSAndrew Thompson crd->is_uref = 0; 36602ac6454SAndrew Thompson } 367a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 36802ac6454SAndrew Thompson } 36902ac6454SAndrew Thompson 370760bc48eSAndrew Thompson static struct usb_fifo * 371a593f6b8SAndrew Thompson usb_fifo_alloc(void) 37202ac6454SAndrew Thompson { 373760bc48eSAndrew Thompson struct usb_fifo *f; 37402ac6454SAndrew Thompson 37502ac6454SAndrew Thompson f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO); 37602ac6454SAndrew Thompson if (f) { 3778437751dSAndrew Thompson cv_init(&f->cv_io, "FIFO-IO"); 3788437751dSAndrew Thompson cv_init(&f->cv_drain, "FIFO-DRAIN"); 37902ac6454SAndrew Thompson f->refcount = 1; 38002ac6454SAndrew Thompson } 38102ac6454SAndrew Thompson return (f); 38202ac6454SAndrew Thompson } 38302ac6454SAndrew Thompson 38402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 385a593f6b8SAndrew Thompson * usb_fifo_create 38602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 38702ac6454SAndrew Thompson static int 388a593f6b8SAndrew Thompson usb_fifo_create(struct usb_cdev_privdata *cpd, 389e13e19faSAndrew Thompson struct usb_cdev_refdata *crd) 39002ac6454SAndrew Thompson { 391760bc48eSAndrew Thompson struct usb_device *udev = cpd->udev; 392760bc48eSAndrew Thompson struct usb_fifo *f; 393ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 39402ac6454SAndrew Thompson uint8_t n; 39502ac6454SAndrew Thompson uint8_t is_tx; 39602ac6454SAndrew Thompson uint8_t is_rx; 39702ac6454SAndrew Thompson uint8_t no_null; 39802ac6454SAndrew Thompson uint8_t is_busy; 399ae60fdfbSAndrew Thompson int e = cpd->ep_addr; 40002ac6454SAndrew Thompson 401ee3e3ff5SAndrew Thompson is_tx = (cpd->fflags & FWRITE) ? 1 : 0; 402ee3e3ff5SAndrew Thompson is_rx = (cpd->fflags & FREAD) ? 1 : 0; 40302ac6454SAndrew Thompson no_null = 1; 40402ac6454SAndrew Thompson is_busy = 0; 40502ac6454SAndrew Thompson 406ee3e3ff5SAndrew Thompson /* Preallocated FIFO */ 407ae60fdfbSAndrew Thompson if (e < 0) { 408ee3e3ff5SAndrew Thompson DPRINTFN(5, "Preallocated FIFO\n"); 409ee3e3ff5SAndrew Thompson if (is_tx) { 410ee3e3ff5SAndrew Thompson f = udev->fifo[cpd->fifo_index + USB_FIFO_TX]; 411ee3e3ff5SAndrew Thompson if (f == NULL) 412ee3e3ff5SAndrew Thompson return (EINVAL); 413e13e19faSAndrew Thompson crd->txfifo = f; 414ee3e3ff5SAndrew Thompson } 415ee3e3ff5SAndrew Thompson if (is_rx) { 416ee3e3ff5SAndrew Thompson f = udev->fifo[cpd->fifo_index + USB_FIFO_RX]; 417ee3e3ff5SAndrew Thompson if (f == NULL) 418ee3e3ff5SAndrew Thompson return (EINVAL); 419e13e19faSAndrew Thompson crd->rxfifo = f; 420ee3e3ff5SAndrew Thompson } 421ee3e3ff5SAndrew Thompson return (0); 422ee3e3ff5SAndrew Thompson } 42302ac6454SAndrew Thompson 424ae60fdfbSAndrew Thompson KASSERT(e >= 0 && e <= 15, ("endpoint %d out of range", e)); 425ee3e3ff5SAndrew Thompson 426ee3e3ff5SAndrew Thompson /* search for a free FIFO slot */ 427ae60fdfbSAndrew Thompson DPRINTFN(5, "Endpoint device, searching for 0x%02x\n", e); 42802ac6454SAndrew Thompson for (n = 0;; n += 2) { 42902ac6454SAndrew Thompson 43002ac6454SAndrew Thompson if (n == USB_FIFO_MAX) { 43102ac6454SAndrew Thompson if (no_null) { 43202ac6454SAndrew Thompson no_null = 0; 43302ac6454SAndrew Thompson n = 0; 43402ac6454SAndrew Thompson } else { 43502ac6454SAndrew Thompson /* end of FIFOs reached */ 436ee3e3ff5SAndrew Thompson DPRINTFN(5, "out of FIFOs\n"); 43702ac6454SAndrew Thompson return (ENOMEM); 43802ac6454SAndrew Thompson } 43902ac6454SAndrew Thompson } 44002ac6454SAndrew Thompson /* Check for TX FIFO */ 44102ac6454SAndrew Thompson if (is_tx) { 44202ac6454SAndrew Thompson f = udev->fifo[n + USB_FIFO_TX]; 44302ac6454SAndrew Thompson if (f != NULL) { 444ae60fdfbSAndrew Thompson if (f->dev_ep_index != e) { 44502ac6454SAndrew Thompson /* wrong endpoint index */ 44602ac6454SAndrew Thompson continue; 44702ac6454SAndrew Thompson } 4487214348fSAndrew Thompson if (f->curr_cpd != NULL) { 44902ac6454SAndrew Thompson /* FIFO is opened */ 45002ac6454SAndrew Thompson is_busy = 1; 45102ac6454SAndrew Thompson continue; 45202ac6454SAndrew Thompson } 45302ac6454SAndrew Thompson } else if (no_null) { 45402ac6454SAndrew Thompson continue; 45502ac6454SAndrew Thompson } 45602ac6454SAndrew Thompson } 45702ac6454SAndrew Thompson /* Check for RX FIFO */ 45802ac6454SAndrew Thompson if (is_rx) { 45902ac6454SAndrew Thompson f = udev->fifo[n + USB_FIFO_RX]; 46002ac6454SAndrew Thompson if (f != NULL) { 461ae60fdfbSAndrew Thompson if (f->dev_ep_index != e) { 46202ac6454SAndrew Thompson /* wrong endpoint index */ 46302ac6454SAndrew Thompson continue; 46402ac6454SAndrew Thompson } 4657214348fSAndrew Thompson if (f->curr_cpd != NULL) { 46602ac6454SAndrew Thompson /* FIFO is opened */ 46702ac6454SAndrew Thompson is_busy = 1; 46802ac6454SAndrew Thompson continue; 46902ac6454SAndrew Thompson } 47002ac6454SAndrew Thompson } else if (no_null) { 47102ac6454SAndrew Thompson continue; 47202ac6454SAndrew Thompson } 47302ac6454SAndrew Thompson } 47402ac6454SAndrew Thompson break; 47502ac6454SAndrew Thompson } 47602ac6454SAndrew Thompson 47702ac6454SAndrew Thompson if (no_null == 0) { 478ae60fdfbSAndrew Thompson if (e >= (USB_EP_MAX / 2)) { 47902ac6454SAndrew Thompson /* we don't create any endpoints in this range */ 4807214348fSAndrew Thompson DPRINTFN(5, "ep out of range\n"); 48102ac6454SAndrew Thompson return (is_busy ? EBUSY : EINVAL); 48202ac6454SAndrew Thompson } 48302ac6454SAndrew Thompson } 4847214348fSAndrew Thompson 485ae60fdfbSAndrew Thompson if ((e != 0) && is_busy) { 4867214348fSAndrew Thompson /* 4877214348fSAndrew Thompson * Only the default control endpoint is allowed to be 4887214348fSAndrew Thompson * opened multiple times! 4897214348fSAndrew Thompson */ 4907214348fSAndrew Thompson DPRINTFN(5, "busy\n"); 4917214348fSAndrew Thompson return (EBUSY); 4927214348fSAndrew Thompson } 4937214348fSAndrew Thompson 49402ac6454SAndrew Thompson /* Check TX FIFO */ 49502ac6454SAndrew Thompson if (is_tx && 49602ac6454SAndrew Thompson (udev->fifo[n + USB_FIFO_TX] == NULL)) { 497a593f6b8SAndrew Thompson ep = usb_dev_get_ep(udev, e, USB_FIFO_TX); 498ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_TX); 499ae60fdfbSAndrew Thompson if (ep == NULL) { 500ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint returned NULL\n"); 50102ac6454SAndrew Thompson return (EINVAL); 50202ac6454SAndrew Thompson } 503a593f6b8SAndrew Thompson f = usb_fifo_alloc(); 50402ac6454SAndrew Thompson if (f == NULL) { 505ee3e3ff5SAndrew Thompson DPRINTFN(5, "could not alloc tx fifo\n"); 50602ac6454SAndrew Thompson return (ENOMEM); 50702ac6454SAndrew Thompson } 50802ac6454SAndrew Thompson /* update some fields */ 50902ac6454SAndrew Thompson f->fifo_index = n + USB_FIFO_TX; 510ae60fdfbSAndrew Thompson f->dev_ep_index = e; 51191cd9240SAndrew Thompson f->priv_mtx = &udev->device_mtx; 512ae60fdfbSAndrew Thompson f->priv_sc0 = ep; 513a593f6b8SAndrew Thompson f->methods = &usb_ugen_methods; 514ae60fdfbSAndrew Thompson f->iface_index = ep->iface_index; 51502ac6454SAndrew Thompson f->udev = udev; 516a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 51702ac6454SAndrew Thompson udev->fifo[n + USB_FIFO_TX] = f; 518a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 51902ac6454SAndrew Thompson } 52002ac6454SAndrew Thompson /* Check RX FIFO */ 52102ac6454SAndrew Thompson if (is_rx && 52202ac6454SAndrew Thompson (udev->fifo[n + USB_FIFO_RX] == NULL)) { 52302ac6454SAndrew Thompson 524a593f6b8SAndrew Thompson ep = usb_dev_get_ep(udev, e, USB_FIFO_RX); 525ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_RX); 526ae60fdfbSAndrew Thompson if (ep == NULL) { 527ae60fdfbSAndrew Thompson DPRINTFN(5, "dev_get_endpoint returned NULL\n"); 52802ac6454SAndrew Thompson return (EINVAL); 52902ac6454SAndrew Thompson } 530a593f6b8SAndrew Thompson f = usb_fifo_alloc(); 53102ac6454SAndrew Thompson if (f == NULL) { 532ee3e3ff5SAndrew Thompson DPRINTFN(5, "could not alloc rx fifo\n"); 53302ac6454SAndrew Thompson return (ENOMEM); 53402ac6454SAndrew Thompson } 53502ac6454SAndrew Thompson /* update some fields */ 53602ac6454SAndrew Thompson f->fifo_index = n + USB_FIFO_RX; 537ae60fdfbSAndrew Thompson f->dev_ep_index = e; 53891cd9240SAndrew Thompson f->priv_mtx = &udev->device_mtx; 539ae60fdfbSAndrew Thompson f->priv_sc0 = ep; 540a593f6b8SAndrew Thompson f->methods = &usb_ugen_methods; 541ae60fdfbSAndrew Thompson f->iface_index = ep->iface_index; 54202ac6454SAndrew Thompson f->udev = udev; 543a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 54402ac6454SAndrew Thompson udev->fifo[n + USB_FIFO_RX] = f; 545a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 54602ac6454SAndrew Thompson } 54702ac6454SAndrew Thompson if (is_tx) { 548e13e19faSAndrew Thompson crd->txfifo = udev->fifo[n + USB_FIFO_TX]; 54902ac6454SAndrew Thompson } 55002ac6454SAndrew Thompson if (is_rx) { 551e13e19faSAndrew Thompson crd->rxfifo = udev->fifo[n + USB_FIFO_RX]; 55202ac6454SAndrew Thompson } 553ee3e3ff5SAndrew Thompson /* fill out fifo index */ 554ee3e3ff5SAndrew Thompson DPRINTFN(5, "fifo index = %d\n", n); 555ee3e3ff5SAndrew Thompson cpd->fifo_index = n; 55602ac6454SAndrew Thompson 55702ac6454SAndrew Thompson /* complete */ 55802ac6454SAndrew Thompson 55902ac6454SAndrew Thompson return (0); 56002ac6454SAndrew Thompson } 56102ac6454SAndrew Thompson 56202ac6454SAndrew Thompson void 563a593f6b8SAndrew Thompson usb_fifo_free(struct usb_fifo *f) 56402ac6454SAndrew Thompson { 56502ac6454SAndrew Thompson uint8_t n; 56602ac6454SAndrew Thompson 56702ac6454SAndrew Thompson if (f == NULL) { 56802ac6454SAndrew Thompson /* be NULL safe */ 56902ac6454SAndrew Thompson return; 57002ac6454SAndrew Thompson } 57102ac6454SAndrew Thompson /* destroy symlink devices, if any */ 57202ac6454SAndrew Thompson for (n = 0; n != 2; n++) { 57302ac6454SAndrew Thompson if (f->symlink[n]) { 574a593f6b8SAndrew Thompson usb_free_symlink(f->symlink[n]); 57502ac6454SAndrew Thompson f->symlink[n] = NULL; 57602ac6454SAndrew Thompson } 57702ac6454SAndrew Thompson } 578a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 57902ac6454SAndrew Thompson 58002ac6454SAndrew Thompson /* delink ourselves to stop calls from userland */ 58102ac6454SAndrew Thompson if ((f->fifo_index < USB_FIFO_MAX) && 58202ac6454SAndrew Thompson (f->udev != NULL) && 58302ac6454SAndrew Thompson (f->udev->fifo[f->fifo_index] == f)) { 58402ac6454SAndrew Thompson f->udev->fifo[f->fifo_index] = NULL; 58502ac6454SAndrew Thompson } else { 586767cb2e2SAndrew Thompson DPRINTFN(0, "USB FIFO %p has not been linked\n", f); 58702ac6454SAndrew Thompson } 58802ac6454SAndrew Thompson 58902ac6454SAndrew Thompson /* decrease refcount */ 59002ac6454SAndrew Thompson f->refcount--; 59102ac6454SAndrew Thompson /* prevent any write flush */ 59202ac6454SAndrew Thompson f->flag_iserror = 1; 59302ac6454SAndrew Thompson /* need to wait until all callers have exited */ 59402ac6454SAndrew Thompson while (f->refcount != 0) { 595a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); /* avoid LOR */ 59602ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 59702ac6454SAndrew Thompson /* get I/O thread out of any sleep state */ 59802ac6454SAndrew Thompson if (f->flag_sleeping) { 59902ac6454SAndrew Thompson f->flag_sleeping = 0; 6008437751dSAndrew Thompson cv_broadcast(&f->cv_io); 60102ac6454SAndrew Thompson } 60202ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 603a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 60402ac6454SAndrew Thompson 605c7d8c1c6SHans Petter Selasky /* 606c7d8c1c6SHans Petter Selasky * Check if the "f->refcount" variable reached zero 607c7d8c1c6SHans Petter Selasky * during the unlocked time before entering wait: 608c7d8c1c6SHans Petter Selasky */ 609c7d8c1c6SHans Petter Selasky if (f->refcount == 0) 610c7d8c1c6SHans Petter Selasky break; 611c7d8c1c6SHans Petter Selasky 61202ac6454SAndrew Thompson /* wait for sync */ 613a593f6b8SAndrew Thompson cv_wait(&f->cv_drain, &usb_ref_lock); 61402ac6454SAndrew Thompson } 615a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 61602ac6454SAndrew Thompson 61702ac6454SAndrew Thompson /* take care of closing the device here, if any */ 618a593f6b8SAndrew Thompson usb_fifo_close(f, 0); 61902ac6454SAndrew Thompson 6208437751dSAndrew Thompson cv_destroy(&f->cv_io); 6218437751dSAndrew Thompson cv_destroy(&f->cv_drain); 62202ac6454SAndrew Thompson 62302ac6454SAndrew Thompson free(f, M_USBDEV); 62402ac6454SAndrew Thompson } 62502ac6454SAndrew Thompson 626ae60fdfbSAndrew Thompson static struct usb_endpoint * 627a593f6b8SAndrew Thompson usb_dev_get_ep(struct usb_device *udev, uint8_t ep_index, uint8_t dir) 62802ac6454SAndrew Thompson { 629ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 63002ac6454SAndrew Thompson uint8_t ep_dir; 63102ac6454SAndrew Thompson 63202ac6454SAndrew Thompson if (ep_index == 0) { 6335b3bb704SAndrew Thompson ep = &udev->ctrl_ep; 63402ac6454SAndrew Thompson } else { 63502ac6454SAndrew Thompson if (dir == USB_FIFO_RX) { 636f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_HOST) { 63702ac6454SAndrew Thompson ep_dir = UE_DIR_IN; 63802ac6454SAndrew Thompson } else { 63902ac6454SAndrew Thompson ep_dir = UE_DIR_OUT; 64002ac6454SAndrew Thompson } 64102ac6454SAndrew Thompson } else { 642f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_HOST) { 64302ac6454SAndrew Thompson ep_dir = UE_DIR_OUT; 64402ac6454SAndrew Thompson } else { 64502ac6454SAndrew Thompson ep_dir = UE_DIR_IN; 64602ac6454SAndrew Thompson } 64702ac6454SAndrew Thompson } 648a593f6b8SAndrew Thompson ep = usbd_get_ep_by_addr(udev, ep_index | ep_dir); 64902ac6454SAndrew Thompson } 65002ac6454SAndrew Thompson 651ae60fdfbSAndrew Thompson if (ep == NULL) { 652ae60fdfbSAndrew Thompson /* if the endpoint does not exist then return */ 65302ac6454SAndrew Thompson return (NULL); 65402ac6454SAndrew Thompson } 655ae60fdfbSAndrew Thompson if (ep->edesc == NULL) { 656ae60fdfbSAndrew Thompson /* invalid endpoint */ 65702ac6454SAndrew Thompson return (NULL); 65802ac6454SAndrew Thompson } 659ae60fdfbSAndrew Thompson return (ep); /* success */ 66002ac6454SAndrew Thompson } 66102ac6454SAndrew Thompson 66202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 663a593f6b8SAndrew Thompson * usb_fifo_open 66402ac6454SAndrew Thompson * 66502ac6454SAndrew Thompson * Returns: 66602ac6454SAndrew Thompson * 0: Success 66702ac6454SAndrew Thompson * Else: Failure 66802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 66902ac6454SAndrew Thompson static int 670a593f6b8SAndrew Thompson usb_fifo_open(struct usb_cdev_privdata *cpd, 671760bc48eSAndrew Thompson struct usb_fifo *f, int fflags) 67202ac6454SAndrew Thompson { 67302ac6454SAndrew Thompson int err; 67402ac6454SAndrew Thompson 67502ac6454SAndrew Thompson if (f == NULL) { 67602ac6454SAndrew Thompson /* no FIFO there */ 67702ac6454SAndrew Thompson DPRINTFN(2, "no FIFO\n"); 67802ac6454SAndrew Thompson return (ENXIO); 67902ac6454SAndrew Thompson } 68002ac6454SAndrew Thompson /* remove FWRITE and FREAD flags */ 68102ac6454SAndrew Thompson fflags &= ~(FWRITE | FREAD); 68202ac6454SAndrew Thompson 68302ac6454SAndrew Thompson /* set correct file flags */ 68402ac6454SAndrew Thompson if ((f->fifo_index & 1) == USB_FIFO_TX) { 68502ac6454SAndrew Thompson fflags |= FWRITE; 68602ac6454SAndrew Thompson } else { 68702ac6454SAndrew Thompson fflags |= FREAD; 68802ac6454SAndrew Thompson } 68902ac6454SAndrew Thompson 69002ac6454SAndrew Thompson /* check if we are already opened */ 69102ac6454SAndrew Thompson /* we don't need any locks when checking this variable */ 6927214348fSAndrew Thompson if (f->curr_cpd != NULL) { 69302ac6454SAndrew Thompson err = EBUSY; 69402ac6454SAndrew Thompson goto done; 69502ac6454SAndrew Thompson } 696ee3e3ff5SAndrew Thompson 6977214348fSAndrew Thompson /* reset short flag before open */ 6987214348fSAndrew Thompson f->flag_short = 0; 6997214348fSAndrew Thompson 70002ac6454SAndrew Thompson /* call open method */ 701ee3e3ff5SAndrew Thompson err = (f->methods->f_open) (f, fflags); 70202ac6454SAndrew Thompson if (err) { 70302ac6454SAndrew Thompson goto done; 70402ac6454SAndrew Thompson } 70502ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 70602ac6454SAndrew Thompson 70702ac6454SAndrew Thompson /* reset sleep flag */ 70802ac6454SAndrew Thompson f->flag_sleeping = 0; 70902ac6454SAndrew Thompson 71002ac6454SAndrew Thompson /* reset error flag */ 71102ac6454SAndrew Thompson f->flag_iserror = 0; 71202ac6454SAndrew Thompson 71302ac6454SAndrew Thompson /* reset complete flag */ 71402ac6454SAndrew Thompson f->flag_iscomplete = 0; 71502ac6454SAndrew Thompson 71602ac6454SAndrew Thompson /* reset select flag */ 71702ac6454SAndrew Thompson f->flag_isselect = 0; 71802ac6454SAndrew Thompson 71902ac6454SAndrew Thompson /* reset flushing flag */ 72002ac6454SAndrew Thompson f->flag_flushing = 0; 72102ac6454SAndrew Thompson 72202ac6454SAndrew Thompson /* reset ASYNC proc flag */ 72302ac6454SAndrew Thompson f->async_p = NULL; 72402ac6454SAndrew Thompson 725a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 7267214348fSAndrew Thompson /* flag the fifo as opened to prevent others */ 7277214348fSAndrew Thompson f->curr_cpd = cpd; 728a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 72902ac6454SAndrew Thompson 73002ac6454SAndrew Thompson /* reset queue */ 731a593f6b8SAndrew Thompson usb_fifo_reset(f); 73202ac6454SAndrew Thompson 73302ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 73402ac6454SAndrew Thompson done: 73502ac6454SAndrew Thompson return (err); 73602ac6454SAndrew Thompson } 73702ac6454SAndrew Thompson 73802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 739a593f6b8SAndrew Thompson * usb_fifo_reset 74002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 74102ac6454SAndrew Thompson void 742a593f6b8SAndrew Thompson usb_fifo_reset(struct usb_fifo *f) 74302ac6454SAndrew Thompson { 744760bc48eSAndrew Thompson struct usb_mbuf *m; 74502ac6454SAndrew Thompson 74602ac6454SAndrew Thompson if (f == NULL) { 74702ac6454SAndrew Thompson return; 74802ac6454SAndrew Thompson } 74902ac6454SAndrew Thompson while (1) { 75002ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 75102ac6454SAndrew Thompson if (m) { 75202ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 75302ac6454SAndrew Thompson } else { 75402ac6454SAndrew Thompson break; 75502ac6454SAndrew Thompson } 75602ac6454SAndrew Thompson } 757bd73b187SAlfred Perlstein /* reset have fragment flag */ 758bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 75902ac6454SAndrew Thompson } 76002ac6454SAndrew Thompson 76102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 762a593f6b8SAndrew Thompson * usb_fifo_close 76302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 76402ac6454SAndrew Thompson static void 765a593f6b8SAndrew Thompson usb_fifo_close(struct usb_fifo *f, int fflags) 76602ac6454SAndrew Thompson { 76702ac6454SAndrew Thompson int err; 76802ac6454SAndrew Thompson 76902ac6454SAndrew Thompson /* check if we are not opened */ 7707214348fSAndrew Thompson if (f->curr_cpd == NULL) { 77102ac6454SAndrew Thompson /* nothing to do - already closed */ 77202ac6454SAndrew Thompson return; 77302ac6454SAndrew Thompson } 77402ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 77502ac6454SAndrew Thompson 7767214348fSAndrew Thompson /* clear current cdev private data pointer */ 7777214348fSAndrew Thompson f->curr_cpd = NULL; 77802ac6454SAndrew Thompson 77902ac6454SAndrew Thompson /* check if we are selected */ 78002ac6454SAndrew Thompson if (f->flag_isselect) { 78102ac6454SAndrew Thompson selwakeup(&f->selinfo); 78202ac6454SAndrew Thompson f->flag_isselect = 0; 78302ac6454SAndrew Thompson } 78402ac6454SAndrew Thompson /* check if a thread wants SIGIO */ 78502ac6454SAndrew Thompson if (f->async_p != NULL) { 78602ac6454SAndrew Thompson PROC_LOCK(f->async_p); 7878451d0ddSKip Macy kern_psignal(f->async_p, SIGIO); 78802ac6454SAndrew Thompson PROC_UNLOCK(f->async_p); 78902ac6454SAndrew Thompson f->async_p = NULL; 79002ac6454SAndrew Thompson } 79102ac6454SAndrew Thompson /* remove FWRITE and FREAD flags */ 79202ac6454SAndrew Thompson fflags &= ~(FWRITE | FREAD); 79302ac6454SAndrew Thompson 79402ac6454SAndrew Thompson /* flush written data, if any */ 79502ac6454SAndrew Thompson if ((f->fifo_index & 1) == USB_FIFO_TX) { 79602ac6454SAndrew Thompson 79702ac6454SAndrew Thompson if (!f->flag_iserror) { 79802ac6454SAndrew Thompson 79902ac6454SAndrew Thompson /* set flushing flag */ 80002ac6454SAndrew Thompson f->flag_flushing = 1; 80102ac6454SAndrew Thompson 802bd73b187SAlfred Perlstein /* get the last packet in */ 803bd73b187SAlfred Perlstein if (f->flag_have_fragment) { 804bd73b187SAlfred Perlstein struct usb_mbuf *m; 805bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 806bd73b187SAlfred Perlstein USB_IF_DEQUEUE(&f->free_q, m); 807bd73b187SAlfred Perlstein if (m) { 808bd73b187SAlfred Perlstein USB_IF_ENQUEUE(&f->used_q, m); 809bd73b187SAlfred Perlstein } 810bd73b187SAlfred Perlstein } 811bd73b187SAlfred Perlstein 81202ac6454SAndrew Thompson /* start write transfer, if not already started */ 81302ac6454SAndrew Thompson (f->methods->f_start_write) (f); 81402ac6454SAndrew Thompson 81502ac6454SAndrew Thompson /* check if flushed already */ 81602ac6454SAndrew Thompson while (f->flag_flushing && 81702ac6454SAndrew Thompson (!f->flag_iserror)) { 81802ac6454SAndrew Thompson /* wait until all data has been written */ 81902ac6454SAndrew Thompson f->flag_sleeping = 1; 8208437751dSAndrew Thompson err = cv_wait_sig(&f->cv_io, f->priv_mtx); 82102ac6454SAndrew Thompson if (err) { 82202ac6454SAndrew Thompson DPRINTF("signal received\n"); 82302ac6454SAndrew Thompson break; 82402ac6454SAndrew Thompson } 82502ac6454SAndrew Thompson } 82602ac6454SAndrew Thompson } 82702ac6454SAndrew Thompson fflags |= FWRITE; 82802ac6454SAndrew Thompson 82902ac6454SAndrew Thompson /* stop write transfer, if not already stopped */ 83002ac6454SAndrew Thompson (f->methods->f_stop_write) (f); 83102ac6454SAndrew Thompson } else { 83202ac6454SAndrew Thompson fflags |= FREAD; 83302ac6454SAndrew Thompson 83402ac6454SAndrew Thompson /* stop write transfer, if not already stopped */ 83502ac6454SAndrew Thompson (f->methods->f_stop_read) (f); 83602ac6454SAndrew Thompson } 83702ac6454SAndrew Thompson 83802ac6454SAndrew Thompson /* check if we are sleeping */ 83902ac6454SAndrew Thompson if (f->flag_sleeping) { 84002ac6454SAndrew Thompson DPRINTFN(2, "Sleeping at close!\n"); 84102ac6454SAndrew Thompson } 84202ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 84302ac6454SAndrew Thompson 84402ac6454SAndrew Thompson /* call close method */ 845ee3e3ff5SAndrew Thompson (f->methods->f_close) (f, fflags); 84602ac6454SAndrew Thompson 84702ac6454SAndrew Thompson DPRINTF("closed\n"); 84802ac6454SAndrew Thompson } 84902ac6454SAndrew Thompson 85002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 851a593f6b8SAndrew Thompson * usb_open - cdev callback 85202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 85302ac6454SAndrew Thompson static int 854a593f6b8SAndrew Thompson usb_open(struct cdev *dev, int fflags, int devtype, struct thread *td) 85502ac6454SAndrew Thompson { 856760bc48eSAndrew Thompson struct usb_fs_privdata* pd = (struct usb_fs_privdata*)dev->si_drv1; 857e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 858760bc48eSAndrew Thompson struct usb_cdev_privdata *cpd; 859ee3e3ff5SAndrew Thompson int err, ep; 86002ac6454SAndrew Thompson 8617870adb6SEd Schouten DPRINTFN(2, "%s fflags=0x%08x\n", devtoname(dev), fflags); 86202ac6454SAndrew Thompson 863ee3e3ff5SAndrew Thompson KASSERT(fflags & (FREAD|FWRITE), ("invalid open flags")); 864ee3e3ff5SAndrew Thompson if (((fflags & FREAD) && !(pd->mode & FREAD)) || 865ee3e3ff5SAndrew Thompson ((fflags & FWRITE) && !(pd->mode & FWRITE))) { 866ee3e3ff5SAndrew Thompson DPRINTFN(2, "access mode not supported\n"); 86702ac6454SAndrew Thompson return (EPERM); 86802ac6454SAndrew Thompson } 869ee3e3ff5SAndrew Thompson 870ee3e3ff5SAndrew Thompson cpd = malloc(sizeof(*cpd), M_USBDEV, M_WAITOK | M_ZERO); 871ee3e3ff5SAndrew Thompson ep = cpd->ep_addr = pd->ep_addr; 872ee3e3ff5SAndrew Thompson 873a593f6b8SAndrew Thompson usb_loc_fill(pd, cpd); 874a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 1); 87502ac6454SAndrew Thompson if (err) { 87602ac6454SAndrew Thompson DPRINTFN(2, "cannot ref device\n"); 877ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 87802ac6454SAndrew Thompson return (ENXIO); 87902ac6454SAndrew Thompson } 880ee3e3ff5SAndrew Thompson cpd->fflags = fflags; /* access mode for open lifetime */ 88102ac6454SAndrew Thompson 88202ac6454SAndrew Thompson /* create FIFOs, if any */ 883a593f6b8SAndrew Thompson err = usb_fifo_create(cpd, &refs); 88402ac6454SAndrew Thompson /* check for error */ 88502ac6454SAndrew Thompson if (err) { 886ee3e3ff5SAndrew Thompson DPRINTFN(2, "cannot create fifo\n"); 887a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 888ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 88902ac6454SAndrew Thompson return (err); 89002ac6454SAndrew Thompson } 89102ac6454SAndrew Thompson if (fflags & FREAD) { 892a593f6b8SAndrew Thompson err = usb_fifo_open(cpd, refs.rxfifo, fflags); 89302ac6454SAndrew Thompson if (err) { 89402ac6454SAndrew Thompson DPRINTFN(2, "read open failed\n"); 895a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 896ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 89702ac6454SAndrew Thompson return (err); 89802ac6454SAndrew Thompson } 89902ac6454SAndrew Thompson } 90002ac6454SAndrew Thompson if (fflags & FWRITE) { 901a593f6b8SAndrew Thompson err = usb_fifo_open(cpd, refs.txfifo, fflags); 90202ac6454SAndrew Thompson if (err) { 90302ac6454SAndrew Thompson DPRINTFN(2, "write open failed\n"); 90402ac6454SAndrew Thompson if (fflags & FREAD) { 905a593f6b8SAndrew Thompson usb_fifo_close(refs.rxfifo, fflags); 90602ac6454SAndrew Thompson } 907a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 908ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 90902ac6454SAndrew Thompson return (err); 91002ac6454SAndrew Thompson } 91102ac6454SAndrew Thompson } 912a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 913a593f6b8SAndrew Thompson devfs_set_cdevpriv(cpd, usb_close); 91402ac6454SAndrew Thompson 915ee3e3ff5SAndrew Thompson return (0); 91602ac6454SAndrew Thompson } 91702ac6454SAndrew Thompson 91802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 919a593f6b8SAndrew Thompson * usb_close - cdev callback 92002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 92102ac6454SAndrew Thompson static void 922a593f6b8SAndrew Thompson usb_close(void *arg) 92302ac6454SAndrew Thompson { 924e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 925760bc48eSAndrew Thompson struct usb_cdev_privdata *cpd = arg; 926ee3e3ff5SAndrew Thompson int err; 92702ac6454SAndrew Thompson 9287214348fSAndrew Thompson DPRINTFN(2, "cpd=%p\n", cpd); 929ee3e3ff5SAndrew Thompson 930f97da128SHans Petter Selasky err = usb_ref_device(cpd, &refs, 931f97da128SHans Petter Selasky 2 /* uref and allow detached state */); 932f97da128SHans Petter Selasky if (err) { 933*5c8c627bSHans Petter Selasky DPRINTFN(2, "Cannot grab USB reference when " 934f97da128SHans Petter Selasky "closing USB file handle\n"); 935fbaee0f1SHans Petter Selasky goto done; 93602ac6454SAndrew Thompson } 937ee3e3ff5SAndrew Thompson if (cpd->fflags & FREAD) { 938a593f6b8SAndrew Thompson usb_fifo_close(refs.rxfifo, cpd->fflags); 93902ac6454SAndrew Thompson } 940ee3e3ff5SAndrew Thompson if (cpd->fflags & FWRITE) { 941a593f6b8SAndrew Thompson usb_fifo_close(refs.txfifo, cpd->fflags); 94202ac6454SAndrew Thompson } 943a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 944fbaee0f1SHans Petter Selasky done: 945ee3e3ff5SAndrew Thompson free(cpd, M_USBDEV); 94602ac6454SAndrew Thompson } 94702ac6454SAndrew Thompson 94802ac6454SAndrew Thompson static void 949a593f6b8SAndrew Thompson usb_dev_init(void *arg) 95002ac6454SAndrew Thompson { 951a593f6b8SAndrew Thompson mtx_init(&usb_ref_lock, "USB ref mutex", NULL, MTX_DEF); 952a593f6b8SAndrew Thompson sx_init(&usb_sym_lock, "USB sym mutex"); 953a593f6b8SAndrew Thompson TAILQ_INIT(&usb_sym_head); 95402ac6454SAndrew Thompson 95502ac6454SAndrew Thompson /* check the UGEN methods */ 956a593f6b8SAndrew Thompson usb_fifo_check_methods(&usb_ugen_methods); 95702ac6454SAndrew Thompson } 95802ac6454SAndrew Thompson 959a593f6b8SAndrew Thompson SYSINIT(usb_dev_init, SI_SUB_KLD, SI_ORDER_FIRST, usb_dev_init, NULL); 96002ac6454SAndrew Thompson 96102ac6454SAndrew Thompson static void 962a593f6b8SAndrew Thompson usb_dev_init_post(void *arg) 96302ac6454SAndrew Thompson { 96402ac6454SAndrew Thompson /* 965ee3e3ff5SAndrew Thompson * Create /dev/usb - this is needed for usbconfig(8), which 966ee3e3ff5SAndrew Thompson * needs a well-known device name to access. 96702ac6454SAndrew Thompson */ 968a593f6b8SAndrew Thompson usb_dev = make_dev(&usb_static_devsw, 0, UID_ROOT, GID_OPERATOR, 969ee3e3ff5SAndrew Thompson 0644, USB_DEVICE_NAME); 970a593f6b8SAndrew Thompson if (usb_dev == NULL) { 971767cb2e2SAndrew Thompson DPRINTFN(0, "Could not create usb bus device\n"); 97202ac6454SAndrew Thompson } 97302ac6454SAndrew Thompson } 97402ac6454SAndrew Thompson 975a593f6b8SAndrew Thompson SYSINIT(usb_dev_init_post, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, usb_dev_init_post, NULL); 97602ac6454SAndrew Thompson 97702ac6454SAndrew Thompson static void 978a593f6b8SAndrew Thompson usb_dev_uninit(void *arg) 97902ac6454SAndrew Thompson { 980a593f6b8SAndrew Thompson if (usb_dev != NULL) { 981a593f6b8SAndrew Thompson destroy_dev(usb_dev); 982a593f6b8SAndrew Thompson usb_dev = NULL; 98302ac6454SAndrew Thompson } 984a593f6b8SAndrew Thompson mtx_destroy(&usb_ref_lock); 985a593f6b8SAndrew Thompson sx_destroy(&usb_sym_lock); 98602ac6454SAndrew Thompson } 98702ac6454SAndrew Thompson 988a593f6b8SAndrew Thompson SYSUNINIT(usb_dev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb_dev_uninit, NULL); 98902ac6454SAndrew Thompson 99002ac6454SAndrew Thompson static int 991a593f6b8SAndrew Thompson usb_ioctl_f_sub(struct usb_fifo *f, u_long cmd, void *addr, 99202ac6454SAndrew Thompson struct thread *td) 99302ac6454SAndrew Thompson { 99402ac6454SAndrew Thompson int error = 0; 99502ac6454SAndrew Thompson 99602ac6454SAndrew Thompson switch (cmd) { 99702ac6454SAndrew Thompson case FIODTYPE: 99802ac6454SAndrew Thompson *(int *)addr = 0; /* character device */ 99902ac6454SAndrew Thompson break; 100002ac6454SAndrew Thompson 100102ac6454SAndrew Thompson case FIONBIO: 100202ac6454SAndrew Thompson /* handled by upper FS layer */ 100302ac6454SAndrew Thompson break; 100402ac6454SAndrew Thompson 100502ac6454SAndrew Thompson case FIOASYNC: 100602ac6454SAndrew Thompson if (*(int *)addr) { 100702ac6454SAndrew Thompson if (f->async_p != NULL) { 100802ac6454SAndrew Thompson error = EBUSY; 100902ac6454SAndrew Thompson break; 101002ac6454SAndrew Thompson } 101102ac6454SAndrew Thompson f->async_p = USB_TD_GET_PROC(td); 101202ac6454SAndrew Thompson } else { 101302ac6454SAndrew Thompson f->async_p = NULL; 101402ac6454SAndrew Thompson } 101502ac6454SAndrew Thompson break; 101602ac6454SAndrew Thompson 101702ac6454SAndrew Thompson /* XXX this is not the most general solution */ 101802ac6454SAndrew Thompson case TIOCSPGRP: 101902ac6454SAndrew Thompson if (f->async_p == NULL) { 102002ac6454SAndrew Thompson error = EINVAL; 102102ac6454SAndrew Thompson break; 102202ac6454SAndrew Thompson } 102302ac6454SAndrew Thompson if (*(int *)addr != USB_PROC_GET_GID(f->async_p)) { 102402ac6454SAndrew Thompson error = EPERM; 102502ac6454SAndrew Thompson break; 102602ac6454SAndrew Thompson } 102702ac6454SAndrew Thompson break; 102802ac6454SAndrew Thompson default: 102902ac6454SAndrew Thompson return (ENOIOCTL); 103002ac6454SAndrew Thompson } 1031cba30496SAndrew Thompson DPRINTFN(3, "cmd 0x%lx = %d\n", cmd, error); 103202ac6454SAndrew Thompson return (error); 103302ac6454SAndrew Thompson } 103402ac6454SAndrew Thompson 1035ee3e3ff5SAndrew Thompson /*------------------------------------------------------------------------* 1036a593f6b8SAndrew Thompson * usb_ioctl - cdev callback 1037ee3e3ff5SAndrew Thompson *------------------------------------------------------------------------*/ 103802ac6454SAndrew Thompson static int 1039a593f6b8SAndrew Thompson usb_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int fflag, struct thread* td) 104002ac6454SAndrew Thompson { 1041e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1042760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1043760bc48eSAndrew Thompson struct usb_fifo *f; 104402ac6454SAndrew Thompson int fflags; 104502ac6454SAndrew Thompson int err; 104602ac6454SAndrew Thompson 1047cba30496SAndrew Thompson DPRINTFN(2, "cmd=0x%lx\n", cmd); 1048cba30496SAndrew Thompson 1049ee3e3ff5SAndrew Thompson err = devfs_get_cdevpriv((void **)&cpd); 1050ee3e3ff5SAndrew Thompson if (err != 0) 1051ee3e3ff5SAndrew Thompson return (err); 1052ee3e3ff5SAndrew Thompson 1053f5f145baSAndrew Thompson /* 1054e13e19faSAndrew Thompson * Performance optimisation: We try to check for IOCTL's that 1055f5f145baSAndrew Thompson * don't need the USB reference first. Then we grab the USB 1056f5f145baSAndrew Thompson * reference if we need it! 1057f5f145baSAndrew Thompson */ 1058a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 1059cb18f7d1SAlfred Perlstein if (err) 106002ac6454SAndrew Thompson return (ENXIO); 1061cb18f7d1SAlfred Perlstein 1062ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 106302ac6454SAndrew Thompson 106402ac6454SAndrew Thompson f = NULL; /* set default value */ 106502ac6454SAndrew Thompson err = ENOIOCTL; /* set default value */ 106602ac6454SAndrew Thompson 106702ac6454SAndrew Thompson if (fflags & FWRITE) { 1068e13e19faSAndrew Thompson f = refs.txfifo; 1069a593f6b8SAndrew Thompson err = usb_ioctl_f_sub(f, cmd, addr, td); 107002ac6454SAndrew Thompson } 107102ac6454SAndrew Thompson if (fflags & FREAD) { 1072e13e19faSAndrew Thompson f = refs.rxfifo; 1073a593f6b8SAndrew Thompson err = usb_ioctl_f_sub(f, cmd, addr, td); 107402ac6454SAndrew Thompson } 1075ee3e3ff5SAndrew Thompson KASSERT(f != NULL, ("fifo not found")); 10768f9750b7SHans Petter Selasky if (err != ENOIOCTL) 10778f9750b7SHans Petter Selasky goto done; 10788f9750b7SHans Petter Selasky 1079ee3e3ff5SAndrew Thompson err = (f->methods->f_ioctl) (f, cmd, addr, fflags); 10808f9750b7SHans Petter Selasky 1081cba30496SAndrew Thompson DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err); 10828f9750b7SHans Petter Selasky 10838f9750b7SHans Petter Selasky if (err != ENOIOCTL) 10848f9750b7SHans Petter Selasky goto done; 10858f9750b7SHans Petter Selasky 1086a593f6b8SAndrew Thompson if (usb_usb_ref_device(cpd, &refs)) { 108702ac6454SAndrew Thompson err = ENXIO; 108802ac6454SAndrew Thompson goto done; 108902ac6454SAndrew Thompson } 10908f9750b7SHans Petter Selasky 1091ee3e3ff5SAndrew Thompson err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags); 10928f9750b7SHans Petter Selasky 1093cba30496SAndrew Thompson DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err); 10948f9750b7SHans Petter Selasky 10958f9750b7SHans Petter Selasky if (err == ENOIOCTL) 109602ac6454SAndrew Thompson err = ENOTTY; 10978f9750b7SHans Petter Selasky 10988f9750b7SHans Petter Selasky if (err) 10998f9750b7SHans Petter Selasky goto done; 11008f9750b7SHans Petter Selasky 11018f9750b7SHans Petter Selasky /* Wait for re-enumeration, if any */ 11028f9750b7SHans Petter Selasky 1103d6f4a9f9SHans Petter Selasky while (f->udev->re_enumerate_wait != USB_RE_ENUM_DONE) { 11048f9750b7SHans Petter Selasky 11058f9750b7SHans Petter Selasky usb_unref_device(cpd, &refs); 11068f9750b7SHans Petter Selasky 11078f9750b7SHans Petter Selasky usb_pause_mtx(NULL, hz / 128); 11088f9750b7SHans Petter Selasky 11098f9750b7SHans Petter Selasky if (usb_ref_device(cpd, &refs, 1 /* need uref */)) { 11108f9750b7SHans Petter Selasky err = ENXIO; 11118f9750b7SHans Petter Selasky goto done; 111202ac6454SAndrew Thompson } 11138f9750b7SHans Petter Selasky } 11148f9750b7SHans Petter Selasky 111502ac6454SAndrew Thompson done: 1116a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 111702ac6454SAndrew Thompson return (err); 111802ac6454SAndrew Thompson } 111902ac6454SAndrew Thompson 112002ac6454SAndrew Thompson /* ARGSUSED */ 112102ac6454SAndrew Thompson static int 1122a593f6b8SAndrew Thompson usb_poll(struct cdev* dev, int events, struct thread* td) 112302ac6454SAndrew Thompson { 1124e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1125760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1126760bc48eSAndrew Thompson struct usb_fifo *f; 1127760bc48eSAndrew Thompson struct usb_mbuf *m; 11288a93629eSAndrew Thompson int fflags, revents; 112902ac6454SAndrew Thompson 11308a93629eSAndrew Thompson if (devfs_get_cdevpriv((void **)&cpd) != 0 || 1131a593f6b8SAndrew Thompson usb_ref_device(cpd, &refs, 0) != 0) 11328a93629eSAndrew Thompson return (events & 11338a93629eSAndrew Thompson (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); 1134ee3e3ff5SAndrew Thompson 1135ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 113602ac6454SAndrew Thompson 113702ac6454SAndrew Thompson /* Figure out who needs service */ 1138ee3e3ff5SAndrew Thompson revents = 0; 113902ac6454SAndrew Thompson if ((events & (POLLOUT | POLLWRNORM)) && 114002ac6454SAndrew Thompson (fflags & FWRITE)) { 114102ac6454SAndrew Thompson 1142e13e19faSAndrew Thompson f = refs.txfifo; 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 got an error */ 114902ac6454SAndrew Thompson m = (void *)1; 115002ac6454SAndrew Thompson } else { 115102ac6454SAndrew Thompson if (f->queue_data == NULL) { 115202ac6454SAndrew Thompson /* 115302ac6454SAndrew Thompson * start write transfer, if not 115402ac6454SAndrew Thompson * already started 115502ac6454SAndrew Thompson */ 115602ac6454SAndrew Thompson (f->methods->f_start_write) (f); 115702ac6454SAndrew Thompson } 115802ac6454SAndrew Thompson /* check if any packets are available */ 115902ac6454SAndrew Thompson USB_IF_POLL(&f->free_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 & (POLLOUT | POLLWRNORM); 117102ac6454SAndrew Thompson } else { 117202ac6454SAndrew Thompson f->flag_isselect = 1; 117302ac6454SAndrew Thompson selrecord(td, &f->selinfo); 117402ac6454SAndrew Thompson } 117502ac6454SAndrew Thompson 117602ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 117702ac6454SAndrew Thompson } 117802ac6454SAndrew Thompson if ((events & (POLLIN | POLLRDNORM)) && 117902ac6454SAndrew Thompson (fflags & FREAD)) { 118002ac6454SAndrew Thompson 1181e13e19faSAndrew Thompson f = refs.rxfifo; 118202ac6454SAndrew Thompson 118302ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 118402ac6454SAndrew Thompson 1185e13e19faSAndrew Thompson if (!refs.is_usbfs) { 118602ac6454SAndrew Thompson if (f->flag_iserror) { 118702ac6454SAndrew Thompson /* we have and error */ 118802ac6454SAndrew Thompson m = (void *)1; 118902ac6454SAndrew Thompson } else { 119002ac6454SAndrew Thompson if (f->queue_data == NULL) { 119102ac6454SAndrew Thompson /* 119202ac6454SAndrew Thompson * start read transfer, if not 119302ac6454SAndrew Thompson * already started 119402ac6454SAndrew Thompson */ 119502ac6454SAndrew Thompson (f->methods->f_start_read) (f); 119602ac6454SAndrew Thompson } 119702ac6454SAndrew Thompson /* check if any packets are available */ 119802ac6454SAndrew Thompson USB_IF_POLL(&f->used_q, m); 119902ac6454SAndrew Thompson } 120002ac6454SAndrew Thompson } else { 120102ac6454SAndrew Thompson if (f->flag_iscomplete) { 120202ac6454SAndrew Thompson m = (void *)1; 120302ac6454SAndrew Thompson } else { 120402ac6454SAndrew Thompson m = NULL; 120502ac6454SAndrew Thompson } 120602ac6454SAndrew Thompson } 120702ac6454SAndrew Thompson 120802ac6454SAndrew Thompson if (m) { 120902ac6454SAndrew Thompson revents |= events & (POLLIN | POLLRDNORM); 121002ac6454SAndrew Thompson } else { 121102ac6454SAndrew Thompson f->flag_isselect = 1; 121202ac6454SAndrew Thompson selrecord(td, &f->selinfo); 121302ac6454SAndrew Thompson 1214e13e19faSAndrew Thompson if (!refs.is_usbfs) { 121502ac6454SAndrew Thompson /* start reading data */ 121602ac6454SAndrew Thompson (f->methods->f_start_read) (f); 121702ac6454SAndrew Thompson } 121802ac6454SAndrew Thompson } 121902ac6454SAndrew Thompson 122002ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 122102ac6454SAndrew Thompson } 1222a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 122302ac6454SAndrew Thompson return (revents); 122402ac6454SAndrew Thompson } 122502ac6454SAndrew Thompson 122602ac6454SAndrew Thompson static int 1227a593f6b8SAndrew Thompson usb_read(struct cdev *dev, struct uio *uio, int ioflag) 122802ac6454SAndrew Thompson { 1229e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1230760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1231760bc48eSAndrew Thompson struct usb_fifo *f; 1232760bc48eSAndrew Thompson struct usb_mbuf *m; 123302ac6454SAndrew Thompson int fflags; 123402ac6454SAndrew Thompson int resid; 123502ac6454SAndrew Thompson int io_len; 123602ac6454SAndrew Thompson int err; 123702ac6454SAndrew Thompson uint8_t tr_data = 0; 123802ac6454SAndrew Thompson 1239ee3e3ff5SAndrew Thompson err = devfs_get_cdevpriv((void **)&cpd); 1240ee3e3ff5SAndrew Thompson if (err != 0) 1241ee3e3ff5SAndrew Thompson return (err); 124202ac6454SAndrew Thompson 1243a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 124402ac6454SAndrew Thompson if (err) { 124502ac6454SAndrew Thompson return (ENXIO); 124602ac6454SAndrew Thompson } 1247ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 1248ee3e3ff5SAndrew Thompson 1249e13e19faSAndrew Thompson f = refs.rxfifo; 125002ac6454SAndrew Thompson if (f == NULL) { 125102ac6454SAndrew Thompson /* should not happen */ 1252a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 125302ac6454SAndrew Thompson return (EPERM); 125402ac6454SAndrew Thompson } 125502ac6454SAndrew Thompson 1256ee3e3ff5SAndrew Thompson resid = uio->uio_resid; 125702ac6454SAndrew Thompson 125802ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 125902ac6454SAndrew Thompson 126002ac6454SAndrew Thompson /* check for permanent read error */ 126102ac6454SAndrew Thompson if (f->flag_iserror) { 126202ac6454SAndrew Thompson err = EIO; 126302ac6454SAndrew Thompson goto done; 126402ac6454SAndrew Thompson } 126502ac6454SAndrew Thompson /* check if USB-FS interface is active */ 1266e13e19faSAndrew Thompson if (refs.is_usbfs) { 126702ac6454SAndrew Thompson /* 126802ac6454SAndrew Thompson * The queue is used for events that should be 126902ac6454SAndrew Thompson * retrieved using the "USB_FS_COMPLETE" ioctl. 127002ac6454SAndrew Thompson */ 127102ac6454SAndrew Thompson err = EINVAL; 127202ac6454SAndrew Thompson goto done; 127302ac6454SAndrew Thompson } 127402ac6454SAndrew Thompson while (uio->uio_resid > 0) { 127502ac6454SAndrew Thompson 127602ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 127702ac6454SAndrew Thompson 127802ac6454SAndrew Thompson if (m == NULL) { 127902ac6454SAndrew Thompson 128002ac6454SAndrew Thompson /* start read transfer, if not already started */ 128102ac6454SAndrew Thompson 128202ac6454SAndrew Thompson (f->methods->f_start_read) (f); 128302ac6454SAndrew Thompson 128407532e49SAndrew Thompson if (ioflag & IO_NDELAY) { 128502ac6454SAndrew Thompson if (tr_data) { 128602ac6454SAndrew Thompson /* return length before error */ 128702ac6454SAndrew Thompson break; 128802ac6454SAndrew Thompson } 128902ac6454SAndrew Thompson err = EWOULDBLOCK; 129002ac6454SAndrew Thompson break; 129102ac6454SAndrew Thompson } 129202ac6454SAndrew Thompson DPRINTF("sleeping\n"); 129302ac6454SAndrew Thompson 1294a593f6b8SAndrew Thompson err = usb_fifo_wait(f); 129502ac6454SAndrew Thompson if (err) { 129602ac6454SAndrew Thompson break; 129702ac6454SAndrew Thompson } 129802ac6454SAndrew Thompson continue; 129902ac6454SAndrew Thompson } 130002ac6454SAndrew Thompson if (f->methods->f_filter_read) { 130102ac6454SAndrew Thompson /* 130202ac6454SAndrew Thompson * Sometimes it is convenient to process data at the 130302ac6454SAndrew Thompson * expense of a userland process instead of a kernel 130402ac6454SAndrew Thompson * process. 130502ac6454SAndrew Thompson */ 130602ac6454SAndrew Thompson (f->methods->f_filter_read) (f, m); 130702ac6454SAndrew Thompson } 130802ac6454SAndrew Thompson tr_data = 1; 130902ac6454SAndrew Thompson 131002ac6454SAndrew Thompson io_len = MIN(m->cur_data_len, uio->uio_resid); 131102ac6454SAndrew Thompson 131202ac6454SAndrew Thompson DPRINTFN(2, "transfer %d bytes from %p\n", 131302ac6454SAndrew Thompson io_len, m->cur_data_ptr); 131402ac6454SAndrew Thompson 1315a593f6b8SAndrew Thompson err = usb_fifo_uiomove(f, 131602ac6454SAndrew Thompson m->cur_data_ptr, io_len, uio); 131702ac6454SAndrew Thompson 131802ac6454SAndrew Thompson m->cur_data_len -= io_len; 131902ac6454SAndrew Thompson m->cur_data_ptr += io_len; 132002ac6454SAndrew Thompson 132102ac6454SAndrew Thompson if (m->cur_data_len == 0) { 132202ac6454SAndrew Thompson 132302ac6454SAndrew Thompson uint8_t last_packet; 132402ac6454SAndrew Thompson 132502ac6454SAndrew Thompson last_packet = m->last_packet; 132602ac6454SAndrew Thompson 132702ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 132802ac6454SAndrew Thompson 132902ac6454SAndrew Thompson if (last_packet) { 133002ac6454SAndrew Thompson /* keep framing */ 133102ac6454SAndrew Thompson break; 133202ac6454SAndrew Thompson } 133302ac6454SAndrew Thompson } else { 133402ac6454SAndrew Thompson USB_IF_PREPEND(&f->used_q, m); 133502ac6454SAndrew Thompson } 133602ac6454SAndrew Thompson 133702ac6454SAndrew Thompson if (err) { 133802ac6454SAndrew Thompson break; 133902ac6454SAndrew Thompson } 134002ac6454SAndrew Thompson } 134102ac6454SAndrew Thompson done: 134202ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 134302ac6454SAndrew Thompson 1344a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 134502ac6454SAndrew Thompson 134602ac6454SAndrew Thompson return (err); 134702ac6454SAndrew Thompson } 134802ac6454SAndrew Thompson 134902ac6454SAndrew Thompson static int 1350a593f6b8SAndrew Thompson usb_write(struct cdev *dev, struct uio *uio, int ioflag) 135102ac6454SAndrew Thompson { 1352e13e19faSAndrew Thompson struct usb_cdev_refdata refs; 1353760bc48eSAndrew Thompson struct usb_cdev_privdata* cpd; 1354760bc48eSAndrew Thompson struct usb_fifo *f; 1355760bc48eSAndrew Thompson struct usb_mbuf *m; 1356bd73b187SAlfred Perlstein uint8_t *pdata; 135702ac6454SAndrew Thompson int fflags; 135802ac6454SAndrew Thompson int resid; 135902ac6454SAndrew Thompson int io_len; 136002ac6454SAndrew Thompson int err; 136102ac6454SAndrew Thompson uint8_t tr_data = 0; 136202ac6454SAndrew Thompson 136302ac6454SAndrew Thompson DPRINTFN(2, "\n"); 136402ac6454SAndrew Thompson 1365ee3e3ff5SAndrew Thompson err = devfs_get_cdevpriv((void **)&cpd); 1366ee3e3ff5SAndrew Thompson if (err != 0) 1367ee3e3ff5SAndrew Thompson return (err); 136802ac6454SAndrew Thompson 1369a593f6b8SAndrew Thompson err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 137002ac6454SAndrew Thompson if (err) { 137102ac6454SAndrew Thompson return (ENXIO); 137202ac6454SAndrew Thompson } 1373ee3e3ff5SAndrew Thompson fflags = cpd->fflags; 1374ee3e3ff5SAndrew Thompson 1375e13e19faSAndrew Thompson f = refs.txfifo; 137602ac6454SAndrew Thompson if (f == NULL) { 137702ac6454SAndrew Thompson /* should not happen */ 1378a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 137902ac6454SAndrew Thompson return (EPERM); 138002ac6454SAndrew Thompson } 138102ac6454SAndrew Thompson resid = uio->uio_resid; 138202ac6454SAndrew Thompson 138302ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 138402ac6454SAndrew Thompson 138502ac6454SAndrew Thompson /* check for permanent write error */ 138602ac6454SAndrew Thompson if (f->flag_iserror) { 138702ac6454SAndrew Thompson err = EIO; 138802ac6454SAndrew Thompson goto done; 138902ac6454SAndrew Thompson } 139002ac6454SAndrew Thompson /* check if USB-FS interface is active */ 1391e13e19faSAndrew Thompson if (refs.is_usbfs) { 139202ac6454SAndrew Thompson /* 139302ac6454SAndrew Thompson * The queue is used for events that should be 139402ac6454SAndrew Thompson * retrieved using the "USB_FS_COMPLETE" ioctl. 139502ac6454SAndrew Thompson */ 139602ac6454SAndrew Thompson err = EINVAL; 139702ac6454SAndrew Thompson goto done; 139802ac6454SAndrew Thompson } 139902ac6454SAndrew Thompson if (f->queue_data == NULL) { 140002ac6454SAndrew Thompson /* start write transfer, if not already started */ 140102ac6454SAndrew Thompson (f->methods->f_start_write) (f); 140202ac6454SAndrew Thompson } 140302ac6454SAndrew Thompson /* we allow writing zero length data */ 140402ac6454SAndrew Thompson do { 140502ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 140602ac6454SAndrew Thompson 140702ac6454SAndrew Thompson if (m == NULL) { 140802ac6454SAndrew Thompson 140907532e49SAndrew Thompson if (ioflag & IO_NDELAY) { 141002ac6454SAndrew Thompson if (tr_data) { 141102ac6454SAndrew Thompson /* return length before error */ 141202ac6454SAndrew Thompson break; 141302ac6454SAndrew Thompson } 141402ac6454SAndrew Thompson err = EWOULDBLOCK; 141502ac6454SAndrew Thompson break; 141602ac6454SAndrew Thompson } 141702ac6454SAndrew Thompson DPRINTF("sleeping\n"); 141802ac6454SAndrew Thompson 1419a593f6b8SAndrew Thompson err = usb_fifo_wait(f); 142002ac6454SAndrew Thompson if (err) { 142102ac6454SAndrew Thompson break; 142202ac6454SAndrew Thompson } 142302ac6454SAndrew Thompson continue; 142402ac6454SAndrew Thompson } 142502ac6454SAndrew Thompson tr_data = 1; 142602ac6454SAndrew Thompson 1427bd73b187SAlfred Perlstein if (f->flag_have_fragment == 0) { 142802ac6454SAndrew Thompson USB_MBUF_RESET(m); 1429bd73b187SAlfred Perlstein io_len = m->cur_data_len; 1430bd73b187SAlfred Perlstein pdata = m->cur_data_ptr; 1431bd73b187SAlfred Perlstein if (io_len > uio->uio_resid) 1432bd73b187SAlfred Perlstein io_len = uio->uio_resid; 143302ac6454SAndrew Thompson m->cur_data_len = io_len; 1434bd73b187SAlfred Perlstein } else { 1435bd73b187SAlfred Perlstein io_len = m->max_data_len - m->cur_data_len; 1436bd73b187SAlfred Perlstein pdata = m->cur_data_ptr + m->cur_data_len; 1437bd73b187SAlfred Perlstein if (io_len > uio->uio_resid) 1438bd73b187SAlfred Perlstein io_len = uio->uio_resid; 1439bd73b187SAlfred Perlstein m->cur_data_len += io_len; 1440bd73b187SAlfred Perlstein } 144102ac6454SAndrew Thompson 144202ac6454SAndrew Thompson DPRINTFN(2, "transfer %d bytes to %p\n", 1443bd73b187SAlfred Perlstein io_len, pdata); 144402ac6454SAndrew Thompson 1445bd73b187SAlfred Perlstein err = usb_fifo_uiomove(f, pdata, io_len, uio); 144602ac6454SAndrew Thompson 144702ac6454SAndrew Thompson if (err) { 1448bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 144902ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 145002ac6454SAndrew Thompson break; 145102ac6454SAndrew Thompson } 1452bd73b187SAlfred Perlstein 1453bd73b187SAlfred Perlstein /* check if the buffer is ready to be transmitted */ 1454bd73b187SAlfred Perlstein 1455bd73b187SAlfred Perlstein if ((f->flag_write_defrag == 0) || 1456bd73b187SAlfred Perlstein (m->cur_data_len == m->max_data_len)) { 1457bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 1458bd73b187SAlfred Perlstein 145902ac6454SAndrew Thompson /* 1460bd73b187SAlfred Perlstein * Check for write filter: 1461bd73b187SAlfred Perlstein * 1462bd73b187SAlfred Perlstein * Sometimes it is convenient to process data 1463bd73b187SAlfred Perlstein * at the expense of a userland process 1464bd73b187SAlfred Perlstein * instead of a kernel process. 146502ac6454SAndrew Thompson */ 1466bd73b187SAlfred Perlstein if (f->methods->f_filter_write) { 146702ac6454SAndrew Thompson (f->methods->f_filter_write) (f, m); 146802ac6454SAndrew Thompson } 1469bd73b187SAlfred Perlstein 1470bd73b187SAlfred Perlstein /* Put USB mbuf in the used queue */ 147102ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 147202ac6454SAndrew Thompson 1473bd73b187SAlfred Perlstein /* Start writing data, if not already started */ 147402ac6454SAndrew Thompson (f->methods->f_start_write) (f); 1475bd73b187SAlfred Perlstein } else { 1476bd73b187SAlfred Perlstein /* Wait for more data or close */ 1477bd73b187SAlfred Perlstein f->flag_have_fragment = 1; 1478bd73b187SAlfred Perlstein USB_IF_PREPEND(&f->free_q, m); 1479bd73b187SAlfred Perlstein } 148002ac6454SAndrew Thompson 148102ac6454SAndrew Thompson } while (uio->uio_resid > 0); 148202ac6454SAndrew Thompson done: 148302ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 148402ac6454SAndrew Thompson 1485a593f6b8SAndrew Thompson usb_unref_device(cpd, &refs); 148602ac6454SAndrew Thompson 1487ee3e3ff5SAndrew Thompson return (err); 1488ee3e3ff5SAndrew Thompson } 148902ac6454SAndrew Thompson 1490ee3e3ff5SAndrew Thompson int 1491a593f6b8SAndrew Thompson usb_static_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 1492ee3e3ff5SAndrew Thompson struct thread *td) 1493ee3e3ff5SAndrew Thompson { 1494ee3e3ff5SAndrew Thompson union { 1495760bc48eSAndrew Thompson struct usb_read_dir *urd; 1496ee3e3ff5SAndrew Thompson void* data; 1497ee3e3ff5SAndrew Thompson } u; 1498ce4092bdSHans Petter Selasky int err; 1499ee3e3ff5SAndrew Thompson 1500ee3e3ff5SAndrew Thompson u.data = data; 1501ee3e3ff5SAndrew Thompson switch (cmd) { 1502ee3e3ff5SAndrew Thompson case USB_READ_DIR: 1503a593f6b8SAndrew Thompson err = usb_read_symlink(u.urd->urd_data, 1504ee3e3ff5SAndrew Thompson u.urd->urd_startentry, u.urd->urd_maxlen); 1505ee3e3ff5SAndrew Thompson break; 1506ee3e3ff5SAndrew Thompson case USB_DEV_QUIRK_GET: 1507ee3e3ff5SAndrew Thompson case USB_QUIRK_NAME_GET: 1508ee3e3ff5SAndrew Thompson case USB_DEV_QUIRK_ADD: 1509ee3e3ff5SAndrew Thompson case USB_DEV_QUIRK_REMOVE: 1510a593f6b8SAndrew Thompson err = usb_quirk_ioctl_p(cmd, data, fflag, td); 1511ee3e3ff5SAndrew Thompson break; 1512ee3e3ff5SAndrew Thompson case USB_GET_TEMPLATE: 1513a593f6b8SAndrew Thompson *(int *)data = usb_template; 1514ce4092bdSHans Petter Selasky err = 0; 1515ee3e3ff5SAndrew Thompson break; 1516ee3e3ff5SAndrew Thompson case USB_SET_TEMPLATE: 151750230f98SAndrew Thompson err = priv_check(curthread, PRIV_DRIVER); 1518ee3e3ff5SAndrew Thompson if (err) 1519ee3e3ff5SAndrew Thompson break; 1520a593f6b8SAndrew Thompson usb_template = *(int *)data; 1521ee3e3ff5SAndrew Thompson break; 1522ce4092bdSHans Petter Selasky default: 1523ce4092bdSHans Petter Selasky err = ENOTTY; 1524ce4092bdSHans Petter Selasky break; 1525ee3e3ff5SAndrew Thompson } 152602ac6454SAndrew Thompson return (err); 152702ac6454SAndrew Thompson } 152802ac6454SAndrew Thompson 152902ac6454SAndrew Thompson static int 1530a593f6b8SAndrew Thompson usb_fifo_uiomove(struct usb_fifo *f, void *cp, 153102ac6454SAndrew Thompson int n, struct uio *uio) 153202ac6454SAndrew Thompson { 153302ac6454SAndrew Thompson int error; 153402ac6454SAndrew Thompson 153502ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 153602ac6454SAndrew Thompson 153702ac6454SAndrew Thompson /* 153802ac6454SAndrew Thompson * "uiomove()" can sleep so one needs to make a wrapper, 153902ac6454SAndrew Thompson * exiting the mutex and checking things: 154002ac6454SAndrew Thompson */ 154102ac6454SAndrew Thompson error = uiomove(cp, n, uio); 154202ac6454SAndrew Thompson 154302ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 154402ac6454SAndrew Thompson 154502ac6454SAndrew Thompson return (error); 154602ac6454SAndrew Thompson } 154702ac6454SAndrew Thompson 154802ac6454SAndrew Thompson int 1549a593f6b8SAndrew Thompson usb_fifo_wait(struct usb_fifo *f) 155002ac6454SAndrew Thompson { 155102ac6454SAndrew Thompson int err; 155202ac6454SAndrew Thompson 155302ac6454SAndrew Thompson mtx_assert(f->priv_mtx, MA_OWNED); 155402ac6454SAndrew Thompson 155502ac6454SAndrew Thompson if (f->flag_iserror) { 155602ac6454SAndrew Thompson /* we are gone */ 155702ac6454SAndrew Thompson return (EIO); 155802ac6454SAndrew Thompson } 155902ac6454SAndrew Thompson f->flag_sleeping = 1; 156002ac6454SAndrew Thompson 15618437751dSAndrew Thompson err = cv_wait_sig(&f->cv_io, f->priv_mtx); 156202ac6454SAndrew Thompson 156302ac6454SAndrew Thompson if (f->flag_iserror) { 156402ac6454SAndrew Thompson /* we are gone */ 156502ac6454SAndrew Thompson err = EIO; 156602ac6454SAndrew Thompson } 156702ac6454SAndrew Thompson return (err); 156802ac6454SAndrew Thompson } 156902ac6454SAndrew Thompson 157002ac6454SAndrew Thompson void 1571a593f6b8SAndrew Thompson usb_fifo_signal(struct usb_fifo *f) 157202ac6454SAndrew Thompson { 157302ac6454SAndrew Thompson if (f->flag_sleeping) { 157402ac6454SAndrew Thompson f->flag_sleeping = 0; 15758437751dSAndrew Thompson cv_broadcast(&f->cv_io); 157602ac6454SAndrew Thompson } 157702ac6454SAndrew Thompson } 157802ac6454SAndrew Thompson 157902ac6454SAndrew Thompson void 1580a593f6b8SAndrew Thompson usb_fifo_wakeup(struct usb_fifo *f) 158102ac6454SAndrew Thompson { 1582a593f6b8SAndrew Thompson usb_fifo_signal(f); 158302ac6454SAndrew Thompson 158402ac6454SAndrew Thompson if (f->flag_isselect) { 158502ac6454SAndrew Thompson selwakeup(&f->selinfo); 158602ac6454SAndrew Thompson f->flag_isselect = 0; 158702ac6454SAndrew Thompson } 158802ac6454SAndrew Thompson if (f->async_p != NULL) { 158902ac6454SAndrew Thompson PROC_LOCK(f->async_p); 15908451d0ddSKip Macy kern_psignal(f->async_p, SIGIO); 159102ac6454SAndrew Thompson PROC_UNLOCK(f->async_p); 159202ac6454SAndrew Thompson } 159302ac6454SAndrew Thompson } 159402ac6454SAndrew Thompson 159502ac6454SAndrew Thompson static int 1596a593f6b8SAndrew Thompson usb_fifo_dummy_open(struct usb_fifo *fifo, int fflags) 159702ac6454SAndrew Thompson { 159802ac6454SAndrew Thompson return (0); 159902ac6454SAndrew Thompson } 160002ac6454SAndrew Thompson 160102ac6454SAndrew Thompson static void 1602a593f6b8SAndrew Thompson usb_fifo_dummy_close(struct usb_fifo *fifo, int fflags) 160302ac6454SAndrew Thompson { 160402ac6454SAndrew Thompson return; 160502ac6454SAndrew Thompson } 160602ac6454SAndrew Thompson 160702ac6454SAndrew Thompson static int 1608a593f6b8SAndrew Thompson usb_fifo_dummy_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) 160902ac6454SAndrew Thompson { 161002ac6454SAndrew Thompson return (ENOIOCTL); 161102ac6454SAndrew Thompson } 161202ac6454SAndrew Thompson 161302ac6454SAndrew Thompson static void 1614a593f6b8SAndrew Thompson usb_fifo_dummy_cmd(struct usb_fifo *fifo) 161502ac6454SAndrew Thompson { 161602ac6454SAndrew Thompson fifo->flag_flushing = 0; /* not flushing */ 161702ac6454SAndrew Thompson } 161802ac6454SAndrew Thompson 161902ac6454SAndrew Thompson static void 1620a593f6b8SAndrew Thompson usb_fifo_check_methods(struct usb_fifo_methods *pm) 162102ac6454SAndrew Thompson { 162202ac6454SAndrew Thompson /* check that all callback functions are OK */ 162302ac6454SAndrew Thompson 162402ac6454SAndrew Thompson if (pm->f_open == NULL) 1625a593f6b8SAndrew Thompson pm->f_open = &usb_fifo_dummy_open; 162602ac6454SAndrew Thompson 162702ac6454SAndrew Thompson if (pm->f_close == NULL) 1628a593f6b8SAndrew Thompson pm->f_close = &usb_fifo_dummy_close; 162902ac6454SAndrew Thompson 163002ac6454SAndrew Thompson if (pm->f_ioctl == NULL) 1631a593f6b8SAndrew Thompson pm->f_ioctl = &usb_fifo_dummy_ioctl; 163202ac6454SAndrew Thompson 163302ac6454SAndrew Thompson if (pm->f_ioctl_post == NULL) 1634a593f6b8SAndrew Thompson pm->f_ioctl_post = &usb_fifo_dummy_ioctl; 163502ac6454SAndrew Thompson 163602ac6454SAndrew Thompson if (pm->f_start_read == NULL) 1637a593f6b8SAndrew Thompson pm->f_start_read = &usb_fifo_dummy_cmd; 163802ac6454SAndrew Thompson 163902ac6454SAndrew Thompson if (pm->f_stop_read == NULL) 1640a593f6b8SAndrew Thompson pm->f_stop_read = &usb_fifo_dummy_cmd; 164102ac6454SAndrew Thompson 164202ac6454SAndrew Thompson if (pm->f_start_write == NULL) 1643a593f6b8SAndrew Thompson pm->f_start_write = &usb_fifo_dummy_cmd; 164402ac6454SAndrew Thompson 164502ac6454SAndrew Thompson if (pm->f_stop_write == NULL) 1646a593f6b8SAndrew Thompson pm->f_stop_write = &usb_fifo_dummy_cmd; 164702ac6454SAndrew Thompson } 164802ac6454SAndrew Thompson 164902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1650a593f6b8SAndrew Thompson * usb_fifo_attach 165102ac6454SAndrew Thompson * 165202ac6454SAndrew Thompson * The following function will create a duplex FIFO. 165302ac6454SAndrew Thompson * 165402ac6454SAndrew Thompson * Return values: 165502ac6454SAndrew Thompson * 0: Success. 165602ac6454SAndrew Thompson * Else: Failure. 165702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 165802ac6454SAndrew Thompson int 1659a593f6b8SAndrew Thompson usb_fifo_attach(struct usb_device *udev, void *priv_sc, 1660760bc48eSAndrew Thompson struct mtx *priv_mtx, struct usb_fifo_methods *pm, 16616d917491SHans Petter Selasky struct usb_fifo_sc *f_sc, uint16_t unit, int16_t subunit, 1662ee3e3ff5SAndrew Thompson uint8_t iface_index, uid_t uid, gid_t gid, int mode) 166302ac6454SAndrew Thompson { 1664760bc48eSAndrew Thompson struct usb_fifo *f_tx; 1665760bc48eSAndrew Thompson struct usb_fifo *f_rx; 1666ee3e3ff5SAndrew Thompson char devname[32]; 166702ac6454SAndrew Thompson uint8_t n; 166802ac6454SAndrew Thompson 166902ac6454SAndrew Thompson f_sc->fp[USB_FIFO_TX] = NULL; 167002ac6454SAndrew Thompson f_sc->fp[USB_FIFO_RX] = NULL; 167102ac6454SAndrew Thompson 167202ac6454SAndrew Thompson if (pm == NULL) 167302ac6454SAndrew Thompson return (EINVAL); 167402ac6454SAndrew Thompson 167502ac6454SAndrew Thompson /* check the methods */ 1676a593f6b8SAndrew Thompson usb_fifo_check_methods(pm); 167702ac6454SAndrew Thompson 167802ac6454SAndrew Thompson if (priv_mtx == NULL) 167902ac6454SAndrew Thompson priv_mtx = &Giant; 168002ac6454SAndrew Thompson 168102ac6454SAndrew Thompson /* search for a free FIFO slot */ 168202ac6454SAndrew Thompson for (n = 0;; n += 2) { 168302ac6454SAndrew Thompson 168402ac6454SAndrew Thompson if (n == USB_FIFO_MAX) { 168502ac6454SAndrew Thompson /* end of FIFOs reached */ 168602ac6454SAndrew Thompson return (ENOMEM); 168702ac6454SAndrew Thompson } 168802ac6454SAndrew Thompson /* Check for TX FIFO */ 168902ac6454SAndrew Thompson if (udev->fifo[n + USB_FIFO_TX] != NULL) { 169002ac6454SAndrew Thompson continue; 169102ac6454SAndrew Thompson } 169202ac6454SAndrew Thompson /* Check for RX FIFO */ 169302ac6454SAndrew Thompson if (udev->fifo[n + USB_FIFO_RX] != NULL) { 169402ac6454SAndrew Thompson continue; 169502ac6454SAndrew Thompson } 169602ac6454SAndrew Thompson break; 169702ac6454SAndrew Thompson } 169802ac6454SAndrew Thompson 1699a593f6b8SAndrew Thompson f_tx = usb_fifo_alloc(); 1700a593f6b8SAndrew Thompson f_rx = usb_fifo_alloc(); 170102ac6454SAndrew Thompson 170202ac6454SAndrew Thompson if ((f_tx == NULL) || (f_rx == NULL)) { 1703a593f6b8SAndrew Thompson usb_fifo_free(f_tx); 1704a593f6b8SAndrew Thompson usb_fifo_free(f_rx); 170502ac6454SAndrew Thompson return (ENOMEM); 170602ac6454SAndrew Thompson } 170702ac6454SAndrew Thompson /* initialise FIFO structures */ 170802ac6454SAndrew Thompson 170902ac6454SAndrew Thompson f_tx->fifo_index = n + USB_FIFO_TX; 17102989a677SAndrew Thompson f_tx->dev_ep_index = -1; 171102ac6454SAndrew Thompson f_tx->priv_mtx = priv_mtx; 171202ac6454SAndrew Thompson f_tx->priv_sc0 = priv_sc; 171302ac6454SAndrew Thompson f_tx->methods = pm; 171402ac6454SAndrew Thompson f_tx->iface_index = iface_index; 171502ac6454SAndrew Thompson f_tx->udev = udev; 171602ac6454SAndrew Thompson 171702ac6454SAndrew Thompson f_rx->fifo_index = n + USB_FIFO_RX; 17182989a677SAndrew Thompson f_rx->dev_ep_index = -1; 171902ac6454SAndrew Thompson f_rx->priv_mtx = priv_mtx; 172002ac6454SAndrew Thompson f_rx->priv_sc0 = priv_sc; 172102ac6454SAndrew Thompson f_rx->methods = pm; 172202ac6454SAndrew Thompson f_rx->iface_index = iface_index; 172302ac6454SAndrew Thompson f_rx->udev = udev; 172402ac6454SAndrew Thompson 172502ac6454SAndrew Thompson f_sc->fp[USB_FIFO_TX] = f_tx; 172602ac6454SAndrew Thompson f_sc->fp[USB_FIFO_RX] = f_rx; 172702ac6454SAndrew Thompson 1728a593f6b8SAndrew Thompson mtx_lock(&usb_ref_lock); 172902ac6454SAndrew Thompson udev->fifo[f_tx->fifo_index] = f_tx; 173002ac6454SAndrew Thompson udev->fifo[f_rx->fifo_index] = f_rx; 1731a593f6b8SAndrew Thompson mtx_unlock(&usb_ref_lock); 173202ac6454SAndrew Thompson 173302ac6454SAndrew Thompson for (n = 0; n != 4; n++) { 173402ac6454SAndrew Thompson 173502ac6454SAndrew Thompson if (pm->basename[n] == NULL) { 173602ac6454SAndrew Thompson continue; 173702ac6454SAndrew Thompson } 17386d917491SHans Petter Selasky if (subunit < 0) { 1739ee3e3ff5SAndrew Thompson if (snprintf(devname, sizeof(devname), 174002ac6454SAndrew Thompson "%s%u%s", pm->basename[n], 174102ac6454SAndrew Thompson unit, pm->postfix[n] ? 174202ac6454SAndrew Thompson pm->postfix[n] : "")) { 174302ac6454SAndrew Thompson /* ignore */ 174402ac6454SAndrew Thompson } 174502ac6454SAndrew Thompson } else { 1746ee3e3ff5SAndrew Thompson if (snprintf(devname, sizeof(devname), 17476d917491SHans Petter Selasky "%s%u.%d%s", pm->basename[n], 174802ac6454SAndrew Thompson unit, subunit, pm->postfix[n] ? 174902ac6454SAndrew Thompson pm->postfix[n] : "")) { 175002ac6454SAndrew Thompson /* ignore */ 175102ac6454SAndrew Thompson } 175202ac6454SAndrew Thompson } 175302ac6454SAndrew Thompson 175402ac6454SAndrew Thompson /* 175502ac6454SAndrew Thompson * Distribute the symbolic links into two FIFO structures: 175602ac6454SAndrew Thompson */ 175702ac6454SAndrew Thompson if (n & 1) { 175802ac6454SAndrew Thompson f_rx->symlink[n / 2] = 1759a593f6b8SAndrew Thompson usb_alloc_symlink(devname); 176002ac6454SAndrew Thompson } else { 176102ac6454SAndrew Thompson f_tx->symlink[n / 2] = 1762a593f6b8SAndrew Thompson usb_alloc_symlink(devname); 176302ac6454SAndrew Thompson } 1764ee3e3ff5SAndrew Thompson 17652ffd5fdcSHans Petter Selasky /* Create the device */ 17662ffd5fdcSHans Petter Selasky f_sc->dev = usb_make_dev(udev, devname, -1, 17672ffd5fdcSHans Petter Selasky f_tx->fifo_index & f_rx->fifo_index, 17682ffd5fdcSHans Petter Selasky FREAD|FWRITE, uid, gid, mode); 176902ac6454SAndrew Thompson } 177002ac6454SAndrew Thompson 177102ac6454SAndrew Thompson DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx); 177202ac6454SAndrew Thompson return (0); 177302ac6454SAndrew Thompson } 177402ac6454SAndrew Thompson 177502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1776a593f6b8SAndrew Thompson * usb_fifo_alloc_buffer 177702ac6454SAndrew Thompson * 177802ac6454SAndrew Thompson * Return values: 177902ac6454SAndrew Thompson * 0: Success 178002ac6454SAndrew Thompson * Else failure 178102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 178202ac6454SAndrew Thompson int 1783a593f6b8SAndrew Thompson usb_fifo_alloc_buffer(struct usb_fifo *f, usb_size_t bufsize, 178402ac6454SAndrew Thompson uint16_t nbuf) 178502ac6454SAndrew Thompson { 1786a593f6b8SAndrew Thompson usb_fifo_free_buffer(f); 178702ac6454SAndrew Thompson 178802ac6454SAndrew Thompson /* allocate an endpoint */ 178902ac6454SAndrew Thompson f->free_q.ifq_maxlen = nbuf; 179002ac6454SAndrew Thompson f->used_q.ifq_maxlen = nbuf; 179102ac6454SAndrew Thompson 1792a593f6b8SAndrew Thompson f->queue_data = usb_alloc_mbufs( 179302ac6454SAndrew Thompson M_USBDEV, &f->free_q, bufsize, nbuf); 179402ac6454SAndrew Thompson 179502ac6454SAndrew Thompson if ((f->queue_data == NULL) && bufsize && nbuf) { 179602ac6454SAndrew Thompson return (ENOMEM); 179702ac6454SAndrew Thompson } 179802ac6454SAndrew Thompson return (0); /* success */ 179902ac6454SAndrew Thompson } 180002ac6454SAndrew Thompson 180102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1802a593f6b8SAndrew Thompson * usb_fifo_free_buffer 180302ac6454SAndrew Thompson * 180402ac6454SAndrew Thompson * This function will free the buffers associated with a FIFO. This 180502ac6454SAndrew Thompson * function can be called multiple times in a row. 180602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 180702ac6454SAndrew Thompson void 1808a593f6b8SAndrew Thompson usb_fifo_free_buffer(struct usb_fifo *f) 180902ac6454SAndrew Thompson { 181002ac6454SAndrew Thompson if (f->queue_data) { 181102ac6454SAndrew Thompson /* free old buffer */ 181202ac6454SAndrew Thompson free(f->queue_data, M_USBDEV); 181302ac6454SAndrew Thompson f->queue_data = NULL; 181402ac6454SAndrew Thompson } 181502ac6454SAndrew Thompson /* reset queues */ 181602ac6454SAndrew Thompson 1817271ae033SHans Petter Selasky memset(&f->free_q, 0, sizeof(f->free_q)); 1818271ae033SHans Petter Selasky memset(&f->used_q, 0, sizeof(f->used_q)); 181902ac6454SAndrew Thompson } 182002ac6454SAndrew Thompson 182102ac6454SAndrew Thompson void 1822a593f6b8SAndrew Thompson usb_fifo_detach(struct usb_fifo_sc *f_sc) 182302ac6454SAndrew Thompson { 182402ac6454SAndrew Thompson if (f_sc == NULL) { 182502ac6454SAndrew Thompson return; 182602ac6454SAndrew Thompson } 1827a593f6b8SAndrew Thompson usb_fifo_free(f_sc->fp[USB_FIFO_TX]); 1828a593f6b8SAndrew Thompson usb_fifo_free(f_sc->fp[USB_FIFO_RX]); 182902ac6454SAndrew Thompson 183002ac6454SAndrew Thompson f_sc->fp[USB_FIFO_TX] = NULL; 183102ac6454SAndrew Thompson f_sc->fp[USB_FIFO_RX] = NULL; 183202ac6454SAndrew Thompson 18332ffd5fdcSHans Petter Selasky usb_destroy_dev(f_sc->dev); 18342ffd5fdcSHans Petter Selasky 18357214348fSAndrew Thompson f_sc->dev = NULL; 1836ee3e3ff5SAndrew Thompson 183702ac6454SAndrew Thompson DPRINTFN(2, "detached %p\n", f_sc); 183802ac6454SAndrew Thompson } 183902ac6454SAndrew Thompson 1840f9cb546cSAndrew Thompson usb_size_t 1841a593f6b8SAndrew Thompson usb_fifo_put_bytes_max(struct usb_fifo *f) 184202ac6454SAndrew Thompson { 1843760bc48eSAndrew Thompson struct usb_mbuf *m; 1844f9cb546cSAndrew Thompson usb_size_t len; 184502ac6454SAndrew Thompson 184602ac6454SAndrew Thompson USB_IF_POLL(&f->free_q, m); 184702ac6454SAndrew Thompson 184802ac6454SAndrew Thompson if (m) { 184902ac6454SAndrew Thompson len = m->max_data_len; 185002ac6454SAndrew Thompson } else { 185102ac6454SAndrew Thompson len = 0; 185202ac6454SAndrew Thompson } 185302ac6454SAndrew Thompson return (len); 185402ac6454SAndrew Thompson } 185502ac6454SAndrew Thompson 185602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1857a593f6b8SAndrew Thompson * usb_fifo_put_data 185802ac6454SAndrew Thompson * 185902ac6454SAndrew Thompson * what: 186002ac6454SAndrew Thompson * 0 - normal operation 186102ac6454SAndrew Thompson * 1 - set last packet flag to enforce framing 186202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 186302ac6454SAndrew Thompson void 1864a593f6b8SAndrew Thompson usb_fifo_put_data(struct usb_fifo *f, struct usb_page_cache *pc, 1865e0a69b51SAndrew Thompson usb_frlength_t offset, usb_frlength_t len, uint8_t what) 186602ac6454SAndrew Thompson { 1867760bc48eSAndrew Thompson struct usb_mbuf *m; 1868e0a69b51SAndrew Thompson usb_frlength_t io_len; 186902ac6454SAndrew Thompson 187002ac6454SAndrew Thompson while (len || (what == 1)) { 187102ac6454SAndrew Thompson 187202ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 187302ac6454SAndrew Thompson 187402ac6454SAndrew Thompson if (m) { 187502ac6454SAndrew Thompson USB_MBUF_RESET(m); 187602ac6454SAndrew Thompson 187702ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 187802ac6454SAndrew Thompson 1879a593f6b8SAndrew Thompson usbd_copy_out(pc, offset, m->cur_data_ptr, io_len); 188002ac6454SAndrew Thompson 188102ac6454SAndrew Thompson m->cur_data_len = io_len; 188202ac6454SAndrew Thompson offset += io_len; 188302ac6454SAndrew Thompson len -= io_len; 188402ac6454SAndrew Thompson 188502ac6454SAndrew Thompson if ((len == 0) && (what == 1)) { 188602ac6454SAndrew Thompson m->last_packet = 1; 188702ac6454SAndrew Thompson } 188802ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 188902ac6454SAndrew Thompson 1890a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 189102ac6454SAndrew Thompson 189202ac6454SAndrew Thompson if ((len == 0) || (what == 1)) { 189302ac6454SAndrew Thompson break; 189402ac6454SAndrew Thompson } 189502ac6454SAndrew Thompson } else { 189602ac6454SAndrew Thompson break; 189702ac6454SAndrew Thompson } 189802ac6454SAndrew Thompson } 189902ac6454SAndrew Thompson } 190002ac6454SAndrew Thompson 190102ac6454SAndrew Thompson void 1902a593f6b8SAndrew Thompson usb_fifo_put_data_linear(struct usb_fifo *f, void *ptr, 1903f9cb546cSAndrew Thompson usb_size_t len, uint8_t what) 190402ac6454SAndrew Thompson { 1905760bc48eSAndrew Thompson struct usb_mbuf *m; 1906f9cb546cSAndrew Thompson usb_size_t io_len; 190702ac6454SAndrew Thompson 190802ac6454SAndrew Thompson while (len || (what == 1)) { 190902ac6454SAndrew Thompson 191002ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 191102ac6454SAndrew Thompson 191202ac6454SAndrew Thompson if (m) { 191302ac6454SAndrew Thompson USB_MBUF_RESET(m); 191402ac6454SAndrew Thompson 191502ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 191602ac6454SAndrew Thompson 1917271ae033SHans Petter Selasky memcpy(m->cur_data_ptr, ptr, io_len); 191802ac6454SAndrew Thompson 191902ac6454SAndrew Thompson m->cur_data_len = io_len; 192002ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, io_len); 192102ac6454SAndrew Thompson len -= io_len; 192202ac6454SAndrew Thompson 192302ac6454SAndrew Thompson if ((len == 0) && (what == 1)) { 192402ac6454SAndrew Thompson m->last_packet = 1; 192502ac6454SAndrew Thompson } 192602ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 192702ac6454SAndrew Thompson 1928a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 192902ac6454SAndrew Thompson 193002ac6454SAndrew Thompson if ((len == 0) || (what == 1)) { 193102ac6454SAndrew Thompson break; 193202ac6454SAndrew Thompson } 193302ac6454SAndrew Thompson } else { 193402ac6454SAndrew Thompson break; 193502ac6454SAndrew Thompson } 193602ac6454SAndrew Thompson } 193702ac6454SAndrew Thompson } 193802ac6454SAndrew Thompson 193902ac6454SAndrew Thompson uint8_t 1940a593f6b8SAndrew Thompson usb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len) 194102ac6454SAndrew Thompson { 1942760bc48eSAndrew Thompson struct usb_mbuf *m; 194302ac6454SAndrew Thompson 194402ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 194502ac6454SAndrew Thompson 194602ac6454SAndrew Thompson if (m) { 194702ac6454SAndrew Thompson m->cur_data_len = len; 194802ac6454SAndrew Thompson m->cur_data_ptr = ptr; 194902ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 1950a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 195102ac6454SAndrew Thompson return (1); 195202ac6454SAndrew Thompson } 195302ac6454SAndrew Thompson return (0); 195402ac6454SAndrew Thompson } 195502ac6454SAndrew Thompson 195602ac6454SAndrew Thompson void 1957a593f6b8SAndrew Thompson usb_fifo_put_data_error(struct usb_fifo *f) 195802ac6454SAndrew Thompson { 195902ac6454SAndrew Thompson f->flag_iserror = 1; 1960a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 196102ac6454SAndrew Thompson } 196202ac6454SAndrew Thompson 196302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 1964a593f6b8SAndrew Thompson * usb_fifo_get_data 196502ac6454SAndrew Thompson * 196602ac6454SAndrew Thompson * what: 196702ac6454SAndrew Thompson * 0 - normal operation 1968760bc48eSAndrew Thompson * 1 - only get one "usb_mbuf" 196902ac6454SAndrew Thompson * 197002ac6454SAndrew Thompson * returns: 197102ac6454SAndrew Thompson * 0 - no more data 197202ac6454SAndrew Thompson * 1 - data in buffer 197302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 197402ac6454SAndrew Thompson uint8_t 1975a593f6b8SAndrew Thompson usb_fifo_get_data(struct usb_fifo *f, struct usb_page_cache *pc, 1976e0a69b51SAndrew Thompson usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen, 197702ac6454SAndrew Thompson uint8_t what) 197802ac6454SAndrew Thompson { 1979760bc48eSAndrew Thompson struct usb_mbuf *m; 1980e0a69b51SAndrew Thompson usb_frlength_t io_len; 198102ac6454SAndrew Thompson uint8_t tr_data = 0; 198202ac6454SAndrew Thompson 198302ac6454SAndrew Thompson actlen[0] = 0; 198402ac6454SAndrew Thompson 198502ac6454SAndrew Thompson while (1) { 198602ac6454SAndrew Thompson 198702ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 198802ac6454SAndrew Thompson 198902ac6454SAndrew Thompson if (m) { 199002ac6454SAndrew Thompson 199102ac6454SAndrew Thompson tr_data = 1; 199202ac6454SAndrew Thompson 199302ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 199402ac6454SAndrew Thompson 1995a593f6b8SAndrew Thompson usbd_copy_in(pc, offset, m->cur_data_ptr, io_len); 199602ac6454SAndrew Thompson 199702ac6454SAndrew Thompson len -= io_len; 199802ac6454SAndrew Thompson offset += io_len; 199902ac6454SAndrew Thompson actlen[0] += io_len; 200002ac6454SAndrew Thompson m->cur_data_ptr += io_len; 200102ac6454SAndrew Thompson m->cur_data_len -= io_len; 200202ac6454SAndrew Thompson 200302ac6454SAndrew Thompson if ((m->cur_data_len == 0) || (what == 1)) { 200402ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 200502ac6454SAndrew Thompson 2006a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 200702ac6454SAndrew Thompson 200802ac6454SAndrew Thompson if (what == 1) { 200902ac6454SAndrew Thompson break; 201002ac6454SAndrew Thompson } 201102ac6454SAndrew Thompson } else { 201202ac6454SAndrew Thompson USB_IF_PREPEND(&f->used_q, m); 201302ac6454SAndrew Thompson } 201402ac6454SAndrew Thompson } else { 201502ac6454SAndrew Thompson 201602ac6454SAndrew Thompson if (tr_data) { 201702ac6454SAndrew Thompson /* wait for data to be written out */ 201802ac6454SAndrew Thompson break; 201902ac6454SAndrew Thompson } 202002ac6454SAndrew Thompson if (f->flag_flushing) { 20217214348fSAndrew Thompson /* check if we should send a short packet */ 20227214348fSAndrew Thompson if (f->flag_short != 0) { 20237214348fSAndrew Thompson f->flag_short = 0; 20247214348fSAndrew Thompson tr_data = 1; 20257214348fSAndrew Thompson break; 20267214348fSAndrew Thompson } 20277214348fSAndrew Thompson /* flushing complete */ 202802ac6454SAndrew Thompson f->flag_flushing = 0; 2029a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 203002ac6454SAndrew Thompson } 203102ac6454SAndrew Thompson break; 203202ac6454SAndrew Thompson } 203302ac6454SAndrew Thompson if (len == 0) { 203402ac6454SAndrew Thompson break; 203502ac6454SAndrew Thompson } 203602ac6454SAndrew Thompson } 203702ac6454SAndrew Thompson return (tr_data); 203802ac6454SAndrew Thompson } 203902ac6454SAndrew Thompson 204002ac6454SAndrew Thompson uint8_t 2041a593f6b8SAndrew Thompson usb_fifo_get_data_linear(struct usb_fifo *f, void *ptr, 2042f9cb546cSAndrew Thompson usb_size_t len, usb_size_t *actlen, uint8_t what) 204302ac6454SAndrew Thompson { 2044760bc48eSAndrew Thompson struct usb_mbuf *m; 2045f9cb546cSAndrew Thompson usb_size_t io_len; 204602ac6454SAndrew Thompson uint8_t tr_data = 0; 204702ac6454SAndrew Thompson 204802ac6454SAndrew Thompson actlen[0] = 0; 204902ac6454SAndrew Thompson 205002ac6454SAndrew Thompson while (1) { 205102ac6454SAndrew Thompson 205202ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 205302ac6454SAndrew Thompson 205402ac6454SAndrew Thompson if (m) { 205502ac6454SAndrew Thompson 205602ac6454SAndrew Thompson tr_data = 1; 205702ac6454SAndrew Thompson 205802ac6454SAndrew Thompson io_len = MIN(len, m->cur_data_len); 205902ac6454SAndrew Thompson 2060271ae033SHans Petter Selasky memcpy(ptr, m->cur_data_ptr, io_len); 206102ac6454SAndrew Thompson 206202ac6454SAndrew Thompson len -= io_len; 206302ac6454SAndrew Thompson ptr = USB_ADD_BYTES(ptr, io_len); 206402ac6454SAndrew Thompson actlen[0] += io_len; 206502ac6454SAndrew Thompson m->cur_data_ptr += io_len; 206602ac6454SAndrew Thompson m->cur_data_len -= io_len; 206702ac6454SAndrew Thompson 206802ac6454SAndrew Thompson if ((m->cur_data_len == 0) || (what == 1)) { 206902ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 207002ac6454SAndrew Thompson 2071a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 207202ac6454SAndrew Thompson 207302ac6454SAndrew Thompson if (what == 1) { 207402ac6454SAndrew Thompson break; 207502ac6454SAndrew Thompson } 207602ac6454SAndrew Thompson } else { 207702ac6454SAndrew Thompson USB_IF_PREPEND(&f->used_q, m); 207802ac6454SAndrew Thompson } 207902ac6454SAndrew Thompson } else { 208002ac6454SAndrew Thompson 208102ac6454SAndrew Thompson if (tr_data) { 208202ac6454SAndrew Thompson /* wait for data to be written out */ 208302ac6454SAndrew Thompson break; 208402ac6454SAndrew Thompson } 208502ac6454SAndrew Thompson if (f->flag_flushing) { 20867214348fSAndrew Thompson /* check if we should send a short packet */ 20877214348fSAndrew Thompson if (f->flag_short != 0) { 20887214348fSAndrew Thompson f->flag_short = 0; 20897214348fSAndrew Thompson tr_data = 1; 20907214348fSAndrew Thompson break; 20917214348fSAndrew Thompson } 20927214348fSAndrew Thompson /* flushing complete */ 209302ac6454SAndrew Thompson f->flag_flushing = 0; 2094a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 209502ac6454SAndrew Thompson } 209602ac6454SAndrew Thompson break; 209702ac6454SAndrew Thompson } 209802ac6454SAndrew Thompson if (len == 0) { 209902ac6454SAndrew Thompson break; 210002ac6454SAndrew Thompson } 210102ac6454SAndrew Thompson } 210202ac6454SAndrew Thompson return (tr_data); 210302ac6454SAndrew Thompson } 210402ac6454SAndrew Thompson 210502ac6454SAndrew Thompson uint8_t 2106a593f6b8SAndrew Thompson usb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen) 210702ac6454SAndrew Thompson { 2108760bc48eSAndrew Thompson struct usb_mbuf *m; 210902ac6454SAndrew Thompson 211002ac6454SAndrew Thompson USB_IF_POLL(&f->used_q, m); 211102ac6454SAndrew Thompson 211202ac6454SAndrew Thompson if (m) { 211302ac6454SAndrew Thompson *plen = m->cur_data_len; 211402ac6454SAndrew Thompson *pptr = m->cur_data_ptr; 211502ac6454SAndrew Thompson 211602ac6454SAndrew Thompson return (1); 211702ac6454SAndrew Thompson } 211802ac6454SAndrew Thompson return (0); 211902ac6454SAndrew Thompson } 212002ac6454SAndrew Thompson 212102ac6454SAndrew Thompson void 2122a593f6b8SAndrew Thompson usb_fifo_get_data_error(struct usb_fifo *f) 212302ac6454SAndrew Thompson { 212402ac6454SAndrew Thompson f->flag_iserror = 1; 2125a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 212602ac6454SAndrew Thompson } 212702ac6454SAndrew Thompson 212802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2129a593f6b8SAndrew Thompson * usb_alloc_symlink 213002ac6454SAndrew Thompson * 213102ac6454SAndrew Thompson * Return values: 213202ac6454SAndrew Thompson * NULL: Failure 213302ac6454SAndrew Thompson * Else: Pointer to symlink entry 213402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 2135760bc48eSAndrew Thompson struct usb_symlink * 2136a593f6b8SAndrew Thompson usb_alloc_symlink(const char *target) 213702ac6454SAndrew Thompson { 2138760bc48eSAndrew Thompson struct usb_symlink *ps; 213902ac6454SAndrew Thompson 214002ac6454SAndrew Thompson ps = malloc(sizeof(*ps), M_USBDEV, M_WAITOK); 214102ac6454SAndrew Thompson if (ps == NULL) { 214202ac6454SAndrew Thompson return (ps); 214302ac6454SAndrew Thompson } 2144ee3e3ff5SAndrew Thompson /* XXX no longer needed */ 2145ee3e3ff5SAndrew Thompson strlcpy(ps->src_path, target, sizeof(ps->src_path)); 2146ee3e3ff5SAndrew Thompson ps->src_len = strlen(ps->src_path); 214702ac6454SAndrew Thompson strlcpy(ps->dst_path, target, sizeof(ps->dst_path)); 214802ac6454SAndrew Thompson ps->dst_len = strlen(ps->dst_path); 214902ac6454SAndrew Thompson 2150a593f6b8SAndrew Thompson sx_xlock(&usb_sym_lock); 2151a593f6b8SAndrew Thompson TAILQ_INSERT_TAIL(&usb_sym_head, ps, sym_entry); 2152a593f6b8SAndrew Thompson sx_unlock(&usb_sym_lock); 215302ac6454SAndrew Thompson return (ps); 215402ac6454SAndrew Thompson } 215502ac6454SAndrew Thompson 215602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2157a593f6b8SAndrew Thompson * usb_free_symlink 215802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 215902ac6454SAndrew Thompson void 2160a593f6b8SAndrew Thompson usb_free_symlink(struct usb_symlink *ps) 216102ac6454SAndrew Thompson { 216202ac6454SAndrew Thompson if (ps == NULL) { 216302ac6454SAndrew Thompson return; 216402ac6454SAndrew Thompson } 2165a593f6b8SAndrew Thompson sx_xlock(&usb_sym_lock); 2166a593f6b8SAndrew Thompson TAILQ_REMOVE(&usb_sym_head, ps, sym_entry); 2167a593f6b8SAndrew Thompson sx_unlock(&usb_sym_lock); 216802ac6454SAndrew Thompson 216902ac6454SAndrew Thompson free(ps, M_USBDEV); 217002ac6454SAndrew Thompson } 217102ac6454SAndrew Thompson 217202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2173a593f6b8SAndrew Thompson * usb_read_symlink 217402ac6454SAndrew Thompson * 217502ac6454SAndrew Thompson * Return value: 217602ac6454SAndrew Thompson * 0: Success 217702ac6454SAndrew Thompson * Else: Failure 217802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 217902ac6454SAndrew Thompson int 2180a593f6b8SAndrew Thompson usb_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len) 218102ac6454SAndrew Thompson { 2182760bc48eSAndrew Thompson struct usb_symlink *ps; 218302ac6454SAndrew Thompson uint32_t temp; 218402ac6454SAndrew Thompson uint32_t delta = 0; 218502ac6454SAndrew Thompson uint8_t len; 218602ac6454SAndrew Thompson int error = 0; 218702ac6454SAndrew Thompson 2188a593f6b8SAndrew Thompson sx_xlock(&usb_sym_lock); 218902ac6454SAndrew Thompson 2190a593f6b8SAndrew Thompson TAILQ_FOREACH(ps, &usb_sym_head, sym_entry) { 219102ac6454SAndrew Thompson 219202ac6454SAndrew Thompson /* 219302ac6454SAndrew Thompson * Compute total length of source and destination symlink 219402ac6454SAndrew Thompson * strings pluss one length byte and two NUL bytes: 219502ac6454SAndrew Thompson */ 219602ac6454SAndrew Thompson temp = ps->src_len + ps->dst_len + 3; 219702ac6454SAndrew Thompson 219802ac6454SAndrew Thompson if (temp > 255) { 219902ac6454SAndrew Thompson /* 220002ac6454SAndrew Thompson * Skip entry because this length cannot fit 220102ac6454SAndrew Thompson * into one byte: 220202ac6454SAndrew Thompson */ 220302ac6454SAndrew Thompson continue; 220402ac6454SAndrew Thompson } 220502ac6454SAndrew Thompson if (startentry != 0) { 220602ac6454SAndrew Thompson /* decrement read offset */ 220702ac6454SAndrew Thompson startentry--; 220802ac6454SAndrew Thompson continue; 220902ac6454SAndrew Thompson } 221002ac6454SAndrew Thompson if (temp > user_len) { 221102ac6454SAndrew Thompson /* out of buffer space */ 221202ac6454SAndrew Thompson break; 221302ac6454SAndrew Thompson } 221402ac6454SAndrew Thompson len = temp; 221502ac6454SAndrew Thompson 221602ac6454SAndrew Thompson /* copy out total length */ 221702ac6454SAndrew Thompson 221802ac6454SAndrew Thompson error = copyout(&len, 221902ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 222002ac6454SAndrew Thompson if (error) { 222102ac6454SAndrew Thompson break; 222202ac6454SAndrew Thompson } 222302ac6454SAndrew Thompson delta += 1; 222402ac6454SAndrew Thompson 222502ac6454SAndrew Thompson /* copy out source string */ 222602ac6454SAndrew Thompson 222702ac6454SAndrew Thompson error = copyout(ps->src_path, 222802ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), ps->src_len); 222902ac6454SAndrew Thompson if (error) { 223002ac6454SAndrew Thompson break; 223102ac6454SAndrew Thompson } 223202ac6454SAndrew Thompson len = 0; 223302ac6454SAndrew Thompson delta += ps->src_len; 223402ac6454SAndrew Thompson error = copyout(&len, 223502ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 223602ac6454SAndrew Thompson if (error) { 223702ac6454SAndrew Thompson break; 223802ac6454SAndrew Thompson } 223902ac6454SAndrew Thompson delta += 1; 224002ac6454SAndrew Thompson 224102ac6454SAndrew Thompson /* copy out destination string */ 224202ac6454SAndrew Thompson 224302ac6454SAndrew Thompson error = copyout(ps->dst_path, 224402ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), ps->dst_len); 224502ac6454SAndrew Thompson if (error) { 224602ac6454SAndrew Thompson break; 224702ac6454SAndrew Thompson } 224802ac6454SAndrew Thompson len = 0; 224902ac6454SAndrew Thompson delta += ps->dst_len; 225002ac6454SAndrew Thompson error = copyout(&len, 225102ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 225202ac6454SAndrew Thompson if (error) { 225302ac6454SAndrew Thompson break; 225402ac6454SAndrew Thompson } 225502ac6454SAndrew Thompson delta += 1; 225602ac6454SAndrew Thompson 225702ac6454SAndrew Thompson user_len -= temp; 225802ac6454SAndrew Thompson } 225902ac6454SAndrew Thompson 226002ac6454SAndrew Thompson /* a zero length entry indicates the end */ 226102ac6454SAndrew Thompson 226202ac6454SAndrew Thompson if ((user_len != 0) && (error == 0)) { 226302ac6454SAndrew Thompson 226402ac6454SAndrew Thompson len = 0; 226502ac6454SAndrew Thompson 226602ac6454SAndrew Thompson error = copyout(&len, 226702ac6454SAndrew Thompson USB_ADD_BYTES(user_ptr, delta), 1); 226802ac6454SAndrew Thompson } 2269a593f6b8SAndrew Thompson sx_unlock(&usb_sym_lock); 227002ac6454SAndrew Thompson return (error); 227102ac6454SAndrew Thompson } 22727214348fSAndrew Thompson 22737214348fSAndrew Thompson void 2274a593f6b8SAndrew Thompson usb_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff) 22757214348fSAndrew Thompson { 22767214348fSAndrew Thompson if (f == NULL) 22777214348fSAndrew Thompson return; 22787214348fSAndrew Thompson 22797214348fSAndrew Thompson /* send a Zero Length Packet, ZLP, before close */ 22807214348fSAndrew Thompson f->flag_short = onoff; 22817214348fSAndrew Thompson } 2282ed6d949aSAndrew Thompson 2283bd73b187SAlfred Perlstein void 2284bd73b187SAlfred Perlstein usb_fifo_set_write_defrag(struct usb_fifo *f, uint8_t onoff) 2285bd73b187SAlfred Perlstein { 2286bd73b187SAlfred Perlstein if (f == NULL) 2287bd73b187SAlfred Perlstein return; 2288bd73b187SAlfred Perlstein 2289bd73b187SAlfred Perlstein /* defrag written data */ 2290bd73b187SAlfred Perlstein f->flag_write_defrag = onoff; 2291bd73b187SAlfred Perlstein /* reset defrag state */ 2292bd73b187SAlfred Perlstein f->flag_have_fragment = 0; 2293bd73b187SAlfred Perlstein } 2294bd73b187SAlfred Perlstein 2295ed6d949aSAndrew Thompson void * 2296ed6d949aSAndrew Thompson usb_fifo_softc(struct usb_fifo *f) 2297ed6d949aSAndrew Thompson { 2298ed6d949aSAndrew Thompson return (f->priv_sc0); 2299ed6d949aSAndrew Thompson } 23008755859aSAndrew Thompson #endif /* USB_HAVE_UGEN */ 2301