xref: /freebsd/sys/dev/usb/usb_dev.c (revision f5f145ba0737eacf37f8723bc3565a5ae16ae9d2)
102ac6454SAndrew Thompson /* $FreeBSD$ */
202ac6454SAndrew Thompson /*-
302ac6454SAndrew Thompson  * Copyright (c) 2006-2008 Hans Petter Selasky. All rights reserved.
402ac6454SAndrew Thompson  *
502ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
602ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
702ac6454SAndrew Thompson  * are met:
802ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
902ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1002ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1102ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1202ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1302ac6454SAndrew Thompson  *
1402ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1502ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1602ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1702ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1802ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1902ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2002ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2102ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2202ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2302ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2402ac6454SAndrew Thompson  * SUCH DAMAGE.
2502ac6454SAndrew Thompson  *
2602ac6454SAndrew Thompson  *
2702ac6454SAndrew Thompson  * usb2_dev.c - An abstraction layer for creating devices under /dev/...
2802ac6454SAndrew Thompson  */
2902ac6454SAndrew Thompson 
3002ac6454SAndrew Thompson #include <dev/usb/usb.h>
3102ac6454SAndrew Thompson #include <dev/usb/usb_ioctl.h>
3202ac6454SAndrew Thompson #include <dev/usb/usb_defs.h>
3302ac6454SAndrew Thompson #include <dev/usb/usb_mfunc.h>
3402ac6454SAndrew Thompson #include <dev/usb/usb_error.h>
3502ac6454SAndrew Thompson 
3602ac6454SAndrew Thompson #define	USB_DEBUG_VAR usb2_fifo_debug
3702ac6454SAndrew Thompson 
3802ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
3902ac6454SAndrew Thompson #include <dev/usb/usb_mbuf.h>
4002ac6454SAndrew Thompson #include <dev/usb/usb_dev.h>
4102ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
4202ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
4302ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
4402ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
4502ac6454SAndrew Thompson #include <dev/usb/usb_generic.h>
4602ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h>
4702ac6454SAndrew Thompson #include <dev/usb/usb_util.h>
4802ac6454SAndrew Thompson 
4902ac6454SAndrew Thompson #include <dev/usb/usb_controller.h>
5002ac6454SAndrew Thompson #include <dev/usb/usb_bus.h>
5102ac6454SAndrew Thompson 
5202ac6454SAndrew Thompson #include <sys/filio.h>
5302ac6454SAndrew Thompson #include <sys/ttycom.h>
5402ac6454SAndrew Thompson #include <sys/syscallsubr.h>
5502ac6454SAndrew Thompson 
5602ac6454SAndrew Thompson #include <machine/stdarg.h>
5702ac6454SAndrew Thompson 
5802ac6454SAndrew Thompson #if USB_DEBUG
5902ac6454SAndrew Thompson static int usb2_fifo_debug = 0;
6002ac6454SAndrew Thompson 
6102ac6454SAndrew Thompson SYSCTL_NODE(_hw_usb2, OID_AUTO, dev, CTLFLAG_RW, 0, "USB device");
6202ac6454SAndrew Thompson SYSCTL_INT(_hw_usb2_dev, OID_AUTO, debug, CTLFLAG_RW,
6302ac6454SAndrew Thompson     &usb2_fifo_debug, 0, "Debug Level");
6402ac6454SAndrew Thompson #endif
6502ac6454SAndrew Thompson 
6602ac6454SAndrew Thompson #if ((__FreeBSD_version >= 700001) || (__FreeBSD_version == 0) || \
6702ac6454SAndrew Thompson      ((__FreeBSD_version >= 600034) && (__FreeBSD_version < 700000)))
6802ac6454SAndrew Thompson #define	USB_UCRED struct ucred *ucred,
6902ac6454SAndrew Thompson #else
7002ac6454SAndrew Thompson #define	USB_UCRED
7102ac6454SAndrew Thompson #endif
7202ac6454SAndrew Thompson 
7302ac6454SAndrew Thompson /* prototypes */
7402ac6454SAndrew Thompson 
75ee3e3ff5SAndrew Thompson static int	usb2_fifo_open(struct usb2_fifo *, int);
76ee3e3ff5SAndrew Thompson static void	usb2_fifo_close(struct usb2_fifo *, int);
7702ac6454SAndrew Thompson static void	usb2_dev_init(void *);
7802ac6454SAndrew Thompson static void	usb2_dev_init_post(void *);
7902ac6454SAndrew Thompson static void	usb2_dev_uninit(void *);
8002ac6454SAndrew Thompson static int	usb2_fifo_uiomove(struct usb2_fifo *, void *, int,
8102ac6454SAndrew Thompson 		    struct uio *);
8202ac6454SAndrew Thompson static void	usb2_fifo_check_methods(struct usb2_fifo_methods *);
8302ac6454SAndrew Thompson static struct	usb2_fifo *usb2_fifo_alloc(void);
8402ac6454SAndrew Thompson static struct	usb2_pipe *usb2_dev_get_pipe(struct usb2_device *, uint8_t,
85f5f145baSAndrew Thompson 		    uint8_t);
86ee3e3ff5SAndrew Thompson static void	usb2_loc_fill(struct usb2_fs_privdata *,
87ee3e3ff5SAndrew Thompson 		    struct usb2_cdev_privdata *);
88ee3e3ff5SAndrew Thompson static void	usb2_close(void *);
89ee3e3ff5SAndrew Thompson static usb2_error_t usb2_ref_device(struct usb2_cdev_privdata *, int);
90ee3e3ff5SAndrew Thompson static usb2_error_t usb2_uref_location(struct usb2_cdev_privdata *);
91ee3e3ff5SAndrew Thompson static void	usb2_unref_device(struct usb2_cdev_privdata *);
9202ac6454SAndrew Thompson 
93ee3e3ff5SAndrew Thompson static d_open_t usb2_open;
9402ac6454SAndrew Thompson static d_ioctl_t usb2_ioctl;
95ee3e3ff5SAndrew Thompson static d_read_t usb2_read;
96ee3e3ff5SAndrew Thompson static d_write_t usb2_write;
97ee3e3ff5SAndrew Thompson static d_poll_t usb2_poll;
9802ac6454SAndrew Thompson 
99ee3e3ff5SAndrew Thompson static d_ioctl_t usb2_static_ioctl;
10002ac6454SAndrew Thompson 
10102ac6454SAndrew Thompson static usb2_fifo_open_t usb2_fifo_dummy_open;
10202ac6454SAndrew Thompson static usb2_fifo_close_t usb2_fifo_dummy_close;
10302ac6454SAndrew Thompson static usb2_fifo_ioctl_t usb2_fifo_dummy_ioctl;
10402ac6454SAndrew Thompson static usb2_fifo_cmd_t usb2_fifo_dummy_cmd;
10502ac6454SAndrew Thompson 
106ee3e3ff5SAndrew Thompson /* character device structure used for devices (/dev/ugenX.Y and /dev/uXXX) */
107ee3e3ff5SAndrew Thompson struct cdevsw usb2_devsw = {
10802ac6454SAndrew Thompson 	.d_version = D_VERSION,
109ee3e3ff5SAndrew Thompson 	.d_open = usb2_open,
11002ac6454SAndrew Thompson 	.d_ioctl = usb2_ioctl,
111ee3e3ff5SAndrew Thompson 	.d_name = "usbdev",
11202ac6454SAndrew Thompson 	.d_flags = D_TRACKCLOSE,
113ee3e3ff5SAndrew Thompson 	.d_read = usb2_read,
114ee3e3ff5SAndrew Thompson 	.d_write = usb2_write,
115ee3e3ff5SAndrew Thompson 	.d_poll = usb2_poll
11602ac6454SAndrew Thompson };
11702ac6454SAndrew Thompson 
118ee3e3ff5SAndrew Thompson static struct cdev* usb2_dev = NULL;
119ee3e3ff5SAndrew Thompson 
120ee3e3ff5SAndrew Thompson /* character device structure used for /dev/usb */
121ee3e3ff5SAndrew Thompson struct cdevsw usb2_static_devsw = {
122ee3e3ff5SAndrew Thompson 	.d_version = D_VERSION,
123ee3e3ff5SAndrew Thompson 	.d_ioctl = usb2_static_ioctl,
124ee3e3ff5SAndrew Thompson 	.d_name = "usb"
12502ac6454SAndrew Thompson };
12602ac6454SAndrew Thompson 
12702ac6454SAndrew Thompson static TAILQ_HEAD(, usb2_symlink) usb2_sym_head;
12802ac6454SAndrew Thompson static struct sx usb2_sym_lock;
12902ac6454SAndrew Thompson 
13002ac6454SAndrew Thompson struct mtx usb2_ref_lock;
13102ac6454SAndrew Thompson 
13202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
133ee3e3ff5SAndrew Thompson  *	usb2_loc_fill
13402ac6454SAndrew Thompson  *
135ee3e3ff5SAndrew Thompson  * This is used to fill out a usb2_cdev_privdata structure based on the
136ee3e3ff5SAndrew Thompson  * device's address as contained in usb2_fs_privdata.
13702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
138ee3e3ff5SAndrew Thompson static void
139ee3e3ff5SAndrew Thompson usb2_loc_fill(struct usb2_fs_privdata* pd, struct usb2_cdev_privdata *cpd)
14002ac6454SAndrew Thompson {
141ee3e3ff5SAndrew Thompson 	cpd->bus_index = pd->bus_index;
142ee3e3ff5SAndrew Thompson 	cpd->dev_index = pd->dev_index;
143ee3e3ff5SAndrew Thompson 	cpd->ep_addr = pd->ep_addr;
144ee3e3ff5SAndrew Thompson 	cpd->fifo_index = pd->fifo_index;
14502ac6454SAndrew Thompson }
14602ac6454SAndrew Thompson 
14702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
14802ac6454SAndrew Thompson  *	usb2_ref_device
14902ac6454SAndrew Thompson  *
15002ac6454SAndrew Thompson  * This function is used to atomically refer an USB device by its
15102ac6454SAndrew Thompson  * device location. If this function returns success the USB device
15202ac6454SAndrew Thompson  * will not dissappear until the USB device is unreferenced.
15302ac6454SAndrew Thompson  *
15402ac6454SAndrew Thompson  * Return values:
15502ac6454SAndrew Thompson  *  0: Success, refcount incremented on the given USB device.
15602ac6454SAndrew Thompson  *  Else: Failure.
15702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
15802ac6454SAndrew Thompson usb2_error_t
159ee3e3ff5SAndrew Thompson usb2_ref_device(struct usb2_cdev_privdata* cpd, int need_uref)
16002ac6454SAndrew Thompson {
16102ac6454SAndrew Thompson 	struct usb2_fifo **ppf;
16202ac6454SAndrew Thompson 	struct usb2_fifo *f;
163ee3e3ff5SAndrew Thompson 	int dev_ep_index;
16402ac6454SAndrew Thompson 
165ee3e3ff5SAndrew Thompson 	DPRINTFN(2, "usb2_ref_device, cpd=%p need uref=%d\n", cpd, need_uref);
16602ac6454SAndrew Thompson 
16702ac6454SAndrew Thompson 	mtx_lock(&usb2_ref_lock);
168ee3e3ff5SAndrew Thompson 	cpd->bus = devclass_get_softc(usb2_devclass_ptr, cpd->bus_index);
169ee3e3ff5SAndrew Thompson 	if (cpd->bus == NULL) {
170ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "no bus at %u\n", cpd->bus_index);
17102ac6454SAndrew Thompson 		goto error;
17202ac6454SAndrew Thompson 	}
173ee3e3ff5SAndrew Thompson 	cpd->udev = cpd->bus->devices[cpd->dev_index];
174ee3e3ff5SAndrew Thompson 	if (cpd->udev == NULL) {
175ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "no device at %u\n", cpd->dev_index);
17602ac6454SAndrew Thompson 		goto error;
17702ac6454SAndrew Thompson 	}
178ee3e3ff5SAndrew Thompson 	if (cpd->udev->refcount == USB_DEV_REF_MAX) {
17902ac6454SAndrew Thompson 		DPRINTFN(2, "no dev ref\n");
18002ac6454SAndrew Thompson 		goto error;
18102ac6454SAndrew Thompson 	}
18202ac6454SAndrew Thompson 	/* check if we are doing an open */
183ee3e3ff5SAndrew Thompson 	dev_ep_index = cpd->ep_addr;
184ee3e3ff5SAndrew Thompson 	if (cpd->fflags == 0) {
18502ac6454SAndrew Thompson 		/* set defaults */
186ee3e3ff5SAndrew Thompson 		cpd->txfifo = NULL;
187ee3e3ff5SAndrew Thompson 		cpd->rxfifo = NULL;
188ee3e3ff5SAndrew Thompson 		cpd->is_write = 0;
189ee3e3ff5SAndrew Thompson 		cpd->is_read = 0;
190ee3e3ff5SAndrew Thompson 		cpd->is_usbfs = 0;
19102ac6454SAndrew Thompson 	} else {
19202ac6454SAndrew Thompson 		/* initialise "is_usbfs" flag */
193ee3e3ff5SAndrew Thompson 		cpd->is_usbfs = 0;
19402ac6454SAndrew Thompson 
19502ac6454SAndrew Thompson 		/* check for write */
196ee3e3ff5SAndrew Thompson 		if (cpd->fflags & FWRITE) {
197ee3e3ff5SAndrew Thompson 			ppf = cpd->udev->fifo;
198ee3e3ff5SAndrew Thompson 			f = ppf[cpd->fifo_index + USB_FIFO_TX];
199ee3e3ff5SAndrew Thompson 			cpd->txfifo = f;
200ee3e3ff5SAndrew Thompson 			cpd->is_write = 1;	/* ref */
201ee3e3ff5SAndrew Thompson 			if (f == NULL || f->refcount == USB_FIFO_REF_MAX)
20202ac6454SAndrew Thompson 				goto error;
20302ac6454SAndrew Thompson 			/* check if USB-FS is active */
20402ac6454SAndrew Thompson 			if (f->fs_ep_max != 0) {
205ee3e3ff5SAndrew Thompson 				cpd->is_usbfs = 1;
20602ac6454SAndrew Thompson 			}
20702ac6454SAndrew Thompson 			/*
20802ac6454SAndrew Thompson 			 * Get real endpoint index associated with
20902ac6454SAndrew Thompson 			 * this FIFO:
21002ac6454SAndrew Thompson 			 */
21102ac6454SAndrew Thompson 			dev_ep_index = f->dev_ep_index;
21202ac6454SAndrew Thompson 		} else {
213ee3e3ff5SAndrew Thompson 			cpd->txfifo = NULL;
214ee3e3ff5SAndrew Thompson 			cpd->is_write = 0;	/* no ref */
21502ac6454SAndrew Thompson 		}
21602ac6454SAndrew Thompson 
21702ac6454SAndrew Thompson 		/* check for read */
218ee3e3ff5SAndrew Thompson 		if (cpd->fflags & FREAD) {
219ee3e3ff5SAndrew Thompson 			ppf = cpd->udev->fifo;
220ee3e3ff5SAndrew Thompson 			f = ppf[cpd->fifo_index + USB_FIFO_RX];
221ee3e3ff5SAndrew Thompson 			cpd->rxfifo = f;
222ee3e3ff5SAndrew Thompson 			cpd->is_read = 1;	/* ref */
223ee3e3ff5SAndrew Thompson 			if (f == NULL || f->refcount == USB_FIFO_REF_MAX)
22402ac6454SAndrew Thompson 				goto error;
22502ac6454SAndrew Thompson 			/* check if USB-FS is active */
22602ac6454SAndrew Thompson 			if (f->fs_ep_max != 0) {
227ee3e3ff5SAndrew Thompson 				cpd->is_usbfs = 1;
22802ac6454SAndrew Thompson 			}
22902ac6454SAndrew Thompson 			/*
23002ac6454SAndrew Thompson 			 * Get real endpoint index associated with
23102ac6454SAndrew Thompson 			 * this FIFO:
23202ac6454SAndrew Thompson 			 */
23302ac6454SAndrew Thompson 			dev_ep_index = f->dev_ep_index;
23402ac6454SAndrew Thompson 		} else {
235ee3e3ff5SAndrew Thompson 			cpd->rxfifo = NULL;
236ee3e3ff5SAndrew Thompson 			cpd->is_read = 0;	/* no ref */
23702ac6454SAndrew Thompson 		}
23802ac6454SAndrew Thompson 	}
23902ac6454SAndrew Thompson 
24002ac6454SAndrew Thompson 	/* when everything is OK we increment the refcounts */
241ee3e3ff5SAndrew Thompson 	if (cpd->is_write) {
24202ac6454SAndrew Thompson 		DPRINTFN(2, "ref write\n");
243ee3e3ff5SAndrew Thompson 		cpd->txfifo->refcount++;
24402ac6454SAndrew Thompson 	}
245ee3e3ff5SAndrew Thompson 	if (cpd->is_read) {
24602ac6454SAndrew Thompson 		DPRINTFN(2, "ref read\n");
247ee3e3ff5SAndrew Thompson 		cpd->rxfifo->refcount++;
24802ac6454SAndrew Thompson 	}
249ee3e3ff5SAndrew Thompson 	if (need_uref) {
25002ac6454SAndrew Thompson 		DPRINTFN(2, "ref udev - needed\n");
251ee3e3ff5SAndrew Thompson 		cpd->udev->refcount++;
252ee3e3ff5SAndrew Thompson 		cpd->is_uref = 1;
25302ac6454SAndrew Thompson 	}
25402ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
25502ac6454SAndrew Thompson 
256ee3e3ff5SAndrew Thompson 	if (cpd->is_uref) {
25702ac6454SAndrew Thompson 		/*
25802ac6454SAndrew Thompson 		 * We are about to alter the bus-state. Apply the
25902ac6454SAndrew Thompson 		 * required locks.
26002ac6454SAndrew Thompson 		 */
261ee3e3ff5SAndrew Thompson 		sx_xlock(cpd->udev->default_sx + 1);
26202ac6454SAndrew Thompson 		mtx_lock(&Giant);	/* XXX */
26302ac6454SAndrew Thompson 	}
26402ac6454SAndrew Thompson 	return (0);
26502ac6454SAndrew Thompson 
26602ac6454SAndrew Thompson error:
26702ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
26802ac6454SAndrew Thompson 	DPRINTFN(2, "fail\n");
26902ac6454SAndrew Thompson 	return (USB_ERR_INVAL);
27002ac6454SAndrew Thompson }
27102ac6454SAndrew Thompson 
27202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
27302ac6454SAndrew Thompson  *	usb2_uref_location
27402ac6454SAndrew Thompson  *
27502ac6454SAndrew Thompson  * This function is used to upgrade an USB reference to include the
27602ac6454SAndrew Thompson  * USB device reference on a USB location.
27702ac6454SAndrew Thompson  *
27802ac6454SAndrew Thompson  * Return values:
27902ac6454SAndrew Thompson  *  0: Success, refcount incremented on the given USB device.
28002ac6454SAndrew Thompson  *  Else: Failure.
28102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
28202ac6454SAndrew Thompson static usb2_error_t
283ee3e3ff5SAndrew Thompson usb2_uref_location(struct usb2_cdev_privdata *cpd)
28402ac6454SAndrew Thompson {
28502ac6454SAndrew Thompson 	/*
28602ac6454SAndrew Thompson 	 * Check if we already got an USB reference on this location:
28702ac6454SAndrew Thompson 	 */
288ee3e3ff5SAndrew Thompson 	if (cpd->is_uref) {
28902ac6454SAndrew Thompson 		return (0);		/* success */
29002ac6454SAndrew Thompson 	}
29102ac6454SAndrew Thompson 	mtx_lock(&usb2_ref_lock);
292ee3e3ff5SAndrew Thompson 	if (cpd->bus != devclass_get_softc(usb2_devclass_ptr, cpd->bus_index)) {
293ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "bus changed at %u\n", cpd->bus_index);
29402ac6454SAndrew Thompson 		goto error;
29502ac6454SAndrew Thompson 	}
296ee3e3ff5SAndrew Thompson 	if (cpd->udev != cpd->bus->devices[cpd->dev_index]) {
297ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "device changed at %u\n", cpd->dev_index);
29802ac6454SAndrew Thompson 		goto error;
29902ac6454SAndrew Thompson 	}
300ee3e3ff5SAndrew Thompson 	if (cpd->udev->refcount == USB_DEV_REF_MAX) {
30102ac6454SAndrew Thompson 		DPRINTFN(2, "no dev ref\n");
30202ac6454SAndrew Thompson 		goto error;
30302ac6454SAndrew Thompson 	}
30402ac6454SAndrew Thompson 	DPRINTFN(2, "ref udev\n");
305ee3e3ff5SAndrew Thompson 	cpd->udev->refcount++;
30602ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
30702ac6454SAndrew Thompson 
30802ac6454SAndrew Thompson 	/* set "uref" */
309ee3e3ff5SAndrew Thompson 	cpd->is_uref = 1;
31002ac6454SAndrew Thompson 
31102ac6454SAndrew Thompson 	/*
31202ac6454SAndrew Thompson 	 * We are about to alter the bus-state. Apply the
31302ac6454SAndrew Thompson 	 * required locks.
31402ac6454SAndrew Thompson 	 */
315ee3e3ff5SAndrew Thompson 	sx_xlock(cpd->udev->default_sx + 1);
31602ac6454SAndrew Thompson 	mtx_lock(&Giant);		/* XXX */
31702ac6454SAndrew Thompson 	return (0);
31802ac6454SAndrew Thompson 
31902ac6454SAndrew Thompson error:
32002ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
32102ac6454SAndrew Thompson 	DPRINTFN(2, "fail\n");
32202ac6454SAndrew Thompson 	return (USB_ERR_INVAL);
32302ac6454SAndrew Thompson }
32402ac6454SAndrew Thompson 
32502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
32602ac6454SAndrew Thompson  *	usb2_unref_device
32702ac6454SAndrew Thompson  *
32802ac6454SAndrew Thompson  * This function will release the reference count by one unit for the
32902ac6454SAndrew Thompson  * given USB device.
33002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
33102ac6454SAndrew Thompson void
332ee3e3ff5SAndrew Thompson usb2_unref_device(struct usb2_cdev_privdata *cpd)
33302ac6454SAndrew Thompson {
334ee3e3ff5SAndrew Thompson 	if (cpd->is_uref) {
33502ac6454SAndrew Thompson 		mtx_unlock(&Giant);	/* XXX */
336ee3e3ff5SAndrew Thompson 		sx_unlock(cpd->udev->default_sx + 1);
33702ac6454SAndrew Thompson 	}
33802ac6454SAndrew Thompson 	mtx_lock(&usb2_ref_lock);
339ee3e3ff5SAndrew Thompson 	if (cpd->is_read) {
340ee3e3ff5SAndrew Thompson 		if (--(cpd->rxfifo->refcount) == 0) {
341ee3e3ff5SAndrew Thompson 			usb2_cv_signal(&cpd->rxfifo->cv_drain);
34202ac6454SAndrew Thompson 		}
343ee3e3ff5SAndrew Thompson 		cpd->is_read = 0;
34402ac6454SAndrew Thompson 	}
345ee3e3ff5SAndrew Thompson 	if (cpd->is_write) {
346ee3e3ff5SAndrew Thompson 		if (--(cpd->txfifo->refcount) == 0) {
347ee3e3ff5SAndrew Thompson 			usb2_cv_signal(&cpd->txfifo->cv_drain);
34802ac6454SAndrew Thompson 		}
349ee3e3ff5SAndrew Thompson 		cpd->is_write = 0;
35002ac6454SAndrew Thompson 	}
351ee3e3ff5SAndrew Thompson 	if (cpd->is_uref) {
352ee3e3ff5SAndrew Thompson 		if (--(cpd->udev->refcount) == 0) {
353ee3e3ff5SAndrew Thompson 			usb2_cv_signal(cpd->udev->default_cv + 1);
35402ac6454SAndrew Thompson 		}
355ee3e3ff5SAndrew Thompson 		cpd->is_uref = 0;
35602ac6454SAndrew Thompson 	}
35702ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
35802ac6454SAndrew Thompson }
35902ac6454SAndrew Thompson 
36002ac6454SAndrew Thompson static struct usb2_fifo *
36102ac6454SAndrew Thompson usb2_fifo_alloc(void)
36202ac6454SAndrew Thompson {
36302ac6454SAndrew Thompson 	struct usb2_fifo *f;
36402ac6454SAndrew Thompson 
36502ac6454SAndrew Thompson 	f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO);
36602ac6454SAndrew Thompson 	if (f) {
36702ac6454SAndrew Thompson 		usb2_cv_init(&f->cv_io, "FIFO-IO");
36802ac6454SAndrew Thompson 		usb2_cv_init(&f->cv_drain, "FIFO-DRAIN");
36902ac6454SAndrew Thompson 		f->refcount = 1;
37002ac6454SAndrew Thompson 	}
37102ac6454SAndrew Thompson 	return (f);
37202ac6454SAndrew Thompson }
37302ac6454SAndrew Thompson 
37402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
37502ac6454SAndrew Thompson  *	usb2_fifo_create
37602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
37702ac6454SAndrew Thompson static int
378ee3e3ff5SAndrew Thompson usb2_fifo_create(struct usb2_cdev_privdata *cpd)
37902ac6454SAndrew Thompson {
380ee3e3ff5SAndrew Thompson 	struct usb2_device *udev = cpd->udev;
38102ac6454SAndrew Thompson 	struct usb2_fifo *f;
38202ac6454SAndrew Thompson 	struct usb2_pipe *pipe;
38302ac6454SAndrew Thompson 	uint8_t n;
38402ac6454SAndrew Thompson 	uint8_t is_tx;
38502ac6454SAndrew Thompson 	uint8_t is_rx;
38602ac6454SAndrew Thompson 	uint8_t no_null;
38702ac6454SAndrew Thompson 	uint8_t is_busy;
388ee3e3ff5SAndrew Thompson 	int ep = cpd->ep_addr;
38902ac6454SAndrew Thompson 
390ee3e3ff5SAndrew Thompson 	is_tx = (cpd->fflags & FWRITE) ? 1 : 0;
391ee3e3ff5SAndrew Thompson 	is_rx = (cpd->fflags & FREAD) ? 1 : 0;
39202ac6454SAndrew Thompson 	no_null = 1;
39302ac6454SAndrew Thompson 	is_busy = 0;
39402ac6454SAndrew Thompson 
395ee3e3ff5SAndrew Thompson 	/* Preallocated FIFO */
396ee3e3ff5SAndrew Thompson 	if (ep < 0) {
397ee3e3ff5SAndrew Thompson 		DPRINTFN(5, "Preallocated FIFO\n");
398ee3e3ff5SAndrew Thompson 		if (is_tx) {
399ee3e3ff5SAndrew Thompson 			f = udev->fifo[cpd->fifo_index + USB_FIFO_TX];
400ee3e3ff5SAndrew Thompson 			if (f == NULL)
401ee3e3ff5SAndrew Thompson 				return (EINVAL);
402ee3e3ff5SAndrew Thompson 			cpd->txfifo = f;
403ee3e3ff5SAndrew Thompson 		}
404ee3e3ff5SAndrew Thompson 		if (is_rx) {
405ee3e3ff5SAndrew Thompson 			f = udev->fifo[cpd->fifo_index + USB_FIFO_RX];
406ee3e3ff5SAndrew Thompson 			if (f == NULL)
407ee3e3ff5SAndrew Thompson 				return (EINVAL);
408ee3e3ff5SAndrew Thompson 			cpd->rxfifo = f;
409ee3e3ff5SAndrew Thompson 		}
410ee3e3ff5SAndrew Thompson 		return (0);
411ee3e3ff5SAndrew Thompson 	}
41202ac6454SAndrew Thompson 
413ee3e3ff5SAndrew Thompson 	KASSERT(ep >= 0 && ep <= 15, ("endpoint %d out of range", ep));
414ee3e3ff5SAndrew Thompson 
415ee3e3ff5SAndrew Thompson 	/* search for a free FIFO slot */
416ee3e3ff5SAndrew Thompson 	DPRINTFN(5, "Endpoint device, searching for 0x%02x\n", ep);
41702ac6454SAndrew Thompson 	for (n = 0;; n += 2) {
41802ac6454SAndrew Thompson 
41902ac6454SAndrew Thompson 		if (n == USB_FIFO_MAX) {
42002ac6454SAndrew Thompson 			if (no_null) {
42102ac6454SAndrew Thompson 				no_null = 0;
42202ac6454SAndrew Thompson 				n = 0;
42302ac6454SAndrew Thompson 			} else {
42402ac6454SAndrew Thompson 				/* end of FIFOs reached */
425ee3e3ff5SAndrew Thompson 				DPRINTFN(5, "out of FIFOs\n");
42602ac6454SAndrew Thompson 				return (ENOMEM);
42702ac6454SAndrew Thompson 			}
42802ac6454SAndrew Thompson 		}
42902ac6454SAndrew Thompson 		/* Check for TX FIFO */
43002ac6454SAndrew Thompson 		if (is_tx) {
43102ac6454SAndrew Thompson 			f = udev->fifo[n + USB_FIFO_TX];
43202ac6454SAndrew Thompson 			if (f != NULL) {
433ee3e3ff5SAndrew Thompson 				if (f->dev_ep_index != ep) {
43402ac6454SAndrew Thompson 					/* wrong endpoint index */
43502ac6454SAndrew Thompson 					continue;
43602ac6454SAndrew Thompson 				}
437ee3e3ff5SAndrew Thompson 				if (f->opened) {
43802ac6454SAndrew Thompson 					/* FIFO is opened */
43902ac6454SAndrew Thompson 					is_busy = 1;
44002ac6454SAndrew Thompson 					continue;
44102ac6454SAndrew Thompson 				}
44202ac6454SAndrew Thompson 			} else if (no_null) {
44302ac6454SAndrew Thompson 				continue;
44402ac6454SAndrew Thompson 			}
44502ac6454SAndrew Thompson 		}
44602ac6454SAndrew Thompson 		/* Check for RX FIFO */
44702ac6454SAndrew Thompson 		if (is_rx) {
44802ac6454SAndrew Thompson 			f = udev->fifo[n + USB_FIFO_RX];
44902ac6454SAndrew Thompson 			if (f != NULL) {
450ee3e3ff5SAndrew Thompson 				if (f->dev_ep_index != ep) {
45102ac6454SAndrew Thompson 					/* wrong endpoint index */
45202ac6454SAndrew Thompson 					continue;
45302ac6454SAndrew Thompson 				}
454ee3e3ff5SAndrew Thompson 				if (f->opened) {
45502ac6454SAndrew Thompson 					/* FIFO is opened */
45602ac6454SAndrew Thompson 					is_busy = 1;
45702ac6454SAndrew Thompson 					continue;
45802ac6454SAndrew Thompson 				}
45902ac6454SAndrew Thompson 			} else if (no_null) {
46002ac6454SAndrew Thompson 				continue;
46102ac6454SAndrew Thompson 			}
46202ac6454SAndrew Thompson 		}
46302ac6454SAndrew Thompson 		break;
46402ac6454SAndrew Thompson 	}
46502ac6454SAndrew Thompson 
46602ac6454SAndrew Thompson 	if (no_null == 0) {
467ee3e3ff5SAndrew Thompson 		if (ep >= (USB_EP_MAX / 2)) {
46802ac6454SAndrew Thompson 			/* we don't create any endpoints in this range */
469ee3e3ff5SAndrew Thompson 			DPRINTFN(5, "dev_ep_index out of range\n");
47002ac6454SAndrew Thompson 			return (is_busy ? EBUSY : EINVAL);
47102ac6454SAndrew Thompson 		}
47202ac6454SAndrew Thompson 	}
47302ac6454SAndrew Thompson 	/* Check TX FIFO */
47402ac6454SAndrew Thompson 	if (is_tx &&
47502ac6454SAndrew Thompson 	    (udev->fifo[n + USB_FIFO_TX] == NULL)) {
47602ac6454SAndrew Thompson 		pipe = usb2_dev_get_pipe(udev,
477f5f145baSAndrew Thompson 		    ep, USB_FIFO_TX);
478f5f145baSAndrew Thompson 		DPRINTFN(5, "dev_get_pipe(%d, 0x%x)\n", ep, USB_FIFO_TX);
47902ac6454SAndrew Thompson 		if (pipe == NULL) {
480ee3e3ff5SAndrew Thompson 			DPRINTFN(5, "dev_get_pipe returned NULL\n");
48102ac6454SAndrew Thompson 			return (EINVAL);
48202ac6454SAndrew Thompson 		}
48302ac6454SAndrew Thompson 		f = usb2_fifo_alloc();
48402ac6454SAndrew Thompson 		if (f == NULL) {
485ee3e3ff5SAndrew Thompson 			DPRINTFN(5, "could not alloc tx fifo\n");
48602ac6454SAndrew Thompson 			return (ENOMEM);
48702ac6454SAndrew Thompson 		}
48802ac6454SAndrew Thompson 		/* update some fields */
48902ac6454SAndrew Thompson 		f->fifo_index = n + USB_FIFO_TX;
490ee3e3ff5SAndrew Thompson 		f->dev_ep_index = ep;
49102ac6454SAndrew Thompson 		f->priv_mtx = udev->default_mtx;
49202ac6454SAndrew Thompson 		f->priv_sc0 = pipe;
49302ac6454SAndrew Thompson 		f->methods = &usb2_ugen_methods;
494f5f145baSAndrew Thompson 		f->iface_index = pipe->iface_index;
49502ac6454SAndrew Thompson 		f->udev = udev;
49602ac6454SAndrew Thompson 		mtx_lock(&usb2_ref_lock);
49702ac6454SAndrew Thompson 		udev->fifo[n + USB_FIFO_TX] = f;
49802ac6454SAndrew Thompson 		mtx_unlock(&usb2_ref_lock);
49902ac6454SAndrew Thompson 	}
50002ac6454SAndrew Thompson 	/* Check RX FIFO */
50102ac6454SAndrew Thompson 	if (is_rx &&
50202ac6454SAndrew Thompson 	    (udev->fifo[n + USB_FIFO_RX] == NULL)) {
50302ac6454SAndrew Thompson 
50402ac6454SAndrew Thompson 		pipe = usb2_dev_get_pipe(udev,
505f5f145baSAndrew Thompson 		    ep, USB_FIFO_RX);
506f5f145baSAndrew Thompson 		DPRINTFN(5, "dev_get_pipe(%d, 0x%x)\n", ep, USB_FIFO_RX);
50702ac6454SAndrew Thompson 		if (pipe == NULL) {
508ee3e3ff5SAndrew Thompson 			DPRINTFN(5, "dev_get_pipe returned NULL\n");
50902ac6454SAndrew Thompson 			return (EINVAL);
51002ac6454SAndrew Thompson 		}
51102ac6454SAndrew Thompson 		f = usb2_fifo_alloc();
51202ac6454SAndrew Thompson 		if (f == NULL) {
513ee3e3ff5SAndrew Thompson 			DPRINTFN(5, "could not alloc rx fifo\n");
51402ac6454SAndrew Thompson 			return (ENOMEM);
51502ac6454SAndrew Thompson 		}
51602ac6454SAndrew Thompson 		/* update some fields */
51702ac6454SAndrew Thompson 		f->fifo_index = n + USB_FIFO_RX;
518ee3e3ff5SAndrew Thompson 		f->dev_ep_index = ep;
51902ac6454SAndrew Thompson 		f->priv_mtx = udev->default_mtx;
52002ac6454SAndrew Thompson 		f->priv_sc0 = pipe;
52102ac6454SAndrew Thompson 		f->methods = &usb2_ugen_methods;
522f5f145baSAndrew Thompson 		f->iface_index = pipe->iface_index;
52302ac6454SAndrew Thompson 		f->udev = udev;
52402ac6454SAndrew Thompson 		mtx_lock(&usb2_ref_lock);
52502ac6454SAndrew Thompson 		udev->fifo[n + USB_FIFO_RX] = f;
52602ac6454SAndrew Thompson 		mtx_unlock(&usb2_ref_lock);
52702ac6454SAndrew Thompson 	}
52802ac6454SAndrew Thompson 	if (is_tx) {
529ee3e3ff5SAndrew Thompson 		cpd->txfifo = udev->fifo[n + USB_FIFO_TX];
53002ac6454SAndrew Thompson 	}
53102ac6454SAndrew Thompson 	if (is_rx) {
532ee3e3ff5SAndrew Thompson 		cpd->rxfifo = udev->fifo[n + USB_FIFO_RX];
53302ac6454SAndrew Thompson 	}
534ee3e3ff5SAndrew Thompson 	/* fill out fifo index */
535ee3e3ff5SAndrew Thompson 	DPRINTFN(5, "fifo index = %d\n", n);
536ee3e3ff5SAndrew Thompson 	cpd->fifo_index = n;
53702ac6454SAndrew Thompson 
53802ac6454SAndrew Thompson 	/* complete */
53902ac6454SAndrew Thompson 
54002ac6454SAndrew Thompson 	return (0);
54102ac6454SAndrew Thompson }
54202ac6454SAndrew Thompson 
54302ac6454SAndrew Thompson void
54402ac6454SAndrew Thompson usb2_fifo_free(struct usb2_fifo *f)
54502ac6454SAndrew Thompson {
54602ac6454SAndrew Thompson 	uint8_t n;
54702ac6454SAndrew Thompson 
54802ac6454SAndrew Thompson 	if (f == NULL) {
54902ac6454SAndrew Thompson 		/* be NULL safe */
55002ac6454SAndrew Thompson 		return;
55102ac6454SAndrew Thompson 	}
55202ac6454SAndrew Thompson 	/* destroy symlink devices, if any */
55302ac6454SAndrew Thompson 	for (n = 0; n != 2; n++) {
55402ac6454SAndrew Thompson 		if (f->symlink[n]) {
55502ac6454SAndrew Thompson 			usb2_free_symlink(f->symlink[n]);
55602ac6454SAndrew Thompson 			f->symlink[n] = NULL;
55702ac6454SAndrew Thompson 		}
55802ac6454SAndrew Thompson 	}
55902ac6454SAndrew Thompson 	mtx_lock(&usb2_ref_lock);
56002ac6454SAndrew Thompson 
56102ac6454SAndrew Thompson 	/* delink ourselves to stop calls from userland */
56202ac6454SAndrew Thompson 	if ((f->fifo_index < USB_FIFO_MAX) &&
56302ac6454SAndrew Thompson 	    (f->udev != NULL) &&
56402ac6454SAndrew Thompson 	    (f->udev->fifo[f->fifo_index] == f)) {
56502ac6454SAndrew Thompson 		f->udev->fifo[f->fifo_index] = NULL;
56602ac6454SAndrew Thompson 	} else {
56702ac6454SAndrew Thompson 		DPRINTFN(0, "USB FIFO %p has not been linked!\n", f);
56802ac6454SAndrew Thompson 	}
56902ac6454SAndrew Thompson 
57002ac6454SAndrew Thompson 	/* decrease refcount */
57102ac6454SAndrew Thompson 	f->refcount--;
57202ac6454SAndrew Thompson 	/* prevent any write flush */
57302ac6454SAndrew Thompson 	f->flag_iserror = 1;
57402ac6454SAndrew Thompson 	/* need to wait until all callers have exited */
57502ac6454SAndrew Thompson 	while (f->refcount != 0) {
57602ac6454SAndrew Thompson 		mtx_unlock(&usb2_ref_lock);	/* avoid LOR */
57702ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
57802ac6454SAndrew Thompson 		/* get I/O thread out of any sleep state */
57902ac6454SAndrew Thompson 		if (f->flag_sleeping) {
58002ac6454SAndrew Thompson 			f->flag_sleeping = 0;
58102ac6454SAndrew Thompson 			usb2_cv_broadcast(&f->cv_io);
58202ac6454SAndrew Thompson 		}
58302ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
58402ac6454SAndrew Thompson 		mtx_lock(&usb2_ref_lock);
58502ac6454SAndrew Thompson 
58602ac6454SAndrew Thompson 		/* wait for sync */
58702ac6454SAndrew Thompson 		usb2_cv_wait(&f->cv_drain, &usb2_ref_lock);
58802ac6454SAndrew Thompson 	}
58902ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
59002ac6454SAndrew Thompson 
59102ac6454SAndrew Thompson 	/* take care of closing the device here, if any */
592ee3e3ff5SAndrew Thompson 	usb2_fifo_close(f, 0);
59302ac6454SAndrew Thompson 
59402ac6454SAndrew Thompson 	usb2_cv_destroy(&f->cv_io);
59502ac6454SAndrew Thompson 	usb2_cv_destroy(&f->cv_drain);
59602ac6454SAndrew Thompson 
59702ac6454SAndrew Thompson 	free(f, M_USBDEV);
59802ac6454SAndrew Thompson }
59902ac6454SAndrew Thompson 
60002ac6454SAndrew Thompson static struct usb2_pipe *
60102ac6454SAndrew Thompson usb2_dev_get_pipe(struct usb2_device *udev,
602f5f145baSAndrew Thompson     uint8_t ep_index, uint8_t dir)
60302ac6454SAndrew Thompson {
60402ac6454SAndrew Thompson 	struct usb2_pipe *pipe;
60502ac6454SAndrew Thompson 	uint8_t ep_dir;
60602ac6454SAndrew Thompson 
60702ac6454SAndrew Thompson 	if (ep_index == 0) {
60802ac6454SAndrew Thompson 		pipe = &udev->default_pipe;
60902ac6454SAndrew Thompson 	} else {
61002ac6454SAndrew Thompson 		if (dir == USB_FIFO_RX) {
61102ac6454SAndrew Thompson 			if (udev->flags.usb2_mode == USB_MODE_HOST) {
61202ac6454SAndrew Thompson 				ep_dir = UE_DIR_IN;
61302ac6454SAndrew Thompson 			} else {
61402ac6454SAndrew Thompson 				ep_dir = UE_DIR_OUT;
61502ac6454SAndrew Thompson 			}
61602ac6454SAndrew Thompson 		} else {
61702ac6454SAndrew Thompson 			if (udev->flags.usb2_mode == USB_MODE_HOST) {
61802ac6454SAndrew Thompson 				ep_dir = UE_DIR_OUT;
61902ac6454SAndrew Thompson 			} else {
62002ac6454SAndrew Thompson 				ep_dir = UE_DIR_IN;
62102ac6454SAndrew Thompson 			}
62202ac6454SAndrew Thompson 		}
62302ac6454SAndrew Thompson 		pipe = usb2_get_pipe_by_addr(udev, ep_index | ep_dir);
62402ac6454SAndrew Thompson 	}
62502ac6454SAndrew Thompson 
62602ac6454SAndrew Thompson 	if (pipe == NULL) {
62702ac6454SAndrew Thompson 		/* if the pipe does not exist then return */
62802ac6454SAndrew Thompson 		return (NULL);
62902ac6454SAndrew Thompson 	}
63002ac6454SAndrew Thompson 	if (pipe->edesc == NULL) {
63102ac6454SAndrew Thompson 		/* invalid pipe */
63202ac6454SAndrew Thompson 		return (NULL);
63302ac6454SAndrew Thompson 	}
63402ac6454SAndrew Thompson 	return (pipe);			/* success */
63502ac6454SAndrew Thompson }
63602ac6454SAndrew Thompson 
63702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
63802ac6454SAndrew Thompson  *	usb2_fifo_open
63902ac6454SAndrew Thompson  *
64002ac6454SAndrew Thompson  * Returns:
64102ac6454SAndrew Thompson  * 0: Success
64202ac6454SAndrew Thompson  * Else: Failure
64302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
64402ac6454SAndrew Thompson static int
645ee3e3ff5SAndrew Thompson usb2_fifo_open(struct usb2_fifo *f, int fflags)
64602ac6454SAndrew Thompson {
64702ac6454SAndrew Thompson 	int err;
64802ac6454SAndrew Thompson 
64902ac6454SAndrew Thompson 	if (f == NULL) {
65002ac6454SAndrew Thompson 		/* no FIFO there */
65102ac6454SAndrew Thompson 		DPRINTFN(2, "no FIFO\n");
65202ac6454SAndrew Thompson 		return (ENXIO);
65302ac6454SAndrew Thompson 	}
65402ac6454SAndrew Thompson 	/* remove FWRITE and FREAD flags */
65502ac6454SAndrew Thompson 	fflags &= ~(FWRITE | FREAD);
65602ac6454SAndrew Thompson 
65702ac6454SAndrew Thompson 	/* set correct file flags */
65802ac6454SAndrew Thompson 	if ((f->fifo_index & 1) == USB_FIFO_TX) {
65902ac6454SAndrew Thompson 		fflags |= FWRITE;
66002ac6454SAndrew Thompson 	} else {
66102ac6454SAndrew Thompson 		fflags |= FREAD;
66202ac6454SAndrew Thompson 	}
66302ac6454SAndrew Thompson 
66402ac6454SAndrew Thompson 	/* check if we are already opened */
66502ac6454SAndrew Thompson 	/* we don't need any locks when checking this variable */
666ee3e3ff5SAndrew Thompson 	if (f->opened) {
66702ac6454SAndrew Thompson 		err = EBUSY;
66802ac6454SAndrew Thompson 		goto done;
66902ac6454SAndrew Thompson 	}
670ee3e3ff5SAndrew Thompson 
67102ac6454SAndrew Thompson 	/* call open method */
672ee3e3ff5SAndrew Thompson 	err = (f->methods->f_open) (f, fflags);
67302ac6454SAndrew Thompson 	if (err) {
67402ac6454SAndrew Thompson 		goto done;
67502ac6454SAndrew Thompson 	}
67602ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
67702ac6454SAndrew Thompson 
67802ac6454SAndrew Thompson 	/* reset sleep flag */
67902ac6454SAndrew Thompson 	f->flag_sleeping = 0;
68002ac6454SAndrew Thompson 
68102ac6454SAndrew Thompson 	/* reset error flag */
68202ac6454SAndrew Thompson 	f->flag_iserror = 0;
68302ac6454SAndrew Thompson 
68402ac6454SAndrew Thompson 	/* reset complete flag */
68502ac6454SAndrew Thompson 	f->flag_iscomplete = 0;
68602ac6454SAndrew Thompson 
68702ac6454SAndrew Thompson 	/* reset select flag */
68802ac6454SAndrew Thompson 	f->flag_isselect = 0;
68902ac6454SAndrew Thompson 
69002ac6454SAndrew Thompson 	/* reset flushing flag */
69102ac6454SAndrew Thompson 	f->flag_flushing = 0;
69202ac6454SAndrew Thompson 
69302ac6454SAndrew Thompson 	/* reset ASYNC proc flag */
69402ac6454SAndrew Thompson 	f->async_p = NULL;
69502ac6454SAndrew Thompson 
696ee3e3ff5SAndrew Thompson 	/* flag the fifo as opened to prevent others */
69702ac6454SAndrew Thompson 	mtx_lock(&usb2_ref_lock);
698ee3e3ff5SAndrew Thompson 	f->opened = 1;
69902ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
70002ac6454SAndrew Thompson 
70102ac6454SAndrew Thompson 	/* reset queue */
70202ac6454SAndrew Thompson 	usb2_fifo_reset(f);
70302ac6454SAndrew Thompson 
70402ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
70502ac6454SAndrew Thompson done:
70602ac6454SAndrew Thompson 	return (err);
70702ac6454SAndrew Thompson }
70802ac6454SAndrew Thompson 
70902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
71002ac6454SAndrew Thompson  *	usb2_fifo_reset
71102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
71202ac6454SAndrew Thompson void
71302ac6454SAndrew Thompson usb2_fifo_reset(struct usb2_fifo *f)
71402ac6454SAndrew Thompson {
71502ac6454SAndrew Thompson 	struct usb2_mbuf *m;
71602ac6454SAndrew Thompson 
71702ac6454SAndrew Thompson 	if (f == NULL) {
71802ac6454SAndrew Thompson 		return;
71902ac6454SAndrew Thompson 	}
72002ac6454SAndrew Thompson 	while (1) {
72102ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
72202ac6454SAndrew Thompson 		if (m) {
72302ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->free_q, m);
72402ac6454SAndrew Thompson 		} else {
72502ac6454SAndrew Thompson 			break;
72602ac6454SAndrew Thompson 		}
72702ac6454SAndrew Thompson 	}
72802ac6454SAndrew Thompson }
72902ac6454SAndrew Thompson 
73002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
73102ac6454SAndrew Thompson  *	usb2_fifo_close
73202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
73302ac6454SAndrew Thompson static void
734ee3e3ff5SAndrew Thompson usb2_fifo_close(struct usb2_fifo *f, int fflags)
73502ac6454SAndrew Thompson {
73602ac6454SAndrew Thompson 	int err;
73702ac6454SAndrew Thompson 
73802ac6454SAndrew Thompson 	/* check if we are not opened */
739ee3e3ff5SAndrew Thompson 	if (!f->opened) {
74002ac6454SAndrew Thompson 		/* nothing to do - already closed */
74102ac6454SAndrew Thompson 		return;
74202ac6454SAndrew Thompson 	}
74302ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
74402ac6454SAndrew Thompson 
74502ac6454SAndrew Thompson 	/* clear current file flag */
746ee3e3ff5SAndrew Thompson 	f->opened = 0;
74702ac6454SAndrew Thompson 
74802ac6454SAndrew Thompson 	/* check if we are selected */
74902ac6454SAndrew Thompson 	if (f->flag_isselect) {
75002ac6454SAndrew Thompson 		selwakeup(&f->selinfo);
75102ac6454SAndrew Thompson 		f->flag_isselect = 0;
75202ac6454SAndrew Thompson 	}
75302ac6454SAndrew Thompson 	/* check if a thread wants SIGIO */
75402ac6454SAndrew Thompson 	if (f->async_p != NULL) {
75502ac6454SAndrew Thompson 		PROC_LOCK(f->async_p);
75602ac6454SAndrew Thompson 		psignal(f->async_p, SIGIO);
75702ac6454SAndrew Thompson 		PROC_UNLOCK(f->async_p);
75802ac6454SAndrew Thompson 		f->async_p = NULL;
75902ac6454SAndrew Thompson 	}
76002ac6454SAndrew Thompson 	/* remove FWRITE and FREAD flags */
76102ac6454SAndrew Thompson 	fflags &= ~(FWRITE | FREAD);
76202ac6454SAndrew Thompson 
76302ac6454SAndrew Thompson 	/* flush written data, if any */
76402ac6454SAndrew Thompson 	if ((f->fifo_index & 1) == USB_FIFO_TX) {
76502ac6454SAndrew Thompson 
76602ac6454SAndrew Thompson 		if (!f->flag_iserror) {
76702ac6454SAndrew Thompson 
76802ac6454SAndrew Thompson 			/* set flushing flag */
76902ac6454SAndrew Thompson 			f->flag_flushing = 1;
77002ac6454SAndrew Thompson 
77102ac6454SAndrew Thompson 			/* start write transfer, if not already started */
77202ac6454SAndrew Thompson 			(f->methods->f_start_write) (f);
77302ac6454SAndrew Thompson 
77402ac6454SAndrew Thompson 			/* check if flushed already */
77502ac6454SAndrew Thompson 			while (f->flag_flushing &&
77602ac6454SAndrew Thompson 			    (!f->flag_iserror)) {
77702ac6454SAndrew Thompson 				/* wait until all data has been written */
77802ac6454SAndrew Thompson 				f->flag_sleeping = 1;
77902ac6454SAndrew Thompson 				err = usb2_cv_wait_sig(&f->cv_io, f->priv_mtx);
78002ac6454SAndrew Thompson 				if (err) {
78102ac6454SAndrew Thompson 					DPRINTF("signal received\n");
78202ac6454SAndrew Thompson 					break;
78302ac6454SAndrew Thompson 				}
78402ac6454SAndrew Thompson 			}
78502ac6454SAndrew Thompson 		}
78602ac6454SAndrew Thompson 		fflags |= FWRITE;
78702ac6454SAndrew Thompson 
78802ac6454SAndrew Thompson 		/* stop write transfer, if not already stopped */
78902ac6454SAndrew Thompson 		(f->methods->f_stop_write) (f);
79002ac6454SAndrew Thompson 	} else {
79102ac6454SAndrew Thompson 		fflags |= FREAD;
79202ac6454SAndrew Thompson 
79302ac6454SAndrew Thompson 		/* stop write transfer, if not already stopped */
79402ac6454SAndrew Thompson 		(f->methods->f_stop_read) (f);
79502ac6454SAndrew Thompson 	}
79602ac6454SAndrew Thompson 
79702ac6454SAndrew Thompson 	/* check if we are sleeping */
79802ac6454SAndrew Thompson 	if (f->flag_sleeping) {
79902ac6454SAndrew Thompson 		DPRINTFN(2, "Sleeping at close!\n");
80002ac6454SAndrew Thompson 	}
80102ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
80202ac6454SAndrew Thompson 
80302ac6454SAndrew Thompson 	/* call close method */
804ee3e3ff5SAndrew Thompson 	(f->methods->f_close) (f, fflags);
80502ac6454SAndrew Thompson 
80602ac6454SAndrew Thompson 	DPRINTF("closed\n");
80702ac6454SAndrew Thompson }
80802ac6454SAndrew Thompson 
80902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
810ee3e3ff5SAndrew Thompson  *	usb2_open - cdev callback
81102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
81202ac6454SAndrew Thompson static int
813ee3e3ff5SAndrew Thompson usb2_open(struct cdev *dev, int fflags, int devtype, struct thread *td)
81402ac6454SAndrew Thompson {
815ee3e3ff5SAndrew Thompson 	struct usb2_fs_privdata* pd = (struct usb2_fs_privdata*)dev->si_drv1;
816ee3e3ff5SAndrew Thompson 	struct usb2_cdev_privdata *cpd;
817ee3e3ff5SAndrew Thompson 	int err, ep;
81802ac6454SAndrew Thompson 
81902ac6454SAndrew Thompson 	DPRINTFN(2, "fflags=0x%08x\n", fflags);
82002ac6454SAndrew Thompson 
821ee3e3ff5SAndrew Thompson 	KASSERT(fflags & (FREAD|FWRITE), ("invalid open flags"));
822ee3e3ff5SAndrew Thompson 	if (((fflags & FREAD) && !(pd->mode & FREAD)) ||
823ee3e3ff5SAndrew Thompson 	    ((fflags & FWRITE) && !(pd->mode & FWRITE))) {
824ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "access mode not supported\n");
82502ac6454SAndrew Thompson 		return (EPERM);
82602ac6454SAndrew Thompson 	}
827ee3e3ff5SAndrew Thompson 
828ee3e3ff5SAndrew Thompson 	cpd = malloc(sizeof(*cpd), M_USBDEV, M_WAITOK | M_ZERO);
829ee3e3ff5SAndrew Thompson 	ep = cpd->ep_addr = pd->ep_addr;
830ee3e3ff5SAndrew Thompson 
831ee3e3ff5SAndrew Thompson 	usb2_loc_fill(pd, cpd);
832ee3e3ff5SAndrew Thompson 	err = usb2_ref_device(cpd, 1);
83302ac6454SAndrew Thompson 	if (err) {
83402ac6454SAndrew Thompson 		DPRINTFN(2, "cannot ref device\n");
835ee3e3ff5SAndrew Thompson 		free(cpd, M_USBDEV);
83602ac6454SAndrew Thompson 		return (ENXIO);
83702ac6454SAndrew Thompson 	}
838ee3e3ff5SAndrew Thompson 	cpd->fflags = fflags;	/* access mode for open lifetime */
83902ac6454SAndrew Thompson 
840ee3e3ff5SAndrew Thompson 	/* Check if the endpoint is already open, we always allow EP0 */
841ee3e3ff5SAndrew Thompson 	if (ep > 0) {
842ee3e3ff5SAndrew Thompson 		if ((fflags & FREAD && cpd->udev->ep_rd_opened & (1 << ep)) ||
843ee3e3ff5SAndrew Thompson 		    (fflags & FWRITE && cpd->udev->ep_wr_opened & (1 << ep))) {
844ee3e3ff5SAndrew Thompson 			DPRINTFN(2, "endpoint already open\n");
845ee3e3ff5SAndrew Thompson 			usb2_unref_device(cpd);
846ee3e3ff5SAndrew Thompson 			free(cpd, M_USBDEV);
847ee3e3ff5SAndrew Thompson 			return (EBUSY);
84802ac6454SAndrew Thompson 		}
849ee3e3ff5SAndrew Thompson 		if (fflags & FREAD)
850ee3e3ff5SAndrew Thompson 			cpd->udev->ep_rd_opened |= (1 << ep);
851ee3e3ff5SAndrew Thompson 		if (fflags & FWRITE)
852ee3e3ff5SAndrew Thompson 			cpd->udev->ep_wr_opened |= (1 << ep);
853ee3e3ff5SAndrew Thompson 	}
854ee3e3ff5SAndrew Thompson 
85502ac6454SAndrew Thompson 	/* create FIFOs, if any */
856ee3e3ff5SAndrew Thompson 	err = usb2_fifo_create(cpd);
85702ac6454SAndrew Thompson 	/* check for error */
85802ac6454SAndrew Thompson 	if (err) {
859ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "cannot create fifo\n");
860ee3e3ff5SAndrew Thompson 		usb2_unref_device(cpd);
861ee3e3ff5SAndrew Thompson 		free(cpd, M_USBDEV);
86202ac6454SAndrew Thompson 		return (err);
86302ac6454SAndrew Thompson 	}
86402ac6454SAndrew Thompson 	if (fflags & FREAD) {
865ee3e3ff5SAndrew Thompson 		err = usb2_fifo_open(cpd->rxfifo, fflags);
86602ac6454SAndrew Thompson 		if (err) {
86702ac6454SAndrew Thompson 			DPRINTFN(2, "read open failed\n");
868ee3e3ff5SAndrew Thompson 			usb2_unref_device(cpd);
869ee3e3ff5SAndrew Thompson 			free(cpd, M_USBDEV);
87002ac6454SAndrew Thompson 			return (err);
87102ac6454SAndrew Thompson 		}
87202ac6454SAndrew Thompson 	}
87302ac6454SAndrew Thompson 	if (fflags & FWRITE) {
874ee3e3ff5SAndrew Thompson 		err = usb2_fifo_open(cpd->txfifo, fflags);
87502ac6454SAndrew Thompson 		if (err) {
87602ac6454SAndrew Thompson 			DPRINTFN(2, "write open failed\n");
87702ac6454SAndrew Thompson 			if (fflags & FREAD) {
878ee3e3ff5SAndrew Thompson 				usb2_fifo_close(cpd->rxfifo, fflags);
87902ac6454SAndrew Thompson 			}
880ee3e3ff5SAndrew Thompson 			usb2_unref_device(cpd);
881ee3e3ff5SAndrew Thompson 			free(cpd, M_USBDEV);
88202ac6454SAndrew Thompson 			return (err);
88302ac6454SAndrew Thompson 		}
88402ac6454SAndrew Thompson 	}
885ee3e3ff5SAndrew Thompson 	usb2_unref_device(cpd);
886ee3e3ff5SAndrew Thompson 	devfs_set_cdevpriv(cpd, usb2_close);
88702ac6454SAndrew Thompson 
888ee3e3ff5SAndrew Thompson 	return (0);
88902ac6454SAndrew Thompson }
89002ac6454SAndrew Thompson 
89102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
89202ac6454SAndrew Thompson  *	usb2_close - cdev callback
89302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
89402ac6454SAndrew Thompson static void
895ee3e3ff5SAndrew Thompson usb2_close(void *arg)
89602ac6454SAndrew Thompson {
897ee3e3ff5SAndrew Thompson 	struct usb2_cdev_privdata *cpd = arg;
898ee3e3ff5SAndrew Thompson 	struct usb2_device *udev;
899ee3e3ff5SAndrew Thompson 	int err;
90002ac6454SAndrew Thompson 
901ee3e3ff5SAndrew Thompson 	DPRINTFN(2, "usb2_close, cpd=%p\n", cpd);
902ee3e3ff5SAndrew Thompson 
903ee3e3ff5SAndrew Thompson 	err = usb2_ref_device(cpd, 1);
904ee3e3ff5SAndrew Thompson 	if (err) {
905ee3e3ff5SAndrew Thompson 		free(cpd, M_USBDEV);
90602ac6454SAndrew Thompson 		return;
90702ac6454SAndrew Thompson 	}
90802ac6454SAndrew Thompson 
909ee3e3ff5SAndrew Thompson 	udev = cpd->udev;
910ee3e3ff5SAndrew Thompson 	if (cpd->fflags & FREAD) {
911ee3e3ff5SAndrew Thompson 		usb2_fifo_close(cpd->rxfifo, cpd->fflags);
912ee3e3ff5SAndrew Thompson 		/* clear read bitmask */
913ee3e3ff5SAndrew Thompson 		udev->ep_rd_opened &= ~(1 << cpd->ep_addr);
91402ac6454SAndrew Thompson 	}
915ee3e3ff5SAndrew Thompson 	if (cpd->fflags & FWRITE) {
916ee3e3ff5SAndrew Thompson 		usb2_fifo_close(cpd->txfifo, cpd->fflags);
917ee3e3ff5SAndrew Thompson 		/* clear write bitmask */
918ee3e3ff5SAndrew Thompson 		udev->ep_wr_opened &= ~(1 << cpd->ep_addr);
91902ac6454SAndrew Thompson 	}
920ee3e3ff5SAndrew Thompson 
921ee3e3ff5SAndrew Thompson 	usb2_unref_device(cpd);
922ee3e3ff5SAndrew Thompson 	free(cpd, M_USBDEV);
92302ac6454SAndrew Thompson 	return;
92402ac6454SAndrew Thompson }
92502ac6454SAndrew Thompson 
92602ac6454SAndrew Thompson static void
92702ac6454SAndrew Thompson usb2_dev_init(void *arg)
92802ac6454SAndrew Thompson {
92902ac6454SAndrew Thompson 	mtx_init(&usb2_ref_lock, "USB ref mutex", NULL, MTX_DEF);
93002ac6454SAndrew Thompson 	sx_init(&usb2_sym_lock, "USB sym mutex");
93102ac6454SAndrew Thompson 	TAILQ_INIT(&usb2_sym_head);
93202ac6454SAndrew Thompson 
93302ac6454SAndrew Thompson 	/* check the UGEN methods */
93402ac6454SAndrew Thompson 	usb2_fifo_check_methods(&usb2_ugen_methods);
93502ac6454SAndrew Thompson }
93602ac6454SAndrew Thompson 
93702ac6454SAndrew Thompson SYSINIT(usb2_dev_init, SI_SUB_KLD, SI_ORDER_FIRST, usb2_dev_init, NULL);
93802ac6454SAndrew Thompson 
93902ac6454SAndrew Thompson static void
94002ac6454SAndrew Thompson usb2_dev_init_post(void *arg)
94102ac6454SAndrew Thompson {
94202ac6454SAndrew Thompson 	/*
943ee3e3ff5SAndrew Thompson 	 * Create /dev/usb - this is needed for usbconfig(8), which
944ee3e3ff5SAndrew Thompson 	 * needs a well-known device name to access.
94502ac6454SAndrew Thompson 	 */
946ee3e3ff5SAndrew Thompson 	usb2_dev = make_dev(&usb2_static_devsw, 0, UID_ROOT, GID_OPERATOR,
947ee3e3ff5SAndrew Thompson 	    0644, USB_DEVICE_NAME);
94802ac6454SAndrew Thompson 	if (usb2_dev == NULL) {
94902ac6454SAndrew Thompson 		DPRINTFN(0, "Could not create usb bus device!\n");
95002ac6454SAndrew Thompson 	}
95102ac6454SAndrew Thompson }
95202ac6454SAndrew Thompson 
95302ac6454SAndrew Thompson SYSINIT(usb2_dev_init_post, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, usb2_dev_init_post, NULL);
95402ac6454SAndrew Thompson 
95502ac6454SAndrew Thompson static void
95602ac6454SAndrew Thompson usb2_dev_uninit(void *arg)
95702ac6454SAndrew Thompson {
958ee3e3ff5SAndrew Thompson 	if (usb2_dev != NULL) {
95902ac6454SAndrew Thompson 		destroy_dev(usb2_dev);
96002ac6454SAndrew Thompson 		usb2_dev = NULL;
961ee3e3ff5SAndrew Thompson 
96202ac6454SAndrew Thompson 	}
96302ac6454SAndrew Thompson 	mtx_destroy(&usb2_ref_lock);
96402ac6454SAndrew Thompson 	sx_destroy(&usb2_sym_lock);
96502ac6454SAndrew Thompson }
96602ac6454SAndrew Thompson 
96702ac6454SAndrew Thompson SYSUNINIT(usb2_dev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb2_dev_uninit, NULL);
96802ac6454SAndrew Thompson 
96902ac6454SAndrew Thompson static int
97002ac6454SAndrew Thompson usb2_ioctl_f_sub(struct usb2_fifo *f, u_long cmd, void *addr,
97102ac6454SAndrew Thompson     struct thread *td)
97202ac6454SAndrew Thompson {
97302ac6454SAndrew Thompson 	int error = 0;
97402ac6454SAndrew Thompson 
97502ac6454SAndrew Thompson 	switch (cmd) {
97602ac6454SAndrew Thompson 	case FIODTYPE:
97702ac6454SAndrew Thompson 		*(int *)addr = 0;	/* character device */
97802ac6454SAndrew Thompson 		break;
97902ac6454SAndrew Thompson 
98002ac6454SAndrew Thompson 	case FIONBIO:
98102ac6454SAndrew Thompson 		/* handled by upper FS layer */
98202ac6454SAndrew Thompson 		break;
98302ac6454SAndrew Thompson 
98402ac6454SAndrew Thompson 	case FIOASYNC:
98502ac6454SAndrew Thompson 		if (*(int *)addr) {
98602ac6454SAndrew Thompson 			if (f->async_p != NULL) {
98702ac6454SAndrew Thompson 				error = EBUSY;
98802ac6454SAndrew Thompson 				break;
98902ac6454SAndrew Thompson 			}
99002ac6454SAndrew Thompson 			f->async_p = USB_TD_GET_PROC(td);
99102ac6454SAndrew Thompson 		} else {
99202ac6454SAndrew Thompson 			f->async_p = NULL;
99302ac6454SAndrew Thompson 		}
99402ac6454SAndrew Thompson 		break;
99502ac6454SAndrew Thompson 
99602ac6454SAndrew Thompson 		/* XXX this is not the most general solution */
99702ac6454SAndrew Thompson 	case TIOCSPGRP:
99802ac6454SAndrew Thompson 		if (f->async_p == NULL) {
99902ac6454SAndrew Thompson 			error = EINVAL;
100002ac6454SAndrew Thompson 			break;
100102ac6454SAndrew Thompson 		}
100202ac6454SAndrew Thompson 		if (*(int *)addr != USB_PROC_GET_GID(f->async_p)) {
100302ac6454SAndrew Thompson 			error = EPERM;
100402ac6454SAndrew Thompson 			break;
100502ac6454SAndrew Thompson 		}
100602ac6454SAndrew Thompson 		break;
100702ac6454SAndrew Thompson 	default:
100802ac6454SAndrew Thompson 		return (ENOIOCTL);
100902ac6454SAndrew Thompson 	}
101002ac6454SAndrew Thompson 	return (error);
101102ac6454SAndrew Thompson }
101202ac6454SAndrew Thompson 
1013ee3e3ff5SAndrew Thompson /*------------------------------------------------------------------------*
1014ee3e3ff5SAndrew Thompson  *	usb2_ioctl - cdev callback
1015ee3e3ff5SAndrew Thompson  *------------------------------------------------------------------------*/
101602ac6454SAndrew Thompson static int
1017ee3e3ff5SAndrew Thompson usb2_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int fflag, struct thread* td)
101802ac6454SAndrew Thompson {
1019ee3e3ff5SAndrew Thompson 	struct usb2_cdev_privdata* cpd;
102002ac6454SAndrew Thompson 	struct usb2_fifo *f;
102102ac6454SAndrew Thompson 	int fflags;
102202ac6454SAndrew Thompson 	int err;
102302ac6454SAndrew Thompson 
1024ee3e3ff5SAndrew Thompson 	err = devfs_get_cdevpriv((void **)&cpd);
1025ee3e3ff5SAndrew Thompson 	if (err != 0)
1026ee3e3ff5SAndrew Thompson 		return (err);
1027ee3e3ff5SAndrew Thompson 
1028f5f145baSAndrew Thompson 	/*
1029f5f145baSAndrew Thompson 	 * Performance optimistaion: We try to check for IOCTL's that
1030f5f145baSAndrew Thompson 	 * don't need the USB reference first. Then we grab the USB
1031f5f145baSAndrew Thompson 	 * reference if we need it!
1032f5f145baSAndrew Thompson 	 */
1033f5f145baSAndrew Thompson 	err = usb2_ref_device(cpd, 0 /* no uref */ );
103402ac6454SAndrew Thompson 	if (err) {
103502ac6454SAndrew Thompson 		return (ENXIO);
103602ac6454SAndrew Thompson 	}
1037ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
103802ac6454SAndrew Thompson 
103902ac6454SAndrew Thompson 	DPRINTFN(2, "fflags=%u, cmd=0x%lx\n", fflags, cmd);
104002ac6454SAndrew Thompson 
104102ac6454SAndrew Thompson 	f = NULL;			/* set default value */
104202ac6454SAndrew Thompson 	err = ENOIOCTL;			/* set default value */
104302ac6454SAndrew Thompson 
104402ac6454SAndrew Thompson 	if (fflags & FWRITE) {
1045ee3e3ff5SAndrew Thompson 		f = cpd->txfifo;
104602ac6454SAndrew Thompson 		err = usb2_ioctl_f_sub(f, cmd, addr, td);
104702ac6454SAndrew Thompson 	}
104802ac6454SAndrew Thompson 	if (fflags & FREAD) {
1049ee3e3ff5SAndrew Thompson 		f = cpd->rxfifo;
105002ac6454SAndrew Thompson 		err = usb2_ioctl_f_sub(f, cmd, addr, td);
105102ac6454SAndrew Thompson 	}
1052ee3e3ff5SAndrew Thompson 	KASSERT(f != NULL, ("fifo not found"));
105302ac6454SAndrew Thompson 	if (err == ENOIOCTL) {
1054ee3e3ff5SAndrew Thompson 		err = (f->methods->f_ioctl) (f, cmd, addr, fflags);
105502ac6454SAndrew Thompson 		if (err == ENOIOCTL) {
1056ee3e3ff5SAndrew Thompson 			if (usb2_uref_location(cpd)) {
105702ac6454SAndrew Thompson 				err = ENXIO;
105802ac6454SAndrew Thompson 				goto done;
105902ac6454SAndrew Thompson 			}
1060ee3e3ff5SAndrew Thompson 			err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags);
106102ac6454SAndrew Thompson 		}
106202ac6454SAndrew Thompson 	}
106302ac6454SAndrew Thompson 	if (err == ENOIOCTL) {
106402ac6454SAndrew Thompson 		err = ENOTTY;
106502ac6454SAndrew Thompson 	}
106602ac6454SAndrew Thompson done:
1067ee3e3ff5SAndrew Thompson 	usb2_unref_device(cpd);
106802ac6454SAndrew Thompson 	return (err);
106902ac6454SAndrew Thompson }
107002ac6454SAndrew Thompson 
107102ac6454SAndrew Thompson /* ARGSUSED */
107202ac6454SAndrew Thompson static int
1073ee3e3ff5SAndrew Thompson usb2_poll(struct cdev* dev, int events, struct thread* td)
107402ac6454SAndrew Thompson {
1075ee3e3ff5SAndrew Thompson 	struct usb2_cdev_privdata* cpd;
107602ac6454SAndrew Thompson 	struct usb2_fifo *f;
107702ac6454SAndrew Thompson 	struct usb2_mbuf *m;
107802ac6454SAndrew Thompson 	int fflags;
1079ee3e3ff5SAndrew Thompson 	int err, revents;
108002ac6454SAndrew Thompson 
1081ee3e3ff5SAndrew Thompson 	err = devfs_get_cdevpriv((void **)&cpd);
1082ee3e3ff5SAndrew Thompson 	if (err != 0)
1083ee3e3ff5SAndrew Thompson 		return (err);
1084ee3e3ff5SAndrew Thompson 
1085ee3e3ff5SAndrew Thompson 	err = usb2_ref_device(cpd, 0 /* no uref */ );
1086ee3e3ff5SAndrew Thompson 	if (err)
108702ac6454SAndrew Thompson 		return (POLLHUP);
1088ee3e3ff5SAndrew Thompson 
1089ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
109002ac6454SAndrew Thompson 
109102ac6454SAndrew Thompson 	/* Figure out who needs service */
1092ee3e3ff5SAndrew Thompson 	revents = 0;
109302ac6454SAndrew Thompson 	if ((events & (POLLOUT | POLLWRNORM)) &&
109402ac6454SAndrew Thompson 	    (fflags & FWRITE)) {
109502ac6454SAndrew Thompson 
1096ee3e3ff5SAndrew Thompson 		f = cpd->txfifo;
109702ac6454SAndrew Thompson 
109802ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
109902ac6454SAndrew Thompson 
1100ee3e3ff5SAndrew Thompson 		if (!cpd->is_usbfs) {
110102ac6454SAndrew Thompson 			if (f->flag_iserror) {
110202ac6454SAndrew Thompson 				/* we got an error */
110302ac6454SAndrew Thompson 				m = (void *)1;
110402ac6454SAndrew Thompson 			} else {
110502ac6454SAndrew Thompson 				if (f->queue_data == NULL) {
110602ac6454SAndrew Thompson 					/*
110702ac6454SAndrew Thompson 					 * start write transfer, if not
110802ac6454SAndrew Thompson 					 * already started
110902ac6454SAndrew Thompson 					 */
111002ac6454SAndrew Thompson 					(f->methods->f_start_write) (f);
111102ac6454SAndrew Thompson 				}
111202ac6454SAndrew Thompson 				/* check if any packets are available */
111302ac6454SAndrew Thompson 				USB_IF_POLL(&f->free_q, m);
111402ac6454SAndrew Thompson 			}
111502ac6454SAndrew Thompson 		} else {
111602ac6454SAndrew Thompson 			if (f->flag_iscomplete) {
111702ac6454SAndrew Thompson 				m = (void *)1;
111802ac6454SAndrew Thompson 			} else {
111902ac6454SAndrew Thompson 				m = NULL;
112002ac6454SAndrew Thompson 			}
112102ac6454SAndrew Thompson 		}
112202ac6454SAndrew Thompson 
112302ac6454SAndrew Thompson 		if (m) {
112402ac6454SAndrew Thompson 			revents |= events & (POLLOUT | POLLWRNORM);
112502ac6454SAndrew Thompson 		} else {
112602ac6454SAndrew Thompson 			f->flag_isselect = 1;
112702ac6454SAndrew Thompson 			selrecord(td, &f->selinfo);
112802ac6454SAndrew Thompson 		}
112902ac6454SAndrew Thompson 
113002ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
113102ac6454SAndrew Thompson 	}
113202ac6454SAndrew Thompson 	if ((events & (POLLIN | POLLRDNORM)) &&
113302ac6454SAndrew Thompson 	    (fflags & FREAD)) {
113402ac6454SAndrew Thompson 
1135ee3e3ff5SAndrew Thompson 		f = cpd->rxfifo;
113602ac6454SAndrew Thompson 
113702ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
113802ac6454SAndrew Thompson 
1139ee3e3ff5SAndrew Thompson 		if (!cpd->is_usbfs) {
114002ac6454SAndrew Thompson 			if (f->flag_iserror) {
114102ac6454SAndrew Thompson 				/* we have and error */
114202ac6454SAndrew Thompson 				m = (void *)1;
114302ac6454SAndrew Thompson 			} else {
114402ac6454SAndrew Thompson 				if (f->queue_data == NULL) {
114502ac6454SAndrew Thompson 					/*
114602ac6454SAndrew Thompson 					 * start read transfer, if not
114702ac6454SAndrew Thompson 					 * already started
114802ac6454SAndrew Thompson 					 */
114902ac6454SAndrew Thompson 					(f->methods->f_start_read) (f);
115002ac6454SAndrew Thompson 				}
115102ac6454SAndrew Thompson 				/* check if any packets are available */
115202ac6454SAndrew Thompson 				USB_IF_POLL(&f->used_q, m);
115302ac6454SAndrew Thompson 			}
115402ac6454SAndrew Thompson 		} else {
115502ac6454SAndrew Thompson 			if (f->flag_iscomplete) {
115602ac6454SAndrew Thompson 				m = (void *)1;
115702ac6454SAndrew Thompson 			} else {
115802ac6454SAndrew Thompson 				m = NULL;
115902ac6454SAndrew Thompson 			}
116002ac6454SAndrew Thompson 		}
116102ac6454SAndrew Thompson 
116202ac6454SAndrew Thompson 		if (m) {
116302ac6454SAndrew Thompson 			revents |= events & (POLLIN | POLLRDNORM);
116402ac6454SAndrew Thompson 		} else {
116502ac6454SAndrew Thompson 			f->flag_isselect = 1;
116602ac6454SAndrew Thompson 			selrecord(td, &f->selinfo);
116702ac6454SAndrew Thompson 
1168ee3e3ff5SAndrew Thompson 			if (!cpd->is_usbfs) {
116902ac6454SAndrew Thompson 				/* start reading data */
117002ac6454SAndrew Thompson 				(f->methods->f_start_read) (f);
117102ac6454SAndrew Thompson 			}
117202ac6454SAndrew Thompson 		}
117302ac6454SAndrew Thompson 
117402ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
117502ac6454SAndrew Thompson 	}
1176ee3e3ff5SAndrew Thompson 	usb2_unref_device(cpd);
117702ac6454SAndrew Thompson 	return (revents);
117802ac6454SAndrew Thompson }
117902ac6454SAndrew Thompson 
118002ac6454SAndrew Thompson static int
1181ee3e3ff5SAndrew Thompson usb2_read(struct cdev *dev, struct uio *uio, int ioflag)
118202ac6454SAndrew Thompson {
1183ee3e3ff5SAndrew Thompson 	struct usb2_cdev_privdata* cpd;
118402ac6454SAndrew Thompson 	struct usb2_fifo *f;
118502ac6454SAndrew Thompson 	struct usb2_mbuf *m;
118602ac6454SAndrew Thompson 	int fflags;
118702ac6454SAndrew Thompson 	int resid;
118802ac6454SAndrew Thompson 	int io_len;
118902ac6454SAndrew Thompson 	int err;
119002ac6454SAndrew Thompson 	uint8_t tr_data = 0;
119102ac6454SAndrew Thompson 
1192ee3e3ff5SAndrew Thompson 	err = devfs_get_cdevpriv((void **)&cpd);
1193ee3e3ff5SAndrew Thompson 	if (err != 0)
1194ee3e3ff5SAndrew Thompson 		return (err);
119502ac6454SAndrew Thompson 
1196ee3e3ff5SAndrew Thompson 	err = usb2_ref_device(cpd, 0 /* no uref */ );
119702ac6454SAndrew Thompson 	if (err) {
119802ac6454SAndrew Thompson 		return (ENXIO);
119902ac6454SAndrew Thompson 	}
1200ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
1201ee3e3ff5SAndrew Thompson 
1202ee3e3ff5SAndrew Thompson 	f = cpd->rxfifo;
120302ac6454SAndrew Thompson 	if (f == NULL) {
120402ac6454SAndrew Thompson 		/* should not happen */
120502ac6454SAndrew Thompson 		return (EPERM);
120602ac6454SAndrew Thompson 	}
120702ac6454SAndrew Thompson 
1208ee3e3ff5SAndrew Thompson 	resid = uio->uio_resid;
120902ac6454SAndrew Thompson 
121002ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
121102ac6454SAndrew Thompson 
121202ac6454SAndrew Thompson 	/* check for permanent read error */
121302ac6454SAndrew Thompson 	if (f->flag_iserror) {
121402ac6454SAndrew Thompson 		err = EIO;
121502ac6454SAndrew Thompson 		goto done;
121602ac6454SAndrew Thompson 	}
121702ac6454SAndrew Thompson 	/* check if USB-FS interface is active */
1218ee3e3ff5SAndrew Thompson 	if (cpd->is_usbfs) {
121902ac6454SAndrew Thompson 		/*
122002ac6454SAndrew Thompson 		 * The queue is used for events that should be
122102ac6454SAndrew Thompson 		 * retrieved using the "USB_FS_COMPLETE" ioctl.
122202ac6454SAndrew Thompson 		 */
122302ac6454SAndrew Thompson 		err = EINVAL;
122402ac6454SAndrew Thompson 		goto done;
122502ac6454SAndrew Thompson 	}
122602ac6454SAndrew Thompson 	while (uio->uio_resid > 0) {
122702ac6454SAndrew Thompson 
122802ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
122902ac6454SAndrew Thompson 
123002ac6454SAndrew Thompson 		if (m == NULL) {
123102ac6454SAndrew Thompson 
123202ac6454SAndrew Thompson 			/* start read transfer, if not already started */
123302ac6454SAndrew Thompson 
123402ac6454SAndrew Thompson 			(f->methods->f_start_read) (f);
123502ac6454SAndrew Thompson 
1236ee3e3ff5SAndrew Thompson 			if (fflags & IO_NDELAY) {
123702ac6454SAndrew Thompson 				if (tr_data) {
123802ac6454SAndrew Thompson 					/* return length before error */
123902ac6454SAndrew Thompson 					break;
124002ac6454SAndrew Thompson 				}
124102ac6454SAndrew Thompson 				err = EWOULDBLOCK;
124202ac6454SAndrew Thompson 				break;
124302ac6454SAndrew Thompson 			}
124402ac6454SAndrew Thompson 			DPRINTF("sleeping\n");
124502ac6454SAndrew Thompson 
124602ac6454SAndrew Thompson 			err = usb2_fifo_wait(f);
124702ac6454SAndrew Thompson 			if (err) {
124802ac6454SAndrew Thompson 				break;
124902ac6454SAndrew Thompson 			}
125002ac6454SAndrew Thompson 			continue;
125102ac6454SAndrew Thompson 		}
125202ac6454SAndrew Thompson 		if (f->methods->f_filter_read) {
125302ac6454SAndrew Thompson 			/*
125402ac6454SAndrew Thompson 			 * Sometimes it is convenient to process data at the
125502ac6454SAndrew Thompson 			 * expense of a userland process instead of a kernel
125602ac6454SAndrew Thompson 			 * process.
125702ac6454SAndrew Thompson 			 */
125802ac6454SAndrew Thompson 			(f->methods->f_filter_read) (f, m);
125902ac6454SAndrew Thompson 		}
126002ac6454SAndrew Thompson 		tr_data = 1;
126102ac6454SAndrew Thompson 
126202ac6454SAndrew Thompson 		io_len = MIN(m->cur_data_len, uio->uio_resid);
126302ac6454SAndrew Thompson 
126402ac6454SAndrew Thompson 		DPRINTFN(2, "transfer %d bytes from %p\n",
126502ac6454SAndrew Thompson 		    io_len, m->cur_data_ptr);
126602ac6454SAndrew Thompson 
126702ac6454SAndrew Thompson 		err = usb2_fifo_uiomove(f,
126802ac6454SAndrew Thompson 		    m->cur_data_ptr, io_len, uio);
126902ac6454SAndrew Thompson 
127002ac6454SAndrew Thompson 		m->cur_data_len -= io_len;
127102ac6454SAndrew Thompson 		m->cur_data_ptr += io_len;
127202ac6454SAndrew Thompson 
127302ac6454SAndrew Thompson 		if (m->cur_data_len == 0) {
127402ac6454SAndrew Thompson 
127502ac6454SAndrew Thompson 			uint8_t last_packet;
127602ac6454SAndrew Thompson 
127702ac6454SAndrew Thompson 			last_packet = m->last_packet;
127802ac6454SAndrew Thompson 
127902ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->free_q, m);
128002ac6454SAndrew Thompson 
128102ac6454SAndrew Thompson 			if (last_packet) {
128202ac6454SAndrew Thompson 				/* keep framing */
128302ac6454SAndrew Thompson 				break;
128402ac6454SAndrew Thompson 			}
128502ac6454SAndrew Thompson 		} else {
128602ac6454SAndrew Thompson 			USB_IF_PREPEND(&f->used_q, m);
128702ac6454SAndrew Thompson 		}
128802ac6454SAndrew Thompson 
128902ac6454SAndrew Thompson 		if (err) {
129002ac6454SAndrew Thompson 			break;
129102ac6454SAndrew Thompson 		}
129202ac6454SAndrew Thompson 	}
129302ac6454SAndrew Thompson done:
129402ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
129502ac6454SAndrew Thompson 
1296ee3e3ff5SAndrew Thompson 	usb2_unref_device(cpd);
129702ac6454SAndrew Thompson 
129802ac6454SAndrew Thompson 	return (err);
129902ac6454SAndrew Thompson }
130002ac6454SAndrew Thompson 
130102ac6454SAndrew Thompson static int
1302ee3e3ff5SAndrew Thompson usb2_write(struct cdev *dev, struct uio *uio, int ioflag)
130302ac6454SAndrew Thompson {
1304ee3e3ff5SAndrew Thompson 	struct usb2_cdev_privdata* cpd;
130502ac6454SAndrew Thompson 	struct usb2_fifo *f;
130602ac6454SAndrew Thompson 	struct usb2_mbuf *m;
130702ac6454SAndrew Thompson 	int fflags;
130802ac6454SAndrew Thompson 	int resid;
130902ac6454SAndrew Thompson 	int io_len;
131002ac6454SAndrew Thompson 	int err;
131102ac6454SAndrew Thompson 	uint8_t tr_data = 0;
131202ac6454SAndrew Thompson 
131302ac6454SAndrew Thompson 	DPRINTFN(2, "\n");
131402ac6454SAndrew Thompson 
1315ee3e3ff5SAndrew Thompson 	err = devfs_get_cdevpriv((void **)&cpd);
1316ee3e3ff5SAndrew Thompson 	if (err != 0)
1317ee3e3ff5SAndrew Thompson 		return (err);
131802ac6454SAndrew Thompson 
1319ee3e3ff5SAndrew Thompson 	err = usb2_ref_device(cpd, 0 /* no uref */ );
132002ac6454SAndrew Thompson 	if (err) {
132102ac6454SAndrew Thompson 		return (ENXIO);
132202ac6454SAndrew Thompson 	}
1323ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
1324ee3e3ff5SAndrew Thompson 
1325ee3e3ff5SAndrew Thompson 	f = cpd->txfifo;
132602ac6454SAndrew Thompson 	if (f == NULL) {
132702ac6454SAndrew Thompson 		/* should not happen */
1328ee3e3ff5SAndrew Thompson 		usb2_unref_device(cpd);
132902ac6454SAndrew Thompson 		return (EPERM);
133002ac6454SAndrew Thompson 	}
133102ac6454SAndrew Thompson 	resid = uio->uio_resid;
133202ac6454SAndrew Thompson 
133302ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
133402ac6454SAndrew Thompson 
133502ac6454SAndrew Thompson 	/* check for permanent write error */
133602ac6454SAndrew Thompson 	if (f->flag_iserror) {
133702ac6454SAndrew Thompson 		err = EIO;
133802ac6454SAndrew Thompson 		goto done;
133902ac6454SAndrew Thompson 	}
134002ac6454SAndrew Thompson 	/* check if USB-FS interface is active */
1341ee3e3ff5SAndrew Thompson 	if (cpd->is_usbfs) {
134202ac6454SAndrew Thompson 		/*
134302ac6454SAndrew Thompson 		 * The queue is used for events that should be
134402ac6454SAndrew Thompson 		 * retrieved using the "USB_FS_COMPLETE" ioctl.
134502ac6454SAndrew Thompson 		 */
134602ac6454SAndrew Thompson 		err = EINVAL;
134702ac6454SAndrew Thompson 		goto done;
134802ac6454SAndrew Thompson 	}
134902ac6454SAndrew Thompson 	if (f->queue_data == NULL) {
135002ac6454SAndrew Thompson 		/* start write transfer, if not already started */
135102ac6454SAndrew Thompson 		(f->methods->f_start_write) (f);
135202ac6454SAndrew Thompson 	}
135302ac6454SAndrew Thompson 	/* we allow writing zero length data */
135402ac6454SAndrew Thompson 	do {
135502ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->free_q, m);
135602ac6454SAndrew Thompson 
135702ac6454SAndrew Thompson 		if (m == NULL) {
135802ac6454SAndrew Thompson 
1359ee3e3ff5SAndrew Thompson 			if (fflags & IO_NDELAY) {
136002ac6454SAndrew Thompson 				if (tr_data) {
136102ac6454SAndrew Thompson 					/* return length before error */
136202ac6454SAndrew Thompson 					break;
136302ac6454SAndrew Thompson 				}
136402ac6454SAndrew Thompson 				err = EWOULDBLOCK;
136502ac6454SAndrew Thompson 				break;
136602ac6454SAndrew Thompson 			}
136702ac6454SAndrew Thompson 			DPRINTF("sleeping\n");
136802ac6454SAndrew Thompson 
136902ac6454SAndrew Thompson 			err = usb2_fifo_wait(f);
137002ac6454SAndrew Thompson 			if (err) {
137102ac6454SAndrew Thompson 				break;
137202ac6454SAndrew Thompson 			}
137302ac6454SAndrew Thompson 			continue;
137402ac6454SAndrew Thompson 		}
137502ac6454SAndrew Thompson 		tr_data = 1;
137602ac6454SAndrew Thompson 
137702ac6454SAndrew Thompson 		USB_MBUF_RESET(m);
137802ac6454SAndrew Thompson 
137902ac6454SAndrew Thompson 		io_len = MIN(m->cur_data_len, uio->uio_resid);
138002ac6454SAndrew Thompson 
138102ac6454SAndrew Thompson 		m->cur_data_len = io_len;
138202ac6454SAndrew Thompson 
138302ac6454SAndrew Thompson 		DPRINTFN(2, "transfer %d bytes to %p\n",
138402ac6454SAndrew Thompson 		    io_len, m->cur_data_ptr);
138502ac6454SAndrew Thompson 
138602ac6454SAndrew Thompson 		err = usb2_fifo_uiomove(f,
138702ac6454SAndrew Thompson 		    m->cur_data_ptr, io_len, uio);
138802ac6454SAndrew Thompson 
138902ac6454SAndrew Thompson 		if (err) {
139002ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->free_q, m);
139102ac6454SAndrew Thompson 			break;
139202ac6454SAndrew Thompson 		}
139302ac6454SAndrew Thompson 		if (f->methods->f_filter_write) {
139402ac6454SAndrew Thompson 			/*
139502ac6454SAndrew Thompson 			 * Sometimes it is convenient to process data at the
139602ac6454SAndrew Thompson 			 * expense of a userland process instead of a kernel
139702ac6454SAndrew Thompson 			 * process.
139802ac6454SAndrew Thompson 			 */
139902ac6454SAndrew Thompson 			(f->methods->f_filter_write) (f, m);
140002ac6454SAndrew Thompson 		}
140102ac6454SAndrew Thompson 		USB_IF_ENQUEUE(&f->used_q, m);
140202ac6454SAndrew Thompson 
140302ac6454SAndrew Thompson 		(f->methods->f_start_write) (f);
140402ac6454SAndrew Thompson 
140502ac6454SAndrew Thompson 	} while (uio->uio_resid > 0);
140602ac6454SAndrew Thompson done:
140702ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
140802ac6454SAndrew Thompson 
1409ee3e3ff5SAndrew Thompson 	usb2_unref_device(cpd);
141002ac6454SAndrew Thompson 
1411ee3e3ff5SAndrew Thompson 	return (err);
1412ee3e3ff5SAndrew Thompson }
141302ac6454SAndrew Thompson 
1414ee3e3ff5SAndrew Thompson int
1415ee3e3ff5SAndrew Thompson usb2_static_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
1416ee3e3ff5SAndrew Thompson     struct thread *td)
1417ee3e3ff5SAndrew Thompson {
1418ee3e3ff5SAndrew Thompson 	union {
1419ee3e3ff5SAndrew Thompson 		struct usb2_read_dir *urd;
1420ee3e3ff5SAndrew Thompson 		void* data;
1421ee3e3ff5SAndrew Thompson 	} u;
1422ee3e3ff5SAndrew Thompson 	int err = ENOTTY;
1423ee3e3ff5SAndrew Thompson 
1424ee3e3ff5SAndrew Thompson 	u.data = data;
1425ee3e3ff5SAndrew Thompson 	switch (cmd) {
1426ee3e3ff5SAndrew Thompson 		case USB_READ_DIR:
1427ee3e3ff5SAndrew Thompson 			err = usb2_read_symlink(u.urd->urd_data,
1428ee3e3ff5SAndrew Thompson 			    u.urd->urd_startentry, u.urd->urd_maxlen);
1429ee3e3ff5SAndrew Thompson 			break;
1430ee3e3ff5SAndrew Thompson 		case USB_DEV_QUIRK_GET:
1431ee3e3ff5SAndrew Thompson 		case USB_QUIRK_NAME_GET:
1432ee3e3ff5SAndrew Thompson 		case USB_DEV_QUIRK_ADD:
1433ee3e3ff5SAndrew Thompson 		case USB_DEV_QUIRK_REMOVE:
1434ee3e3ff5SAndrew Thompson 			err = usb2_quirk_ioctl_p(cmd, data, fflag, td);
1435ee3e3ff5SAndrew Thompson 			break;
1436ee3e3ff5SAndrew Thompson 		case USB_GET_TEMPLATE:
1437ee3e3ff5SAndrew Thompson 			*(int *)data = usb2_template;
1438ee3e3ff5SAndrew Thompson 			break;
1439ee3e3ff5SAndrew Thompson 		case USB_SET_TEMPLATE:
144050230f98SAndrew Thompson 			err = priv_check(curthread, PRIV_DRIVER);
1441ee3e3ff5SAndrew Thompson 			if (err)
1442ee3e3ff5SAndrew Thompson 				break;
1443ee3e3ff5SAndrew Thompson 			usb2_template = *(int *)data;
1444ee3e3ff5SAndrew Thompson 			break;
1445ee3e3ff5SAndrew Thompson 	}
144602ac6454SAndrew Thompson 	return (err);
144702ac6454SAndrew Thompson }
144802ac6454SAndrew Thompson 
144902ac6454SAndrew Thompson static int
145002ac6454SAndrew Thompson usb2_fifo_uiomove(struct usb2_fifo *f, void *cp,
145102ac6454SAndrew Thompson     int n, struct uio *uio)
145202ac6454SAndrew Thompson {
145302ac6454SAndrew Thompson 	int error;
145402ac6454SAndrew Thompson 
145502ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
145602ac6454SAndrew Thompson 
145702ac6454SAndrew Thompson 	/*
145802ac6454SAndrew Thompson 	 * "uiomove()" can sleep so one needs to make a wrapper,
145902ac6454SAndrew Thompson 	 * exiting the mutex and checking things:
146002ac6454SAndrew Thompson 	 */
146102ac6454SAndrew Thompson 	error = uiomove(cp, n, uio);
146202ac6454SAndrew Thompson 
146302ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
146402ac6454SAndrew Thompson 
146502ac6454SAndrew Thompson 	return (error);
146602ac6454SAndrew Thompson }
146702ac6454SAndrew Thompson 
146802ac6454SAndrew Thompson int
146902ac6454SAndrew Thompson usb2_fifo_wait(struct usb2_fifo *f)
147002ac6454SAndrew Thompson {
147102ac6454SAndrew Thompson 	int err;
147202ac6454SAndrew Thompson 
147302ac6454SAndrew Thompson 	mtx_assert(f->priv_mtx, MA_OWNED);
147402ac6454SAndrew Thompson 
147502ac6454SAndrew Thompson 	if (f->flag_iserror) {
147602ac6454SAndrew Thompson 		/* we are gone */
147702ac6454SAndrew Thompson 		return (EIO);
147802ac6454SAndrew Thompson 	}
147902ac6454SAndrew Thompson 	f->flag_sleeping = 1;
148002ac6454SAndrew Thompson 
148102ac6454SAndrew Thompson 	err = usb2_cv_wait_sig(&f->cv_io, f->priv_mtx);
148202ac6454SAndrew Thompson 
148302ac6454SAndrew Thompson 	if (f->flag_iserror) {
148402ac6454SAndrew Thompson 		/* we are gone */
148502ac6454SAndrew Thompson 		err = EIO;
148602ac6454SAndrew Thompson 	}
148702ac6454SAndrew Thompson 	return (err);
148802ac6454SAndrew Thompson }
148902ac6454SAndrew Thompson 
149002ac6454SAndrew Thompson void
149102ac6454SAndrew Thompson usb2_fifo_signal(struct usb2_fifo *f)
149202ac6454SAndrew Thompson {
149302ac6454SAndrew Thompson 	if (f->flag_sleeping) {
149402ac6454SAndrew Thompson 		f->flag_sleeping = 0;
149502ac6454SAndrew Thompson 		usb2_cv_broadcast(&f->cv_io);
149602ac6454SAndrew Thompson 	}
149702ac6454SAndrew Thompson }
149802ac6454SAndrew Thompson 
149902ac6454SAndrew Thompson void
150002ac6454SAndrew Thompson usb2_fifo_wakeup(struct usb2_fifo *f)
150102ac6454SAndrew Thompson {
150202ac6454SAndrew Thompson 	usb2_fifo_signal(f);
150302ac6454SAndrew Thompson 
150402ac6454SAndrew Thompson 	if (f->flag_isselect) {
150502ac6454SAndrew Thompson 		selwakeup(&f->selinfo);
150602ac6454SAndrew Thompson 		f->flag_isselect = 0;
150702ac6454SAndrew Thompson 	}
150802ac6454SAndrew Thompson 	if (f->async_p != NULL) {
150902ac6454SAndrew Thompson 		PROC_LOCK(f->async_p);
151002ac6454SAndrew Thompson 		psignal(f->async_p, SIGIO);
151102ac6454SAndrew Thompson 		PROC_UNLOCK(f->async_p);
151202ac6454SAndrew Thompson 	}
151302ac6454SAndrew Thompson }
151402ac6454SAndrew Thompson 
151502ac6454SAndrew Thompson static int
1516ee3e3ff5SAndrew Thompson usb2_fifo_dummy_open(struct usb2_fifo *fifo, int fflags)
151702ac6454SAndrew Thompson {
151802ac6454SAndrew Thompson 	return (0);
151902ac6454SAndrew Thompson }
152002ac6454SAndrew Thompson 
152102ac6454SAndrew Thompson static void
1522ee3e3ff5SAndrew Thompson usb2_fifo_dummy_close(struct usb2_fifo *fifo, int fflags)
152302ac6454SAndrew Thompson {
152402ac6454SAndrew Thompson 	return;
152502ac6454SAndrew Thompson }
152602ac6454SAndrew Thompson 
152702ac6454SAndrew Thompson static int
1528ee3e3ff5SAndrew Thompson usb2_fifo_dummy_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr, int fflags)
152902ac6454SAndrew Thompson {
153002ac6454SAndrew Thompson 	return (ENOIOCTL);
153102ac6454SAndrew Thompson }
153202ac6454SAndrew Thompson 
153302ac6454SAndrew Thompson static void
153402ac6454SAndrew Thompson usb2_fifo_dummy_cmd(struct usb2_fifo *fifo)
153502ac6454SAndrew Thompson {
153602ac6454SAndrew Thompson 	fifo->flag_flushing = 0;	/* not flushing */
153702ac6454SAndrew Thompson }
153802ac6454SAndrew Thompson 
153902ac6454SAndrew Thompson static void
154002ac6454SAndrew Thompson usb2_fifo_check_methods(struct usb2_fifo_methods *pm)
154102ac6454SAndrew Thompson {
154202ac6454SAndrew Thompson 	/* check that all callback functions are OK */
154302ac6454SAndrew Thompson 
154402ac6454SAndrew Thompson 	if (pm->f_open == NULL)
154502ac6454SAndrew Thompson 		pm->f_open = &usb2_fifo_dummy_open;
154602ac6454SAndrew Thompson 
154702ac6454SAndrew Thompson 	if (pm->f_close == NULL)
154802ac6454SAndrew Thompson 		pm->f_close = &usb2_fifo_dummy_close;
154902ac6454SAndrew Thompson 
155002ac6454SAndrew Thompson 	if (pm->f_ioctl == NULL)
155102ac6454SAndrew Thompson 		pm->f_ioctl = &usb2_fifo_dummy_ioctl;
155202ac6454SAndrew Thompson 
155302ac6454SAndrew Thompson 	if (pm->f_ioctl_post == NULL)
155402ac6454SAndrew Thompson 		pm->f_ioctl_post = &usb2_fifo_dummy_ioctl;
155502ac6454SAndrew Thompson 
155602ac6454SAndrew Thompson 	if (pm->f_start_read == NULL)
155702ac6454SAndrew Thompson 		pm->f_start_read = &usb2_fifo_dummy_cmd;
155802ac6454SAndrew Thompson 
155902ac6454SAndrew Thompson 	if (pm->f_stop_read == NULL)
156002ac6454SAndrew Thompson 		pm->f_stop_read = &usb2_fifo_dummy_cmd;
156102ac6454SAndrew Thompson 
156202ac6454SAndrew Thompson 	if (pm->f_start_write == NULL)
156302ac6454SAndrew Thompson 		pm->f_start_write = &usb2_fifo_dummy_cmd;
156402ac6454SAndrew Thompson 
156502ac6454SAndrew Thompson 	if (pm->f_stop_write == NULL)
156602ac6454SAndrew Thompson 		pm->f_stop_write = &usb2_fifo_dummy_cmd;
156702ac6454SAndrew Thompson }
156802ac6454SAndrew Thompson 
156902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
157002ac6454SAndrew Thompson  *	usb2_fifo_attach
157102ac6454SAndrew Thompson  *
157202ac6454SAndrew Thompson  * The following function will create a duplex FIFO.
157302ac6454SAndrew Thompson  *
157402ac6454SAndrew Thompson  * Return values:
157502ac6454SAndrew Thompson  * 0: Success.
157602ac6454SAndrew Thompson  * Else: Failure.
157702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
157802ac6454SAndrew Thompson int
157902ac6454SAndrew Thompson usb2_fifo_attach(struct usb2_device *udev, void *priv_sc,
158002ac6454SAndrew Thompson     struct mtx *priv_mtx, struct usb2_fifo_methods *pm,
158102ac6454SAndrew Thompson     struct usb2_fifo_sc *f_sc, uint16_t unit, uint16_t subunit,
1582ee3e3ff5SAndrew Thompson     uint8_t iface_index, uid_t uid, gid_t gid, int mode)
158302ac6454SAndrew Thompson {
158402ac6454SAndrew Thompson 	struct usb2_fifo *f_tx;
158502ac6454SAndrew Thompson 	struct usb2_fifo *f_rx;
1586ee3e3ff5SAndrew Thompson 	char devname[32];
158702ac6454SAndrew Thompson 	uint8_t n;
1588ee3e3ff5SAndrew Thompson 	struct usb2_fs_privdata* pd;
158902ac6454SAndrew Thompson 
159002ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_TX] = NULL;
159102ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_RX] = NULL;
159202ac6454SAndrew Thompson 
159302ac6454SAndrew Thompson 	if (pm == NULL)
159402ac6454SAndrew Thompson 		return (EINVAL);
159502ac6454SAndrew Thompson 
159602ac6454SAndrew Thompson 	/* check the methods */
159702ac6454SAndrew Thompson 	usb2_fifo_check_methods(pm);
159802ac6454SAndrew Thompson 
159902ac6454SAndrew Thompson 	if (priv_mtx == NULL)
160002ac6454SAndrew Thompson 		priv_mtx = &Giant;
160102ac6454SAndrew Thompson 
160202ac6454SAndrew Thompson 	/* search for a free FIFO slot */
160302ac6454SAndrew Thompson 	for (n = 0;; n += 2) {
160402ac6454SAndrew Thompson 
160502ac6454SAndrew Thompson 		if (n == USB_FIFO_MAX) {
160602ac6454SAndrew Thompson 			/* end of FIFOs reached */
160702ac6454SAndrew Thompson 			return (ENOMEM);
160802ac6454SAndrew Thompson 		}
160902ac6454SAndrew Thompson 		/* Check for TX FIFO */
161002ac6454SAndrew Thompson 		if (udev->fifo[n + USB_FIFO_TX] != NULL) {
161102ac6454SAndrew Thompson 			continue;
161202ac6454SAndrew Thompson 		}
161302ac6454SAndrew Thompson 		/* Check for RX FIFO */
161402ac6454SAndrew Thompson 		if (udev->fifo[n + USB_FIFO_RX] != NULL) {
161502ac6454SAndrew Thompson 			continue;
161602ac6454SAndrew Thompson 		}
161702ac6454SAndrew Thompson 		break;
161802ac6454SAndrew Thompson 	}
161902ac6454SAndrew Thompson 
162002ac6454SAndrew Thompson 	f_tx = usb2_fifo_alloc();
162102ac6454SAndrew Thompson 	f_rx = usb2_fifo_alloc();
162202ac6454SAndrew Thompson 
162302ac6454SAndrew Thompson 	if ((f_tx == NULL) || (f_rx == NULL)) {
162402ac6454SAndrew Thompson 		usb2_fifo_free(f_tx);
162502ac6454SAndrew Thompson 		usb2_fifo_free(f_rx);
162602ac6454SAndrew Thompson 		return (ENOMEM);
162702ac6454SAndrew Thompson 	}
162802ac6454SAndrew Thompson 	/* initialise FIFO structures */
162902ac6454SAndrew Thompson 
163002ac6454SAndrew Thompson 	f_tx->fifo_index = n + USB_FIFO_TX;
163102ac6454SAndrew Thompson 	f_tx->dev_ep_index = (n / 2) + (USB_EP_MAX / 2);
163202ac6454SAndrew Thompson 	f_tx->priv_mtx = priv_mtx;
163302ac6454SAndrew Thompson 	f_tx->priv_sc0 = priv_sc;
163402ac6454SAndrew Thompson 	f_tx->methods = pm;
163502ac6454SAndrew Thompson 	f_tx->iface_index = iface_index;
163602ac6454SAndrew Thompson 	f_tx->udev = udev;
163702ac6454SAndrew Thompson 
163802ac6454SAndrew Thompson 	f_rx->fifo_index = n + USB_FIFO_RX;
163902ac6454SAndrew Thompson 	f_rx->dev_ep_index = (n / 2) + (USB_EP_MAX / 2);
164002ac6454SAndrew Thompson 	f_rx->priv_mtx = priv_mtx;
164102ac6454SAndrew Thompson 	f_rx->priv_sc0 = priv_sc;
164202ac6454SAndrew Thompson 	f_rx->methods = pm;
164302ac6454SAndrew Thompson 	f_rx->iface_index = iface_index;
164402ac6454SAndrew Thompson 	f_rx->udev = udev;
164502ac6454SAndrew Thompson 
164602ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_TX] = f_tx;
164702ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_RX] = f_rx;
164802ac6454SAndrew Thompson 
164902ac6454SAndrew Thompson 	mtx_lock(&usb2_ref_lock);
165002ac6454SAndrew Thompson 	udev->fifo[f_tx->fifo_index] = f_tx;
165102ac6454SAndrew Thompson 	udev->fifo[f_rx->fifo_index] = f_rx;
165202ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
165302ac6454SAndrew Thompson 
165402ac6454SAndrew Thompson 	for (n = 0; n != 4; n++) {
165502ac6454SAndrew Thompson 
165602ac6454SAndrew Thompson 		if (pm->basename[n] == NULL) {
165702ac6454SAndrew Thompson 			continue;
165802ac6454SAndrew Thompson 		}
165902ac6454SAndrew Thompson 		if (subunit == 0xFFFF) {
1660ee3e3ff5SAndrew Thompson 			if (snprintf(devname, sizeof(devname),
166102ac6454SAndrew Thompson 			    "%s%u%s", pm->basename[n],
166202ac6454SAndrew Thompson 			    unit, pm->postfix[n] ?
166302ac6454SAndrew Thompson 			    pm->postfix[n] : "")) {
166402ac6454SAndrew Thompson 				/* ignore */
166502ac6454SAndrew Thompson 			}
166602ac6454SAndrew Thompson 		} else {
1667ee3e3ff5SAndrew Thompson 			if (snprintf(devname, sizeof(devname),
166802ac6454SAndrew Thompson 			    "%s%u.%u%s", pm->basename[n],
166902ac6454SAndrew Thompson 			    unit, subunit, pm->postfix[n] ?
167002ac6454SAndrew Thompson 			    pm->postfix[n] : "")) {
167102ac6454SAndrew Thompson 				/* ignore */
167202ac6454SAndrew Thompson 			}
167302ac6454SAndrew Thompson 		}
167402ac6454SAndrew Thompson 
167502ac6454SAndrew Thompson 		/*
167602ac6454SAndrew Thompson 		 * Distribute the symbolic links into two FIFO structures:
167702ac6454SAndrew Thompson 		 */
167802ac6454SAndrew Thompson 		if (n & 1) {
167902ac6454SAndrew Thompson 			f_rx->symlink[n / 2] =
1680ee3e3ff5SAndrew Thompson 			    usb2_alloc_symlink(devname);
168102ac6454SAndrew Thompson 		} else {
168202ac6454SAndrew Thompson 			f_tx->symlink[n / 2] =
1683ee3e3ff5SAndrew Thompson 			    usb2_alloc_symlink(devname);
168402ac6454SAndrew Thompson 		}
1685ee3e3ff5SAndrew Thompson 
1686ee3e3ff5SAndrew Thompson 		/*
1687ee3e3ff5SAndrew Thompson 		 * Initialize device private data - this is used to find the
1688ee3e3ff5SAndrew Thompson 		 * actual USB device itself.
1689ee3e3ff5SAndrew Thompson 		 */
1690ee3e3ff5SAndrew Thompson 		pd = malloc(sizeof(struct usb2_fs_privdata), M_USBDEV, M_WAITOK | M_ZERO);
1691ee3e3ff5SAndrew Thompson 		pd->bus_index = device_get_unit(udev->bus->bdev);
1692ee3e3ff5SAndrew Thompson 		pd->dev_index = udev->device_index;
1693ee3e3ff5SAndrew Thompson 		pd->ep_addr = -1;	/* not an endpoint */
1694ee3e3ff5SAndrew Thompson 		pd->fifo_index = f_tx->fifo_index;
1695ee3e3ff5SAndrew Thompson 		pd->mode = FREAD|FWRITE;
1696ee3e3ff5SAndrew Thompson 
1697ee3e3ff5SAndrew Thompson 		/* Now, create the device itself */
1698ee3e3ff5SAndrew Thompson 		f_sc->dev = make_dev(&usb2_devsw, 0, uid, gid, mode,
1699ee3e3ff5SAndrew Thompson 		    devname);
1700ee3e3ff5SAndrew Thompson 		f_sc->dev->si_drv1 = pd;
170102ac6454SAndrew Thompson 	}
170202ac6454SAndrew Thompson 
170302ac6454SAndrew Thompson 	DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx);
170402ac6454SAndrew Thompson 	return (0);
170502ac6454SAndrew Thompson }
170602ac6454SAndrew Thompson 
170702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
170802ac6454SAndrew Thompson  *	usb2_fifo_alloc_buffer
170902ac6454SAndrew Thompson  *
171002ac6454SAndrew Thompson  * Return values:
171102ac6454SAndrew Thompson  * 0: Success
171202ac6454SAndrew Thompson  * Else failure
171302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
171402ac6454SAndrew Thompson int
171502ac6454SAndrew Thompson usb2_fifo_alloc_buffer(struct usb2_fifo *f, uint32_t bufsize,
171602ac6454SAndrew Thompson     uint16_t nbuf)
171702ac6454SAndrew Thompson {
171802ac6454SAndrew Thompson 	usb2_fifo_free_buffer(f);
171902ac6454SAndrew Thompson 
172002ac6454SAndrew Thompson 	/* allocate an endpoint */
172102ac6454SAndrew Thompson 	f->free_q.ifq_maxlen = nbuf;
172202ac6454SAndrew Thompson 	f->used_q.ifq_maxlen = nbuf;
172302ac6454SAndrew Thompson 
172402ac6454SAndrew Thompson 	f->queue_data = usb2_alloc_mbufs(
172502ac6454SAndrew Thompson 	    M_USBDEV, &f->free_q, bufsize, nbuf);
172602ac6454SAndrew Thompson 
172702ac6454SAndrew Thompson 	if ((f->queue_data == NULL) && bufsize && nbuf) {
172802ac6454SAndrew Thompson 		return (ENOMEM);
172902ac6454SAndrew Thompson 	}
173002ac6454SAndrew Thompson 	return (0);			/* success */
173102ac6454SAndrew Thompson }
173202ac6454SAndrew Thompson 
173302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
173402ac6454SAndrew Thompson  *	usb2_fifo_free_buffer
173502ac6454SAndrew Thompson  *
173602ac6454SAndrew Thompson  * This function will free the buffers associated with a FIFO. This
173702ac6454SAndrew Thompson  * function can be called multiple times in a row.
173802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
173902ac6454SAndrew Thompson void
174002ac6454SAndrew Thompson usb2_fifo_free_buffer(struct usb2_fifo *f)
174102ac6454SAndrew Thompson {
174202ac6454SAndrew Thompson 	if (f->queue_data) {
174302ac6454SAndrew Thompson 		/* free old buffer */
174402ac6454SAndrew Thompson 		free(f->queue_data, M_USBDEV);
174502ac6454SAndrew Thompson 		f->queue_data = NULL;
174602ac6454SAndrew Thompson 	}
174702ac6454SAndrew Thompson 	/* reset queues */
174802ac6454SAndrew Thompson 
174902ac6454SAndrew Thompson 	bzero(&f->free_q, sizeof(f->free_q));
175002ac6454SAndrew Thompson 	bzero(&f->used_q, sizeof(f->used_q));
175102ac6454SAndrew Thompson }
175202ac6454SAndrew Thompson 
1753ee3e3ff5SAndrew Thompson static void
1754ee3e3ff5SAndrew Thompson usb2_fifo_cleanup(void* ptr)
1755ee3e3ff5SAndrew Thompson {
1756ee3e3ff5SAndrew Thompson 	free(ptr, M_USBDEV);
1757ee3e3ff5SAndrew Thompson }
1758ee3e3ff5SAndrew Thompson 
175902ac6454SAndrew Thompson void
176002ac6454SAndrew Thompson usb2_fifo_detach(struct usb2_fifo_sc *f_sc)
176102ac6454SAndrew Thompson {
176202ac6454SAndrew Thompson 	if (f_sc == NULL) {
176302ac6454SAndrew Thompson 		return;
176402ac6454SAndrew Thompson 	}
176502ac6454SAndrew Thompson 	usb2_fifo_free(f_sc->fp[USB_FIFO_TX]);
176602ac6454SAndrew Thompson 	usb2_fifo_free(f_sc->fp[USB_FIFO_RX]);
176702ac6454SAndrew Thompson 
176802ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_TX] = NULL;
176902ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_RX] = NULL;
177002ac6454SAndrew Thompson 
1771ee3e3ff5SAndrew Thompson 	if (f_sc->dev != NULL) {
1772ee3e3ff5SAndrew Thompson 		destroy_dev_sched_cb(f_sc->dev, usb2_fifo_cleanup, f_sc->dev->si_drv1);
1773ee3e3ff5SAndrew Thompson 	}
1774ee3e3ff5SAndrew Thompson 
177502ac6454SAndrew Thompson 	DPRINTFN(2, "detached %p\n", f_sc);
177602ac6454SAndrew Thompson }
177702ac6454SAndrew Thompson 
177802ac6454SAndrew Thompson uint32_t
177902ac6454SAndrew Thompson usb2_fifo_put_bytes_max(struct usb2_fifo *f)
178002ac6454SAndrew Thompson {
178102ac6454SAndrew Thompson 	struct usb2_mbuf *m;
178202ac6454SAndrew Thompson 	uint32_t len;
178302ac6454SAndrew Thompson 
178402ac6454SAndrew Thompson 	USB_IF_POLL(&f->free_q, m);
178502ac6454SAndrew Thompson 
178602ac6454SAndrew Thompson 	if (m) {
178702ac6454SAndrew Thompson 		len = m->max_data_len;
178802ac6454SAndrew Thompson 	} else {
178902ac6454SAndrew Thompson 		len = 0;
179002ac6454SAndrew Thompson 	}
179102ac6454SAndrew Thompson 	return (len);
179202ac6454SAndrew Thompson }
179302ac6454SAndrew Thompson 
179402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
179502ac6454SAndrew Thompson  *	usb2_fifo_put_data
179602ac6454SAndrew Thompson  *
179702ac6454SAndrew Thompson  * what:
179802ac6454SAndrew Thompson  *  0 - normal operation
179902ac6454SAndrew Thompson  *  1 - set last packet flag to enforce framing
180002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
180102ac6454SAndrew Thompson void
180202ac6454SAndrew Thompson usb2_fifo_put_data(struct usb2_fifo *f, struct usb2_page_cache *pc,
180302ac6454SAndrew Thompson     uint32_t offset, uint32_t len, uint8_t what)
180402ac6454SAndrew Thompson {
180502ac6454SAndrew Thompson 	struct usb2_mbuf *m;
180602ac6454SAndrew Thompson 	uint32_t io_len;
180702ac6454SAndrew Thompson 
180802ac6454SAndrew Thompson 	while (len || (what == 1)) {
180902ac6454SAndrew Thompson 
181002ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->free_q, m);
181102ac6454SAndrew Thompson 
181202ac6454SAndrew Thompson 		if (m) {
181302ac6454SAndrew Thompson 			USB_MBUF_RESET(m);
181402ac6454SAndrew Thompson 
181502ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
181602ac6454SAndrew Thompson 
181702ac6454SAndrew Thompson 			usb2_copy_out(pc, offset, m->cur_data_ptr, io_len);
181802ac6454SAndrew Thompson 
181902ac6454SAndrew Thompson 			m->cur_data_len = io_len;
182002ac6454SAndrew Thompson 			offset += io_len;
182102ac6454SAndrew Thompson 			len -= io_len;
182202ac6454SAndrew Thompson 
182302ac6454SAndrew Thompson 			if ((len == 0) && (what == 1)) {
182402ac6454SAndrew Thompson 				m->last_packet = 1;
182502ac6454SAndrew Thompson 			}
182602ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->used_q, m);
182702ac6454SAndrew Thompson 
182802ac6454SAndrew Thompson 			usb2_fifo_wakeup(f);
182902ac6454SAndrew Thompson 
183002ac6454SAndrew Thompson 			if ((len == 0) || (what == 1)) {
183102ac6454SAndrew Thompson 				break;
183202ac6454SAndrew Thompson 			}
183302ac6454SAndrew Thompson 		} else {
183402ac6454SAndrew Thompson 			break;
183502ac6454SAndrew Thompson 		}
183602ac6454SAndrew Thompson 	}
183702ac6454SAndrew Thompson }
183802ac6454SAndrew Thompson 
183902ac6454SAndrew Thompson void
184002ac6454SAndrew Thompson usb2_fifo_put_data_linear(struct usb2_fifo *f, void *ptr,
184102ac6454SAndrew Thompson     uint32_t len, uint8_t what)
184202ac6454SAndrew Thompson {
184302ac6454SAndrew Thompson 	struct usb2_mbuf *m;
184402ac6454SAndrew Thompson 	uint32_t io_len;
184502ac6454SAndrew Thompson 
184602ac6454SAndrew Thompson 	while (len || (what == 1)) {
184702ac6454SAndrew Thompson 
184802ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->free_q, m);
184902ac6454SAndrew Thompson 
185002ac6454SAndrew Thompson 		if (m) {
185102ac6454SAndrew Thompson 			USB_MBUF_RESET(m);
185202ac6454SAndrew Thompson 
185302ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
185402ac6454SAndrew Thompson 
185502ac6454SAndrew Thompson 			bcopy(ptr, m->cur_data_ptr, io_len);
185602ac6454SAndrew Thompson 
185702ac6454SAndrew Thompson 			m->cur_data_len = io_len;
185802ac6454SAndrew Thompson 			ptr = USB_ADD_BYTES(ptr, io_len);
185902ac6454SAndrew Thompson 			len -= io_len;
186002ac6454SAndrew Thompson 
186102ac6454SAndrew Thompson 			if ((len == 0) && (what == 1)) {
186202ac6454SAndrew Thompson 				m->last_packet = 1;
186302ac6454SAndrew Thompson 			}
186402ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->used_q, m);
186502ac6454SAndrew Thompson 
186602ac6454SAndrew Thompson 			usb2_fifo_wakeup(f);
186702ac6454SAndrew Thompson 
186802ac6454SAndrew Thompson 			if ((len == 0) || (what == 1)) {
186902ac6454SAndrew Thompson 				break;
187002ac6454SAndrew Thompson 			}
187102ac6454SAndrew Thompson 		} else {
187202ac6454SAndrew Thompson 			break;
187302ac6454SAndrew Thompson 		}
187402ac6454SAndrew Thompson 	}
187502ac6454SAndrew Thompson }
187602ac6454SAndrew Thompson 
187702ac6454SAndrew Thompson uint8_t
187802ac6454SAndrew Thompson usb2_fifo_put_data_buffer(struct usb2_fifo *f, void *ptr, uint32_t len)
187902ac6454SAndrew Thompson {
188002ac6454SAndrew Thompson 	struct usb2_mbuf *m;
188102ac6454SAndrew Thompson 
188202ac6454SAndrew Thompson 	USB_IF_DEQUEUE(&f->free_q, m);
188302ac6454SAndrew Thompson 
188402ac6454SAndrew Thompson 	if (m) {
188502ac6454SAndrew Thompson 		m->cur_data_len = len;
188602ac6454SAndrew Thompson 		m->cur_data_ptr = ptr;
188702ac6454SAndrew Thompson 		USB_IF_ENQUEUE(&f->used_q, m);
188802ac6454SAndrew Thompson 		usb2_fifo_wakeup(f);
188902ac6454SAndrew Thompson 		return (1);
189002ac6454SAndrew Thompson 	}
189102ac6454SAndrew Thompson 	return (0);
189202ac6454SAndrew Thompson }
189302ac6454SAndrew Thompson 
189402ac6454SAndrew Thompson void
189502ac6454SAndrew Thompson usb2_fifo_put_data_error(struct usb2_fifo *f)
189602ac6454SAndrew Thompson {
189702ac6454SAndrew Thompson 	f->flag_iserror = 1;
189802ac6454SAndrew Thompson 	usb2_fifo_wakeup(f);
189902ac6454SAndrew Thompson }
190002ac6454SAndrew Thompson 
190102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
190202ac6454SAndrew Thompson  *	usb2_fifo_get_data
190302ac6454SAndrew Thompson  *
190402ac6454SAndrew Thompson  * what:
190502ac6454SAndrew Thompson  *  0 - normal operation
190602ac6454SAndrew Thompson  *  1 - only get one "usb2_mbuf"
190702ac6454SAndrew Thompson  *
190802ac6454SAndrew Thompson  * returns:
190902ac6454SAndrew Thompson  *  0 - no more data
191002ac6454SAndrew Thompson  *  1 - data in buffer
191102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
191202ac6454SAndrew Thompson uint8_t
191302ac6454SAndrew Thompson usb2_fifo_get_data(struct usb2_fifo *f, struct usb2_page_cache *pc,
191402ac6454SAndrew Thompson     uint32_t offset, uint32_t len, uint32_t *actlen,
191502ac6454SAndrew Thompson     uint8_t what)
191602ac6454SAndrew Thompson {
191702ac6454SAndrew Thompson 	struct usb2_mbuf *m;
191802ac6454SAndrew Thompson 	uint32_t io_len;
191902ac6454SAndrew Thompson 	uint8_t tr_data = 0;
192002ac6454SAndrew Thompson 
192102ac6454SAndrew Thompson 	actlen[0] = 0;
192202ac6454SAndrew Thompson 
192302ac6454SAndrew Thompson 	while (1) {
192402ac6454SAndrew Thompson 
192502ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
192602ac6454SAndrew Thompson 
192702ac6454SAndrew Thompson 		if (m) {
192802ac6454SAndrew Thompson 
192902ac6454SAndrew Thompson 			tr_data = 1;
193002ac6454SAndrew Thompson 
193102ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
193202ac6454SAndrew Thompson 
193302ac6454SAndrew Thompson 			usb2_copy_in(pc, offset, m->cur_data_ptr, io_len);
193402ac6454SAndrew Thompson 
193502ac6454SAndrew Thompson 			len -= io_len;
193602ac6454SAndrew Thompson 			offset += io_len;
193702ac6454SAndrew Thompson 			actlen[0] += io_len;
193802ac6454SAndrew Thompson 			m->cur_data_ptr += io_len;
193902ac6454SAndrew Thompson 			m->cur_data_len -= io_len;
194002ac6454SAndrew Thompson 
194102ac6454SAndrew Thompson 			if ((m->cur_data_len == 0) || (what == 1)) {
194202ac6454SAndrew Thompson 				USB_IF_ENQUEUE(&f->free_q, m);
194302ac6454SAndrew Thompson 
194402ac6454SAndrew Thompson 				usb2_fifo_wakeup(f);
194502ac6454SAndrew Thompson 
194602ac6454SAndrew Thompson 				if (what == 1) {
194702ac6454SAndrew Thompson 					break;
194802ac6454SAndrew Thompson 				}
194902ac6454SAndrew Thompson 			} else {
195002ac6454SAndrew Thompson 				USB_IF_PREPEND(&f->used_q, m);
195102ac6454SAndrew Thompson 			}
195202ac6454SAndrew Thompson 		} else {
195302ac6454SAndrew Thompson 
195402ac6454SAndrew Thompson 			if (tr_data) {
195502ac6454SAndrew Thompson 				/* wait for data to be written out */
195602ac6454SAndrew Thompson 				break;
195702ac6454SAndrew Thompson 			}
195802ac6454SAndrew Thompson 			if (f->flag_flushing) {
195902ac6454SAndrew Thompson 				f->flag_flushing = 0;
196002ac6454SAndrew Thompson 				usb2_fifo_wakeup(f);
196102ac6454SAndrew Thompson 			}
196202ac6454SAndrew Thompson 			break;
196302ac6454SAndrew Thompson 		}
196402ac6454SAndrew Thompson 		if (len == 0) {
196502ac6454SAndrew Thompson 			break;
196602ac6454SAndrew Thompson 		}
196702ac6454SAndrew Thompson 	}
196802ac6454SAndrew Thompson 	return (tr_data);
196902ac6454SAndrew Thompson }
197002ac6454SAndrew Thompson 
197102ac6454SAndrew Thompson uint8_t
197202ac6454SAndrew Thompson usb2_fifo_get_data_linear(struct usb2_fifo *f, void *ptr,
197302ac6454SAndrew Thompson     uint32_t len, uint32_t *actlen, uint8_t what)
197402ac6454SAndrew Thompson {
197502ac6454SAndrew Thompson 	struct usb2_mbuf *m;
197602ac6454SAndrew Thompson 	uint32_t io_len;
197702ac6454SAndrew Thompson 	uint8_t tr_data = 0;
197802ac6454SAndrew Thompson 
197902ac6454SAndrew Thompson 	actlen[0] = 0;
198002ac6454SAndrew Thompson 
198102ac6454SAndrew Thompson 	while (1) {
198202ac6454SAndrew Thompson 
198302ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
198402ac6454SAndrew Thompson 
198502ac6454SAndrew Thompson 		if (m) {
198602ac6454SAndrew Thompson 
198702ac6454SAndrew Thompson 			tr_data = 1;
198802ac6454SAndrew Thompson 
198902ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
199002ac6454SAndrew Thompson 
199102ac6454SAndrew Thompson 			bcopy(m->cur_data_ptr, ptr, io_len);
199202ac6454SAndrew Thompson 
199302ac6454SAndrew Thompson 			len -= io_len;
199402ac6454SAndrew Thompson 			ptr = USB_ADD_BYTES(ptr, io_len);
199502ac6454SAndrew Thompson 			actlen[0] += io_len;
199602ac6454SAndrew Thompson 			m->cur_data_ptr += io_len;
199702ac6454SAndrew Thompson 			m->cur_data_len -= io_len;
199802ac6454SAndrew Thompson 
199902ac6454SAndrew Thompson 			if ((m->cur_data_len == 0) || (what == 1)) {
200002ac6454SAndrew Thompson 				USB_IF_ENQUEUE(&f->free_q, m);
200102ac6454SAndrew Thompson 
200202ac6454SAndrew Thompson 				usb2_fifo_wakeup(f);
200302ac6454SAndrew Thompson 
200402ac6454SAndrew Thompson 				if (what == 1) {
200502ac6454SAndrew Thompson 					break;
200602ac6454SAndrew Thompson 				}
200702ac6454SAndrew Thompson 			} else {
200802ac6454SAndrew Thompson 				USB_IF_PREPEND(&f->used_q, m);
200902ac6454SAndrew Thompson 			}
201002ac6454SAndrew Thompson 		} else {
201102ac6454SAndrew Thompson 
201202ac6454SAndrew Thompson 			if (tr_data) {
201302ac6454SAndrew Thompson 				/* wait for data to be written out */
201402ac6454SAndrew Thompson 				break;
201502ac6454SAndrew Thompson 			}
201602ac6454SAndrew Thompson 			if (f->flag_flushing) {
201702ac6454SAndrew Thompson 				f->flag_flushing = 0;
201802ac6454SAndrew Thompson 				usb2_fifo_wakeup(f);
201902ac6454SAndrew Thompson 			}
202002ac6454SAndrew Thompson 			break;
202102ac6454SAndrew Thompson 		}
202202ac6454SAndrew Thompson 		if (len == 0) {
202302ac6454SAndrew Thompson 			break;
202402ac6454SAndrew Thompson 		}
202502ac6454SAndrew Thompson 	}
202602ac6454SAndrew Thompson 	return (tr_data);
202702ac6454SAndrew Thompson }
202802ac6454SAndrew Thompson 
202902ac6454SAndrew Thompson uint8_t
203002ac6454SAndrew Thompson usb2_fifo_get_data_buffer(struct usb2_fifo *f, void **pptr, uint32_t *plen)
203102ac6454SAndrew Thompson {
203202ac6454SAndrew Thompson 	struct usb2_mbuf *m;
203302ac6454SAndrew Thompson 
203402ac6454SAndrew Thompson 	USB_IF_POLL(&f->used_q, m);
203502ac6454SAndrew Thompson 
203602ac6454SAndrew Thompson 	if (m) {
203702ac6454SAndrew Thompson 		*plen = m->cur_data_len;
203802ac6454SAndrew Thompson 		*pptr = m->cur_data_ptr;
203902ac6454SAndrew Thompson 
204002ac6454SAndrew Thompson 		return (1);
204102ac6454SAndrew Thompson 	}
204202ac6454SAndrew Thompson 	return (0);
204302ac6454SAndrew Thompson }
204402ac6454SAndrew Thompson 
204502ac6454SAndrew Thompson void
204602ac6454SAndrew Thompson usb2_fifo_get_data_error(struct usb2_fifo *f)
204702ac6454SAndrew Thompson {
204802ac6454SAndrew Thompson 	f->flag_iserror = 1;
204902ac6454SAndrew Thompson 	usb2_fifo_wakeup(f);
205002ac6454SAndrew Thompson }
205102ac6454SAndrew Thompson 
205202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
205302ac6454SAndrew Thompson  *	usb2_alloc_symlink
205402ac6454SAndrew Thompson  *
205502ac6454SAndrew Thompson  * Return values:
205602ac6454SAndrew Thompson  * NULL: Failure
205702ac6454SAndrew Thompson  * Else: Pointer to symlink entry
205802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
205902ac6454SAndrew Thompson struct usb2_symlink *
2060ee3e3ff5SAndrew Thompson usb2_alloc_symlink(const char *target)
206102ac6454SAndrew Thompson {
206202ac6454SAndrew Thompson 	struct usb2_symlink *ps;
206302ac6454SAndrew Thompson 
206402ac6454SAndrew Thompson 	ps = malloc(sizeof(*ps), M_USBDEV, M_WAITOK);
206502ac6454SAndrew Thompson 	if (ps == NULL) {
206602ac6454SAndrew Thompson 		return (ps);
206702ac6454SAndrew Thompson 	}
2068ee3e3ff5SAndrew Thompson 	/* XXX no longer needed */
2069ee3e3ff5SAndrew Thompson 	strlcpy(ps->src_path, target, sizeof(ps->src_path));
2070ee3e3ff5SAndrew Thompson 	ps->src_len = strlen(ps->src_path);
207102ac6454SAndrew Thompson 	strlcpy(ps->dst_path, target, sizeof(ps->dst_path));
207202ac6454SAndrew Thompson 	ps->dst_len = strlen(ps->dst_path);
207302ac6454SAndrew Thompson 
207402ac6454SAndrew Thompson 	sx_xlock(&usb2_sym_lock);
207502ac6454SAndrew Thompson 	TAILQ_INSERT_TAIL(&usb2_sym_head, ps, sym_entry);
207602ac6454SAndrew Thompson 	sx_unlock(&usb2_sym_lock);
207702ac6454SAndrew Thompson 	return (ps);
207802ac6454SAndrew Thompson }
207902ac6454SAndrew Thompson 
208002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
208102ac6454SAndrew Thompson  *	usb2_free_symlink
208202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
208302ac6454SAndrew Thompson void
208402ac6454SAndrew Thompson usb2_free_symlink(struct usb2_symlink *ps)
208502ac6454SAndrew Thompson {
208602ac6454SAndrew Thompson 	if (ps == NULL) {
208702ac6454SAndrew Thompson 		return;
208802ac6454SAndrew Thompson 	}
208902ac6454SAndrew Thompson 	sx_xlock(&usb2_sym_lock);
209002ac6454SAndrew Thompson 	TAILQ_REMOVE(&usb2_sym_head, ps, sym_entry);
209102ac6454SAndrew Thompson 	sx_unlock(&usb2_sym_lock);
209202ac6454SAndrew Thompson 
209302ac6454SAndrew Thompson 	free(ps, M_USBDEV);
209402ac6454SAndrew Thompson }
209502ac6454SAndrew Thompson 
209602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
209702ac6454SAndrew Thompson  *	usb2_read_symlink
209802ac6454SAndrew Thompson  *
209902ac6454SAndrew Thompson  * Return value:
210002ac6454SAndrew Thompson  * 0: Success
210102ac6454SAndrew Thompson  * Else: Failure
210202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
210302ac6454SAndrew Thompson int
210402ac6454SAndrew Thompson usb2_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len)
210502ac6454SAndrew Thompson {
210602ac6454SAndrew Thompson 	struct usb2_symlink *ps;
210702ac6454SAndrew Thompson 	uint32_t temp;
210802ac6454SAndrew Thompson 	uint32_t delta = 0;
210902ac6454SAndrew Thompson 	uint8_t len;
211002ac6454SAndrew Thompson 	int error = 0;
211102ac6454SAndrew Thompson 
211202ac6454SAndrew Thompson 	sx_xlock(&usb2_sym_lock);
211302ac6454SAndrew Thompson 
211402ac6454SAndrew Thompson 	TAILQ_FOREACH(ps, &usb2_sym_head, sym_entry) {
211502ac6454SAndrew Thompson 
211602ac6454SAndrew Thompson 		/*
211702ac6454SAndrew Thompson 		 * Compute total length of source and destination symlink
211802ac6454SAndrew Thompson 		 * strings pluss one length byte and two NUL bytes:
211902ac6454SAndrew Thompson 		 */
212002ac6454SAndrew Thompson 		temp = ps->src_len + ps->dst_len + 3;
212102ac6454SAndrew Thompson 
212202ac6454SAndrew Thompson 		if (temp > 255) {
212302ac6454SAndrew Thompson 			/*
212402ac6454SAndrew Thompson 			 * Skip entry because this length cannot fit
212502ac6454SAndrew Thompson 			 * into one byte:
212602ac6454SAndrew Thompson 			 */
212702ac6454SAndrew Thompson 			continue;
212802ac6454SAndrew Thompson 		}
212902ac6454SAndrew Thompson 		if (startentry != 0) {
213002ac6454SAndrew Thompson 			/* decrement read offset */
213102ac6454SAndrew Thompson 			startentry--;
213202ac6454SAndrew Thompson 			continue;
213302ac6454SAndrew Thompson 		}
213402ac6454SAndrew Thompson 		if (temp > user_len) {
213502ac6454SAndrew Thompson 			/* out of buffer space */
213602ac6454SAndrew Thompson 			break;
213702ac6454SAndrew Thompson 		}
213802ac6454SAndrew Thompson 		len = temp;
213902ac6454SAndrew Thompson 
214002ac6454SAndrew Thompson 		/* copy out total length */
214102ac6454SAndrew Thompson 
214202ac6454SAndrew Thompson 		error = copyout(&len,
214302ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
214402ac6454SAndrew Thompson 		if (error) {
214502ac6454SAndrew Thompson 			break;
214602ac6454SAndrew Thompson 		}
214702ac6454SAndrew Thompson 		delta += 1;
214802ac6454SAndrew Thompson 
214902ac6454SAndrew Thompson 		/* copy out source string */
215002ac6454SAndrew Thompson 
215102ac6454SAndrew Thompson 		error = copyout(ps->src_path,
215202ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), ps->src_len);
215302ac6454SAndrew Thompson 		if (error) {
215402ac6454SAndrew Thompson 			break;
215502ac6454SAndrew Thompson 		}
215602ac6454SAndrew Thompson 		len = 0;
215702ac6454SAndrew Thompson 		delta += ps->src_len;
215802ac6454SAndrew Thompson 		error = copyout(&len,
215902ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
216002ac6454SAndrew Thompson 		if (error) {
216102ac6454SAndrew Thompson 			break;
216202ac6454SAndrew Thompson 		}
216302ac6454SAndrew Thompson 		delta += 1;
216402ac6454SAndrew Thompson 
216502ac6454SAndrew Thompson 		/* copy out destination string */
216602ac6454SAndrew Thompson 
216702ac6454SAndrew Thompson 		error = copyout(ps->dst_path,
216802ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), ps->dst_len);
216902ac6454SAndrew Thompson 		if (error) {
217002ac6454SAndrew Thompson 			break;
217102ac6454SAndrew Thompson 		}
217202ac6454SAndrew Thompson 		len = 0;
217302ac6454SAndrew Thompson 		delta += ps->dst_len;
217402ac6454SAndrew Thompson 		error = copyout(&len,
217502ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
217602ac6454SAndrew Thompson 		if (error) {
217702ac6454SAndrew Thompson 			break;
217802ac6454SAndrew Thompson 		}
217902ac6454SAndrew Thompson 		delta += 1;
218002ac6454SAndrew Thompson 
218102ac6454SAndrew Thompson 		user_len -= temp;
218202ac6454SAndrew Thompson 	}
218302ac6454SAndrew Thompson 
218402ac6454SAndrew Thompson 	/* a zero length entry indicates the end */
218502ac6454SAndrew Thompson 
218602ac6454SAndrew Thompson 	if ((user_len != 0) && (error == 0)) {
218702ac6454SAndrew Thompson 
218802ac6454SAndrew Thompson 		len = 0;
218902ac6454SAndrew Thompson 
219002ac6454SAndrew Thompson 		error = copyout(&len,
219102ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
219202ac6454SAndrew Thompson 	}
219302ac6454SAndrew Thompson 	sx_unlock(&usb2_sym_lock);
219402ac6454SAndrew Thompson 	return (error);
219502ac6454SAndrew Thompson }
2196