1184610Salfred/* $FreeBSD$ */ 2184610Salfred/*- 3184610Salfred * Copyright (c) 2006-2008 Hans Petter Selasky. All rights reserved. 4184610Salfred * 5184610Salfred * Redistribution and use in source and binary forms, with or without 6184610Salfred * modification, are permitted provided that the following conditions 7184610Salfred * are met: 8184610Salfred * 1. Redistributions of source code must retain the above copyright 9184610Salfred * notice, this list of conditions and the following disclaimer. 10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 11184610Salfred * notice, this list of conditions and the following disclaimer in the 12184610Salfred * documentation and/or other materials provided with the distribution. 13184610Salfred * 14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24184610Salfred * SUCH DAMAGE. 25184610Salfred * 26184610Salfred * 27194228Sthompsa * usb_dev.c - An abstraction layer for creating devices under /dev/... 28184610Salfred */ 29184610Salfred 30246122Shselasky#ifdef USB_GLOBAL_INCLUDE_FILE 31246122Shselasky#include USB_GLOBAL_INCLUDE_FILE 32246122Shselasky#else 33194677Sthompsa#include <sys/stdint.h> 34194677Sthompsa#include <sys/stddef.h> 35194677Sthompsa#include <sys/param.h> 36194677Sthompsa#include <sys/queue.h> 37194677Sthompsa#include <sys/types.h> 38194677Sthompsa#include <sys/systm.h> 39194677Sthompsa#include <sys/kernel.h> 40194677Sthompsa#include <sys/bus.h> 41194677Sthompsa#include <sys/module.h> 42194677Sthompsa#include <sys/lock.h> 43194677Sthompsa#include <sys/mutex.h> 44194677Sthompsa#include <sys/condvar.h> 45194677Sthompsa#include <sys/sysctl.h> 46194677Sthompsa#include <sys/sx.h> 47194677Sthompsa#include <sys/unistd.h> 48194677Sthompsa#include <sys/callout.h> 49194677Sthompsa#include <sys/malloc.h> 50194677Sthompsa#include <sys/priv.h> 51194677Sthompsa#include <sys/vnode.h> 52194677Sthompsa#include <sys/conf.h> 53194677Sthompsa#include <sys/fcntl.h> 54194677Sthompsa 55188942Sthompsa#include <dev/usb/usb.h> 56188942Sthompsa#include <dev/usb/usb_ioctl.h> 57194677Sthompsa#include <dev/usb/usbdi.h> 58194677Sthompsa#include <dev/usb/usbdi_util.h> 59184610Salfred 60194228Sthompsa#define USB_DEBUG_VAR usb_fifo_debug 61184610Salfred 62188942Sthompsa#include <dev/usb/usb_core.h> 63194677Sthompsa#include <dev/usb/usb_dev.h> 64188942Sthompsa#include <dev/usb/usb_mbuf.h> 65188942Sthompsa#include <dev/usb/usb_process.h> 66188942Sthompsa#include <dev/usb/usb_device.h> 67188942Sthompsa#include <dev/usb/usb_debug.h> 68188942Sthompsa#include <dev/usb/usb_busdma.h> 69188942Sthompsa#include <dev/usb/usb_generic.h> 70188942Sthompsa#include <dev/usb/usb_dynamic.h> 71188942Sthompsa#include <dev/usb/usb_util.h> 72184610Salfred 73188942Sthompsa#include <dev/usb/usb_controller.h> 74188942Sthompsa#include <dev/usb/usb_bus.h> 75184610Salfred 76184610Salfred#include <sys/filio.h> 77184610Salfred#include <sys/ttycom.h> 78184610Salfred#include <sys/syscallsubr.h> 79184610Salfred 80184610Salfred#include <machine/stdarg.h> 81246122Shselasky#endif /* USB_GLOBAL_INCLUDE_FILE */ 82184610Salfred 83190191Sthompsa#if USB_HAVE_UGEN 84190191Sthompsa 85194677Sthompsa#ifdef USB_DEBUG 86194228Sthompsastatic int usb_fifo_debug = 0; 87184610Salfred 88227309Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, dev, CTLFLAG_RW, 0, "USB device"); 89242126ShselaskySYSCTL_INT(_hw_usb_dev, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, 90194228Sthompsa &usb_fifo_debug, 0, "Debug Level"); 91199675SthompsaTUNABLE_INT("hw.usb.dev.debug", &usb_fifo_debug); 92184610Salfred#endif 93184610Salfred 94184610Salfred#if ((__FreeBSD_version >= 700001) || (__FreeBSD_version == 0) || \ 95184610Salfred ((__FreeBSD_version >= 600034) && (__FreeBSD_version < 700000))) 96184610Salfred#define USB_UCRED struct ucred *ucred, 97184610Salfred#else 98184610Salfred#define USB_UCRED 99184610Salfred#endif 100184610Salfred 101184610Salfred/* prototypes */ 102184610Salfred 103194228Sthompsastatic int usb_fifo_open(struct usb_cdev_privdata *, 104192984Sthompsa struct usb_fifo *, int); 105194228Sthompsastatic void usb_fifo_close(struct usb_fifo *, int); 106194228Sthompsastatic void usb_dev_init(void *); 107194228Sthompsastatic void usb_dev_init_post(void *); 108194228Sthompsastatic void usb_dev_uninit(void *); 109194228Sthompsastatic int usb_fifo_uiomove(struct usb_fifo *, void *, int, 110185948Sthompsa struct uio *); 111194228Sthompsastatic void usb_fifo_check_methods(struct usb_fifo_methods *); 112263162Shselaskystatic struct usb_fifo *usb_fifo_alloc(struct mtx *); 113194228Sthompsastatic struct usb_endpoint *usb_dev_get_ep(struct usb_device *, uint8_t, 114189172Sthompsa uint8_t); 115194228Sthompsastatic void usb_loc_fill(struct usb_fs_privdata *, 116192984Sthompsa struct usb_cdev_privdata *); 117194228Sthompsastatic void usb_close(void *); 118194228Sthompsastatic usb_error_t usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *, int); 119194228Sthompsastatic usb_error_t usb_usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *); 120194228Sthompsastatic void usb_unref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *); 121184610Salfred 122194228Sthompsastatic d_open_t usb_open; 123194228Sthompsastatic d_ioctl_t usb_ioctl; 124194228Sthompsastatic d_read_t usb_read; 125194228Sthompsastatic d_write_t usb_write; 126194228Sthompsastatic d_poll_t usb_poll; 127263162Shselaskystatic d_kqfilter_t usb_kqfilter; 128184610Salfred 129194228Sthompsastatic d_ioctl_t usb_static_ioctl; 130184610Salfred 131194228Sthompsastatic usb_fifo_open_t usb_fifo_dummy_open; 132194228Sthompsastatic usb_fifo_close_t usb_fifo_dummy_close; 133194228Sthompsastatic usb_fifo_ioctl_t usb_fifo_dummy_ioctl; 134194228Sthompsastatic usb_fifo_cmd_t usb_fifo_dummy_cmd; 135184610Salfred 136189110Sthompsa/* character device structure used for devices (/dev/ugenX.Y and /dev/uXXX) */ 137194228Sthompsastruct cdevsw usb_devsw = { 138184610Salfred .d_version = D_VERSION, 139194228Sthompsa .d_open = usb_open, 140194228Sthompsa .d_ioctl = usb_ioctl, 141189110Sthompsa .d_name = "usbdev", 142184610Salfred .d_flags = D_TRACKCLOSE, 143194228Sthompsa .d_read = usb_read, 144194228Sthompsa .d_write = usb_write, 145263162Shselasky .d_poll = usb_poll, 146263162Shselasky .d_kqfilter = usb_kqfilter, 147184610Salfred}; 148184610Salfred 149194228Sthompsastatic struct cdev* usb_dev = NULL; 150189110Sthompsa 151189110Sthompsa/* character device structure used for /dev/usb */ 152194228Sthompsastatic struct cdevsw usb_static_devsw = { 153189110Sthompsa .d_version = D_VERSION, 154194228Sthompsa .d_ioctl = usb_static_ioctl, 155189110Sthompsa .d_name = "usb" 156184610Salfred}; 157184610Salfred 158194228Sthompsastatic TAILQ_HEAD(, usb_symlink) usb_sym_head; 159194228Sthompsastatic struct sx usb_sym_lock; 160184610Salfred 161194228Sthompsastruct mtx usb_ref_lock; 162184610Salfred 163184610Salfred/*------------------------------------------------------------------------* 164194228Sthompsa * usb_loc_fill 165184610Salfred * 166192984Sthompsa * This is used to fill out a usb_cdev_privdata structure based on the 167192984Sthompsa * device's address as contained in usb_fs_privdata. 168184610Salfred *------------------------------------------------------------------------*/ 169189110Sthompsastatic void 170194228Sthompsausb_loc_fill(struct usb_fs_privdata* pd, struct usb_cdev_privdata *cpd) 171184610Salfred{ 172189110Sthompsa cpd->bus_index = pd->bus_index; 173189110Sthompsa cpd->dev_index = pd->dev_index; 174189110Sthompsa cpd->ep_addr = pd->ep_addr; 175189110Sthompsa cpd->fifo_index = pd->fifo_index; 176184610Salfred} 177184610Salfred 178184610Salfred/*------------------------------------------------------------------------* 179194228Sthompsa * usb_ref_device 180184610Salfred * 181184610Salfred * This function is used to atomically refer an USB device by its 182184610Salfred * device location. If this function returns success the USB device 183184610Salfred * will not dissappear until the USB device is unreferenced. 184184610Salfred * 185184610Salfred * Return values: 186184610Salfred * 0: Success, refcount incremented on the given USB device. 187184610Salfred * Else: Failure. 188184610Salfred *------------------------------------------------------------------------*/ 189208007Sthompsastatic usb_error_t 190194228Sthompsausb_ref_device(struct usb_cdev_privdata *cpd, 191193338Sthompsa struct usb_cdev_refdata *crd, int need_uref) 192184610Salfred{ 193192984Sthompsa struct usb_fifo **ppf; 194192984Sthompsa struct usb_fifo *f; 195184610Salfred 196193338Sthompsa DPRINTFN(2, "cpd=%p need uref=%d\n", cpd, need_uref); 197184610Salfred 198193338Sthompsa /* clear all refs */ 199193338Sthompsa memset(crd, 0, sizeof(*crd)); 200193338Sthompsa 201194228Sthompsa mtx_lock(&usb_ref_lock); 202194228Sthompsa cpd->bus = devclass_get_softc(usb_devclass_ptr, cpd->bus_index); 203189110Sthompsa if (cpd->bus == NULL) { 204189110Sthompsa DPRINTFN(2, "no bus at %u\n", cpd->bus_index); 205184610Salfred goto error; 206184610Salfred } 207189110Sthompsa cpd->udev = cpd->bus->devices[cpd->dev_index]; 208189110Sthompsa if (cpd->udev == NULL) { 209189110Sthompsa DPRINTFN(2, "no device at %u\n", cpd->dev_index); 210184610Salfred goto error; 211184610Salfred } 212261110Shselasky if (cpd->udev->state == USB_STATE_DETACHED && 213261110Shselasky (need_uref != 2)) { 214261110Shselasky DPRINTFN(2, "device is detached\n"); 215261110Shselasky goto error; 216261110Shselasky } 217190729Sthompsa if (need_uref) { 218190729Sthompsa DPRINTFN(2, "ref udev - needed\n"); 219263799Shselasky 220263799Shselasky if (cpd->udev->refcount == USB_DEV_REF_MAX) { 221263799Shselasky DPRINTFN(2, "no dev ref\n"); 222263799Shselasky goto error; 223263799Shselasky } 224190729Sthompsa cpd->udev->refcount++; 225190729Sthompsa 226194228Sthompsa mtx_unlock(&usb_ref_lock); 227190729Sthompsa 228190729Sthompsa /* 229246759Shselasky * We need to grab the enumeration SX-lock before 230246759Shselasky * grabbing the FIFO refs to avoid deadlock at detach! 231190729Sthompsa */ 232301253Shselasky crd->do_unlock = usbd_enum_lock_sig(cpd->udev); 233190729Sthompsa 234194228Sthompsa mtx_lock(&usb_ref_lock); 235193316Sthompsa 236193316Sthompsa /* 237193316Sthompsa * Set "is_uref" after grabbing the default SX lock 238193316Sthompsa */ 239193338Sthompsa crd->is_uref = 1; 240301253Shselasky 241301253Shselasky /* check for signal */ 242301253Shselasky if (crd->do_unlock > 1) { 243301253Shselasky crd->do_unlock = 0; 244301253Shselasky goto error; 245301253Shselasky } 246190729Sthompsa } 247190729Sthompsa 248184610Salfred /* check if we are doing an open */ 249189110Sthompsa if (cpd->fflags == 0) { 250193338Sthompsa /* use zero defaults */ 251184610Salfred } else { 252184610Salfred /* check for write */ 253189110Sthompsa if (cpd->fflags & FWRITE) { 254189110Sthompsa ppf = cpd->udev->fifo; 255189110Sthompsa f = ppf[cpd->fifo_index + USB_FIFO_TX]; 256193338Sthompsa crd->txfifo = f; 257193338Sthompsa crd->is_write = 1; /* ref */ 258189110Sthompsa if (f == NULL || f->refcount == USB_FIFO_REF_MAX) 259184610Salfred goto error; 260189906Sthompsa if (f->curr_cpd != cpd) 261189906Sthompsa goto error; 262185087Salfred /* check if USB-FS is active */ 263185087Salfred if (f->fs_ep_max != 0) { 264193338Sthompsa crd->is_usbfs = 1; 265185087Salfred } 266184610Salfred } 267184610Salfred 268184610Salfred /* check for read */ 269189110Sthompsa if (cpd->fflags & FREAD) { 270189110Sthompsa ppf = cpd->udev->fifo; 271189110Sthompsa f = ppf[cpd->fifo_index + USB_FIFO_RX]; 272193338Sthompsa crd->rxfifo = f; 273193338Sthompsa crd->is_read = 1; /* ref */ 274189110Sthompsa if (f == NULL || f->refcount == USB_FIFO_REF_MAX) 275184610Salfred goto error; 276189906Sthompsa if (f->curr_cpd != cpd) 277189906Sthompsa goto error; 278185087Salfred /* check if USB-FS is active */ 279185087Salfred if (f->fs_ep_max != 0) { 280193338Sthompsa crd->is_usbfs = 1; 281185087Salfred } 282184610Salfred } 283184610Salfred } 284184610Salfred 285184610Salfred /* when everything is OK we increment the refcounts */ 286193338Sthompsa if (crd->is_write) { 287184610Salfred DPRINTFN(2, "ref write\n"); 288193338Sthompsa crd->txfifo->refcount++; 289184610Salfred } 290193338Sthompsa if (crd->is_read) { 291184610Salfred DPRINTFN(2, "ref read\n"); 292193338Sthompsa crd->rxfifo->refcount++; 293184610Salfred } 294194228Sthompsa mtx_unlock(&usb_ref_lock); 295184610Salfred 296184610Salfred return (0); 297184610Salfred 298184610Salfrederror: 299246759Shselasky if (crd->do_unlock) 300196498Salfred usbd_enum_unlock(cpd->udev); 301196498Salfred 302246759Shselasky if (crd->is_uref) { 303278289Shselasky if (--(cpd->udev->refcount) == 0) 304278289Shselasky cv_broadcast(&cpd->udev->ref_cv); 305190729Sthompsa } 306194228Sthompsa mtx_unlock(&usb_ref_lock); 307184610Salfred DPRINTFN(2, "fail\n"); 308272592Shselasky 309272592Shselasky /* clear all refs */ 310272592Shselasky memset(crd, 0, sizeof(*crd)); 311272592Shselasky 312184610Salfred return (USB_ERR_INVAL); 313184610Salfred} 314184610Salfred 315184610Salfred/*------------------------------------------------------------------------* 316194228Sthompsa * usb_usb_ref_device 317185087Salfred * 318185087Salfred * This function is used to upgrade an USB reference to include the 319185087Salfred * USB device reference on a USB location. 320185087Salfred * 321185087Salfred * Return values: 322185087Salfred * 0: Success, refcount incremented on the given USB device. 323185087Salfred * Else: Failure. 324185087Salfred *------------------------------------------------------------------------*/ 325193045Sthompsastatic usb_error_t 326194228Sthompsausb_usb_ref_device(struct usb_cdev_privdata *cpd, 327193338Sthompsa struct usb_cdev_refdata *crd) 328185087Salfred{ 329185087Salfred /* 330185087Salfred * Check if we already got an USB reference on this location: 331185087Salfred */ 332193338Sthompsa if (crd->is_uref) 333185087Salfred return (0); /* success */ 334185087Salfred 335185087Salfred /* 336190729Sthompsa * To avoid deadlock at detach we need to drop the FIFO ref 337190729Sthompsa * and re-acquire a new ref! 338185087Salfred */ 339194228Sthompsa usb_unref_device(cpd, crd); 340185087Salfred 341194228Sthompsa return (usb_ref_device(cpd, crd, 1 /* need uref */)); 342185087Salfred} 343185087Salfred 344185087Salfred/*------------------------------------------------------------------------* 345194228Sthompsa * usb_unref_device 346184610Salfred * 347184610Salfred * This function will release the reference count by one unit for the 348184610Salfred * given USB device. 349184610Salfred *------------------------------------------------------------------------*/ 350208007Sthompsastatic void 351194228Sthompsausb_unref_device(struct usb_cdev_privdata *cpd, 352193338Sthompsa struct usb_cdev_refdata *crd) 353184610Salfred{ 354193316Sthompsa 355193338Sthompsa DPRINTFN(2, "cpd=%p is_uref=%d\n", cpd, crd->is_uref); 356193316Sthompsa 357246759Shselasky if (crd->do_unlock) 358196498Salfred usbd_enum_unlock(cpd->udev); 359196498Salfred 360194228Sthompsa mtx_lock(&usb_ref_lock); 361193338Sthompsa if (crd->is_read) { 362193338Sthompsa if (--(crd->rxfifo->refcount) == 0) { 363194227Sthompsa cv_signal(&crd->rxfifo->cv_drain); 364184610Salfred } 365193338Sthompsa crd->is_read = 0; 366184610Salfred } 367193338Sthompsa if (crd->is_write) { 368193338Sthompsa if (--(crd->txfifo->refcount) == 0) { 369194227Sthompsa cv_signal(&crd->txfifo->cv_drain); 370184610Salfred } 371193338Sthompsa crd->is_write = 0; 372184610Salfred } 373193338Sthompsa if (crd->is_uref) { 374193338Sthompsa crd->is_uref = 0; 375278289Shselasky if (--(cpd->udev->refcount) == 0) 376278289Shselasky cv_broadcast(&cpd->udev->ref_cv); 377184610Salfred } 378194228Sthompsa mtx_unlock(&usb_ref_lock); 379184610Salfred} 380184610Salfred 381192984Sthompsastatic struct usb_fifo * 382263162Shselaskyusb_fifo_alloc(struct mtx *mtx) 383184610Salfred{ 384192984Sthompsa struct usb_fifo *f; 385184610Salfred 386184610Salfred f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO); 387263162Shselasky if (f != NULL) { 388194227Sthompsa cv_init(&f->cv_io, "FIFO-IO"); 389194227Sthompsa cv_init(&f->cv_drain, "FIFO-DRAIN"); 390263162Shselasky f->priv_mtx = mtx; 391184610Salfred f->refcount = 1; 392263162Shselasky knlist_init_mtx(&f->selinfo.si_note, mtx); 393184610Salfred } 394184610Salfred return (f); 395184610Salfred} 396184610Salfred 397184610Salfred/*------------------------------------------------------------------------* 398194228Sthompsa * usb_fifo_create 399184610Salfred *------------------------------------------------------------------------*/ 400184610Salfredstatic int 401194228Sthompsausb_fifo_create(struct usb_cdev_privdata *cpd, 402193338Sthompsa struct usb_cdev_refdata *crd) 403184610Salfred{ 404192984Sthompsa struct usb_device *udev = cpd->udev; 405192984Sthompsa struct usb_fifo *f; 406193644Sthompsa struct usb_endpoint *ep; 407184610Salfred uint8_t n; 408184610Salfred uint8_t is_tx; 409184610Salfred uint8_t is_rx; 410184610Salfred uint8_t no_null; 411184610Salfred uint8_t is_busy; 412193644Sthompsa int e = cpd->ep_addr; 413184610Salfred 414189110Sthompsa is_tx = (cpd->fflags & FWRITE) ? 1 : 0; 415189110Sthompsa is_rx = (cpd->fflags & FREAD) ? 1 : 0; 416184610Salfred no_null = 1; 417184610Salfred is_busy = 0; 418184610Salfred 419189110Sthompsa /* Preallocated FIFO */ 420193644Sthompsa if (e < 0) { 421189110Sthompsa DPRINTFN(5, "Preallocated FIFO\n"); 422189110Sthompsa if (is_tx) { 423189110Sthompsa f = udev->fifo[cpd->fifo_index + USB_FIFO_TX]; 424189110Sthompsa if (f == NULL) 425189110Sthompsa return (EINVAL); 426193338Sthompsa crd->txfifo = f; 427189110Sthompsa } 428189110Sthompsa if (is_rx) { 429189110Sthompsa f = udev->fifo[cpd->fifo_index + USB_FIFO_RX]; 430189110Sthompsa if (f == NULL) 431189110Sthompsa return (EINVAL); 432193338Sthompsa crd->rxfifo = f; 433189110Sthompsa } 434189110Sthompsa return (0); 435189110Sthompsa } 436189110Sthompsa 437193644Sthompsa KASSERT(e >= 0 && e <= 15, ("endpoint %d out of range", e)); 438189110Sthompsa 439184610Salfred /* search for a free FIFO slot */ 440193644Sthompsa DPRINTFN(5, "Endpoint device, searching for 0x%02x\n", e); 441184610Salfred for (n = 0;; n += 2) { 442184610Salfred 443184610Salfred if (n == USB_FIFO_MAX) { 444184610Salfred if (no_null) { 445184610Salfred no_null = 0; 446184610Salfred n = 0; 447184610Salfred } else { 448184610Salfred /* end of FIFOs reached */ 449189110Sthompsa DPRINTFN(5, "out of FIFOs\n"); 450184610Salfred return (ENOMEM); 451184610Salfred } 452184610Salfred } 453184610Salfred /* Check for TX FIFO */ 454184610Salfred if (is_tx) { 455184610Salfred f = udev->fifo[n + USB_FIFO_TX]; 456184610Salfred if (f != NULL) { 457193644Sthompsa if (f->dev_ep_index != e) { 458184610Salfred /* wrong endpoint index */ 459184610Salfred continue; 460184610Salfred } 461189906Sthompsa if (f->curr_cpd != NULL) { 462184610Salfred /* FIFO is opened */ 463184610Salfred is_busy = 1; 464184610Salfred continue; 465184610Salfred } 466184610Salfred } else if (no_null) { 467184610Salfred continue; 468184610Salfred } 469184610Salfred } 470184610Salfred /* Check for RX FIFO */ 471184610Salfred if (is_rx) { 472184610Salfred f = udev->fifo[n + USB_FIFO_RX]; 473184610Salfred if (f != NULL) { 474193644Sthompsa if (f->dev_ep_index != e) { 475184610Salfred /* wrong endpoint index */ 476184610Salfred continue; 477184610Salfred } 478189906Sthompsa if (f->curr_cpd != NULL) { 479184610Salfred /* FIFO is opened */ 480184610Salfred is_busy = 1; 481184610Salfred continue; 482184610Salfred } 483184610Salfred } else if (no_null) { 484184610Salfred continue; 485184610Salfred } 486184610Salfred } 487184610Salfred break; 488184610Salfred } 489184610Salfred 490184610Salfred if (no_null == 0) { 491193644Sthompsa if (e >= (USB_EP_MAX / 2)) { 492184610Salfred /* we don't create any endpoints in this range */ 493189906Sthompsa DPRINTFN(5, "ep out of range\n"); 494184610Salfred return (is_busy ? EBUSY : EINVAL); 495184610Salfred } 496184610Salfred } 497189906Sthompsa 498193644Sthompsa if ((e != 0) && is_busy) { 499189906Sthompsa /* 500189906Sthompsa * Only the default control endpoint is allowed to be 501189906Sthompsa * opened multiple times! 502189906Sthompsa */ 503189906Sthompsa DPRINTFN(5, "busy\n"); 504189906Sthompsa return (EBUSY); 505189906Sthompsa } 506189906Sthompsa 507184610Salfred /* Check TX FIFO */ 508184610Salfred if (is_tx && 509184610Salfred (udev->fifo[n + USB_FIFO_TX] == NULL)) { 510194228Sthompsa ep = usb_dev_get_ep(udev, e, USB_FIFO_TX); 511193644Sthompsa DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_TX); 512193644Sthompsa if (ep == NULL) { 513193644Sthompsa DPRINTFN(5, "dev_get_endpoint returned NULL\n"); 514184610Salfred return (EINVAL); 515184610Salfred } 516263162Shselasky f = usb_fifo_alloc(&udev->device_mtx); 517184610Salfred if (f == NULL) { 518189110Sthompsa DPRINTFN(5, "could not alloc tx fifo\n"); 519184610Salfred return (ENOMEM); 520184610Salfred } 521184610Salfred /* update some fields */ 522184610Salfred f->fifo_index = n + USB_FIFO_TX; 523193644Sthompsa f->dev_ep_index = e; 524193644Sthompsa f->priv_sc0 = ep; 525194228Sthompsa f->methods = &usb_ugen_methods; 526193644Sthompsa f->iface_index = ep->iface_index; 527184610Salfred f->udev = udev; 528194228Sthompsa mtx_lock(&usb_ref_lock); 529184610Salfred udev->fifo[n + USB_FIFO_TX] = f; 530194228Sthompsa mtx_unlock(&usb_ref_lock); 531184610Salfred } 532184610Salfred /* Check RX FIFO */ 533184610Salfred if (is_rx && 534184610Salfred (udev->fifo[n + USB_FIFO_RX] == NULL)) { 535184610Salfred 536194228Sthompsa ep = usb_dev_get_ep(udev, e, USB_FIFO_RX); 537193644Sthompsa DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_RX); 538193644Sthompsa if (ep == NULL) { 539193644Sthompsa DPRINTFN(5, "dev_get_endpoint returned NULL\n"); 540184610Salfred return (EINVAL); 541184610Salfred } 542263162Shselasky f = usb_fifo_alloc(&udev->device_mtx); 543184610Salfred if (f == NULL) { 544189110Sthompsa DPRINTFN(5, "could not alloc rx fifo\n"); 545184610Salfred return (ENOMEM); 546184610Salfred } 547184610Salfred /* update some fields */ 548184610Salfred f->fifo_index = n + USB_FIFO_RX; 549193644Sthompsa f->dev_ep_index = e; 550193644Sthompsa f->priv_sc0 = ep; 551194228Sthompsa f->methods = &usb_ugen_methods; 552193644Sthompsa f->iface_index = ep->iface_index; 553184610Salfred f->udev = udev; 554194228Sthompsa mtx_lock(&usb_ref_lock); 555184610Salfred udev->fifo[n + USB_FIFO_RX] = f; 556194228Sthompsa mtx_unlock(&usb_ref_lock); 557184610Salfred } 558184610Salfred if (is_tx) { 559193338Sthompsa crd->txfifo = udev->fifo[n + USB_FIFO_TX]; 560184610Salfred } 561184610Salfred if (is_rx) { 562193338Sthompsa crd->rxfifo = udev->fifo[n + USB_FIFO_RX]; 563184610Salfred } 564189110Sthompsa /* fill out fifo index */ 565189110Sthompsa DPRINTFN(5, "fifo index = %d\n", n); 566189110Sthompsa cpd->fifo_index = n; 567184610Salfred 568184610Salfred /* complete */ 569184610Salfred 570184610Salfred return (0); 571184610Salfred} 572184610Salfred 573184610Salfredvoid 574194228Sthompsausb_fifo_free(struct usb_fifo *f) 575184610Salfred{ 576184610Salfred uint8_t n; 577184610Salfred 578184610Salfred if (f == NULL) { 579184610Salfred /* be NULL safe */ 580184610Salfred return; 581184610Salfred } 582184610Salfred /* destroy symlink devices, if any */ 583184610Salfred for (n = 0; n != 2; n++) { 584184610Salfred if (f->symlink[n]) { 585194228Sthompsa usb_free_symlink(f->symlink[n]); 586184610Salfred f->symlink[n] = NULL; 587184610Salfred } 588184610Salfred } 589194228Sthompsa mtx_lock(&usb_ref_lock); 590184610Salfred 591184610Salfred /* delink ourselves to stop calls from userland */ 592184610Salfred if ((f->fifo_index < USB_FIFO_MAX) && 593184610Salfred (f->udev != NULL) && 594184610Salfred (f->udev->fifo[f->fifo_index] == f)) { 595184610Salfred f->udev->fifo[f->fifo_index] = NULL; 596184610Salfred } else { 597199816Sthompsa DPRINTFN(0, "USB FIFO %p has not been linked\n", f); 598184610Salfred } 599184610Salfred 600184610Salfred /* decrease refcount */ 601184610Salfred f->refcount--; 602184610Salfred /* need to wait until all callers have exited */ 603184610Salfred while (f->refcount != 0) { 604194228Sthompsa mtx_unlock(&usb_ref_lock); /* avoid LOR */ 605184610Salfred mtx_lock(f->priv_mtx); 606278289Shselasky /* prevent write flush, if any */ 607278289Shselasky f->flag_iserror = 1; 608184610Salfred /* get I/O thread out of any sleep state */ 609184610Salfred if (f->flag_sleeping) { 610184610Salfred f->flag_sleeping = 0; 611194227Sthompsa cv_broadcast(&f->cv_io); 612184610Salfred } 613184610Salfred mtx_unlock(f->priv_mtx); 614194228Sthompsa mtx_lock(&usb_ref_lock); 615184610Salfred 616261110Shselasky /* 617261110Shselasky * Check if the "f->refcount" variable reached zero 618261110Shselasky * during the unlocked time before entering wait: 619261110Shselasky */ 620261110Shselasky if (f->refcount == 0) 621261110Shselasky break; 622261110Shselasky 623184610Salfred /* wait for sync */ 624194228Sthompsa cv_wait(&f->cv_drain, &usb_ref_lock); 625184610Salfred } 626194228Sthompsa mtx_unlock(&usb_ref_lock); 627184610Salfred 628184610Salfred /* take care of closing the device here, if any */ 629194228Sthompsa usb_fifo_close(f, 0); 630184610Salfred 631194227Sthompsa cv_destroy(&f->cv_io); 632194227Sthompsa cv_destroy(&f->cv_drain); 633184610Salfred 634263162Shselasky knlist_clear(&f->selinfo.si_note, 0); 635263162Shselasky seldrain(&f->selinfo); 636263162Shselasky knlist_destroy(&f->selinfo.si_note); 637263162Shselasky 638184610Salfred free(f, M_USBDEV); 639184610Salfred} 640184610Salfred 641193644Sthompsastatic struct usb_endpoint * 642194228Sthompsausb_dev_get_ep(struct usb_device *udev, uint8_t ep_index, uint8_t dir) 643184610Salfred{ 644193644Sthompsa struct usb_endpoint *ep; 645184610Salfred uint8_t ep_dir; 646184610Salfred 647184610Salfred if (ep_index == 0) { 648207080Sthompsa ep = &udev->ctrl_ep; 649184610Salfred } else { 650184610Salfred if (dir == USB_FIFO_RX) { 651192499Sthompsa if (udev->flags.usb_mode == USB_MODE_HOST) { 652184610Salfred ep_dir = UE_DIR_IN; 653184610Salfred } else { 654184610Salfred ep_dir = UE_DIR_OUT; 655184610Salfred } 656184610Salfred } else { 657192499Sthompsa if (udev->flags.usb_mode == USB_MODE_HOST) { 658184610Salfred ep_dir = UE_DIR_OUT; 659184610Salfred } else { 660184610Salfred ep_dir = UE_DIR_IN; 661184610Salfred } 662184610Salfred } 663194228Sthompsa ep = usbd_get_ep_by_addr(udev, ep_index | ep_dir); 664184610Salfred } 665184610Salfred 666193644Sthompsa if (ep == NULL) { 667193644Sthompsa /* if the endpoint does not exist then return */ 668184610Salfred return (NULL); 669184610Salfred } 670193644Sthompsa if (ep->edesc == NULL) { 671193644Sthompsa /* invalid endpoint */ 672184610Salfred return (NULL); 673184610Salfred } 674193644Sthompsa return (ep); /* success */ 675184610Salfred} 676184610Salfred 677184610Salfred/*------------------------------------------------------------------------* 678194228Sthompsa * usb_fifo_open 679184610Salfred * 680184610Salfred * Returns: 681184610Salfred * 0: Success 682184610Salfred * Else: Failure 683184610Salfred *------------------------------------------------------------------------*/ 684184610Salfredstatic int 685194228Sthompsausb_fifo_open(struct usb_cdev_privdata *cpd, 686192984Sthompsa struct usb_fifo *f, int fflags) 687184610Salfred{ 688184610Salfred int err; 689184610Salfred 690184610Salfred if (f == NULL) { 691184610Salfred /* no FIFO there */ 692184610Salfred DPRINTFN(2, "no FIFO\n"); 693184610Salfred return (ENXIO); 694184610Salfred } 695184610Salfred /* remove FWRITE and FREAD flags */ 696184610Salfred fflags &= ~(FWRITE | FREAD); 697184610Salfred 698184610Salfred /* set correct file flags */ 699184610Salfred if ((f->fifo_index & 1) == USB_FIFO_TX) { 700184610Salfred fflags |= FWRITE; 701184610Salfred } else { 702184610Salfred fflags |= FREAD; 703184610Salfred } 704184610Salfred 705184610Salfred /* check if we are already opened */ 706184610Salfred /* we don't need any locks when checking this variable */ 707189906Sthompsa if (f->curr_cpd != NULL) { 708184610Salfred err = EBUSY; 709184610Salfred goto done; 710184610Salfred } 711189110Sthompsa 712189906Sthompsa /* reset short flag before open */ 713189906Sthompsa f->flag_short = 0; 714189906Sthompsa 715184610Salfred /* call open method */ 716189110Sthompsa err = (f->methods->f_open) (f, fflags); 717184610Salfred if (err) { 718184610Salfred goto done; 719184610Salfred } 720184610Salfred mtx_lock(f->priv_mtx); 721184610Salfred 722184610Salfred /* reset sleep flag */ 723184610Salfred f->flag_sleeping = 0; 724184610Salfred 725184610Salfred /* reset error flag */ 726184610Salfred f->flag_iserror = 0; 727184610Salfred 728184610Salfred /* reset complete flag */ 729184610Salfred f->flag_iscomplete = 0; 730184610Salfred 731184610Salfred /* reset select flag */ 732184610Salfred f->flag_isselect = 0; 733184610Salfred 734184610Salfred /* reset flushing flag */ 735184610Salfred f->flag_flushing = 0; 736184610Salfred 737184610Salfred /* reset ASYNC proc flag */ 738184610Salfred f->async_p = NULL; 739184610Salfred 740194228Sthompsa mtx_lock(&usb_ref_lock); 741189110Sthompsa /* flag the fifo as opened to prevent others */ 742189906Sthompsa f->curr_cpd = cpd; 743194228Sthompsa mtx_unlock(&usb_ref_lock); 744184610Salfred 745184610Salfred /* reset queue */ 746194228Sthompsa usb_fifo_reset(f); 747184610Salfred 748184610Salfred mtx_unlock(f->priv_mtx); 749184610Salfreddone: 750184610Salfred return (err); 751184610Salfred} 752184610Salfred 753184610Salfred/*------------------------------------------------------------------------* 754194228Sthompsa * usb_fifo_reset 755184610Salfred *------------------------------------------------------------------------*/ 756184610Salfredvoid 757194228Sthompsausb_fifo_reset(struct usb_fifo *f) 758184610Salfred{ 759192984Sthompsa struct usb_mbuf *m; 760184610Salfred 761184610Salfred if (f == NULL) { 762184610Salfred return; 763184610Salfred } 764184610Salfred while (1) { 765184610Salfred USB_IF_DEQUEUE(&f->used_q, m); 766184610Salfred if (m) { 767184610Salfred USB_IF_ENQUEUE(&f->free_q, m); 768184610Salfred } else { 769184610Salfred break; 770184610Salfred } 771184610Salfred } 772195963Salfred /* reset have fragment flag */ 773195963Salfred f->flag_have_fragment = 0; 774184610Salfred} 775184610Salfred 776184610Salfred/*------------------------------------------------------------------------* 777194228Sthompsa * usb_fifo_close 778184610Salfred *------------------------------------------------------------------------*/ 779184610Salfredstatic void 780194228Sthompsausb_fifo_close(struct usb_fifo *f, int fflags) 781184610Salfred{ 782184610Salfred int err; 783184610Salfred 784184610Salfred /* check if we are not opened */ 785189906Sthompsa if (f->curr_cpd == NULL) { 786184610Salfred /* nothing to do - already closed */ 787184610Salfred return; 788184610Salfred } 789184610Salfred mtx_lock(f->priv_mtx); 790184610Salfred 791189906Sthompsa /* clear current cdev private data pointer */ 792263162Shselasky mtx_lock(&usb_ref_lock); 793189906Sthompsa f->curr_cpd = NULL; 794263162Shselasky mtx_unlock(&usb_ref_lock); 795184610Salfred 796263162Shselasky /* check if we are watched by kevent */ 797263162Shselasky KNOTE_LOCKED(&f->selinfo.si_note, 0); 798263162Shselasky 799184610Salfred /* check if we are selected */ 800184610Salfred if (f->flag_isselect) { 801184610Salfred selwakeup(&f->selinfo); 802184610Salfred f->flag_isselect = 0; 803184610Salfred } 804184610Salfred /* check if a thread wants SIGIO */ 805184610Salfred if (f->async_p != NULL) { 806184610Salfred PROC_LOCK(f->async_p); 807225617Skmacy kern_psignal(f->async_p, SIGIO); 808184610Salfred PROC_UNLOCK(f->async_p); 809184610Salfred f->async_p = NULL; 810184610Salfred } 811184610Salfred /* remove FWRITE and FREAD flags */ 812184610Salfred fflags &= ~(FWRITE | FREAD); 813184610Salfred 814184610Salfred /* flush written data, if any */ 815184610Salfred if ((f->fifo_index & 1) == USB_FIFO_TX) { 816184610Salfred 817184610Salfred if (!f->flag_iserror) { 818184610Salfred 819184610Salfred /* set flushing flag */ 820184610Salfred f->flag_flushing = 1; 821184610Salfred 822195963Salfred /* get the last packet in */ 823195963Salfred if (f->flag_have_fragment) { 824195963Salfred struct usb_mbuf *m; 825195963Salfred f->flag_have_fragment = 0; 826195963Salfred USB_IF_DEQUEUE(&f->free_q, m); 827195963Salfred if (m) { 828195963Salfred USB_IF_ENQUEUE(&f->used_q, m); 829195963Salfred } 830195963Salfred } 831195963Salfred 832184610Salfred /* start write transfer, if not already started */ 833184610Salfred (f->methods->f_start_write) (f); 834184610Salfred 835184610Salfred /* check if flushed already */ 836184610Salfred while (f->flag_flushing && 837184610Salfred (!f->flag_iserror)) { 838184610Salfred /* wait until all data has been written */ 839184610Salfred f->flag_sleeping = 1; 840284500Shselasky err = cv_timedwait_sig(&f->cv_io, f->priv_mtx, 841284500Shselasky USB_MS_TO_TICKS(USB_DEFAULT_TIMEOUT)); 842184610Salfred if (err) { 843184610Salfred DPRINTF("signal received\n"); 844184610Salfred break; 845184610Salfred } 846184610Salfred } 847184610Salfred } 848184610Salfred fflags |= FWRITE; 849184610Salfred 850184610Salfred /* stop write transfer, if not already stopped */ 851184610Salfred (f->methods->f_stop_write) (f); 852184610Salfred } else { 853184610Salfred fflags |= FREAD; 854184610Salfred 855184610Salfred /* stop write transfer, if not already stopped */ 856184610Salfred (f->methods->f_stop_read) (f); 857184610Salfred } 858184610Salfred 859184610Salfred /* check if we are sleeping */ 860184610Salfred if (f->flag_sleeping) { 861184610Salfred DPRINTFN(2, "Sleeping at close!\n"); 862184610Salfred } 863184610Salfred mtx_unlock(f->priv_mtx); 864184610Salfred 865184610Salfred /* call close method */ 866189110Sthompsa (f->methods->f_close) (f, fflags); 867184610Salfred 868184610Salfred DPRINTF("closed\n"); 869184610Salfred} 870184610Salfred 871184610Salfred/*------------------------------------------------------------------------* 872194228Sthompsa * usb_open - cdev callback 873184610Salfred *------------------------------------------------------------------------*/ 874184610Salfredstatic int 875194228Sthompsausb_open(struct cdev *dev, int fflags, int devtype, struct thread *td) 876184610Salfred{ 877192984Sthompsa struct usb_fs_privdata* pd = (struct usb_fs_privdata*)dev->si_drv1; 878193338Sthompsa struct usb_cdev_refdata refs; 879192984Sthompsa struct usb_cdev_privdata *cpd; 880189110Sthompsa int err, ep; 881184610Salfred 882231378Sed DPRINTFN(2, "%s fflags=0x%08x\n", devtoname(dev), fflags); 883184610Salfred 884189110Sthompsa KASSERT(fflags & (FREAD|FWRITE), ("invalid open flags")); 885189110Sthompsa if (((fflags & FREAD) && !(pd->mode & FREAD)) || 886189110Sthompsa ((fflags & FWRITE) && !(pd->mode & FWRITE))) { 887189110Sthompsa DPRINTFN(2, "access mode not supported\n"); 888184610Salfred return (EPERM); 889184610Salfred } 890189110Sthompsa 891189110Sthompsa cpd = malloc(sizeof(*cpd), M_USBDEV, M_WAITOK | M_ZERO); 892189110Sthompsa ep = cpd->ep_addr = pd->ep_addr; 893189110Sthompsa 894194228Sthompsa usb_loc_fill(pd, cpd); 895194228Sthompsa err = usb_ref_device(cpd, &refs, 1); 896184610Salfred if (err) { 897184610Salfred DPRINTFN(2, "cannot ref device\n"); 898189110Sthompsa free(cpd, M_USBDEV); 899184610Salfred return (ENXIO); 900184610Salfred } 901189110Sthompsa cpd->fflags = fflags; /* access mode for open lifetime */ 902184610Salfred 903184610Salfred /* create FIFOs, if any */ 904194228Sthompsa err = usb_fifo_create(cpd, &refs); 905184610Salfred /* check for error */ 906184610Salfred if (err) { 907189110Sthompsa DPRINTFN(2, "cannot create fifo\n"); 908194228Sthompsa usb_unref_device(cpd, &refs); 909189110Sthompsa free(cpd, M_USBDEV); 910184610Salfred return (err); 911184610Salfred } 912184610Salfred if (fflags & FREAD) { 913194228Sthompsa err = usb_fifo_open(cpd, refs.rxfifo, fflags); 914184610Salfred if (err) { 915184610Salfred DPRINTFN(2, "read open failed\n"); 916194228Sthompsa usb_unref_device(cpd, &refs); 917189110Sthompsa free(cpd, M_USBDEV); 918184610Salfred return (err); 919184610Salfred } 920184610Salfred } 921184610Salfred if (fflags & FWRITE) { 922194228Sthompsa err = usb_fifo_open(cpd, refs.txfifo, fflags); 923184610Salfred if (err) { 924184610Salfred DPRINTFN(2, "write open failed\n"); 925184610Salfred if (fflags & FREAD) { 926194228Sthompsa usb_fifo_close(refs.rxfifo, fflags); 927184610Salfred } 928194228Sthompsa usb_unref_device(cpd, &refs); 929189110Sthompsa free(cpd, M_USBDEV); 930184610Salfred return (err); 931184610Salfred } 932184610Salfred } 933194228Sthompsa usb_unref_device(cpd, &refs); 934194228Sthompsa devfs_set_cdevpriv(cpd, usb_close); 935184610Salfred 936189110Sthompsa return (0); 937184610Salfred} 938184610Salfred 939184610Salfred/*------------------------------------------------------------------------* 940194228Sthompsa * usb_close - cdev callback 941184610Salfred *------------------------------------------------------------------------*/ 942189110Sthompsastatic void 943194228Sthompsausb_close(void *arg) 944184610Salfred{ 945193338Sthompsa struct usb_cdev_refdata refs; 946192984Sthompsa struct usb_cdev_privdata *cpd = arg; 947184610Salfred int err; 948184610Salfred 949189906Sthompsa DPRINTFN(2, "cpd=%p\n", cpd); 950184610Salfred 951261110Shselasky err = usb_ref_device(cpd, &refs, 952261110Shselasky 2 /* uref and allow detached state */); 953261110Shselasky if (err) { 954261468Shselasky DPRINTFN(2, "Cannot grab USB reference when " 955261110Shselasky "closing USB file handle\n"); 956225038Shselasky goto done; 957184610Salfred } 958189110Sthompsa if (cpd->fflags & FREAD) { 959194228Sthompsa usb_fifo_close(refs.rxfifo, cpd->fflags); 960184610Salfred } 961189110Sthompsa if (cpd->fflags & FWRITE) { 962194228Sthompsa usb_fifo_close(refs.txfifo, cpd->fflags); 963184610Salfred } 964194228Sthompsa usb_unref_device(cpd, &refs); 965225038Shselaskydone: 966189110Sthompsa free(cpd, M_USBDEV); 967184610Salfred} 968184610Salfred 969184610Salfredstatic void 970194228Sthompsausb_dev_init(void *arg) 971184610Salfred{ 972194228Sthompsa mtx_init(&usb_ref_lock, "USB ref mutex", NULL, MTX_DEF); 973194228Sthompsa sx_init(&usb_sym_lock, "USB sym mutex"); 974194228Sthompsa TAILQ_INIT(&usb_sym_head); 975184610Salfred 976184610Salfred /* check the UGEN methods */ 977194228Sthompsa usb_fifo_check_methods(&usb_ugen_methods); 978184610Salfred} 979184610Salfred 980194228SthompsaSYSINIT(usb_dev_init, SI_SUB_KLD, SI_ORDER_FIRST, usb_dev_init, NULL); 981184610Salfred 982184610Salfredstatic void 983194228Sthompsausb_dev_init_post(void *arg) 984184610Salfred{ 985184610Salfred /* 986189110Sthompsa * Create /dev/usb - this is needed for usbconfig(8), which 987189110Sthompsa * needs a well-known device name to access. 988184610Salfred */ 989194228Sthompsa usb_dev = make_dev(&usb_static_devsw, 0, UID_ROOT, GID_OPERATOR, 990189110Sthompsa 0644, USB_DEVICE_NAME); 991194228Sthompsa if (usb_dev == NULL) { 992199816Sthompsa DPRINTFN(0, "Could not create usb bus device\n"); 993184610Salfred } 994184610Salfred} 995184610Salfred 996194228SthompsaSYSINIT(usb_dev_init_post, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, usb_dev_init_post, NULL); 997184610Salfred 998184610Salfredstatic void 999194228Sthompsausb_dev_uninit(void *arg) 1000184610Salfred{ 1001194228Sthompsa if (usb_dev != NULL) { 1002194228Sthompsa destroy_dev(usb_dev); 1003194228Sthompsa usb_dev = NULL; 1004184610Salfred } 1005194228Sthompsa mtx_destroy(&usb_ref_lock); 1006194228Sthompsa sx_destroy(&usb_sym_lock); 1007184610Salfred} 1008184610Salfred 1009194228SthompsaSYSUNINIT(usb_dev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb_dev_uninit, NULL); 1010184610Salfred 1011184610Salfredstatic int 1012194228Sthompsausb_ioctl_f_sub(struct usb_fifo *f, u_long cmd, void *addr, 1013184610Salfred struct thread *td) 1014184610Salfred{ 1015184610Salfred int error = 0; 1016184610Salfred 1017184610Salfred switch (cmd) { 1018184610Salfred case FIODTYPE: 1019184610Salfred *(int *)addr = 0; /* character device */ 1020184610Salfred break; 1021184610Salfred 1022184610Salfred case FIONBIO: 1023184610Salfred /* handled by upper FS layer */ 1024184610Salfred break; 1025184610Salfred 1026184610Salfred case FIOASYNC: 1027184610Salfred if (*(int *)addr) { 1028184610Salfred if (f->async_p != NULL) { 1029184610Salfred error = EBUSY; 1030184610Salfred break; 1031184610Salfred } 1032184610Salfred f->async_p = USB_TD_GET_PROC(td); 1033184610Salfred } else { 1034184610Salfred f->async_p = NULL; 1035184610Salfred } 1036184610Salfred break; 1037184610Salfred 1038184610Salfred /* XXX this is not the most general solution */ 1039184610Salfred case TIOCSPGRP: 1040184610Salfred if (f->async_p == NULL) { 1041184610Salfred error = EINVAL; 1042184610Salfred break; 1043184610Salfred } 1044184610Salfred if (*(int *)addr != USB_PROC_GET_GID(f->async_p)) { 1045184610Salfred error = EPERM; 1046184610Salfred break; 1047184610Salfred } 1048184610Salfred break; 1049184610Salfred default: 1050185087Salfred return (ENOIOCTL); 1051184610Salfred } 1052190238Sthompsa DPRINTFN(3, "cmd 0x%lx = %d\n", cmd, error); 1053184610Salfred return (error); 1054184610Salfred} 1055184610Salfred 1056189110Sthompsa/*------------------------------------------------------------------------* 1057194228Sthompsa * usb_ioctl - cdev callback 1058189110Sthompsa *------------------------------------------------------------------------*/ 1059184610Salfredstatic int 1060194228Sthompsausb_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int fflag, struct thread* td) 1061184610Salfred{ 1062193338Sthompsa struct usb_cdev_refdata refs; 1063192984Sthompsa struct usb_cdev_privdata* cpd; 1064192984Sthompsa struct usb_fifo *f; 1065184610Salfred int fflags; 1066184610Salfred int err; 1067184610Salfred 1068190238Sthompsa DPRINTFN(2, "cmd=0x%lx\n", cmd); 1069190238Sthompsa 1070189110Sthompsa err = devfs_get_cdevpriv((void **)&cpd); 1071189110Sthompsa if (err != 0) 1072189110Sthompsa return (err); 1073189110Sthompsa 1074189172Sthompsa /* 1075193338Sthompsa * Performance optimisation: We try to check for IOCTL's that 1076189172Sthompsa * don't need the USB reference first. Then we grab the USB 1077189172Sthompsa * reference if we need it! 1078189172Sthompsa */ 1079194228Sthompsa err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 1080196498Salfred if (err) 1081184610Salfred return (ENXIO); 1082196498Salfred 1083189110Sthompsa fflags = cpd->fflags; 1084184610Salfred 1085185087Salfred f = NULL; /* set default value */ 1086185087Salfred err = ENOIOCTL; /* set default value */ 1087185087Salfred 1088185087Salfred if (fflags & FWRITE) { 1089193338Sthompsa f = refs.txfifo; 1090194228Sthompsa err = usb_ioctl_f_sub(f, cmd, addr, td); 1091185087Salfred } 1092184610Salfred if (fflags & FREAD) { 1093193338Sthompsa f = refs.rxfifo; 1094194228Sthompsa err = usb_ioctl_f_sub(f, cmd, addr, td); 1095184610Salfred } 1096189110Sthompsa KASSERT(f != NULL, ("fifo not found")); 1097213432Shselasky if (err != ENOIOCTL) 1098213432Shselasky goto done; 1099213432Shselasky 1100213432Shselasky err = (f->methods->f_ioctl) (f, cmd, addr, fflags); 1101213432Shselasky 1102213432Shselasky DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err); 1103213432Shselasky 1104213432Shselasky if (err != ENOIOCTL) 1105213432Shselasky goto done; 1106213432Shselasky 1107213432Shselasky if (usb_usb_ref_device(cpd, &refs)) { 1108272592Shselasky /* we lost the reference */ 1109272592Shselasky return (ENXIO); 1110184610Salfred } 1111213432Shselasky 1112213432Shselasky err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags); 1113213432Shselasky 1114213432Shselasky DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err); 1115213432Shselasky 1116213432Shselasky if (err == ENOIOCTL) 1117185087Salfred err = ENOTTY; 1118213432Shselasky 1119213432Shselasky if (err) 1120213432Shselasky goto done; 1121213432Shselasky 1122213432Shselasky /* Wait for re-enumeration, if any */ 1123213432Shselasky 1124257375Shselasky while (f->udev->re_enumerate_wait != USB_RE_ENUM_DONE) { 1125213432Shselasky 1126213432Shselasky usb_unref_device(cpd, &refs); 1127213432Shselasky 1128213432Shselasky usb_pause_mtx(NULL, hz / 128); 1129213432Shselasky 1130267347Shselasky while (usb_ref_device(cpd, &refs, 1 /* need uref */)) { 1131267347Shselasky if (usb_ref_device(cpd, &refs, 0)) { 1132272592Shselasky /* device no longer exists */ 1133272592Shselasky return (ENXIO); 1134267347Shselasky } 1135267347Shselasky usb_unref_device(cpd, &refs); 1136267347Shselasky usb_pause_mtx(NULL, hz / 128); 1137213432Shselasky } 1138184610Salfred } 1139213432Shselasky 1140185087Salfreddone: 1141194228Sthompsa usb_unref_device(cpd, &refs); 1142184610Salfred return (err); 1143184610Salfred} 1144184610Salfred 1145263162Shselaskystatic void 1146263162Shselaskyusb_filter_detach(struct knote *kn) 1147263162Shselasky{ 1148263162Shselasky struct usb_fifo *f = kn->kn_hook; 1149263162Shselasky knlist_remove(&f->selinfo.si_note, kn, 0); 1150263162Shselasky} 1151263162Shselasky 1152263162Shselaskystatic int 1153263162Shselaskyusb_filter_write(struct knote *kn, long hint) 1154263162Shselasky{ 1155263162Shselasky struct usb_cdev_privdata* cpd; 1156263162Shselasky struct usb_fifo *f; 1157263162Shselasky struct usb_mbuf *m; 1158263162Shselasky 1159263162Shselasky DPRINTFN(2, "\n"); 1160263162Shselasky 1161263162Shselasky f = kn->kn_hook; 1162263162Shselasky 1163263162Shselasky mtx_assert(f->priv_mtx, MA_OWNED); 1164263162Shselasky 1165263162Shselasky cpd = f->curr_cpd; 1166263162Shselasky if (cpd == NULL) { 1167263162Shselasky m = (void *)1; 1168263162Shselasky } else if (f->fs_ep_max == 0) { 1169263162Shselasky if (f->flag_iserror) { 1170263162Shselasky /* we got an error */ 1171263162Shselasky m = (void *)1; 1172263162Shselasky } else { 1173263162Shselasky if (f->queue_data == NULL) { 1174263162Shselasky /* 1175263162Shselasky * start write transfer, if not 1176263162Shselasky * already started 1177263162Shselasky */ 1178263162Shselasky (f->methods->f_start_write) (f); 1179263162Shselasky } 1180263162Shselasky /* check if any packets are available */ 1181263162Shselasky USB_IF_POLL(&f->free_q, m); 1182263162Shselasky } 1183263162Shselasky } else { 1184263162Shselasky if (f->flag_iscomplete) { 1185263162Shselasky m = (void *)1; 1186263162Shselasky } else { 1187263162Shselasky m = NULL; 1188263162Shselasky } 1189263162Shselasky } 1190263162Shselasky return (m ? 1 : 0); 1191263162Shselasky} 1192263162Shselasky 1193263162Shselaskystatic int 1194263162Shselaskyusb_filter_read(struct knote *kn, long hint) 1195263162Shselasky{ 1196263162Shselasky struct usb_cdev_privdata* cpd; 1197263162Shselasky struct usb_fifo *f; 1198263162Shselasky struct usb_mbuf *m; 1199263162Shselasky 1200263162Shselasky DPRINTFN(2, "\n"); 1201263162Shselasky 1202263162Shselasky f = kn->kn_hook; 1203263162Shselasky 1204263162Shselasky mtx_assert(f->priv_mtx, MA_OWNED); 1205263162Shselasky 1206263162Shselasky cpd = f->curr_cpd; 1207263162Shselasky if (cpd == NULL) { 1208263162Shselasky m = (void *)1; 1209263162Shselasky } else if (f->fs_ep_max == 0) { 1210263162Shselasky if (f->flag_iserror) { 1211263162Shselasky /* we have an error */ 1212263162Shselasky m = (void *)1; 1213263162Shselasky } else { 1214263162Shselasky if (f->queue_data == NULL) { 1215263162Shselasky /* 1216263162Shselasky * start read transfer, if not 1217263162Shselasky * already started 1218263162Shselasky */ 1219263162Shselasky (f->methods->f_start_read) (f); 1220263162Shselasky } 1221263162Shselasky /* check if any packets are available */ 1222263162Shselasky USB_IF_POLL(&f->used_q, m); 1223263162Shselasky 1224263162Shselasky /* start reading data, if any */ 1225263162Shselasky if (m == NULL) 1226263162Shselasky (f->methods->f_start_read) (f); 1227263162Shselasky } 1228263162Shselasky } else { 1229263162Shselasky if (f->flag_iscomplete) { 1230263162Shselasky m = (void *)1; 1231263162Shselasky } else { 1232263162Shselasky m = NULL; 1233263162Shselasky } 1234263162Shselasky } 1235263162Shselasky return (m ? 1 : 0); 1236263162Shselasky} 1237263162Shselasky 1238263162Shselaskystatic struct filterops usb_filtops_write = { 1239263162Shselasky .f_isfd = 1, 1240263162Shselasky .f_detach = usb_filter_detach, 1241263162Shselasky .f_event = usb_filter_write, 1242263162Shselasky}; 1243263162Shselasky 1244263162Shselaskystatic struct filterops usb_filtops_read = { 1245263162Shselasky .f_isfd = 1, 1246263162Shselasky .f_detach = usb_filter_detach, 1247263162Shselasky .f_event = usb_filter_read, 1248263162Shselasky}; 1249263162Shselasky 1250263162Shselasky 1251184610Salfred/* ARGSUSED */ 1252184610Salfredstatic int 1253263162Shselaskyusb_kqfilter(struct cdev* dev, struct knote *kn) 1254263162Shselasky{ 1255263162Shselasky struct usb_cdev_refdata refs; 1256263162Shselasky struct usb_cdev_privdata* cpd; 1257263162Shselasky struct usb_fifo *f; 1258263162Shselasky int fflags; 1259263162Shselasky int err = EINVAL; 1260263162Shselasky 1261263162Shselasky DPRINTFN(2, "\n"); 1262263162Shselasky 1263263162Shselasky if (devfs_get_cdevpriv((void **)&cpd) != 0 || 1264263162Shselasky usb_ref_device(cpd, &refs, 0) != 0) 1265263162Shselasky return (ENXIO); 1266263162Shselasky 1267263162Shselasky fflags = cpd->fflags; 1268263162Shselasky 1269263162Shselasky /* Figure out who needs service */ 1270263162Shselasky switch (kn->kn_filter) { 1271263162Shselasky case EVFILT_WRITE: 1272263162Shselasky if (fflags & FWRITE) { 1273263162Shselasky f = refs.txfifo; 1274263162Shselasky kn->kn_fop = &usb_filtops_write; 1275263162Shselasky err = 0; 1276263162Shselasky } 1277263162Shselasky break; 1278263162Shselasky case EVFILT_READ: 1279263162Shselasky if (fflags & FREAD) { 1280263162Shselasky f = refs.rxfifo; 1281263162Shselasky kn->kn_fop = &usb_filtops_read; 1282263162Shselasky err = 0; 1283263162Shselasky } 1284263162Shselasky break; 1285263162Shselasky default: 1286263162Shselasky err = EOPNOTSUPP; 1287263162Shselasky break; 1288263162Shselasky } 1289263162Shselasky 1290263162Shselasky if (err == 0) { 1291263162Shselasky kn->kn_hook = f; 1292263162Shselasky mtx_lock(f->priv_mtx); 1293263162Shselasky knlist_add(&f->selinfo.si_note, kn, 1); 1294263162Shselasky mtx_unlock(f->priv_mtx); 1295263162Shselasky } 1296263162Shselasky 1297263162Shselasky usb_unref_device(cpd, &refs); 1298263162Shselasky return (err); 1299263162Shselasky} 1300263162Shselasky 1301263162Shselasky/* ARGSUSED */ 1302263162Shselaskystatic int 1303194228Sthompsausb_poll(struct cdev* dev, int events, struct thread* td) 1304184610Salfred{ 1305193338Sthompsa struct usb_cdev_refdata refs; 1306192984Sthompsa struct usb_cdev_privdata* cpd; 1307192984Sthompsa struct usb_fifo *f; 1308192984Sthompsa struct usb_mbuf *m; 1309189422Sthompsa int fflags, revents; 1310184610Salfred 1311189422Sthompsa if (devfs_get_cdevpriv((void **)&cpd) != 0 || 1312194228Sthompsa usb_ref_device(cpd, &refs, 0) != 0) 1313189422Sthompsa return (events & 1314189422Sthompsa (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); 1315189110Sthompsa 1316189110Sthompsa fflags = cpd->fflags; 1317189110Sthompsa 1318184610Salfred /* Figure out who needs service */ 1319189110Sthompsa revents = 0; 1320184610Salfred if ((events & (POLLOUT | POLLWRNORM)) && 1321184610Salfred (fflags & FWRITE)) { 1322184610Salfred 1323193338Sthompsa f = refs.txfifo; 1324184610Salfred 1325184610Salfred mtx_lock(f->priv_mtx); 1326184610Salfred 1327193338Sthompsa if (!refs.is_usbfs) { 1328184610Salfred if (f->flag_iserror) { 1329184610Salfred /* we got an error */ 1330184610Salfred m = (void *)1; 1331184610Salfred } else { 1332184610Salfred if (f->queue_data == NULL) { 1333184610Salfred /* 1334184610Salfred * start write transfer, if not 1335184610Salfred * already started 1336184610Salfred */ 1337184610Salfred (f->methods->f_start_write) (f); 1338184610Salfred } 1339184610Salfred /* check if any packets are available */ 1340184610Salfred USB_IF_POLL(&f->free_q, m); 1341184610Salfred } 1342184610Salfred } else { 1343184610Salfred if (f->flag_iscomplete) { 1344184610Salfred m = (void *)1; 1345184610Salfred } else { 1346184610Salfred m = NULL; 1347184610Salfred } 1348184610Salfred } 1349184610Salfred 1350184610Salfred if (m) { 1351184610Salfred revents |= events & (POLLOUT | POLLWRNORM); 1352184610Salfred } else { 1353184610Salfred f->flag_isselect = 1; 1354184610Salfred selrecord(td, &f->selinfo); 1355184610Salfred } 1356184610Salfred 1357184610Salfred mtx_unlock(f->priv_mtx); 1358184610Salfred } 1359184610Salfred if ((events & (POLLIN | POLLRDNORM)) && 1360184610Salfred (fflags & FREAD)) { 1361184610Salfred 1362193338Sthompsa f = refs.rxfifo; 1363184610Salfred 1364184610Salfred mtx_lock(f->priv_mtx); 1365184610Salfred 1366193338Sthompsa if (!refs.is_usbfs) { 1367184610Salfred if (f->flag_iserror) { 1368263162Shselasky /* we have an error */ 1369184610Salfred m = (void *)1; 1370184610Salfred } else { 1371184610Salfred if (f->queue_data == NULL) { 1372184610Salfred /* 1373184610Salfred * start read transfer, if not 1374184610Salfred * already started 1375184610Salfred */ 1376184610Salfred (f->methods->f_start_read) (f); 1377184610Salfred } 1378184610Salfred /* check if any packets are available */ 1379184610Salfred USB_IF_POLL(&f->used_q, m); 1380184610Salfred } 1381184610Salfred } else { 1382184610Salfred if (f->flag_iscomplete) { 1383184610Salfred m = (void *)1; 1384184610Salfred } else { 1385184610Salfred m = NULL; 1386184610Salfred } 1387184610Salfred } 1388184610Salfred 1389184610Salfred if (m) { 1390184610Salfred revents |= events & (POLLIN | POLLRDNORM); 1391184610Salfred } else { 1392184610Salfred f->flag_isselect = 1; 1393184610Salfred selrecord(td, &f->selinfo); 1394184610Salfred 1395193338Sthompsa if (!refs.is_usbfs) { 1396185087Salfred /* start reading data */ 1397185087Salfred (f->methods->f_start_read) (f); 1398185087Salfred } 1399184610Salfred } 1400184610Salfred 1401184610Salfred mtx_unlock(f->priv_mtx); 1402184610Salfred } 1403194228Sthompsa usb_unref_device(cpd, &refs); 1404184610Salfred return (revents); 1405184610Salfred} 1406184610Salfred 1407184610Salfredstatic int 1408194228Sthompsausb_read(struct cdev *dev, struct uio *uio, int ioflag) 1409184610Salfred{ 1410193338Sthompsa struct usb_cdev_refdata refs; 1411192984Sthompsa struct usb_cdev_privdata* cpd; 1412192984Sthompsa struct usb_fifo *f; 1413192984Sthompsa struct usb_mbuf *m; 1414184610Salfred int fflags; 1415184610Salfred int resid; 1416184610Salfred int io_len; 1417184610Salfred int err; 1418184610Salfred uint8_t tr_data = 0; 1419184610Salfred 1420189110Sthompsa err = devfs_get_cdevpriv((void **)&cpd); 1421189110Sthompsa if (err != 0) 1422189110Sthompsa return (err); 1423184610Salfred 1424194228Sthompsa err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 1425272592Shselasky if (err) 1426184610Salfred return (ENXIO); 1427272592Shselasky 1428189110Sthompsa fflags = cpd->fflags; 1429189110Sthompsa 1430193338Sthompsa f = refs.rxfifo; 1431184610Salfred if (f == NULL) { 1432184610Salfred /* should not happen */ 1433194228Sthompsa usb_unref_device(cpd, &refs); 1434184610Salfred return (EPERM); 1435184610Salfred } 1436189110Sthompsa 1437184610Salfred resid = uio->uio_resid; 1438184610Salfred 1439184610Salfred mtx_lock(f->priv_mtx); 1440184610Salfred 1441185087Salfred /* check for permanent read error */ 1442184610Salfred if (f->flag_iserror) { 1443184610Salfred err = EIO; 1444184610Salfred goto done; 1445184610Salfred } 1446185087Salfred /* check if USB-FS interface is active */ 1447193338Sthompsa if (refs.is_usbfs) { 1448185087Salfred /* 1449185087Salfred * The queue is used for events that should be 1450185087Salfred * retrieved using the "USB_FS_COMPLETE" ioctl. 1451185087Salfred */ 1452185087Salfred err = EINVAL; 1453185087Salfred goto done; 1454185087Salfred } 1455184610Salfred while (uio->uio_resid > 0) { 1456184610Salfred 1457185087Salfred USB_IF_DEQUEUE(&f->used_q, m); 1458184610Salfred 1459184610Salfred if (m == NULL) { 1460184610Salfred 1461184610Salfred /* start read transfer, if not already started */ 1462184610Salfred 1463184610Salfred (f->methods->f_start_read) (f); 1464184610Salfred 1465192054Sthompsa if (ioflag & IO_NDELAY) { 1466184610Salfred if (tr_data) { 1467184610Salfred /* return length before error */ 1468184610Salfred break; 1469184610Salfred } 1470184610Salfred err = EWOULDBLOCK; 1471184610Salfred break; 1472184610Salfred } 1473184610Salfred DPRINTF("sleeping\n"); 1474184610Salfred 1475194228Sthompsa err = usb_fifo_wait(f); 1476184610Salfred if (err) { 1477184610Salfred break; 1478184610Salfred } 1479184610Salfred continue; 1480184610Salfred } 1481185087Salfred if (f->methods->f_filter_read) { 1482185087Salfred /* 1483185087Salfred * Sometimes it is convenient to process data at the 1484185087Salfred * expense of a userland process instead of a kernel 1485185087Salfred * process. 1486185087Salfred */ 1487185087Salfred (f->methods->f_filter_read) (f, m); 1488185087Salfred } 1489185087Salfred tr_data = 1; 1490184610Salfred 1491184610Salfred io_len = MIN(m->cur_data_len, uio->uio_resid); 1492184610Salfred 1493184610Salfred DPRINTFN(2, "transfer %d bytes from %p\n", 1494184610Salfred io_len, m->cur_data_ptr); 1495184610Salfred 1496194228Sthompsa err = usb_fifo_uiomove(f, 1497184610Salfred m->cur_data_ptr, io_len, uio); 1498184610Salfred 1499184610Salfred m->cur_data_len -= io_len; 1500184610Salfred m->cur_data_ptr += io_len; 1501184610Salfred 1502184610Salfred if (m->cur_data_len == 0) { 1503184610Salfred 1504184610Salfred uint8_t last_packet; 1505184610Salfred 1506184610Salfred last_packet = m->last_packet; 1507184610Salfred 1508184610Salfred USB_IF_ENQUEUE(&f->free_q, m); 1509184610Salfred 1510184610Salfred if (last_packet) { 1511184610Salfred /* keep framing */ 1512184610Salfred break; 1513184610Salfred } 1514184610Salfred } else { 1515184610Salfred USB_IF_PREPEND(&f->used_q, m); 1516184610Salfred } 1517184610Salfred 1518184610Salfred if (err) { 1519184610Salfred break; 1520184610Salfred } 1521184610Salfred } 1522184610Salfreddone: 1523184610Salfred mtx_unlock(f->priv_mtx); 1524184610Salfred 1525194228Sthompsa usb_unref_device(cpd, &refs); 1526184610Salfred 1527184610Salfred return (err); 1528184610Salfred} 1529184610Salfred 1530184610Salfredstatic int 1531194228Sthompsausb_write(struct cdev *dev, struct uio *uio, int ioflag) 1532184610Salfred{ 1533193338Sthompsa struct usb_cdev_refdata refs; 1534192984Sthompsa struct usb_cdev_privdata* cpd; 1535192984Sthompsa struct usb_fifo *f; 1536192984Sthompsa struct usb_mbuf *m; 1537195963Salfred uint8_t *pdata; 1538184610Salfred int fflags; 1539184610Salfred int resid; 1540184610Salfred int io_len; 1541184610Salfred int err; 1542184610Salfred uint8_t tr_data = 0; 1543184610Salfred 1544184610Salfred DPRINTFN(2, "\n"); 1545184610Salfred 1546189110Sthompsa err = devfs_get_cdevpriv((void **)&cpd); 1547189110Sthompsa if (err != 0) 1548189110Sthompsa return (err); 1549184610Salfred 1550194228Sthompsa err = usb_ref_device(cpd, &refs, 0 /* no uref */ ); 1551272592Shselasky if (err) 1552184610Salfred return (ENXIO); 1553272592Shselasky 1554189110Sthompsa fflags = cpd->fflags; 1555189110Sthompsa 1556193338Sthompsa f = refs.txfifo; 1557184610Salfred if (f == NULL) { 1558184610Salfred /* should not happen */ 1559194228Sthompsa usb_unref_device(cpd, &refs); 1560184610Salfred return (EPERM); 1561184610Salfred } 1562184610Salfred resid = uio->uio_resid; 1563184610Salfred 1564184610Salfred mtx_lock(f->priv_mtx); 1565184610Salfred 1566185087Salfred /* check for permanent write error */ 1567184610Salfred if (f->flag_iserror) { 1568184610Salfred err = EIO; 1569184610Salfred goto done; 1570184610Salfred } 1571185087Salfred /* check if USB-FS interface is active */ 1572193338Sthompsa if (refs.is_usbfs) { 1573185087Salfred /* 1574185087Salfred * The queue is used for events that should be 1575185087Salfred * retrieved using the "USB_FS_COMPLETE" ioctl. 1576185087Salfred */ 1577185087Salfred err = EINVAL; 1578185087Salfred goto done; 1579185087Salfred } 1580185087Salfred if (f->queue_data == NULL) { 1581184610Salfred /* start write transfer, if not already started */ 1582184610Salfred (f->methods->f_start_write) (f); 1583184610Salfred } 1584184610Salfred /* we allow writing zero length data */ 1585184610Salfred do { 1586185087Salfred USB_IF_DEQUEUE(&f->free_q, m); 1587184610Salfred 1588184610Salfred if (m == NULL) { 1589184610Salfred 1590192054Sthompsa if (ioflag & IO_NDELAY) { 1591184610Salfred if (tr_data) { 1592184610Salfred /* return length before error */ 1593184610Salfred break; 1594184610Salfred } 1595184610Salfred err = EWOULDBLOCK; 1596184610Salfred break; 1597184610Salfred } 1598184610Salfred DPRINTF("sleeping\n"); 1599184610Salfred 1600194228Sthompsa err = usb_fifo_wait(f); 1601184610Salfred if (err) { 1602184610Salfred break; 1603184610Salfred } 1604184610Salfred continue; 1605184610Salfred } 1606185087Salfred tr_data = 1; 1607184610Salfred 1608195963Salfred if (f->flag_have_fragment == 0) { 1609195963Salfred USB_MBUF_RESET(m); 1610195963Salfred io_len = m->cur_data_len; 1611195963Salfred pdata = m->cur_data_ptr; 1612195963Salfred if (io_len > uio->uio_resid) 1613195963Salfred io_len = uio->uio_resid; 1614195963Salfred m->cur_data_len = io_len; 1615195963Salfred } else { 1616195963Salfred io_len = m->max_data_len - m->cur_data_len; 1617195963Salfred pdata = m->cur_data_ptr + m->cur_data_len; 1618195963Salfred if (io_len > uio->uio_resid) 1619195963Salfred io_len = uio->uio_resid; 1620195963Salfred m->cur_data_len += io_len; 1621195963Salfred } 1622184610Salfred 1623184610Salfred DPRINTFN(2, "transfer %d bytes to %p\n", 1624195963Salfred io_len, pdata); 1625184610Salfred 1626195963Salfred err = usb_fifo_uiomove(f, pdata, io_len, uio); 1627184610Salfred 1628184610Salfred if (err) { 1629195963Salfred f->flag_have_fragment = 0; 1630184610Salfred USB_IF_ENQUEUE(&f->free_q, m); 1631184610Salfred break; 1632184610Salfred } 1633195963Salfred 1634195963Salfred /* check if the buffer is ready to be transmitted */ 1635195963Salfred 1636195963Salfred if ((f->flag_write_defrag == 0) || 1637195963Salfred (m->cur_data_len == m->max_data_len)) { 1638195963Salfred f->flag_have_fragment = 0; 1639195963Salfred 1640185087Salfred /* 1641195963Salfred * Check for write filter: 1642195963Salfred * 1643195963Salfred * Sometimes it is convenient to process data 1644195963Salfred * at the expense of a userland process 1645195963Salfred * instead of a kernel process. 1646185087Salfred */ 1647195963Salfred if (f->methods->f_filter_write) { 1648195963Salfred (f->methods->f_filter_write) (f, m); 1649195963Salfred } 1650195963Salfred 1651195963Salfred /* Put USB mbuf in the used queue */ 1652195963Salfred USB_IF_ENQUEUE(&f->used_q, m); 1653195963Salfred 1654195963Salfred /* Start writing data, if not already started */ 1655195963Salfred (f->methods->f_start_write) (f); 1656195963Salfred } else { 1657195963Salfred /* Wait for more data or close */ 1658195963Salfred f->flag_have_fragment = 1; 1659195963Salfred USB_IF_PREPEND(&f->free_q, m); 1660185087Salfred } 1661185087Salfred 1662184610Salfred } while (uio->uio_resid > 0); 1663184610Salfreddone: 1664184610Salfred mtx_unlock(f->priv_mtx); 1665184610Salfred 1666194228Sthompsa usb_unref_device(cpd, &refs); 1667184610Salfred 1668189110Sthompsa return (err); 1669189110Sthompsa} 1670184610Salfred 1671189110Sthompsaint 1672194228Sthompsausb_static_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 1673189110Sthompsa struct thread *td) 1674189110Sthompsa{ 1675189110Sthompsa union { 1676192984Sthompsa struct usb_read_dir *urd; 1677189110Sthompsa void* data; 1678189110Sthompsa } u; 1679213431Shselasky int err; 1680189110Sthompsa 1681189110Sthompsa u.data = data; 1682189110Sthompsa switch (cmd) { 1683189110Sthompsa case USB_READ_DIR: 1684194228Sthompsa err = usb_read_symlink(u.urd->urd_data, 1685189110Sthompsa u.urd->urd_startentry, u.urd->urd_maxlen); 1686189110Sthompsa break; 1687189110Sthompsa case USB_DEV_QUIRK_GET: 1688189110Sthompsa case USB_QUIRK_NAME_GET: 1689189110Sthompsa case USB_DEV_QUIRK_ADD: 1690189110Sthompsa case USB_DEV_QUIRK_REMOVE: 1691194228Sthompsa err = usb_quirk_ioctl_p(cmd, data, fflag, td); 1692189110Sthompsa break; 1693189110Sthompsa case USB_GET_TEMPLATE: 1694194228Sthompsa *(int *)data = usb_template; 1695213431Shselasky err = 0; 1696189110Sthompsa break; 1697189110Sthompsa case USB_SET_TEMPLATE: 1698189125Sthompsa err = priv_check(curthread, PRIV_DRIVER); 1699189110Sthompsa if (err) 1700189110Sthompsa break; 1701194228Sthompsa usb_template = *(int *)data; 1702189110Sthompsa break; 1703213431Shselasky default: 1704213431Shselasky err = ENOTTY; 1705213431Shselasky break; 1706189110Sthompsa } 1707184610Salfred return (err); 1708184610Salfred} 1709184610Salfred 1710184610Salfredstatic int 1711194228Sthompsausb_fifo_uiomove(struct usb_fifo *f, void *cp, 1712184610Salfred int n, struct uio *uio) 1713184610Salfred{ 1714184610Salfred int error; 1715184610Salfred 1716184610Salfred mtx_unlock(f->priv_mtx); 1717184610Salfred 1718184610Salfred /* 1719184610Salfred * "uiomove()" can sleep so one needs to make a wrapper, 1720184610Salfred * exiting the mutex and checking things: 1721184610Salfred */ 1722184610Salfred error = uiomove(cp, n, uio); 1723184610Salfred 1724184610Salfred mtx_lock(f->priv_mtx); 1725184610Salfred 1726184610Salfred return (error); 1727184610Salfred} 1728184610Salfred 1729184610Salfredint 1730194228Sthompsausb_fifo_wait(struct usb_fifo *f) 1731184610Salfred{ 1732184610Salfred int err; 1733184610Salfred 1734184610Salfred mtx_assert(f->priv_mtx, MA_OWNED); 1735184610Salfred 1736184610Salfred if (f->flag_iserror) { 1737184610Salfred /* we are gone */ 1738184610Salfred return (EIO); 1739184610Salfred } 1740184610Salfred f->flag_sleeping = 1; 1741184610Salfred 1742194227Sthompsa err = cv_wait_sig(&f->cv_io, f->priv_mtx); 1743184610Salfred 1744184610Salfred if (f->flag_iserror) { 1745184610Salfred /* we are gone */ 1746184610Salfred err = EIO; 1747184610Salfred } 1748184610Salfred return (err); 1749184610Salfred} 1750184610Salfred 1751184610Salfredvoid 1752194228Sthompsausb_fifo_signal(struct usb_fifo *f) 1753184610Salfred{ 1754184610Salfred if (f->flag_sleeping) { 1755184610Salfred f->flag_sleeping = 0; 1756194227Sthompsa cv_broadcast(&f->cv_io); 1757184610Salfred } 1758184610Salfred} 1759184610Salfred 1760184610Salfredvoid 1761194228Sthompsausb_fifo_wakeup(struct usb_fifo *f) 1762184610Salfred{ 1763194228Sthompsa usb_fifo_signal(f); 1764184610Salfred 1765263162Shselasky KNOTE_LOCKED(&f->selinfo.si_note, 0); 1766263162Shselasky 1767184610Salfred if (f->flag_isselect) { 1768184610Salfred selwakeup(&f->selinfo); 1769184610Salfred f->flag_isselect = 0; 1770184610Salfred } 1771184610Salfred if (f->async_p != NULL) { 1772184610Salfred PROC_LOCK(f->async_p); 1773225617Skmacy kern_psignal(f->async_p, SIGIO); 1774184610Salfred PROC_UNLOCK(f->async_p); 1775184610Salfred } 1776184610Salfred} 1777184610Salfred 1778184610Salfredstatic int 1779194228Sthompsausb_fifo_dummy_open(struct usb_fifo *fifo, int fflags) 1780184610Salfred{ 1781184610Salfred return (0); 1782184610Salfred} 1783184610Salfred 1784184610Salfredstatic void 1785194228Sthompsausb_fifo_dummy_close(struct usb_fifo *fifo, int fflags) 1786184610Salfred{ 1787184610Salfred return; 1788184610Salfred} 1789184610Salfred 1790184610Salfredstatic int 1791194228Sthompsausb_fifo_dummy_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) 1792184610Salfred{ 1793185087Salfred return (ENOIOCTL); 1794184610Salfred} 1795184610Salfred 1796184610Salfredstatic void 1797194228Sthompsausb_fifo_dummy_cmd(struct usb_fifo *fifo) 1798184610Salfred{ 1799184610Salfred fifo->flag_flushing = 0; /* not flushing */ 1800184610Salfred} 1801184610Salfred 1802184610Salfredstatic void 1803194228Sthompsausb_fifo_check_methods(struct usb_fifo_methods *pm) 1804184610Salfred{ 1805184610Salfred /* check that all callback functions are OK */ 1806184610Salfred 1807184610Salfred if (pm->f_open == NULL) 1808194228Sthompsa pm->f_open = &usb_fifo_dummy_open; 1809184610Salfred 1810184610Salfred if (pm->f_close == NULL) 1811194228Sthompsa pm->f_close = &usb_fifo_dummy_close; 1812184610Salfred 1813184610Salfred if (pm->f_ioctl == NULL) 1814194228Sthompsa pm->f_ioctl = &usb_fifo_dummy_ioctl; 1815184610Salfred 1816185087Salfred if (pm->f_ioctl_post == NULL) 1817194228Sthompsa pm->f_ioctl_post = &usb_fifo_dummy_ioctl; 1818185087Salfred 1819184610Salfred if (pm->f_start_read == NULL) 1820194228Sthompsa pm->f_start_read = &usb_fifo_dummy_cmd; 1821184610Salfred 1822184610Salfred if (pm->f_stop_read == NULL) 1823194228Sthompsa pm->f_stop_read = &usb_fifo_dummy_cmd; 1824184610Salfred 1825184610Salfred if (pm->f_start_write == NULL) 1826194228Sthompsa pm->f_start_write = &usb_fifo_dummy_cmd; 1827184610Salfred 1828184610Salfred if (pm->f_stop_write == NULL) 1829194228Sthompsa pm->f_stop_write = &usb_fifo_dummy_cmd; 1830184610Salfred} 1831184610Salfred 1832184610Salfred/*------------------------------------------------------------------------* 1833194228Sthompsa * usb_fifo_attach 1834184610Salfred * 1835184610Salfred * The following function will create a duplex FIFO. 1836184610Salfred * 1837184610Salfred * Return values: 1838184610Salfred * 0: Success. 1839184610Salfred * Else: Failure. 1840184610Salfred *------------------------------------------------------------------------*/ 1841184610Salfredint 1842194228Sthompsausb_fifo_attach(struct usb_device *udev, void *priv_sc, 1843192984Sthompsa struct mtx *priv_mtx, struct usb_fifo_methods *pm, 1844233774Shselasky struct usb_fifo_sc *f_sc, uint16_t unit, int16_t subunit, 1845189110Sthompsa uint8_t iface_index, uid_t uid, gid_t gid, int mode) 1846184610Salfred{ 1847192984Sthompsa struct usb_fifo *f_tx; 1848192984Sthompsa struct usb_fifo *f_rx; 1849189110Sthompsa char devname[32]; 1850184610Salfred uint8_t n; 1851184610Salfred 1852184610Salfred f_sc->fp[USB_FIFO_TX] = NULL; 1853184610Salfred f_sc->fp[USB_FIFO_RX] = NULL; 1854184610Salfred 1855184610Salfred if (pm == NULL) 1856184610Salfred return (EINVAL); 1857184610Salfred 1858184610Salfred /* check the methods */ 1859194228Sthompsa usb_fifo_check_methods(pm); 1860184610Salfred 1861184610Salfred if (priv_mtx == NULL) 1862184610Salfred priv_mtx = &Giant; 1863184610Salfred 1864184610Salfred /* search for a free FIFO slot */ 1865184610Salfred for (n = 0;; n += 2) { 1866184610Salfred 1867184610Salfred if (n == USB_FIFO_MAX) { 1868184610Salfred /* end of FIFOs reached */ 1869184610Salfred return (ENOMEM); 1870184610Salfred } 1871184610Salfred /* Check for TX FIFO */ 1872184610Salfred if (udev->fifo[n + USB_FIFO_TX] != NULL) { 1873184610Salfred continue; 1874184610Salfred } 1875184610Salfred /* Check for RX FIFO */ 1876184610Salfred if (udev->fifo[n + USB_FIFO_RX] != NULL) { 1877184610Salfred continue; 1878184610Salfred } 1879184610Salfred break; 1880184610Salfred } 1881184610Salfred 1882263162Shselasky f_tx = usb_fifo_alloc(priv_mtx); 1883263162Shselasky f_rx = usb_fifo_alloc(priv_mtx); 1884184610Salfred 1885184610Salfred if ((f_tx == NULL) || (f_rx == NULL)) { 1886194228Sthompsa usb_fifo_free(f_tx); 1887194228Sthompsa usb_fifo_free(f_rx); 1888184610Salfred return (ENOMEM); 1889184610Salfred } 1890184610Salfred /* initialise FIFO structures */ 1891184610Salfred 1892184610Salfred f_tx->fifo_index = n + USB_FIFO_TX; 1893190269Sthompsa f_tx->dev_ep_index = -1; 1894184610Salfred f_tx->priv_sc0 = priv_sc; 1895184610Salfred f_tx->methods = pm; 1896184610Salfred f_tx->iface_index = iface_index; 1897184610Salfred f_tx->udev = udev; 1898184610Salfred 1899184610Salfred f_rx->fifo_index = n + USB_FIFO_RX; 1900190269Sthompsa f_rx->dev_ep_index = -1; 1901184610Salfred f_rx->priv_sc0 = priv_sc; 1902184610Salfred f_rx->methods = pm; 1903184610Salfred f_rx->iface_index = iface_index; 1904184610Salfred f_rx->udev = udev; 1905184610Salfred 1906184610Salfred f_sc->fp[USB_FIFO_TX] = f_tx; 1907184610Salfred f_sc->fp[USB_FIFO_RX] = f_rx; 1908184610Salfred 1909194228Sthompsa mtx_lock(&usb_ref_lock); 1910184610Salfred udev->fifo[f_tx->fifo_index] = f_tx; 1911184610Salfred udev->fifo[f_rx->fifo_index] = f_rx; 1912194228Sthompsa mtx_unlock(&usb_ref_lock); 1913184610Salfred 1914184610Salfred for (n = 0; n != 4; n++) { 1915184610Salfred 1916184610Salfred if (pm->basename[n] == NULL) { 1917184610Salfred continue; 1918184610Salfred } 1919233774Shselasky if (subunit < 0) { 1920189110Sthompsa if (snprintf(devname, sizeof(devname), 1921184610Salfred "%s%u%s", pm->basename[n], 1922184610Salfred unit, pm->postfix[n] ? 1923184610Salfred pm->postfix[n] : "")) { 1924184610Salfred /* ignore */ 1925184610Salfred } 1926184610Salfred } else { 1927189110Sthompsa if (snprintf(devname, sizeof(devname), 1928233774Shselasky "%s%u.%d%s", pm->basename[n], 1929184610Salfred unit, subunit, pm->postfix[n] ? 1930184610Salfred pm->postfix[n] : "")) { 1931184610Salfred /* ignore */ 1932184610Salfred } 1933184610Salfred } 1934184610Salfred 1935184610Salfred /* 1936184610Salfred * Distribute the symbolic links into two FIFO structures: 1937184610Salfred */ 1938184610Salfred if (n & 1) { 1939184610Salfred f_rx->symlink[n / 2] = 1940194228Sthompsa usb_alloc_symlink(devname); 1941184610Salfred } else { 1942184610Salfred f_tx->symlink[n / 2] = 1943194228Sthompsa usb_alloc_symlink(devname); 1944184610Salfred } 1945189110Sthompsa 1946224777Shselasky /* Create the device */ 1947224777Shselasky f_sc->dev = usb_make_dev(udev, devname, -1, 1948224777Shselasky f_tx->fifo_index & f_rx->fifo_index, 1949224777Shselasky FREAD|FWRITE, uid, gid, mode); 1950184610Salfred } 1951184610Salfred 1952184610Salfred DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx); 1953184610Salfred return (0); 1954184610Salfred} 1955184610Salfred 1956184610Salfred/*------------------------------------------------------------------------* 1957194228Sthompsa * usb_fifo_alloc_buffer 1958184610Salfred * 1959184610Salfred * Return values: 1960184610Salfred * 0: Success 1961184610Salfred * Else failure 1962184610Salfred *------------------------------------------------------------------------*/ 1963184610Salfredint 1964194228Sthompsausb_fifo_alloc_buffer(struct usb_fifo *f, usb_size_t bufsize, 1965184610Salfred uint16_t nbuf) 1966184610Salfred{ 1967194228Sthompsa usb_fifo_free_buffer(f); 1968184610Salfred 1969184610Salfred /* allocate an endpoint */ 1970184610Salfred f->free_q.ifq_maxlen = nbuf; 1971184610Salfred f->used_q.ifq_maxlen = nbuf; 1972184610Salfred 1973194228Sthompsa f->queue_data = usb_alloc_mbufs( 1974184610Salfred M_USBDEV, &f->free_q, bufsize, nbuf); 1975184610Salfred 1976184610Salfred if ((f->queue_data == NULL) && bufsize && nbuf) { 1977184610Salfred return (ENOMEM); 1978184610Salfred } 1979184610Salfred return (0); /* success */ 1980184610Salfred} 1981184610Salfred 1982184610Salfred/*------------------------------------------------------------------------* 1983194228Sthompsa * usb_fifo_free_buffer 1984184610Salfred * 1985184610Salfred * This function will free the buffers associated with a FIFO. This 1986184610Salfred * function can be called multiple times in a row. 1987184610Salfred *------------------------------------------------------------------------*/ 1988184610Salfredvoid 1989194228Sthompsausb_fifo_free_buffer(struct usb_fifo *f) 1990184610Salfred{ 1991184610Salfred if (f->queue_data) { 1992184610Salfred /* free old buffer */ 1993184610Salfred free(f->queue_data, M_USBDEV); 1994184610Salfred f->queue_data = NULL; 1995184610Salfred } 1996184610Salfred /* reset queues */ 1997184610Salfred 1998227461Shselasky memset(&f->free_q, 0, sizeof(f->free_q)); 1999227461Shselasky memset(&f->used_q, 0, sizeof(f->used_q)); 2000184610Salfred} 2001184610Salfred 2002184610Salfredvoid 2003194228Sthompsausb_fifo_detach(struct usb_fifo_sc *f_sc) 2004184610Salfred{ 2005184610Salfred if (f_sc == NULL) { 2006184610Salfred return; 2007184610Salfred } 2008194228Sthompsa usb_fifo_free(f_sc->fp[USB_FIFO_TX]); 2009194228Sthompsa usb_fifo_free(f_sc->fp[USB_FIFO_RX]); 2010184610Salfred 2011184610Salfred f_sc->fp[USB_FIFO_TX] = NULL; 2012184610Salfred f_sc->fp[USB_FIFO_RX] = NULL; 2013184610Salfred 2014224777Shselasky usb_destroy_dev(f_sc->dev); 2015189110Sthompsa 2016224777Shselasky f_sc->dev = NULL; 2017224777Shselasky 2018184610Salfred DPRINTFN(2, "detached %p\n", f_sc); 2019184610Salfred} 2020184610Salfred 2021193074Sthompsausb_size_t 2022194228Sthompsausb_fifo_put_bytes_max(struct usb_fifo *f) 2023184610Salfred{ 2024192984Sthompsa struct usb_mbuf *m; 2025193074Sthompsa usb_size_t len; 2026184610Salfred 2027184610Salfred USB_IF_POLL(&f->free_q, m); 2028184610Salfred 2029184610Salfred if (m) { 2030184610Salfred len = m->max_data_len; 2031184610Salfred } else { 2032184610Salfred len = 0; 2033184610Salfred } 2034184610Salfred return (len); 2035184610Salfred} 2036184610Salfred 2037184610Salfred/*------------------------------------------------------------------------* 2038194228Sthompsa * usb_fifo_put_data 2039184610Salfred * 2040184610Salfred * what: 2041184610Salfred * 0 - normal operation 2042184610Salfred * 1 - set last packet flag to enforce framing 2043184610Salfred *------------------------------------------------------------------------*/ 2044184610Salfredvoid 2045194228Sthompsausb_fifo_put_data(struct usb_fifo *f, struct usb_page_cache *pc, 2046193045Sthompsa usb_frlength_t offset, usb_frlength_t len, uint8_t what) 2047184610Salfred{ 2048192984Sthompsa struct usb_mbuf *m; 2049193045Sthompsa usb_frlength_t io_len; 2050184610Salfred 2051184610Salfred while (len || (what == 1)) { 2052184610Salfred 2053184610Salfred USB_IF_DEQUEUE(&f->free_q, m); 2054184610Salfred 2055184610Salfred if (m) { 2056184610Salfred USB_MBUF_RESET(m); 2057184610Salfred 2058184610Salfred io_len = MIN(len, m->cur_data_len); 2059184610Salfred 2060194228Sthompsa usbd_copy_out(pc, offset, m->cur_data_ptr, io_len); 2061184610Salfred 2062184610Salfred m->cur_data_len = io_len; 2063184610Salfred offset += io_len; 2064184610Salfred len -= io_len; 2065184610Salfred 2066184610Salfred if ((len == 0) && (what == 1)) { 2067184610Salfred m->last_packet = 1; 2068184610Salfred } 2069184610Salfred USB_IF_ENQUEUE(&f->used_q, m); 2070184610Salfred 2071194228Sthompsa usb_fifo_wakeup(f); 2072184610Salfred 2073184610Salfred if ((len == 0) || (what == 1)) { 2074184610Salfred break; 2075184610Salfred } 2076184610Salfred } else { 2077184610Salfred break; 2078184610Salfred } 2079184610Salfred } 2080184610Salfred} 2081184610Salfred 2082184610Salfredvoid 2083194228Sthompsausb_fifo_put_data_linear(struct usb_fifo *f, void *ptr, 2084193074Sthompsa usb_size_t len, uint8_t what) 2085184610Salfred{ 2086192984Sthompsa struct usb_mbuf *m; 2087193074Sthompsa usb_size_t io_len; 2088184610Salfred 2089184610Salfred while (len || (what == 1)) { 2090184610Salfred 2091184610Salfred USB_IF_DEQUEUE(&f->free_q, m); 2092184610Salfred 2093184610Salfred if (m) { 2094184610Salfred USB_MBUF_RESET(m); 2095184610Salfred 2096184610Salfred io_len = MIN(len, m->cur_data_len); 2097184610Salfred 2098227461Shselasky memcpy(m->cur_data_ptr, ptr, io_len); 2099184610Salfred 2100184610Salfred m->cur_data_len = io_len; 2101184610Salfred ptr = USB_ADD_BYTES(ptr, io_len); 2102184610Salfred len -= io_len; 2103184610Salfred 2104184610Salfred if ((len == 0) && (what == 1)) { 2105184610Salfred m->last_packet = 1; 2106184610Salfred } 2107184610Salfred USB_IF_ENQUEUE(&f->used_q, m); 2108184610Salfred 2109194228Sthompsa usb_fifo_wakeup(f); 2110184610Salfred 2111184610Salfred if ((len == 0) || (what == 1)) { 2112184610Salfred break; 2113184610Salfred } 2114184610Salfred } else { 2115184610Salfred break; 2116184610Salfred } 2117184610Salfred } 2118184610Salfred} 2119184610Salfred 2120184610Salfreduint8_t 2121194228Sthompsausb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len) 2122184610Salfred{ 2123192984Sthompsa struct usb_mbuf *m; 2124184610Salfred 2125184610Salfred USB_IF_DEQUEUE(&f->free_q, m); 2126184610Salfred 2127184610Salfred if (m) { 2128184610Salfred m->cur_data_len = len; 2129184610Salfred m->cur_data_ptr = ptr; 2130184610Salfred USB_IF_ENQUEUE(&f->used_q, m); 2131194228Sthompsa usb_fifo_wakeup(f); 2132184610Salfred return (1); 2133184610Salfred } 2134184610Salfred return (0); 2135184610Salfred} 2136184610Salfred 2137184610Salfredvoid 2138194228Sthompsausb_fifo_put_data_error(struct usb_fifo *f) 2139184610Salfred{ 2140184610Salfred f->flag_iserror = 1; 2141194228Sthompsa usb_fifo_wakeup(f); 2142184610Salfred} 2143184610Salfred 2144184610Salfred/*------------------------------------------------------------------------* 2145194228Sthompsa * usb_fifo_get_data 2146184610Salfred * 2147184610Salfred * what: 2148184610Salfred * 0 - normal operation 2149192984Sthompsa * 1 - only get one "usb_mbuf" 2150184610Salfred * 2151184610Salfred * returns: 2152184610Salfred * 0 - no more data 2153184610Salfred * 1 - data in buffer 2154184610Salfred *------------------------------------------------------------------------*/ 2155184610Salfreduint8_t 2156194228Sthompsausb_fifo_get_data(struct usb_fifo *f, struct usb_page_cache *pc, 2157193045Sthompsa usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen, 2158184610Salfred uint8_t what) 2159184610Salfred{ 2160192984Sthompsa struct usb_mbuf *m; 2161193045Sthompsa usb_frlength_t io_len; 2162184610Salfred uint8_t tr_data = 0; 2163184610Salfred 2164184610Salfred actlen[0] = 0; 2165184610Salfred 2166184610Salfred while (1) { 2167184610Salfred 2168184610Salfred USB_IF_DEQUEUE(&f->used_q, m); 2169184610Salfred 2170184610Salfred if (m) { 2171184610Salfred 2172184610Salfred tr_data = 1; 2173184610Salfred 2174184610Salfred io_len = MIN(len, m->cur_data_len); 2175184610Salfred 2176194228Sthompsa usbd_copy_in(pc, offset, m->cur_data_ptr, io_len); 2177184610Salfred 2178184610Salfred len -= io_len; 2179184610Salfred offset += io_len; 2180184610Salfred actlen[0] += io_len; 2181184610Salfred m->cur_data_ptr += io_len; 2182184610Salfred m->cur_data_len -= io_len; 2183184610Salfred 2184184610Salfred if ((m->cur_data_len == 0) || (what == 1)) { 2185184610Salfred USB_IF_ENQUEUE(&f->free_q, m); 2186184610Salfred 2187194228Sthompsa usb_fifo_wakeup(f); 2188184610Salfred 2189184610Salfred if (what == 1) { 2190184610Salfred break; 2191184610Salfred } 2192184610Salfred } else { 2193184610Salfred USB_IF_PREPEND(&f->used_q, m); 2194184610Salfred } 2195184610Salfred } else { 2196184610Salfred 2197184610Salfred if (tr_data) { 2198184610Salfred /* wait for data to be written out */ 2199184610Salfred break; 2200184610Salfred } 2201184610Salfred if (f->flag_flushing) { 2202189906Sthompsa /* check if we should send a short packet */ 2203189906Sthompsa if (f->flag_short != 0) { 2204189906Sthompsa f->flag_short = 0; 2205189906Sthompsa tr_data = 1; 2206189906Sthompsa break; 2207189906Sthompsa } 2208189906Sthompsa /* flushing complete */ 2209184610Salfred f->flag_flushing = 0; 2210194228Sthompsa usb_fifo_wakeup(f); 2211184610Salfred } 2212184610Salfred break; 2213184610Salfred } 2214184610Salfred if (len == 0) { 2215184610Salfred break; 2216184610Salfred } 2217184610Salfred } 2218184610Salfred return (tr_data); 2219184610Salfred} 2220184610Salfred 2221184610Salfreduint8_t 2222194228Sthompsausb_fifo_get_data_linear(struct usb_fifo *f, void *ptr, 2223193074Sthompsa usb_size_t len, usb_size_t *actlen, uint8_t what) 2224184610Salfred{ 2225192984Sthompsa struct usb_mbuf *m; 2226193074Sthompsa usb_size_t io_len; 2227184610Salfred uint8_t tr_data = 0; 2228184610Salfred 2229184610Salfred actlen[0] = 0; 2230184610Salfred 2231184610Salfred while (1) { 2232184610Salfred 2233184610Salfred USB_IF_DEQUEUE(&f->used_q, m); 2234184610Salfred 2235184610Salfred if (m) { 2236184610Salfred 2237184610Salfred tr_data = 1; 2238184610Salfred 2239184610Salfred io_len = MIN(len, m->cur_data_len); 2240184610Salfred 2241227461Shselasky memcpy(ptr, m->cur_data_ptr, io_len); 2242184610Salfred 2243184610Salfred len -= io_len; 2244184610Salfred ptr = USB_ADD_BYTES(ptr, io_len); 2245184610Salfred actlen[0] += io_len; 2246184610Salfred m->cur_data_ptr += io_len; 2247184610Salfred m->cur_data_len -= io_len; 2248184610Salfred 2249184610Salfred if ((m->cur_data_len == 0) || (what == 1)) { 2250184610Salfred USB_IF_ENQUEUE(&f->free_q, m); 2251184610Salfred 2252194228Sthompsa usb_fifo_wakeup(f); 2253184610Salfred 2254184610Salfred if (what == 1) { 2255184610Salfred break; 2256184610Salfred } 2257184610Salfred } else { 2258184610Salfred USB_IF_PREPEND(&f->used_q, m); 2259184610Salfred } 2260184610Salfred } else { 2261184610Salfred 2262184610Salfred if (tr_data) { 2263184610Salfred /* wait for data to be written out */ 2264184610Salfred break; 2265184610Salfred } 2266184610Salfred if (f->flag_flushing) { 2267189906Sthompsa /* check if we should send a short packet */ 2268189906Sthompsa if (f->flag_short != 0) { 2269189906Sthompsa f->flag_short = 0; 2270189906Sthompsa tr_data = 1; 2271189906Sthompsa break; 2272189906Sthompsa } 2273189906Sthompsa /* flushing complete */ 2274184610Salfred f->flag_flushing = 0; 2275194228Sthompsa usb_fifo_wakeup(f); 2276184610Salfred } 2277184610Salfred break; 2278184610Salfred } 2279184610Salfred if (len == 0) { 2280184610Salfred break; 2281184610Salfred } 2282184610Salfred } 2283184610Salfred return (tr_data); 2284184610Salfred} 2285184610Salfred 2286184610Salfreduint8_t 2287194228Sthompsausb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen) 2288184610Salfred{ 2289192984Sthompsa struct usb_mbuf *m; 2290184610Salfred 2291185087Salfred USB_IF_POLL(&f->used_q, m); 2292184610Salfred 2293184610Salfred if (m) { 2294184610Salfred *plen = m->cur_data_len; 2295184610Salfred *pptr = m->cur_data_ptr; 2296184610Salfred 2297184610Salfred return (1); 2298184610Salfred } 2299184610Salfred return (0); 2300184610Salfred} 2301184610Salfred 2302184610Salfredvoid 2303194228Sthompsausb_fifo_get_data_error(struct usb_fifo *f) 2304184610Salfred{ 2305184610Salfred f->flag_iserror = 1; 2306194228Sthompsa usb_fifo_wakeup(f); 2307184610Salfred} 2308184610Salfred 2309184610Salfred/*------------------------------------------------------------------------* 2310194228Sthompsa * usb_alloc_symlink 2311184610Salfred * 2312184610Salfred * Return values: 2313184610Salfred * NULL: Failure 2314184610Salfred * Else: Pointer to symlink entry 2315184610Salfred *------------------------------------------------------------------------*/ 2316192984Sthompsastruct usb_symlink * 2317194228Sthompsausb_alloc_symlink(const char *target) 2318184610Salfred{ 2319192984Sthompsa struct usb_symlink *ps; 2320184610Salfred 2321184610Salfred ps = malloc(sizeof(*ps), M_USBDEV, M_WAITOK); 2322184610Salfred if (ps == NULL) { 2323184610Salfred return (ps); 2324184610Salfred } 2325189110Sthompsa /* XXX no longer needed */ 2326189110Sthompsa strlcpy(ps->src_path, target, sizeof(ps->src_path)); 2327189110Sthompsa ps->src_len = strlen(ps->src_path); 2328184610Salfred strlcpy(ps->dst_path, target, sizeof(ps->dst_path)); 2329184610Salfred ps->dst_len = strlen(ps->dst_path); 2330184610Salfred 2331194228Sthompsa sx_xlock(&usb_sym_lock); 2332194228Sthompsa TAILQ_INSERT_TAIL(&usb_sym_head, ps, sym_entry); 2333194228Sthompsa sx_unlock(&usb_sym_lock); 2334184610Salfred return (ps); 2335184610Salfred} 2336184610Salfred 2337184610Salfred/*------------------------------------------------------------------------* 2338194228Sthompsa * usb_free_symlink 2339184610Salfred *------------------------------------------------------------------------*/ 2340184610Salfredvoid 2341194228Sthompsausb_free_symlink(struct usb_symlink *ps) 2342184610Salfred{ 2343184610Salfred if (ps == NULL) { 2344184610Salfred return; 2345184610Salfred } 2346194228Sthompsa sx_xlock(&usb_sym_lock); 2347194228Sthompsa TAILQ_REMOVE(&usb_sym_head, ps, sym_entry); 2348194228Sthompsa sx_unlock(&usb_sym_lock); 2349184610Salfred 2350184610Salfred free(ps, M_USBDEV); 2351184610Salfred} 2352184610Salfred 2353184610Salfred/*------------------------------------------------------------------------* 2354194228Sthompsa * usb_read_symlink 2355184610Salfred * 2356184610Salfred * Return value: 2357184610Salfred * 0: Success 2358184610Salfred * Else: Failure 2359184610Salfred *------------------------------------------------------------------------*/ 2360184610Salfredint 2361194228Sthompsausb_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len) 2362184610Salfred{ 2363192984Sthompsa struct usb_symlink *ps; 2364184610Salfred uint32_t temp; 2365184610Salfred uint32_t delta = 0; 2366184610Salfred uint8_t len; 2367184610Salfred int error = 0; 2368184610Salfred 2369194228Sthompsa sx_xlock(&usb_sym_lock); 2370184610Salfred 2371194228Sthompsa TAILQ_FOREACH(ps, &usb_sym_head, sym_entry) { 2372184610Salfred 2373184610Salfred /* 2374184610Salfred * Compute total length of source and destination symlink 2375184610Salfred * strings pluss one length byte and two NUL bytes: 2376184610Salfred */ 2377184610Salfred temp = ps->src_len + ps->dst_len + 3; 2378184610Salfred 2379184610Salfred if (temp > 255) { 2380184610Salfred /* 2381184610Salfred * Skip entry because this length cannot fit 2382184610Salfred * into one byte: 2383184610Salfred */ 2384184610Salfred continue; 2385184610Salfred } 2386184610Salfred if (startentry != 0) { 2387184610Salfred /* decrement read offset */ 2388184610Salfred startentry--; 2389184610Salfred continue; 2390184610Salfred } 2391184610Salfred if (temp > user_len) { 2392184610Salfred /* out of buffer space */ 2393184610Salfred break; 2394184610Salfred } 2395184610Salfred len = temp; 2396184610Salfred 2397184610Salfred /* copy out total length */ 2398184610Salfred 2399184610Salfred error = copyout(&len, 2400184610Salfred USB_ADD_BYTES(user_ptr, delta), 1); 2401184610Salfred if (error) { 2402184610Salfred break; 2403184610Salfred } 2404184610Salfred delta += 1; 2405184610Salfred 2406184610Salfred /* copy out source string */ 2407184610Salfred 2408184610Salfred error = copyout(ps->src_path, 2409184610Salfred USB_ADD_BYTES(user_ptr, delta), ps->src_len); 2410184610Salfred if (error) { 2411184610Salfred break; 2412184610Salfred } 2413184610Salfred len = 0; 2414184610Salfred delta += ps->src_len; 2415184610Salfred error = copyout(&len, 2416184610Salfred USB_ADD_BYTES(user_ptr, delta), 1); 2417184610Salfred if (error) { 2418184610Salfred break; 2419184610Salfred } 2420184610Salfred delta += 1; 2421184610Salfred 2422184610Salfred /* copy out destination string */ 2423184610Salfred 2424184610Salfred error = copyout(ps->dst_path, 2425184610Salfred USB_ADD_BYTES(user_ptr, delta), ps->dst_len); 2426184610Salfred if (error) { 2427184610Salfred break; 2428184610Salfred } 2429184610Salfred len = 0; 2430184610Salfred delta += ps->dst_len; 2431184610Salfred error = copyout(&len, 2432184610Salfred USB_ADD_BYTES(user_ptr, delta), 1); 2433184610Salfred if (error) { 2434184610Salfred break; 2435184610Salfred } 2436184610Salfred delta += 1; 2437184610Salfred 2438184610Salfred user_len -= temp; 2439184610Salfred } 2440184610Salfred 2441184610Salfred /* a zero length entry indicates the end */ 2442184610Salfred 2443184610Salfred if ((user_len != 0) && (error == 0)) { 2444184610Salfred 2445184610Salfred len = 0; 2446184610Salfred 2447184610Salfred error = copyout(&len, 2448184610Salfred USB_ADD_BYTES(user_ptr, delta), 1); 2449184610Salfred } 2450194228Sthompsa sx_unlock(&usb_sym_lock); 2451184610Salfred return (error); 2452184610Salfred} 2453189906Sthompsa 2454189906Sthompsavoid 2455194228Sthompsausb_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff) 2456189906Sthompsa{ 2457189906Sthompsa if (f == NULL) 2458189906Sthompsa return; 2459189906Sthompsa 2460189906Sthompsa /* send a Zero Length Packet, ZLP, before close */ 2461189906Sthompsa f->flag_short = onoff; 2462189906Sthompsa} 2463194677Sthompsa 2464195963Salfredvoid 2465195963Salfredusb_fifo_set_write_defrag(struct usb_fifo *f, uint8_t onoff) 2466195963Salfred{ 2467195963Salfred if (f == NULL) 2468195963Salfred return; 2469195963Salfred 2470195963Salfred /* defrag written data */ 2471195963Salfred f->flag_write_defrag = onoff; 2472195963Salfred /* reset defrag state */ 2473195963Salfred f->flag_have_fragment = 0; 2474195963Salfred} 2475195963Salfred 2476194677Sthompsavoid * 2477194677Sthompsausb_fifo_softc(struct usb_fifo *f) 2478194677Sthompsa{ 2479194677Sthompsa return (f->priv_sc0); 2480194677Sthompsa} 2481190191Sthompsa#endif /* USB_HAVE_UGEN */ 2482