xref: /freebsd/sys/dev/usb/usb_generic.c (revision 6950c75f)
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 
27d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE
28d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE
29d2b99310SHans Petter Selasky #else
30ed6d949aSAndrew Thompson #include <sys/stdint.h>
31ed6d949aSAndrew Thompson #include <sys/stddef.h>
32ed6d949aSAndrew Thompson #include <sys/param.h>
33ed6d949aSAndrew Thompson #include <sys/queue.h>
34ed6d949aSAndrew Thompson #include <sys/types.h>
35ed6d949aSAndrew Thompson #include <sys/systm.h>
36ed6d949aSAndrew Thompson #include <sys/kernel.h>
37ed6d949aSAndrew Thompson #include <sys/bus.h>
38ed6d949aSAndrew Thompson #include <sys/module.h>
39ed6d949aSAndrew Thompson #include <sys/lock.h>
40ed6d949aSAndrew Thompson #include <sys/mutex.h>
41ed6d949aSAndrew Thompson #include <sys/condvar.h>
42ed6d949aSAndrew Thompson #include <sys/sysctl.h>
43ed6d949aSAndrew Thompson #include <sys/sx.h>
44ed6d949aSAndrew Thompson #include <sys/unistd.h>
45ed6d949aSAndrew Thompson #include <sys/callout.h>
46ed6d949aSAndrew Thompson #include <sys/malloc.h>
47ed6d949aSAndrew Thompson #include <sys/priv.h>
48ed6d949aSAndrew Thompson #include <sys/conf.h>
49ed6d949aSAndrew Thompson #include <sys/fcntl.h>
50ed6d949aSAndrew Thompson 
5102ac6454SAndrew Thompson #include <dev/usb/usb.h>
5202ac6454SAndrew Thompson #include <dev/usb/usb_ioctl.h>
53ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
54ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
5502ac6454SAndrew Thompson 
5602ac6454SAndrew Thompson #define	USB_DEBUG_VAR ugen_debug
5702ac6454SAndrew Thompson 
5802ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
5902ac6454SAndrew Thompson #include <dev/usb/usb_dev.h>
60ed6d949aSAndrew Thompson #include <dev/usb/usb_mbuf.h>
6102ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
6202ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
6302ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
6402ac6454SAndrew Thompson #include <dev/usb/usb_request.h>
6502ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
6602ac6454SAndrew Thompson #include <dev/usb/usb_util.h>
6702ac6454SAndrew Thompson #include <dev/usb/usb_hub.h>
6802ac6454SAndrew Thompson #include <dev/usb/usb_generic.h>
6902ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h>
7002ac6454SAndrew Thompson 
7102ac6454SAndrew Thompson #include <dev/usb/usb_controller.h>
7202ac6454SAndrew Thompson #include <dev/usb/usb_bus.h>
73d2b99310SHans Petter Selasky #endif			/* USB_GLOBAL_INCLUDE_FILE */
7402ac6454SAndrew Thompson 
758755859aSAndrew Thompson #if USB_HAVE_UGEN
768755859aSAndrew Thompson 
7702ac6454SAndrew Thompson /* defines */
7802ac6454SAndrew Thompson 
7902ac6454SAndrew Thompson #define	UGEN_BULK_FS_BUFFER_SIZE	(64*32)	/* bytes */
8002ac6454SAndrew Thompson #define	UGEN_BULK_HS_BUFFER_SIZE	(1024*32)	/* bytes */
8102ac6454SAndrew Thompson #define	UGEN_HW_FRAMES	50		/* number of milliseconds per transfer */
8202ac6454SAndrew Thompson 
8302ac6454SAndrew Thompson /* function prototypes */
8402ac6454SAndrew Thompson 
85e0a69b51SAndrew Thompson static usb_callback_t ugen_read_clear_stall_callback;
86e0a69b51SAndrew Thompson static usb_callback_t ugen_write_clear_stall_callback;
875b3bb704SAndrew Thompson static usb_callback_t ugen_ctrl_read_callback;
885b3bb704SAndrew Thompson static usb_callback_t ugen_ctrl_write_callback;
89e0a69b51SAndrew Thompson static usb_callback_t ugen_isoc_read_callback;
90e0a69b51SAndrew Thompson static usb_callback_t ugen_isoc_write_callback;
915b3bb704SAndrew Thompson static usb_callback_t ugen_ctrl_fs_callback;
9202ac6454SAndrew Thompson 
93e0a69b51SAndrew Thompson static usb_fifo_open_t ugen_open;
94e0a69b51SAndrew Thompson static usb_fifo_close_t ugen_close;
95e0a69b51SAndrew Thompson static usb_fifo_ioctl_t ugen_ioctl;
96e0a69b51SAndrew Thompson static usb_fifo_ioctl_t ugen_ioctl_post;
97e0a69b51SAndrew Thompson static usb_fifo_cmd_t ugen_start_read;
98e0a69b51SAndrew Thompson static usb_fifo_cmd_t ugen_start_write;
99e0a69b51SAndrew Thompson static usb_fifo_cmd_t ugen_stop_io;
10002ac6454SAndrew Thompson 
101760bc48eSAndrew Thompson static int	ugen_transfer_setup(struct usb_fifo *,
102760bc48eSAndrew Thompson 		     const struct usb_config *, uint8_t);
103760bc48eSAndrew Thompson static int	ugen_open_pipe_write(struct usb_fifo *);
104760bc48eSAndrew Thompson static int	ugen_open_pipe_read(struct usb_fifo *);
105760bc48eSAndrew Thompson static int	ugen_set_config(struct usb_fifo *, uint8_t);
106760bc48eSAndrew Thompson static int	ugen_set_interface(struct usb_fifo *, uint8_t, uint8_t);
107760bc48eSAndrew Thompson static int	ugen_get_cdesc(struct usb_fifo *, struct usb_gen_descriptor *);
108760bc48eSAndrew Thompson static int	ugen_get_sdesc(struct usb_fifo *, struct usb_gen_descriptor *);
109760bc48eSAndrew Thompson static int	ugen_get_iface_driver(struct usb_fifo *f, struct usb_gen_descriptor *ugd);
110a593f6b8SAndrew Thompson static int	usb_gen_fill_deviceinfo(struct usb_fifo *,
111760bc48eSAndrew Thompson 		    struct usb_device_info *);
112760bc48eSAndrew Thompson static int	ugen_re_enumerate(struct usb_fifo *);
113760bc48eSAndrew Thompson static int	ugen_iface_ioctl(struct usb_fifo *, u_long, void *, int);
114760bc48eSAndrew Thompson static uint8_t	ugen_fs_get_complete(struct usb_fifo *, uint8_t *);
115760bc48eSAndrew Thompson static int	ugen_fs_uninit(struct usb_fifo *f);
11602ac6454SAndrew Thompson 
11702ac6454SAndrew Thompson /* structures */
11802ac6454SAndrew Thompson 
119a593f6b8SAndrew Thompson struct usb_fifo_methods usb_ugen_methods = {
12002ac6454SAndrew Thompson 	.f_open = &ugen_open,
12102ac6454SAndrew Thompson 	.f_close = &ugen_close,
12202ac6454SAndrew Thompson 	.f_ioctl = &ugen_ioctl,
12302ac6454SAndrew Thompson 	.f_ioctl_post = &ugen_ioctl_post,
12402ac6454SAndrew Thompson 	.f_start_read = &ugen_start_read,
12502ac6454SAndrew Thompson 	.f_stop_read = &ugen_stop_io,
12602ac6454SAndrew Thompson 	.f_start_write = &ugen_start_write,
12702ac6454SAndrew Thompson 	.f_stop_write = &ugen_stop_io,
12802ac6454SAndrew Thompson };
12902ac6454SAndrew Thompson 
130ed6d949aSAndrew Thompson #ifdef USB_DEBUG
13102ac6454SAndrew Thompson static int ugen_debug = 0;
13202ac6454SAndrew Thompson 
1336472ac3dSEd Schouten static SYSCTL_NODE(_hw_usb, OID_AUTO, ugen, CTLFLAG_RW, 0, "USB generic");
13483cadd7dSHans Petter Selasky SYSCTL_INT(_hw_usb_ugen, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &ugen_debug,
13502ac6454SAndrew Thompson     0, "Debug level");
136c13fd8d4SAndrew Thompson TUNABLE_INT("hw.usb.ugen.debug", &ugen_debug);
13702ac6454SAndrew Thompson #endif
13802ac6454SAndrew Thompson 
13902ac6454SAndrew Thompson 
14002ac6454SAndrew Thompson /* prototypes */
14102ac6454SAndrew Thompson 
14202ac6454SAndrew Thompson static int
143760bc48eSAndrew Thompson ugen_transfer_setup(struct usb_fifo *f,
144760bc48eSAndrew Thompson     const struct usb_config *setup, uint8_t n_setup)
14502ac6454SAndrew Thompson {
146ed6d949aSAndrew Thompson 	struct usb_endpoint *ep = usb_fifo_softc(f);
147760bc48eSAndrew Thompson 	struct usb_device *udev = f->udev;
148ae60fdfbSAndrew Thompson 	uint8_t iface_index = ep->iface_index;
14902ac6454SAndrew Thompson 	int error;
15002ac6454SAndrew Thompson 
15102ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
15202ac6454SAndrew Thompson 
15302ac6454SAndrew Thompson 	/*
154a593f6b8SAndrew Thompson 	 * "usbd_transfer_setup()" can sleep so one needs to make a wrapper,
15502ac6454SAndrew Thompson 	 * exiting the mutex and checking things
15602ac6454SAndrew Thompson 	 */
157a593f6b8SAndrew Thompson 	error = usbd_transfer_setup(udev, &iface_index, f->xfer,
15802ac6454SAndrew Thompson 	    setup, n_setup, f, f->priv_mtx);
15902ac6454SAndrew Thompson 	if (error == 0) {
16002ac6454SAndrew Thompson 
16102ac6454SAndrew Thompson 		if (f->xfer[0]->nframes == 1) {
162a593f6b8SAndrew Thompson 			error = usb_fifo_alloc_buffer(f,
16302ac6454SAndrew Thompson 			    f->xfer[0]->max_data_length, 2);
16402ac6454SAndrew Thompson 		} else {
165a593f6b8SAndrew Thompson 			error = usb_fifo_alloc_buffer(f,
16602ac6454SAndrew Thompson 			    f->xfer[0]->max_frame_size,
16702ac6454SAndrew Thompson 			    2 * f->xfer[0]->nframes);
16802ac6454SAndrew Thompson 		}
16902ac6454SAndrew Thompson 		if (error) {
170a593f6b8SAndrew Thompson 			usbd_transfer_unsetup(f->xfer, n_setup);
17102ac6454SAndrew Thompson 		}
17202ac6454SAndrew Thompson 	}
17302ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
17402ac6454SAndrew Thompson 
17502ac6454SAndrew Thompson 	return (error);
17602ac6454SAndrew Thompson }
17702ac6454SAndrew Thompson 
17802ac6454SAndrew Thompson static int
179760bc48eSAndrew Thompson ugen_open(struct usb_fifo *f, int fflags)
18002ac6454SAndrew Thompson {
181ed6d949aSAndrew Thompson 	struct usb_endpoint *ep = usb_fifo_softc(f);
182ae60fdfbSAndrew Thompson 	struct usb_endpoint_descriptor *ed = ep->edesc;
18302ac6454SAndrew Thompson 	uint8_t type;
18402ac6454SAndrew Thompson 
18502ac6454SAndrew Thompson 	DPRINTFN(6, "flag=0x%x\n", fflags);
18602ac6454SAndrew Thompson 
18702ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
188a593f6b8SAndrew Thompson 	switch (usbd_get_speed(f->udev)) {
18902ac6454SAndrew Thompson 	case USB_SPEED_LOW:
19002ac6454SAndrew Thompson 	case USB_SPEED_FULL:
19102ac6454SAndrew Thompson 		f->nframes = UGEN_HW_FRAMES;
19202ac6454SAndrew Thompson 		f->bufsize = UGEN_BULK_FS_BUFFER_SIZE;
19302ac6454SAndrew Thompson 		break;
19402ac6454SAndrew Thompson 	default:
19502ac6454SAndrew Thompson 		f->nframes = UGEN_HW_FRAMES * 8;
19602ac6454SAndrew Thompson 		f->bufsize = UGEN_BULK_HS_BUFFER_SIZE;
19702ac6454SAndrew Thompson 		break;
19802ac6454SAndrew Thompson 	}
19902ac6454SAndrew Thompson 
20002ac6454SAndrew Thompson 	type = ed->bmAttributes & UE_XFERTYPE;
20102ac6454SAndrew Thompson 	if (type == UE_INTERRUPT) {
20202ac6454SAndrew Thompson 		f->bufsize = 0;		/* use "wMaxPacketSize" */
20302ac6454SAndrew Thompson 	}
20402ac6454SAndrew Thompson 	f->timeout = USB_NO_TIMEOUT;
20502ac6454SAndrew Thompson 	f->flag_short = 0;
20602ac6454SAndrew Thompson 	f->fifo_zlp = 0;
20702ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
20802ac6454SAndrew Thompson 
20902ac6454SAndrew Thompson 	return (0);
21002ac6454SAndrew Thompson }
21102ac6454SAndrew Thompson 
21202ac6454SAndrew Thompson static void
213760bc48eSAndrew Thompson ugen_close(struct usb_fifo *f, int fflags)
21402ac6454SAndrew Thompson {
21502ac6454SAndrew Thompson 	DPRINTFN(6, "flag=0x%x\n", fflags);
21602ac6454SAndrew Thompson 
21702ac6454SAndrew Thompson 	/* cleanup */
21802ac6454SAndrew Thompson 
21902ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
220a593f6b8SAndrew Thompson 	usbd_transfer_stop(f->xfer[0]);
221a593f6b8SAndrew Thompson 	usbd_transfer_stop(f->xfer[1]);
22202ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
22302ac6454SAndrew Thompson 
224a593f6b8SAndrew Thompson 	usbd_transfer_unsetup(f->xfer, 2);
225a593f6b8SAndrew Thompson 	usb_fifo_free_buffer(f);
22602ac6454SAndrew Thompson 
22702ac6454SAndrew Thompson 	if (ugen_fs_uninit(f)) {
22802ac6454SAndrew Thompson 		/* ignore any errors - we are closing */
22902ac6454SAndrew Thompson 		DPRINTFN(6, "no FIFOs\n");
23002ac6454SAndrew Thompson 	}
23102ac6454SAndrew Thompson }
23202ac6454SAndrew Thompson 
23302ac6454SAndrew Thompson static int
234760bc48eSAndrew Thompson ugen_open_pipe_write(struct usb_fifo *f)
23502ac6454SAndrew Thompson {
236760bc48eSAndrew Thompson 	struct usb_config usb_config[2];
237ed6d949aSAndrew Thompson 	struct usb_endpoint *ep = usb_fifo_softc(f);
238ae60fdfbSAndrew Thompson 	struct usb_endpoint_descriptor *ed = ep->edesc;
23902ac6454SAndrew Thompson 
24002ac6454SAndrew Thompson 	mtx_assert(f->priv_mtx, MA_OWNED);
24102ac6454SAndrew Thompson 
24202ac6454SAndrew Thompson 	if (f->xfer[0] || f->xfer[1]) {
24302ac6454SAndrew Thompson 		/* transfers are already opened */
24402ac6454SAndrew Thompson 		return (0);
24502ac6454SAndrew Thompson 	}
246271ae033SHans Petter Selasky 	memset(usb_config, 0, sizeof(usb_config));
24702ac6454SAndrew Thompson 
248760bc48eSAndrew Thompson 	usb_config[1].type = UE_CONTROL;
249760bc48eSAndrew Thompson 	usb_config[1].endpoint = 0;
250760bc48eSAndrew Thompson 	usb_config[1].direction = UE_DIR_ANY;
251760bc48eSAndrew Thompson 	usb_config[1].timeout = 1000;	/* 1 second */
252760bc48eSAndrew Thompson 	usb_config[1].interval = 50;/* 50 milliseconds */
253760bc48eSAndrew Thompson 	usb_config[1].bufsize = sizeof(struct usb_device_request);
254760bc48eSAndrew Thompson 	usb_config[1].callback = &ugen_write_clear_stall_callback;
255760bc48eSAndrew Thompson 	usb_config[1].usb_mode = USB_MODE_HOST;
25602ac6454SAndrew Thompson 
257760bc48eSAndrew Thompson 	usb_config[0].type = ed->bmAttributes & UE_XFERTYPE;
258760bc48eSAndrew Thompson 	usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR;
259a5cf1aaaSHans Petter Selasky 	usb_config[0].stream_id = 0;	/* XXX support more stream ID's */
260760bc48eSAndrew Thompson 	usb_config[0].direction = UE_DIR_TX;
261760bc48eSAndrew Thompson 	usb_config[0].interval = USB_DEFAULT_INTERVAL;
262760bc48eSAndrew Thompson 	usb_config[0].flags.proxy_buffer = 1;
263760bc48eSAndrew Thompson 	usb_config[0].usb_mode = USB_MODE_DUAL;	/* both modes */
26402ac6454SAndrew Thompson 
26502ac6454SAndrew Thompson 	switch (ed->bmAttributes & UE_XFERTYPE) {
26602ac6454SAndrew Thompson 	case UE_INTERRUPT:
26702ac6454SAndrew Thompson 	case UE_BULK:
26802ac6454SAndrew Thompson 		if (f->flag_short) {
269760bc48eSAndrew Thompson 			usb_config[0].flags.force_short_xfer = 1;
27002ac6454SAndrew Thompson 		}
2715b3bb704SAndrew Thompson 		usb_config[0].callback = &ugen_ctrl_write_callback;
272760bc48eSAndrew Thompson 		usb_config[0].timeout = f->timeout;
273760bc48eSAndrew Thompson 		usb_config[0].frames = 1;
274760bc48eSAndrew Thompson 		usb_config[0].bufsize = f->bufsize;
275760bc48eSAndrew Thompson 		if (ugen_transfer_setup(f, usb_config, 2)) {
27602ac6454SAndrew Thompson 			return (EIO);
27702ac6454SAndrew Thompson 		}
27802ac6454SAndrew Thompson 		/* first transfer does not clear stall */
27902ac6454SAndrew Thompson 		f->flag_stall = 0;
28002ac6454SAndrew Thompson 		break;
28102ac6454SAndrew Thompson 
28202ac6454SAndrew Thompson 	case UE_ISOCHRONOUS:
283760bc48eSAndrew Thompson 		usb_config[0].flags.short_xfer_ok = 1;
284760bc48eSAndrew Thompson 		usb_config[0].bufsize = 0;	/* use default */
285760bc48eSAndrew Thompson 		usb_config[0].frames = f->nframes;
286760bc48eSAndrew Thompson 		usb_config[0].callback = &ugen_isoc_write_callback;
287760bc48eSAndrew Thompson 		usb_config[0].timeout = 0;
28802ac6454SAndrew Thompson 
28902ac6454SAndrew Thompson 		/* clone configuration */
290760bc48eSAndrew Thompson 		usb_config[1] = usb_config[0];
29102ac6454SAndrew Thompson 
292760bc48eSAndrew Thompson 		if (ugen_transfer_setup(f, usb_config, 2)) {
29302ac6454SAndrew Thompson 			return (EIO);
29402ac6454SAndrew Thompson 		}
29502ac6454SAndrew Thompson 		break;
29602ac6454SAndrew Thompson 	default:
29702ac6454SAndrew Thompson 		return (EINVAL);
29802ac6454SAndrew Thompson 	}
29902ac6454SAndrew Thompson 	return (0);
30002ac6454SAndrew Thompson }
30102ac6454SAndrew Thompson 
30202ac6454SAndrew Thompson static int
303760bc48eSAndrew Thompson ugen_open_pipe_read(struct usb_fifo *f)
30402ac6454SAndrew Thompson {
305760bc48eSAndrew Thompson 	struct usb_config usb_config[2];
306ed6d949aSAndrew Thompson 	struct usb_endpoint *ep = usb_fifo_softc(f);
307ae60fdfbSAndrew Thompson 	struct usb_endpoint_descriptor *ed = ep->edesc;
30802ac6454SAndrew Thompson 
30902ac6454SAndrew Thompson 	mtx_assert(f->priv_mtx, MA_OWNED);
31002ac6454SAndrew Thompson 
31102ac6454SAndrew Thompson 	if (f->xfer[0] || f->xfer[1]) {
31202ac6454SAndrew Thompson 		/* transfers are already opened */
31302ac6454SAndrew Thompson 		return (0);
31402ac6454SAndrew Thompson 	}
315271ae033SHans Petter Selasky 	memset(usb_config, 0, sizeof(usb_config));
31602ac6454SAndrew Thompson 
317760bc48eSAndrew Thompson 	usb_config[1].type = UE_CONTROL;
318760bc48eSAndrew Thompson 	usb_config[1].endpoint = 0;
319760bc48eSAndrew Thompson 	usb_config[1].direction = UE_DIR_ANY;
320760bc48eSAndrew Thompson 	usb_config[1].timeout = 1000;	/* 1 second */
321760bc48eSAndrew Thompson 	usb_config[1].interval = 50;/* 50 milliseconds */
322760bc48eSAndrew Thompson 	usb_config[1].bufsize = sizeof(struct usb_device_request);
323760bc48eSAndrew Thompson 	usb_config[1].callback = &ugen_read_clear_stall_callback;
324760bc48eSAndrew Thompson 	usb_config[1].usb_mode = USB_MODE_HOST;
32502ac6454SAndrew Thompson 
326760bc48eSAndrew Thompson 	usb_config[0].type = ed->bmAttributes & UE_XFERTYPE;
327760bc48eSAndrew Thompson 	usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR;
328a5cf1aaaSHans Petter Selasky 	usb_config[0].stream_id = 0;	/* XXX support more stream ID's */
329760bc48eSAndrew Thompson 	usb_config[0].direction = UE_DIR_RX;
330760bc48eSAndrew Thompson 	usb_config[0].interval = USB_DEFAULT_INTERVAL;
331760bc48eSAndrew Thompson 	usb_config[0].flags.proxy_buffer = 1;
332760bc48eSAndrew Thompson 	usb_config[0].usb_mode = USB_MODE_DUAL;	/* both modes */
33302ac6454SAndrew Thompson 
33402ac6454SAndrew Thompson 	switch (ed->bmAttributes & UE_XFERTYPE) {
33502ac6454SAndrew Thompson 	case UE_INTERRUPT:
33602ac6454SAndrew Thompson 	case UE_BULK:
33702ac6454SAndrew Thompson 		if (f->flag_short) {
338760bc48eSAndrew Thompson 			usb_config[0].flags.short_xfer_ok = 1;
33902ac6454SAndrew Thompson 		}
340760bc48eSAndrew Thompson 		usb_config[0].timeout = f->timeout;
341760bc48eSAndrew Thompson 		usb_config[0].frames = 1;
3425b3bb704SAndrew Thompson 		usb_config[0].callback = &ugen_ctrl_read_callback;
343760bc48eSAndrew Thompson 		usb_config[0].bufsize = f->bufsize;
34402ac6454SAndrew Thompson 
345760bc48eSAndrew Thompson 		if (ugen_transfer_setup(f, usb_config, 2)) {
34602ac6454SAndrew Thompson 			return (EIO);
34702ac6454SAndrew Thompson 		}
34802ac6454SAndrew Thompson 		/* first transfer does not clear stall */
34902ac6454SAndrew Thompson 		f->flag_stall = 0;
35002ac6454SAndrew Thompson 		break;
35102ac6454SAndrew Thompson 
35202ac6454SAndrew Thompson 	case UE_ISOCHRONOUS:
353760bc48eSAndrew Thompson 		usb_config[0].flags.short_xfer_ok = 1;
354760bc48eSAndrew Thompson 		usb_config[0].bufsize = 0;	/* use default */
355760bc48eSAndrew Thompson 		usb_config[0].frames = f->nframes;
356760bc48eSAndrew Thompson 		usb_config[0].callback = &ugen_isoc_read_callback;
357760bc48eSAndrew Thompson 		usb_config[0].timeout = 0;
35802ac6454SAndrew Thompson 
35902ac6454SAndrew Thompson 		/* clone configuration */
360760bc48eSAndrew Thompson 		usb_config[1] = usb_config[0];
36102ac6454SAndrew Thompson 
362760bc48eSAndrew Thompson 		if (ugen_transfer_setup(f, usb_config, 2)) {
36302ac6454SAndrew Thompson 			return (EIO);
36402ac6454SAndrew Thompson 		}
36502ac6454SAndrew Thompson 		break;
36602ac6454SAndrew Thompson 
36702ac6454SAndrew Thompson 	default:
36802ac6454SAndrew Thompson 		return (EINVAL);
36902ac6454SAndrew Thompson 	}
37002ac6454SAndrew Thompson 	return (0);
37102ac6454SAndrew Thompson }
37202ac6454SAndrew Thompson 
37302ac6454SAndrew Thompson static void
374760bc48eSAndrew Thompson ugen_start_read(struct usb_fifo *f)
37502ac6454SAndrew Thompson {
37602ac6454SAndrew Thompson 	/* check that pipes are open */
37702ac6454SAndrew Thompson 	if (ugen_open_pipe_read(f)) {
37802ac6454SAndrew Thompson 		/* signal error */
379a593f6b8SAndrew Thompson 		usb_fifo_put_data_error(f);
38002ac6454SAndrew Thompson 	}
38102ac6454SAndrew Thompson 	/* start transfers */
382a593f6b8SAndrew Thompson 	usbd_transfer_start(f->xfer[0]);
383a593f6b8SAndrew Thompson 	usbd_transfer_start(f->xfer[1]);
38402ac6454SAndrew Thompson }
38502ac6454SAndrew Thompson 
38602ac6454SAndrew Thompson static void
387760bc48eSAndrew Thompson ugen_start_write(struct usb_fifo *f)
38802ac6454SAndrew Thompson {
38902ac6454SAndrew Thompson 	/* check that pipes are open */
39002ac6454SAndrew Thompson 	if (ugen_open_pipe_write(f)) {
39102ac6454SAndrew Thompson 		/* signal error */
392a593f6b8SAndrew Thompson 		usb_fifo_get_data_error(f);
39302ac6454SAndrew Thompson 	}
39402ac6454SAndrew Thompson 	/* start transfers */
395a593f6b8SAndrew Thompson 	usbd_transfer_start(f->xfer[0]);
396a593f6b8SAndrew Thompson 	usbd_transfer_start(f->xfer[1]);
39702ac6454SAndrew Thompson }
39802ac6454SAndrew Thompson 
39902ac6454SAndrew Thompson static void
400760bc48eSAndrew Thompson ugen_stop_io(struct usb_fifo *f)
40102ac6454SAndrew Thompson {
40202ac6454SAndrew Thompson 	/* stop transfers */
403a593f6b8SAndrew Thompson 	usbd_transfer_stop(f->xfer[0]);
404a593f6b8SAndrew Thompson 	usbd_transfer_stop(f->xfer[1]);
40502ac6454SAndrew Thompson }
40602ac6454SAndrew Thompson 
40702ac6454SAndrew Thompson static void
4085b3bb704SAndrew Thompson ugen_ctrl_read_callback(struct usb_xfer *xfer, usb_error_t error)
40902ac6454SAndrew Thompson {
410ed6d949aSAndrew Thompson 	struct usb_fifo *f = usbd_xfer_softc(xfer);
411760bc48eSAndrew Thompson 	struct usb_mbuf *m;
41202ac6454SAndrew Thompson 
41302ac6454SAndrew Thompson 	DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes);
41402ac6454SAndrew Thompson 
41502ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
41602ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
41702ac6454SAndrew Thompson 		if (xfer->actlen == 0) {
41802ac6454SAndrew Thompson 			if (f->fifo_zlp != 4) {
41902ac6454SAndrew Thompson 				f->fifo_zlp++;
42002ac6454SAndrew Thompson 			} else {
42102ac6454SAndrew Thompson 				/*
42202ac6454SAndrew Thompson 				 * Throttle a little bit we have multiple ZLPs
42302ac6454SAndrew Thompson 				 * in a row!
42402ac6454SAndrew Thompson 				 */
42502ac6454SAndrew Thompson 				xfer->interval = 64;	/* ms */
42602ac6454SAndrew Thompson 			}
42702ac6454SAndrew Thompson 		} else {
42802ac6454SAndrew Thompson 			/* clear throttle */
42902ac6454SAndrew Thompson 			xfer->interval = 0;
43002ac6454SAndrew Thompson 			f->fifo_zlp = 0;
43102ac6454SAndrew Thompson 		}
432a593f6b8SAndrew Thompson 		usb_fifo_put_data(f, xfer->frbuffers, 0,
43302ac6454SAndrew Thompson 		    xfer->actlen, 1);
43402ac6454SAndrew Thompson 
43502ac6454SAndrew Thompson 	case USB_ST_SETUP:
43602ac6454SAndrew Thompson 		if (f->flag_stall) {
437a593f6b8SAndrew Thompson 			usbd_transfer_start(f->xfer[1]);
43802ac6454SAndrew Thompson 			break;
43902ac6454SAndrew Thompson 		}
44002ac6454SAndrew Thompson 		USB_IF_POLL(&f->free_q, m);
44102ac6454SAndrew Thompson 		if (m) {
442ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
443a593f6b8SAndrew Thompson 			usbd_transfer_submit(xfer);
44402ac6454SAndrew Thompson 		}
44502ac6454SAndrew Thompson 		break;
44602ac6454SAndrew Thompson 
44702ac6454SAndrew Thompson 	default:			/* Error */
44802ac6454SAndrew Thompson 		if (xfer->error != USB_ERR_CANCELLED) {
44901cf7831SAndrew Thompson 			/* send a zero length packet to userland */
450a593f6b8SAndrew Thompson 			usb_fifo_put_data(f, xfer->frbuffers, 0, 0, 1);
45102ac6454SAndrew Thompson 			f->flag_stall = 1;
45202ac6454SAndrew Thompson 			f->fifo_zlp = 0;
453a593f6b8SAndrew Thompson 			usbd_transfer_start(f->xfer[1]);
45402ac6454SAndrew Thompson 		}
45502ac6454SAndrew Thompson 		break;
45602ac6454SAndrew Thompson 	}
45702ac6454SAndrew Thompson }
45802ac6454SAndrew Thompson 
45902ac6454SAndrew Thompson static void
4605b3bb704SAndrew Thompson ugen_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
46102ac6454SAndrew Thompson {
462ed6d949aSAndrew Thompson 	struct usb_fifo *f = usbd_xfer_softc(xfer);
463e0a69b51SAndrew Thompson 	usb_frlength_t actlen;
46402ac6454SAndrew Thompson 
46502ac6454SAndrew Thompson 	DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes);
46602ac6454SAndrew Thompson 
46702ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
46802ac6454SAndrew Thompson 	case USB_ST_SETUP:
46902ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
47002ac6454SAndrew Thompson 		/*
47102ac6454SAndrew Thompson 		 * If writing is in stall, just jump to clear stall
47202ac6454SAndrew Thompson 		 * callback and solve the situation.
47302ac6454SAndrew Thompson 		 */
47402ac6454SAndrew Thompson 		if (f->flag_stall) {
475a593f6b8SAndrew Thompson 			usbd_transfer_start(f->xfer[1]);
47602ac6454SAndrew Thompson 			break;
47702ac6454SAndrew Thompson 		}
47802ac6454SAndrew Thompson 		/*
47902ac6454SAndrew Thompson 		 * Write data, setup and perform hardware transfer.
48002ac6454SAndrew Thompson 		 */
481a593f6b8SAndrew Thompson 		if (usb_fifo_get_data(f, xfer->frbuffers, 0,
48202ac6454SAndrew Thompson 		    xfer->max_data_length, &actlen, 0)) {
483ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, 0, actlen);
484a593f6b8SAndrew Thompson 			usbd_transfer_submit(xfer);
48502ac6454SAndrew Thompson 		}
48602ac6454SAndrew Thompson 		break;
48702ac6454SAndrew Thompson 
48802ac6454SAndrew Thompson 	default:			/* Error */
48902ac6454SAndrew Thompson 		if (xfer->error != USB_ERR_CANCELLED) {
49002ac6454SAndrew Thompson 			f->flag_stall = 1;
491a593f6b8SAndrew Thompson 			usbd_transfer_start(f->xfer[1]);
49202ac6454SAndrew Thompson 		}
49302ac6454SAndrew Thompson 		break;
49402ac6454SAndrew Thompson 	}
49502ac6454SAndrew Thompson }
49602ac6454SAndrew Thompson 
49702ac6454SAndrew Thompson static void
498ed6d949aSAndrew Thompson ugen_read_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error)
49902ac6454SAndrew Thompson {
500ed6d949aSAndrew Thompson 	struct usb_fifo *f = usbd_xfer_softc(xfer);
501760bc48eSAndrew Thompson 	struct usb_xfer *xfer_other = f->xfer[0];
50202ac6454SAndrew Thompson 
50302ac6454SAndrew Thompson 	if (f->flag_stall == 0) {
50402ac6454SAndrew Thompson 		/* nothing to do */
50502ac6454SAndrew Thompson 		return;
50602ac6454SAndrew Thompson 	}
507a593f6b8SAndrew Thompson 	if (usbd_clear_stall_callback(xfer, xfer_other)) {
50802ac6454SAndrew Thompson 		DPRINTFN(5, "f=%p: stall cleared\n", f);
50902ac6454SAndrew Thompson 		f->flag_stall = 0;
510a593f6b8SAndrew Thompson 		usbd_transfer_start(xfer_other);
51102ac6454SAndrew Thompson 	}
51202ac6454SAndrew Thompson }
51302ac6454SAndrew Thompson 
51402ac6454SAndrew Thompson static void
515ed6d949aSAndrew Thompson ugen_write_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error)
51602ac6454SAndrew Thompson {
517ed6d949aSAndrew Thompson 	struct usb_fifo *f = usbd_xfer_softc(xfer);
518760bc48eSAndrew Thompson 	struct usb_xfer *xfer_other = f->xfer[0];
51902ac6454SAndrew Thompson 
52002ac6454SAndrew Thompson 	if (f->flag_stall == 0) {
52102ac6454SAndrew Thompson 		/* nothing to do */
52202ac6454SAndrew Thompson 		return;
52302ac6454SAndrew Thompson 	}
524a593f6b8SAndrew Thompson 	if (usbd_clear_stall_callback(xfer, xfer_other)) {
52502ac6454SAndrew Thompson 		DPRINTFN(5, "f=%p: stall cleared\n", f);
52602ac6454SAndrew Thompson 		f->flag_stall = 0;
527a593f6b8SAndrew Thompson 		usbd_transfer_start(xfer_other);
52802ac6454SAndrew Thompson 	}
52902ac6454SAndrew Thompson }
53002ac6454SAndrew Thompson 
53102ac6454SAndrew Thompson static void
532ed6d949aSAndrew Thompson ugen_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
53302ac6454SAndrew Thompson {
534ed6d949aSAndrew Thompson 	struct usb_fifo *f = usbd_xfer_softc(xfer);
535e0a69b51SAndrew Thompson 	usb_frlength_t offset;
536e0a69b51SAndrew Thompson 	usb_frcount_t n;
53702ac6454SAndrew Thompson 
53802ac6454SAndrew Thompson 	DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes);
53902ac6454SAndrew Thompson 
54002ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
54102ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
54202ac6454SAndrew Thompson 
54302ac6454SAndrew Thompson 		DPRINTFN(6, "actlen=%d\n", xfer->actlen);
54402ac6454SAndrew Thompson 
54502ac6454SAndrew Thompson 		offset = 0;
54602ac6454SAndrew Thompson 
54702ac6454SAndrew Thompson 		for (n = 0; n != xfer->aframes; n++) {
548a593f6b8SAndrew Thompson 			usb_fifo_put_data(f, xfer->frbuffers, offset,
54902ac6454SAndrew Thompson 			    xfer->frlengths[n], 1);
55002ac6454SAndrew Thompson 			offset += xfer->max_frame_size;
55102ac6454SAndrew Thompson 		}
55202ac6454SAndrew Thompson 
55302ac6454SAndrew Thompson 	case USB_ST_SETUP:
55402ac6454SAndrew Thompson tr_setup:
55502ac6454SAndrew Thompson 		for (n = 0; n != xfer->nframes; n++) {
55602ac6454SAndrew Thompson 			/* setup size for next transfer */
557ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, n, xfer->max_frame_size);
55802ac6454SAndrew Thompson 		}
559a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
56002ac6454SAndrew Thompson 		break;
56102ac6454SAndrew Thompson 
56202ac6454SAndrew Thompson 	default:			/* Error */
56302ac6454SAndrew Thompson 		if (xfer->error == USB_ERR_CANCELLED) {
56402ac6454SAndrew Thompson 			break;
56502ac6454SAndrew Thompson 		}
56602ac6454SAndrew Thompson 		goto tr_setup;
56702ac6454SAndrew Thompson 	}
56802ac6454SAndrew Thompson }
56902ac6454SAndrew Thompson 
57002ac6454SAndrew Thompson static void
571ed6d949aSAndrew Thompson ugen_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
57202ac6454SAndrew Thompson {
573ed6d949aSAndrew Thompson 	struct usb_fifo *f = usbd_xfer_softc(xfer);
574e0a69b51SAndrew Thompson 	usb_frlength_t actlen;
575e0a69b51SAndrew Thompson 	usb_frlength_t offset;
576e0a69b51SAndrew Thompson 	usb_frcount_t n;
57702ac6454SAndrew Thompson 
57802ac6454SAndrew Thompson 	DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes);
57902ac6454SAndrew Thompson 
58002ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
58102ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
58202ac6454SAndrew Thompson 	case USB_ST_SETUP:
58302ac6454SAndrew Thompson tr_setup:
58402ac6454SAndrew Thompson 		offset = 0;
58502ac6454SAndrew Thompson 		for (n = 0; n != xfer->nframes; n++) {
586a593f6b8SAndrew Thompson 			if (usb_fifo_get_data(f, xfer->frbuffers, offset,
58702ac6454SAndrew Thompson 			    xfer->max_frame_size, &actlen, 1)) {
588ed6d949aSAndrew Thompson 				usbd_xfer_set_frame_len(xfer, n, actlen);
58902ac6454SAndrew Thompson 				offset += actlen;
59002ac6454SAndrew Thompson 			} else {
59102ac6454SAndrew Thompson 				break;
59202ac6454SAndrew Thompson 			}
59302ac6454SAndrew Thompson 		}
59402ac6454SAndrew Thompson 
59502ac6454SAndrew Thompson 		for (; n != xfer->nframes; n++) {
59602ac6454SAndrew Thompson 			/* fill in zero frames */
597ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, n, 0);
59802ac6454SAndrew Thompson 		}
599a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
60002ac6454SAndrew Thompson 		break;
60102ac6454SAndrew Thompson 
60202ac6454SAndrew Thompson 	default:			/* Error */
60302ac6454SAndrew Thompson 		if (xfer->error == USB_ERR_CANCELLED) {
60402ac6454SAndrew Thompson 			break;
60502ac6454SAndrew Thompson 		}
60602ac6454SAndrew Thompson 		goto tr_setup;
60702ac6454SAndrew Thompson 	}
60802ac6454SAndrew Thompson }
60902ac6454SAndrew Thompson 
61002ac6454SAndrew Thompson static int
611760bc48eSAndrew Thompson ugen_set_config(struct usb_fifo *f, uint8_t index)
61202ac6454SAndrew Thompson {
61302ac6454SAndrew Thompson 	DPRINTFN(2, "index %u\n", index);
61402ac6454SAndrew Thompson 
615f29a0724SAndrew Thompson 	if (f->udev->flags.usb_mode != USB_MODE_HOST) {
61602ac6454SAndrew Thompson 		/* not possible in device side mode */
61702ac6454SAndrew Thompson 		return (ENOTTY);
61802ac6454SAndrew Thompson 	}
61902ac6454SAndrew Thompson 	if (f->udev->curr_config_index == index) {
62002ac6454SAndrew Thompson 		/* no change needed */
62102ac6454SAndrew Thompson 		return (0);
62202ac6454SAndrew Thompson 	}
62302ac6454SAndrew Thompson 	/* make sure all FIFO's are gone */
62402ac6454SAndrew Thompson 	/* else there can be a deadlock */
62502ac6454SAndrew Thompson 	if (ugen_fs_uninit(f)) {
62602ac6454SAndrew Thompson 		/* ignore any errors */
62702ac6454SAndrew Thompson 		DPRINTFN(6, "no FIFOs\n");
62802ac6454SAndrew Thompson 	}
62902ac6454SAndrew Thompson 	/* change setting - will free generic FIFOs, if any */
630a593f6b8SAndrew Thompson 	if (usbd_set_config_index(f->udev, index)) {
63102ac6454SAndrew Thompson 		return (EIO);
63202ac6454SAndrew Thompson 	}
63302ac6454SAndrew Thompson 	/* probe and attach */
634a593f6b8SAndrew Thompson 	if (usb_probe_and_attach(f->udev, USB_IFACE_INDEX_ANY)) {
63502ac6454SAndrew Thompson 		return (EIO);
63602ac6454SAndrew Thompson 	}
63702ac6454SAndrew Thompson 	return (0);
63802ac6454SAndrew Thompson }
63902ac6454SAndrew Thompson 
64002ac6454SAndrew Thompson static int
641760bc48eSAndrew Thompson ugen_set_interface(struct usb_fifo *f,
64202ac6454SAndrew Thompson     uint8_t iface_index, uint8_t alt_index)
64302ac6454SAndrew Thompson {
64402ac6454SAndrew Thompson 	DPRINTFN(2, "%u, %u\n", iface_index, alt_index);
64502ac6454SAndrew Thompson 
646f29a0724SAndrew Thompson 	if (f->udev->flags.usb_mode != USB_MODE_HOST) {
64702ac6454SAndrew Thompson 		/* not possible in device side mode */
64802ac6454SAndrew Thompson 		return (ENOTTY);
64902ac6454SAndrew Thompson 	}
65002ac6454SAndrew Thompson 	/* make sure all FIFO's are gone */
65102ac6454SAndrew Thompson 	/* else there can be a deadlock */
65202ac6454SAndrew Thompson 	if (ugen_fs_uninit(f)) {
65302ac6454SAndrew Thompson 		/* ignore any errors */
65402ac6454SAndrew Thompson 		DPRINTFN(6, "no FIFOs\n");
65502ac6454SAndrew Thompson 	}
65602ac6454SAndrew Thompson 	/* change setting - will free generic FIFOs, if any */
657a593f6b8SAndrew Thompson 	if (usbd_set_alt_interface_index(f->udev, iface_index, alt_index)) {
65802ac6454SAndrew Thompson 		return (EIO);
65902ac6454SAndrew Thompson 	}
66002ac6454SAndrew Thompson 	/* probe and attach */
661a593f6b8SAndrew Thompson 	if (usb_probe_and_attach(f->udev, iface_index)) {
66202ac6454SAndrew Thompson 		return (EIO);
66302ac6454SAndrew Thompson 	}
66402ac6454SAndrew Thompson 	return (0);
66502ac6454SAndrew Thompson }
66602ac6454SAndrew Thompson 
66702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
66802ac6454SAndrew Thompson  *	ugen_get_cdesc
66902ac6454SAndrew Thompson  *
67002ac6454SAndrew Thompson  * This function will retrieve the complete configuration descriptor
67102ac6454SAndrew Thompson  * at the given index.
67202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
67302ac6454SAndrew Thompson static int
674760bc48eSAndrew Thompson ugen_get_cdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd)
67502ac6454SAndrew Thompson {
676760bc48eSAndrew Thompson 	struct usb_config_descriptor *cdesc;
677760bc48eSAndrew Thompson 	struct usb_device *udev = f->udev;
67802ac6454SAndrew Thompson 	int error;
67902ac6454SAndrew Thompson 	uint16_t len;
68002ac6454SAndrew Thompson 	uint8_t free_data;
68102ac6454SAndrew Thompson 
68202ac6454SAndrew Thompson 	DPRINTFN(6, "\n");
68302ac6454SAndrew Thompson 
68402ac6454SAndrew Thompson 	if (ugd->ugd_data == NULL) {
68502ac6454SAndrew Thompson 		/* userland pointer should not be zero */
68602ac6454SAndrew Thompson 		return (EINVAL);
68702ac6454SAndrew Thompson 	}
68802ac6454SAndrew Thompson 	if ((ugd->ugd_config_index == USB_UNCONFIG_INDEX) ||
68902ac6454SAndrew Thompson 	    (ugd->ugd_config_index == udev->curr_config_index)) {
690a593f6b8SAndrew Thompson 		cdesc = usbd_get_config_descriptor(udev);
69102ac6454SAndrew Thompson 		if (cdesc == NULL) {
69202ac6454SAndrew Thompson 			return (ENXIO);
69302ac6454SAndrew Thompson 		}
69402ac6454SAndrew Thompson 		free_data = 0;
69502ac6454SAndrew Thompson 
69602ac6454SAndrew Thompson 	} else {
697a593f6b8SAndrew Thompson 		if (usbd_req_get_config_desc_full(udev,
698e2805033SAndrew Thompson 		    NULL, &cdesc, M_USBDEV,
69902ac6454SAndrew Thompson 		    ugd->ugd_config_index)) {
70002ac6454SAndrew Thompson 			return (ENXIO);
70102ac6454SAndrew Thompson 		}
70202ac6454SAndrew Thompson 		free_data = 1;
70302ac6454SAndrew Thompson 	}
70402ac6454SAndrew Thompson 
70502ac6454SAndrew Thompson 	len = UGETW(cdesc->wTotalLength);
70602ac6454SAndrew Thompson 	if (len > ugd->ugd_maxlen) {
70702ac6454SAndrew Thompson 		len = ugd->ugd_maxlen;
70802ac6454SAndrew Thompson 	}
70902ac6454SAndrew Thompson 	DPRINTFN(6, "len=%u\n", len);
71002ac6454SAndrew Thompson 
71102ac6454SAndrew Thompson 	ugd->ugd_actlen = len;
71202ac6454SAndrew Thompson 	ugd->ugd_offset = 0;
71302ac6454SAndrew Thompson 
71402ac6454SAndrew Thompson 	error = copyout(cdesc, ugd->ugd_data, len);
71502ac6454SAndrew Thompson 
71602ac6454SAndrew Thompson 	if (free_data) {
71702ac6454SAndrew Thompson 		free(cdesc, M_USBDEV);
71802ac6454SAndrew Thompson 	}
71902ac6454SAndrew Thompson 	return (error);
72002ac6454SAndrew Thompson }
72102ac6454SAndrew Thompson 
7226950c75fSHans Petter Selasky /*
7236950c75fSHans Petter Selasky  * This function is called having the enumeration SX locked which
7246950c75fSHans Petter Selasky  * protects the scratch area used.
7256950c75fSHans Petter Selasky  */
72602ac6454SAndrew Thompson static int
727760bc48eSAndrew Thompson ugen_get_sdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd)
72802ac6454SAndrew Thompson {
7296950c75fSHans Petter Selasky 	void *ptr;
7306950c75fSHans Petter Selasky 	uint16_t size;
73102ac6454SAndrew Thompson 	int error;
73202ac6454SAndrew Thompson 
7336950c75fSHans Petter Selasky 	ptr = f->udev->scratch.data;
7346950c75fSHans Petter Selasky 	size = sizeof(f->udev->scratch.data);
7356950c75fSHans Petter Selasky 
736a593f6b8SAndrew Thompson 	if (usbd_req_get_string_desc(f->udev, NULL, ptr,
73702ac6454SAndrew Thompson 	    size, ugd->ugd_lang_id, ugd->ugd_string_index)) {
73802ac6454SAndrew Thompson 		error = EINVAL;
73902ac6454SAndrew Thompson 	} else {
74002ac6454SAndrew Thompson 
74102ac6454SAndrew Thompson 		if (size > ((uint8_t *)ptr)[0]) {
74202ac6454SAndrew Thompson 			size = ((uint8_t *)ptr)[0];
74302ac6454SAndrew Thompson 		}
74402ac6454SAndrew Thompson 		if (size > ugd->ugd_maxlen) {
74502ac6454SAndrew Thompson 			size = ugd->ugd_maxlen;
74602ac6454SAndrew Thompson 		}
74702ac6454SAndrew Thompson 		ugd->ugd_actlen = size;
74802ac6454SAndrew Thompson 		ugd->ugd_offset = 0;
74902ac6454SAndrew Thompson 
75002ac6454SAndrew Thompson 		error = copyout(ptr, ugd->ugd_data, size);
75102ac6454SAndrew Thompson 	}
75202ac6454SAndrew Thompson 	return (error);
75302ac6454SAndrew Thompson }
75402ac6454SAndrew Thompson 
75502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
75602ac6454SAndrew Thompson  *	ugen_get_iface_driver
75702ac6454SAndrew Thompson  *
75802ac6454SAndrew Thompson  * This function generates an USB interface description for userland.
75902ac6454SAndrew Thompson  *
76002ac6454SAndrew Thompson  * Returns:
76102ac6454SAndrew Thompson  *    0: Success
76202ac6454SAndrew Thompson  * Else: Failure
76302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
76402ac6454SAndrew Thompson static int
765760bc48eSAndrew Thompson ugen_get_iface_driver(struct usb_fifo *f, struct usb_gen_descriptor *ugd)
76602ac6454SAndrew Thompson {
767760bc48eSAndrew Thompson 	struct usb_device *udev = f->udev;
768760bc48eSAndrew Thompson 	struct usb_interface *iface;
76902ac6454SAndrew Thompson 	const char *ptr;
77002ac6454SAndrew Thompson 	const char *desc;
77102ac6454SAndrew Thompson 	unsigned int len;
77202ac6454SAndrew Thompson 	unsigned int maxlen;
77302ac6454SAndrew Thompson 	char buf[128];
77402ac6454SAndrew Thompson 	int error;
77502ac6454SAndrew Thompson 
77602ac6454SAndrew Thompson 	DPRINTFN(6, "\n");
77702ac6454SAndrew Thompson 
77802ac6454SAndrew Thompson 	if ((ugd->ugd_data == NULL) || (ugd->ugd_maxlen == 0)) {
77902ac6454SAndrew Thompson 		/* userland pointer should not be zero */
78002ac6454SAndrew Thompson 		return (EINVAL);
78102ac6454SAndrew Thompson 	}
78202ac6454SAndrew Thompson 
783a593f6b8SAndrew Thompson 	iface = usbd_get_iface(udev, ugd->ugd_iface_index);
78402ac6454SAndrew Thompson 	if ((iface == NULL) || (iface->idesc == NULL)) {
78502ac6454SAndrew Thompson 		/* invalid interface index */
78602ac6454SAndrew Thompson 		return (EINVAL);
78702ac6454SAndrew Thompson 	}
78802ac6454SAndrew Thompson 
78902ac6454SAndrew Thompson 	/* read out device nameunit string, if any */
79002ac6454SAndrew Thompson 	if ((iface->subdev != NULL) &&
79102ac6454SAndrew Thompson 	    device_is_attached(iface->subdev) &&
79202ac6454SAndrew Thompson 	    (ptr = device_get_nameunit(iface->subdev)) &&
79302ac6454SAndrew Thompson 	    (desc = device_get_desc(iface->subdev))) {
79402ac6454SAndrew Thompson 
79502ac6454SAndrew Thompson 		/* print description */
79602ac6454SAndrew Thompson 		snprintf(buf, sizeof(buf), "%s: <%s>", ptr, desc);
79702ac6454SAndrew Thompson 
79802ac6454SAndrew Thompson 		/* range checks */
79902ac6454SAndrew Thompson 		maxlen = ugd->ugd_maxlen - 1;
80002ac6454SAndrew Thompson 		len = strlen(buf);
80102ac6454SAndrew Thompson 		if (len > maxlen)
80202ac6454SAndrew Thompson 			len = maxlen;
80302ac6454SAndrew Thompson 
80402ac6454SAndrew Thompson 		/* update actual length, including terminating zero */
80502ac6454SAndrew Thompson 		ugd->ugd_actlen = len + 1;
80602ac6454SAndrew Thompson 
80702ac6454SAndrew Thompson 		/* copy out interface description */
80802ac6454SAndrew Thompson 		error = copyout(buf, ugd->ugd_data, ugd->ugd_actlen);
80902ac6454SAndrew Thompson 	} else {
81002ac6454SAndrew Thompson 		/* zero length string is default */
81102ac6454SAndrew Thompson 		error = copyout("", ugd->ugd_data, 1);
81202ac6454SAndrew Thompson 	}
81302ac6454SAndrew Thompson 	return (error);
81402ac6454SAndrew Thompson }
81502ac6454SAndrew Thompson 
81602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
817a593f6b8SAndrew Thompson  *	usb_gen_fill_deviceinfo
81802ac6454SAndrew Thompson  *
81902ac6454SAndrew Thompson  * This function dumps information about an USB device to the
82002ac6454SAndrew Thompson  * structure pointed to by the "di" argument.
82102ac6454SAndrew Thompson  *
82202ac6454SAndrew Thompson  * Returns:
82302ac6454SAndrew Thompson  *    0: Success
82402ac6454SAndrew Thompson  * Else: Failure
82502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
82602ac6454SAndrew Thompson static int
827a593f6b8SAndrew Thompson usb_gen_fill_deviceinfo(struct usb_fifo *f, struct usb_device_info *di)
82802ac6454SAndrew Thompson {
829760bc48eSAndrew Thompson 	struct usb_device *udev;
830760bc48eSAndrew Thompson 	struct usb_device *hub;
83102ac6454SAndrew Thompson 
83202ac6454SAndrew Thompson 	udev = f->udev;
83302ac6454SAndrew Thompson 
83402ac6454SAndrew Thompson 	bzero(di, sizeof(di[0]));
83502ac6454SAndrew Thompson 
83602ac6454SAndrew Thompson 	di->udi_bus = device_get_unit(udev->bus->bdev);
83702ac6454SAndrew Thompson 	di->udi_addr = udev->address;
83802ac6454SAndrew Thompson 	di->udi_index = udev->device_index;
839ae538d85SAndrew Thompson 	strlcpy(di->udi_serial, usb_get_serial(udev), sizeof(di->udi_serial));
840ae538d85SAndrew Thompson 	strlcpy(di->udi_vendor, usb_get_manufacturer(udev), sizeof(di->udi_vendor));
841ae538d85SAndrew Thompson 	strlcpy(di->udi_product, usb_get_product(udev), sizeof(di->udi_product));
842a593f6b8SAndrew Thompson 	usb_printbcd(di->udi_release, sizeof(di->udi_release),
84302ac6454SAndrew Thompson 	    UGETW(udev->ddesc.bcdDevice));
84402ac6454SAndrew Thompson 	di->udi_vendorNo = UGETW(udev->ddesc.idVendor);
84502ac6454SAndrew Thompson 	di->udi_productNo = UGETW(udev->ddesc.idProduct);
84602ac6454SAndrew Thompson 	di->udi_releaseNo = UGETW(udev->ddesc.bcdDevice);
84702ac6454SAndrew Thompson 	di->udi_class = udev->ddesc.bDeviceClass;
84802ac6454SAndrew Thompson 	di->udi_subclass = udev->ddesc.bDeviceSubClass;
84902ac6454SAndrew Thompson 	di->udi_protocol = udev->ddesc.bDeviceProtocol;
85002ac6454SAndrew Thompson 	di->udi_config_no = udev->curr_config_no;
85102ac6454SAndrew Thompson 	di->udi_config_index = udev->curr_config_index;
85202ac6454SAndrew Thompson 	di->udi_power = udev->flags.self_powered ? 0 : udev->power;
85302ac6454SAndrew Thompson 	di->udi_speed = udev->speed;
854f29a0724SAndrew Thompson 	di->udi_mode = udev->flags.usb_mode;
85502ac6454SAndrew Thompson 	di->udi_power_mode = udev->power_mode;
856ec8f3127SAndrew Thompson 	di->udi_suspended = udev->flags.peer_suspended;
85702ac6454SAndrew Thompson 
85802ac6454SAndrew Thompson 	hub = udev->parent_hub;
85902ac6454SAndrew Thompson 	if (hub) {
86002ac6454SAndrew Thompson 		di->udi_hubaddr = hub->address;
86102ac6454SAndrew Thompson 		di->udi_hubindex = hub->device_index;
86202ac6454SAndrew Thompson 		di->udi_hubport = udev->port_no;
86302ac6454SAndrew Thompson 	}
86402ac6454SAndrew Thompson 	return (0);
86502ac6454SAndrew Thompson }
86602ac6454SAndrew Thompson 
86702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
86802ac6454SAndrew Thompson  *	ugen_check_request
86902ac6454SAndrew Thompson  *
87002ac6454SAndrew Thompson  * Return values:
87102ac6454SAndrew Thompson  * 0: Access allowed
87202ac6454SAndrew Thompson  * Else: No access
87302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
87402ac6454SAndrew Thompson static int
875760bc48eSAndrew Thompson ugen_check_request(struct usb_device *udev, struct usb_device_request *req)
87602ac6454SAndrew Thompson {
877ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep;
87802ac6454SAndrew Thompson 	int error;
87902ac6454SAndrew Thompson 
88002ac6454SAndrew Thompson 	/*
88102ac6454SAndrew Thompson 	 * Avoid requests that would damage the bus integrity:
88202ac6454SAndrew Thompson 	 */
88302ac6454SAndrew Thompson 	if (((req->bmRequestType == UT_WRITE_DEVICE) &&
88402ac6454SAndrew Thompson 	    (req->bRequest == UR_SET_ADDRESS)) ||
88502ac6454SAndrew Thompson 	    ((req->bmRequestType == UT_WRITE_DEVICE) &&
88602ac6454SAndrew Thompson 	    (req->bRequest == UR_SET_CONFIG)) ||
88702ac6454SAndrew Thompson 	    ((req->bmRequestType == UT_WRITE_INTERFACE) &&
88802ac6454SAndrew Thompson 	    (req->bRequest == UR_SET_INTERFACE))) {
88902ac6454SAndrew Thompson 		/*
89002ac6454SAndrew Thompson 		 * These requests can be useful for testing USB drivers.
89102ac6454SAndrew Thompson 		 */
89202ac6454SAndrew Thompson 		error = priv_check(curthread, PRIV_DRIVER);
89302ac6454SAndrew Thompson 		if (error) {
89402ac6454SAndrew Thompson 			return (error);
89502ac6454SAndrew Thompson 		}
89602ac6454SAndrew Thompson 	}
89702ac6454SAndrew Thompson 	/*
89802ac6454SAndrew Thompson 	 * Special case - handle clearing of stall
89902ac6454SAndrew Thompson 	 */
90002ac6454SAndrew Thompson 	if (req->bmRequestType == UT_WRITE_ENDPOINT) {
90102ac6454SAndrew Thompson 
902a593f6b8SAndrew Thompson 		ep = usbd_get_ep_by_addr(udev, req->wIndex[0]);
903ae60fdfbSAndrew Thompson 		if (ep == NULL) {
90402ac6454SAndrew Thompson 			return (EINVAL);
90502ac6454SAndrew Thompson 		}
90602ac6454SAndrew Thompson 		if ((req->bRequest == UR_CLEAR_FEATURE) &&
90702ac6454SAndrew Thompson 		    (UGETW(req->wValue) == UF_ENDPOINT_HALT)) {
908a593f6b8SAndrew Thompson 			usbd_clear_data_toggle(udev, ep);
90902ac6454SAndrew Thompson 		}
91002ac6454SAndrew Thompson 	}
91102ac6454SAndrew Thompson 	/* TODO: add more checks to verify the interface index */
91202ac6454SAndrew Thompson 
91302ac6454SAndrew Thompson 	return (0);
91402ac6454SAndrew Thompson }
91502ac6454SAndrew Thompson 
91602ac6454SAndrew Thompson int
917760bc48eSAndrew Thompson ugen_do_request(struct usb_fifo *f, struct usb_ctl_request *ur)
91802ac6454SAndrew Thompson {
91902ac6454SAndrew Thompson 	int error;
92002ac6454SAndrew Thompson 	uint16_t len;
92102ac6454SAndrew Thompson 	uint16_t actlen;
92202ac6454SAndrew Thompson 
92302ac6454SAndrew Thompson 	if (ugen_check_request(f->udev, &ur->ucr_request)) {
92402ac6454SAndrew Thompson 		return (EPERM);
92502ac6454SAndrew Thompson 	}
92602ac6454SAndrew Thompson 	len = UGETW(ur->ucr_request.wLength);
92702ac6454SAndrew Thompson 
92802ac6454SAndrew Thompson 	/* check if "ucr_data" is valid */
92902ac6454SAndrew Thompson 	if (len != 0) {
93002ac6454SAndrew Thompson 		if (ur->ucr_data == NULL) {
93102ac6454SAndrew Thompson 			return (EFAULT);
93202ac6454SAndrew Thompson 		}
93302ac6454SAndrew Thompson 	}
93402ac6454SAndrew Thompson 	/* do the USB request */
935a593f6b8SAndrew Thompson 	error = usbd_do_request_flags
93602ac6454SAndrew Thompson 	    (f->udev, NULL, &ur->ucr_request, ur->ucr_data,
93702ac6454SAndrew Thompson 	    (ur->ucr_flags & USB_SHORT_XFER_OK) |
93802ac6454SAndrew Thompson 	    USB_USER_DATA_PTR, &actlen,
93902ac6454SAndrew Thompson 	    USB_DEFAULT_TIMEOUT);
94002ac6454SAndrew Thompson 
94102ac6454SAndrew Thompson 	ur->ucr_actlen = actlen;
94202ac6454SAndrew Thompson 
94302ac6454SAndrew Thompson 	if (error) {
94402ac6454SAndrew Thompson 		error = EIO;
94502ac6454SAndrew Thompson 	}
94602ac6454SAndrew Thompson 	return (error);
94702ac6454SAndrew Thompson }
94802ac6454SAndrew Thompson 
94902ac6454SAndrew Thompson /*------------------------------------------------------------------------
95002ac6454SAndrew Thompson  *	ugen_re_enumerate
95102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
95202ac6454SAndrew Thompson static int
953760bc48eSAndrew Thompson ugen_re_enumerate(struct usb_fifo *f)
95402ac6454SAndrew Thompson {
955760bc48eSAndrew Thompson 	struct usb_device *udev = f->udev;
95602ac6454SAndrew Thompson 	int error;
95702ac6454SAndrew Thompson 
95802ac6454SAndrew Thompson 	/*
95902ac6454SAndrew Thompson 	 * This request can be useful for testing USB drivers:
96002ac6454SAndrew Thompson 	 */
96102ac6454SAndrew Thompson 	error = priv_check(curthread, PRIV_DRIVER);
96202ac6454SAndrew Thompson 	if (error) {
96302ac6454SAndrew Thompson 		return (error);
96402ac6454SAndrew Thompson 	}
9658f9750b7SHans Petter Selasky 	if (udev->flags.usb_mode != USB_MODE_HOST) {
9668f9750b7SHans Petter Selasky 		/* not possible in device side mode */
96756b57046SHans Petter Selasky 		DPRINTFN(6, "device mode\n");
9688f9750b7SHans Petter Selasky 		return (ENOTTY);
96902ac6454SAndrew Thompson 	}
97056b57046SHans Petter Selasky 	if (udev->parent_hub == NULL) {
97156b57046SHans Petter Selasky 		/* the root HUB cannot be re-enumerated */
97256b57046SHans Petter Selasky 		DPRINTFN(6, "cannot reset root HUB\n");
97356b57046SHans Petter Selasky 		return (EINVAL);
97456b57046SHans Petter Selasky 	}
9758f9750b7SHans Petter Selasky 	/* make sure all FIFO's are gone */
9768f9750b7SHans Petter Selasky 	/* else there can be a deadlock */
9778f9750b7SHans Petter Selasky 	if (ugen_fs_uninit(f)) {
9788f9750b7SHans Petter Selasky 		/* ignore any errors */
9798f9750b7SHans Petter Selasky 		DPRINTFN(6, "no FIFOs\n");
98002ac6454SAndrew Thompson 	}
9819eb0d702SHans Petter Selasky 	/* start re-enumeration of device */
9829eb0d702SHans Petter Selasky 	usbd_start_re_enumerate(udev);
98302ac6454SAndrew Thompson 	return (0);
98402ac6454SAndrew Thompson }
98502ac6454SAndrew Thompson 
98602ac6454SAndrew Thompson int
987760bc48eSAndrew Thompson ugen_fs_uninit(struct usb_fifo *f)
98802ac6454SAndrew Thompson {
98902ac6454SAndrew Thompson 	if (f->fs_xfer == NULL) {
99002ac6454SAndrew Thompson 		return (EINVAL);
99102ac6454SAndrew Thompson 	}
992a593f6b8SAndrew Thompson 	usbd_transfer_unsetup(f->fs_xfer, f->fs_ep_max);
99302ac6454SAndrew Thompson 	free(f->fs_xfer, M_USB);
99402ac6454SAndrew Thompson 	f->fs_xfer = NULL;
99502ac6454SAndrew Thompson 	f->fs_ep_max = 0;
99602ac6454SAndrew Thompson 	f->fs_ep_ptr = NULL;
99702ac6454SAndrew Thompson 	f->flag_iscomplete = 0;
998a593f6b8SAndrew Thompson 	usb_fifo_free_buffer(f);
99902ac6454SAndrew Thompson 	return (0);
100002ac6454SAndrew Thompson }
100102ac6454SAndrew Thompson 
100202ac6454SAndrew Thompson static uint8_t
1003760bc48eSAndrew Thompson ugen_fs_get_complete(struct usb_fifo *f, uint8_t *pindex)
100402ac6454SAndrew Thompson {
1005760bc48eSAndrew Thompson 	struct usb_mbuf *m;
100602ac6454SAndrew Thompson 
100702ac6454SAndrew Thompson 	USB_IF_DEQUEUE(&f->used_q, m);
100802ac6454SAndrew Thompson 
100902ac6454SAndrew Thompson 	if (m) {
101002ac6454SAndrew Thompson 		*pindex = *((uint8_t *)(m->cur_data_ptr));
101102ac6454SAndrew Thompson 
101202ac6454SAndrew Thompson 		USB_IF_ENQUEUE(&f->free_q, m);
101302ac6454SAndrew Thompson 
101402ac6454SAndrew Thompson 		return (0);		/* success */
101502ac6454SAndrew Thompson 	} else {
101602ac6454SAndrew Thompson 
101702ac6454SAndrew Thompson 		*pindex = 0;		/* fix compiler warning */
101802ac6454SAndrew Thompson 
101902ac6454SAndrew Thompson 		f->flag_iscomplete = 0;
102002ac6454SAndrew Thompson 	}
102102ac6454SAndrew Thompson 	return (1);			/* failure */
102202ac6454SAndrew Thompson }
102302ac6454SAndrew Thompson 
102402ac6454SAndrew Thompson static void
1025760bc48eSAndrew Thompson ugen_fs_set_complete(struct usb_fifo *f, uint8_t index)
102602ac6454SAndrew Thompson {
1027760bc48eSAndrew Thompson 	struct usb_mbuf *m;
102802ac6454SAndrew Thompson 
102902ac6454SAndrew Thompson 	USB_IF_DEQUEUE(&f->free_q, m);
103002ac6454SAndrew Thompson 
103102ac6454SAndrew Thompson 	if (m == NULL) {
103202ac6454SAndrew Thompson 		/* can happen during close */
103302ac6454SAndrew Thompson 		DPRINTF("out of buffers\n");
103402ac6454SAndrew Thompson 		return;
103502ac6454SAndrew Thompson 	}
103602ac6454SAndrew Thompson 	USB_MBUF_RESET(m);
103702ac6454SAndrew Thompson 
103802ac6454SAndrew Thompson 	*((uint8_t *)(m->cur_data_ptr)) = index;
103902ac6454SAndrew Thompson 
104002ac6454SAndrew Thompson 	USB_IF_ENQUEUE(&f->used_q, m);
104102ac6454SAndrew Thompson 
104202ac6454SAndrew Thompson 	f->flag_iscomplete = 1;
104302ac6454SAndrew Thompson 
1044a593f6b8SAndrew Thompson 	usb_fifo_wakeup(f);
104502ac6454SAndrew Thompson }
104602ac6454SAndrew Thompson 
104702ac6454SAndrew Thompson static int
1048760bc48eSAndrew Thompson ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index)
104902ac6454SAndrew Thompson {
1050760bc48eSAndrew Thompson 	struct usb_device_request *req;
1051760bc48eSAndrew Thompson 	struct usb_xfer *xfer;
1052760bc48eSAndrew Thompson 	struct usb_fs_endpoint fs_ep;
105302ac6454SAndrew Thompson 	void *uaddr;			/* userland pointer */
105402ac6454SAndrew Thompson 	void *kaddr;
1055e0a69b51SAndrew Thompson 	usb_frlength_t offset;
1056e0a69b51SAndrew Thompson 	usb_frlength_t rem;
1057e0a69b51SAndrew Thompson 	usb_frcount_t n;
105802ac6454SAndrew Thompson 	uint32_t length;
105902ac6454SAndrew Thompson 	int error;
106002ac6454SAndrew Thompson 	uint8_t isread;
106102ac6454SAndrew Thompson 
106202ac6454SAndrew Thompson 	if (ep_index >= f->fs_ep_max) {
106302ac6454SAndrew Thompson 		return (EINVAL);
106402ac6454SAndrew Thompson 	}
106502ac6454SAndrew Thompson 	xfer = f->fs_xfer[ep_index];
106602ac6454SAndrew Thompson 	if (xfer == NULL) {
106702ac6454SAndrew Thompson 		return (EINVAL);
106802ac6454SAndrew Thompson 	}
106902ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
1070a593f6b8SAndrew Thompson 	if (usbd_transfer_pending(xfer)) {
107102ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
107202ac6454SAndrew Thompson 		return (EBUSY);		/* should not happen */
107302ac6454SAndrew Thompson 	}
107402ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
107502ac6454SAndrew Thompson 
107602ac6454SAndrew Thompson 	error = copyin(f->fs_ep_ptr +
107702ac6454SAndrew Thompson 	    ep_index, &fs_ep, sizeof(fs_ep));
107802ac6454SAndrew Thompson 	if (error) {
107902ac6454SAndrew Thompson 		return (error);
108002ac6454SAndrew Thompson 	}
108102ac6454SAndrew Thompson 	/* security checks */
108202ac6454SAndrew Thompson 
108302ac6454SAndrew Thompson 	if (fs_ep.nFrames > xfer->max_frame_count) {
108402ac6454SAndrew Thompson 		xfer->error = USB_ERR_INVAL;
108502ac6454SAndrew Thompson 		goto complete;
108602ac6454SAndrew Thompson 	}
108702ac6454SAndrew Thompson 	if (fs_ep.nFrames == 0) {
108802ac6454SAndrew Thompson 		xfer->error = USB_ERR_INVAL;
108902ac6454SAndrew Thompson 		goto complete;
109002ac6454SAndrew Thompson 	}
109102ac6454SAndrew Thompson 	error = copyin(fs_ep.ppBuffer,
109202ac6454SAndrew Thompson 	    &uaddr, sizeof(uaddr));
109302ac6454SAndrew Thompson 	if (error) {
109402ac6454SAndrew Thompson 		return (error);
109502ac6454SAndrew Thompson 	}
109602ac6454SAndrew Thompson 	/* reset first frame */
1097ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_offset(xfer, 0, 0);
109802ac6454SAndrew Thompson 
109902ac6454SAndrew Thompson 	if (xfer->flags_int.control_xfr) {
110002ac6454SAndrew Thompson 
110102ac6454SAndrew Thompson 		req = xfer->frbuffers[0].buffer;
110202ac6454SAndrew Thompson 
110302ac6454SAndrew Thompson 		error = copyin(fs_ep.pLength,
110402ac6454SAndrew Thompson 		    &length, sizeof(length));
110502ac6454SAndrew Thompson 		if (error) {
110602ac6454SAndrew Thompson 			return (error);
110702ac6454SAndrew Thompson 		}
1108599ce1c3SAndrew Thompson 		if (length != sizeof(*req)) {
110902ac6454SAndrew Thompson 			xfer->error = USB_ERR_INVAL;
111002ac6454SAndrew Thompson 			goto complete;
111102ac6454SAndrew Thompson 		}
111202ac6454SAndrew Thompson 		if (length != 0) {
111302ac6454SAndrew Thompson 			error = copyin(uaddr, req, length);
111402ac6454SAndrew Thompson 			if (error) {
111502ac6454SAndrew Thompson 				return (error);
111602ac6454SAndrew Thompson 			}
111702ac6454SAndrew Thompson 		}
111802ac6454SAndrew Thompson 		if (ugen_check_request(f->udev, req)) {
111902ac6454SAndrew Thompson 			xfer->error = USB_ERR_INVAL;
112002ac6454SAndrew Thompson 			goto complete;
112102ac6454SAndrew Thompson 		}
1122ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, length);
112302ac6454SAndrew Thompson 
112402ac6454SAndrew Thompson 		/* Host mode only ! */
112502ac6454SAndrew Thompson 		if ((req->bmRequestType &
112602ac6454SAndrew Thompson 		    (UT_READ | UT_WRITE)) == UT_READ) {
112702ac6454SAndrew Thompson 			isread = 1;
112802ac6454SAndrew Thompson 		} else {
112902ac6454SAndrew Thompson 			isread = 0;
113002ac6454SAndrew Thompson 		}
113102ac6454SAndrew Thompson 		n = 1;
113202ac6454SAndrew Thompson 		offset = sizeof(*req);
113302ac6454SAndrew Thompson 
113402ac6454SAndrew Thompson 	} else {
113502ac6454SAndrew Thompson 		/* Device and Host mode */
113602ac6454SAndrew Thompson 		if (USB_GET_DATA_ISREAD(xfer)) {
113702ac6454SAndrew Thompson 			isread = 1;
113802ac6454SAndrew Thompson 		} else {
113902ac6454SAndrew Thompson 			isread = 0;
114002ac6454SAndrew Thompson 		}
114102ac6454SAndrew Thompson 		n = 0;
114202ac6454SAndrew Thompson 		offset = 0;
114302ac6454SAndrew Thompson 	}
114402ac6454SAndrew Thompson 
1145ed6d949aSAndrew Thompson 	rem = usbd_xfer_max_len(xfer);
114602ac6454SAndrew Thompson 	xfer->nframes = fs_ep.nFrames;
114702ac6454SAndrew Thompson 	xfer->timeout = fs_ep.timeout;
114802ac6454SAndrew Thompson 	if (xfer->timeout > 65535) {
114902ac6454SAndrew Thompson 		xfer->timeout = 65535;
115002ac6454SAndrew Thompson 	}
115102ac6454SAndrew Thompson 	if (fs_ep.flags & USB_FS_FLAG_SINGLE_SHORT_OK)
115202ac6454SAndrew Thompson 		xfer->flags.short_xfer_ok = 1;
115302ac6454SAndrew Thompson 	else
115402ac6454SAndrew Thompson 		xfer->flags.short_xfer_ok = 0;
115502ac6454SAndrew Thompson 
115602ac6454SAndrew Thompson 	if (fs_ep.flags & USB_FS_FLAG_MULTI_SHORT_OK)
115702ac6454SAndrew Thompson 		xfer->flags.short_frames_ok = 1;
115802ac6454SAndrew Thompson 	else
115902ac6454SAndrew Thompson 		xfer->flags.short_frames_ok = 0;
116002ac6454SAndrew Thompson 
116102ac6454SAndrew Thompson 	if (fs_ep.flags & USB_FS_FLAG_FORCE_SHORT)
116202ac6454SAndrew Thompson 		xfer->flags.force_short_xfer = 1;
116302ac6454SAndrew Thompson 	else
116402ac6454SAndrew Thompson 		xfer->flags.force_short_xfer = 0;
116502ac6454SAndrew Thompson 
116602ac6454SAndrew Thompson 	if (fs_ep.flags & USB_FS_FLAG_CLEAR_STALL)
1167ed6d949aSAndrew Thompson 		usbd_xfer_set_stall(xfer);
116802ac6454SAndrew Thompson 	else
116902ac6454SAndrew Thompson 		xfer->flags.stall_pipe = 0;
117002ac6454SAndrew Thompson 
117102ac6454SAndrew Thompson 	for (; n != xfer->nframes; n++) {
117202ac6454SAndrew Thompson 
117302ac6454SAndrew Thompson 		error = copyin(fs_ep.pLength + n,
117402ac6454SAndrew Thompson 		    &length, sizeof(length));
117502ac6454SAndrew Thompson 		if (error) {
117602ac6454SAndrew Thompson 			break;
117702ac6454SAndrew Thompson 		}
1178ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, n, length);
117902ac6454SAndrew Thompson 
118002ac6454SAndrew Thompson 		if (length > rem) {
118102ac6454SAndrew Thompson 			xfer->error = USB_ERR_INVAL;
118202ac6454SAndrew Thompson 			goto complete;
118302ac6454SAndrew Thompson 		}
118402ac6454SAndrew Thompson 		rem -= length;
118502ac6454SAndrew Thompson 
118602ac6454SAndrew Thompson 		if (!isread) {
118702ac6454SAndrew Thompson 
118802ac6454SAndrew Thompson 			/* we need to know the source buffer */
118902ac6454SAndrew Thompson 			error = copyin(fs_ep.ppBuffer + n,
119002ac6454SAndrew Thompson 			    &uaddr, sizeof(uaddr));
119102ac6454SAndrew Thompson 			if (error) {
119202ac6454SAndrew Thompson 				break;
119302ac6454SAndrew Thompson 			}
119402ac6454SAndrew Thompson 			if (xfer->flags_int.isochronous_xfr) {
119502ac6454SAndrew Thompson 				/* get kernel buffer address */
119602ac6454SAndrew Thompson 				kaddr = xfer->frbuffers[0].buffer;
119702ac6454SAndrew Thompson 				kaddr = USB_ADD_BYTES(kaddr, offset);
119802ac6454SAndrew Thompson 			} else {
119902ac6454SAndrew Thompson 				/* set current frame offset */
1200ed6d949aSAndrew Thompson 				usbd_xfer_set_frame_offset(xfer, offset, n);
120102ac6454SAndrew Thompson 
120202ac6454SAndrew Thompson 				/* get kernel buffer address */
120302ac6454SAndrew Thompson 				kaddr = xfer->frbuffers[n].buffer;
120402ac6454SAndrew Thompson 			}
120502ac6454SAndrew Thompson 
120602ac6454SAndrew Thompson 			/* move data */
120702ac6454SAndrew Thompson 			error = copyin(uaddr, kaddr, length);
120802ac6454SAndrew Thompson 			if (error) {
120902ac6454SAndrew Thompson 				break;
121002ac6454SAndrew Thompson 			}
121102ac6454SAndrew Thompson 		}
121202ac6454SAndrew Thompson 		offset += length;
121302ac6454SAndrew Thompson 	}
121402ac6454SAndrew Thompson 	return (error);
121502ac6454SAndrew Thompson 
121602ac6454SAndrew Thompson complete:
121702ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
121802ac6454SAndrew Thompson 	ugen_fs_set_complete(f, ep_index);
121902ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
122002ac6454SAndrew Thompson 	return (0);
122102ac6454SAndrew Thompson }
122202ac6454SAndrew Thompson 
122302ac6454SAndrew Thompson static int
1224760bc48eSAndrew Thompson ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index)
122502ac6454SAndrew Thompson {
1226760bc48eSAndrew Thompson 	struct usb_device_request *req;
1227760bc48eSAndrew Thompson 	struct usb_xfer *xfer;
1228760bc48eSAndrew Thompson 	struct usb_fs_endpoint fs_ep;
1229760bc48eSAndrew Thompson 	struct usb_fs_endpoint *fs_ep_uptr;	/* userland ptr */
123002ac6454SAndrew Thompson 	void *uaddr;			/* userland ptr */
123102ac6454SAndrew Thompson 	void *kaddr;
1232e0a69b51SAndrew Thompson 	usb_frlength_t offset;
1233e0a69b51SAndrew Thompson 	usb_frlength_t rem;
1234e0a69b51SAndrew Thompson 	usb_frcount_t n;
123502ac6454SAndrew Thompson 	uint32_t length;
123602ac6454SAndrew Thompson 	uint32_t temp;
123702ac6454SAndrew Thompson 	int error;
123802ac6454SAndrew Thompson 	uint8_t isread;
123902ac6454SAndrew Thompson 
1240578d0effSAndrew Thompson 	if (ep_index >= f->fs_ep_max)
124102ac6454SAndrew Thompson 		return (EINVAL);
1242578d0effSAndrew Thompson 
124302ac6454SAndrew Thompson 	xfer = f->fs_xfer[ep_index];
1244578d0effSAndrew Thompson 	if (xfer == NULL)
124502ac6454SAndrew Thompson 		return (EINVAL);
1246578d0effSAndrew Thompson 
124702ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
1248a593f6b8SAndrew Thompson 	if (usbd_transfer_pending(xfer)) {
124902ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
125002ac6454SAndrew Thompson 		return (EBUSY);		/* should not happen */
125102ac6454SAndrew Thompson 	}
125202ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
125302ac6454SAndrew Thompson 
125402ac6454SAndrew Thompson 	fs_ep_uptr = f->fs_ep_ptr + ep_index;
125502ac6454SAndrew Thompson 	error = copyin(fs_ep_uptr, &fs_ep, sizeof(fs_ep));
125602ac6454SAndrew Thompson 	if (error) {
125702ac6454SAndrew Thompson 		return (error);
125802ac6454SAndrew Thompson 	}
125902ac6454SAndrew Thompson 	fs_ep.status = xfer->error;
126002ac6454SAndrew Thompson 	fs_ep.aFrames = xfer->aframes;
126102ac6454SAndrew Thompson 	fs_ep.isoc_time_complete = xfer->isoc_time_complete;
126202ac6454SAndrew Thompson 	if (xfer->error) {
126302ac6454SAndrew Thompson 		goto complete;
126402ac6454SAndrew Thompson 	}
126502ac6454SAndrew Thompson 	if (xfer->flags_int.control_xfr) {
126602ac6454SAndrew Thompson 		req = xfer->frbuffers[0].buffer;
126702ac6454SAndrew Thompson 
126802ac6454SAndrew Thompson 		/* Host mode only ! */
126902ac6454SAndrew Thompson 		if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_READ) {
127002ac6454SAndrew Thompson 			isread = 1;
127102ac6454SAndrew Thompson 		} else {
127202ac6454SAndrew Thompson 			isread = 0;
127302ac6454SAndrew Thompson 		}
127402ac6454SAndrew Thompson 		if (xfer->nframes == 0)
127502ac6454SAndrew Thompson 			n = 0;		/* should never happen */
127602ac6454SAndrew Thompson 		else
127702ac6454SAndrew Thompson 			n = 1;
127802ac6454SAndrew Thompson 	} else {
127902ac6454SAndrew Thompson 		/* Device and Host mode */
128002ac6454SAndrew Thompson 		if (USB_GET_DATA_ISREAD(xfer)) {
128102ac6454SAndrew Thompson 			isread = 1;
128202ac6454SAndrew Thompson 		} else {
128302ac6454SAndrew Thompson 			isread = 0;
128402ac6454SAndrew Thompson 		}
128502ac6454SAndrew Thompson 		n = 0;
128602ac6454SAndrew Thompson 	}
128702ac6454SAndrew Thompson 
128802ac6454SAndrew Thompson 	/* Update lengths and copy out data */
128902ac6454SAndrew Thompson 
1290ed6d949aSAndrew Thompson 	rem = usbd_xfer_max_len(xfer);
129102ac6454SAndrew Thompson 	offset = 0;
129202ac6454SAndrew Thompson 
129302ac6454SAndrew Thompson 	for (; n != xfer->nframes; n++) {
129402ac6454SAndrew Thompson 
129502ac6454SAndrew Thompson 		/* get initial length into "temp" */
129602ac6454SAndrew Thompson 		error = copyin(fs_ep.pLength + n,
129702ac6454SAndrew Thompson 		    &temp, sizeof(temp));
129802ac6454SAndrew Thompson 		if (error) {
129902ac6454SAndrew Thompson 			return (error);
130002ac6454SAndrew Thompson 		}
130102ac6454SAndrew Thompson 		if (temp > rem) {
130202ac6454SAndrew Thompson 			/* the userland length has been corrupted */
130302ac6454SAndrew Thompson 			DPRINTF("corrupt userland length "
130402ac6454SAndrew Thompson 			    "%u > %u\n", temp, rem);
130502ac6454SAndrew Thompson 			fs_ep.status = USB_ERR_INVAL;
130602ac6454SAndrew Thompson 			goto complete;
130702ac6454SAndrew Thompson 		}
130802ac6454SAndrew Thompson 		rem -= temp;
130902ac6454SAndrew Thompson 
131002ac6454SAndrew Thompson 		/* get actual transfer length */
131102ac6454SAndrew Thompson 		length = xfer->frlengths[n];
131202ac6454SAndrew Thompson 		if (length > temp) {
131302ac6454SAndrew Thompson 			/* data overflow */
131402ac6454SAndrew Thompson 			fs_ep.status = USB_ERR_INVAL;
131502ac6454SAndrew Thompson 			DPRINTF("data overflow %u > %u\n",
131602ac6454SAndrew Thompson 			    length, temp);
131702ac6454SAndrew Thompson 			goto complete;
131802ac6454SAndrew Thompson 		}
131902ac6454SAndrew Thompson 		if (isread) {
132002ac6454SAndrew Thompson 
132102ac6454SAndrew Thompson 			/* we need to know the destination buffer */
132202ac6454SAndrew Thompson 			error = copyin(fs_ep.ppBuffer + n,
132302ac6454SAndrew Thompson 			    &uaddr, sizeof(uaddr));
132402ac6454SAndrew Thompson 			if (error) {
132502ac6454SAndrew Thompson 				return (error);
132602ac6454SAndrew Thompson 			}
132702ac6454SAndrew Thompson 			if (xfer->flags_int.isochronous_xfr) {
132802ac6454SAndrew Thompson 				/* only one frame buffer */
132902ac6454SAndrew Thompson 				kaddr = USB_ADD_BYTES(
133002ac6454SAndrew Thompson 				    xfer->frbuffers[0].buffer, offset);
133102ac6454SAndrew Thompson 			} else {
133202ac6454SAndrew Thompson 				/* multiple frame buffers */
133302ac6454SAndrew Thompson 				kaddr = xfer->frbuffers[n].buffer;
133402ac6454SAndrew Thompson 			}
133502ac6454SAndrew Thompson 
133602ac6454SAndrew Thompson 			/* move data */
133702ac6454SAndrew Thompson 			error = copyout(kaddr, uaddr, length);
133802ac6454SAndrew Thompson 			if (error) {
133902ac6454SAndrew Thompson 				return (error);
134002ac6454SAndrew Thompson 			}
134102ac6454SAndrew Thompson 		}
134202ac6454SAndrew Thompson 		/*
134302ac6454SAndrew Thompson 		 * Update offset according to initial length, which is
134402ac6454SAndrew Thompson 		 * needed by isochronous transfers!
134502ac6454SAndrew Thompson 		 */
134602ac6454SAndrew Thompson 		offset += temp;
134702ac6454SAndrew Thompson 
134802ac6454SAndrew Thompson 		/* update length */
134902ac6454SAndrew Thompson 		error = copyout(&length,
135002ac6454SAndrew Thompson 		    fs_ep.pLength + n, sizeof(length));
135102ac6454SAndrew Thompson 		if (error) {
135202ac6454SAndrew Thompson 			return (error);
135302ac6454SAndrew Thompson 		}
135402ac6454SAndrew Thompson 	}
135502ac6454SAndrew Thompson 
135602ac6454SAndrew Thompson complete:
135702ac6454SAndrew Thompson 	/* update "aFrames" */
135802ac6454SAndrew Thompson 	error = copyout(&fs_ep.aFrames, &fs_ep_uptr->aFrames,
135902ac6454SAndrew Thompson 	    sizeof(fs_ep.aFrames));
136002ac6454SAndrew Thompson 	if (error)
136102ac6454SAndrew Thompson 		goto done;
136202ac6454SAndrew Thompson 
136302ac6454SAndrew Thompson 	/* update "isoc_time_complete" */
136402ac6454SAndrew Thompson 	error = copyout(&fs_ep.isoc_time_complete,
136502ac6454SAndrew Thompson 	    &fs_ep_uptr->isoc_time_complete,
136602ac6454SAndrew Thompson 	    sizeof(fs_ep.isoc_time_complete));
136702ac6454SAndrew Thompson 	if (error)
136802ac6454SAndrew Thompson 		goto done;
136902ac6454SAndrew Thompson 	/* update "status" */
137002ac6454SAndrew Thompson 	error = copyout(&fs_ep.status, &fs_ep_uptr->status,
137102ac6454SAndrew Thompson 	    sizeof(fs_ep.status));
137202ac6454SAndrew Thompson done:
137302ac6454SAndrew Thompson 	return (error);
137402ac6454SAndrew Thompson }
137502ac6454SAndrew Thompson 
137602ac6454SAndrew Thompson static uint8_t
1377760bc48eSAndrew Thompson ugen_fifo_in_use(struct usb_fifo *f, int fflags)
137802ac6454SAndrew Thompson {
1379760bc48eSAndrew Thompson 	struct usb_fifo *f_rx;
1380760bc48eSAndrew Thompson 	struct usb_fifo *f_tx;
138102ac6454SAndrew Thompson 
138202ac6454SAndrew Thompson 	f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX];
138302ac6454SAndrew Thompson 	f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX];
138402ac6454SAndrew Thompson 
138502ac6454SAndrew Thompson 	if ((fflags & FREAD) && f_rx &&
138602ac6454SAndrew Thompson 	    (f_rx->xfer[0] || f_rx->xfer[1])) {
138702ac6454SAndrew Thompson 		return (1);		/* RX FIFO in use */
138802ac6454SAndrew Thompson 	}
138902ac6454SAndrew Thompson 	if ((fflags & FWRITE) && f_tx &&
139002ac6454SAndrew Thompson 	    (f_tx->xfer[0] || f_tx->xfer[1])) {
139102ac6454SAndrew Thompson 		return (1);		/* TX FIFO in use */
139202ac6454SAndrew Thompson 	}
139302ac6454SAndrew Thompson 	return (0);			/* not in use */
139402ac6454SAndrew Thompson }
139502ac6454SAndrew Thompson 
139602ac6454SAndrew Thompson static int
1397760bc48eSAndrew Thompson ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
139802ac6454SAndrew Thompson {
1399760bc48eSAndrew Thompson 	struct usb_config usb_config[1];
1400760bc48eSAndrew Thompson 	struct usb_device_request req;
140102ac6454SAndrew Thompson 	union {
1402760bc48eSAndrew Thompson 		struct usb_fs_complete *pcomp;
1403760bc48eSAndrew Thompson 		struct usb_fs_start *pstart;
1404760bc48eSAndrew Thompson 		struct usb_fs_stop *pstop;
1405760bc48eSAndrew Thompson 		struct usb_fs_open *popen;
1406e9cec016SHans Petter Selasky 		struct usb_fs_open_stream *popen_stream;
1407760bc48eSAndrew Thompson 		struct usb_fs_close *pclose;
1408760bc48eSAndrew Thompson 		struct usb_fs_clear_stall_sync *pstall;
140902ac6454SAndrew Thompson 		void   *addr;
141002ac6454SAndrew Thompson 	}     u;
1411ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep;
1412760bc48eSAndrew Thompson 	struct usb_endpoint_descriptor *ed;
14136c352e99SHans Petter Selasky 	struct usb_xfer *xfer;
141402ac6454SAndrew Thompson 	int error = 0;
141502ac6454SAndrew Thompson 	uint8_t iface_index;
141602ac6454SAndrew Thompson 	uint8_t isread;
141702ac6454SAndrew Thompson 	uint8_t ep_index;
14181c497368SHans Petter Selasky 	uint8_t pre_scale;
141902ac6454SAndrew Thompson 
142002ac6454SAndrew Thompson 	u.addr = addr;
142102ac6454SAndrew Thompson 
142202ac6454SAndrew Thompson 	DPRINTFN(6, "cmd=0x%08lx\n", cmd);
142302ac6454SAndrew Thompson 
142402ac6454SAndrew Thompson 	switch (cmd) {
142502ac6454SAndrew Thompson 	case USB_FS_COMPLETE:
142602ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
142702ac6454SAndrew Thompson 		error = ugen_fs_get_complete(f, &ep_index);
142802ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
142902ac6454SAndrew Thompson 
143002ac6454SAndrew Thompson 		if (error) {
143102ac6454SAndrew Thompson 			error = EBUSY;
143202ac6454SAndrew Thompson 			break;
143302ac6454SAndrew Thompson 		}
143402ac6454SAndrew Thompson 		u.pcomp->ep_index = ep_index;
143502ac6454SAndrew Thompson 		error = ugen_fs_copy_out(f, u.pcomp->ep_index);
143602ac6454SAndrew Thompson 		break;
143702ac6454SAndrew Thompson 
143802ac6454SAndrew Thompson 	case USB_FS_START:
143902ac6454SAndrew Thompson 		error = ugen_fs_copy_in(f, u.pstart->ep_index);
14406c352e99SHans Petter Selasky 		if (error)
144102ac6454SAndrew Thompson 			break;
144202ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
14436c352e99SHans Petter Selasky 		xfer = f->fs_xfer[u.pstart->ep_index];
14446c352e99SHans Petter Selasky 		usbd_transfer_start(xfer);
144502ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
144602ac6454SAndrew Thompson 		break;
144702ac6454SAndrew Thompson 
144802ac6454SAndrew Thompson 	case USB_FS_STOP:
144902ac6454SAndrew Thompson 		if (u.pstop->ep_index >= f->fs_ep_max) {
145002ac6454SAndrew Thompson 			error = EINVAL;
145102ac6454SAndrew Thompson 			break;
145202ac6454SAndrew Thompson 		}
145302ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
14546c352e99SHans Petter Selasky 		xfer = f->fs_xfer[u.pstart->ep_index];
14556c352e99SHans Petter Selasky 		if (usbd_transfer_pending(xfer)) {
14566c352e99SHans Petter Selasky 			usbd_transfer_stop(xfer);
14576c352e99SHans Petter Selasky 			/*
14586c352e99SHans Petter Selasky 			 * Check if the USB transfer was stopped
14596c352e99SHans Petter Selasky 			 * before it was even started. Else a cancel
14606c352e99SHans Petter Selasky 			 * callback will be pending.
14616c352e99SHans Petter Selasky 			 */
14626c352e99SHans Petter Selasky 			if (!xfer->flags_int.transferring) {
14636c352e99SHans Petter Selasky 				ugen_fs_set_complete(xfer->priv_sc,
14646c352e99SHans Petter Selasky 				    USB_P2U(xfer->priv_fifo));
14656c352e99SHans Petter Selasky 			}
14666c352e99SHans Petter Selasky 		}
146702ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
146802ac6454SAndrew Thompson 		break;
146902ac6454SAndrew Thompson 
147002ac6454SAndrew Thompson 	case USB_FS_OPEN:
1471e9cec016SHans Petter Selasky 	case USB_FS_OPEN_STREAM:
147202ac6454SAndrew Thompson 		if (u.popen->ep_index >= f->fs_ep_max) {
147302ac6454SAndrew Thompson 			error = EINVAL;
147402ac6454SAndrew Thompson 			break;
147502ac6454SAndrew Thompson 		}
147602ac6454SAndrew Thompson 		if (f->fs_xfer[u.popen->ep_index] != NULL) {
147702ac6454SAndrew Thompson 			error = EBUSY;
147802ac6454SAndrew Thompson 			break;
147902ac6454SAndrew Thompson 		}
148002ac6454SAndrew Thompson 		if (u.popen->max_bufsize > USB_FS_MAX_BUFSIZE) {
148102ac6454SAndrew Thompson 			u.popen->max_bufsize = USB_FS_MAX_BUFSIZE;
148202ac6454SAndrew Thompson 		}
14831c497368SHans Petter Selasky 		if (u.popen->max_frames & USB_FS_MAX_FRAMES_PRE_SCALE) {
14841c497368SHans Petter Selasky 			pre_scale = 1;
14851c497368SHans Petter Selasky 			u.popen->max_frames &= ~USB_FS_MAX_FRAMES_PRE_SCALE;
14861c497368SHans Petter Selasky 		} else {
14871c497368SHans Petter Selasky 			pre_scale = 0;
14881c497368SHans Petter Selasky 		}
148902ac6454SAndrew Thompson 		if (u.popen->max_frames > USB_FS_MAX_FRAMES) {
149002ac6454SAndrew Thompson 			u.popen->max_frames = USB_FS_MAX_FRAMES;
149102ac6454SAndrew Thompson 			break;
149202ac6454SAndrew Thompson 		}
149302ac6454SAndrew Thompson 		if (u.popen->max_frames == 0) {
149402ac6454SAndrew Thompson 			error = EINVAL;
149502ac6454SAndrew Thompson 			break;
149602ac6454SAndrew Thompson 		}
1497a593f6b8SAndrew Thompson 		ep = usbd_get_ep_by_addr(f->udev, u.popen->ep_no);
1498ae60fdfbSAndrew Thompson 		if (ep == NULL) {
149902ac6454SAndrew Thompson 			error = EINVAL;
150002ac6454SAndrew Thompson 			break;
150102ac6454SAndrew Thompson 		}
1502ae60fdfbSAndrew Thompson 		ed = ep->edesc;
150302ac6454SAndrew Thompson 		if (ed == NULL) {
150402ac6454SAndrew Thompson 			error = ENXIO;
150502ac6454SAndrew Thompson 			break;
150602ac6454SAndrew Thompson 		}
1507ae60fdfbSAndrew Thompson 		iface_index = ep->iface_index;
150802ac6454SAndrew Thompson 
15091c497368SHans Petter Selasky 		memset(usb_config, 0, sizeof(usb_config));
151002ac6454SAndrew Thompson 
1511760bc48eSAndrew Thompson 		usb_config[0].type = ed->bmAttributes & UE_XFERTYPE;
1512760bc48eSAndrew Thompson 		usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR;
1513760bc48eSAndrew Thompson 		usb_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN);
1514760bc48eSAndrew Thompson 		usb_config[0].interval = USB_DEFAULT_INTERVAL;
1515760bc48eSAndrew Thompson 		usb_config[0].flags.proxy_buffer = 1;
15161c497368SHans Petter Selasky 		if (pre_scale != 0)
15171c497368SHans Petter Selasky 			usb_config[0].flags.pre_scale_frames = 1;
15185b3bb704SAndrew Thompson 		usb_config[0].callback = &ugen_ctrl_fs_callback;
1519760bc48eSAndrew Thompson 		usb_config[0].timeout = 0;	/* no timeout */
1520760bc48eSAndrew Thompson 		usb_config[0].frames = u.popen->max_frames;
1521760bc48eSAndrew Thompson 		usb_config[0].bufsize = u.popen->max_bufsize;
1522760bc48eSAndrew Thompson 		usb_config[0].usb_mode = USB_MODE_DUAL;	/* both modes */
1523e9cec016SHans Petter Selasky 		if (cmd == USB_FS_OPEN_STREAM)
1524e9cec016SHans Petter Selasky 			usb_config[0].stream_id = u.popen_stream->stream_id;
152502ac6454SAndrew Thompson 
1526760bc48eSAndrew Thompson 		if (usb_config[0].type == UE_CONTROL) {
1527f29a0724SAndrew Thompson 			if (f->udev->flags.usb_mode != USB_MODE_HOST) {
152802ac6454SAndrew Thompson 				error = EINVAL;
152902ac6454SAndrew Thompson 				break;
153002ac6454SAndrew Thompson 			}
153102ac6454SAndrew Thompson 		} else {
153202ac6454SAndrew Thompson 
1533760bc48eSAndrew Thompson 			isread = ((usb_config[0].endpoint &
153402ac6454SAndrew Thompson 			    (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN);
153502ac6454SAndrew Thompson 
1536f29a0724SAndrew Thompson 			if (f->udev->flags.usb_mode != USB_MODE_HOST) {
153702ac6454SAndrew Thompson 				isread = !isread;
153802ac6454SAndrew Thompson 			}
153902ac6454SAndrew Thompson 			/* check permissions */
154002ac6454SAndrew Thompson 			if (isread) {
154102ac6454SAndrew Thompson 				if (!(fflags & FREAD)) {
154202ac6454SAndrew Thompson 					error = EPERM;
154302ac6454SAndrew Thompson 					break;
154402ac6454SAndrew Thompson 				}
154502ac6454SAndrew Thompson 			} else {
154602ac6454SAndrew Thompson 				if (!(fflags & FWRITE)) {
154702ac6454SAndrew Thompson 					error = EPERM;
154802ac6454SAndrew Thompson 					break;
154902ac6454SAndrew Thompson 				}
155002ac6454SAndrew Thompson 			}
155102ac6454SAndrew Thompson 		}
1552a593f6b8SAndrew Thompson 		error = usbd_transfer_setup(f->udev, &iface_index,
1553760bc48eSAndrew Thompson 		    f->fs_xfer + u.popen->ep_index, usb_config, 1,
155402ac6454SAndrew Thompson 		    f, f->priv_mtx);
155502ac6454SAndrew Thompson 		if (error == 0) {
155602ac6454SAndrew Thompson 			/* update maximums */
155702ac6454SAndrew Thompson 			u.popen->max_packet_length =
155802ac6454SAndrew Thompson 			    f->fs_xfer[u.popen->ep_index]->max_frame_size;
155902ac6454SAndrew Thompson 			u.popen->max_bufsize =
156002ac6454SAndrew Thompson 			    f->fs_xfer[u.popen->ep_index]->max_data_length;
15611c497368SHans Petter Selasky 			/* update number of frames */
15621c497368SHans Petter Selasky 			u.popen->max_frames =
15631c497368SHans Petter Selasky 			    f->fs_xfer[u.popen->ep_index]->nframes;
15641c497368SHans Petter Selasky 			/* store index of endpoint */
156502ac6454SAndrew Thompson 			f->fs_xfer[u.popen->ep_index]->priv_fifo =
156602ac6454SAndrew Thompson 			    ((uint8_t *)0) + u.popen->ep_index;
156702ac6454SAndrew Thompson 		} else {
156802ac6454SAndrew Thompson 			error = ENOMEM;
156902ac6454SAndrew Thompson 		}
157002ac6454SAndrew Thompson 		break;
157102ac6454SAndrew Thompson 
157202ac6454SAndrew Thompson 	case USB_FS_CLOSE:
157302ac6454SAndrew Thompson 		if (u.pclose->ep_index >= f->fs_ep_max) {
157402ac6454SAndrew Thompson 			error = EINVAL;
157502ac6454SAndrew Thompson 			break;
157602ac6454SAndrew Thompson 		}
157702ac6454SAndrew Thompson 		if (f->fs_xfer[u.pclose->ep_index] == NULL) {
157802ac6454SAndrew Thompson 			error = EINVAL;
157902ac6454SAndrew Thompson 			break;
158002ac6454SAndrew Thompson 		}
1581a593f6b8SAndrew Thompson 		usbd_transfer_unsetup(f->fs_xfer + u.pclose->ep_index, 1);
158202ac6454SAndrew Thompson 		break;
158302ac6454SAndrew Thompson 
158402ac6454SAndrew Thompson 	case USB_FS_CLEAR_STALL_SYNC:
158502ac6454SAndrew Thompson 		if (u.pstall->ep_index >= f->fs_ep_max) {
158602ac6454SAndrew Thompson 			error = EINVAL;
158702ac6454SAndrew Thompson 			break;
158802ac6454SAndrew Thompson 		}
158902ac6454SAndrew Thompson 		if (f->fs_xfer[u.pstall->ep_index] == NULL) {
159002ac6454SAndrew Thompson 			error = EINVAL;
159102ac6454SAndrew Thompson 			break;
159202ac6454SAndrew Thompson 		}
1593f29a0724SAndrew Thompson 		if (f->udev->flags.usb_mode != USB_MODE_HOST) {
159402ac6454SAndrew Thompson 			error = EINVAL;
159502ac6454SAndrew Thompson 			break;
159602ac6454SAndrew Thompson 		}
159702ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
1598a593f6b8SAndrew Thompson 		error = usbd_transfer_pending(f->fs_xfer[u.pstall->ep_index]);
159902ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
160002ac6454SAndrew Thompson 
160102ac6454SAndrew Thompson 		if (error) {
160202ac6454SAndrew Thompson 			return (EBUSY);
160302ac6454SAndrew Thompson 		}
1604ae60fdfbSAndrew Thompson 		ep = f->fs_xfer[u.pstall->ep_index]->endpoint;
160502ac6454SAndrew Thompson 
160602ac6454SAndrew Thompson 		/* setup a clear-stall packet */
160702ac6454SAndrew Thompson 		req.bmRequestType = UT_WRITE_ENDPOINT;
160802ac6454SAndrew Thompson 		req.bRequest = UR_CLEAR_FEATURE;
160902ac6454SAndrew Thompson 		USETW(req.wValue, UF_ENDPOINT_HALT);
1610ae60fdfbSAndrew Thompson 		req.wIndex[0] = ep->edesc->bEndpointAddress;
161102ac6454SAndrew Thompson 		req.wIndex[1] = 0;
161202ac6454SAndrew Thompson 		USETW(req.wLength, 0);
161302ac6454SAndrew Thompson 
1614a593f6b8SAndrew Thompson 		error = usbd_do_request(f->udev, NULL, &req, NULL);
161502ac6454SAndrew Thompson 		if (error == 0) {
1616a593f6b8SAndrew Thompson 			usbd_clear_data_toggle(f->udev, ep);
161702ac6454SAndrew Thompson 		} else {
161802ac6454SAndrew Thompson 			error = ENXIO;
161902ac6454SAndrew Thompson 		}
162002ac6454SAndrew Thompson 		break;
162102ac6454SAndrew Thompson 
162202ac6454SAndrew Thompson 	default:
162302ac6454SAndrew Thompson 		error = ENOIOCTL;
162402ac6454SAndrew Thompson 		break;
162502ac6454SAndrew Thompson 	}
162602ac6454SAndrew Thompson 
162702ac6454SAndrew Thompson 	DPRINTFN(6, "error=%d\n", error);
162802ac6454SAndrew Thompson 
162902ac6454SAndrew Thompson 	return (error);
163002ac6454SAndrew Thompson }
163102ac6454SAndrew Thompson 
163202ac6454SAndrew Thompson static int
1633760bc48eSAndrew Thompson ugen_set_short_xfer(struct usb_fifo *f, void *addr)
163402ac6454SAndrew Thompson {
163502ac6454SAndrew Thompson 	uint8_t t;
163602ac6454SAndrew Thompson 
163702ac6454SAndrew Thompson 	if (*(int *)addr)
163802ac6454SAndrew Thompson 		t = 1;
163902ac6454SAndrew Thompson 	else
164002ac6454SAndrew Thompson 		t = 0;
164102ac6454SAndrew Thompson 
164202ac6454SAndrew Thompson 	if (f->flag_short == t) {
164302ac6454SAndrew Thompson 		/* same value like before - accept */
164402ac6454SAndrew Thompson 		return (0);
164502ac6454SAndrew Thompson 	}
164602ac6454SAndrew Thompson 	if (f->xfer[0] || f->xfer[1]) {
164702ac6454SAndrew Thompson 		/* cannot change this during transfer */
164802ac6454SAndrew Thompson 		return (EBUSY);
164902ac6454SAndrew Thompson 	}
165002ac6454SAndrew Thompson 	f->flag_short = t;
165102ac6454SAndrew Thompson 	return (0);
165202ac6454SAndrew Thompson }
165302ac6454SAndrew Thompson 
165402ac6454SAndrew Thompson static int
1655760bc48eSAndrew Thompson ugen_set_timeout(struct usb_fifo *f, void *addr)
165602ac6454SAndrew Thompson {
165702ac6454SAndrew Thompson 	f->timeout = *(int *)addr;
165802ac6454SAndrew Thompson 	if (f->timeout > 65535) {
165902ac6454SAndrew Thompson 		/* limit user input */
166002ac6454SAndrew Thompson 		f->timeout = 65535;
166102ac6454SAndrew Thompson 	}
166202ac6454SAndrew Thompson 	return (0);
166302ac6454SAndrew Thompson }
166402ac6454SAndrew Thompson 
166502ac6454SAndrew Thompson static int
1666760bc48eSAndrew Thompson ugen_get_frame_size(struct usb_fifo *f, void *addr)
166702ac6454SAndrew Thompson {
166802ac6454SAndrew Thompson 	if (f->xfer[0]) {
166902ac6454SAndrew Thompson 		*(int *)addr = f->xfer[0]->max_frame_size;
167002ac6454SAndrew Thompson 	} else {
167102ac6454SAndrew Thompson 		return (EINVAL);
167202ac6454SAndrew Thompson 	}
167302ac6454SAndrew Thompson 	return (0);
167402ac6454SAndrew Thompson }
167502ac6454SAndrew Thompson 
167602ac6454SAndrew Thompson static int
1677760bc48eSAndrew Thompson ugen_set_buffer_size(struct usb_fifo *f, void *addr)
167802ac6454SAndrew Thompson {
1679e0a69b51SAndrew Thompson 	usb_frlength_t t;
168002ac6454SAndrew Thompson 
168101cf7831SAndrew Thompson 	if (*(int *)addr < 0)
168201cf7831SAndrew Thompson 		t = 0;		/* use "wMaxPacketSize" */
168302ac6454SAndrew Thompson 	else if (*(int *)addr < (256 * 1024))
168402ac6454SAndrew Thompson 		t = *(int *)addr;
168502ac6454SAndrew Thompson 	else
168602ac6454SAndrew Thompson 		t = 256 * 1024;
168702ac6454SAndrew Thompson 
168802ac6454SAndrew Thompson 	if (f->bufsize == t) {
168902ac6454SAndrew Thompson 		/* same value like before - accept */
169002ac6454SAndrew Thompson 		return (0);
169102ac6454SAndrew Thompson 	}
169202ac6454SAndrew Thompson 	if (f->xfer[0] || f->xfer[1]) {
169302ac6454SAndrew Thompson 		/* cannot change this during transfer */
169402ac6454SAndrew Thompson 		return (EBUSY);
169502ac6454SAndrew Thompson 	}
169602ac6454SAndrew Thompson 	f->bufsize = t;
169702ac6454SAndrew Thompson 	return (0);
169802ac6454SAndrew Thompson }
169902ac6454SAndrew Thompson 
170002ac6454SAndrew Thompson static int
1701760bc48eSAndrew Thompson ugen_get_buffer_size(struct usb_fifo *f, void *addr)
170202ac6454SAndrew Thompson {
170302ac6454SAndrew Thompson 	*(int *)addr = f->bufsize;
170402ac6454SAndrew Thompson 	return (0);
170502ac6454SAndrew Thompson }
170602ac6454SAndrew Thompson 
170702ac6454SAndrew Thompson static int
1708760bc48eSAndrew Thompson ugen_get_iface_desc(struct usb_fifo *f,
1709760bc48eSAndrew Thompson     struct usb_interface_descriptor *idesc)
171002ac6454SAndrew Thompson {
1711760bc48eSAndrew Thompson 	struct usb_interface *iface;
171202ac6454SAndrew Thompson 
1713a593f6b8SAndrew Thompson 	iface = usbd_get_iface(f->udev, f->iface_index);
171402ac6454SAndrew Thompson 	if (iface && iface->idesc) {
171502ac6454SAndrew Thompson 		*idesc = *(iface->idesc);
171602ac6454SAndrew Thompson 	} else {
171702ac6454SAndrew Thompson 		return (EIO);
171802ac6454SAndrew Thompson 	}
171902ac6454SAndrew Thompson 	return (0);
172002ac6454SAndrew Thompson }
172102ac6454SAndrew Thompson 
172202ac6454SAndrew Thompson static int
1723760bc48eSAndrew Thompson ugen_get_endpoint_desc(struct usb_fifo *f,
1724760bc48eSAndrew Thompson     struct usb_endpoint_descriptor *ed)
172502ac6454SAndrew Thompson {
1726ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep;
172702ac6454SAndrew Thompson 
1728ed6d949aSAndrew Thompson 	ep = usb_fifo_softc(f);
172902ac6454SAndrew Thompson 
1730ae60fdfbSAndrew Thompson 	if (ep && ep->edesc) {
1731ae60fdfbSAndrew Thompson 		*ed = *ep->edesc;
173202ac6454SAndrew Thompson 	} else {
173302ac6454SAndrew Thompson 		return (EINVAL);
173402ac6454SAndrew Thompson 	}
173502ac6454SAndrew Thompson 	return (0);
173602ac6454SAndrew Thompson }
173702ac6454SAndrew Thompson 
173802ac6454SAndrew Thompson static int
1739760bc48eSAndrew Thompson ugen_set_power_mode(struct usb_fifo *f, int mode)
174002ac6454SAndrew Thompson {
1741760bc48eSAndrew Thompson 	struct usb_device *udev = f->udev;
174202ac6454SAndrew Thompson 	int err;
174302ac6454SAndrew Thompson 	uint8_t old_mode;
174402ac6454SAndrew Thompson 
174502ac6454SAndrew Thompson 	if ((udev == NULL) ||
174602ac6454SAndrew Thompson 	    (udev->parent_hub == NULL)) {
174702ac6454SAndrew Thompson 		return (EINVAL);
174802ac6454SAndrew Thompson 	}
174950230f98SAndrew Thompson 	err = priv_check(curthread, PRIV_DRIVER);
175002ac6454SAndrew Thompson 	if (err)
175102ac6454SAndrew Thompson 		return (err);
175202ac6454SAndrew Thompson 
175302ac6454SAndrew Thompson 	/* get old power mode */
175402ac6454SAndrew Thompson 	old_mode = udev->power_mode;
175502ac6454SAndrew Thompson 
175602ac6454SAndrew Thompson 	/* if no change, then just return */
175702ac6454SAndrew Thompson 	if (old_mode == mode)
175802ac6454SAndrew Thompson 		return (0);
175902ac6454SAndrew Thompson 
176002ac6454SAndrew Thompson 	switch (mode) {
176102ac6454SAndrew Thompson 	case USB_POWER_MODE_OFF:
176202ac6454SAndrew Thompson 		/* get the device unconfigured */
176302ac6454SAndrew Thompson 		err = ugen_set_config(f, USB_UNCONFIG_INDEX);
176402ac6454SAndrew Thompson 		if (err) {
176502ac6454SAndrew Thompson 			DPRINTFN(0, "Could not unconfigure "
176602ac6454SAndrew Thompson 			    "device (ignored)\n");
176702ac6454SAndrew Thompson 		}
176802ac6454SAndrew Thompson 
176902ac6454SAndrew Thompson 		/* clear port enable */
1770a593f6b8SAndrew Thompson 		err = usbd_req_clear_port_feature(udev->parent_hub,
177102ac6454SAndrew Thompson 		    NULL, udev->port_no, UHF_PORT_ENABLE);
177202ac6454SAndrew Thompson 		break;
177302ac6454SAndrew Thompson 
177402ac6454SAndrew Thompson 	case USB_POWER_MODE_ON:
177502ac6454SAndrew Thompson 	case USB_POWER_MODE_SAVE:
177602ac6454SAndrew Thompson 		break;
177702ac6454SAndrew Thompson 
177802ac6454SAndrew Thompson 	case USB_POWER_MODE_RESUME:
17792df1e9a6SAndrew Thompson #if USB_HAVE_POWERD
17802df1e9a6SAndrew Thompson 		/* let USB-powerd handle resume */
17812df1e9a6SAndrew Thompson 		USB_BUS_LOCK(udev->bus);
17822df1e9a6SAndrew Thompson 		udev->pwr_save.write_refs++;
17832df1e9a6SAndrew Thompson 		udev->pwr_save.last_xfer_time = ticks;
17842df1e9a6SAndrew Thompson 		USB_BUS_UNLOCK(udev->bus);
17852df1e9a6SAndrew Thompson 
17862df1e9a6SAndrew Thompson 		/* set new power mode */
17872df1e9a6SAndrew Thompson 		usbd_set_power_mode(udev, USB_POWER_MODE_SAVE);
17882df1e9a6SAndrew Thompson 
17892df1e9a6SAndrew Thompson 		/* wait for resume to complete */
17902df1e9a6SAndrew Thompson 		usb_pause_mtx(NULL, hz / 4);
17912df1e9a6SAndrew Thompson 
17922df1e9a6SAndrew Thompson 		/* clear write reference */
17932df1e9a6SAndrew Thompson 		USB_BUS_LOCK(udev->bus);
17942df1e9a6SAndrew Thompson 		udev->pwr_save.write_refs--;
17952df1e9a6SAndrew Thompson 		USB_BUS_UNLOCK(udev->bus);
17962df1e9a6SAndrew Thompson #endif
179702ac6454SAndrew Thompson 		mode = USB_POWER_MODE_SAVE;
179802ac6454SAndrew Thompson 		break;
179902ac6454SAndrew Thompson 
180002ac6454SAndrew Thompson 	case USB_POWER_MODE_SUSPEND:
18012df1e9a6SAndrew Thompson #if USB_HAVE_POWERD
18022df1e9a6SAndrew Thompson 		/* let USB-powerd handle suspend */
18032df1e9a6SAndrew Thompson 		USB_BUS_LOCK(udev->bus);
18042df1e9a6SAndrew Thompson 		udev->pwr_save.last_xfer_time = ticks - (256 * hz);
18052df1e9a6SAndrew Thompson 		USB_BUS_UNLOCK(udev->bus);
18062df1e9a6SAndrew Thompson #endif
180702ac6454SAndrew Thompson 		mode = USB_POWER_MODE_SAVE;
180802ac6454SAndrew Thompson 		break;
180902ac6454SAndrew Thompson 
181002ac6454SAndrew Thompson 	default:
181102ac6454SAndrew Thompson 		return (EINVAL);
181202ac6454SAndrew Thompson 	}
181302ac6454SAndrew Thompson 
181402ac6454SAndrew Thompson 	if (err)
181502ac6454SAndrew Thompson 		return (ENXIO);		/* I/O failure */
181602ac6454SAndrew Thompson 
181702ac6454SAndrew Thompson 	/* if we are powered off we need to re-enumerate first */
181802ac6454SAndrew Thompson 	if (old_mode == USB_POWER_MODE_OFF) {
18198f9750b7SHans Petter Selasky 		if (udev->flags.usb_mode == USB_MODE_HOST) {
18208f9750b7SHans Petter Selasky 			if (udev->re_enumerate_wait == 0)
18218f9750b7SHans Petter Selasky 				udev->re_enumerate_wait = 1;
18228f9750b7SHans Petter Selasky 		}
18238f9750b7SHans Petter Selasky 		/* set power mode will wake up the explore thread */
182402ac6454SAndrew Thompson 	}
182502ac6454SAndrew Thompson 
182602ac6454SAndrew Thompson 	/* set new power mode */
1827a593f6b8SAndrew Thompson 	usbd_set_power_mode(udev, mode);
182802ac6454SAndrew Thompson 
182902ac6454SAndrew Thompson 	return (0);			/* success */
183002ac6454SAndrew Thompson }
183102ac6454SAndrew Thompson 
183202ac6454SAndrew Thompson static int
1833760bc48eSAndrew Thompson ugen_get_power_mode(struct usb_fifo *f)
183402ac6454SAndrew Thompson {
1835760bc48eSAndrew Thompson 	struct usb_device *udev = f->udev;
183602ac6454SAndrew Thompson 
1837d2d71ce7SAndrew Thompson 	if (udev == NULL)
183802ac6454SAndrew Thompson 		return (USB_POWER_MODE_ON);
1839d2d71ce7SAndrew Thompson 
184002ac6454SAndrew Thompson 	return (udev->power_mode);
184102ac6454SAndrew Thompson }
184202ac6454SAndrew Thompson 
184302ac6454SAndrew Thompson static int
1844760bc48eSAndrew Thompson ugen_do_port_feature(struct usb_fifo *f, uint8_t port_no,
184502ac6454SAndrew Thompson     uint8_t set, uint16_t feature)
184602ac6454SAndrew Thompson {
1847760bc48eSAndrew Thompson 	struct usb_device *udev = f->udev;
1848760bc48eSAndrew Thompson 	struct usb_hub *hub;
184902ac6454SAndrew Thompson 	int err;
185002ac6454SAndrew Thompson 
185150230f98SAndrew Thompson 	err = priv_check(curthread, PRIV_DRIVER);
185202ac6454SAndrew Thompson 	if (err) {
185302ac6454SAndrew Thompson 		return (err);
185402ac6454SAndrew Thompson 	}
185502ac6454SAndrew Thompson 	if (port_no == 0) {
185602ac6454SAndrew Thompson 		return (EINVAL);
185702ac6454SAndrew Thompson 	}
185802ac6454SAndrew Thompson 	if ((udev == NULL) ||
185902ac6454SAndrew Thompson 	    (udev->hub == NULL)) {
186002ac6454SAndrew Thompson 		return (EINVAL);
186102ac6454SAndrew Thompson 	}
186202ac6454SAndrew Thompson 	hub = udev->hub;
186302ac6454SAndrew Thompson 
186402ac6454SAndrew Thompson 	if (port_no > hub->nports) {
186502ac6454SAndrew Thompson 		return (EINVAL);
186602ac6454SAndrew Thompson 	}
186702ac6454SAndrew Thompson 	if (set)
1868a593f6b8SAndrew Thompson 		err = usbd_req_set_port_feature(udev,
186902ac6454SAndrew Thompson 		    NULL, port_no, feature);
187002ac6454SAndrew Thompson 	else
1871a593f6b8SAndrew Thompson 		err = usbd_req_clear_port_feature(udev,
187202ac6454SAndrew Thompson 		    NULL, port_no, feature);
187302ac6454SAndrew Thompson 
187402ac6454SAndrew Thompson 	if (err)
187502ac6454SAndrew Thompson 		return (ENXIO);		/* failure */
187602ac6454SAndrew Thompson 
187702ac6454SAndrew Thompson 	return (0);			/* success */
187802ac6454SAndrew Thompson }
187902ac6454SAndrew Thompson 
188002ac6454SAndrew Thompson static int
1881760bc48eSAndrew Thompson ugen_iface_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
188202ac6454SAndrew Thompson {
1883760bc48eSAndrew Thompson 	struct usb_fifo *f_rx;
1884760bc48eSAndrew Thompson 	struct usb_fifo *f_tx;
188502ac6454SAndrew Thompson 	int error = 0;
188602ac6454SAndrew Thompson 
188702ac6454SAndrew Thompson 	f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX];
188802ac6454SAndrew Thompson 	f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX];
188902ac6454SAndrew Thompson 
189002ac6454SAndrew Thompson 	switch (cmd) {
189102ac6454SAndrew Thompson 	case USB_SET_RX_SHORT_XFER:
189202ac6454SAndrew Thompson 		if (fflags & FREAD) {
189302ac6454SAndrew Thompson 			error = ugen_set_short_xfer(f_rx, addr);
189402ac6454SAndrew Thompson 		} else {
189502ac6454SAndrew Thompson 			error = EINVAL;
189602ac6454SAndrew Thompson 		}
189702ac6454SAndrew Thompson 		break;
189802ac6454SAndrew Thompson 
189902ac6454SAndrew Thompson 	case USB_SET_TX_FORCE_SHORT:
190002ac6454SAndrew Thompson 		if (fflags & FWRITE) {
190102ac6454SAndrew Thompson 			error = ugen_set_short_xfer(f_tx, addr);
190202ac6454SAndrew Thompson 		} else {
190302ac6454SAndrew Thompson 			error = EINVAL;
190402ac6454SAndrew Thompson 		}
190502ac6454SAndrew Thompson 		break;
190602ac6454SAndrew Thompson 
190702ac6454SAndrew Thompson 	case USB_SET_RX_TIMEOUT:
190802ac6454SAndrew Thompson 		if (fflags & FREAD) {
190902ac6454SAndrew Thompson 			error = ugen_set_timeout(f_rx, addr);
191002ac6454SAndrew Thompson 		} else {
191102ac6454SAndrew Thompson 			error = EINVAL;
191202ac6454SAndrew Thompson 		}
191302ac6454SAndrew Thompson 		break;
191402ac6454SAndrew Thompson 
191502ac6454SAndrew Thompson 	case USB_SET_TX_TIMEOUT:
191602ac6454SAndrew Thompson 		if (fflags & FWRITE) {
191702ac6454SAndrew Thompson 			error = ugen_set_timeout(f_tx, addr);
191802ac6454SAndrew Thompson 		} else {
191902ac6454SAndrew Thompson 			error = EINVAL;
192002ac6454SAndrew Thompson 		}
192102ac6454SAndrew Thompson 		break;
192202ac6454SAndrew Thompson 
192302ac6454SAndrew Thompson 	case USB_GET_RX_FRAME_SIZE:
192402ac6454SAndrew Thompson 		if (fflags & FREAD) {
192502ac6454SAndrew Thompson 			error = ugen_get_frame_size(f_rx, addr);
192602ac6454SAndrew Thompson 		} else {
192702ac6454SAndrew Thompson 			error = EINVAL;
192802ac6454SAndrew Thompson 		}
192902ac6454SAndrew Thompson 		break;
193002ac6454SAndrew Thompson 
193102ac6454SAndrew Thompson 	case USB_GET_TX_FRAME_SIZE:
193202ac6454SAndrew Thompson 		if (fflags & FWRITE) {
193302ac6454SAndrew Thompson 			error = ugen_get_frame_size(f_tx, addr);
193402ac6454SAndrew Thompson 		} else {
193502ac6454SAndrew Thompson 			error = EINVAL;
193602ac6454SAndrew Thompson 		}
193702ac6454SAndrew Thompson 		break;
193802ac6454SAndrew Thompson 
193902ac6454SAndrew Thompson 	case USB_SET_RX_BUFFER_SIZE:
194002ac6454SAndrew Thompson 		if (fflags & FREAD) {
194102ac6454SAndrew Thompson 			error = ugen_set_buffer_size(f_rx, addr);
194202ac6454SAndrew Thompson 		} else {
194302ac6454SAndrew Thompson 			error = EINVAL;
194402ac6454SAndrew Thompson 		}
194502ac6454SAndrew Thompson 		break;
194602ac6454SAndrew Thompson 
194702ac6454SAndrew Thompson 	case USB_SET_TX_BUFFER_SIZE:
194802ac6454SAndrew Thompson 		if (fflags & FWRITE) {
194902ac6454SAndrew Thompson 			error = ugen_set_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_BUFFER_SIZE:
195602ac6454SAndrew Thompson 		if (fflags & FREAD) {
195702ac6454SAndrew Thompson 			error = ugen_get_buffer_size(f_rx, addr);
195802ac6454SAndrew Thompson 		} else {
195902ac6454SAndrew Thompson 			error = EINVAL;
196002ac6454SAndrew Thompson 		}
196102ac6454SAndrew Thompson 		break;
196202ac6454SAndrew Thompson 
196302ac6454SAndrew Thompson 	case USB_GET_TX_BUFFER_SIZE:
196402ac6454SAndrew Thompson 		if (fflags & FWRITE) {
196502ac6454SAndrew Thompson 			error = ugen_get_buffer_size(f_tx, addr);
196602ac6454SAndrew Thompson 		} else {
196702ac6454SAndrew Thompson 			error = EINVAL;
196802ac6454SAndrew Thompson 		}
196902ac6454SAndrew Thompson 		break;
197002ac6454SAndrew Thompson 
197102ac6454SAndrew Thompson 	case USB_GET_RX_INTERFACE_DESC:
197202ac6454SAndrew Thompson 		if (fflags & FREAD) {
197302ac6454SAndrew Thompson 			error = ugen_get_iface_desc(f_rx, addr);
197402ac6454SAndrew Thompson 		} else {
197502ac6454SAndrew Thompson 			error = EINVAL;
197602ac6454SAndrew Thompson 		}
197702ac6454SAndrew Thompson 		break;
197802ac6454SAndrew Thompson 
197902ac6454SAndrew Thompson 	case USB_GET_TX_INTERFACE_DESC:
198002ac6454SAndrew Thompson 		if (fflags & FWRITE) {
198102ac6454SAndrew Thompson 			error = ugen_get_iface_desc(f_tx, addr);
198202ac6454SAndrew Thompson 		} else {
198302ac6454SAndrew Thompson 			error = EINVAL;
198402ac6454SAndrew Thompson 		}
198502ac6454SAndrew Thompson 		break;
198602ac6454SAndrew Thompson 
198702ac6454SAndrew Thompson 	case USB_GET_RX_ENDPOINT_DESC:
198802ac6454SAndrew Thompson 		if (fflags & FREAD) {
198902ac6454SAndrew Thompson 			error = ugen_get_endpoint_desc(f_rx, addr);
199002ac6454SAndrew Thompson 		} else {
199102ac6454SAndrew Thompson 			error = EINVAL;
199202ac6454SAndrew Thompson 		}
199302ac6454SAndrew Thompson 		break;
199402ac6454SAndrew Thompson 
199502ac6454SAndrew Thompson 	case USB_GET_TX_ENDPOINT_DESC:
199602ac6454SAndrew Thompson 		if (fflags & FWRITE) {
199702ac6454SAndrew Thompson 			error = ugen_get_endpoint_desc(f_tx, addr);
199802ac6454SAndrew Thompson 		} else {
199902ac6454SAndrew Thompson 			error = EINVAL;
200002ac6454SAndrew Thompson 		}
200102ac6454SAndrew Thompson 		break;
200202ac6454SAndrew Thompson 
200302ac6454SAndrew Thompson 	case USB_SET_RX_STALL_FLAG:
200402ac6454SAndrew Thompson 		if ((fflags & FREAD) && (*(int *)addr)) {
200502ac6454SAndrew Thompson 			f_rx->flag_stall = 1;
200602ac6454SAndrew Thompson 		}
200702ac6454SAndrew Thompson 		break;
200802ac6454SAndrew Thompson 
200902ac6454SAndrew Thompson 	case USB_SET_TX_STALL_FLAG:
201002ac6454SAndrew Thompson 		if ((fflags & FWRITE) && (*(int *)addr)) {
201102ac6454SAndrew Thompson 			f_tx->flag_stall = 1;
201202ac6454SAndrew Thompson 		}
201302ac6454SAndrew Thompson 		break;
201402ac6454SAndrew Thompson 
201502ac6454SAndrew Thompson 	default:
201602ac6454SAndrew Thompson 		error = ENOIOCTL;
201702ac6454SAndrew Thompson 		break;
201802ac6454SAndrew Thompson 	}
201902ac6454SAndrew Thompson 	return (error);
202002ac6454SAndrew Thompson }
202102ac6454SAndrew Thompson 
202202ac6454SAndrew Thompson static int
2023760bc48eSAndrew Thompson ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
202402ac6454SAndrew Thompson {
202502ac6454SAndrew Thompson 	union {
2026760bc48eSAndrew Thompson 		struct usb_interface_descriptor *idesc;
2027760bc48eSAndrew Thompson 		struct usb_alt_interface *ai;
2028760bc48eSAndrew Thompson 		struct usb_device_descriptor *ddesc;
2029760bc48eSAndrew Thompson 		struct usb_config_descriptor *cdesc;
2030760bc48eSAndrew Thompson 		struct usb_device_stats *stat;
2031760bc48eSAndrew Thompson 		struct usb_fs_init *pinit;
2032760bc48eSAndrew Thompson 		struct usb_fs_uninit *puninit;
203302ac6454SAndrew Thompson 		uint32_t *ptime;
203402ac6454SAndrew Thompson 		void   *addr;
203502ac6454SAndrew Thompson 		int    *pint;
203602ac6454SAndrew Thompson 	}     u;
2037760bc48eSAndrew Thompson 	struct usb_device_descriptor *dtemp;
2038760bc48eSAndrew Thompson 	struct usb_config_descriptor *ctemp;
2039760bc48eSAndrew Thompson 	struct usb_interface *iface;
204002ac6454SAndrew Thompson 	int error = 0;
204102ac6454SAndrew Thompson 	uint8_t n;
204202ac6454SAndrew Thompson 
204302ac6454SAndrew Thompson 	u.addr = addr;
204402ac6454SAndrew Thompson 
204502ac6454SAndrew Thompson 	DPRINTFN(6, "cmd=0x%08lx\n", cmd);
204602ac6454SAndrew Thompson 
204702ac6454SAndrew Thompson 	switch (cmd) {
204802ac6454SAndrew Thompson 	case USB_DISCOVER:
2049a593f6b8SAndrew Thompson 		usb_needs_explore_all();
205002ac6454SAndrew Thompson 		break;
205102ac6454SAndrew Thompson 
205202ac6454SAndrew Thompson 	case USB_SETDEBUG:
205302ac6454SAndrew Thompson 		if (!(fflags & FWRITE)) {
205402ac6454SAndrew Thompson 			error = EPERM;
205502ac6454SAndrew Thompson 			break;
205602ac6454SAndrew Thompson 		}
2057a593f6b8SAndrew Thompson 		usb_debug = *(int *)addr;
205802ac6454SAndrew Thompson 		break;
205902ac6454SAndrew Thompson 
206002ac6454SAndrew Thompson 	case USB_GET_CONFIG:
206102ac6454SAndrew Thompson 		*(int *)addr = f->udev->curr_config_index;
206202ac6454SAndrew Thompson 		break;
206302ac6454SAndrew Thompson 
206402ac6454SAndrew Thompson 	case USB_SET_CONFIG:
206502ac6454SAndrew Thompson 		if (!(fflags & FWRITE)) {
206602ac6454SAndrew Thompson 			error = EPERM;
206702ac6454SAndrew Thompson 			break;
206802ac6454SAndrew Thompson 		}
206902ac6454SAndrew Thompson 		error = ugen_set_config(f, *(int *)addr);
207002ac6454SAndrew Thompson 		break;
207102ac6454SAndrew Thompson 
207202ac6454SAndrew Thompson 	case USB_GET_ALTINTERFACE:
2073a593f6b8SAndrew Thompson 		iface = usbd_get_iface(f->udev,
207402ac6454SAndrew Thompson 		    u.ai->uai_interface_index);
207502ac6454SAndrew Thompson 		if (iface && iface->idesc) {
207602ac6454SAndrew Thompson 			u.ai->uai_alt_index = iface->alt_index;
207702ac6454SAndrew Thompson 		} else {
207802ac6454SAndrew Thompson 			error = EINVAL;
207902ac6454SAndrew Thompson 		}
208002ac6454SAndrew Thompson 		break;
208102ac6454SAndrew Thompson 
208202ac6454SAndrew Thompson 	case USB_SET_ALTINTERFACE:
208302ac6454SAndrew Thompson 		if (!(fflags & FWRITE)) {
208402ac6454SAndrew Thompson 			error = EPERM;
208502ac6454SAndrew Thompson 			break;
208602ac6454SAndrew Thompson 		}
208702ac6454SAndrew Thompson 		error = ugen_set_interface(f,
208802ac6454SAndrew Thompson 		    u.ai->uai_interface_index, u.ai->uai_alt_index);
208902ac6454SAndrew Thompson 		break;
209002ac6454SAndrew Thompson 
209102ac6454SAndrew Thompson 	case USB_GET_DEVICE_DESC:
2092a593f6b8SAndrew Thompson 		dtemp = usbd_get_device_descriptor(f->udev);
209302ac6454SAndrew Thompson 		if (!dtemp) {
209402ac6454SAndrew Thompson 			error = EIO;
209502ac6454SAndrew Thompson 			break;
209602ac6454SAndrew Thompson 		}
209702ac6454SAndrew Thompson 		*u.ddesc = *dtemp;
209802ac6454SAndrew Thompson 		break;
209902ac6454SAndrew Thompson 
210002ac6454SAndrew Thompson 	case USB_GET_CONFIG_DESC:
2101a593f6b8SAndrew Thompson 		ctemp = usbd_get_config_descriptor(f->udev);
210202ac6454SAndrew Thompson 		if (!ctemp) {
210302ac6454SAndrew Thompson 			error = EIO;
210402ac6454SAndrew Thompson 			break;
210502ac6454SAndrew Thompson 		}
210602ac6454SAndrew Thompson 		*u.cdesc = *ctemp;
210702ac6454SAndrew Thompson 		break;
210802ac6454SAndrew Thompson 
210902ac6454SAndrew Thompson 	case USB_GET_FULL_DESC:
211002ac6454SAndrew Thompson 		error = ugen_get_cdesc(f, addr);
211102ac6454SAndrew Thompson 		break;
211202ac6454SAndrew Thompson 
211302ac6454SAndrew Thompson 	case USB_GET_STRING_DESC:
211402ac6454SAndrew Thompson 		error = ugen_get_sdesc(f, addr);
211502ac6454SAndrew Thompson 		break;
211602ac6454SAndrew Thompson 
211702ac6454SAndrew Thompson 	case USB_GET_IFACE_DRIVER:
211802ac6454SAndrew Thompson 		error = ugen_get_iface_driver(f, addr);
211902ac6454SAndrew Thompson 		break;
212002ac6454SAndrew Thompson 
212102ac6454SAndrew Thompson 	case USB_REQUEST:
212202ac6454SAndrew Thompson 	case USB_DO_REQUEST:
212302ac6454SAndrew Thompson 		if (!(fflags & FWRITE)) {
212402ac6454SAndrew Thompson 			error = EPERM;
212502ac6454SAndrew Thompson 			break;
212602ac6454SAndrew Thompson 		}
212702ac6454SAndrew Thompson 		error = ugen_do_request(f, addr);
212802ac6454SAndrew Thompson 		break;
212902ac6454SAndrew Thompson 
213002ac6454SAndrew Thompson 	case USB_DEVICEINFO:
213102ac6454SAndrew Thompson 	case USB_GET_DEVICEINFO:
2132a593f6b8SAndrew Thompson 		error = usb_gen_fill_deviceinfo(f, addr);
213302ac6454SAndrew Thompson 		break;
213402ac6454SAndrew Thompson 
213502ac6454SAndrew Thompson 	case USB_DEVICESTATS:
213602ac6454SAndrew Thompson 		for (n = 0; n != 4; n++) {
213702ac6454SAndrew Thompson 
213802ac6454SAndrew Thompson 			u.stat->uds_requests_fail[n] =
213902ac6454SAndrew Thompson 			    f->udev->bus->stats_err.uds_requests[n];
214002ac6454SAndrew Thompson 
214102ac6454SAndrew Thompson 			u.stat->uds_requests_ok[n] =
214202ac6454SAndrew Thompson 			    f->udev->bus->stats_ok.uds_requests[n];
214302ac6454SAndrew Thompson 		}
214402ac6454SAndrew Thompson 		break;
214502ac6454SAndrew Thompson 
214602ac6454SAndrew Thompson 	case USB_DEVICEENUMERATE:
214702ac6454SAndrew Thompson 		error = ugen_re_enumerate(f);
214802ac6454SAndrew Thompson 		break;
214902ac6454SAndrew Thompson 
215002ac6454SAndrew Thompson 	case USB_GET_PLUGTIME:
215102ac6454SAndrew Thompson 		*u.ptime = f->udev->plugtime;
215202ac6454SAndrew Thompson 		break;
215302ac6454SAndrew Thompson 
215402ac6454SAndrew Thompson 	case USB_CLAIM_INTERFACE:
215502ac6454SAndrew Thompson 	case USB_RELEASE_INTERFACE:
215602ac6454SAndrew Thompson 		/* TODO */
215702ac6454SAndrew Thompson 		break;
215802ac6454SAndrew Thompson 
215902ac6454SAndrew Thompson 	case USB_IFACE_DRIVER_ACTIVE:
2160a7aca4cdSAndrew Thompson 
2161a7aca4cdSAndrew Thompson 		n = *u.pint & 0xFF;
2162a7aca4cdSAndrew Thompson 
2163a7aca4cdSAndrew Thompson 		iface = usbd_get_iface(f->udev, n);
2164a7aca4cdSAndrew Thompson 
2165a7aca4cdSAndrew Thompson 		if (iface && iface->subdev)
2166a7aca4cdSAndrew Thompson 			error = 0;
2167a7aca4cdSAndrew Thompson 		else
2168a7aca4cdSAndrew Thompson 			error = ENXIO;
216902ac6454SAndrew Thompson 		break;
217002ac6454SAndrew Thompson 
217102ac6454SAndrew Thompson 	case USB_IFACE_DRIVER_DETACH:
2172a7aca4cdSAndrew Thompson 
217302ac6454SAndrew Thompson 		error = priv_check(curthread, PRIV_DRIVER);
2174a7aca4cdSAndrew Thompson 
2175a7aca4cdSAndrew Thompson 		if (error)
2176a7aca4cdSAndrew Thompson 			break;
2177a7aca4cdSAndrew Thompson 
2178a7aca4cdSAndrew Thompson 		n = *u.pint & 0xFF;
2179a7aca4cdSAndrew Thompson 
2180a7aca4cdSAndrew Thompson 		if (n == USB_IFACE_INDEX_ANY) {
2181a7aca4cdSAndrew Thompson 			error = EINVAL;
218202ac6454SAndrew Thompson 			break;
218302ac6454SAndrew Thompson 		}
2184a7aca4cdSAndrew Thompson 
2185d9073c1eSHans Petter Selasky 		/*
2186d9073c1eSHans Petter Selasky 		 * Detach the currently attached driver.
2187d9073c1eSHans Petter Selasky 		 */
2188a7aca4cdSAndrew Thompson 		usb_detach_device(f->udev, n, 0);
2189d9073c1eSHans Petter Selasky 
2190d9073c1eSHans Petter Selasky 		/*
2191d9073c1eSHans Petter Selasky 		 * Set parent to self, this should keep attach away
2192d9073c1eSHans Petter Selasky 		 * until the next set configuration event.
2193d9073c1eSHans Petter Selasky 		 */
2194d9073c1eSHans Petter Selasky 		usbd_set_parent_iface(f->udev, n, n);
219502ac6454SAndrew Thompson 		break;
219602ac6454SAndrew Thompson 
219702ac6454SAndrew Thompson 	case USB_SET_POWER_MODE:
219802ac6454SAndrew Thompson 		error = ugen_set_power_mode(f, *u.pint);
219902ac6454SAndrew Thompson 		break;
220002ac6454SAndrew Thompson 
220102ac6454SAndrew Thompson 	case USB_GET_POWER_MODE:
220202ac6454SAndrew Thompson 		*u.pint = ugen_get_power_mode(f);
220302ac6454SAndrew Thompson 		break;
220402ac6454SAndrew Thompson 
220502ac6454SAndrew Thompson 	case USB_SET_PORT_ENABLE:
220602ac6454SAndrew Thompson 		error = ugen_do_port_feature(f,
220702ac6454SAndrew Thompson 		    *u.pint, 1, UHF_PORT_ENABLE);
220802ac6454SAndrew Thompson 		break;
220902ac6454SAndrew Thompson 
221002ac6454SAndrew Thompson 	case USB_SET_PORT_DISABLE:
221102ac6454SAndrew Thompson 		error = ugen_do_port_feature(f,
221202ac6454SAndrew Thompson 		    *u.pint, 0, UHF_PORT_ENABLE);
221302ac6454SAndrew Thompson 		break;
221402ac6454SAndrew Thompson 
221502ac6454SAndrew Thompson 	case USB_FS_INIT:
221602ac6454SAndrew Thompson 		/* verify input parameters */
221702ac6454SAndrew Thompson 		if (u.pinit->pEndpoints == NULL) {
221802ac6454SAndrew Thompson 			error = EINVAL;
221902ac6454SAndrew Thompson 			break;
222002ac6454SAndrew Thompson 		}
222102ac6454SAndrew Thompson 		if (u.pinit->ep_index_max > 127) {
222202ac6454SAndrew Thompson 			error = EINVAL;
222302ac6454SAndrew Thompson 			break;
222402ac6454SAndrew Thompson 		}
222502ac6454SAndrew Thompson 		if (u.pinit->ep_index_max == 0) {
222602ac6454SAndrew Thompson 			error = EINVAL;
222702ac6454SAndrew Thompson 			break;
222802ac6454SAndrew Thompson 		}
222902ac6454SAndrew Thompson 		if (f->fs_xfer != NULL) {
223002ac6454SAndrew Thompson 			error = EBUSY;
223102ac6454SAndrew Thompson 			break;
223202ac6454SAndrew Thompson 		}
223302ac6454SAndrew Thompson 		if (f->dev_ep_index != 0) {
223402ac6454SAndrew Thompson 			error = EINVAL;
223502ac6454SAndrew Thompson 			break;
223602ac6454SAndrew Thompson 		}
223702ac6454SAndrew Thompson 		if (ugen_fifo_in_use(f, fflags)) {
223802ac6454SAndrew Thompson 			error = EBUSY;
223902ac6454SAndrew Thompson 			break;
224002ac6454SAndrew Thompson 		}
2241a593f6b8SAndrew Thompson 		error = usb_fifo_alloc_buffer(f, 1, u.pinit->ep_index_max);
224202ac6454SAndrew Thompson 		if (error) {
224302ac6454SAndrew Thompson 			break;
224402ac6454SAndrew Thompson 		}
224502ac6454SAndrew Thompson 		f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) *
224602ac6454SAndrew Thompson 		    u.pinit->ep_index_max, M_USB, M_WAITOK | M_ZERO);
224702ac6454SAndrew Thompson 		if (f->fs_xfer == NULL) {
2248a593f6b8SAndrew Thompson 			usb_fifo_free_buffer(f);
224902ac6454SAndrew Thompson 			error = ENOMEM;
225002ac6454SAndrew Thompson 			break;
225102ac6454SAndrew Thompson 		}
225202ac6454SAndrew Thompson 		f->fs_ep_max = u.pinit->ep_index_max;
225302ac6454SAndrew Thompson 		f->fs_ep_ptr = u.pinit->pEndpoints;
225402ac6454SAndrew Thompson 		break;
225502ac6454SAndrew Thompson 
225602ac6454SAndrew Thompson 	case USB_FS_UNINIT:
225702ac6454SAndrew Thompson 		if (u.puninit->dummy != 0) {
225802ac6454SAndrew Thompson 			error = EINVAL;
225902ac6454SAndrew Thompson 			break;
226002ac6454SAndrew Thompson 		}
226102ac6454SAndrew Thompson 		error = ugen_fs_uninit(f);
226202ac6454SAndrew Thompson 		break;
226302ac6454SAndrew Thompson 
226402ac6454SAndrew Thompson 	default:
226502ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
226602ac6454SAndrew Thompson 		error = ugen_iface_ioctl(f, cmd, addr, fflags);
226702ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
226802ac6454SAndrew Thompson 		break;
226902ac6454SAndrew Thompson 	}
227002ac6454SAndrew Thompson 	DPRINTFN(6, "error=%d\n", error);
227102ac6454SAndrew Thompson 	return (error);
227202ac6454SAndrew Thompson }
227302ac6454SAndrew Thompson 
227402ac6454SAndrew Thompson static void
22755b3bb704SAndrew Thompson ugen_ctrl_fs_callback(struct usb_xfer *xfer, usb_error_t error)
227602ac6454SAndrew Thompson {
227702ac6454SAndrew Thompson 	;				/* workaround for a bug in "indent" */
227802ac6454SAndrew Thompson 
227902ac6454SAndrew Thompson 	DPRINTF("st=%u alen=%u aframes=%u\n",
228002ac6454SAndrew Thompson 	    USB_GET_STATE(xfer), xfer->actlen, xfer->aframes);
228102ac6454SAndrew Thompson 
228202ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
228302ac6454SAndrew Thompson 	case USB_ST_SETUP:
2284a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
228502ac6454SAndrew Thompson 		break;
228602ac6454SAndrew Thompson 	default:
228702ac6454SAndrew Thompson 		ugen_fs_set_complete(xfer->priv_sc, USB_P2U(xfer->priv_fifo));
228802ac6454SAndrew Thompson 		break;
228902ac6454SAndrew Thompson 	}
229002ac6454SAndrew Thompson }
22918755859aSAndrew Thompson #endif	/* USB_HAVE_UGEN */
2292