102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 302ac6454SAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 402ac6454SAndrew Thompson * 502ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 602ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 702ac6454SAndrew Thompson * are met: 802ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 902ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1002ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1202ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1302ac6454SAndrew Thompson * 1402ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1502ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1602ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1702ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1802ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1902ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2002ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2102ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2202ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2302ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2402ac6454SAndrew Thompson * SUCH DAMAGE. 2502ac6454SAndrew Thompson */ 2602ac6454SAndrew Thompson 27ed6d949aSAndrew Thompson #include <sys/stdint.h> 28ed6d949aSAndrew Thompson #include <sys/stddef.h> 29ed6d949aSAndrew Thompson #include <sys/param.h> 30ed6d949aSAndrew Thompson #include <sys/queue.h> 31ed6d949aSAndrew Thompson #include <sys/types.h> 32ed6d949aSAndrew Thompson #include <sys/systm.h> 33ed6d949aSAndrew Thompson #include <sys/kernel.h> 34ed6d949aSAndrew Thompson #include <sys/bus.h> 35ed6d949aSAndrew Thompson #include <sys/module.h> 36ed6d949aSAndrew Thompson #include <sys/lock.h> 37ed6d949aSAndrew Thompson #include <sys/mutex.h> 38ed6d949aSAndrew Thompson #include <sys/condvar.h> 39ed6d949aSAndrew Thompson #include <sys/sysctl.h> 40ed6d949aSAndrew Thompson #include <sys/sx.h> 41ed6d949aSAndrew Thompson #include <sys/unistd.h> 42ed6d949aSAndrew Thompson #include <sys/callout.h> 43ed6d949aSAndrew Thompson #include <sys/malloc.h> 44ed6d949aSAndrew Thompson #include <sys/priv.h> 45ed6d949aSAndrew Thompson #include <sys/conf.h> 46ed6d949aSAndrew Thompson #include <sys/fcntl.h> 47ed6d949aSAndrew Thompson 4802ac6454SAndrew Thompson #include <dev/usb/usb.h> 4902ac6454SAndrew Thompson #include <dev/usb/usb_ioctl.h> 50ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 51ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 5202ac6454SAndrew Thompson 5302ac6454SAndrew Thompson #define USB_DEBUG_VAR ugen_debug 5402ac6454SAndrew Thompson 5502ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 5602ac6454SAndrew Thompson #include <dev/usb/usb_dev.h> 57ed6d949aSAndrew Thompson #include <dev/usb/usb_mbuf.h> 5802ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 5902ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 6002ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6102ac6454SAndrew Thompson #include <dev/usb/usb_request.h> 6202ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 6302ac6454SAndrew Thompson #include <dev/usb/usb_util.h> 6402ac6454SAndrew Thompson #include <dev/usb/usb_hub.h> 6502ac6454SAndrew Thompson #include <dev/usb/usb_generic.h> 6602ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h> 6702ac6454SAndrew Thompson 6802ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 6902ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 7002ac6454SAndrew Thompson 718755859aSAndrew Thompson #if USB_HAVE_UGEN 728755859aSAndrew Thompson 7302ac6454SAndrew Thompson /* defines */ 7402ac6454SAndrew Thompson 7502ac6454SAndrew Thompson #define UGEN_BULK_FS_BUFFER_SIZE (64*32) /* bytes */ 7602ac6454SAndrew Thompson #define UGEN_BULK_HS_BUFFER_SIZE (1024*32) /* bytes */ 7702ac6454SAndrew Thompson #define UGEN_HW_FRAMES 50 /* number of milliseconds per transfer */ 7802ac6454SAndrew Thompson 7902ac6454SAndrew Thompson /* function prototypes */ 8002ac6454SAndrew Thompson 81e0a69b51SAndrew Thompson static usb_callback_t ugen_read_clear_stall_callback; 82e0a69b51SAndrew Thompson static usb_callback_t ugen_write_clear_stall_callback; 835b3bb704SAndrew Thompson static usb_callback_t ugen_ctrl_read_callback; 845b3bb704SAndrew Thompson static usb_callback_t ugen_ctrl_write_callback; 85e0a69b51SAndrew Thompson static usb_callback_t ugen_isoc_read_callback; 86e0a69b51SAndrew Thompson static usb_callback_t ugen_isoc_write_callback; 875b3bb704SAndrew Thompson static usb_callback_t ugen_ctrl_fs_callback; 8802ac6454SAndrew Thompson 89e0a69b51SAndrew Thompson static usb_fifo_open_t ugen_open; 90e0a69b51SAndrew Thompson static usb_fifo_close_t ugen_close; 91e0a69b51SAndrew Thompson static usb_fifo_ioctl_t ugen_ioctl; 92e0a69b51SAndrew Thompson static usb_fifo_ioctl_t ugen_ioctl_post; 93e0a69b51SAndrew Thompson static usb_fifo_cmd_t ugen_start_read; 94e0a69b51SAndrew Thompson static usb_fifo_cmd_t ugen_start_write; 95e0a69b51SAndrew Thompson static usb_fifo_cmd_t ugen_stop_io; 9602ac6454SAndrew Thompson 97760bc48eSAndrew Thompson static int ugen_transfer_setup(struct usb_fifo *, 98760bc48eSAndrew Thompson const struct usb_config *, uint8_t); 99760bc48eSAndrew Thompson static int ugen_open_pipe_write(struct usb_fifo *); 100760bc48eSAndrew Thompson static int ugen_open_pipe_read(struct usb_fifo *); 101760bc48eSAndrew Thompson static int ugen_set_config(struct usb_fifo *, uint8_t); 102760bc48eSAndrew Thompson static int ugen_set_interface(struct usb_fifo *, uint8_t, uint8_t); 103760bc48eSAndrew Thompson static int ugen_get_cdesc(struct usb_fifo *, struct usb_gen_descriptor *); 104760bc48eSAndrew Thompson static int ugen_get_sdesc(struct usb_fifo *, struct usb_gen_descriptor *); 105760bc48eSAndrew Thompson static int ugen_get_iface_driver(struct usb_fifo *f, struct usb_gen_descriptor *ugd); 106a593f6b8SAndrew Thompson static int usb_gen_fill_deviceinfo(struct usb_fifo *, 107760bc48eSAndrew Thompson struct usb_device_info *); 108760bc48eSAndrew Thompson static int ugen_re_enumerate(struct usb_fifo *); 109760bc48eSAndrew Thompson static int ugen_iface_ioctl(struct usb_fifo *, u_long, void *, int); 110760bc48eSAndrew Thompson static uint8_t ugen_fs_get_complete(struct usb_fifo *, uint8_t *); 111760bc48eSAndrew Thompson static int ugen_fs_uninit(struct usb_fifo *f); 11202ac6454SAndrew Thompson 11302ac6454SAndrew Thompson /* structures */ 11402ac6454SAndrew Thompson 115a593f6b8SAndrew Thompson struct usb_fifo_methods usb_ugen_methods = { 11602ac6454SAndrew Thompson .f_open = &ugen_open, 11702ac6454SAndrew Thompson .f_close = &ugen_close, 11802ac6454SAndrew Thompson .f_ioctl = &ugen_ioctl, 11902ac6454SAndrew Thompson .f_ioctl_post = &ugen_ioctl_post, 12002ac6454SAndrew Thompson .f_start_read = &ugen_start_read, 12102ac6454SAndrew Thompson .f_stop_read = &ugen_stop_io, 12202ac6454SAndrew Thompson .f_start_write = &ugen_start_write, 12302ac6454SAndrew Thompson .f_stop_write = &ugen_stop_io, 12402ac6454SAndrew Thompson }; 12502ac6454SAndrew Thompson 126ed6d949aSAndrew Thompson #ifdef USB_DEBUG 12702ac6454SAndrew Thompson static int ugen_debug = 0; 12802ac6454SAndrew Thompson 1296472ac3dSEd Schouten static SYSCTL_NODE(_hw_usb, OID_AUTO, ugen, CTLFLAG_RW, 0, "USB generic"); 1309360ae40SAndrew Thompson SYSCTL_INT(_hw_usb_ugen, OID_AUTO, debug, CTLFLAG_RW, &ugen_debug, 13102ac6454SAndrew Thompson 0, "Debug level"); 132c13fd8d4SAndrew Thompson 133c13fd8d4SAndrew Thompson TUNABLE_INT("hw.usb.ugen.debug", &ugen_debug); 13402ac6454SAndrew Thompson #endif 13502ac6454SAndrew Thompson 13602ac6454SAndrew Thompson 13702ac6454SAndrew Thompson /* prototypes */ 13802ac6454SAndrew Thompson 13902ac6454SAndrew Thompson static int 140760bc48eSAndrew Thompson ugen_transfer_setup(struct usb_fifo *f, 141760bc48eSAndrew Thompson const struct usb_config *setup, uint8_t n_setup) 14202ac6454SAndrew Thompson { 143ed6d949aSAndrew Thompson struct usb_endpoint *ep = usb_fifo_softc(f); 144760bc48eSAndrew Thompson struct usb_device *udev = f->udev; 145ae60fdfbSAndrew Thompson uint8_t iface_index = ep->iface_index; 14602ac6454SAndrew Thompson int error; 14702ac6454SAndrew Thompson 14802ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 14902ac6454SAndrew Thompson 15002ac6454SAndrew Thompson /* 151a593f6b8SAndrew Thompson * "usbd_transfer_setup()" can sleep so one needs to make a wrapper, 15202ac6454SAndrew Thompson * exiting the mutex and checking things 15302ac6454SAndrew Thompson */ 154a593f6b8SAndrew Thompson error = usbd_transfer_setup(udev, &iface_index, f->xfer, 15502ac6454SAndrew Thompson setup, n_setup, f, f->priv_mtx); 15602ac6454SAndrew Thompson if (error == 0) { 15702ac6454SAndrew Thompson 15802ac6454SAndrew Thompson if (f->xfer[0]->nframes == 1) { 159a593f6b8SAndrew Thompson error = usb_fifo_alloc_buffer(f, 16002ac6454SAndrew Thompson f->xfer[0]->max_data_length, 2); 16102ac6454SAndrew Thompson } else { 162a593f6b8SAndrew Thompson error = usb_fifo_alloc_buffer(f, 16302ac6454SAndrew Thompson f->xfer[0]->max_frame_size, 16402ac6454SAndrew Thompson 2 * f->xfer[0]->nframes); 16502ac6454SAndrew Thompson } 16602ac6454SAndrew Thompson if (error) { 167a593f6b8SAndrew Thompson usbd_transfer_unsetup(f->xfer, n_setup); 16802ac6454SAndrew Thompson } 16902ac6454SAndrew Thompson } 17002ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 17102ac6454SAndrew Thompson 17202ac6454SAndrew Thompson return (error); 17302ac6454SAndrew Thompson } 17402ac6454SAndrew Thompson 17502ac6454SAndrew Thompson static int 176760bc48eSAndrew Thompson ugen_open(struct usb_fifo *f, int fflags) 17702ac6454SAndrew Thompson { 178ed6d949aSAndrew Thompson struct usb_endpoint *ep = usb_fifo_softc(f); 179ae60fdfbSAndrew Thompson struct usb_endpoint_descriptor *ed = ep->edesc; 18002ac6454SAndrew Thompson uint8_t type; 18102ac6454SAndrew Thompson 18202ac6454SAndrew Thompson DPRINTFN(6, "flag=0x%x\n", fflags); 18302ac6454SAndrew Thompson 18402ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 185a593f6b8SAndrew Thompson switch (usbd_get_speed(f->udev)) { 18602ac6454SAndrew Thompson case USB_SPEED_LOW: 18702ac6454SAndrew Thompson case USB_SPEED_FULL: 18802ac6454SAndrew Thompson f->nframes = UGEN_HW_FRAMES; 18902ac6454SAndrew Thompson f->bufsize = UGEN_BULK_FS_BUFFER_SIZE; 19002ac6454SAndrew Thompson break; 19102ac6454SAndrew Thompson default: 19202ac6454SAndrew Thompson f->nframes = UGEN_HW_FRAMES * 8; 19302ac6454SAndrew Thompson f->bufsize = UGEN_BULK_HS_BUFFER_SIZE; 19402ac6454SAndrew Thompson break; 19502ac6454SAndrew Thompson } 19602ac6454SAndrew Thompson 19702ac6454SAndrew Thompson type = ed->bmAttributes & UE_XFERTYPE; 19802ac6454SAndrew Thompson if (type == UE_INTERRUPT) { 19902ac6454SAndrew Thompson f->bufsize = 0; /* use "wMaxPacketSize" */ 20002ac6454SAndrew Thompson } 20102ac6454SAndrew Thompson f->timeout = USB_NO_TIMEOUT; 20202ac6454SAndrew Thompson f->flag_short = 0; 20302ac6454SAndrew Thompson f->fifo_zlp = 0; 20402ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 20502ac6454SAndrew Thompson 20602ac6454SAndrew Thompson return (0); 20702ac6454SAndrew Thompson } 20802ac6454SAndrew Thompson 20902ac6454SAndrew Thompson static void 210760bc48eSAndrew Thompson ugen_close(struct usb_fifo *f, int fflags) 21102ac6454SAndrew Thompson { 21202ac6454SAndrew Thompson DPRINTFN(6, "flag=0x%x\n", fflags); 21302ac6454SAndrew Thompson 21402ac6454SAndrew Thompson /* cleanup */ 21502ac6454SAndrew Thompson 21602ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 217a593f6b8SAndrew Thompson usbd_transfer_stop(f->xfer[0]); 218a593f6b8SAndrew Thompson usbd_transfer_stop(f->xfer[1]); 21902ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 22002ac6454SAndrew Thompson 221a593f6b8SAndrew Thompson usbd_transfer_unsetup(f->xfer, 2); 222a593f6b8SAndrew Thompson usb_fifo_free_buffer(f); 22302ac6454SAndrew Thompson 22402ac6454SAndrew Thompson if (ugen_fs_uninit(f)) { 22502ac6454SAndrew Thompson /* ignore any errors - we are closing */ 22602ac6454SAndrew Thompson DPRINTFN(6, "no FIFOs\n"); 22702ac6454SAndrew Thompson } 22802ac6454SAndrew Thompson } 22902ac6454SAndrew Thompson 23002ac6454SAndrew Thompson static int 231760bc48eSAndrew Thompson ugen_open_pipe_write(struct usb_fifo *f) 23202ac6454SAndrew Thompson { 233760bc48eSAndrew Thompson struct usb_config usb_config[2]; 234ed6d949aSAndrew Thompson struct usb_endpoint *ep = usb_fifo_softc(f); 235ae60fdfbSAndrew Thompson struct usb_endpoint_descriptor *ed = ep->edesc; 23602ac6454SAndrew Thompson 23702ac6454SAndrew Thompson mtx_assert(f->priv_mtx, MA_OWNED); 23802ac6454SAndrew Thompson 23902ac6454SAndrew Thompson if (f->xfer[0] || f->xfer[1]) { 24002ac6454SAndrew Thompson /* transfers are already opened */ 24102ac6454SAndrew Thompson return (0); 24202ac6454SAndrew Thompson } 243271ae033SHans Petter Selasky memset(usb_config, 0, sizeof(usb_config)); 24402ac6454SAndrew Thompson 245760bc48eSAndrew Thompson usb_config[1].type = UE_CONTROL; 246760bc48eSAndrew Thompson usb_config[1].endpoint = 0; 247760bc48eSAndrew Thompson usb_config[1].direction = UE_DIR_ANY; 248760bc48eSAndrew Thompson usb_config[1].timeout = 1000; /* 1 second */ 249760bc48eSAndrew Thompson usb_config[1].interval = 50;/* 50 milliseconds */ 250760bc48eSAndrew Thompson usb_config[1].bufsize = sizeof(struct usb_device_request); 251760bc48eSAndrew Thompson usb_config[1].callback = &ugen_write_clear_stall_callback; 252760bc48eSAndrew Thompson usb_config[1].usb_mode = USB_MODE_HOST; 25302ac6454SAndrew Thompson 254760bc48eSAndrew Thompson usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; 255760bc48eSAndrew Thompson usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; 256a5cf1aaaSHans Petter Selasky usb_config[0].stream_id = 0; /* XXX support more stream ID's */ 257760bc48eSAndrew Thompson usb_config[0].direction = UE_DIR_TX; 258760bc48eSAndrew Thompson usb_config[0].interval = USB_DEFAULT_INTERVAL; 259760bc48eSAndrew Thompson usb_config[0].flags.proxy_buffer = 1; 260760bc48eSAndrew Thompson usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ 26102ac6454SAndrew Thompson 26202ac6454SAndrew Thompson switch (ed->bmAttributes & UE_XFERTYPE) { 26302ac6454SAndrew Thompson case UE_INTERRUPT: 26402ac6454SAndrew Thompson case UE_BULK: 26502ac6454SAndrew Thompson if (f->flag_short) { 266760bc48eSAndrew Thompson usb_config[0].flags.force_short_xfer = 1; 26702ac6454SAndrew Thompson } 2685b3bb704SAndrew Thompson usb_config[0].callback = &ugen_ctrl_write_callback; 269760bc48eSAndrew Thompson usb_config[0].timeout = f->timeout; 270760bc48eSAndrew Thompson usb_config[0].frames = 1; 271760bc48eSAndrew Thompson usb_config[0].bufsize = f->bufsize; 272760bc48eSAndrew Thompson if (ugen_transfer_setup(f, usb_config, 2)) { 27302ac6454SAndrew Thompson return (EIO); 27402ac6454SAndrew Thompson } 27502ac6454SAndrew Thompson /* first transfer does not clear stall */ 27602ac6454SAndrew Thompson f->flag_stall = 0; 27702ac6454SAndrew Thompson break; 27802ac6454SAndrew Thompson 27902ac6454SAndrew Thompson case UE_ISOCHRONOUS: 280760bc48eSAndrew Thompson usb_config[0].flags.short_xfer_ok = 1; 281760bc48eSAndrew Thompson usb_config[0].bufsize = 0; /* use default */ 282760bc48eSAndrew Thompson usb_config[0].frames = f->nframes; 283760bc48eSAndrew Thompson usb_config[0].callback = &ugen_isoc_write_callback; 284760bc48eSAndrew Thompson usb_config[0].timeout = 0; 28502ac6454SAndrew Thompson 28602ac6454SAndrew Thompson /* clone configuration */ 287760bc48eSAndrew Thompson usb_config[1] = usb_config[0]; 28802ac6454SAndrew Thompson 289760bc48eSAndrew Thompson if (ugen_transfer_setup(f, usb_config, 2)) { 29002ac6454SAndrew Thompson return (EIO); 29102ac6454SAndrew Thompson } 29202ac6454SAndrew Thompson break; 29302ac6454SAndrew Thompson default: 29402ac6454SAndrew Thompson return (EINVAL); 29502ac6454SAndrew Thompson } 29602ac6454SAndrew Thompson return (0); 29702ac6454SAndrew Thompson } 29802ac6454SAndrew Thompson 29902ac6454SAndrew Thompson static int 300760bc48eSAndrew Thompson ugen_open_pipe_read(struct usb_fifo *f) 30102ac6454SAndrew Thompson { 302760bc48eSAndrew Thompson struct usb_config usb_config[2]; 303ed6d949aSAndrew Thompson struct usb_endpoint *ep = usb_fifo_softc(f); 304ae60fdfbSAndrew Thompson struct usb_endpoint_descriptor *ed = ep->edesc; 30502ac6454SAndrew Thompson 30602ac6454SAndrew Thompson mtx_assert(f->priv_mtx, MA_OWNED); 30702ac6454SAndrew Thompson 30802ac6454SAndrew Thompson if (f->xfer[0] || f->xfer[1]) { 30902ac6454SAndrew Thompson /* transfers are already opened */ 31002ac6454SAndrew Thompson return (0); 31102ac6454SAndrew Thompson } 312271ae033SHans Petter Selasky memset(usb_config, 0, sizeof(usb_config)); 31302ac6454SAndrew Thompson 314760bc48eSAndrew Thompson usb_config[1].type = UE_CONTROL; 315760bc48eSAndrew Thompson usb_config[1].endpoint = 0; 316760bc48eSAndrew Thompson usb_config[1].direction = UE_DIR_ANY; 317760bc48eSAndrew Thompson usb_config[1].timeout = 1000; /* 1 second */ 318760bc48eSAndrew Thompson usb_config[1].interval = 50;/* 50 milliseconds */ 319760bc48eSAndrew Thompson usb_config[1].bufsize = sizeof(struct usb_device_request); 320760bc48eSAndrew Thompson usb_config[1].callback = &ugen_read_clear_stall_callback; 321760bc48eSAndrew Thompson usb_config[1].usb_mode = USB_MODE_HOST; 32202ac6454SAndrew Thompson 323760bc48eSAndrew Thompson usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; 324760bc48eSAndrew Thompson usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; 325a5cf1aaaSHans Petter Selasky usb_config[0].stream_id = 0; /* XXX support more stream ID's */ 326760bc48eSAndrew Thompson usb_config[0].direction = UE_DIR_RX; 327760bc48eSAndrew Thompson usb_config[0].interval = USB_DEFAULT_INTERVAL; 328760bc48eSAndrew Thompson usb_config[0].flags.proxy_buffer = 1; 329760bc48eSAndrew Thompson usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ 33002ac6454SAndrew Thompson 33102ac6454SAndrew Thompson switch (ed->bmAttributes & UE_XFERTYPE) { 33202ac6454SAndrew Thompson case UE_INTERRUPT: 33302ac6454SAndrew Thompson case UE_BULK: 33402ac6454SAndrew Thompson if (f->flag_short) { 335760bc48eSAndrew Thompson usb_config[0].flags.short_xfer_ok = 1; 33602ac6454SAndrew Thompson } 337760bc48eSAndrew Thompson usb_config[0].timeout = f->timeout; 338760bc48eSAndrew Thompson usb_config[0].frames = 1; 3395b3bb704SAndrew Thompson usb_config[0].callback = &ugen_ctrl_read_callback; 340760bc48eSAndrew Thompson usb_config[0].bufsize = f->bufsize; 34102ac6454SAndrew Thompson 342760bc48eSAndrew Thompson if (ugen_transfer_setup(f, usb_config, 2)) { 34302ac6454SAndrew Thompson return (EIO); 34402ac6454SAndrew Thompson } 34502ac6454SAndrew Thompson /* first transfer does not clear stall */ 34602ac6454SAndrew Thompson f->flag_stall = 0; 34702ac6454SAndrew Thompson break; 34802ac6454SAndrew Thompson 34902ac6454SAndrew Thompson case UE_ISOCHRONOUS: 350760bc48eSAndrew Thompson usb_config[0].flags.short_xfer_ok = 1; 351760bc48eSAndrew Thompson usb_config[0].bufsize = 0; /* use default */ 352760bc48eSAndrew Thompson usb_config[0].frames = f->nframes; 353760bc48eSAndrew Thompson usb_config[0].callback = &ugen_isoc_read_callback; 354760bc48eSAndrew Thompson usb_config[0].timeout = 0; 35502ac6454SAndrew Thompson 35602ac6454SAndrew Thompson /* clone configuration */ 357760bc48eSAndrew Thompson usb_config[1] = usb_config[0]; 35802ac6454SAndrew Thompson 359760bc48eSAndrew Thompson if (ugen_transfer_setup(f, usb_config, 2)) { 36002ac6454SAndrew Thompson return (EIO); 36102ac6454SAndrew Thompson } 36202ac6454SAndrew Thompson break; 36302ac6454SAndrew Thompson 36402ac6454SAndrew Thompson default: 36502ac6454SAndrew Thompson return (EINVAL); 36602ac6454SAndrew Thompson } 36702ac6454SAndrew Thompson return (0); 36802ac6454SAndrew Thompson } 36902ac6454SAndrew Thompson 37002ac6454SAndrew Thompson static void 371760bc48eSAndrew Thompson ugen_start_read(struct usb_fifo *f) 37202ac6454SAndrew Thompson { 37302ac6454SAndrew Thompson /* check that pipes are open */ 37402ac6454SAndrew Thompson if (ugen_open_pipe_read(f)) { 37502ac6454SAndrew Thompson /* signal error */ 376a593f6b8SAndrew Thompson usb_fifo_put_data_error(f); 37702ac6454SAndrew Thompson } 37802ac6454SAndrew Thompson /* start transfers */ 379a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[0]); 380a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[1]); 38102ac6454SAndrew Thompson } 38202ac6454SAndrew Thompson 38302ac6454SAndrew Thompson static void 384760bc48eSAndrew Thompson ugen_start_write(struct usb_fifo *f) 38502ac6454SAndrew Thompson { 38602ac6454SAndrew Thompson /* check that pipes are open */ 38702ac6454SAndrew Thompson if (ugen_open_pipe_write(f)) { 38802ac6454SAndrew Thompson /* signal error */ 389a593f6b8SAndrew Thompson usb_fifo_get_data_error(f); 39002ac6454SAndrew Thompson } 39102ac6454SAndrew Thompson /* start transfers */ 392a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[0]); 393a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[1]); 39402ac6454SAndrew Thompson } 39502ac6454SAndrew Thompson 39602ac6454SAndrew Thompson static void 397760bc48eSAndrew Thompson ugen_stop_io(struct usb_fifo *f) 39802ac6454SAndrew Thompson { 39902ac6454SAndrew Thompson /* stop transfers */ 400a593f6b8SAndrew Thompson usbd_transfer_stop(f->xfer[0]); 401a593f6b8SAndrew Thompson usbd_transfer_stop(f->xfer[1]); 40202ac6454SAndrew Thompson } 40302ac6454SAndrew Thompson 40402ac6454SAndrew Thompson static void 4055b3bb704SAndrew Thompson ugen_ctrl_read_callback(struct usb_xfer *xfer, usb_error_t error) 40602ac6454SAndrew Thompson { 407ed6d949aSAndrew Thompson struct usb_fifo *f = usbd_xfer_softc(xfer); 408760bc48eSAndrew Thompson struct usb_mbuf *m; 40902ac6454SAndrew Thompson 41002ac6454SAndrew Thompson DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); 41102ac6454SAndrew Thompson 41202ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 41302ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 41402ac6454SAndrew Thompson if (xfer->actlen == 0) { 41502ac6454SAndrew Thompson if (f->fifo_zlp != 4) { 41602ac6454SAndrew Thompson f->fifo_zlp++; 41702ac6454SAndrew Thompson } else { 41802ac6454SAndrew Thompson /* 41902ac6454SAndrew Thompson * Throttle a little bit we have multiple ZLPs 42002ac6454SAndrew Thompson * in a row! 42102ac6454SAndrew Thompson */ 42202ac6454SAndrew Thompson xfer->interval = 64; /* ms */ 42302ac6454SAndrew Thompson } 42402ac6454SAndrew Thompson } else { 42502ac6454SAndrew Thompson /* clear throttle */ 42602ac6454SAndrew Thompson xfer->interval = 0; 42702ac6454SAndrew Thompson f->fifo_zlp = 0; 42802ac6454SAndrew Thompson } 429a593f6b8SAndrew Thompson usb_fifo_put_data(f, xfer->frbuffers, 0, 43002ac6454SAndrew Thompson xfer->actlen, 1); 43102ac6454SAndrew Thompson 43202ac6454SAndrew Thompson case USB_ST_SETUP: 43302ac6454SAndrew Thompson if (f->flag_stall) { 434a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[1]); 43502ac6454SAndrew Thompson break; 43602ac6454SAndrew Thompson } 43702ac6454SAndrew Thompson USB_IF_POLL(&f->free_q, m); 43802ac6454SAndrew Thompson if (m) { 439ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 440a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 44102ac6454SAndrew Thompson } 44202ac6454SAndrew Thompson break; 44302ac6454SAndrew Thompson 44402ac6454SAndrew Thompson default: /* Error */ 44502ac6454SAndrew Thompson if (xfer->error != USB_ERR_CANCELLED) { 44601cf7831SAndrew Thompson /* send a zero length packet to userland */ 447a593f6b8SAndrew Thompson usb_fifo_put_data(f, xfer->frbuffers, 0, 0, 1); 44802ac6454SAndrew Thompson f->flag_stall = 1; 44902ac6454SAndrew Thompson f->fifo_zlp = 0; 450a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[1]); 45102ac6454SAndrew Thompson } 45202ac6454SAndrew Thompson break; 45302ac6454SAndrew Thompson } 45402ac6454SAndrew Thompson } 45502ac6454SAndrew Thompson 45602ac6454SAndrew Thompson static void 4575b3bb704SAndrew Thompson ugen_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) 45802ac6454SAndrew Thompson { 459ed6d949aSAndrew Thompson struct usb_fifo *f = usbd_xfer_softc(xfer); 460e0a69b51SAndrew Thompson usb_frlength_t actlen; 46102ac6454SAndrew Thompson 46202ac6454SAndrew Thompson DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); 46302ac6454SAndrew Thompson 46402ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 46502ac6454SAndrew Thompson case USB_ST_SETUP: 46602ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 46702ac6454SAndrew Thompson /* 46802ac6454SAndrew Thompson * If writing is in stall, just jump to clear stall 46902ac6454SAndrew Thompson * callback and solve the situation. 47002ac6454SAndrew Thompson */ 47102ac6454SAndrew Thompson if (f->flag_stall) { 472a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[1]); 47302ac6454SAndrew Thompson break; 47402ac6454SAndrew Thompson } 47502ac6454SAndrew Thompson /* 47602ac6454SAndrew Thompson * Write data, setup and perform hardware transfer. 47702ac6454SAndrew Thompson */ 478a593f6b8SAndrew Thompson if (usb_fifo_get_data(f, xfer->frbuffers, 0, 47902ac6454SAndrew Thompson xfer->max_data_length, &actlen, 0)) { 480ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, actlen); 481a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 48202ac6454SAndrew Thompson } 48302ac6454SAndrew Thompson break; 48402ac6454SAndrew Thompson 48502ac6454SAndrew Thompson default: /* Error */ 48602ac6454SAndrew Thompson if (xfer->error != USB_ERR_CANCELLED) { 48702ac6454SAndrew Thompson f->flag_stall = 1; 488a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[1]); 48902ac6454SAndrew Thompson } 49002ac6454SAndrew Thompson break; 49102ac6454SAndrew Thompson } 49202ac6454SAndrew Thompson } 49302ac6454SAndrew Thompson 49402ac6454SAndrew Thompson static void 495ed6d949aSAndrew Thompson ugen_read_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 49602ac6454SAndrew Thompson { 497ed6d949aSAndrew Thompson struct usb_fifo *f = usbd_xfer_softc(xfer); 498760bc48eSAndrew Thompson struct usb_xfer *xfer_other = f->xfer[0]; 49902ac6454SAndrew Thompson 50002ac6454SAndrew Thompson if (f->flag_stall == 0) { 50102ac6454SAndrew Thompson /* nothing to do */ 50202ac6454SAndrew Thompson return; 50302ac6454SAndrew Thompson } 504a593f6b8SAndrew Thompson if (usbd_clear_stall_callback(xfer, xfer_other)) { 50502ac6454SAndrew Thompson DPRINTFN(5, "f=%p: stall cleared\n", f); 50602ac6454SAndrew Thompson f->flag_stall = 0; 507a593f6b8SAndrew Thompson usbd_transfer_start(xfer_other); 50802ac6454SAndrew Thompson } 50902ac6454SAndrew Thompson } 51002ac6454SAndrew Thompson 51102ac6454SAndrew Thompson static void 512ed6d949aSAndrew Thompson ugen_write_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 51302ac6454SAndrew Thompson { 514ed6d949aSAndrew Thompson struct usb_fifo *f = usbd_xfer_softc(xfer); 515760bc48eSAndrew Thompson struct usb_xfer *xfer_other = f->xfer[0]; 51602ac6454SAndrew Thompson 51702ac6454SAndrew Thompson if (f->flag_stall == 0) { 51802ac6454SAndrew Thompson /* nothing to do */ 51902ac6454SAndrew Thompson return; 52002ac6454SAndrew Thompson } 521a593f6b8SAndrew Thompson if (usbd_clear_stall_callback(xfer, xfer_other)) { 52202ac6454SAndrew Thompson DPRINTFN(5, "f=%p: stall cleared\n", f); 52302ac6454SAndrew Thompson f->flag_stall = 0; 524a593f6b8SAndrew Thompson usbd_transfer_start(xfer_other); 52502ac6454SAndrew Thompson } 52602ac6454SAndrew Thompson } 52702ac6454SAndrew Thompson 52802ac6454SAndrew Thompson static void 529ed6d949aSAndrew Thompson ugen_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error) 53002ac6454SAndrew Thompson { 531ed6d949aSAndrew Thompson struct usb_fifo *f = usbd_xfer_softc(xfer); 532e0a69b51SAndrew Thompson usb_frlength_t offset; 533e0a69b51SAndrew Thompson usb_frcount_t n; 53402ac6454SAndrew Thompson 53502ac6454SAndrew Thompson DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); 53602ac6454SAndrew Thompson 53702ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 53802ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 53902ac6454SAndrew Thompson 54002ac6454SAndrew Thompson DPRINTFN(6, "actlen=%d\n", xfer->actlen); 54102ac6454SAndrew Thompson 54202ac6454SAndrew Thompson offset = 0; 54302ac6454SAndrew Thompson 54402ac6454SAndrew Thompson for (n = 0; n != xfer->aframes; n++) { 545a593f6b8SAndrew Thompson usb_fifo_put_data(f, xfer->frbuffers, offset, 54602ac6454SAndrew Thompson xfer->frlengths[n], 1); 54702ac6454SAndrew Thompson offset += xfer->max_frame_size; 54802ac6454SAndrew Thompson } 54902ac6454SAndrew Thompson 55002ac6454SAndrew Thompson case USB_ST_SETUP: 55102ac6454SAndrew Thompson tr_setup: 55202ac6454SAndrew Thompson for (n = 0; n != xfer->nframes; n++) { 55302ac6454SAndrew Thompson /* setup size for next transfer */ 554ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, xfer->max_frame_size); 55502ac6454SAndrew Thompson } 556a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 55702ac6454SAndrew Thompson break; 55802ac6454SAndrew Thompson 55902ac6454SAndrew Thompson default: /* Error */ 56002ac6454SAndrew Thompson if (xfer->error == USB_ERR_CANCELLED) { 56102ac6454SAndrew Thompson break; 56202ac6454SAndrew Thompson } 56302ac6454SAndrew Thompson goto tr_setup; 56402ac6454SAndrew Thompson } 56502ac6454SAndrew Thompson } 56602ac6454SAndrew Thompson 56702ac6454SAndrew Thompson static void 568ed6d949aSAndrew Thompson ugen_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error) 56902ac6454SAndrew Thompson { 570ed6d949aSAndrew Thompson struct usb_fifo *f = usbd_xfer_softc(xfer); 571e0a69b51SAndrew Thompson usb_frlength_t actlen; 572e0a69b51SAndrew Thompson usb_frlength_t offset; 573e0a69b51SAndrew Thompson usb_frcount_t n; 57402ac6454SAndrew Thompson 57502ac6454SAndrew Thompson DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); 57602ac6454SAndrew Thompson 57702ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 57802ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 57902ac6454SAndrew Thompson case USB_ST_SETUP: 58002ac6454SAndrew Thompson tr_setup: 58102ac6454SAndrew Thompson offset = 0; 58202ac6454SAndrew Thompson for (n = 0; n != xfer->nframes; n++) { 583a593f6b8SAndrew Thompson if (usb_fifo_get_data(f, xfer->frbuffers, offset, 58402ac6454SAndrew Thompson xfer->max_frame_size, &actlen, 1)) { 585ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, actlen); 58602ac6454SAndrew Thompson offset += actlen; 58702ac6454SAndrew Thompson } else { 58802ac6454SAndrew Thompson break; 58902ac6454SAndrew Thompson } 59002ac6454SAndrew Thompson } 59102ac6454SAndrew Thompson 59202ac6454SAndrew Thompson for (; n != xfer->nframes; n++) { 59302ac6454SAndrew Thompson /* fill in zero frames */ 594ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, 0); 59502ac6454SAndrew Thompson } 596a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 59702ac6454SAndrew Thompson break; 59802ac6454SAndrew Thompson 59902ac6454SAndrew Thompson default: /* Error */ 60002ac6454SAndrew Thompson if (xfer->error == USB_ERR_CANCELLED) { 60102ac6454SAndrew Thompson break; 60202ac6454SAndrew Thompson } 60302ac6454SAndrew Thompson goto tr_setup; 60402ac6454SAndrew Thompson } 60502ac6454SAndrew Thompson } 60602ac6454SAndrew Thompson 60702ac6454SAndrew Thompson static int 608760bc48eSAndrew Thompson ugen_set_config(struct usb_fifo *f, uint8_t index) 60902ac6454SAndrew Thompson { 61002ac6454SAndrew Thompson DPRINTFN(2, "index %u\n", index); 61102ac6454SAndrew Thompson 612f29a0724SAndrew Thompson if (f->udev->flags.usb_mode != USB_MODE_HOST) { 61302ac6454SAndrew Thompson /* not possible in device side mode */ 61402ac6454SAndrew Thompson return (ENOTTY); 61502ac6454SAndrew Thompson } 61602ac6454SAndrew Thompson if (f->udev->curr_config_index == index) { 61702ac6454SAndrew Thompson /* no change needed */ 61802ac6454SAndrew Thompson return (0); 61902ac6454SAndrew Thompson } 62002ac6454SAndrew Thompson /* make sure all FIFO's are gone */ 62102ac6454SAndrew Thompson /* else there can be a deadlock */ 62202ac6454SAndrew Thompson if (ugen_fs_uninit(f)) { 62302ac6454SAndrew Thompson /* ignore any errors */ 62402ac6454SAndrew Thompson DPRINTFN(6, "no FIFOs\n"); 62502ac6454SAndrew Thompson } 62602ac6454SAndrew Thompson /* change setting - will free generic FIFOs, if any */ 627a593f6b8SAndrew Thompson if (usbd_set_config_index(f->udev, index)) { 62802ac6454SAndrew Thompson return (EIO); 62902ac6454SAndrew Thompson } 63002ac6454SAndrew Thompson /* probe and attach */ 631a593f6b8SAndrew Thompson if (usb_probe_and_attach(f->udev, USB_IFACE_INDEX_ANY)) { 63202ac6454SAndrew Thompson return (EIO); 63302ac6454SAndrew Thompson } 63402ac6454SAndrew Thompson return (0); 63502ac6454SAndrew Thompson } 63602ac6454SAndrew Thompson 63702ac6454SAndrew Thompson static int 638760bc48eSAndrew Thompson ugen_set_interface(struct usb_fifo *f, 63902ac6454SAndrew Thompson uint8_t iface_index, uint8_t alt_index) 64002ac6454SAndrew Thompson { 64102ac6454SAndrew Thompson DPRINTFN(2, "%u, %u\n", iface_index, alt_index); 64202ac6454SAndrew Thompson 643f29a0724SAndrew Thompson if (f->udev->flags.usb_mode != USB_MODE_HOST) { 64402ac6454SAndrew Thompson /* not possible in device side mode */ 64502ac6454SAndrew Thompson return (ENOTTY); 64602ac6454SAndrew Thompson } 64702ac6454SAndrew Thompson /* make sure all FIFO's are gone */ 64802ac6454SAndrew Thompson /* else there can be a deadlock */ 64902ac6454SAndrew Thompson if (ugen_fs_uninit(f)) { 65002ac6454SAndrew Thompson /* ignore any errors */ 65102ac6454SAndrew Thompson DPRINTFN(6, "no FIFOs\n"); 65202ac6454SAndrew Thompson } 65302ac6454SAndrew Thompson /* change setting - will free generic FIFOs, if any */ 654a593f6b8SAndrew Thompson if (usbd_set_alt_interface_index(f->udev, iface_index, alt_index)) { 65502ac6454SAndrew Thompson return (EIO); 65602ac6454SAndrew Thompson } 65702ac6454SAndrew Thompson /* probe and attach */ 658a593f6b8SAndrew Thompson if (usb_probe_and_attach(f->udev, iface_index)) { 65902ac6454SAndrew Thompson return (EIO); 66002ac6454SAndrew Thompson } 66102ac6454SAndrew Thompson return (0); 66202ac6454SAndrew Thompson } 66302ac6454SAndrew Thompson 66402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 66502ac6454SAndrew Thompson * ugen_get_cdesc 66602ac6454SAndrew Thompson * 66702ac6454SAndrew Thompson * This function will retrieve the complete configuration descriptor 66802ac6454SAndrew Thompson * at the given index. 66902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 67002ac6454SAndrew Thompson static int 671760bc48eSAndrew Thompson ugen_get_cdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd) 67202ac6454SAndrew Thompson { 673760bc48eSAndrew Thompson struct usb_config_descriptor *cdesc; 674760bc48eSAndrew Thompson struct usb_device *udev = f->udev; 67502ac6454SAndrew Thompson int error; 67602ac6454SAndrew Thompson uint16_t len; 67702ac6454SAndrew Thompson uint8_t free_data; 67802ac6454SAndrew Thompson 67902ac6454SAndrew Thompson DPRINTFN(6, "\n"); 68002ac6454SAndrew Thompson 68102ac6454SAndrew Thompson if (ugd->ugd_data == NULL) { 68202ac6454SAndrew Thompson /* userland pointer should not be zero */ 68302ac6454SAndrew Thompson return (EINVAL); 68402ac6454SAndrew Thompson } 68502ac6454SAndrew Thompson if ((ugd->ugd_config_index == USB_UNCONFIG_INDEX) || 68602ac6454SAndrew Thompson (ugd->ugd_config_index == udev->curr_config_index)) { 687a593f6b8SAndrew Thompson cdesc = usbd_get_config_descriptor(udev); 68802ac6454SAndrew Thompson if (cdesc == NULL) { 68902ac6454SAndrew Thompson return (ENXIO); 69002ac6454SAndrew Thompson } 69102ac6454SAndrew Thompson free_data = 0; 69202ac6454SAndrew Thompson 69302ac6454SAndrew Thompson } else { 694a593f6b8SAndrew Thompson if (usbd_req_get_config_desc_full(udev, 695e2805033SAndrew Thompson NULL, &cdesc, M_USBDEV, 69602ac6454SAndrew Thompson ugd->ugd_config_index)) { 69702ac6454SAndrew Thompson return (ENXIO); 69802ac6454SAndrew Thompson } 69902ac6454SAndrew Thompson free_data = 1; 70002ac6454SAndrew Thompson } 70102ac6454SAndrew Thompson 70202ac6454SAndrew Thompson len = UGETW(cdesc->wTotalLength); 70302ac6454SAndrew Thompson if (len > ugd->ugd_maxlen) { 70402ac6454SAndrew Thompson len = ugd->ugd_maxlen; 70502ac6454SAndrew Thompson } 70602ac6454SAndrew Thompson DPRINTFN(6, "len=%u\n", len); 70702ac6454SAndrew Thompson 70802ac6454SAndrew Thompson ugd->ugd_actlen = len; 70902ac6454SAndrew Thompson ugd->ugd_offset = 0; 71002ac6454SAndrew Thompson 71102ac6454SAndrew Thompson error = copyout(cdesc, ugd->ugd_data, len); 71202ac6454SAndrew Thompson 71302ac6454SAndrew Thompson if (free_data) { 71402ac6454SAndrew Thompson free(cdesc, M_USBDEV); 71502ac6454SAndrew Thompson } 71602ac6454SAndrew Thompson return (error); 71702ac6454SAndrew Thompson } 71802ac6454SAndrew Thompson 71902ac6454SAndrew Thompson static int 720760bc48eSAndrew Thompson ugen_get_sdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd) 72102ac6454SAndrew Thompson { 72202ac6454SAndrew Thompson void *ptr = f->udev->bus->scratch[0].data; 72302ac6454SAndrew Thompson uint16_t size = sizeof(f->udev->bus->scratch[0].data); 72402ac6454SAndrew Thompson int error; 72502ac6454SAndrew Thompson 726a593f6b8SAndrew Thompson if (usbd_req_get_string_desc(f->udev, NULL, ptr, 72702ac6454SAndrew Thompson size, ugd->ugd_lang_id, ugd->ugd_string_index)) { 72802ac6454SAndrew Thompson error = EINVAL; 72902ac6454SAndrew Thompson } else { 73002ac6454SAndrew Thompson 73102ac6454SAndrew Thompson if (size > ((uint8_t *)ptr)[0]) { 73202ac6454SAndrew Thompson size = ((uint8_t *)ptr)[0]; 73302ac6454SAndrew Thompson } 73402ac6454SAndrew Thompson if (size > ugd->ugd_maxlen) { 73502ac6454SAndrew Thompson size = ugd->ugd_maxlen; 73602ac6454SAndrew Thompson } 73702ac6454SAndrew Thompson ugd->ugd_actlen = size; 73802ac6454SAndrew Thompson ugd->ugd_offset = 0; 73902ac6454SAndrew Thompson 74002ac6454SAndrew Thompson error = copyout(ptr, ugd->ugd_data, size); 74102ac6454SAndrew Thompson } 74202ac6454SAndrew Thompson return (error); 74302ac6454SAndrew Thompson } 74402ac6454SAndrew Thompson 74502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 74602ac6454SAndrew Thompson * ugen_get_iface_driver 74702ac6454SAndrew Thompson * 74802ac6454SAndrew Thompson * This function generates an USB interface description for userland. 74902ac6454SAndrew Thompson * 75002ac6454SAndrew Thompson * Returns: 75102ac6454SAndrew Thompson * 0: Success 75202ac6454SAndrew Thompson * Else: Failure 75302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 75402ac6454SAndrew Thompson static int 755760bc48eSAndrew Thompson ugen_get_iface_driver(struct usb_fifo *f, struct usb_gen_descriptor *ugd) 75602ac6454SAndrew Thompson { 757760bc48eSAndrew Thompson struct usb_device *udev = f->udev; 758760bc48eSAndrew Thompson struct usb_interface *iface; 75902ac6454SAndrew Thompson const char *ptr; 76002ac6454SAndrew Thompson const char *desc; 76102ac6454SAndrew Thompson unsigned int len; 76202ac6454SAndrew Thompson unsigned int maxlen; 76302ac6454SAndrew Thompson char buf[128]; 76402ac6454SAndrew Thompson int error; 76502ac6454SAndrew Thompson 76602ac6454SAndrew Thompson DPRINTFN(6, "\n"); 76702ac6454SAndrew Thompson 76802ac6454SAndrew Thompson if ((ugd->ugd_data == NULL) || (ugd->ugd_maxlen == 0)) { 76902ac6454SAndrew Thompson /* userland pointer should not be zero */ 77002ac6454SAndrew Thompson return (EINVAL); 77102ac6454SAndrew Thompson } 77202ac6454SAndrew Thompson 773a593f6b8SAndrew Thompson iface = usbd_get_iface(udev, ugd->ugd_iface_index); 77402ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 77502ac6454SAndrew Thompson /* invalid interface index */ 77602ac6454SAndrew Thompson return (EINVAL); 77702ac6454SAndrew Thompson } 77802ac6454SAndrew Thompson 77902ac6454SAndrew Thompson /* read out device nameunit string, if any */ 78002ac6454SAndrew Thompson if ((iface->subdev != NULL) && 78102ac6454SAndrew Thompson device_is_attached(iface->subdev) && 78202ac6454SAndrew Thompson (ptr = device_get_nameunit(iface->subdev)) && 78302ac6454SAndrew Thompson (desc = device_get_desc(iface->subdev))) { 78402ac6454SAndrew Thompson 78502ac6454SAndrew Thompson /* print description */ 78602ac6454SAndrew Thompson snprintf(buf, sizeof(buf), "%s: <%s>", ptr, desc); 78702ac6454SAndrew Thompson 78802ac6454SAndrew Thompson /* range checks */ 78902ac6454SAndrew Thompson maxlen = ugd->ugd_maxlen - 1; 79002ac6454SAndrew Thompson len = strlen(buf); 79102ac6454SAndrew Thompson if (len > maxlen) 79202ac6454SAndrew Thompson len = maxlen; 79302ac6454SAndrew Thompson 79402ac6454SAndrew Thompson /* update actual length, including terminating zero */ 79502ac6454SAndrew Thompson ugd->ugd_actlen = len + 1; 79602ac6454SAndrew Thompson 79702ac6454SAndrew Thompson /* copy out interface description */ 79802ac6454SAndrew Thompson error = copyout(buf, ugd->ugd_data, ugd->ugd_actlen); 79902ac6454SAndrew Thompson } else { 80002ac6454SAndrew Thompson /* zero length string is default */ 80102ac6454SAndrew Thompson error = copyout("", ugd->ugd_data, 1); 80202ac6454SAndrew Thompson } 80302ac6454SAndrew Thompson return (error); 80402ac6454SAndrew Thompson } 80502ac6454SAndrew Thompson 80602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 807a593f6b8SAndrew Thompson * usb_gen_fill_deviceinfo 80802ac6454SAndrew Thompson * 80902ac6454SAndrew Thompson * This function dumps information about an USB device to the 81002ac6454SAndrew Thompson * structure pointed to by the "di" argument. 81102ac6454SAndrew Thompson * 81202ac6454SAndrew Thompson * Returns: 81302ac6454SAndrew Thompson * 0: Success 81402ac6454SAndrew Thompson * Else: Failure 81502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 81602ac6454SAndrew Thompson static int 817a593f6b8SAndrew Thompson usb_gen_fill_deviceinfo(struct usb_fifo *f, struct usb_device_info *di) 81802ac6454SAndrew Thompson { 819760bc48eSAndrew Thompson struct usb_device *udev; 820760bc48eSAndrew Thompson struct usb_device *hub; 82102ac6454SAndrew Thompson 82202ac6454SAndrew Thompson udev = f->udev; 82302ac6454SAndrew Thompson 82402ac6454SAndrew Thompson bzero(di, sizeof(di[0])); 82502ac6454SAndrew Thompson 82602ac6454SAndrew Thompson di->udi_bus = device_get_unit(udev->bus->bdev); 82702ac6454SAndrew Thompson di->udi_addr = udev->address; 82802ac6454SAndrew Thompson di->udi_index = udev->device_index; 829ae538d85SAndrew Thompson strlcpy(di->udi_serial, usb_get_serial(udev), sizeof(di->udi_serial)); 830ae538d85SAndrew Thompson strlcpy(di->udi_vendor, usb_get_manufacturer(udev), sizeof(di->udi_vendor)); 831ae538d85SAndrew Thompson strlcpy(di->udi_product, usb_get_product(udev), sizeof(di->udi_product)); 832a593f6b8SAndrew Thompson usb_printbcd(di->udi_release, sizeof(di->udi_release), 83302ac6454SAndrew Thompson UGETW(udev->ddesc.bcdDevice)); 83402ac6454SAndrew Thompson di->udi_vendorNo = UGETW(udev->ddesc.idVendor); 83502ac6454SAndrew Thompson di->udi_productNo = UGETW(udev->ddesc.idProduct); 83602ac6454SAndrew Thompson di->udi_releaseNo = UGETW(udev->ddesc.bcdDevice); 83702ac6454SAndrew Thompson di->udi_class = udev->ddesc.bDeviceClass; 83802ac6454SAndrew Thompson di->udi_subclass = udev->ddesc.bDeviceSubClass; 83902ac6454SAndrew Thompson di->udi_protocol = udev->ddesc.bDeviceProtocol; 84002ac6454SAndrew Thompson di->udi_config_no = udev->curr_config_no; 84102ac6454SAndrew Thompson di->udi_config_index = udev->curr_config_index; 84202ac6454SAndrew Thompson di->udi_power = udev->flags.self_powered ? 0 : udev->power; 84302ac6454SAndrew Thompson di->udi_speed = udev->speed; 844f29a0724SAndrew Thompson di->udi_mode = udev->flags.usb_mode; 84502ac6454SAndrew Thompson di->udi_power_mode = udev->power_mode; 846ec8f3127SAndrew Thompson di->udi_suspended = udev->flags.peer_suspended; 84702ac6454SAndrew Thompson 84802ac6454SAndrew Thompson hub = udev->parent_hub; 84902ac6454SAndrew Thompson if (hub) { 85002ac6454SAndrew Thompson di->udi_hubaddr = hub->address; 85102ac6454SAndrew Thompson di->udi_hubindex = hub->device_index; 85202ac6454SAndrew Thompson di->udi_hubport = udev->port_no; 85302ac6454SAndrew Thompson } 85402ac6454SAndrew Thompson return (0); 85502ac6454SAndrew Thompson } 85602ac6454SAndrew Thompson 85702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 85802ac6454SAndrew Thompson * ugen_check_request 85902ac6454SAndrew Thompson * 86002ac6454SAndrew Thompson * Return values: 86102ac6454SAndrew Thompson * 0: Access allowed 86202ac6454SAndrew Thompson * Else: No access 86302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 86402ac6454SAndrew Thompson static int 865760bc48eSAndrew Thompson ugen_check_request(struct usb_device *udev, struct usb_device_request *req) 86602ac6454SAndrew Thompson { 867ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 86802ac6454SAndrew Thompson int error; 86902ac6454SAndrew Thompson 87002ac6454SAndrew Thompson /* 87102ac6454SAndrew Thompson * Avoid requests that would damage the bus integrity: 87202ac6454SAndrew Thompson */ 87302ac6454SAndrew Thompson if (((req->bmRequestType == UT_WRITE_DEVICE) && 87402ac6454SAndrew Thompson (req->bRequest == UR_SET_ADDRESS)) || 87502ac6454SAndrew Thompson ((req->bmRequestType == UT_WRITE_DEVICE) && 87602ac6454SAndrew Thompson (req->bRequest == UR_SET_CONFIG)) || 87702ac6454SAndrew Thompson ((req->bmRequestType == UT_WRITE_INTERFACE) && 87802ac6454SAndrew Thompson (req->bRequest == UR_SET_INTERFACE))) { 87902ac6454SAndrew Thompson /* 88002ac6454SAndrew Thompson * These requests can be useful for testing USB drivers. 88102ac6454SAndrew Thompson */ 88202ac6454SAndrew Thompson error = priv_check(curthread, PRIV_DRIVER); 88302ac6454SAndrew Thompson if (error) { 88402ac6454SAndrew Thompson return (error); 88502ac6454SAndrew Thompson } 88602ac6454SAndrew Thompson } 88702ac6454SAndrew Thompson /* 88802ac6454SAndrew Thompson * Special case - handle clearing of stall 88902ac6454SAndrew Thompson */ 89002ac6454SAndrew Thompson if (req->bmRequestType == UT_WRITE_ENDPOINT) { 89102ac6454SAndrew Thompson 892a593f6b8SAndrew Thompson ep = usbd_get_ep_by_addr(udev, req->wIndex[0]); 893ae60fdfbSAndrew Thompson if (ep == NULL) { 89402ac6454SAndrew Thompson return (EINVAL); 89502ac6454SAndrew Thompson } 89602ac6454SAndrew Thompson if ((req->bRequest == UR_CLEAR_FEATURE) && 89702ac6454SAndrew Thompson (UGETW(req->wValue) == UF_ENDPOINT_HALT)) { 898a593f6b8SAndrew Thompson usbd_clear_data_toggle(udev, ep); 89902ac6454SAndrew Thompson } 90002ac6454SAndrew Thompson } 90102ac6454SAndrew Thompson /* TODO: add more checks to verify the interface index */ 90202ac6454SAndrew Thompson 90302ac6454SAndrew Thompson return (0); 90402ac6454SAndrew Thompson } 90502ac6454SAndrew Thompson 90602ac6454SAndrew Thompson int 907760bc48eSAndrew Thompson ugen_do_request(struct usb_fifo *f, struct usb_ctl_request *ur) 90802ac6454SAndrew Thompson { 90902ac6454SAndrew Thompson int error; 91002ac6454SAndrew Thompson uint16_t len; 91102ac6454SAndrew Thompson uint16_t actlen; 91202ac6454SAndrew Thompson 91302ac6454SAndrew Thompson if (ugen_check_request(f->udev, &ur->ucr_request)) { 91402ac6454SAndrew Thompson return (EPERM); 91502ac6454SAndrew Thompson } 91602ac6454SAndrew Thompson len = UGETW(ur->ucr_request.wLength); 91702ac6454SAndrew Thompson 91802ac6454SAndrew Thompson /* check if "ucr_data" is valid */ 91902ac6454SAndrew Thompson if (len != 0) { 92002ac6454SAndrew Thompson if (ur->ucr_data == NULL) { 92102ac6454SAndrew Thompson return (EFAULT); 92202ac6454SAndrew Thompson } 92302ac6454SAndrew Thompson } 92402ac6454SAndrew Thompson /* do the USB request */ 925a593f6b8SAndrew Thompson error = usbd_do_request_flags 92602ac6454SAndrew Thompson (f->udev, NULL, &ur->ucr_request, ur->ucr_data, 92702ac6454SAndrew Thompson (ur->ucr_flags & USB_SHORT_XFER_OK) | 92802ac6454SAndrew Thompson USB_USER_DATA_PTR, &actlen, 92902ac6454SAndrew Thompson USB_DEFAULT_TIMEOUT); 93002ac6454SAndrew Thompson 93102ac6454SAndrew Thompson ur->ucr_actlen = actlen; 93202ac6454SAndrew Thompson 93302ac6454SAndrew Thompson if (error) { 93402ac6454SAndrew Thompson error = EIO; 93502ac6454SAndrew Thompson } 93602ac6454SAndrew Thompson return (error); 93702ac6454SAndrew Thompson } 93802ac6454SAndrew Thompson 93902ac6454SAndrew Thompson /*------------------------------------------------------------------------ 94002ac6454SAndrew Thompson * ugen_re_enumerate 94102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 94202ac6454SAndrew Thompson static int 943760bc48eSAndrew Thompson ugen_re_enumerate(struct usb_fifo *f) 94402ac6454SAndrew Thompson { 945760bc48eSAndrew Thompson struct usb_device *udev = f->udev; 94602ac6454SAndrew Thompson int error; 94702ac6454SAndrew Thompson 94802ac6454SAndrew Thompson /* 94902ac6454SAndrew Thompson * This request can be useful for testing USB drivers: 95002ac6454SAndrew Thompson */ 95102ac6454SAndrew Thompson error = priv_check(curthread, PRIV_DRIVER); 95202ac6454SAndrew Thompson if (error) { 95302ac6454SAndrew Thompson return (error); 95402ac6454SAndrew Thompson } 9558f9750b7SHans Petter Selasky if (udev->flags.usb_mode != USB_MODE_HOST) { 9568f9750b7SHans Petter Selasky /* not possible in device side mode */ 95756b57046SHans Petter Selasky DPRINTFN(6, "device mode\n"); 9588f9750b7SHans Petter Selasky return (ENOTTY); 95902ac6454SAndrew Thompson } 96056b57046SHans Petter Selasky if (udev->parent_hub == NULL) { 96156b57046SHans Petter Selasky /* the root HUB cannot be re-enumerated */ 96256b57046SHans Petter Selasky DPRINTFN(6, "cannot reset root HUB\n"); 96356b57046SHans Petter Selasky return (EINVAL); 96456b57046SHans Petter Selasky } 9658f9750b7SHans Petter Selasky /* make sure all FIFO's are gone */ 9668f9750b7SHans Petter Selasky /* else there can be a deadlock */ 9678f9750b7SHans Petter Selasky if (ugen_fs_uninit(f)) { 9688f9750b7SHans Petter Selasky /* ignore any errors */ 9698f9750b7SHans Petter Selasky DPRINTFN(6, "no FIFOs\n"); 97002ac6454SAndrew Thompson } 9719eb0d702SHans Petter Selasky /* start re-enumeration of device */ 9729eb0d702SHans Petter Selasky usbd_start_re_enumerate(udev); 97302ac6454SAndrew Thompson return (0); 97402ac6454SAndrew Thompson } 97502ac6454SAndrew Thompson 97602ac6454SAndrew Thompson int 977760bc48eSAndrew Thompson ugen_fs_uninit(struct usb_fifo *f) 97802ac6454SAndrew Thompson { 97902ac6454SAndrew Thompson if (f->fs_xfer == NULL) { 98002ac6454SAndrew Thompson return (EINVAL); 98102ac6454SAndrew Thompson } 982a593f6b8SAndrew Thompson usbd_transfer_unsetup(f->fs_xfer, f->fs_ep_max); 98302ac6454SAndrew Thompson free(f->fs_xfer, M_USB); 98402ac6454SAndrew Thompson f->fs_xfer = NULL; 98502ac6454SAndrew Thompson f->fs_ep_max = 0; 98602ac6454SAndrew Thompson f->fs_ep_ptr = NULL; 98702ac6454SAndrew Thompson f->flag_iscomplete = 0; 988a593f6b8SAndrew Thompson usb_fifo_free_buffer(f); 98902ac6454SAndrew Thompson return (0); 99002ac6454SAndrew Thompson } 99102ac6454SAndrew Thompson 99202ac6454SAndrew Thompson static uint8_t 993760bc48eSAndrew Thompson ugen_fs_get_complete(struct usb_fifo *f, uint8_t *pindex) 99402ac6454SAndrew Thompson { 995760bc48eSAndrew Thompson struct usb_mbuf *m; 99602ac6454SAndrew Thompson 99702ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 99802ac6454SAndrew Thompson 99902ac6454SAndrew Thompson if (m) { 100002ac6454SAndrew Thompson *pindex = *((uint8_t *)(m->cur_data_ptr)); 100102ac6454SAndrew Thompson 100202ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 100302ac6454SAndrew Thompson 100402ac6454SAndrew Thompson return (0); /* success */ 100502ac6454SAndrew Thompson } else { 100602ac6454SAndrew Thompson 100702ac6454SAndrew Thompson *pindex = 0; /* fix compiler warning */ 100802ac6454SAndrew Thompson 100902ac6454SAndrew Thompson f->flag_iscomplete = 0; 101002ac6454SAndrew Thompson } 101102ac6454SAndrew Thompson return (1); /* failure */ 101202ac6454SAndrew Thompson } 101302ac6454SAndrew Thompson 101402ac6454SAndrew Thompson static void 1015760bc48eSAndrew Thompson ugen_fs_set_complete(struct usb_fifo *f, uint8_t index) 101602ac6454SAndrew Thompson { 1017760bc48eSAndrew Thompson struct usb_mbuf *m; 101802ac6454SAndrew Thompson 101902ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 102002ac6454SAndrew Thompson 102102ac6454SAndrew Thompson if (m == NULL) { 102202ac6454SAndrew Thompson /* can happen during close */ 102302ac6454SAndrew Thompson DPRINTF("out of buffers\n"); 102402ac6454SAndrew Thompson return; 102502ac6454SAndrew Thompson } 102602ac6454SAndrew Thompson USB_MBUF_RESET(m); 102702ac6454SAndrew Thompson 102802ac6454SAndrew Thompson *((uint8_t *)(m->cur_data_ptr)) = index; 102902ac6454SAndrew Thompson 103002ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 103102ac6454SAndrew Thompson 103202ac6454SAndrew Thompson f->flag_iscomplete = 1; 103302ac6454SAndrew Thompson 1034a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 103502ac6454SAndrew Thompson } 103602ac6454SAndrew Thompson 103702ac6454SAndrew Thompson static int 1038760bc48eSAndrew Thompson ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index) 103902ac6454SAndrew Thompson { 1040760bc48eSAndrew Thompson struct usb_device_request *req; 1041760bc48eSAndrew Thompson struct usb_xfer *xfer; 1042760bc48eSAndrew Thompson struct usb_fs_endpoint fs_ep; 104302ac6454SAndrew Thompson void *uaddr; /* userland pointer */ 104402ac6454SAndrew Thompson void *kaddr; 1045e0a69b51SAndrew Thompson usb_frlength_t offset; 1046e0a69b51SAndrew Thompson usb_frlength_t rem; 1047e0a69b51SAndrew Thompson usb_frcount_t n; 104802ac6454SAndrew Thompson uint32_t length; 104902ac6454SAndrew Thompson int error; 105002ac6454SAndrew Thompson uint8_t isread; 105102ac6454SAndrew Thompson 105202ac6454SAndrew Thompson if (ep_index >= f->fs_ep_max) { 105302ac6454SAndrew Thompson return (EINVAL); 105402ac6454SAndrew Thompson } 105502ac6454SAndrew Thompson xfer = f->fs_xfer[ep_index]; 105602ac6454SAndrew Thompson if (xfer == NULL) { 105702ac6454SAndrew Thompson return (EINVAL); 105802ac6454SAndrew Thompson } 105902ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 1060a593f6b8SAndrew Thompson if (usbd_transfer_pending(xfer)) { 106102ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 106202ac6454SAndrew Thompson return (EBUSY); /* should not happen */ 106302ac6454SAndrew Thompson } 106402ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 106502ac6454SAndrew Thompson 106602ac6454SAndrew Thompson error = copyin(f->fs_ep_ptr + 106702ac6454SAndrew Thompson ep_index, &fs_ep, sizeof(fs_ep)); 106802ac6454SAndrew Thompson if (error) { 106902ac6454SAndrew Thompson return (error); 107002ac6454SAndrew Thompson } 107102ac6454SAndrew Thompson /* security checks */ 107202ac6454SAndrew Thompson 107302ac6454SAndrew Thompson if (fs_ep.nFrames > xfer->max_frame_count) { 107402ac6454SAndrew Thompson xfer->error = USB_ERR_INVAL; 107502ac6454SAndrew Thompson goto complete; 107602ac6454SAndrew Thompson } 107702ac6454SAndrew Thompson if (fs_ep.nFrames == 0) { 107802ac6454SAndrew Thompson xfer->error = USB_ERR_INVAL; 107902ac6454SAndrew Thompson goto complete; 108002ac6454SAndrew Thompson } 108102ac6454SAndrew Thompson error = copyin(fs_ep.ppBuffer, 108202ac6454SAndrew Thompson &uaddr, sizeof(uaddr)); 108302ac6454SAndrew Thompson if (error) { 108402ac6454SAndrew Thompson return (error); 108502ac6454SAndrew Thompson } 108602ac6454SAndrew Thompson /* reset first frame */ 1087ed6d949aSAndrew Thompson usbd_xfer_set_frame_offset(xfer, 0, 0); 108802ac6454SAndrew Thompson 108902ac6454SAndrew Thompson if (xfer->flags_int.control_xfr) { 109002ac6454SAndrew Thompson 109102ac6454SAndrew Thompson req = xfer->frbuffers[0].buffer; 109202ac6454SAndrew Thompson 109302ac6454SAndrew Thompson error = copyin(fs_ep.pLength, 109402ac6454SAndrew Thompson &length, sizeof(length)); 109502ac6454SAndrew Thompson if (error) { 109602ac6454SAndrew Thompson return (error); 109702ac6454SAndrew Thompson } 1098599ce1c3SAndrew Thompson if (length != sizeof(*req)) { 109902ac6454SAndrew Thompson xfer->error = USB_ERR_INVAL; 110002ac6454SAndrew Thompson goto complete; 110102ac6454SAndrew Thompson } 110202ac6454SAndrew Thompson if (length != 0) { 110302ac6454SAndrew Thompson error = copyin(uaddr, req, length); 110402ac6454SAndrew Thompson if (error) { 110502ac6454SAndrew Thompson return (error); 110602ac6454SAndrew Thompson } 110702ac6454SAndrew Thompson } 110802ac6454SAndrew Thompson if (ugen_check_request(f->udev, req)) { 110902ac6454SAndrew Thompson xfer->error = USB_ERR_INVAL; 111002ac6454SAndrew Thompson goto complete; 111102ac6454SAndrew Thompson } 1112ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, length); 111302ac6454SAndrew Thompson 111402ac6454SAndrew Thompson /* Host mode only ! */ 111502ac6454SAndrew Thompson if ((req->bmRequestType & 111602ac6454SAndrew Thompson (UT_READ | UT_WRITE)) == UT_READ) { 111702ac6454SAndrew Thompson isread = 1; 111802ac6454SAndrew Thompson } else { 111902ac6454SAndrew Thompson isread = 0; 112002ac6454SAndrew Thompson } 112102ac6454SAndrew Thompson n = 1; 112202ac6454SAndrew Thompson offset = sizeof(*req); 112302ac6454SAndrew Thompson 112402ac6454SAndrew Thompson } else { 112502ac6454SAndrew Thompson /* Device and Host mode */ 112602ac6454SAndrew Thompson if (USB_GET_DATA_ISREAD(xfer)) { 112702ac6454SAndrew Thompson isread = 1; 112802ac6454SAndrew Thompson } else { 112902ac6454SAndrew Thompson isread = 0; 113002ac6454SAndrew Thompson } 113102ac6454SAndrew Thompson n = 0; 113202ac6454SAndrew Thompson offset = 0; 113302ac6454SAndrew Thompson } 113402ac6454SAndrew Thompson 1135ed6d949aSAndrew Thompson rem = usbd_xfer_max_len(xfer); 113602ac6454SAndrew Thompson xfer->nframes = fs_ep.nFrames; 113702ac6454SAndrew Thompson xfer->timeout = fs_ep.timeout; 113802ac6454SAndrew Thompson if (xfer->timeout > 65535) { 113902ac6454SAndrew Thompson xfer->timeout = 65535; 114002ac6454SAndrew Thompson } 114102ac6454SAndrew Thompson if (fs_ep.flags & USB_FS_FLAG_SINGLE_SHORT_OK) 114202ac6454SAndrew Thompson xfer->flags.short_xfer_ok = 1; 114302ac6454SAndrew Thompson else 114402ac6454SAndrew Thompson xfer->flags.short_xfer_ok = 0; 114502ac6454SAndrew Thompson 114602ac6454SAndrew Thompson if (fs_ep.flags & USB_FS_FLAG_MULTI_SHORT_OK) 114702ac6454SAndrew Thompson xfer->flags.short_frames_ok = 1; 114802ac6454SAndrew Thompson else 114902ac6454SAndrew Thompson xfer->flags.short_frames_ok = 0; 115002ac6454SAndrew Thompson 115102ac6454SAndrew Thompson if (fs_ep.flags & USB_FS_FLAG_FORCE_SHORT) 115202ac6454SAndrew Thompson xfer->flags.force_short_xfer = 1; 115302ac6454SAndrew Thompson else 115402ac6454SAndrew Thompson xfer->flags.force_short_xfer = 0; 115502ac6454SAndrew Thompson 115602ac6454SAndrew Thompson if (fs_ep.flags & USB_FS_FLAG_CLEAR_STALL) 1157ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); 115802ac6454SAndrew Thompson else 115902ac6454SAndrew Thompson xfer->flags.stall_pipe = 0; 116002ac6454SAndrew Thompson 116102ac6454SAndrew Thompson for (; n != xfer->nframes; n++) { 116202ac6454SAndrew Thompson 116302ac6454SAndrew Thompson error = copyin(fs_ep.pLength + n, 116402ac6454SAndrew Thompson &length, sizeof(length)); 116502ac6454SAndrew Thompson if (error) { 116602ac6454SAndrew Thompson break; 116702ac6454SAndrew Thompson } 1168ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, length); 116902ac6454SAndrew Thompson 117002ac6454SAndrew Thompson if (length > rem) { 117102ac6454SAndrew Thompson xfer->error = USB_ERR_INVAL; 117202ac6454SAndrew Thompson goto complete; 117302ac6454SAndrew Thompson } 117402ac6454SAndrew Thompson rem -= length; 117502ac6454SAndrew Thompson 117602ac6454SAndrew Thompson if (!isread) { 117702ac6454SAndrew Thompson 117802ac6454SAndrew Thompson /* we need to know the source buffer */ 117902ac6454SAndrew Thompson error = copyin(fs_ep.ppBuffer + n, 118002ac6454SAndrew Thompson &uaddr, sizeof(uaddr)); 118102ac6454SAndrew Thompson if (error) { 118202ac6454SAndrew Thompson break; 118302ac6454SAndrew Thompson } 118402ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 118502ac6454SAndrew Thompson /* get kernel buffer address */ 118602ac6454SAndrew Thompson kaddr = xfer->frbuffers[0].buffer; 118702ac6454SAndrew Thompson kaddr = USB_ADD_BYTES(kaddr, offset); 118802ac6454SAndrew Thompson } else { 118902ac6454SAndrew Thompson /* set current frame offset */ 1190ed6d949aSAndrew Thompson usbd_xfer_set_frame_offset(xfer, offset, n); 119102ac6454SAndrew Thompson 119202ac6454SAndrew Thompson /* get kernel buffer address */ 119302ac6454SAndrew Thompson kaddr = xfer->frbuffers[n].buffer; 119402ac6454SAndrew Thompson } 119502ac6454SAndrew Thompson 119602ac6454SAndrew Thompson /* move data */ 119702ac6454SAndrew Thompson error = copyin(uaddr, kaddr, length); 119802ac6454SAndrew Thompson if (error) { 119902ac6454SAndrew Thompson break; 120002ac6454SAndrew Thompson } 120102ac6454SAndrew Thompson } 120202ac6454SAndrew Thompson offset += length; 120302ac6454SAndrew Thompson } 120402ac6454SAndrew Thompson return (error); 120502ac6454SAndrew Thompson 120602ac6454SAndrew Thompson complete: 120702ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 120802ac6454SAndrew Thompson ugen_fs_set_complete(f, ep_index); 120902ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 121002ac6454SAndrew Thompson return (0); 121102ac6454SAndrew Thompson } 121202ac6454SAndrew Thompson 121302ac6454SAndrew Thompson static int 1214760bc48eSAndrew Thompson ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index) 121502ac6454SAndrew Thompson { 1216760bc48eSAndrew Thompson struct usb_device_request *req; 1217760bc48eSAndrew Thompson struct usb_xfer *xfer; 1218760bc48eSAndrew Thompson struct usb_fs_endpoint fs_ep; 1219760bc48eSAndrew Thompson struct usb_fs_endpoint *fs_ep_uptr; /* userland ptr */ 122002ac6454SAndrew Thompson void *uaddr; /* userland ptr */ 122102ac6454SAndrew Thompson void *kaddr; 1222e0a69b51SAndrew Thompson usb_frlength_t offset; 1223e0a69b51SAndrew Thompson usb_frlength_t rem; 1224e0a69b51SAndrew Thompson usb_frcount_t n; 122502ac6454SAndrew Thompson uint32_t length; 122602ac6454SAndrew Thompson uint32_t temp; 122702ac6454SAndrew Thompson int error; 122802ac6454SAndrew Thompson uint8_t isread; 122902ac6454SAndrew Thompson 1230578d0effSAndrew Thompson if (ep_index >= f->fs_ep_max) 123102ac6454SAndrew Thompson return (EINVAL); 1232578d0effSAndrew Thompson 123302ac6454SAndrew Thompson xfer = f->fs_xfer[ep_index]; 1234578d0effSAndrew Thompson if (xfer == NULL) 123502ac6454SAndrew Thompson return (EINVAL); 1236578d0effSAndrew Thompson 123702ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 1238a593f6b8SAndrew Thompson if (usbd_transfer_pending(xfer)) { 123902ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 124002ac6454SAndrew Thompson return (EBUSY); /* should not happen */ 124102ac6454SAndrew Thompson } 124202ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 124302ac6454SAndrew Thompson 124402ac6454SAndrew Thompson fs_ep_uptr = f->fs_ep_ptr + ep_index; 124502ac6454SAndrew Thompson error = copyin(fs_ep_uptr, &fs_ep, sizeof(fs_ep)); 124602ac6454SAndrew Thompson if (error) { 124702ac6454SAndrew Thompson return (error); 124802ac6454SAndrew Thompson } 124902ac6454SAndrew Thompson fs_ep.status = xfer->error; 125002ac6454SAndrew Thompson fs_ep.aFrames = xfer->aframes; 125102ac6454SAndrew Thompson fs_ep.isoc_time_complete = xfer->isoc_time_complete; 125202ac6454SAndrew Thompson if (xfer->error) { 125302ac6454SAndrew Thompson goto complete; 125402ac6454SAndrew Thompson } 125502ac6454SAndrew Thompson if (xfer->flags_int.control_xfr) { 125602ac6454SAndrew Thompson req = xfer->frbuffers[0].buffer; 125702ac6454SAndrew Thompson 125802ac6454SAndrew Thompson /* Host mode only ! */ 125902ac6454SAndrew Thompson if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_READ) { 126002ac6454SAndrew Thompson isread = 1; 126102ac6454SAndrew Thompson } else { 126202ac6454SAndrew Thompson isread = 0; 126302ac6454SAndrew Thompson } 126402ac6454SAndrew Thompson if (xfer->nframes == 0) 126502ac6454SAndrew Thompson n = 0; /* should never happen */ 126602ac6454SAndrew Thompson else 126702ac6454SAndrew Thompson n = 1; 126802ac6454SAndrew Thompson } else { 126902ac6454SAndrew Thompson /* Device and Host mode */ 127002ac6454SAndrew Thompson if (USB_GET_DATA_ISREAD(xfer)) { 127102ac6454SAndrew Thompson isread = 1; 127202ac6454SAndrew Thompson } else { 127302ac6454SAndrew Thompson isread = 0; 127402ac6454SAndrew Thompson } 127502ac6454SAndrew Thompson n = 0; 127602ac6454SAndrew Thompson } 127702ac6454SAndrew Thompson 127802ac6454SAndrew Thompson /* Update lengths and copy out data */ 127902ac6454SAndrew Thompson 1280ed6d949aSAndrew Thompson rem = usbd_xfer_max_len(xfer); 128102ac6454SAndrew Thompson offset = 0; 128202ac6454SAndrew Thompson 128302ac6454SAndrew Thompson for (; n != xfer->nframes; n++) { 128402ac6454SAndrew Thompson 128502ac6454SAndrew Thompson /* get initial length into "temp" */ 128602ac6454SAndrew Thompson error = copyin(fs_ep.pLength + n, 128702ac6454SAndrew Thompson &temp, sizeof(temp)); 128802ac6454SAndrew Thompson if (error) { 128902ac6454SAndrew Thompson return (error); 129002ac6454SAndrew Thompson } 129102ac6454SAndrew Thompson if (temp > rem) { 129202ac6454SAndrew Thompson /* the userland length has been corrupted */ 129302ac6454SAndrew Thompson DPRINTF("corrupt userland length " 129402ac6454SAndrew Thompson "%u > %u\n", temp, rem); 129502ac6454SAndrew Thompson fs_ep.status = USB_ERR_INVAL; 129602ac6454SAndrew Thompson goto complete; 129702ac6454SAndrew Thompson } 129802ac6454SAndrew Thompson rem -= temp; 129902ac6454SAndrew Thompson 130002ac6454SAndrew Thompson /* get actual transfer length */ 130102ac6454SAndrew Thompson length = xfer->frlengths[n]; 130202ac6454SAndrew Thompson if (length > temp) { 130302ac6454SAndrew Thompson /* data overflow */ 130402ac6454SAndrew Thompson fs_ep.status = USB_ERR_INVAL; 130502ac6454SAndrew Thompson DPRINTF("data overflow %u > %u\n", 130602ac6454SAndrew Thompson length, temp); 130702ac6454SAndrew Thompson goto complete; 130802ac6454SAndrew Thompson } 130902ac6454SAndrew Thompson if (isread) { 131002ac6454SAndrew Thompson 131102ac6454SAndrew Thompson /* we need to know the destination buffer */ 131202ac6454SAndrew Thompson error = copyin(fs_ep.ppBuffer + n, 131302ac6454SAndrew Thompson &uaddr, sizeof(uaddr)); 131402ac6454SAndrew Thompson if (error) { 131502ac6454SAndrew Thompson return (error); 131602ac6454SAndrew Thompson } 131702ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 131802ac6454SAndrew Thompson /* only one frame buffer */ 131902ac6454SAndrew Thompson kaddr = USB_ADD_BYTES( 132002ac6454SAndrew Thompson xfer->frbuffers[0].buffer, offset); 132102ac6454SAndrew Thompson } else { 132202ac6454SAndrew Thompson /* multiple frame buffers */ 132302ac6454SAndrew Thompson kaddr = xfer->frbuffers[n].buffer; 132402ac6454SAndrew Thompson } 132502ac6454SAndrew Thompson 132602ac6454SAndrew Thompson /* move data */ 132702ac6454SAndrew Thompson error = copyout(kaddr, uaddr, length); 132802ac6454SAndrew Thompson if (error) { 132902ac6454SAndrew Thompson return (error); 133002ac6454SAndrew Thompson } 133102ac6454SAndrew Thompson } 133202ac6454SAndrew Thompson /* 133302ac6454SAndrew Thompson * Update offset according to initial length, which is 133402ac6454SAndrew Thompson * needed by isochronous transfers! 133502ac6454SAndrew Thompson */ 133602ac6454SAndrew Thompson offset += temp; 133702ac6454SAndrew Thompson 133802ac6454SAndrew Thompson /* update length */ 133902ac6454SAndrew Thompson error = copyout(&length, 134002ac6454SAndrew Thompson fs_ep.pLength + n, sizeof(length)); 134102ac6454SAndrew Thompson if (error) { 134202ac6454SAndrew Thompson return (error); 134302ac6454SAndrew Thompson } 134402ac6454SAndrew Thompson } 134502ac6454SAndrew Thompson 134602ac6454SAndrew Thompson complete: 134702ac6454SAndrew Thompson /* update "aFrames" */ 134802ac6454SAndrew Thompson error = copyout(&fs_ep.aFrames, &fs_ep_uptr->aFrames, 134902ac6454SAndrew Thompson sizeof(fs_ep.aFrames)); 135002ac6454SAndrew Thompson if (error) 135102ac6454SAndrew Thompson goto done; 135202ac6454SAndrew Thompson 135302ac6454SAndrew Thompson /* update "isoc_time_complete" */ 135402ac6454SAndrew Thompson error = copyout(&fs_ep.isoc_time_complete, 135502ac6454SAndrew Thompson &fs_ep_uptr->isoc_time_complete, 135602ac6454SAndrew Thompson sizeof(fs_ep.isoc_time_complete)); 135702ac6454SAndrew Thompson if (error) 135802ac6454SAndrew Thompson goto done; 135902ac6454SAndrew Thompson /* update "status" */ 136002ac6454SAndrew Thompson error = copyout(&fs_ep.status, &fs_ep_uptr->status, 136102ac6454SAndrew Thompson sizeof(fs_ep.status)); 136202ac6454SAndrew Thompson done: 136302ac6454SAndrew Thompson return (error); 136402ac6454SAndrew Thompson } 136502ac6454SAndrew Thompson 136602ac6454SAndrew Thompson static uint8_t 1367760bc48eSAndrew Thompson ugen_fifo_in_use(struct usb_fifo *f, int fflags) 136802ac6454SAndrew Thompson { 1369760bc48eSAndrew Thompson struct usb_fifo *f_rx; 1370760bc48eSAndrew Thompson struct usb_fifo *f_tx; 137102ac6454SAndrew Thompson 137202ac6454SAndrew Thompson f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX]; 137302ac6454SAndrew Thompson f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX]; 137402ac6454SAndrew Thompson 137502ac6454SAndrew Thompson if ((fflags & FREAD) && f_rx && 137602ac6454SAndrew Thompson (f_rx->xfer[0] || f_rx->xfer[1])) { 137702ac6454SAndrew Thompson return (1); /* RX FIFO in use */ 137802ac6454SAndrew Thompson } 137902ac6454SAndrew Thompson if ((fflags & FWRITE) && f_tx && 138002ac6454SAndrew Thompson (f_tx->xfer[0] || f_tx->xfer[1])) { 138102ac6454SAndrew Thompson return (1); /* TX FIFO in use */ 138202ac6454SAndrew Thompson } 138302ac6454SAndrew Thompson return (0); /* not in use */ 138402ac6454SAndrew Thompson } 138502ac6454SAndrew Thompson 138602ac6454SAndrew Thompson static int 1387760bc48eSAndrew Thompson ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) 138802ac6454SAndrew Thompson { 1389760bc48eSAndrew Thompson struct usb_config usb_config[1]; 1390760bc48eSAndrew Thompson struct usb_device_request req; 139102ac6454SAndrew Thompson union { 1392760bc48eSAndrew Thompson struct usb_fs_complete *pcomp; 1393760bc48eSAndrew Thompson struct usb_fs_start *pstart; 1394760bc48eSAndrew Thompson struct usb_fs_stop *pstop; 1395760bc48eSAndrew Thompson struct usb_fs_open *popen; 1396e9cec016SHans Petter Selasky struct usb_fs_open_stream *popen_stream; 1397760bc48eSAndrew Thompson struct usb_fs_close *pclose; 1398760bc48eSAndrew Thompson struct usb_fs_clear_stall_sync *pstall; 139902ac6454SAndrew Thompson void *addr; 140002ac6454SAndrew Thompson } u; 1401ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 1402760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed; 14036c352e99SHans Petter Selasky struct usb_xfer *xfer; 140402ac6454SAndrew Thompson int error = 0; 140502ac6454SAndrew Thompson uint8_t iface_index; 140602ac6454SAndrew Thompson uint8_t isread; 140702ac6454SAndrew Thompson uint8_t ep_index; 14081c497368SHans Petter Selasky uint8_t pre_scale; 140902ac6454SAndrew Thompson 141002ac6454SAndrew Thompson u.addr = addr; 141102ac6454SAndrew Thompson 141202ac6454SAndrew Thompson DPRINTFN(6, "cmd=0x%08lx\n", cmd); 141302ac6454SAndrew Thompson 141402ac6454SAndrew Thompson switch (cmd) { 141502ac6454SAndrew Thompson case USB_FS_COMPLETE: 141602ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 141702ac6454SAndrew Thompson error = ugen_fs_get_complete(f, &ep_index); 141802ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 141902ac6454SAndrew Thompson 142002ac6454SAndrew Thompson if (error) { 142102ac6454SAndrew Thompson error = EBUSY; 142202ac6454SAndrew Thompson break; 142302ac6454SAndrew Thompson } 142402ac6454SAndrew Thompson u.pcomp->ep_index = ep_index; 142502ac6454SAndrew Thompson error = ugen_fs_copy_out(f, u.pcomp->ep_index); 142602ac6454SAndrew Thompson break; 142702ac6454SAndrew Thompson 142802ac6454SAndrew Thompson case USB_FS_START: 142902ac6454SAndrew Thompson error = ugen_fs_copy_in(f, u.pstart->ep_index); 14306c352e99SHans Petter Selasky if (error) 143102ac6454SAndrew Thompson break; 143202ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 14336c352e99SHans Petter Selasky xfer = f->fs_xfer[u.pstart->ep_index]; 14346c352e99SHans Petter Selasky usbd_transfer_start(xfer); 143502ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 143602ac6454SAndrew Thompson break; 143702ac6454SAndrew Thompson 143802ac6454SAndrew Thompson case USB_FS_STOP: 143902ac6454SAndrew Thompson if (u.pstop->ep_index >= f->fs_ep_max) { 144002ac6454SAndrew Thompson error = EINVAL; 144102ac6454SAndrew Thompson break; 144202ac6454SAndrew Thompson } 144302ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 14446c352e99SHans Petter Selasky xfer = f->fs_xfer[u.pstart->ep_index]; 14456c352e99SHans Petter Selasky if (usbd_transfer_pending(xfer)) { 14466c352e99SHans Petter Selasky usbd_transfer_stop(xfer); 14476c352e99SHans Petter Selasky /* 14486c352e99SHans Petter Selasky * Check if the USB transfer was stopped 14496c352e99SHans Petter Selasky * before it was even started. Else a cancel 14506c352e99SHans Petter Selasky * callback will be pending. 14516c352e99SHans Petter Selasky */ 14526c352e99SHans Petter Selasky if (!xfer->flags_int.transferring) { 14536c352e99SHans Petter Selasky ugen_fs_set_complete(xfer->priv_sc, 14546c352e99SHans Petter Selasky USB_P2U(xfer->priv_fifo)); 14556c352e99SHans Petter Selasky } 14566c352e99SHans Petter Selasky } 145702ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 145802ac6454SAndrew Thompson break; 145902ac6454SAndrew Thompson 146002ac6454SAndrew Thompson case USB_FS_OPEN: 1461e9cec016SHans Petter Selasky case USB_FS_OPEN_STREAM: 146202ac6454SAndrew Thompson if (u.popen->ep_index >= f->fs_ep_max) { 146302ac6454SAndrew Thompson error = EINVAL; 146402ac6454SAndrew Thompson break; 146502ac6454SAndrew Thompson } 146602ac6454SAndrew Thompson if (f->fs_xfer[u.popen->ep_index] != NULL) { 146702ac6454SAndrew Thompson error = EBUSY; 146802ac6454SAndrew Thompson break; 146902ac6454SAndrew Thompson } 147002ac6454SAndrew Thompson if (u.popen->max_bufsize > USB_FS_MAX_BUFSIZE) { 147102ac6454SAndrew Thompson u.popen->max_bufsize = USB_FS_MAX_BUFSIZE; 147202ac6454SAndrew Thompson } 14731c497368SHans Petter Selasky if (u.popen->max_frames & USB_FS_MAX_FRAMES_PRE_SCALE) { 14741c497368SHans Petter Selasky pre_scale = 1; 14751c497368SHans Petter Selasky u.popen->max_frames &= ~USB_FS_MAX_FRAMES_PRE_SCALE; 14761c497368SHans Petter Selasky } else { 14771c497368SHans Petter Selasky pre_scale = 0; 14781c497368SHans Petter Selasky } 147902ac6454SAndrew Thompson if (u.popen->max_frames > USB_FS_MAX_FRAMES) { 148002ac6454SAndrew Thompson u.popen->max_frames = USB_FS_MAX_FRAMES; 148102ac6454SAndrew Thompson break; 148202ac6454SAndrew Thompson } 148302ac6454SAndrew Thompson if (u.popen->max_frames == 0) { 148402ac6454SAndrew Thompson error = EINVAL; 148502ac6454SAndrew Thompson break; 148602ac6454SAndrew Thompson } 1487a593f6b8SAndrew Thompson ep = usbd_get_ep_by_addr(f->udev, u.popen->ep_no); 1488ae60fdfbSAndrew Thompson if (ep == NULL) { 148902ac6454SAndrew Thompson error = EINVAL; 149002ac6454SAndrew Thompson break; 149102ac6454SAndrew Thompson } 1492ae60fdfbSAndrew Thompson ed = ep->edesc; 149302ac6454SAndrew Thompson if (ed == NULL) { 149402ac6454SAndrew Thompson error = ENXIO; 149502ac6454SAndrew Thompson break; 149602ac6454SAndrew Thompson } 1497ae60fdfbSAndrew Thompson iface_index = ep->iface_index; 149802ac6454SAndrew Thompson 14991c497368SHans Petter Selasky memset(usb_config, 0, sizeof(usb_config)); 150002ac6454SAndrew Thompson 1501760bc48eSAndrew Thompson usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; 1502760bc48eSAndrew Thompson usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; 1503760bc48eSAndrew Thompson usb_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN); 1504760bc48eSAndrew Thompson usb_config[0].interval = USB_DEFAULT_INTERVAL; 1505760bc48eSAndrew Thompson usb_config[0].flags.proxy_buffer = 1; 15061c497368SHans Petter Selasky if (pre_scale != 0) 15071c497368SHans Petter Selasky usb_config[0].flags.pre_scale_frames = 1; 15085b3bb704SAndrew Thompson usb_config[0].callback = &ugen_ctrl_fs_callback; 1509760bc48eSAndrew Thompson usb_config[0].timeout = 0; /* no timeout */ 1510760bc48eSAndrew Thompson usb_config[0].frames = u.popen->max_frames; 1511760bc48eSAndrew Thompson usb_config[0].bufsize = u.popen->max_bufsize; 1512760bc48eSAndrew Thompson usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ 1513e9cec016SHans Petter Selasky if (cmd == USB_FS_OPEN_STREAM) 1514e9cec016SHans Petter Selasky usb_config[0].stream_id = u.popen_stream->stream_id; 151502ac6454SAndrew Thompson 1516760bc48eSAndrew Thompson if (usb_config[0].type == UE_CONTROL) { 1517f29a0724SAndrew Thompson if (f->udev->flags.usb_mode != USB_MODE_HOST) { 151802ac6454SAndrew Thompson error = EINVAL; 151902ac6454SAndrew Thompson break; 152002ac6454SAndrew Thompson } 152102ac6454SAndrew Thompson } else { 152202ac6454SAndrew Thompson 1523760bc48eSAndrew Thompson isread = ((usb_config[0].endpoint & 152402ac6454SAndrew Thompson (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN); 152502ac6454SAndrew Thompson 1526f29a0724SAndrew Thompson if (f->udev->flags.usb_mode != USB_MODE_HOST) { 152702ac6454SAndrew Thompson isread = !isread; 152802ac6454SAndrew Thompson } 152902ac6454SAndrew Thompson /* check permissions */ 153002ac6454SAndrew Thompson if (isread) { 153102ac6454SAndrew Thompson if (!(fflags & FREAD)) { 153202ac6454SAndrew Thompson error = EPERM; 153302ac6454SAndrew Thompson break; 153402ac6454SAndrew Thompson } 153502ac6454SAndrew Thompson } else { 153602ac6454SAndrew Thompson if (!(fflags & FWRITE)) { 153702ac6454SAndrew Thompson error = EPERM; 153802ac6454SAndrew Thompson break; 153902ac6454SAndrew Thompson } 154002ac6454SAndrew Thompson } 154102ac6454SAndrew Thompson } 1542a593f6b8SAndrew Thompson error = usbd_transfer_setup(f->udev, &iface_index, 1543760bc48eSAndrew Thompson f->fs_xfer + u.popen->ep_index, usb_config, 1, 154402ac6454SAndrew Thompson f, f->priv_mtx); 154502ac6454SAndrew Thompson if (error == 0) { 154602ac6454SAndrew Thompson /* update maximums */ 154702ac6454SAndrew Thompson u.popen->max_packet_length = 154802ac6454SAndrew Thompson f->fs_xfer[u.popen->ep_index]->max_frame_size; 154902ac6454SAndrew Thompson u.popen->max_bufsize = 155002ac6454SAndrew Thompson f->fs_xfer[u.popen->ep_index]->max_data_length; 15511c497368SHans Petter Selasky /* update number of frames */ 15521c497368SHans Petter Selasky u.popen->max_frames = 15531c497368SHans Petter Selasky f->fs_xfer[u.popen->ep_index]->nframes; 15541c497368SHans Petter Selasky /* store index of endpoint */ 155502ac6454SAndrew Thompson f->fs_xfer[u.popen->ep_index]->priv_fifo = 155602ac6454SAndrew Thompson ((uint8_t *)0) + u.popen->ep_index; 155702ac6454SAndrew Thompson } else { 155802ac6454SAndrew Thompson error = ENOMEM; 155902ac6454SAndrew Thompson } 156002ac6454SAndrew Thompson break; 156102ac6454SAndrew Thompson 156202ac6454SAndrew Thompson case USB_FS_CLOSE: 156302ac6454SAndrew Thompson if (u.pclose->ep_index >= f->fs_ep_max) { 156402ac6454SAndrew Thompson error = EINVAL; 156502ac6454SAndrew Thompson break; 156602ac6454SAndrew Thompson } 156702ac6454SAndrew Thompson if (f->fs_xfer[u.pclose->ep_index] == NULL) { 156802ac6454SAndrew Thompson error = EINVAL; 156902ac6454SAndrew Thompson break; 157002ac6454SAndrew Thompson } 1571a593f6b8SAndrew Thompson usbd_transfer_unsetup(f->fs_xfer + u.pclose->ep_index, 1); 157202ac6454SAndrew Thompson break; 157302ac6454SAndrew Thompson 157402ac6454SAndrew Thompson case USB_FS_CLEAR_STALL_SYNC: 157502ac6454SAndrew Thompson if (u.pstall->ep_index >= f->fs_ep_max) { 157602ac6454SAndrew Thompson error = EINVAL; 157702ac6454SAndrew Thompson break; 157802ac6454SAndrew Thompson } 157902ac6454SAndrew Thompson if (f->fs_xfer[u.pstall->ep_index] == NULL) { 158002ac6454SAndrew Thompson error = EINVAL; 158102ac6454SAndrew Thompson break; 158202ac6454SAndrew Thompson } 1583f29a0724SAndrew Thompson if (f->udev->flags.usb_mode != USB_MODE_HOST) { 158402ac6454SAndrew Thompson error = EINVAL; 158502ac6454SAndrew Thompson break; 158602ac6454SAndrew Thompson } 158702ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 1588a593f6b8SAndrew Thompson error = usbd_transfer_pending(f->fs_xfer[u.pstall->ep_index]); 158902ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 159002ac6454SAndrew Thompson 159102ac6454SAndrew Thompson if (error) { 159202ac6454SAndrew Thompson return (EBUSY); 159302ac6454SAndrew Thompson } 1594ae60fdfbSAndrew Thompson ep = f->fs_xfer[u.pstall->ep_index]->endpoint; 159502ac6454SAndrew Thompson 159602ac6454SAndrew Thompson /* setup a clear-stall packet */ 159702ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_ENDPOINT; 159802ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 159902ac6454SAndrew Thompson USETW(req.wValue, UF_ENDPOINT_HALT); 1600ae60fdfbSAndrew Thompson req.wIndex[0] = ep->edesc->bEndpointAddress; 160102ac6454SAndrew Thompson req.wIndex[1] = 0; 160202ac6454SAndrew Thompson USETW(req.wLength, 0); 160302ac6454SAndrew Thompson 1604a593f6b8SAndrew Thompson error = usbd_do_request(f->udev, NULL, &req, NULL); 160502ac6454SAndrew Thompson if (error == 0) { 1606a593f6b8SAndrew Thompson usbd_clear_data_toggle(f->udev, ep); 160702ac6454SAndrew Thompson } else { 160802ac6454SAndrew Thompson error = ENXIO; 160902ac6454SAndrew Thompson } 161002ac6454SAndrew Thompson break; 161102ac6454SAndrew Thompson 161202ac6454SAndrew Thompson default: 161302ac6454SAndrew Thompson error = ENOIOCTL; 161402ac6454SAndrew Thompson break; 161502ac6454SAndrew Thompson } 161602ac6454SAndrew Thompson 161702ac6454SAndrew Thompson DPRINTFN(6, "error=%d\n", error); 161802ac6454SAndrew Thompson 161902ac6454SAndrew Thompson return (error); 162002ac6454SAndrew Thompson } 162102ac6454SAndrew Thompson 162202ac6454SAndrew Thompson static int 1623760bc48eSAndrew Thompson ugen_set_short_xfer(struct usb_fifo *f, void *addr) 162402ac6454SAndrew Thompson { 162502ac6454SAndrew Thompson uint8_t t; 162602ac6454SAndrew Thompson 162702ac6454SAndrew Thompson if (*(int *)addr) 162802ac6454SAndrew Thompson t = 1; 162902ac6454SAndrew Thompson else 163002ac6454SAndrew Thompson t = 0; 163102ac6454SAndrew Thompson 163202ac6454SAndrew Thompson if (f->flag_short == t) { 163302ac6454SAndrew Thompson /* same value like before - accept */ 163402ac6454SAndrew Thompson return (0); 163502ac6454SAndrew Thompson } 163602ac6454SAndrew Thompson if (f->xfer[0] || f->xfer[1]) { 163702ac6454SAndrew Thompson /* cannot change this during transfer */ 163802ac6454SAndrew Thompson return (EBUSY); 163902ac6454SAndrew Thompson } 164002ac6454SAndrew Thompson f->flag_short = t; 164102ac6454SAndrew Thompson return (0); 164202ac6454SAndrew Thompson } 164302ac6454SAndrew Thompson 164402ac6454SAndrew Thompson static int 1645760bc48eSAndrew Thompson ugen_set_timeout(struct usb_fifo *f, void *addr) 164602ac6454SAndrew Thompson { 164702ac6454SAndrew Thompson f->timeout = *(int *)addr; 164802ac6454SAndrew Thompson if (f->timeout > 65535) { 164902ac6454SAndrew Thompson /* limit user input */ 165002ac6454SAndrew Thompson f->timeout = 65535; 165102ac6454SAndrew Thompson } 165202ac6454SAndrew Thompson return (0); 165302ac6454SAndrew Thompson } 165402ac6454SAndrew Thompson 165502ac6454SAndrew Thompson static int 1656760bc48eSAndrew Thompson ugen_get_frame_size(struct usb_fifo *f, void *addr) 165702ac6454SAndrew Thompson { 165802ac6454SAndrew Thompson if (f->xfer[0]) { 165902ac6454SAndrew Thompson *(int *)addr = f->xfer[0]->max_frame_size; 166002ac6454SAndrew Thompson } else { 166102ac6454SAndrew Thompson return (EINVAL); 166202ac6454SAndrew Thompson } 166302ac6454SAndrew Thompson return (0); 166402ac6454SAndrew Thompson } 166502ac6454SAndrew Thompson 166602ac6454SAndrew Thompson static int 1667760bc48eSAndrew Thompson ugen_set_buffer_size(struct usb_fifo *f, void *addr) 166802ac6454SAndrew Thompson { 1669e0a69b51SAndrew Thompson usb_frlength_t t; 167002ac6454SAndrew Thompson 167101cf7831SAndrew Thompson if (*(int *)addr < 0) 167201cf7831SAndrew Thompson t = 0; /* use "wMaxPacketSize" */ 167302ac6454SAndrew Thompson else if (*(int *)addr < (256 * 1024)) 167402ac6454SAndrew Thompson t = *(int *)addr; 167502ac6454SAndrew Thompson else 167602ac6454SAndrew Thompson t = 256 * 1024; 167702ac6454SAndrew Thompson 167802ac6454SAndrew Thompson if (f->bufsize == t) { 167902ac6454SAndrew Thompson /* same value like before - accept */ 168002ac6454SAndrew Thompson return (0); 168102ac6454SAndrew Thompson } 168202ac6454SAndrew Thompson if (f->xfer[0] || f->xfer[1]) { 168302ac6454SAndrew Thompson /* cannot change this during transfer */ 168402ac6454SAndrew Thompson return (EBUSY); 168502ac6454SAndrew Thompson } 168602ac6454SAndrew Thompson f->bufsize = t; 168702ac6454SAndrew Thompson return (0); 168802ac6454SAndrew Thompson } 168902ac6454SAndrew Thompson 169002ac6454SAndrew Thompson static int 1691760bc48eSAndrew Thompson ugen_get_buffer_size(struct usb_fifo *f, void *addr) 169202ac6454SAndrew Thompson { 169302ac6454SAndrew Thompson *(int *)addr = f->bufsize; 169402ac6454SAndrew Thompson return (0); 169502ac6454SAndrew Thompson } 169602ac6454SAndrew Thompson 169702ac6454SAndrew Thompson static int 1698760bc48eSAndrew Thompson ugen_get_iface_desc(struct usb_fifo *f, 1699760bc48eSAndrew Thompson struct usb_interface_descriptor *idesc) 170002ac6454SAndrew Thompson { 1701760bc48eSAndrew Thompson struct usb_interface *iface; 170202ac6454SAndrew Thompson 1703a593f6b8SAndrew Thompson iface = usbd_get_iface(f->udev, f->iface_index); 170402ac6454SAndrew Thompson if (iface && iface->idesc) { 170502ac6454SAndrew Thompson *idesc = *(iface->idesc); 170602ac6454SAndrew Thompson } else { 170702ac6454SAndrew Thompson return (EIO); 170802ac6454SAndrew Thompson } 170902ac6454SAndrew Thompson return (0); 171002ac6454SAndrew Thompson } 171102ac6454SAndrew Thompson 171202ac6454SAndrew Thompson static int 1713760bc48eSAndrew Thompson ugen_get_endpoint_desc(struct usb_fifo *f, 1714760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed) 171502ac6454SAndrew Thompson { 1716ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 171702ac6454SAndrew Thompson 1718ed6d949aSAndrew Thompson ep = usb_fifo_softc(f); 171902ac6454SAndrew Thompson 1720ae60fdfbSAndrew Thompson if (ep && ep->edesc) { 1721ae60fdfbSAndrew Thompson *ed = *ep->edesc; 172202ac6454SAndrew Thompson } else { 172302ac6454SAndrew Thompson return (EINVAL); 172402ac6454SAndrew Thompson } 172502ac6454SAndrew Thompson return (0); 172602ac6454SAndrew Thompson } 172702ac6454SAndrew Thompson 172802ac6454SAndrew Thompson static int 1729760bc48eSAndrew Thompson ugen_set_power_mode(struct usb_fifo *f, int mode) 173002ac6454SAndrew Thompson { 1731760bc48eSAndrew Thompson struct usb_device *udev = f->udev; 173202ac6454SAndrew Thompson int err; 173302ac6454SAndrew Thompson uint8_t old_mode; 173402ac6454SAndrew Thompson 173502ac6454SAndrew Thompson if ((udev == NULL) || 173602ac6454SAndrew Thompson (udev->parent_hub == NULL)) { 173702ac6454SAndrew Thompson return (EINVAL); 173802ac6454SAndrew Thompson } 173950230f98SAndrew Thompson err = priv_check(curthread, PRIV_DRIVER); 174002ac6454SAndrew Thompson if (err) 174102ac6454SAndrew Thompson return (err); 174202ac6454SAndrew Thompson 174302ac6454SAndrew Thompson /* get old power mode */ 174402ac6454SAndrew Thompson old_mode = udev->power_mode; 174502ac6454SAndrew Thompson 174602ac6454SAndrew Thompson /* if no change, then just return */ 174702ac6454SAndrew Thompson if (old_mode == mode) 174802ac6454SAndrew Thompson return (0); 174902ac6454SAndrew Thompson 175002ac6454SAndrew Thompson switch (mode) { 175102ac6454SAndrew Thompson case USB_POWER_MODE_OFF: 175202ac6454SAndrew Thompson /* get the device unconfigured */ 175302ac6454SAndrew Thompson err = ugen_set_config(f, USB_UNCONFIG_INDEX); 175402ac6454SAndrew Thompson if (err) { 175502ac6454SAndrew Thompson DPRINTFN(0, "Could not unconfigure " 175602ac6454SAndrew Thompson "device (ignored)\n"); 175702ac6454SAndrew Thompson } 175802ac6454SAndrew Thompson 175902ac6454SAndrew Thompson /* clear port enable */ 1760a593f6b8SAndrew Thompson err = usbd_req_clear_port_feature(udev->parent_hub, 176102ac6454SAndrew Thompson NULL, udev->port_no, UHF_PORT_ENABLE); 176202ac6454SAndrew Thompson break; 176302ac6454SAndrew Thompson 176402ac6454SAndrew Thompson case USB_POWER_MODE_ON: 176502ac6454SAndrew Thompson case USB_POWER_MODE_SAVE: 176602ac6454SAndrew Thompson break; 176702ac6454SAndrew Thompson 176802ac6454SAndrew Thompson case USB_POWER_MODE_RESUME: 17692df1e9a6SAndrew Thompson #if USB_HAVE_POWERD 17702df1e9a6SAndrew Thompson /* let USB-powerd handle resume */ 17712df1e9a6SAndrew Thompson USB_BUS_LOCK(udev->bus); 17722df1e9a6SAndrew Thompson udev->pwr_save.write_refs++; 17732df1e9a6SAndrew Thompson udev->pwr_save.last_xfer_time = ticks; 17742df1e9a6SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 17752df1e9a6SAndrew Thompson 17762df1e9a6SAndrew Thompson /* set new power mode */ 17772df1e9a6SAndrew Thompson usbd_set_power_mode(udev, USB_POWER_MODE_SAVE); 17782df1e9a6SAndrew Thompson 17792df1e9a6SAndrew Thompson /* wait for resume to complete */ 17802df1e9a6SAndrew Thompson usb_pause_mtx(NULL, hz / 4); 17812df1e9a6SAndrew Thompson 17822df1e9a6SAndrew Thompson /* clear write reference */ 17832df1e9a6SAndrew Thompson USB_BUS_LOCK(udev->bus); 17842df1e9a6SAndrew Thompson udev->pwr_save.write_refs--; 17852df1e9a6SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 17862df1e9a6SAndrew Thompson #endif 178702ac6454SAndrew Thompson mode = USB_POWER_MODE_SAVE; 178802ac6454SAndrew Thompson break; 178902ac6454SAndrew Thompson 179002ac6454SAndrew Thompson case USB_POWER_MODE_SUSPEND: 17912df1e9a6SAndrew Thompson #if USB_HAVE_POWERD 17922df1e9a6SAndrew Thompson /* let USB-powerd handle suspend */ 17932df1e9a6SAndrew Thompson USB_BUS_LOCK(udev->bus); 17942df1e9a6SAndrew Thompson udev->pwr_save.last_xfer_time = ticks - (256 * hz); 17952df1e9a6SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 17962df1e9a6SAndrew Thompson #endif 179702ac6454SAndrew Thompson mode = USB_POWER_MODE_SAVE; 179802ac6454SAndrew Thompson break; 179902ac6454SAndrew Thompson 180002ac6454SAndrew Thompson default: 180102ac6454SAndrew Thompson return (EINVAL); 180202ac6454SAndrew Thompson } 180302ac6454SAndrew Thompson 180402ac6454SAndrew Thompson if (err) 180502ac6454SAndrew Thompson return (ENXIO); /* I/O failure */ 180602ac6454SAndrew Thompson 180702ac6454SAndrew Thompson /* if we are powered off we need to re-enumerate first */ 180802ac6454SAndrew Thompson if (old_mode == USB_POWER_MODE_OFF) { 18098f9750b7SHans Petter Selasky if (udev->flags.usb_mode == USB_MODE_HOST) { 18108f9750b7SHans Petter Selasky if (udev->re_enumerate_wait == 0) 18118f9750b7SHans Petter Selasky udev->re_enumerate_wait = 1; 18128f9750b7SHans Petter Selasky } 18138f9750b7SHans Petter Selasky /* set power mode will wake up the explore thread */ 181402ac6454SAndrew Thompson } 181502ac6454SAndrew Thompson 181602ac6454SAndrew Thompson /* set new power mode */ 1817a593f6b8SAndrew Thompson usbd_set_power_mode(udev, mode); 181802ac6454SAndrew Thompson 181902ac6454SAndrew Thompson return (0); /* success */ 182002ac6454SAndrew Thompson } 182102ac6454SAndrew Thompson 182202ac6454SAndrew Thompson static int 1823760bc48eSAndrew Thompson ugen_get_power_mode(struct usb_fifo *f) 182402ac6454SAndrew Thompson { 1825760bc48eSAndrew Thompson struct usb_device *udev = f->udev; 182602ac6454SAndrew Thompson 1827d2d71ce7SAndrew Thompson if (udev == NULL) 182802ac6454SAndrew Thompson return (USB_POWER_MODE_ON); 1829d2d71ce7SAndrew Thompson 183002ac6454SAndrew Thompson return (udev->power_mode); 183102ac6454SAndrew Thompson } 183202ac6454SAndrew Thompson 183302ac6454SAndrew Thompson static int 1834760bc48eSAndrew Thompson ugen_do_port_feature(struct usb_fifo *f, uint8_t port_no, 183502ac6454SAndrew Thompson uint8_t set, uint16_t feature) 183602ac6454SAndrew Thompson { 1837760bc48eSAndrew Thompson struct usb_device *udev = f->udev; 1838760bc48eSAndrew Thompson struct usb_hub *hub; 183902ac6454SAndrew Thompson int err; 184002ac6454SAndrew Thompson 184150230f98SAndrew Thompson err = priv_check(curthread, PRIV_DRIVER); 184202ac6454SAndrew Thompson if (err) { 184302ac6454SAndrew Thompson return (err); 184402ac6454SAndrew Thompson } 184502ac6454SAndrew Thompson if (port_no == 0) { 184602ac6454SAndrew Thompson return (EINVAL); 184702ac6454SAndrew Thompson } 184802ac6454SAndrew Thompson if ((udev == NULL) || 184902ac6454SAndrew Thompson (udev->hub == NULL)) { 185002ac6454SAndrew Thompson return (EINVAL); 185102ac6454SAndrew Thompson } 185202ac6454SAndrew Thompson hub = udev->hub; 185302ac6454SAndrew Thompson 185402ac6454SAndrew Thompson if (port_no > hub->nports) { 185502ac6454SAndrew Thompson return (EINVAL); 185602ac6454SAndrew Thompson } 185702ac6454SAndrew Thompson if (set) 1858a593f6b8SAndrew Thompson err = usbd_req_set_port_feature(udev, 185902ac6454SAndrew Thompson NULL, port_no, feature); 186002ac6454SAndrew Thompson else 1861a593f6b8SAndrew Thompson err = usbd_req_clear_port_feature(udev, 186202ac6454SAndrew Thompson NULL, port_no, feature); 186302ac6454SAndrew Thompson 186402ac6454SAndrew Thompson if (err) 186502ac6454SAndrew Thompson return (ENXIO); /* failure */ 186602ac6454SAndrew Thompson 186702ac6454SAndrew Thompson return (0); /* success */ 186802ac6454SAndrew Thompson } 186902ac6454SAndrew Thompson 187002ac6454SAndrew Thompson static int 1871760bc48eSAndrew Thompson ugen_iface_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) 187202ac6454SAndrew Thompson { 1873760bc48eSAndrew Thompson struct usb_fifo *f_rx; 1874760bc48eSAndrew Thompson struct usb_fifo *f_tx; 187502ac6454SAndrew Thompson int error = 0; 187602ac6454SAndrew Thompson 187702ac6454SAndrew Thompson f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX]; 187802ac6454SAndrew Thompson f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX]; 187902ac6454SAndrew Thompson 188002ac6454SAndrew Thompson switch (cmd) { 188102ac6454SAndrew Thompson case USB_SET_RX_SHORT_XFER: 188202ac6454SAndrew Thompson if (fflags & FREAD) { 188302ac6454SAndrew Thompson error = ugen_set_short_xfer(f_rx, addr); 188402ac6454SAndrew Thompson } else { 188502ac6454SAndrew Thompson error = EINVAL; 188602ac6454SAndrew Thompson } 188702ac6454SAndrew Thompson break; 188802ac6454SAndrew Thompson 188902ac6454SAndrew Thompson case USB_SET_TX_FORCE_SHORT: 189002ac6454SAndrew Thompson if (fflags & FWRITE) { 189102ac6454SAndrew Thompson error = ugen_set_short_xfer(f_tx, addr); 189202ac6454SAndrew Thompson } else { 189302ac6454SAndrew Thompson error = EINVAL; 189402ac6454SAndrew Thompson } 189502ac6454SAndrew Thompson break; 189602ac6454SAndrew Thompson 189702ac6454SAndrew Thompson case USB_SET_RX_TIMEOUT: 189802ac6454SAndrew Thompson if (fflags & FREAD) { 189902ac6454SAndrew Thompson error = ugen_set_timeout(f_rx, addr); 190002ac6454SAndrew Thompson } else { 190102ac6454SAndrew Thompson error = EINVAL; 190202ac6454SAndrew Thompson } 190302ac6454SAndrew Thompson break; 190402ac6454SAndrew Thompson 190502ac6454SAndrew Thompson case USB_SET_TX_TIMEOUT: 190602ac6454SAndrew Thompson if (fflags & FWRITE) { 190702ac6454SAndrew Thompson error = ugen_set_timeout(f_tx, addr); 190802ac6454SAndrew Thompson } else { 190902ac6454SAndrew Thompson error = EINVAL; 191002ac6454SAndrew Thompson } 191102ac6454SAndrew Thompson break; 191202ac6454SAndrew Thompson 191302ac6454SAndrew Thompson case USB_GET_RX_FRAME_SIZE: 191402ac6454SAndrew Thompson if (fflags & FREAD) { 191502ac6454SAndrew Thompson error = ugen_get_frame_size(f_rx, addr); 191602ac6454SAndrew Thompson } else { 191702ac6454SAndrew Thompson error = EINVAL; 191802ac6454SAndrew Thompson } 191902ac6454SAndrew Thompson break; 192002ac6454SAndrew Thompson 192102ac6454SAndrew Thompson case USB_GET_TX_FRAME_SIZE: 192202ac6454SAndrew Thompson if (fflags & FWRITE) { 192302ac6454SAndrew Thompson error = ugen_get_frame_size(f_tx, addr); 192402ac6454SAndrew Thompson } else { 192502ac6454SAndrew Thompson error = EINVAL; 192602ac6454SAndrew Thompson } 192702ac6454SAndrew Thompson break; 192802ac6454SAndrew Thompson 192902ac6454SAndrew Thompson case USB_SET_RX_BUFFER_SIZE: 193002ac6454SAndrew Thompson if (fflags & FREAD) { 193102ac6454SAndrew Thompson error = ugen_set_buffer_size(f_rx, addr); 193202ac6454SAndrew Thompson } else { 193302ac6454SAndrew Thompson error = EINVAL; 193402ac6454SAndrew Thompson } 193502ac6454SAndrew Thompson break; 193602ac6454SAndrew Thompson 193702ac6454SAndrew Thompson case USB_SET_TX_BUFFER_SIZE: 193802ac6454SAndrew Thompson if (fflags & FWRITE) { 193902ac6454SAndrew Thompson error = ugen_set_buffer_size(f_tx, addr); 194002ac6454SAndrew Thompson } else { 194102ac6454SAndrew Thompson error = EINVAL; 194202ac6454SAndrew Thompson } 194302ac6454SAndrew Thompson break; 194402ac6454SAndrew Thompson 194502ac6454SAndrew Thompson case USB_GET_RX_BUFFER_SIZE: 194602ac6454SAndrew Thompson if (fflags & FREAD) { 194702ac6454SAndrew Thompson error = ugen_get_buffer_size(f_rx, addr); 194802ac6454SAndrew Thompson } else { 194902ac6454SAndrew Thompson error = EINVAL; 195002ac6454SAndrew Thompson } 195102ac6454SAndrew Thompson break; 195202ac6454SAndrew Thompson 195302ac6454SAndrew Thompson case USB_GET_TX_BUFFER_SIZE: 195402ac6454SAndrew Thompson if (fflags & FWRITE) { 195502ac6454SAndrew Thompson error = ugen_get_buffer_size(f_tx, addr); 195602ac6454SAndrew Thompson } else { 195702ac6454SAndrew Thompson error = EINVAL; 195802ac6454SAndrew Thompson } 195902ac6454SAndrew Thompson break; 196002ac6454SAndrew Thompson 196102ac6454SAndrew Thompson case USB_GET_RX_INTERFACE_DESC: 196202ac6454SAndrew Thompson if (fflags & FREAD) { 196302ac6454SAndrew Thompson error = ugen_get_iface_desc(f_rx, addr); 196402ac6454SAndrew Thompson } else { 196502ac6454SAndrew Thompson error = EINVAL; 196602ac6454SAndrew Thompson } 196702ac6454SAndrew Thompson break; 196802ac6454SAndrew Thompson 196902ac6454SAndrew Thompson case USB_GET_TX_INTERFACE_DESC: 197002ac6454SAndrew Thompson if (fflags & FWRITE) { 197102ac6454SAndrew Thompson error = ugen_get_iface_desc(f_tx, addr); 197202ac6454SAndrew Thompson } else { 197302ac6454SAndrew Thompson error = EINVAL; 197402ac6454SAndrew Thompson } 197502ac6454SAndrew Thompson break; 197602ac6454SAndrew Thompson 197702ac6454SAndrew Thompson case USB_GET_RX_ENDPOINT_DESC: 197802ac6454SAndrew Thompson if (fflags & FREAD) { 197902ac6454SAndrew Thompson error = ugen_get_endpoint_desc(f_rx, addr); 198002ac6454SAndrew Thompson } else { 198102ac6454SAndrew Thompson error = EINVAL; 198202ac6454SAndrew Thompson } 198302ac6454SAndrew Thompson break; 198402ac6454SAndrew Thompson 198502ac6454SAndrew Thompson case USB_GET_TX_ENDPOINT_DESC: 198602ac6454SAndrew Thompson if (fflags & FWRITE) { 198702ac6454SAndrew Thompson error = ugen_get_endpoint_desc(f_tx, addr); 198802ac6454SAndrew Thompson } else { 198902ac6454SAndrew Thompson error = EINVAL; 199002ac6454SAndrew Thompson } 199102ac6454SAndrew Thompson break; 199202ac6454SAndrew Thompson 199302ac6454SAndrew Thompson case USB_SET_RX_STALL_FLAG: 199402ac6454SAndrew Thompson if ((fflags & FREAD) && (*(int *)addr)) { 199502ac6454SAndrew Thompson f_rx->flag_stall = 1; 199602ac6454SAndrew Thompson } 199702ac6454SAndrew Thompson break; 199802ac6454SAndrew Thompson 199902ac6454SAndrew Thompson case USB_SET_TX_STALL_FLAG: 200002ac6454SAndrew Thompson if ((fflags & FWRITE) && (*(int *)addr)) { 200102ac6454SAndrew Thompson f_tx->flag_stall = 1; 200202ac6454SAndrew Thompson } 200302ac6454SAndrew Thompson break; 200402ac6454SAndrew Thompson 200502ac6454SAndrew Thompson default: 200602ac6454SAndrew Thompson error = ENOIOCTL; 200702ac6454SAndrew Thompson break; 200802ac6454SAndrew Thompson } 200902ac6454SAndrew Thompson return (error); 201002ac6454SAndrew Thompson } 201102ac6454SAndrew Thompson 201202ac6454SAndrew Thompson static int 2013760bc48eSAndrew Thompson ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags) 201402ac6454SAndrew Thompson { 201502ac6454SAndrew Thompson union { 2016760bc48eSAndrew Thompson struct usb_interface_descriptor *idesc; 2017760bc48eSAndrew Thompson struct usb_alt_interface *ai; 2018760bc48eSAndrew Thompson struct usb_device_descriptor *ddesc; 2019760bc48eSAndrew Thompson struct usb_config_descriptor *cdesc; 2020760bc48eSAndrew Thompson struct usb_device_stats *stat; 2021760bc48eSAndrew Thompson struct usb_fs_init *pinit; 2022760bc48eSAndrew Thompson struct usb_fs_uninit *puninit; 202302ac6454SAndrew Thompson uint32_t *ptime; 202402ac6454SAndrew Thompson void *addr; 202502ac6454SAndrew Thompson int *pint; 202602ac6454SAndrew Thompson } u; 2027760bc48eSAndrew Thompson struct usb_device_descriptor *dtemp; 2028760bc48eSAndrew Thompson struct usb_config_descriptor *ctemp; 2029760bc48eSAndrew Thompson struct usb_interface *iface; 203002ac6454SAndrew Thompson int error = 0; 203102ac6454SAndrew Thompson uint8_t n; 203202ac6454SAndrew Thompson 203302ac6454SAndrew Thompson u.addr = addr; 203402ac6454SAndrew Thompson 203502ac6454SAndrew Thompson DPRINTFN(6, "cmd=0x%08lx\n", cmd); 203602ac6454SAndrew Thompson 203702ac6454SAndrew Thompson switch (cmd) { 203802ac6454SAndrew Thompson case USB_DISCOVER: 2039a593f6b8SAndrew Thompson usb_needs_explore_all(); 204002ac6454SAndrew Thompson break; 204102ac6454SAndrew Thompson 204202ac6454SAndrew Thompson case USB_SETDEBUG: 204302ac6454SAndrew Thompson if (!(fflags & FWRITE)) { 204402ac6454SAndrew Thompson error = EPERM; 204502ac6454SAndrew Thompson break; 204602ac6454SAndrew Thompson } 2047a593f6b8SAndrew Thompson usb_debug = *(int *)addr; 204802ac6454SAndrew Thompson break; 204902ac6454SAndrew Thompson 205002ac6454SAndrew Thompson case USB_GET_CONFIG: 205102ac6454SAndrew Thompson *(int *)addr = f->udev->curr_config_index; 205202ac6454SAndrew Thompson break; 205302ac6454SAndrew Thompson 205402ac6454SAndrew Thompson case USB_SET_CONFIG: 205502ac6454SAndrew Thompson if (!(fflags & FWRITE)) { 205602ac6454SAndrew Thompson error = EPERM; 205702ac6454SAndrew Thompson break; 205802ac6454SAndrew Thompson } 205902ac6454SAndrew Thompson error = ugen_set_config(f, *(int *)addr); 206002ac6454SAndrew Thompson break; 206102ac6454SAndrew Thompson 206202ac6454SAndrew Thompson case USB_GET_ALTINTERFACE: 2063a593f6b8SAndrew Thompson iface = usbd_get_iface(f->udev, 206402ac6454SAndrew Thompson u.ai->uai_interface_index); 206502ac6454SAndrew Thompson if (iface && iface->idesc) { 206602ac6454SAndrew Thompson u.ai->uai_alt_index = iface->alt_index; 206702ac6454SAndrew Thompson } else { 206802ac6454SAndrew Thompson error = EINVAL; 206902ac6454SAndrew Thompson } 207002ac6454SAndrew Thompson break; 207102ac6454SAndrew Thompson 207202ac6454SAndrew Thompson case USB_SET_ALTINTERFACE: 207302ac6454SAndrew Thompson if (!(fflags & FWRITE)) { 207402ac6454SAndrew Thompson error = EPERM; 207502ac6454SAndrew Thompson break; 207602ac6454SAndrew Thompson } 207702ac6454SAndrew Thompson error = ugen_set_interface(f, 207802ac6454SAndrew Thompson u.ai->uai_interface_index, u.ai->uai_alt_index); 207902ac6454SAndrew Thompson break; 208002ac6454SAndrew Thompson 208102ac6454SAndrew Thompson case USB_GET_DEVICE_DESC: 2082a593f6b8SAndrew Thompson dtemp = usbd_get_device_descriptor(f->udev); 208302ac6454SAndrew Thompson if (!dtemp) { 208402ac6454SAndrew Thompson error = EIO; 208502ac6454SAndrew Thompson break; 208602ac6454SAndrew Thompson } 208702ac6454SAndrew Thompson *u.ddesc = *dtemp; 208802ac6454SAndrew Thompson break; 208902ac6454SAndrew Thompson 209002ac6454SAndrew Thompson case USB_GET_CONFIG_DESC: 2091a593f6b8SAndrew Thompson ctemp = usbd_get_config_descriptor(f->udev); 209202ac6454SAndrew Thompson if (!ctemp) { 209302ac6454SAndrew Thompson error = EIO; 209402ac6454SAndrew Thompson break; 209502ac6454SAndrew Thompson } 209602ac6454SAndrew Thompson *u.cdesc = *ctemp; 209702ac6454SAndrew Thompson break; 209802ac6454SAndrew Thompson 209902ac6454SAndrew Thompson case USB_GET_FULL_DESC: 210002ac6454SAndrew Thompson error = ugen_get_cdesc(f, addr); 210102ac6454SAndrew Thompson break; 210202ac6454SAndrew Thompson 210302ac6454SAndrew Thompson case USB_GET_STRING_DESC: 210402ac6454SAndrew Thompson error = ugen_get_sdesc(f, addr); 210502ac6454SAndrew Thompson break; 210602ac6454SAndrew Thompson 210702ac6454SAndrew Thompson case USB_GET_IFACE_DRIVER: 210802ac6454SAndrew Thompson error = ugen_get_iface_driver(f, addr); 210902ac6454SAndrew Thompson break; 211002ac6454SAndrew Thompson 211102ac6454SAndrew Thompson case USB_REQUEST: 211202ac6454SAndrew Thompson case USB_DO_REQUEST: 211302ac6454SAndrew Thompson if (!(fflags & FWRITE)) { 211402ac6454SAndrew Thompson error = EPERM; 211502ac6454SAndrew Thompson break; 211602ac6454SAndrew Thompson } 211702ac6454SAndrew Thompson error = ugen_do_request(f, addr); 211802ac6454SAndrew Thompson break; 211902ac6454SAndrew Thompson 212002ac6454SAndrew Thompson case USB_DEVICEINFO: 212102ac6454SAndrew Thompson case USB_GET_DEVICEINFO: 2122a593f6b8SAndrew Thompson error = usb_gen_fill_deviceinfo(f, addr); 212302ac6454SAndrew Thompson break; 212402ac6454SAndrew Thompson 212502ac6454SAndrew Thompson case USB_DEVICESTATS: 212602ac6454SAndrew Thompson for (n = 0; n != 4; n++) { 212702ac6454SAndrew Thompson 212802ac6454SAndrew Thompson u.stat->uds_requests_fail[n] = 212902ac6454SAndrew Thompson f->udev->bus->stats_err.uds_requests[n]; 213002ac6454SAndrew Thompson 213102ac6454SAndrew Thompson u.stat->uds_requests_ok[n] = 213202ac6454SAndrew Thompson f->udev->bus->stats_ok.uds_requests[n]; 213302ac6454SAndrew Thompson } 213402ac6454SAndrew Thompson break; 213502ac6454SAndrew Thompson 213602ac6454SAndrew Thompson case USB_DEVICEENUMERATE: 213702ac6454SAndrew Thompson error = ugen_re_enumerate(f); 213802ac6454SAndrew Thompson break; 213902ac6454SAndrew Thompson 214002ac6454SAndrew Thompson case USB_GET_PLUGTIME: 214102ac6454SAndrew Thompson *u.ptime = f->udev->plugtime; 214202ac6454SAndrew Thompson break; 214302ac6454SAndrew Thompson 214402ac6454SAndrew Thompson case USB_CLAIM_INTERFACE: 214502ac6454SAndrew Thompson case USB_RELEASE_INTERFACE: 214602ac6454SAndrew Thompson /* TODO */ 214702ac6454SAndrew Thompson break; 214802ac6454SAndrew Thompson 214902ac6454SAndrew Thompson case USB_IFACE_DRIVER_ACTIVE: 2150a7aca4cdSAndrew Thompson 2151a7aca4cdSAndrew Thompson n = *u.pint & 0xFF; 2152a7aca4cdSAndrew Thompson 2153a7aca4cdSAndrew Thompson iface = usbd_get_iface(f->udev, n); 2154a7aca4cdSAndrew Thompson 2155a7aca4cdSAndrew Thompson if (iface && iface->subdev) 2156a7aca4cdSAndrew Thompson error = 0; 2157a7aca4cdSAndrew Thompson else 2158a7aca4cdSAndrew Thompson error = ENXIO; 215902ac6454SAndrew Thompson break; 216002ac6454SAndrew Thompson 216102ac6454SAndrew Thompson case USB_IFACE_DRIVER_DETACH: 2162a7aca4cdSAndrew Thompson 216302ac6454SAndrew Thompson error = priv_check(curthread, PRIV_DRIVER); 2164a7aca4cdSAndrew Thompson 2165a7aca4cdSAndrew Thompson if (error) 2166a7aca4cdSAndrew Thompson break; 2167a7aca4cdSAndrew Thompson 2168a7aca4cdSAndrew Thompson n = *u.pint & 0xFF; 2169a7aca4cdSAndrew Thompson 2170a7aca4cdSAndrew Thompson if (n == USB_IFACE_INDEX_ANY) { 2171a7aca4cdSAndrew Thompson error = EINVAL; 217202ac6454SAndrew Thompson break; 217302ac6454SAndrew Thompson } 2174a7aca4cdSAndrew Thompson 2175d9073c1eSHans Petter Selasky /* 2176d9073c1eSHans Petter Selasky * Detach the currently attached driver. 2177d9073c1eSHans Petter Selasky */ 2178a7aca4cdSAndrew Thompson usb_detach_device(f->udev, n, 0); 2179d9073c1eSHans Petter Selasky 2180d9073c1eSHans Petter Selasky /* 2181d9073c1eSHans Petter Selasky * Set parent to self, this should keep attach away 2182d9073c1eSHans Petter Selasky * until the next set configuration event. 2183d9073c1eSHans Petter Selasky */ 2184d9073c1eSHans Petter Selasky usbd_set_parent_iface(f->udev, n, n); 218502ac6454SAndrew Thompson break; 218602ac6454SAndrew Thompson 218702ac6454SAndrew Thompson case USB_SET_POWER_MODE: 218802ac6454SAndrew Thompson error = ugen_set_power_mode(f, *u.pint); 218902ac6454SAndrew Thompson break; 219002ac6454SAndrew Thompson 219102ac6454SAndrew Thompson case USB_GET_POWER_MODE: 219202ac6454SAndrew Thompson *u.pint = ugen_get_power_mode(f); 219302ac6454SAndrew Thompson break; 219402ac6454SAndrew Thompson 219502ac6454SAndrew Thompson case USB_SET_PORT_ENABLE: 219602ac6454SAndrew Thompson error = ugen_do_port_feature(f, 219702ac6454SAndrew Thompson *u.pint, 1, UHF_PORT_ENABLE); 219802ac6454SAndrew Thompson break; 219902ac6454SAndrew Thompson 220002ac6454SAndrew Thompson case USB_SET_PORT_DISABLE: 220102ac6454SAndrew Thompson error = ugen_do_port_feature(f, 220202ac6454SAndrew Thompson *u.pint, 0, UHF_PORT_ENABLE); 220302ac6454SAndrew Thompson break; 220402ac6454SAndrew Thompson 220502ac6454SAndrew Thompson case USB_FS_INIT: 220602ac6454SAndrew Thompson /* verify input parameters */ 220702ac6454SAndrew Thompson if (u.pinit->pEndpoints == NULL) { 220802ac6454SAndrew Thompson error = EINVAL; 220902ac6454SAndrew Thompson break; 221002ac6454SAndrew Thompson } 221102ac6454SAndrew Thompson if (u.pinit->ep_index_max > 127) { 221202ac6454SAndrew Thompson error = EINVAL; 221302ac6454SAndrew Thompson break; 221402ac6454SAndrew Thompson } 221502ac6454SAndrew Thompson if (u.pinit->ep_index_max == 0) { 221602ac6454SAndrew Thompson error = EINVAL; 221702ac6454SAndrew Thompson break; 221802ac6454SAndrew Thompson } 221902ac6454SAndrew Thompson if (f->fs_xfer != NULL) { 222002ac6454SAndrew Thompson error = EBUSY; 222102ac6454SAndrew Thompson break; 222202ac6454SAndrew Thompson } 222302ac6454SAndrew Thompson if (f->dev_ep_index != 0) { 222402ac6454SAndrew Thompson error = EINVAL; 222502ac6454SAndrew Thompson break; 222602ac6454SAndrew Thompson } 222702ac6454SAndrew Thompson if (ugen_fifo_in_use(f, fflags)) { 222802ac6454SAndrew Thompson error = EBUSY; 222902ac6454SAndrew Thompson break; 223002ac6454SAndrew Thompson } 2231a593f6b8SAndrew Thompson error = usb_fifo_alloc_buffer(f, 1, u.pinit->ep_index_max); 223202ac6454SAndrew Thompson if (error) { 223302ac6454SAndrew Thompson break; 223402ac6454SAndrew Thompson } 223502ac6454SAndrew Thompson f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) * 223602ac6454SAndrew Thompson u.pinit->ep_index_max, M_USB, M_WAITOK | M_ZERO); 223702ac6454SAndrew Thompson if (f->fs_xfer == NULL) { 2238a593f6b8SAndrew Thompson usb_fifo_free_buffer(f); 223902ac6454SAndrew Thompson error = ENOMEM; 224002ac6454SAndrew Thompson break; 224102ac6454SAndrew Thompson } 224202ac6454SAndrew Thompson f->fs_ep_max = u.pinit->ep_index_max; 224302ac6454SAndrew Thompson f->fs_ep_ptr = u.pinit->pEndpoints; 224402ac6454SAndrew Thompson break; 224502ac6454SAndrew Thompson 224602ac6454SAndrew Thompson case USB_FS_UNINIT: 224702ac6454SAndrew Thompson if (u.puninit->dummy != 0) { 224802ac6454SAndrew Thompson error = EINVAL; 224902ac6454SAndrew Thompson break; 225002ac6454SAndrew Thompson } 225102ac6454SAndrew Thompson error = ugen_fs_uninit(f); 225202ac6454SAndrew Thompson break; 225302ac6454SAndrew Thompson 225402ac6454SAndrew Thompson default: 225502ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 225602ac6454SAndrew Thompson error = ugen_iface_ioctl(f, cmd, addr, fflags); 225702ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 225802ac6454SAndrew Thompson break; 225902ac6454SAndrew Thompson } 226002ac6454SAndrew Thompson DPRINTFN(6, "error=%d\n", error); 226102ac6454SAndrew Thompson return (error); 226202ac6454SAndrew Thompson } 226302ac6454SAndrew Thompson 226402ac6454SAndrew Thompson static void 22655b3bb704SAndrew Thompson ugen_ctrl_fs_callback(struct usb_xfer *xfer, usb_error_t error) 226602ac6454SAndrew Thompson { 226702ac6454SAndrew Thompson ; /* workaround for a bug in "indent" */ 226802ac6454SAndrew Thompson 226902ac6454SAndrew Thompson DPRINTF("st=%u alen=%u aframes=%u\n", 227002ac6454SAndrew Thompson USB_GET_STATE(xfer), xfer->actlen, xfer->aframes); 227102ac6454SAndrew Thompson 227202ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 227302ac6454SAndrew Thompson case USB_ST_SETUP: 2274a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 227502ac6454SAndrew Thompson break; 227602ac6454SAndrew Thompson default: 227702ac6454SAndrew Thompson ugen_fs_set_complete(xfer->priv_sc, USB_P2U(xfer->priv_fifo)); 227802ac6454SAndrew Thompson break; 227902ac6454SAndrew Thompson } 228002ac6454SAndrew Thompson } 22818755859aSAndrew Thompson #endif /* USB_HAVE_UGEN */ 2282