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 1299360ae40SAndrew Thompson 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 } 243760bc48eSAndrew Thompson bzero(usb_config, 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; 256760bc48eSAndrew Thompson usb_config[0].direction = UE_DIR_TX; 257760bc48eSAndrew Thompson usb_config[0].interval = USB_DEFAULT_INTERVAL; 258760bc48eSAndrew Thompson usb_config[0].flags.proxy_buffer = 1; 259760bc48eSAndrew Thompson usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ 26002ac6454SAndrew Thompson 26102ac6454SAndrew Thompson switch (ed->bmAttributes & UE_XFERTYPE) { 26202ac6454SAndrew Thompson case UE_INTERRUPT: 26302ac6454SAndrew Thompson case UE_BULK: 26402ac6454SAndrew Thompson if (f->flag_short) { 265760bc48eSAndrew Thompson usb_config[0].flags.force_short_xfer = 1; 26602ac6454SAndrew Thompson } 2675b3bb704SAndrew Thompson usb_config[0].callback = &ugen_ctrl_write_callback; 268760bc48eSAndrew Thompson usb_config[0].timeout = f->timeout; 269760bc48eSAndrew Thompson usb_config[0].frames = 1; 270760bc48eSAndrew Thompson usb_config[0].bufsize = f->bufsize; 271760bc48eSAndrew Thompson if (ugen_transfer_setup(f, usb_config, 2)) { 27202ac6454SAndrew Thompson return (EIO); 27302ac6454SAndrew Thompson } 27402ac6454SAndrew Thompson /* first transfer does not clear stall */ 27502ac6454SAndrew Thompson f->flag_stall = 0; 27602ac6454SAndrew Thompson break; 27702ac6454SAndrew Thompson 27802ac6454SAndrew Thompson case UE_ISOCHRONOUS: 279760bc48eSAndrew Thompson usb_config[0].flags.short_xfer_ok = 1; 280760bc48eSAndrew Thompson usb_config[0].bufsize = 0; /* use default */ 281760bc48eSAndrew Thompson usb_config[0].frames = f->nframes; 282760bc48eSAndrew Thompson usb_config[0].callback = &ugen_isoc_write_callback; 283760bc48eSAndrew Thompson usb_config[0].timeout = 0; 28402ac6454SAndrew Thompson 28502ac6454SAndrew Thompson /* clone configuration */ 286760bc48eSAndrew Thompson usb_config[1] = usb_config[0]; 28702ac6454SAndrew Thompson 288760bc48eSAndrew Thompson if (ugen_transfer_setup(f, usb_config, 2)) { 28902ac6454SAndrew Thompson return (EIO); 29002ac6454SAndrew Thompson } 29102ac6454SAndrew Thompson break; 29202ac6454SAndrew Thompson default: 29302ac6454SAndrew Thompson return (EINVAL); 29402ac6454SAndrew Thompson } 29502ac6454SAndrew Thompson return (0); 29602ac6454SAndrew Thompson } 29702ac6454SAndrew Thompson 29802ac6454SAndrew Thompson static int 299760bc48eSAndrew Thompson ugen_open_pipe_read(struct usb_fifo *f) 30002ac6454SAndrew Thompson { 301760bc48eSAndrew Thompson struct usb_config usb_config[2]; 302ed6d949aSAndrew Thompson struct usb_endpoint *ep = usb_fifo_softc(f); 303ae60fdfbSAndrew Thompson struct usb_endpoint_descriptor *ed = ep->edesc; 30402ac6454SAndrew Thompson 30502ac6454SAndrew Thompson mtx_assert(f->priv_mtx, MA_OWNED); 30602ac6454SAndrew Thompson 30702ac6454SAndrew Thompson if (f->xfer[0] || f->xfer[1]) { 30802ac6454SAndrew Thompson /* transfers are already opened */ 30902ac6454SAndrew Thompson return (0); 31002ac6454SAndrew Thompson } 311760bc48eSAndrew Thompson bzero(usb_config, sizeof(usb_config)); 31202ac6454SAndrew Thompson 313760bc48eSAndrew Thompson usb_config[1].type = UE_CONTROL; 314760bc48eSAndrew Thompson usb_config[1].endpoint = 0; 315760bc48eSAndrew Thompson usb_config[1].direction = UE_DIR_ANY; 316760bc48eSAndrew Thompson usb_config[1].timeout = 1000; /* 1 second */ 317760bc48eSAndrew Thompson usb_config[1].interval = 50;/* 50 milliseconds */ 318760bc48eSAndrew Thompson usb_config[1].bufsize = sizeof(struct usb_device_request); 319760bc48eSAndrew Thompson usb_config[1].callback = &ugen_read_clear_stall_callback; 320760bc48eSAndrew Thompson usb_config[1].usb_mode = USB_MODE_HOST; 32102ac6454SAndrew Thompson 322760bc48eSAndrew Thompson usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; 323760bc48eSAndrew Thompson usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; 324760bc48eSAndrew Thompson usb_config[0].direction = UE_DIR_RX; 325760bc48eSAndrew Thompson usb_config[0].interval = USB_DEFAULT_INTERVAL; 326760bc48eSAndrew Thompson usb_config[0].flags.proxy_buffer = 1; 327760bc48eSAndrew Thompson usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ 32802ac6454SAndrew Thompson 32902ac6454SAndrew Thompson switch (ed->bmAttributes & UE_XFERTYPE) { 33002ac6454SAndrew Thompson case UE_INTERRUPT: 33102ac6454SAndrew Thompson case UE_BULK: 33202ac6454SAndrew Thompson if (f->flag_short) { 333760bc48eSAndrew Thompson usb_config[0].flags.short_xfer_ok = 1; 33402ac6454SAndrew Thompson } 335760bc48eSAndrew Thompson usb_config[0].timeout = f->timeout; 336760bc48eSAndrew Thompson usb_config[0].frames = 1; 3375b3bb704SAndrew Thompson usb_config[0].callback = &ugen_ctrl_read_callback; 338760bc48eSAndrew Thompson usb_config[0].bufsize = f->bufsize; 33902ac6454SAndrew Thompson 340760bc48eSAndrew Thompson if (ugen_transfer_setup(f, usb_config, 2)) { 34102ac6454SAndrew Thompson return (EIO); 34202ac6454SAndrew Thompson } 34302ac6454SAndrew Thompson /* first transfer does not clear stall */ 34402ac6454SAndrew Thompson f->flag_stall = 0; 34502ac6454SAndrew Thompson break; 34602ac6454SAndrew Thompson 34702ac6454SAndrew Thompson case UE_ISOCHRONOUS: 348760bc48eSAndrew Thompson usb_config[0].flags.short_xfer_ok = 1; 349760bc48eSAndrew Thompson usb_config[0].bufsize = 0; /* use default */ 350760bc48eSAndrew Thompson usb_config[0].frames = f->nframes; 351760bc48eSAndrew Thompson usb_config[0].callback = &ugen_isoc_read_callback; 352760bc48eSAndrew Thompson usb_config[0].timeout = 0; 35302ac6454SAndrew Thompson 35402ac6454SAndrew Thompson /* clone configuration */ 355760bc48eSAndrew Thompson usb_config[1] = usb_config[0]; 35602ac6454SAndrew Thompson 357760bc48eSAndrew Thompson if (ugen_transfer_setup(f, usb_config, 2)) { 35802ac6454SAndrew Thompson return (EIO); 35902ac6454SAndrew Thompson } 36002ac6454SAndrew Thompson break; 36102ac6454SAndrew Thompson 36202ac6454SAndrew Thompson default: 36302ac6454SAndrew Thompson return (EINVAL); 36402ac6454SAndrew Thompson } 36502ac6454SAndrew Thompson return (0); 36602ac6454SAndrew Thompson } 36702ac6454SAndrew Thompson 36802ac6454SAndrew Thompson static void 369760bc48eSAndrew Thompson ugen_start_read(struct usb_fifo *f) 37002ac6454SAndrew Thompson { 37102ac6454SAndrew Thompson /* check that pipes are open */ 37202ac6454SAndrew Thompson if (ugen_open_pipe_read(f)) { 37302ac6454SAndrew Thompson /* signal error */ 374a593f6b8SAndrew Thompson usb_fifo_put_data_error(f); 37502ac6454SAndrew Thompson } 37602ac6454SAndrew Thompson /* start transfers */ 377a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[0]); 378a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[1]); 37902ac6454SAndrew Thompson } 38002ac6454SAndrew Thompson 38102ac6454SAndrew Thompson static void 382760bc48eSAndrew Thompson ugen_start_write(struct usb_fifo *f) 38302ac6454SAndrew Thompson { 38402ac6454SAndrew Thompson /* check that pipes are open */ 38502ac6454SAndrew Thompson if (ugen_open_pipe_write(f)) { 38602ac6454SAndrew Thompson /* signal error */ 387a593f6b8SAndrew Thompson usb_fifo_get_data_error(f); 38802ac6454SAndrew Thompson } 38902ac6454SAndrew Thompson /* start transfers */ 390a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[0]); 391a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[1]); 39202ac6454SAndrew Thompson } 39302ac6454SAndrew Thompson 39402ac6454SAndrew Thompson static void 395760bc48eSAndrew Thompson ugen_stop_io(struct usb_fifo *f) 39602ac6454SAndrew Thompson { 39702ac6454SAndrew Thompson /* stop transfers */ 398a593f6b8SAndrew Thompson usbd_transfer_stop(f->xfer[0]); 399a593f6b8SAndrew Thompson usbd_transfer_stop(f->xfer[1]); 40002ac6454SAndrew Thompson } 40102ac6454SAndrew Thompson 40202ac6454SAndrew Thompson static void 4035b3bb704SAndrew Thompson ugen_ctrl_read_callback(struct usb_xfer *xfer, usb_error_t error) 40402ac6454SAndrew Thompson { 405ed6d949aSAndrew Thompson struct usb_fifo *f = usbd_xfer_softc(xfer); 406760bc48eSAndrew Thompson struct usb_mbuf *m; 40702ac6454SAndrew Thompson 40802ac6454SAndrew Thompson DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); 40902ac6454SAndrew Thompson 41002ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 41102ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 41202ac6454SAndrew Thompson if (xfer->actlen == 0) { 41302ac6454SAndrew Thompson if (f->fifo_zlp != 4) { 41402ac6454SAndrew Thompson f->fifo_zlp++; 41502ac6454SAndrew Thompson } else { 41602ac6454SAndrew Thompson /* 41702ac6454SAndrew Thompson * Throttle a little bit we have multiple ZLPs 41802ac6454SAndrew Thompson * in a row! 41902ac6454SAndrew Thompson */ 42002ac6454SAndrew Thompson xfer->interval = 64; /* ms */ 42102ac6454SAndrew Thompson } 42202ac6454SAndrew Thompson } else { 42302ac6454SAndrew Thompson /* clear throttle */ 42402ac6454SAndrew Thompson xfer->interval = 0; 42502ac6454SAndrew Thompson f->fifo_zlp = 0; 42602ac6454SAndrew Thompson } 427a593f6b8SAndrew Thompson usb_fifo_put_data(f, xfer->frbuffers, 0, 42802ac6454SAndrew Thompson xfer->actlen, 1); 42902ac6454SAndrew Thompson 43002ac6454SAndrew Thompson case USB_ST_SETUP: 43102ac6454SAndrew Thompson if (f->flag_stall) { 432a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[1]); 43302ac6454SAndrew Thompson break; 43402ac6454SAndrew Thompson } 43502ac6454SAndrew Thompson USB_IF_POLL(&f->free_q, m); 43602ac6454SAndrew Thompson if (m) { 437ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 438a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 43902ac6454SAndrew Thompson } 44002ac6454SAndrew Thompson break; 44102ac6454SAndrew Thompson 44202ac6454SAndrew Thompson default: /* Error */ 44302ac6454SAndrew Thompson if (xfer->error != USB_ERR_CANCELLED) { 44401cf7831SAndrew Thompson /* send a zero length packet to userland */ 445a593f6b8SAndrew Thompson usb_fifo_put_data(f, xfer->frbuffers, 0, 0, 1); 44602ac6454SAndrew Thompson f->flag_stall = 1; 44702ac6454SAndrew Thompson f->fifo_zlp = 0; 448a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[1]); 44902ac6454SAndrew Thompson } 45002ac6454SAndrew Thompson break; 45102ac6454SAndrew Thompson } 45202ac6454SAndrew Thompson } 45302ac6454SAndrew Thompson 45402ac6454SAndrew Thompson static void 4555b3bb704SAndrew Thompson ugen_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) 45602ac6454SAndrew Thompson { 457ed6d949aSAndrew Thompson struct usb_fifo *f = usbd_xfer_softc(xfer); 458e0a69b51SAndrew Thompson usb_frlength_t actlen; 45902ac6454SAndrew Thompson 46002ac6454SAndrew Thompson DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); 46102ac6454SAndrew Thompson 46202ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 46302ac6454SAndrew Thompson case USB_ST_SETUP: 46402ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 46502ac6454SAndrew Thompson /* 46602ac6454SAndrew Thompson * If writing is in stall, just jump to clear stall 46702ac6454SAndrew Thompson * callback and solve the situation. 46802ac6454SAndrew Thompson */ 46902ac6454SAndrew Thompson if (f->flag_stall) { 470a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[1]); 47102ac6454SAndrew Thompson break; 47202ac6454SAndrew Thompson } 47302ac6454SAndrew Thompson /* 47402ac6454SAndrew Thompson * Write data, setup and perform hardware transfer. 47502ac6454SAndrew Thompson */ 476a593f6b8SAndrew Thompson if (usb_fifo_get_data(f, xfer->frbuffers, 0, 47702ac6454SAndrew Thompson xfer->max_data_length, &actlen, 0)) { 478ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, actlen); 479a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 48002ac6454SAndrew Thompson } 48102ac6454SAndrew Thompson break; 48202ac6454SAndrew Thompson 48302ac6454SAndrew Thompson default: /* Error */ 48402ac6454SAndrew Thompson if (xfer->error != USB_ERR_CANCELLED) { 48502ac6454SAndrew Thompson f->flag_stall = 1; 486a593f6b8SAndrew Thompson usbd_transfer_start(f->xfer[1]); 48702ac6454SAndrew Thompson } 48802ac6454SAndrew Thompson break; 48902ac6454SAndrew Thompson } 49002ac6454SAndrew Thompson } 49102ac6454SAndrew Thompson 49202ac6454SAndrew Thompson static void 493ed6d949aSAndrew Thompson ugen_read_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 49402ac6454SAndrew Thompson { 495ed6d949aSAndrew Thompson struct usb_fifo *f = usbd_xfer_softc(xfer); 496760bc48eSAndrew Thompson struct usb_xfer *xfer_other = f->xfer[0]; 49702ac6454SAndrew Thompson 49802ac6454SAndrew Thompson if (f->flag_stall == 0) { 49902ac6454SAndrew Thompson /* nothing to do */ 50002ac6454SAndrew Thompson return; 50102ac6454SAndrew Thompson } 502a593f6b8SAndrew Thompson if (usbd_clear_stall_callback(xfer, xfer_other)) { 50302ac6454SAndrew Thompson DPRINTFN(5, "f=%p: stall cleared\n", f); 50402ac6454SAndrew Thompson f->flag_stall = 0; 505a593f6b8SAndrew Thompson usbd_transfer_start(xfer_other); 50602ac6454SAndrew Thompson } 50702ac6454SAndrew Thompson } 50802ac6454SAndrew Thompson 50902ac6454SAndrew Thompson static void 510ed6d949aSAndrew Thompson ugen_write_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 51102ac6454SAndrew Thompson { 512ed6d949aSAndrew Thompson struct usb_fifo *f = usbd_xfer_softc(xfer); 513760bc48eSAndrew Thompson struct usb_xfer *xfer_other = f->xfer[0]; 51402ac6454SAndrew Thompson 51502ac6454SAndrew Thompson if (f->flag_stall == 0) { 51602ac6454SAndrew Thompson /* nothing to do */ 51702ac6454SAndrew Thompson return; 51802ac6454SAndrew Thompson } 519a593f6b8SAndrew Thompson if (usbd_clear_stall_callback(xfer, xfer_other)) { 52002ac6454SAndrew Thompson DPRINTFN(5, "f=%p: stall cleared\n", f); 52102ac6454SAndrew Thompson f->flag_stall = 0; 522a593f6b8SAndrew Thompson usbd_transfer_start(xfer_other); 52302ac6454SAndrew Thompson } 52402ac6454SAndrew Thompson } 52502ac6454SAndrew Thompson 52602ac6454SAndrew Thompson static void 527ed6d949aSAndrew Thompson ugen_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error) 52802ac6454SAndrew Thompson { 529ed6d949aSAndrew Thompson struct usb_fifo *f = usbd_xfer_softc(xfer); 530e0a69b51SAndrew Thompson usb_frlength_t offset; 531e0a69b51SAndrew Thompson usb_frcount_t n; 53202ac6454SAndrew Thompson 53302ac6454SAndrew Thompson DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); 53402ac6454SAndrew Thompson 53502ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 53602ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 53702ac6454SAndrew Thompson 53802ac6454SAndrew Thompson DPRINTFN(6, "actlen=%d\n", xfer->actlen); 53902ac6454SAndrew Thompson 54002ac6454SAndrew Thompson offset = 0; 54102ac6454SAndrew Thompson 54202ac6454SAndrew Thompson for (n = 0; n != xfer->aframes; n++) { 543a593f6b8SAndrew Thompson usb_fifo_put_data(f, xfer->frbuffers, offset, 54402ac6454SAndrew Thompson xfer->frlengths[n], 1); 54502ac6454SAndrew Thompson offset += xfer->max_frame_size; 54602ac6454SAndrew Thompson } 54702ac6454SAndrew Thompson 54802ac6454SAndrew Thompson case USB_ST_SETUP: 54902ac6454SAndrew Thompson tr_setup: 55002ac6454SAndrew Thompson for (n = 0; n != xfer->nframes; n++) { 55102ac6454SAndrew Thompson /* setup size for next transfer */ 552ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, xfer->max_frame_size); 55302ac6454SAndrew Thompson } 554a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 55502ac6454SAndrew Thompson break; 55602ac6454SAndrew Thompson 55702ac6454SAndrew Thompson default: /* Error */ 55802ac6454SAndrew Thompson if (xfer->error == USB_ERR_CANCELLED) { 55902ac6454SAndrew Thompson break; 56002ac6454SAndrew Thompson } 56102ac6454SAndrew Thompson goto tr_setup; 56202ac6454SAndrew Thompson } 56302ac6454SAndrew Thompson } 56402ac6454SAndrew Thompson 56502ac6454SAndrew Thompson static void 566ed6d949aSAndrew Thompson ugen_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error) 56702ac6454SAndrew Thompson { 568ed6d949aSAndrew Thompson struct usb_fifo *f = usbd_xfer_softc(xfer); 569e0a69b51SAndrew Thompson usb_frlength_t actlen; 570e0a69b51SAndrew Thompson usb_frlength_t offset; 571e0a69b51SAndrew Thompson usb_frcount_t n; 57202ac6454SAndrew Thompson 57302ac6454SAndrew Thompson DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); 57402ac6454SAndrew Thompson 57502ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 57602ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 57702ac6454SAndrew Thompson case USB_ST_SETUP: 57802ac6454SAndrew Thompson tr_setup: 57902ac6454SAndrew Thompson offset = 0; 58002ac6454SAndrew Thompson for (n = 0; n != xfer->nframes; n++) { 581a593f6b8SAndrew Thompson if (usb_fifo_get_data(f, xfer->frbuffers, offset, 58202ac6454SAndrew Thompson xfer->max_frame_size, &actlen, 1)) { 583ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, actlen); 58402ac6454SAndrew Thompson offset += actlen; 58502ac6454SAndrew Thompson } else { 58602ac6454SAndrew Thompson break; 58702ac6454SAndrew Thompson } 58802ac6454SAndrew Thompson } 58902ac6454SAndrew Thompson 59002ac6454SAndrew Thompson for (; n != xfer->nframes; n++) { 59102ac6454SAndrew Thompson /* fill in zero frames */ 592ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, 0); 59302ac6454SAndrew Thompson } 594a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 59502ac6454SAndrew Thompson break; 59602ac6454SAndrew Thompson 59702ac6454SAndrew Thompson default: /* Error */ 59802ac6454SAndrew Thompson if (xfer->error == USB_ERR_CANCELLED) { 59902ac6454SAndrew Thompson break; 60002ac6454SAndrew Thompson } 60102ac6454SAndrew Thompson goto tr_setup; 60202ac6454SAndrew Thompson } 60302ac6454SAndrew Thompson } 60402ac6454SAndrew Thompson 60502ac6454SAndrew Thompson static int 606760bc48eSAndrew Thompson ugen_set_config(struct usb_fifo *f, uint8_t index) 60702ac6454SAndrew Thompson { 60802ac6454SAndrew Thompson DPRINTFN(2, "index %u\n", index); 60902ac6454SAndrew Thompson 610f29a0724SAndrew Thompson if (f->udev->flags.usb_mode != USB_MODE_HOST) { 61102ac6454SAndrew Thompson /* not possible in device side mode */ 61202ac6454SAndrew Thompson return (ENOTTY); 61302ac6454SAndrew Thompson } 61402ac6454SAndrew Thompson if (f->udev->curr_config_index == index) { 61502ac6454SAndrew Thompson /* no change needed */ 61602ac6454SAndrew Thompson return (0); 61702ac6454SAndrew Thompson } 61802ac6454SAndrew Thompson /* make sure all FIFO's are gone */ 61902ac6454SAndrew Thompson /* else there can be a deadlock */ 62002ac6454SAndrew Thompson if (ugen_fs_uninit(f)) { 62102ac6454SAndrew Thompson /* ignore any errors */ 62202ac6454SAndrew Thompson DPRINTFN(6, "no FIFOs\n"); 62302ac6454SAndrew Thompson } 62402ac6454SAndrew Thompson /* change setting - will free generic FIFOs, if any */ 625a593f6b8SAndrew Thompson if (usbd_set_config_index(f->udev, index)) { 62602ac6454SAndrew Thompson return (EIO); 62702ac6454SAndrew Thompson } 62802ac6454SAndrew Thompson /* probe and attach */ 629a593f6b8SAndrew Thompson if (usb_probe_and_attach(f->udev, USB_IFACE_INDEX_ANY)) { 63002ac6454SAndrew Thompson return (EIO); 63102ac6454SAndrew Thompson } 63202ac6454SAndrew Thompson return (0); 63302ac6454SAndrew Thompson } 63402ac6454SAndrew Thompson 63502ac6454SAndrew Thompson static int 636760bc48eSAndrew Thompson ugen_set_interface(struct usb_fifo *f, 63702ac6454SAndrew Thompson uint8_t iface_index, uint8_t alt_index) 63802ac6454SAndrew Thompson { 63902ac6454SAndrew Thompson DPRINTFN(2, "%u, %u\n", iface_index, alt_index); 64002ac6454SAndrew Thompson 641f29a0724SAndrew Thompson if (f->udev->flags.usb_mode != USB_MODE_HOST) { 64202ac6454SAndrew Thompson /* not possible in device side mode */ 64302ac6454SAndrew Thompson return (ENOTTY); 64402ac6454SAndrew Thompson } 64502ac6454SAndrew Thompson /* make sure all FIFO's are gone */ 64602ac6454SAndrew Thompson /* else there can be a deadlock */ 64702ac6454SAndrew Thompson if (ugen_fs_uninit(f)) { 64802ac6454SAndrew Thompson /* ignore any errors */ 64902ac6454SAndrew Thompson DPRINTFN(6, "no FIFOs\n"); 65002ac6454SAndrew Thompson } 65102ac6454SAndrew Thompson /* change setting - will free generic FIFOs, if any */ 652a593f6b8SAndrew Thompson if (usbd_set_alt_interface_index(f->udev, iface_index, alt_index)) { 65302ac6454SAndrew Thompson return (EIO); 65402ac6454SAndrew Thompson } 65502ac6454SAndrew Thompson /* probe and attach */ 656a593f6b8SAndrew Thompson if (usb_probe_and_attach(f->udev, iface_index)) { 65702ac6454SAndrew Thompson return (EIO); 65802ac6454SAndrew Thompson } 65902ac6454SAndrew Thompson return (0); 66002ac6454SAndrew Thompson } 66102ac6454SAndrew Thompson 66202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 66302ac6454SAndrew Thompson * ugen_get_cdesc 66402ac6454SAndrew Thompson * 66502ac6454SAndrew Thompson * This function will retrieve the complete configuration descriptor 66602ac6454SAndrew Thompson * at the given index. 66702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 66802ac6454SAndrew Thompson static int 669760bc48eSAndrew Thompson ugen_get_cdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd) 67002ac6454SAndrew Thompson { 671760bc48eSAndrew Thompson struct usb_config_descriptor *cdesc; 672760bc48eSAndrew Thompson struct usb_device *udev = f->udev; 67302ac6454SAndrew Thompson int error; 67402ac6454SAndrew Thompson uint16_t len; 67502ac6454SAndrew Thompson uint8_t free_data; 67602ac6454SAndrew Thompson 67702ac6454SAndrew Thompson DPRINTFN(6, "\n"); 67802ac6454SAndrew Thompson 67902ac6454SAndrew Thompson if (ugd->ugd_data == NULL) { 68002ac6454SAndrew Thompson /* userland pointer should not be zero */ 68102ac6454SAndrew Thompson return (EINVAL); 68202ac6454SAndrew Thompson } 68302ac6454SAndrew Thompson if ((ugd->ugd_config_index == USB_UNCONFIG_INDEX) || 68402ac6454SAndrew Thompson (ugd->ugd_config_index == udev->curr_config_index)) { 685a593f6b8SAndrew Thompson cdesc = usbd_get_config_descriptor(udev); 68602ac6454SAndrew Thompson if (cdesc == NULL) { 68702ac6454SAndrew Thompson return (ENXIO); 68802ac6454SAndrew Thompson } 68902ac6454SAndrew Thompson free_data = 0; 69002ac6454SAndrew Thompson 69102ac6454SAndrew Thompson } else { 692a593f6b8SAndrew Thompson if (usbd_req_get_config_desc_full(udev, 693e2805033SAndrew Thompson NULL, &cdesc, M_USBDEV, 69402ac6454SAndrew Thompson ugd->ugd_config_index)) { 69502ac6454SAndrew Thompson return (ENXIO); 69602ac6454SAndrew Thompson } 69702ac6454SAndrew Thompson free_data = 1; 69802ac6454SAndrew Thompson } 69902ac6454SAndrew Thompson 70002ac6454SAndrew Thompson len = UGETW(cdesc->wTotalLength); 70102ac6454SAndrew Thompson if (len > ugd->ugd_maxlen) { 70202ac6454SAndrew Thompson len = ugd->ugd_maxlen; 70302ac6454SAndrew Thompson } 70402ac6454SAndrew Thompson DPRINTFN(6, "len=%u\n", len); 70502ac6454SAndrew Thompson 70602ac6454SAndrew Thompson ugd->ugd_actlen = len; 70702ac6454SAndrew Thompson ugd->ugd_offset = 0; 70802ac6454SAndrew Thompson 70902ac6454SAndrew Thompson error = copyout(cdesc, ugd->ugd_data, len); 71002ac6454SAndrew Thompson 71102ac6454SAndrew Thompson if (free_data) { 71202ac6454SAndrew Thompson free(cdesc, M_USBDEV); 71302ac6454SAndrew Thompson } 71402ac6454SAndrew Thompson return (error); 71502ac6454SAndrew Thompson } 71602ac6454SAndrew Thompson 71702ac6454SAndrew Thompson static int 718760bc48eSAndrew Thompson ugen_get_sdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd) 71902ac6454SAndrew Thompson { 72002ac6454SAndrew Thompson void *ptr = f->udev->bus->scratch[0].data; 72102ac6454SAndrew Thompson uint16_t size = sizeof(f->udev->bus->scratch[0].data); 72202ac6454SAndrew Thompson int error; 72302ac6454SAndrew Thompson 724a593f6b8SAndrew Thompson if (usbd_req_get_string_desc(f->udev, NULL, ptr, 72502ac6454SAndrew Thompson size, ugd->ugd_lang_id, ugd->ugd_string_index)) { 72602ac6454SAndrew Thompson error = EINVAL; 72702ac6454SAndrew Thompson } else { 72802ac6454SAndrew Thompson 72902ac6454SAndrew Thompson if (size > ((uint8_t *)ptr)[0]) { 73002ac6454SAndrew Thompson size = ((uint8_t *)ptr)[0]; 73102ac6454SAndrew Thompson } 73202ac6454SAndrew Thompson if (size > ugd->ugd_maxlen) { 73302ac6454SAndrew Thompson size = ugd->ugd_maxlen; 73402ac6454SAndrew Thompson } 73502ac6454SAndrew Thompson ugd->ugd_actlen = size; 73602ac6454SAndrew Thompson ugd->ugd_offset = 0; 73702ac6454SAndrew Thompson 73802ac6454SAndrew Thompson error = copyout(ptr, ugd->ugd_data, size); 73902ac6454SAndrew Thompson } 74002ac6454SAndrew Thompson return (error); 74102ac6454SAndrew Thompson } 74202ac6454SAndrew Thompson 74302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 74402ac6454SAndrew Thompson * ugen_get_iface_driver 74502ac6454SAndrew Thompson * 74602ac6454SAndrew Thompson * This function generates an USB interface description for userland. 74702ac6454SAndrew Thompson * 74802ac6454SAndrew Thompson * Returns: 74902ac6454SAndrew Thompson * 0: Success 75002ac6454SAndrew Thompson * Else: Failure 75102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 75202ac6454SAndrew Thompson static int 753760bc48eSAndrew Thompson ugen_get_iface_driver(struct usb_fifo *f, struct usb_gen_descriptor *ugd) 75402ac6454SAndrew Thompson { 755760bc48eSAndrew Thompson struct usb_device *udev = f->udev; 756760bc48eSAndrew Thompson struct usb_interface *iface; 75702ac6454SAndrew Thompson const char *ptr; 75802ac6454SAndrew Thompson const char *desc; 75902ac6454SAndrew Thompson unsigned int len; 76002ac6454SAndrew Thompson unsigned int maxlen; 76102ac6454SAndrew Thompson char buf[128]; 76202ac6454SAndrew Thompson int error; 76302ac6454SAndrew Thompson 76402ac6454SAndrew Thompson DPRINTFN(6, "\n"); 76502ac6454SAndrew Thompson 76602ac6454SAndrew Thompson if ((ugd->ugd_data == NULL) || (ugd->ugd_maxlen == 0)) { 76702ac6454SAndrew Thompson /* userland pointer should not be zero */ 76802ac6454SAndrew Thompson return (EINVAL); 76902ac6454SAndrew Thompson } 77002ac6454SAndrew Thompson 771a593f6b8SAndrew Thompson iface = usbd_get_iface(udev, ugd->ugd_iface_index); 77202ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) { 77302ac6454SAndrew Thompson /* invalid interface index */ 77402ac6454SAndrew Thompson return (EINVAL); 77502ac6454SAndrew Thompson } 77602ac6454SAndrew Thompson 77702ac6454SAndrew Thompson /* read out device nameunit string, if any */ 77802ac6454SAndrew Thompson if ((iface->subdev != NULL) && 77902ac6454SAndrew Thompson device_is_attached(iface->subdev) && 78002ac6454SAndrew Thompson (ptr = device_get_nameunit(iface->subdev)) && 78102ac6454SAndrew Thompson (desc = device_get_desc(iface->subdev))) { 78202ac6454SAndrew Thompson 78302ac6454SAndrew Thompson /* print description */ 78402ac6454SAndrew Thompson snprintf(buf, sizeof(buf), "%s: <%s>", ptr, desc); 78502ac6454SAndrew Thompson 78602ac6454SAndrew Thompson /* range checks */ 78702ac6454SAndrew Thompson maxlen = ugd->ugd_maxlen - 1; 78802ac6454SAndrew Thompson len = strlen(buf); 78902ac6454SAndrew Thompson if (len > maxlen) 79002ac6454SAndrew Thompson len = maxlen; 79102ac6454SAndrew Thompson 79202ac6454SAndrew Thompson /* update actual length, including terminating zero */ 79302ac6454SAndrew Thompson ugd->ugd_actlen = len + 1; 79402ac6454SAndrew Thompson 79502ac6454SAndrew Thompson /* copy out interface description */ 79602ac6454SAndrew Thompson error = copyout(buf, ugd->ugd_data, ugd->ugd_actlen); 79702ac6454SAndrew Thompson } else { 79802ac6454SAndrew Thompson /* zero length string is default */ 79902ac6454SAndrew Thompson error = copyout("", ugd->ugd_data, 1); 80002ac6454SAndrew Thompson } 80102ac6454SAndrew Thompson return (error); 80202ac6454SAndrew Thompson } 80302ac6454SAndrew Thompson 80402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 805a593f6b8SAndrew Thompson * usb_gen_fill_deviceinfo 80602ac6454SAndrew Thompson * 80702ac6454SAndrew Thompson * This function dumps information about an USB device to the 80802ac6454SAndrew Thompson * structure pointed to by the "di" argument. 80902ac6454SAndrew Thompson * 81002ac6454SAndrew Thompson * Returns: 81102ac6454SAndrew Thompson * 0: Success 81202ac6454SAndrew Thompson * Else: Failure 81302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 81402ac6454SAndrew Thompson static int 815a593f6b8SAndrew Thompson usb_gen_fill_deviceinfo(struct usb_fifo *f, struct usb_device_info *di) 81602ac6454SAndrew Thompson { 817760bc48eSAndrew Thompson struct usb_device *udev; 818760bc48eSAndrew Thompson struct usb_device *hub; 81902ac6454SAndrew Thompson 82002ac6454SAndrew Thompson udev = f->udev; 82102ac6454SAndrew Thompson 82202ac6454SAndrew Thompson bzero(di, sizeof(di[0])); 82302ac6454SAndrew Thompson 82402ac6454SAndrew Thompson di->udi_bus = device_get_unit(udev->bus->bdev); 82502ac6454SAndrew Thompson di->udi_addr = udev->address; 82602ac6454SAndrew Thompson di->udi_index = udev->device_index; 827ae538d85SAndrew Thompson strlcpy(di->udi_serial, usb_get_serial(udev), sizeof(di->udi_serial)); 828ae538d85SAndrew Thompson strlcpy(di->udi_vendor, usb_get_manufacturer(udev), sizeof(di->udi_vendor)); 829ae538d85SAndrew Thompson strlcpy(di->udi_product, usb_get_product(udev), sizeof(di->udi_product)); 830a593f6b8SAndrew Thompson usb_printbcd(di->udi_release, sizeof(di->udi_release), 83102ac6454SAndrew Thompson UGETW(udev->ddesc.bcdDevice)); 83202ac6454SAndrew Thompson di->udi_vendorNo = UGETW(udev->ddesc.idVendor); 83302ac6454SAndrew Thompson di->udi_productNo = UGETW(udev->ddesc.idProduct); 83402ac6454SAndrew Thompson di->udi_releaseNo = UGETW(udev->ddesc.bcdDevice); 83502ac6454SAndrew Thompson di->udi_class = udev->ddesc.bDeviceClass; 83602ac6454SAndrew Thompson di->udi_subclass = udev->ddesc.bDeviceSubClass; 83702ac6454SAndrew Thompson di->udi_protocol = udev->ddesc.bDeviceProtocol; 83802ac6454SAndrew Thompson di->udi_config_no = udev->curr_config_no; 83902ac6454SAndrew Thompson di->udi_config_index = udev->curr_config_index; 84002ac6454SAndrew Thompson di->udi_power = udev->flags.self_powered ? 0 : udev->power; 84102ac6454SAndrew Thompson di->udi_speed = udev->speed; 842f29a0724SAndrew Thompson di->udi_mode = udev->flags.usb_mode; 84302ac6454SAndrew Thompson di->udi_power_mode = udev->power_mode; 844ec8f3127SAndrew Thompson di->udi_suspended = udev->flags.peer_suspended; 84502ac6454SAndrew Thompson 84602ac6454SAndrew Thompson hub = udev->parent_hub; 84702ac6454SAndrew Thompson if (hub) { 84802ac6454SAndrew Thompson di->udi_hubaddr = hub->address; 84902ac6454SAndrew Thompson di->udi_hubindex = hub->device_index; 85002ac6454SAndrew Thompson di->udi_hubport = udev->port_no; 85102ac6454SAndrew Thompson } 85202ac6454SAndrew Thompson return (0); 85302ac6454SAndrew Thompson } 85402ac6454SAndrew Thompson 85502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 85602ac6454SAndrew Thompson * ugen_check_request 85702ac6454SAndrew Thompson * 85802ac6454SAndrew Thompson * Return values: 85902ac6454SAndrew Thompson * 0: Access allowed 86002ac6454SAndrew Thompson * Else: No access 86102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 86202ac6454SAndrew Thompson static int 863760bc48eSAndrew Thompson ugen_check_request(struct usb_device *udev, struct usb_device_request *req) 86402ac6454SAndrew Thompson { 865ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 86602ac6454SAndrew Thompson int error; 86702ac6454SAndrew Thompson 86802ac6454SAndrew Thompson /* 86902ac6454SAndrew Thompson * Avoid requests that would damage the bus integrity: 87002ac6454SAndrew Thompson */ 87102ac6454SAndrew Thompson if (((req->bmRequestType == UT_WRITE_DEVICE) && 87202ac6454SAndrew Thompson (req->bRequest == UR_SET_ADDRESS)) || 87302ac6454SAndrew Thompson ((req->bmRequestType == UT_WRITE_DEVICE) && 87402ac6454SAndrew Thompson (req->bRequest == UR_SET_CONFIG)) || 87502ac6454SAndrew Thompson ((req->bmRequestType == UT_WRITE_INTERFACE) && 87602ac6454SAndrew Thompson (req->bRequest == UR_SET_INTERFACE))) { 87702ac6454SAndrew Thompson /* 87802ac6454SAndrew Thompson * These requests can be useful for testing USB drivers. 87902ac6454SAndrew Thompson */ 88002ac6454SAndrew Thompson error = priv_check(curthread, PRIV_DRIVER); 88102ac6454SAndrew Thompson if (error) { 88202ac6454SAndrew Thompson return (error); 88302ac6454SAndrew Thompson } 88402ac6454SAndrew Thompson } 88502ac6454SAndrew Thompson /* 88602ac6454SAndrew Thompson * Special case - handle clearing of stall 88702ac6454SAndrew Thompson */ 88802ac6454SAndrew Thompson if (req->bmRequestType == UT_WRITE_ENDPOINT) { 88902ac6454SAndrew Thompson 890a593f6b8SAndrew Thompson ep = usbd_get_ep_by_addr(udev, req->wIndex[0]); 891ae60fdfbSAndrew Thompson if (ep == NULL) { 89202ac6454SAndrew Thompson return (EINVAL); 89302ac6454SAndrew Thompson } 89402ac6454SAndrew Thompson if ((req->bRequest == UR_CLEAR_FEATURE) && 89502ac6454SAndrew Thompson (UGETW(req->wValue) == UF_ENDPOINT_HALT)) { 896a593f6b8SAndrew Thompson usbd_clear_data_toggle(udev, ep); 89702ac6454SAndrew Thompson } 89802ac6454SAndrew Thompson } 89902ac6454SAndrew Thompson /* TODO: add more checks to verify the interface index */ 90002ac6454SAndrew Thompson 90102ac6454SAndrew Thompson return (0); 90202ac6454SAndrew Thompson } 90302ac6454SAndrew Thompson 90402ac6454SAndrew Thompson int 905760bc48eSAndrew Thompson ugen_do_request(struct usb_fifo *f, struct usb_ctl_request *ur) 90602ac6454SAndrew Thompson { 90702ac6454SAndrew Thompson int error; 90802ac6454SAndrew Thompson uint16_t len; 90902ac6454SAndrew Thompson uint16_t actlen; 91002ac6454SAndrew Thompson 91102ac6454SAndrew Thompson if (ugen_check_request(f->udev, &ur->ucr_request)) { 91202ac6454SAndrew Thompson return (EPERM); 91302ac6454SAndrew Thompson } 91402ac6454SAndrew Thompson len = UGETW(ur->ucr_request.wLength); 91502ac6454SAndrew Thompson 91602ac6454SAndrew Thompson /* check if "ucr_data" is valid */ 91702ac6454SAndrew Thompson if (len != 0) { 91802ac6454SAndrew Thompson if (ur->ucr_data == NULL) { 91902ac6454SAndrew Thompson return (EFAULT); 92002ac6454SAndrew Thompson } 92102ac6454SAndrew Thompson } 92202ac6454SAndrew Thompson /* do the USB request */ 923a593f6b8SAndrew Thompson error = usbd_do_request_flags 92402ac6454SAndrew Thompson (f->udev, NULL, &ur->ucr_request, ur->ucr_data, 92502ac6454SAndrew Thompson (ur->ucr_flags & USB_SHORT_XFER_OK) | 92602ac6454SAndrew Thompson USB_USER_DATA_PTR, &actlen, 92702ac6454SAndrew Thompson USB_DEFAULT_TIMEOUT); 92802ac6454SAndrew Thompson 92902ac6454SAndrew Thompson ur->ucr_actlen = actlen; 93002ac6454SAndrew Thompson 93102ac6454SAndrew Thompson if (error) { 93202ac6454SAndrew Thompson error = EIO; 93302ac6454SAndrew Thompson } 93402ac6454SAndrew Thompson return (error); 93502ac6454SAndrew Thompson } 93602ac6454SAndrew Thompson 93702ac6454SAndrew Thompson /*------------------------------------------------------------------------ 93802ac6454SAndrew Thompson * ugen_re_enumerate 93902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 94002ac6454SAndrew Thompson static int 941760bc48eSAndrew Thompson ugen_re_enumerate(struct usb_fifo *f) 94202ac6454SAndrew Thompson { 943760bc48eSAndrew Thompson struct usb_device *udev = f->udev; 94402ac6454SAndrew Thompson int error; 94502ac6454SAndrew Thompson 94602ac6454SAndrew Thompson /* 94702ac6454SAndrew Thompson * This request can be useful for testing USB drivers: 94802ac6454SAndrew Thompson */ 94902ac6454SAndrew Thompson error = priv_check(curthread, PRIV_DRIVER); 95002ac6454SAndrew Thompson if (error) { 95102ac6454SAndrew Thompson return (error); 95202ac6454SAndrew Thompson } 9538f9750b7SHans Petter Selasky if (udev->flags.usb_mode != USB_MODE_HOST) { 9548f9750b7SHans Petter Selasky /* not possible in device side mode */ 95556b57046SHans Petter Selasky DPRINTFN(6, "device mode\n"); 9568f9750b7SHans Petter Selasky return (ENOTTY); 95702ac6454SAndrew Thompson } 95856b57046SHans Petter Selasky if (udev->parent_hub == NULL) { 95956b57046SHans Petter Selasky /* the root HUB cannot be re-enumerated */ 96056b57046SHans Petter Selasky DPRINTFN(6, "cannot reset root HUB\n"); 96156b57046SHans Petter Selasky return (EINVAL); 96256b57046SHans Petter Selasky } 9638f9750b7SHans Petter Selasky /* make sure all FIFO's are gone */ 9648f9750b7SHans Petter Selasky /* else there can be a deadlock */ 9658f9750b7SHans Petter Selasky if (ugen_fs_uninit(f)) { 9668f9750b7SHans Petter Selasky /* ignore any errors */ 9678f9750b7SHans Petter Selasky DPRINTFN(6, "no FIFOs\n"); 96802ac6454SAndrew Thompson } 9699eb0d702SHans Petter Selasky /* start re-enumeration of device */ 9709eb0d702SHans Petter Selasky usbd_start_re_enumerate(udev); 97102ac6454SAndrew Thompson return (0); 97202ac6454SAndrew Thompson } 97302ac6454SAndrew Thompson 97402ac6454SAndrew Thompson int 975760bc48eSAndrew Thompson ugen_fs_uninit(struct usb_fifo *f) 97602ac6454SAndrew Thompson { 97702ac6454SAndrew Thompson if (f->fs_xfer == NULL) { 97802ac6454SAndrew Thompson return (EINVAL); 97902ac6454SAndrew Thompson } 980a593f6b8SAndrew Thompson usbd_transfer_unsetup(f->fs_xfer, f->fs_ep_max); 98102ac6454SAndrew Thompson free(f->fs_xfer, M_USB); 98202ac6454SAndrew Thompson f->fs_xfer = NULL; 98302ac6454SAndrew Thompson f->fs_ep_max = 0; 98402ac6454SAndrew Thompson f->fs_ep_ptr = NULL; 98502ac6454SAndrew Thompson f->flag_iscomplete = 0; 986a593f6b8SAndrew Thompson usb_fifo_free_buffer(f); 98702ac6454SAndrew Thompson return (0); 98802ac6454SAndrew Thompson } 98902ac6454SAndrew Thompson 99002ac6454SAndrew Thompson static uint8_t 991760bc48eSAndrew Thompson ugen_fs_get_complete(struct usb_fifo *f, uint8_t *pindex) 99202ac6454SAndrew Thompson { 993760bc48eSAndrew Thompson struct usb_mbuf *m; 99402ac6454SAndrew Thompson 99502ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->used_q, m); 99602ac6454SAndrew Thompson 99702ac6454SAndrew Thompson if (m) { 99802ac6454SAndrew Thompson *pindex = *((uint8_t *)(m->cur_data_ptr)); 99902ac6454SAndrew Thompson 100002ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->free_q, m); 100102ac6454SAndrew Thompson 100202ac6454SAndrew Thompson return (0); /* success */ 100302ac6454SAndrew Thompson } else { 100402ac6454SAndrew Thompson 100502ac6454SAndrew Thompson *pindex = 0; /* fix compiler warning */ 100602ac6454SAndrew Thompson 100702ac6454SAndrew Thompson f->flag_iscomplete = 0; 100802ac6454SAndrew Thompson } 100902ac6454SAndrew Thompson return (1); /* failure */ 101002ac6454SAndrew Thompson } 101102ac6454SAndrew Thompson 101202ac6454SAndrew Thompson static void 1013760bc48eSAndrew Thompson ugen_fs_set_complete(struct usb_fifo *f, uint8_t index) 101402ac6454SAndrew Thompson { 1015760bc48eSAndrew Thompson struct usb_mbuf *m; 101602ac6454SAndrew Thompson 101702ac6454SAndrew Thompson USB_IF_DEQUEUE(&f->free_q, m); 101802ac6454SAndrew Thompson 101902ac6454SAndrew Thompson if (m == NULL) { 102002ac6454SAndrew Thompson /* can happen during close */ 102102ac6454SAndrew Thompson DPRINTF("out of buffers\n"); 102202ac6454SAndrew Thompson return; 102302ac6454SAndrew Thompson } 102402ac6454SAndrew Thompson USB_MBUF_RESET(m); 102502ac6454SAndrew Thompson 102602ac6454SAndrew Thompson *((uint8_t *)(m->cur_data_ptr)) = index; 102702ac6454SAndrew Thompson 102802ac6454SAndrew Thompson USB_IF_ENQUEUE(&f->used_q, m); 102902ac6454SAndrew Thompson 103002ac6454SAndrew Thompson f->flag_iscomplete = 1; 103102ac6454SAndrew Thompson 1032a593f6b8SAndrew Thompson usb_fifo_wakeup(f); 103302ac6454SAndrew Thompson } 103402ac6454SAndrew Thompson 103502ac6454SAndrew Thompson static int 1036760bc48eSAndrew Thompson ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index) 103702ac6454SAndrew Thompson { 1038760bc48eSAndrew Thompson struct usb_device_request *req; 1039760bc48eSAndrew Thompson struct usb_xfer *xfer; 1040760bc48eSAndrew Thompson struct usb_fs_endpoint fs_ep; 104102ac6454SAndrew Thompson void *uaddr; /* userland pointer */ 104202ac6454SAndrew Thompson void *kaddr; 1043e0a69b51SAndrew Thompson usb_frlength_t offset; 1044e0a69b51SAndrew Thompson usb_frlength_t rem; 1045e0a69b51SAndrew Thompson usb_frcount_t n; 104602ac6454SAndrew Thompson uint32_t length; 104702ac6454SAndrew Thompson int error; 104802ac6454SAndrew Thompson uint8_t isread; 104902ac6454SAndrew Thompson 105002ac6454SAndrew Thompson if (ep_index >= f->fs_ep_max) { 105102ac6454SAndrew Thompson return (EINVAL); 105202ac6454SAndrew Thompson } 105302ac6454SAndrew Thompson xfer = f->fs_xfer[ep_index]; 105402ac6454SAndrew Thompson if (xfer == NULL) { 105502ac6454SAndrew Thompson return (EINVAL); 105602ac6454SAndrew Thompson } 105702ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 1058a593f6b8SAndrew Thompson if (usbd_transfer_pending(xfer)) { 105902ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 106002ac6454SAndrew Thompson return (EBUSY); /* should not happen */ 106102ac6454SAndrew Thompson } 106202ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 106302ac6454SAndrew Thompson 106402ac6454SAndrew Thompson error = copyin(f->fs_ep_ptr + 106502ac6454SAndrew Thompson ep_index, &fs_ep, sizeof(fs_ep)); 106602ac6454SAndrew Thompson if (error) { 106702ac6454SAndrew Thompson return (error); 106802ac6454SAndrew Thompson } 106902ac6454SAndrew Thompson /* security checks */ 107002ac6454SAndrew Thompson 107102ac6454SAndrew Thompson if (fs_ep.nFrames > xfer->max_frame_count) { 107202ac6454SAndrew Thompson xfer->error = USB_ERR_INVAL; 107302ac6454SAndrew Thompson goto complete; 107402ac6454SAndrew Thompson } 107502ac6454SAndrew Thompson if (fs_ep.nFrames == 0) { 107602ac6454SAndrew Thompson xfer->error = USB_ERR_INVAL; 107702ac6454SAndrew Thompson goto complete; 107802ac6454SAndrew Thompson } 107902ac6454SAndrew Thompson error = copyin(fs_ep.ppBuffer, 108002ac6454SAndrew Thompson &uaddr, sizeof(uaddr)); 108102ac6454SAndrew Thompson if (error) { 108202ac6454SAndrew Thompson return (error); 108302ac6454SAndrew Thompson } 108402ac6454SAndrew Thompson /* reset first frame */ 1085ed6d949aSAndrew Thompson usbd_xfer_set_frame_offset(xfer, 0, 0); 108602ac6454SAndrew Thompson 108702ac6454SAndrew Thompson if (xfer->flags_int.control_xfr) { 108802ac6454SAndrew Thompson 108902ac6454SAndrew Thompson req = xfer->frbuffers[0].buffer; 109002ac6454SAndrew Thompson 109102ac6454SAndrew Thompson error = copyin(fs_ep.pLength, 109202ac6454SAndrew Thompson &length, sizeof(length)); 109302ac6454SAndrew Thompson if (error) { 109402ac6454SAndrew Thompson return (error); 109502ac6454SAndrew Thompson } 1096599ce1c3SAndrew Thompson if (length != sizeof(*req)) { 109702ac6454SAndrew Thompson xfer->error = USB_ERR_INVAL; 109802ac6454SAndrew Thompson goto complete; 109902ac6454SAndrew Thompson } 110002ac6454SAndrew Thompson if (length != 0) { 110102ac6454SAndrew Thompson error = copyin(uaddr, req, length); 110202ac6454SAndrew Thompson if (error) { 110302ac6454SAndrew Thompson return (error); 110402ac6454SAndrew Thompson } 110502ac6454SAndrew Thompson } 110602ac6454SAndrew Thompson if (ugen_check_request(f->udev, req)) { 110702ac6454SAndrew Thompson xfer->error = USB_ERR_INVAL; 110802ac6454SAndrew Thompson goto complete; 110902ac6454SAndrew Thompson } 1110ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, length); 111102ac6454SAndrew Thompson 111202ac6454SAndrew Thompson /* Host mode only ! */ 111302ac6454SAndrew Thompson if ((req->bmRequestType & 111402ac6454SAndrew Thompson (UT_READ | UT_WRITE)) == UT_READ) { 111502ac6454SAndrew Thompson isread = 1; 111602ac6454SAndrew Thompson } else { 111702ac6454SAndrew Thompson isread = 0; 111802ac6454SAndrew Thompson } 111902ac6454SAndrew Thompson n = 1; 112002ac6454SAndrew Thompson offset = sizeof(*req); 112102ac6454SAndrew Thompson 112202ac6454SAndrew Thompson } else { 112302ac6454SAndrew Thompson /* Device and Host mode */ 112402ac6454SAndrew Thompson if (USB_GET_DATA_ISREAD(xfer)) { 112502ac6454SAndrew Thompson isread = 1; 112602ac6454SAndrew Thompson } else { 112702ac6454SAndrew Thompson isread = 0; 112802ac6454SAndrew Thompson } 112902ac6454SAndrew Thompson n = 0; 113002ac6454SAndrew Thompson offset = 0; 113102ac6454SAndrew Thompson } 113202ac6454SAndrew Thompson 1133ed6d949aSAndrew Thompson rem = usbd_xfer_max_len(xfer); 113402ac6454SAndrew Thompson xfer->nframes = fs_ep.nFrames; 113502ac6454SAndrew Thompson xfer->timeout = fs_ep.timeout; 113602ac6454SAndrew Thompson if (xfer->timeout > 65535) { 113702ac6454SAndrew Thompson xfer->timeout = 65535; 113802ac6454SAndrew Thompson } 113902ac6454SAndrew Thompson if (fs_ep.flags & USB_FS_FLAG_SINGLE_SHORT_OK) 114002ac6454SAndrew Thompson xfer->flags.short_xfer_ok = 1; 114102ac6454SAndrew Thompson else 114202ac6454SAndrew Thompson xfer->flags.short_xfer_ok = 0; 114302ac6454SAndrew Thompson 114402ac6454SAndrew Thompson if (fs_ep.flags & USB_FS_FLAG_MULTI_SHORT_OK) 114502ac6454SAndrew Thompson xfer->flags.short_frames_ok = 1; 114602ac6454SAndrew Thompson else 114702ac6454SAndrew Thompson xfer->flags.short_frames_ok = 0; 114802ac6454SAndrew Thompson 114902ac6454SAndrew Thompson if (fs_ep.flags & USB_FS_FLAG_FORCE_SHORT) 115002ac6454SAndrew Thompson xfer->flags.force_short_xfer = 1; 115102ac6454SAndrew Thompson else 115202ac6454SAndrew Thompson xfer->flags.force_short_xfer = 0; 115302ac6454SAndrew Thompson 115402ac6454SAndrew Thompson if (fs_ep.flags & USB_FS_FLAG_CLEAR_STALL) 1155ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); 115602ac6454SAndrew Thompson else 115702ac6454SAndrew Thompson xfer->flags.stall_pipe = 0; 115802ac6454SAndrew Thompson 115902ac6454SAndrew Thompson for (; n != xfer->nframes; n++) { 116002ac6454SAndrew Thompson 116102ac6454SAndrew Thompson error = copyin(fs_ep.pLength + n, 116202ac6454SAndrew Thompson &length, sizeof(length)); 116302ac6454SAndrew Thompson if (error) { 116402ac6454SAndrew Thompson break; 116502ac6454SAndrew Thompson } 1166ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, length); 116702ac6454SAndrew Thompson 116802ac6454SAndrew Thompson if (length > rem) { 116902ac6454SAndrew Thompson xfer->error = USB_ERR_INVAL; 117002ac6454SAndrew Thompson goto complete; 117102ac6454SAndrew Thompson } 117202ac6454SAndrew Thompson rem -= length; 117302ac6454SAndrew Thompson 117402ac6454SAndrew Thompson if (!isread) { 117502ac6454SAndrew Thompson 117602ac6454SAndrew Thompson /* we need to know the source buffer */ 117702ac6454SAndrew Thompson error = copyin(fs_ep.ppBuffer + n, 117802ac6454SAndrew Thompson &uaddr, sizeof(uaddr)); 117902ac6454SAndrew Thompson if (error) { 118002ac6454SAndrew Thompson break; 118102ac6454SAndrew Thompson } 118202ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 118302ac6454SAndrew Thompson /* get kernel buffer address */ 118402ac6454SAndrew Thompson kaddr = xfer->frbuffers[0].buffer; 118502ac6454SAndrew Thompson kaddr = USB_ADD_BYTES(kaddr, offset); 118602ac6454SAndrew Thompson } else { 118702ac6454SAndrew Thompson /* set current frame offset */ 1188ed6d949aSAndrew Thompson usbd_xfer_set_frame_offset(xfer, offset, n); 118902ac6454SAndrew Thompson 119002ac6454SAndrew Thompson /* get kernel buffer address */ 119102ac6454SAndrew Thompson kaddr = xfer->frbuffers[n].buffer; 119202ac6454SAndrew Thompson } 119302ac6454SAndrew Thompson 119402ac6454SAndrew Thompson /* move data */ 119502ac6454SAndrew Thompson error = copyin(uaddr, kaddr, length); 119602ac6454SAndrew Thompson if (error) { 119702ac6454SAndrew Thompson break; 119802ac6454SAndrew Thompson } 119902ac6454SAndrew Thompson } 120002ac6454SAndrew Thompson offset += length; 120102ac6454SAndrew Thompson } 120202ac6454SAndrew Thompson return (error); 120302ac6454SAndrew Thompson 120402ac6454SAndrew Thompson complete: 120502ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 120602ac6454SAndrew Thompson ugen_fs_set_complete(f, ep_index); 120702ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 120802ac6454SAndrew Thompson return (0); 120902ac6454SAndrew Thompson } 121002ac6454SAndrew Thompson 121102ac6454SAndrew Thompson static int 1212760bc48eSAndrew Thompson ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index) 121302ac6454SAndrew Thompson { 1214760bc48eSAndrew Thompson struct usb_device_request *req; 1215760bc48eSAndrew Thompson struct usb_xfer *xfer; 1216760bc48eSAndrew Thompson struct usb_fs_endpoint fs_ep; 1217760bc48eSAndrew Thompson struct usb_fs_endpoint *fs_ep_uptr; /* userland ptr */ 121802ac6454SAndrew Thompson void *uaddr; /* userland ptr */ 121902ac6454SAndrew Thompson void *kaddr; 1220e0a69b51SAndrew Thompson usb_frlength_t offset; 1221e0a69b51SAndrew Thompson usb_frlength_t rem; 1222e0a69b51SAndrew Thompson usb_frcount_t n; 122302ac6454SAndrew Thompson uint32_t length; 122402ac6454SAndrew Thompson uint32_t temp; 122502ac6454SAndrew Thompson int error; 122602ac6454SAndrew Thompson uint8_t isread; 122702ac6454SAndrew Thompson 1228578d0effSAndrew Thompson if (ep_index >= f->fs_ep_max) 122902ac6454SAndrew Thompson return (EINVAL); 1230578d0effSAndrew Thompson 123102ac6454SAndrew Thompson xfer = f->fs_xfer[ep_index]; 1232578d0effSAndrew Thompson if (xfer == NULL) 123302ac6454SAndrew Thompson return (EINVAL); 1234578d0effSAndrew Thompson 123502ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 1236a593f6b8SAndrew Thompson if (usbd_transfer_pending(xfer)) { 123702ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 123802ac6454SAndrew Thompson return (EBUSY); /* should not happen */ 123902ac6454SAndrew Thompson } 124002ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 124102ac6454SAndrew Thompson 124202ac6454SAndrew Thompson fs_ep_uptr = f->fs_ep_ptr + ep_index; 124302ac6454SAndrew Thompson error = copyin(fs_ep_uptr, &fs_ep, sizeof(fs_ep)); 124402ac6454SAndrew Thompson if (error) { 124502ac6454SAndrew Thompson return (error); 124602ac6454SAndrew Thompson } 124702ac6454SAndrew Thompson fs_ep.status = xfer->error; 124802ac6454SAndrew Thompson fs_ep.aFrames = xfer->aframes; 124902ac6454SAndrew Thompson fs_ep.isoc_time_complete = xfer->isoc_time_complete; 125002ac6454SAndrew Thompson if (xfer->error) { 125102ac6454SAndrew Thompson goto complete; 125202ac6454SAndrew Thompson } 125302ac6454SAndrew Thompson if (xfer->flags_int.control_xfr) { 125402ac6454SAndrew Thompson req = xfer->frbuffers[0].buffer; 125502ac6454SAndrew Thompson 125602ac6454SAndrew Thompson /* Host mode only ! */ 125702ac6454SAndrew Thompson if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_READ) { 125802ac6454SAndrew Thompson isread = 1; 125902ac6454SAndrew Thompson } else { 126002ac6454SAndrew Thompson isread = 0; 126102ac6454SAndrew Thompson } 126202ac6454SAndrew Thompson if (xfer->nframes == 0) 126302ac6454SAndrew Thompson n = 0; /* should never happen */ 126402ac6454SAndrew Thompson else 126502ac6454SAndrew Thompson n = 1; 126602ac6454SAndrew Thompson } else { 126702ac6454SAndrew Thompson /* Device and Host mode */ 126802ac6454SAndrew Thompson if (USB_GET_DATA_ISREAD(xfer)) { 126902ac6454SAndrew Thompson isread = 1; 127002ac6454SAndrew Thompson } else { 127102ac6454SAndrew Thompson isread = 0; 127202ac6454SAndrew Thompson } 127302ac6454SAndrew Thompson n = 0; 127402ac6454SAndrew Thompson } 127502ac6454SAndrew Thompson 127602ac6454SAndrew Thompson /* Update lengths and copy out data */ 127702ac6454SAndrew Thompson 1278ed6d949aSAndrew Thompson rem = usbd_xfer_max_len(xfer); 127902ac6454SAndrew Thompson offset = 0; 128002ac6454SAndrew Thompson 128102ac6454SAndrew Thompson for (; n != xfer->nframes; n++) { 128202ac6454SAndrew Thompson 128302ac6454SAndrew Thompson /* get initial length into "temp" */ 128402ac6454SAndrew Thompson error = copyin(fs_ep.pLength + n, 128502ac6454SAndrew Thompson &temp, sizeof(temp)); 128602ac6454SAndrew Thompson if (error) { 128702ac6454SAndrew Thompson return (error); 128802ac6454SAndrew Thompson } 128902ac6454SAndrew Thompson if (temp > rem) { 129002ac6454SAndrew Thompson /* the userland length has been corrupted */ 129102ac6454SAndrew Thompson DPRINTF("corrupt userland length " 129202ac6454SAndrew Thompson "%u > %u\n", temp, rem); 129302ac6454SAndrew Thompson fs_ep.status = USB_ERR_INVAL; 129402ac6454SAndrew Thompson goto complete; 129502ac6454SAndrew Thompson } 129602ac6454SAndrew Thompson rem -= temp; 129702ac6454SAndrew Thompson 129802ac6454SAndrew Thompson /* get actual transfer length */ 129902ac6454SAndrew Thompson length = xfer->frlengths[n]; 130002ac6454SAndrew Thompson if (length > temp) { 130102ac6454SAndrew Thompson /* data overflow */ 130202ac6454SAndrew Thompson fs_ep.status = USB_ERR_INVAL; 130302ac6454SAndrew Thompson DPRINTF("data overflow %u > %u\n", 130402ac6454SAndrew Thompson length, temp); 130502ac6454SAndrew Thompson goto complete; 130602ac6454SAndrew Thompson } 130702ac6454SAndrew Thompson if (isread) { 130802ac6454SAndrew Thompson 130902ac6454SAndrew Thompson /* we need to know the destination buffer */ 131002ac6454SAndrew Thompson error = copyin(fs_ep.ppBuffer + n, 131102ac6454SAndrew Thompson &uaddr, sizeof(uaddr)); 131202ac6454SAndrew Thompson if (error) { 131302ac6454SAndrew Thompson return (error); 131402ac6454SAndrew Thompson } 131502ac6454SAndrew Thompson if (xfer->flags_int.isochronous_xfr) { 131602ac6454SAndrew Thompson /* only one frame buffer */ 131702ac6454SAndrew Thompson kaddr = USB_ADD_BYTES( 131802ac6454SAndrew Thompson xfer->frbuffers[0].buffer, offset); 131902ac6454SAndrew Thompson } else { 132002ac6454SAndrew Thompson /* multiple frame buffers */ 132102ac6454SAndrew Thompson kaddr = xfer->frbuffers[n].buffer; 132202ac6454SAndrew Thompson } 132302ac6454SAndrew Thompson 132402ac6454SAndrew Thompson /* move data */ 132502ac6454SAndrew Thompson error = copyout(kaddr, uaddr, length); 132602ac6454SAndrew Thompson if (error) { 132702ac6454SAndrew Thompson return (error); 132802ac6454SAndrew Thompson } 132902ac6454SAndrew Thompson } 133002ac6454SAndrew Thompson /* 133102ac6454SAndrew Thompson * Update offset according to initial length, which is 133202ac6454SAndrew Thompson * needed by isochronous transfers! 133302ac6454SAndrew Thompson */ 133402ac6454SAndrew Thompson offset += temp; 133502ac6454SAndrew Thompson 133602ac6454SAndrew Thompson /* update length */ 133702ac6454SAndrew Thompson error = copyout(&length, 133802ac6454SAndrew Thompson fs_ep.pLength + n, sizeof(length)); 133902ac6454SAndrew Thompson if (error) { 134002ac6454SAndrew Thompson return (error); 134102ac6454SAndrew Thompson } 134202ac6454SAndrew Thompson } 134302ac6454SAndrew Thompson 134402ac6454SAndrew Thompson complete: 134502ac6454SAndrew Thompson /* update "aFrames" */ 134602ac6454SAndrew Thompson error = copyout(&fs_ep.aFrames, &fs_ep_uptr->aFrames, 134702ac6454SAndrew Thompson sizeof(fs_ep.aFrames)); 134802ac6454SAndrew Thompson if (error) 134902ac6454SAndrew Thompson goto done; 135002ac6454SAndrew Thompson 135102ac6454SAndrew Thompson /* update "isoc_time_complete" */ 135202ac6454SAndrew Thompson error = copyout(&fs_ep.isoc_time_complete, 135302ac6454SAndrew Thompson &fs_ep_uptr->isoc_time_complete, 135402ac6454SAndrew Thompson sizeof(fs_ep.isoc_time_complete)); 135502ac6454SAndrew Thompson if (error) 135602ac6454SAndrew Thompson goto done; 135702ac6454SAndrew Thompson /* update "status" */ 135802ac6454SAndrew Thompson error = copyout(&fs_ep.status, &fs_ep_uptr->status, 135902ac6454SAndrew Thompson sizeof(fs_ep.status)); 136002ac6454SAndrew Thompson done: 136102ac6454SAndrew Thompson return (error); 136202ac6454SAndrew Thompson } 136302ac6454SAndrew Thompson 136402ac6454SAndrew Thompson static uint8_t 1365760bc48eSAndrew Thompson ugen_fifo_in_use(struct usb_fifo *f, int fflags) 136602ac6454SAndrew Thompson { 1367760bc48eSAndrew Thompson struct usb_fifo *f_rx; 1368760bc48eSAndrew Thompson struct usb_fifo *f_tx; 136902ac6454SAndrew Thompson 137002ac6454SAndrew Thompson f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX]; 137102ac6454SAndrew Thompson f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX]; 137202ac6454SAndrew Thompson 137302ac6454SAndrew Thompson if ((fflags & FREAD) && f_rx && 137402ac6454SAndrew Thompson (f_rx->xfer[0] || f_rx->xfer[1])) { 137502ac6454SAndrew Thompson return (1); /* RX FIFO in use */ 137602ac6454SAndrew Thompson } 137702ac6454SAndrew Thompson if ((fflags & FWRITE) && f_tx && 137802ac6454SAndrew Thompson (f_tx->xfer[0] || f_tx->xfer[1])) { 137902ac6454SAndrew Thompson return (1); /* TX FIFO in use */ 138002ac6454SAndrew Thompson } 138102ac6454SAndrew Thompson return (0); /* not in use */ 138202ac6454SAndrew Thompson } 138302ac6454SAndrew Thompson 138402ac6454SAndrew Thompson static int 1385760bc48eSAndrew Thompson ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) 138602ac6454SAndrew Thompson { 1387760bc48eSAndrew Thompson struct usb_config usb_config[1]; 1388760bc48eSAndrew Thompson struct usb_device_request req; 138902ac6454SAndrew Thompson union { 1390760bc48eSAndrew Thompson struct usb_fs_complete *pcomp; 1391760bc48eSAndrew Thompson struct usb_fs_start *pstart; 1392760bc48eSAndrew Thompson struct usb_fs_stop *pstop; 1393760bc48eSAndrew Thompson struct usb_fs_open *popen; 1394760bc48eSAndrew Thompson struct usb_fs_close *pclose; 1395760bc48eSAndrew Thompson struct usb_fs_clear_stall_sync *pstall; 139602ac6454SAndrew Thompson void *addr; 139702ac6454SAndrew Thompson } u; 1398ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 1399760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed; 14006c352e99SHans Petter Selasky struct usb_xfer *xfer; 140102ac6454SAndrew Thompson int error = 0; 140202ac6454SAndrew Thompson uint8_t iface_index; 140302ac6454SAndrew Thompson uint8_t isread; 140402ac6454SAndrew Thompson uint8_t ep_index; 14051c497368SHans Petter Selasky uint8_t pre_scale; 140602ac6454SAndrew Thompson 140702ac6454SAndrew Thompson u.addr = addr; 140802ac6454SAndrew Thompson 140902ac6454SAndrew Thompson DPRINTFN(6, "cmd=0x%08lx\n", cmd); 141002ac6454SAndrew Thompson 141102ac6454SAndrew Thompson switch (cmd) { 141202ac6454SAndrew Thompson case USB_FS_COMPLETE: 141302ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 141402ac6454SAndrew Thompson error = ugen_fs_get_complete(f, &ep_index); 141502ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 141602ac6454SAndrew Thompson 141702ac6454SAndrew Thompson if (error) { 141802ac6454SAndrew Thompson error = EBUSY; 141902ac6454SAndrew Thompson break; 142002ac6454SAndrew Thompson } 142102ac6454SAndrew Thompson u.pcomp->ep_index = ep_index; 142202ac6454SAndrew Thompson error = ugen_fs_copy_out(f, u.pcomp->ep_index); 142302ac6454SAndrew Thompson break; 142402ac6454SAndrew Thompson 142502ac6454SAndrew Thompson case USB_FS_START: 142602ac6454SAndrew Thompson error = ugen_fs_copy_in(f, u.pstart->ep_index); 14276c352e99SHans Petter Selasky if (error) 142802ac6454SAndrew Thompson break; 142902ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 14306c352e99SHans Petter Selasky xfer = f->fs_xfer[u.pstart->ep_index]; 14316c352e99SHans Petter Selasky usbd_transfer_start(xfer); 143202ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 143302ac6454SAndrew Thompson break; 143402ac6454SAndrew Thompson 143502ac6454SAndrew Thompson case USB_FS_STOP: 143602ac6454SAndrew Thompson if (u.pstop->ep_index >= f->fs_ep_max) { 143702ac6454SAndrew Thompson error = EINVAL; 143802ac6454SAndrew Thompson break; 143902ac6454SAndrew Thompson } 144002ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 14416c352e99SHans Petter Selasky xfer = f->fs_xfer[u.pstart->ep_index]; 14426c352e99SHans Petter Selasky if (usbd_transfer_pending(xfer)) { 14436c352e99SHans Petter Selasky usbd_transfer_stop(xfer); 14446c352e99SHans Petter Selasky /* 14456c352e99SHans Petter Selasky * Check if the USB transfer was stopped 14466c352e99SHans Petter Selasky * before it was even started. Else a cancel 14476c352e99SHans Petter Selasky * callback will be pending. 14486c352e99SHans Petter Selasky */ 14496c352e99SHans Petter Selasky if (!xfer->flags_int.transferring) { 14506c352e99SHans Petter Selasky ugen_fs_set_complete(xfer->priv_sc, 14516c352e99SHans Petter Selasky USB_P2U(xfer->priv_fifo)); 14526c352e99SHans Petter Selasky } 14536c352e99SHans Petter Selasky } 145402ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 145502ac6454SAndrew Thompson break; 145602ac6454SAndrew Thompson 145702ac6454SAndrew Thompson case USB_FS_OPEN: 145802ac6454SAndrew Thompson if (u.popen->ep_index >= f->fs_ep_max) { 145902ac6454SAndrew Thompson error = EINVAL; 146002ac6454SAndrew Thompson break; 146102ac6454SAndrew Thompson } 146202ac6454SAndrew Thompson if (f->fs_xfer[u.popen->ep_index] != NULL) { 146302ac6454SAndrew Thompson error = EBUSY; 146402ac6454SAndrew Thompson break; 146502ac6454SAndrew Thompson } 146602ac6454SAndrew Thompson if (u.popen->max_bufsize > USB_FS_MAX_BUFSIZE) { 146702ac6454SAndrew Thompson u.popen->max_bufsize = USB_FS_MAX_BUFSIZE; 146802ac6454SAndrew Thompson } 14691c497368SHans Petter Selasky if (u.popen->max_frames & USB_FS_MAX_FRAMES_PRE_SCALE) { 14701c497368SHans Petter Selasky pre_scale = 1; 14711c497368SHans Petter Selasky u.popen->max_frames &= ~USB_FS_MAX_FRAMES_PRE_SCALE; 14721c497368SHans Petter Selasky } else { 14731c497368SHans Petter Selasky pre_scale = 0; 14741c497368SHans Petter Selasky } 147502ac6454SAndrew Thompson if (u.popen->max_frames > USB_FS_MAX_FRAMES) { 147602ac6454SAndrew Thompson u.popen->max_frames = USB_FS_MAX_FRAMES; 147702ac6454SAndrew Thompson break; 147802ac6454SAndrew Thompson } 147902ac6454SAndrew Thompson if (u.popen->max_frames == 0) { 148002ac6454SAndrew Thompson error = EINVAL; 148102ac6454SAndrew Thompson break; 148202ac6454SAndrew Thompson } 1483a593f6b8SAndrew Thompson ep = usbd_get_ep_by_addr(f->udev, u.popen->ep_no); 1484ae60fdfbSAndrew Thompson if (ep == NULL) { 148502ac6454SAndrew Thompson error = EINVAL; 148602ac6454SAndrew Thompson break; 148702ac6454SAndrew Thompson } 1488ae60fdfbSAndrew Thompson ed = ep->edesc; 148902ac6454SAndrew Thompson if (ed == NULL) { 149002ac6454SAndrew Thompson error = ENXIO; 149102ac6454SAndrew Thompson break; 149202ac6454SAndrew Thompson } 1493ae60fdfbSAndrew Thompson iface_index = ep->iface_index; 149402ac6454SAndrew Thompson 14951c497368SHans Petter Selasky memset(usb_config, 0, sizeof(usb_config)); 149602ac6454SAndrew Thompson 1497760bc48eSAndrew Thompson usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; 1498760bc48eSAndrew Thompson usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; 1499760bc48eSAndrew Thompson usb_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN); 1500760bc48eSAndrew Thompson usb_config[0].interval = USB_DEFAULT_INTERVAL; 1501760bc48eSAndrew Thompson usb_config[0].flags.proxy_buffer = 1; 15021c497368SHans Petter Selasky if (pre_scale != 0) 15031c497368SHans Petter Selasky usb_config[0].flags.pre_scale_frames = 1; 15045b3bb704SAndrew Thompson usb_config[0].callback = &ugen_ctrl_fs_callback; 1505760bc48eSAndrew Thompson usb_config[0].timeout = 0; /* no timeout */ 1506760bc48eSAndrew Thompson usb_config[0].frames = u.popen->max_frames; 1507760bc48eSAndrew Thompson usb_config[0].bufsize = u.popen->max_bufsize; 1508760bc48eSAndrew Thompson usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ 150902ac6454SAndrew Thompson 1510760bc48eSAndrew Thompson if (usb_config[0].type == UE_CONTROL) { 1511f29a0724SAndrew Thompson if (f->udev->flags.usb_mode != USB_MODE_HOST) { 151202ac6454SAndrew Thompson error = EINVAL; 151302ac6454SAndrew Thompson break; 151402ac6454SAndrew Thompson } 151502ac6454SAndrew Thompson } else { 151602ac6454SAndrew Thompson 1517760bc48eSAndrew Thompson isread = ((usb_config[0].endpoint & 151802ac6454SAndrew Thompson (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN); 151902ac6454SAndrew Thompson 1520f29a0724SAndrew Thompson if (f->udev->flags.usb_mode != USB_MODE_HOST) { 152102ac6454SAndrew Thompson isread = !isread; 152202ac6454SAndrew Thompson } 152302ac6454SAndrew Thompson /* check permissions */ 152402ac6454SAndrew Thompson if (isread) { 152502ac6454SAndrew Thompson if (!(fflags & FREAD)) { 152602ac6454SAndrew Thompson error = EPERM; 152702ac6454SAndrew Thompson break; 152802ac6454SAndrew Thompson } 152902ac6454SAndrew Thompson } else { 153002ac6454SAndrew Thompson if (!(fflags & FWRITE)) { 153102ac6454SAndrew Thompson error = EPERM; 153202ac6454SAndrew Thompson break; 153302ac6454SAndrew Thompson } 153402ac6454SAndrew Thompson } 153502ac6454SAndrew Thompson } 1536a593f6b8SAndrew Thompson error = usbd_transfer_setup(f->udev, &iface_index, 1537760bc48eSAndrew Thompson f->fs_xfer + u.popen->ep_index, usb_config, 1, 153802ac6454SAndrew Thompson f, f->priv_mtx); 153902ac6454SAndrew Thompson if (error == 0) { 154002ac6454SAndrew Thompson /* update maximums */ 154102ac6454SAndrew Thompson u.popen->max_packet_length = 154202ac6454SAndrew Thompson f->fs_xfer[u.popen->ep_index]->max_frame_size; 154302ac6454SAndrew Thompson u.popen->max_bufsize = 154402ac6454SAndrew Thompson f->fs_xfer[u.popen->ep_index]->max_data_length; 15451c497368SHans Petter Selasky /* update number of frames */ 15461c497368SHans Petter Selasky u.popen->max_frames = 15471c497368SHans Petter Selasky f->fs_xfer[u.popen->ep_index]->nframes; 15481c497368SHans Petter Selasky /* store index of endpoint */ 154902ac6454SAndrew Thompson f->fs_xfer[u.popen->ep_index]->priv_fifo = 155002ac6454SAndrew Thompson ((uint8_t *)0) + u.popen->ep_index; 155102ac6454SAndrew Thompson } else { 155202ac6454SAndrew Thompson error = ENOMEM; 155302ac6454SAndrew Thompson } 155402ac6454SAndrew Thompson break; 155502ac6454SAndrew Thompson 155602ac6454SAndrew Thompson case USB_FS_CLOSE: 155702ac6454SAndrew Thompson if (u.pclose->ep_index >= f->fs_ep_max) { 155802ac6454SAndrew Thompson error = EINVAL; 155902ac6454SAndrew Thompson break; 156002ac6454SAndrew Thompson } 156102ac6454SAndrew Thompson if (f->fs_xfer[u.pclose->ep_index] == NULL) { 156202ac6454SAndrew Thompson error = EINVAL; 156302ac6454SAndrew Thompson break; 156402ac6454SAndrew Thompson } 1565a593f6b8SAndrew Thompson usbd_transfer_unsetup(f->fs_xfer + u.pclose->ep_index, 1); 156602ac6454SAndrew Thompson break; 156702ac6454SAndrew Thompson 156802ac6454SAndrew Thompson case USB_FS_CLEAR_STALL_SYNC: 156902ac6454SAndrew Thompson if (u.pstall->ep_index >= f->fs_ep_max) { 157002ac6454SAndrew Thompson error = EINVAL; 157102ac6454SAndrew Thompson break; 157202ac6454SAndrew Thompson } 157302ac6454SAndrew Thompson if (f->fs_xfer[u.pstall->ep_index] == NULL) { 157402ac6454SAndrew Thompson error = EINVAL; 157502ac6454SAndrew Thompson break; 157602ac6454SAndrew Thompson } 1577f29a0724SAndrew Thompson if (f->udev->flags.usb_mode != USB_MODE_HOST) { 157802ac6454SAndrew Thompson error = EINVAL; 157902ac6454SAndrew Thompson break; 158002ac6454SAndrew Thompson } 158102ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 1582a593f6b8SAndrew Thompson error = usbd_transfer_pending(f->fs_xfer[u.pstall->ep_index]); 158302ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 158402ac6454SAndrew Thompson 158502ac6454SAndrew Thompson if (error) { 158602ac6454SAndrew Thompson return (EBUSY); 158702ac6454SAndrew Thompson } 1588ae60fdfbSAndrew Thompson ep = f->fs_xfer[u.pstall->ep_index]->endpoint; 158902ac6454SAndrew Thompson 159002ac6454SAndrew Thompson /* setup a clear-stall packet */ 159102ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_ENDPOINT; 159202ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE; 159302ac6454SAndrew Thompson USETW(req.wValue, UF_ENDPOINT_HALT); 1594ae60fdfbSAndrew Thompson req.wIndex[0] = ep->edesc->bEndpointAddress; 159502ac6454SAndrew Thompson req.wIndex[1] = 0; 159602ac6454SAndrew Thompson USETW(req.wLength, 0); 159702ac6454SAndrew Thompson 1598a593f6b8SAndrew Thompson error = usbd_do_request(f->udev, NULL, &req, NULL); 159902ac6454SAndrew Thompson if (error == 0) { 1600a593f6b8SAndrew Thompson usbd_clear_data_toggle(f->udev, ep); 160102ac6454SAndrew Thompson } else { 160202ac6454SAndrew Thompson error = ENXIO; 160302ac6454SAndrew Thompson } 160402ac6454SAndrew Thompson break; 160502ac6454SAndrew Thompson 160602ac6454SAndrew Thompson default: 160702ac6454SAndrew Thompson error = ENOIOCTL; 160802ac6454SAndrew Thompson break; 160902ac6454SAndrew Thompson } 161002ac6454SAndrew Thompson 161102ac6454SAndrew Thompson DPRINTFN(6, "error=%d\n", error); 161202ac6454SAndrew Thompson 161302ac6454SAndrew Thompson return (error); 161402ac6454SAndrew Thompson } 161502ac6454SAndrew Thompson 161602ac6454SAndrew Thompson static int 1617760bc48eSAndrew Thompson ugen_set_short_xfer(struct usb_fifo *f, void *addr) 161802ac6454SAndrew Thompson { 161902ac6454SAndrew Thompson uint8_t t; 162002ac6454SAndrew Thompson 162102ac6454SAndrew Thompson if (*(int *)addr) 162202ac6454SAndrew Thompson t = 1; 162302ac6454SAndrew Thompson else 162402ac6454SAndrew Thompson t = 0; 162502ac6454SAndrew Thompson 162602ac6454SAndrew Thompson if (f->flag_short == t) { 162702ac6454SAndrew Thompson /* same value like before - accept */ 162802ac6454SAndrew Thompson return (0); 162902ac6454SAndrew Thompson } 163002ac6454SAndrew Thompson if (f->xfer[0] || f->xfer[1]) { 163102ac6454SAndrew Thompson /* cannot change this during transfer */ 163202ac6454SAndrew Thompson return (EBUSY); 163302ac6454SAndrew Thompson } 163402ac6454SAndrew Thompson f->flag_short = t; 163502ac6454SAndrew Thompson return (0); 163602ac6454SAndrew Thompson } 163702ac6454SAndrew Thompson 163802ac6454SAndrew Thompson static int 1639760bc48eSAndrew Thompson ugen_set_timeout(struct usb_fifo *f, void *addr) 164002ac6454SAndrew Thompson { 164102ac6454SAndrew Thompson f->timeout = *(int *)addr; 164202ac6454SAndrew Thompson if (f->timeout > 65535) { 164302ac6454SAndrew Thompson /* limit user input */ 164402ac6454SAndrew Thompson f->timeout = 65535; 164502ac6454SAndrew Thompson } 164602ac6454SAndrew Thompson return (0); 164702ac6454SAndrew Thompson } 164802ac6454SAndrew Thompson 164902ac6454SAndrew Thompson static int 1650760bc48eSAndrew Thompson ugen_get_frame_size(struct usb_fifo *f, void *addr) 165102ac6454SAndrew Thompson { 165202ac6454SAndrew Thompson if (f->xfer[0]) { 165302ac6454SAndrew Thompson *(int *)addr = f->xfer[0]->max_frame_size; 165402ac6454SAndrew Thompson } else { 165502ac6454SAndrew Thompson return (EINVAL); 165602ac6454SAndrew Thompson } 165702ac6454SAndrew Thompson return (0); 165802ac6454SAndrew Thompson } 165902ac6454SAndrew Thompson 166002ac6454SAndrew Thompson static int 1661760bc48eSAndrew Thompson ugen_set_buffer_size(struct usb_fifo *f, void *addr) 166202ac6454SAndrew Thompson { 1663e0a69b51SAndrew Thompson usb_frlength_t t; 166402ac6454SAndrew Thompson 166501cf7831SAndrew Thompson if (*(int *)addr < 0) 166601cf7831SAndrew Thompson t = 0; /* use "wMaxPacketSize" */ 166702ac6454SAndrew Thompson else if (*(int *)addr < (256 * 1024)) 166802ac6454SAndrew Thompson t = *(int *)addr; 166902ac6454SAndrew Thompson else 167002ac6454SAndrew Thompson t = 256 * 1024; 167102ac6454SAndrew Thompson 167202ac6454SAndrew Thompson if (f->bufsize == t) { 167302ac6454SAndrew Thompson /* same value like before - accept */ 167402ac6454SAndrew Thompson return (0); 167502ac6454SAndrew Thompson } 167602ac6454SAndrew Thompson if (f->xfer[0] || f->xfer[1]) { 167702ac6454SAndrew Thompson /* cannot change this during transfer */ 167802ac6454SAndrew Thompson return (EBUSY); 167902ac6454SAndrew Thompson } 168002ac6454SAndrew Thompson f->bufsize = t; 168102ac6454SAndrew Thompson return (0); 168202ac6454SAndrew Thompson } 168302ac6454SAndrew Thompson 168402ac6454SAndrew Thompson static int 1685760bc48eSAndrew Thompson ugen_get_buffer_size(struct usb_fifo *f, void *addr) 168602ac6454SAndrew Thompson { 168702ac6454SAndrew Thompson *(int *)addr = f->bufsize; 168802ac6454SAndrew Thompson return (0); 168902ac6454SAndrew Thompson } 169002ac6454SAndrew Thompson 169102ac6454SAndrew Thompson static int 1692760bc48eSAndrew Thompson ugen_get_iface_desc(struct usb_fifo *f, 1693760bc48eSAndrew Thompson struct usb_interface_descriptor *idesc) 169402ac6454SAndrew Thompson { 1695760bc48eSAndrew Thompson struct usb_interface *iface; 169602ac6454SAndrew Thompson 1697a593f6b8SAndrew Thompson iface = usbd_get_iface(f->udev, f->iface_index); 169802ac6454SAndrew Thompson if (iface && iface->idesc) { 169902ac6454SAndrew Thompson *idesc = *(iface->idesc); 170002ac6454SAndrew Thompson } else { 170102ac6454SAndrew Thompson return (EIO); 170202ac6454SAndrew Thompson } 170302ac6454SAndrew Thompson return (0); 170402ac6454SAndrew Thompson } 170502ac6454SAndrew Thompson 170602ac6454SAndrew Thompson static int 1707760bc48eSAndrew Thompson ugen_get_endpoint_desc(struct usb_fifo *f, 1708760bc48eSAndrew Thompson struct usb_endpoint_descriptor *ed) 170902ac6454SAndrew Thompson { 1710ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 171102ac6454SAndrew Thompson 1712ed6d949aSAndrew Thompson ep = usb_fifo_softc(f); 171302ac6454SAndrew Thompson 1714ae60fdfbSAndrew Thompson if (ep && ep->edesc) { 1715ae60fdfbSAndrew Thompson *ed = *ep->edesc; 171602ac6454SAndrew Thompson } else { 171702ac6454SAndrew Thompson return (EINVAL); 171802ac6454SAndrew Thompson } 171902ac6454SAndrew Thompson return (0); 172002ac6454SAndrew Thompson } 172102ac6454SAndrew Thompson 172202ac6454SAndrew Thompson static int 1723760bc48eSAndrew Thompson ugen_set_power_mode(struct usb_fifo *f, int mode) 172402ac6454SAndrew Thompson { 1725760bc48eSAndrew Thompson struct usb_device *udev = f->udev; 172602ac6454SAndrew Thompson int err; 172702ac6454SAndrew Thompson uint8_t old_mode; 172802ac6454SAndrew Thompson 172902ac6454SAndrew Thompson if ((udev == NULL) || 173002ac6454SAndrew Thompson (udev->parent_hub == NULL)) { 173102ac6454SAndrew Thompson return (EINVAL); 173202ac6454SAndrew Thompson } 173350230f98SAndrew Thompson err = priv_check(curthread, PRIV_DRIVER); 173402ac6454SAndrew Thompson if (err) 173502ac6454SAndrew Thompson return (err); 173602ac6454SAndrew Thompson 173702ac6454SAndrew Thompson /* get old power mode */ 173802ac6454SAndrew Thompson old_mode = udev->power_mode; 173902ac6454SAndrew Thompson 174002ac6454SAndrew Thompson /* if no change, then just return */ 174102ac6454SAndrew Thompson if (old_mode == mode) 174202ac6454SAndrew Thompson return (0); 174302ac6454SAndrew Thompson 174402ac6454SAndrew Thompson switch (mode) { 174502ac6454SAndrew Thompson case USB_POWER_MODE_OFF: 174602ac6454SAndrew Thompson /* get the device unconfigured */ 174702ac6454SAndrew Thompson err = ugen_set_config(f, USB_UNCONFIG_INDEX); 174802ac6454SAndrew Thompson if (err) { 174902ac6454SAndrew Thompson DPRINTFN(0, "Could not unconfigure " 175002ac6454SAndrew Thompson "device (ignored)\n"); 175102ac6454SAndrew Thompson } 175202ac6454SAndrew Thompson 175302ac6454SAndrew Thompson /* clear port enable */ 1754a593f6b8SAndrew Thompson err = usbd_req_clear_port_feature(udev->parent_hub, 175502ac6454SAndrew Thompson NULL, udev->port_no, UHF_PORT_ENABLE); 175602ac6454SAndrew Thompson break; 175702ac6454SAndrew Thompson 175802ac6454SAndrew Thompson case USB_POWER_MODE_ON: 175902ac6454SAndrew Thompson case USB_POWER_MODE_SAVE: 176002ac6454SAndrew Thompson break; 176102ac6454SAndrew Thompson 176202ac6454SAndrew Thompson case USB_POWER_MODE_RESUME: 17632df1e9a6SAndrew Thompson #if USB_HAVE_POWERD 17642df1e9a6SAndrew Thompson /* let USB-powerd handle resume */ 17652df1e9a6SAndrew Thompson USB_BUS_LOCK(udev->bus); 17662df1e9a6SAndrew Thompson udev->pwr_save.write_refs++; 17672df1e9a6SAndrew Thompson udev->pwr_save.last_xfer_time = ticks; 17682df1e9a6SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 17692df1e9a6SAndrew Thompson 17702df1e9a6SAndrew Thompson /* set new power mode */ 17712df1e9a6SAndrew Thompson usbd_set_power_mode(udev, USB_POWER_MODE_SAVE); 17722df1e9a6SAndrew Thompson 17732df1e9a6SAndrew Thompson /* wait for resume to complete */ 17742df1e9a6SAndrew Thompson usb_pause_mtx(NULL, hz / 4); 17752df1e9a6SAndrew Thompson 17762df1e9a6SAndrew Thompson /* clear write reference */ 17772df1e9a6SAndrew Thompson USB_BUS_LOCK(udev->bus); 17782df1e9a6SAndrew Thompson udev->pwr_save.write_refs--; 17792df1e9a6SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 17802df1e9a6SAndrew Thompson #endif 178102ac6454SAndrew Thompson mode = USB_POWER_MODE_SAVE; 178202ac6454SAndrew Thompson break; 178302ac6454SAndrew Thompson 178402ac6454SAndrew Thompson case USB_POWER_MODE_SUSPEND: 17852df1e9a6SAndrew Thompson #if USB_HAVE_POWERD 17862df1e9a6SAndrew Thompson /* let USB-powerd handle suspend */ 17872df1e9a6SAndrew Thompson USB_BUS_LOCK(udev->bus); 17882df1e9a6SAndrew Thompson udev->pwr_save.last_xfer_time = ticks - (256 * hz); 17892df1e9a6SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 17902df1e9a6SAndrew Thompson #endif 179102ac6454SAndrew Thompson mode = USB_POWER_MODE_SAVE; 179202ac6454SAndrew Thompson break; 179302ac6454SAndrew Thompson 179402ac6454SAndrew Thompson default: 179502ac6454SAndrew Thompson return (EINVAL); 179602ac6454SAndrew Thompson } 179702ac6454SAndrew Thompson 179802ac6454SAndrew Thompson if (err) 179902ac6454SAndrew Thompson return (ENXIO); /* I/O failure */ 180002ac6454SAndrew Thompson 180102ac6454SAndrew Thompson /* if we are powered off we need to re-enumerate first */ 180202ac6454SAndrew Thompson if (old_mode == USB_POWER_MODE_OFF) { 18038f9750b7SHans Petter Selasky if (udev->flags.usb_mode == USB_MODE_HOST) { 18048f9750b7SHans Petter Selasky if (udev->re_enumerate_wait == 0) 18058f9750b7SHans Petter Selasky udev->re_enumerate_wait = 1; 18068f9750b7SHans Petter Selasky } 18078f9750b7SHans Petter Selasky /* set power mode will wake up the explore thread */ 180802ac6454SAndrew Thompson } 180902ac6454SAndrew Thompson 181002ac6454SAndrew Thompson /* set new power mode */ 1811a593f6b8SAndrew Thompson usbd_set_power_mode(udev, mode); 181202ac6454SAndrew Thompson 181302ac6454SAndrew Thompson return (0); /* success */ 181402ac6454SAndrew Thompson } 181502ac6454SAndrew Thompson 181602ac6454SAndrew Thompson static int 1817760bc48eSAndrew Thompson ugen_get_power_mode(struct usb_fifo *f) 181802ac6454SAndrew Thompson { 1819760bc48eSAndrew Thompson struct usb_device *udev = f->udev; 182002ac6454SAndrew Thompson 1821d2d71ce7SAndrew Thompson if (udev == NULL) 182202ac6454SAndrew Thompson return (USB_POWER_MODE_ON); 1823d2d71ce7SAndrew Thompson 182402ac6454SAndrew Thompson return (udev->power_mode); 182502ac6454SAndrew Thompson } 182602ac6454SAndrew Thompson 182702ac6454SAndrew Thompson static int 1828760bc48eSAndrew Thompson ugen_do_port_feature(struct usb_fifo *f, uint8_t port_no, 182902ac6454SAndrew Thompson uint8_t set, uint16_t feature) 183002ac6454SAndrew Thompson { 1831760bc48eSAndrew Thompson struct usb_device *udev = f->udev; 1832760bc48eSAndrew Thompson struct usb_hub *hub; 183302ac6454SAndrew Thompson int err; 183402ac6454SAndrew Thompson 183550230f98SAndrew Thompson err = priv_check(curthread, PRIV_DRIVER); 183602ac6454SAndrew Thompson if (err) { 183702ac6454SAndrew Thompson return (err); 183802ac6454SAndrew Thompson } 183902ac6454SAndrew Thompson if (port_no == 0) { 184002ac6454SAndrew Thompson return (EINVAL); 184102ac6454SAndrew Thompson } 184202ac6454SAndrew Thompson if ((udev == NULL) || 184302ac6454SAndrew Thompson (udev->hub == NULL)) { 184402ac6454SAndrew Thompson return (EINVAL); 184502ac6454SAndrew Thompson } 184602ac6454SAndrew Thompson hub = udev->hub; 184702ac6454SAndrew Thompson 184802ac6454SAndrew Thompson if (port_no > hub->nports) { 184902ac6454SAndrew Thompson return (EINVAL); 185002ac6454SAndrew Thompson } 185102ac6454SAndrew Thompson if (set) 1852a593f6b8SAndrew Thompson err = usbd_req_set_port_feature(udev, 185302ac6454SAndrew Thompson NULL, port_no, feature); 185402ac6454SAndrew Thompson else 1855a593f6b8SAndrew Thompson err = usbd_req_clear_port_feature(udev, 185602ac6454SAndrew Thompson NULL, port_no, feature); 185702ac6454SAndrew Thompson 185802ac6454SAndrew Thompson if (err) 185902ac6454SAndrew Thompson return (ENXIO); /* failure */ 186002ac6454SAndrew Thompson 186102ac6454SAndrew Thompson return (0); /* success */ 186202ac6454SAndrew Thompson } 186302ac6454SAndrew Thompson 186402ac6454SAndrew Thompson static int 1865760bc48eSAndrew Thompson ugen_iface_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) 186602ac6454SAndrew Thompson { 1867760bc48eSAndrew Thompson struct usb_fifo *f_rx; 1868760bc48eSAndrew Thompson struct usb_fifo *f_tx; 186902ac6454SAndrew Thompson int error = 0; 187002ac6454SAndrew Thompson 187102ac6454SAndrew Thompson f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX]; 187202ac6454SAndrew Thompson f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX]; 187302ac6454SAndrew Thompson 187402ac6454SAndrew Thompson switch (cmd) { 187502ac6454SAndrew Thompson case USB_SET_RX_SHORT_XFER: 187602ac6454SAndrew Thompson if (fflags & FREAD) { 187702ac6454SAndrew Thompson error = ugen_set_short_xfer(f_rx, addr); 187802ac6454SAndrew Thompson } else { 187902ac6454SAndrew Thompson error = EINVAL; 188002ac6454SAndrew Thompson } 188102ac6454SAndrew Thompson break; 188202ac6454SAndrew Thompson 188302ac6454SAndrew Thompson case USB_SET_TX_FORCE_SHORT: 188402ac6454SAndrew Thompson if (fflags & FWRITE) { 188502ac6454SAndrew Thompson error = ugen_set_short_xfer(f_tx, addr); 188602ac6454SAndrew Thompson } else { 188702ac6454SAndrew Thompson error = EINVAL; 188802ac6454SAndrew Thompson } 188902ac6454SAndrew Thompson break; 189002ac6454SAndrew Thompson 189102ac6454SAndrew Thompson case USB_SET_RX_TIMEOUT: 189202ac6454SAndrew Thompson if (fflags & FREAD) { 189302ac6454SAndrew Thompson error = ugen_set_timeout(f_rx, addr); 189402ac6454SAndrew Thompson } else { 189502ac6454SAndrew Thompson error = EINVAL; 189602ac6454SAndrew Thompson } 189702ac6454SAndrew Thompson break; 189802ac6454SAndrew Thompson 189902ac6454SAndrew Thompson case USB_SET_TX_TIMEOUT: 190002ac6454SAndrew Thompson if (fflags & FWRITE) { 190102ac6454SAndrew Thompson error = ugen_set_timeout(f_tx, addr); 190202ac6454SAndrew Thompson } else { 190302ac6454SAndrew Thompson error = EINVAL; 190402ac6454SAndrew Thompson } 190502ac6454SAndrew Thompson break; 190602ac6454SAndrew Thompson 190702ac6454SAndrew Thompson case USB_GET_RX_FRAME_SIZE: 190802ac6454SAndrew Thompson if (fflags & FREAD) { 190902ac6454SAndrew Thompson error = ugen_get_frame_size(f_rx, addr); 191002ac6454SAndrew Thompson } else { 191102ac6454SAndrew Thompson error = EINVAL; 191202ac6454SAndrew Thompson } 191302ac6454SAndrew Thompson break; 191402ac6454SAndrew Thompson 191502ac6454SAndrew Thompson case USB_GET_TX_FRAME_SIZE: 191602ac6454SAndrew Thompson if (fflags & FWRITE) { 191702ac6454SAndrew Thompson error = ugen_get_frame_size(f_tx, addr); 191802ac6454SAndrew Thompson } else { 191902ac6454SAndrew Thompson error = EINVAL; 192002ac6454SAndrew Thompson } 192102ac6454SAndrew Thompson break; 192202ac6454SAndrew Thompson 192302ac6454SAndrew Thompson case USB_SET_RX_BUFFER_SIZE: 192402ac6454SAndrew Thompson if (fflags & FREAD) { 192502ac6454SAndrew Thompson error = ugen_set_buffer_size(f_rx, addr); 192602ac6454SAndrew Thompson } else { 192702ac6454SAndrew Thompson error = EINVAL; 192802ac6454SAndrew Thompson } 192902ac6454SAndrew Thompson break; 193002ac6454SAndrew Thompson 193102ac6454SAndrew Thompson case USB_SET_TX_BUFFER_SIZE: 193202ac6454SAndrew Thompson if (fflags & FWRITE) { 193302ac6454SAndrew Thompson error = ugen_set_buffer_size(f_tx, addr); 193402ac6454SAndrew Thompson } else { 193502ac6454SAndrew Thompson error = EINVAL; 193602ac6454SAndrew Thompson } 193702ac6454SAndrew Thompson break; 193802ac6454SAndrew Thompson 193902ac6454SAndrew Thompson case USB_GET_RX_BUFFER_SIZE: 194002ac6454SAndrew Thompson if (fflags & FREAD) { 194102ac6454SAndrew Thompson error = ugen_get_buffer_size(f_rx, addr); 194202ac6454SAndrew Thompson } else { 194302ac6454SAndrew Thompson error = EINVAL; 194402ac6454SAndrew Thompson } 194502ac6454SAndrew Thompson break; 194602ac6454SAndrew Thompson 194702ac6454SAndrew Thompson case USB_GET_TX_BUFFER_SIZE: 194802ac6454SAndrew Thompson if (fflags & FWRITE) { 194902ac6454SAndrew Thompson error = ugen_get_buffer_size(f_tx, addr); 195002ac6454SAndrew Thompson } else { 195102ac6454SAndrew Thompson error = EINVAL; 195202ac6454SAndrew Thompson } 195302ac6454SAndrew Thompson break; 195402ac6454SAndrew Thompson 195502ac6454SAndrew Thompson case USB_GET_RX_INTERFACE_DESC: 195602ac6454SAndrew Thompson if (fflags & FREAD) { 195702ac6454SAndrew Thompson error = ugen_get_iface_desc(f_rx, addr); 195802ac6454SAndrew Thompson } else { 195902ac6454SAndrew Thompson error = EINVAL; 196002ac6454SAndrew Thompson } 196102ac6454SAndrew Thompson break; 196202ac6454SAndrew Thompson 196302ac6454SAndrew Thompson case USB_GET_TX_INTERFACE_DESC: 196402ac6454SAndrew Thompson if (fflags & FWRITE) { 196502ac6454SAndrew Thompson error = ugen_get_iface_desc(f_tx, addr); 196602ac6454SAndrew Thompson } else { 196702ac6454SAndrew Thompson error = EINVAL; 196802ac6454SAndrew Thompson } 196902ac6454SAndrew Thompson break; 197002ac6454SAndrew Thompson 197102ac6454SAndrew Thompson case USB_GET_RX_ENDPOINT_DESC: 197202ac6454SAndrew Thompson if (fflags & FREAD) { 197302ac6454SAndrew Thompson error = ugen_get_endpoint_desc(f_rx, addr); 197402ac6454SAndrew Thompson } else { 197502ac6454SAndrew Thompson error = EINVAL; 197602ac6454SAndrew Thompson } 197702ac6454SAndrew Thompson break; 197802ac6454SAndrew Thompson 197902ac6454SAndrew Thompson case USB_GET_TX_ENDPOINT_DESC: 198002ac6454SAndrew Thompson if (fflags & FWRITE) { 198102ac6454SAndrew Thompson error = ugen_get_endpoint_desc(f_tx, addr); 198202ac6454SAndrew Thompson } else { 198302ac6454SAndrew Thompson error = EINVAL; 198402ac6454SAndrew Thompson } 198502ac6454SAndrew Thompson break; 198602ac6454SAndrew Thompson 198702ac6454SAndrew Thompson case USB_SET_RX_STALL_FLAG: 198802ac6454SAndrew Thompson if ((fflags & FREAD) && (*(int *)addr)) { 198902ac6454SAndrew Thompson f_rx->flag_stall = 1; 199002ac6454SAndrew Thompson } 199102ac6454SAndrew Thompson break; 199202ac6454SAndrew Thompson 199302ac6454SAndrew Thompson case USB_SET_TX_STALL_FLAG: 199402ac6454SAndrew Thompson if ((fflags & FWRITE) && (*(int *)addr)) { 199502ac6454SAndrew Thompson f_tx->flag_stall = 1; 199602ac6454SAndrew Thompson } 199702ac6454SAndrew Thompson break; 199802ac6454SAndrew Thompson 199902ac6454SAndrew Thompson default: 200002ac6454SAndrew Thompson error = ENOIOCTL; 200102ac6454SAndrew Thompson break; 200202ac6454SAndrew Thompson } 200302ac6454SAndrew Thompson return (error); 200402ac6454SAndrew Thompson } 200502ac6454SAndrew Thompson 200602ac6454SAndrew Thompson static int 2007760bc48eSAndrew Thompson ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags) 200802ac6454SAndrew Thompson { 200902ac6454SAndrew Thompson union { 2010760bc48eSAndrew Thompson struct usb_interface_descriptor *idesc; 2011760bc48eSAndrew Thompson struct usb_alt_interface *ai; 2012760bc48eSAndrew Thompson struct usb_device_descriptor *ddesc; 2013760bc48eSAndrew Thompson struct usb_config_descriptor *cdesc; 2014760bc48eSAndrew Thompson struct usb_device_stats *stat; 2015760bc48eSAndrew Thompson struct usb_fs_init *pinit; 2016760bc48eSAndrew Thompson struct usb_fs_uninit *puninit; 201702ac6454SAndrew Thompson uint32_t *ptime; 201802ac6454SAndrew Thompson void *addr; 201902ac6454SAndrew Thompson int *pint; 202002ac6454SAndrew Thompson } u; 2021760bc48eSAndrew Thompson struct usb_device_descriptor *dtemp; 2022760bc48eSAndrew Thompson struct usb_config_descriptor *ctemp; 2023760bc48eSAndrew Thompson struct usb_interface *iface; 202402ac6454SAndrew Thompson int error = 0; 202502ac6454SAndrew Thompson uint8_t n; 202602ac6454SAndrew Thompson 202702ac6454SAndrew Thompson u.addr = addr; 202802ac6454SAndrew Thompson 202902ac6454SAndrew Thompson DPRINTFN(6, "cmd=0x%08lx\n", cmd); 203002ac6454SAndrew Thompson 203102ac6454SAndrew Thompson switch (cmd) { 203202ac6454SAndrew Thompson case USB_DISCOVER: 2033a593f6b8SAndrew Thompson usb_needs_explore_all(); 203402ac6454SAndrew Thompson break; 203502ac6454SAndrew Thompson 203602ac6454SAndrew Thompson case USB_SETDEBUG: 203702ac6454SAndrew Thompson if (!(fflags & FWRITE)) { 203802ac6454SAndrew Thompson error = EPERM; 203902ac6454SAndrew Thompson break; 204002ac6454SAndrew Thompson } 2041a593f6b8SAndrew Thompson usb_debug = *(int *)addr; 204202ac6454SAndrew Thompson break; 204302ac6454SAndrew Thompson 204402ac6454SAndrew Thompson case USB_GET_CONFIG: 204502ac6454SAndrew Thompson *(int *)addr = f->udev->curr_config_index; 204602ac6454SAndrew Thompson break; 204702ac6454SAndrew Thompson 204802ac6454SAndrew Thompson case USB_SET_CONFIG: 204902ac6454SAndrew Thompson if (!(fflags & FWRITE)) { 205002ac6454SAndrew Thompson error = EPERM; 205102ac6454SAndrew Thompson break; 205202ac6454SAndrew Thompson } 205302ac6454SAndrew Thompson error = ugen_set_config(f, *(int *)addr); 205402ac6454SAndrew Thompson break; 205502ac6454SAndrew Thompson 205602ac6454SAndrew Thompson case USB_GET_ALTINTERFACE: 2057a593f6b8SAndrew Thompson iface = usbd_get_iface(f->udev, 205802ac6454SAndrew Thompson u.ai->uai_interface_index); 205902ac6454SAndrew Thompson if (iface && iface->idesc) { 206002ac6454SAndrew Thompson u.ai->uai_alt_index = iface->alt_index; 206102ac6454SAndrew Thompson } else { 206202ac6454SAndrew Thompson error = EINVAL; 206302ac6454SAndrew Thompson } 206402ac6454SAndrew Thompson break; 206502ac6454SAndrew Thompson 206602ac6454SAndrew Thompson case USB_SET_ALTINTERFACE: 206702ac6454SAndrew Thompson if (!(fflags & FWRITE)) { 206802ac6454SAndrew Thompson error = EPERM; 206902ac6454SAndrew Thompson break; 207002ac6454SAndrew Thompson } 207102ac6454SAndrew Thompson error = ugen_set_interface(f, 207202ac6454SAndrew Thompson u.ai->uai_interface_index, u.ai->uai_alt_index); 207302ac6454SAndrew Thompson break; 207402ac6454SAndrew Thompson 207502ac6454SAndrew Thompson case USB_GET_DEVICE_DESC: 2076a593f6b8SAndrew Thompson dtemp = usbd_get_device_descriptor(f->udev); 207702ac6454SAndrew Thompson if (!dtemp) { 207802ac6454SAndrew Thompson error = EIO; 207902ac6454SAndrew Thompson break; 208002ac6454SAndrew Thompson } 208102ac6454SAndrew Thompson *u.ddesc = *dtemp; 208202ac6454SAndrew Thompson break; 208302ac6454SAndrew Thompson 208402ac6454SAndrew Thompson case USB_GET_CONFIG_DESC: 2085a593f6b8SAndrew Thompson ctemp = usbd_get_config_descriptor(f->udev); 208602ac6454SAndrew Thompson if (!ctemp) { 208702ac6454SAndrew Thompson error = EIO; 208802ac6454SAndrew Thompson break; 208902ac6454SAndrew Thompson } 209002ac6454SAndrew Thompson *u.cdesc = *ctemp; 209102ac6454SAndrew Thompson break; 209202ac6454SAndrew Thompson 209302ac6454SAndrew Thompson case USB_GET_FULL_DESC: 209402ac6454SAndrew Thompson error = ugen_get_cdesc(f, addr); 209502ac6454SAndrew Thompson break; 209602ac6454SAndrew Thompson 209702ac6454SAndrew Thompson case USB_GET_STRING_DESC: 209802ac6454SAndrew Thompson error = ugen_get_sdesc(f, addr); 209902ac6454SAndrew Thompson break; 210002ac6454SAndrew Thompson 210102ac6454SAndrew Thompson case USB_GET_IFACE_DRIVER: 210202ac6454SAndrew Thompson error = ugen_get_iface_driver(f, addr); 210302ac6454SAndrew Thompson break; 210402ac6454SAndrew Thompson 210502ac6454SAndrew Thompson case USB_REQUEST: 210602ac6454SAndrew Thompson case USB_DO_REQUEST: 210702ac6454SAndrew Thompson if (!(fflags & FWRITE)) { 210802ac6454SAndrew Thompson error = EPERM; 210902ac6454SAndrew Thompson break; 211002ac6454SAndrew Thompson } 211102ac6454SAndrew Thompson error = ugen_do_request(f, addr); 211202ac6454SAndrew Thompson break; 211302ac6454SAndrew Thompson 211402ac6454SAndrew Thompson case USB_DEVICEINFO: 211502ac6454SAndrew Thompson case USB_GET_DEVICEINFO: 2116a593f6b8SAndrew Thompson error = usb_gen_fill_deviceinfo(f, addr); 211702ac6454SAndrew Thompson break; 211802ac6454SAndrew Thompson 211902ac6454SAndrew Thompson case USB_DEVICESTATS: 212002ac6454SAndrew Thompson for (n = 0; n != 4; n++) { 212102ac6454SAndrew Thompson 212202ac6454SAndrew Thompson u.stat->uds_requests_fail[n] = 212302ac6454SAndrew Thompson f->udev->bus->stats_err.uds_requests[n]; 212402ac6454SAndrew Thompson 212502ac6454SAndrew Thompson u.stat->uds_requests_ok[n] = 212602ac6454SAndrew Thompson f->udev->bus->stats_ok.uds_requests[n]; 212702ac6454SAndrew Thompson } 212802ac6454SAndrew Thompson break; 212902ac6454SAndrew Thompson 213002ac6454SAndrew Thompson case USB_DEVICEENUMERATE: 213102ac6454SAndrew Thompson error = ugen_re_enumerate(f); 213202ac6454SAndrew Thompson break; 213302ac6454SAndrew Thompson 213402ac6454SAndrew Thompson case USB_GET_PLUGTIME: 213502ac6454SAndrew Thompson *u.ptime = f->udev->plugtime; 213602ac6454SAndrew Thompson break; 213702ac6454SAndrew Thompson 213802ac6454SAndrew Thompson case USB_CLAIM_INTERFACE: 213902ac6454SAndrew Thompson case USB_RELEASE_INTERFACE: 214002ac6454SAndrew Thompson /* TODO */ 214102ac6454SAndrew Thompson break; 214202ac6454SAndrew Thompson 214302ac6454SAndrew Thompson case USB_IFACE_DRIVER_ACTIVE: 2144a7aca4cdSAndrew Thompson 2145a7aca4cdSAndrew Thompson n = *u.pint & 0xFF; 2146a7aca4cdSAndrew Thompson 2147a7aca4cdSAndrew Thompson iface = usbd_get_iface(f->udev, n); 2148a7aca4cdSAndrew Thompson 2149a7aca4cdSAndrew Thompson if (iface && iface->subdev) 2150a7aca4cdSAndrew Thompson error = 0; 2151a7aca4cdSAndrew Thompson else 2152a7aca4cdSAndrew Thompson error = ENXIO; 215302ac6454SAndrew Thompson break; 215402ac6454SAndrew Thompson 215502ac6454SAndrew Thompson case USB_IFACE_DRIVER_DETACH: 2156a7aca4cdSAndrew Thompson 215702ac6454SAndrew Thompson error = priv_check(curthread, PRIV_DRIVER); 2158a7aca4cdSAndrew Thompson 2159a7aca4cdSAndrew Thompson if (error) 2160a7aca4cdSAndrew Thompson break; 2161a7aca4cdSAndrew Thompson 2162a7aca4cdSAndrew Thompson n = *u.pint & 0xFF; 2163a7aca4cdSAndrew Thompson 2164a7aca4cdSAndrew Thompson if (n == USB_IFACE_INDEX_ANY) { 2165a7aca4cdSAndrew Thompson error = EINVAL; 216602ac6454SAndrew Thompson break; 216702ac6454SAndrew Thompson } 2168a7aca4cdSAndrew Thompson 2169a7aca4cdSAndrew Thompson usb_detach_device(f->udev, n, 0); 217002ac6454SAndrew Thompson break; 217102ac6454SAndrew Thompson 217202ac6454SAndrew Thompson case USB_SET_POWER_MODE: 217302ac6454SAndrew Thompson error = ugen_set_power_mode(f, *u.pint); 217402ac6454SAndrew Thompson break; 217502ac6454SAndrew Thompson 217602ac6454SAndrew Thompson case USB_GET_POWER_MODE: 217702ac6454SAndrew Thompson *u.pint = ugen_get_power_mode(f); 217802ac6454SAndrew Thompson break; 217902ac6454SAndrew Thompson 218002ac6454SAndrew Thompson case USB_SET_PORT_ENABLE: 218102ac6454SAndrew Thompson error = ugen_do_port_feature(f, 218202ac6454SAndrew Thompson *u.pint, 1, UHF_PORT_ENABLE); 218302ac6454SAndrew Thompson break; 218402ac6454SAndrew Thompson 218502ac6454SAndrew Thompson case USB_SET_PORT_DISABLE: 218602ac6454SAndrew Thompson error = ugen_do_port_feature(f, 218702ac6454SAndrew Thompson *u.pint, 0, UHF_PORT_ENABLE); 218802ac6454SAndrew Thompson break; 218902ac6454SAndrew Thompson 219002ac6454SAndrew Thompson case USB_FS_INIT: 219102ac6454SAndrew Thompson /* verify input parameters */ 219202ac6454SAndrew Thompson if (u.pinit->pEndpoints == NULL) { 219302ac6454SAndrew Thompson error = EINVAL; 219402ac6454SAndrew Thompson break; 219502ac6454SAndrew Thompson } 219602ac6454SAndrew Thompson if (u.pinit->ep_index_max > 127) { 219702ac6454SAndrew Thompson error = EINVAL; 219802ac6454SAndrew Thompson break; 219902ac6454SAndrew Thompson } 220002ac6454SAndrew Thompson if (u.pinit->ep_index_max == 0) { 220102ac6454SAndrew Thompson error = EINVAL; 220202ac6454SAndrew Thompson break; 220302ac6454SAndrew Thompson } 220402ac6454SAndrew Thompson if (f->fs_xfer != NULL) { 220502ac6454SAndrew Thompson error = EBUSY; 220602ac6454SAndrew Thompson break; 220702ac6454SAndrew Thompson } 220802ac6454SAndrew Thompson if (f->dev_ep_index != 0) { 220902ac6454SAndrew Thompson error = EINVAL; 221002ac6454SAndrew Thompson break; 221102ac6454SAndrew Thompson } 221202ac6454SAndrew Thompson if (ugen_fifo_in_use(f, fflags)) { 221302ac6454SAndrew Thompson error = EBUSY; 221402ac6454SAndrew Thompson break; 221502ac6454SAndrew Thompson } 2216a593f6b8SAndrew Thompson error = usb_fifo_alloc_buffer(f, 1, u.pinit->ep_index_max); 221702ac6454SAndrew Thompson if (error) { 221802ac6454SAndrew Thompson break; 221902ac6454SAndrew Thompson } 222002ac6454SAndrew Thompson f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) * 222102ac6454SAndrew Thompson u.pinit->ep_index_max, M_USB, M_WAITOK | M_ZERO); 222202ac6454SAndrew Thompson if (f->fs_xfer == NULL) { 2223a593f6b8SAndrew Thompson usb_fifo_free_buffer(f); 222402ac6454SAndrew Thompson error = ENOMEM; 222502ac6454SAndrew Thompson break; 222602ac6454SAndrew Thompson } 222702ac6454SAndrew Thompson f->fs_ep_max = u.pinit->ep_index_max; 222802ac6454SAndrew Thompson f->fs_ep_ptr = u.pinit->pEndpoints; 222902ac6454SAndrew Thompson break; 223002ac6454SAndrew Thompson 223102ac6454SAndrew Thompson case USB_FS_UNINIT: 223202ac6454SAndrew Thompson if (u.puninit->dummy != 0) { 223302ac6454SAndrew Thompson error = EINVAL; 223402ac6454SAndrew Thompson break; 223502ac6454SAndrew Thompson } 223602ac6454SAndrew Thompson error = ugen_fs_uninit(f); 223702ac6454SAndrew Thompson break; 223802ac6454SAndrew Thompson 223902ac6454SAndrew Thompson default: 224002ac6454SAndrew Thompson mtx_lock(f->priv_mtx); 224102ac6454SAndrew Thompson error = ugen_iface_ioctl(f, cmd, addr, fflags); 224202ac6454SAndrew Thompson mtx_unlock(f->priv_mtx); 224302ac6454SAndrew Thompson break; 224402ac6454SAndrew Thompson } 224502ac6454SAndrew Thompson DPRINTFN(6, "error=%d\n", error); 224602ac6454SAndrew Thompson return (error); 224702ac6454SAndrew Thompson } 224802ac6454SAndrew Thompson 224902ac6454SAndrew Thompson static void 22505b3bb704SAndrew Thompson ugen_ctrl_fs_callback(struct usb_xfer *xfer, usb_error_t error) 225102ac6454SAndrew Thompson { 225202ac6454SAndrew Thompson ; /* workaround for a bug in "indent" */ 225302ac6454SAndrew Thompson 225402ac6454SAndrew Thompson DPRINTF("st=%u alen=%u aframes=%u\n", 225502ac6454SAndrew Thompson USB_GET_STATE(xfer), xfer->actlen, xfer->aframes); 225602ac6454SAndrew Thompson 225702ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 225802ac6454SAndrew Thompson case USB_ST_SETUP: 2259a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 226002ac6454SAndrew Thompson break; 226102ac6454SAndrew Thompson default: 226202ac6454SAndrew Thompson ugen_fs_set_complete(xfer->priv_sc, USB_P2U(xfer->priv_fifo)); 226302ac6454SAndrew Thompson break; 226402ac6454SAndrew Thompson } 226502ac6454SAndrew Thompson } 22668755859aSAndrew Thompson #endif /* USB_HAVE_UGEN */ 2267