xref: /freebsd/sys/dev/usb/usb_dev.c (revision 760bc48e7ee4471fe04fa5fee89d00bf7d698ddb)
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_mfunc.h>
3302ac6454SAndrew Thompson #include <dev/usb/usb_error.h>
3402ac6454SAndrew Thompson 
3502ac6454SAndrew Thompson #define	USB_DEBUG_VAR usb2_fifo_debug
3602ac6454SAndrew Thompson 
3702ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
3802ac6454SAndrew Thompson #include <dev/usb/usb_mbuf.h>
3902ac6454SAndrew Thompson #include <dev/usb/usb_dev.h>
4002ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
4102ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
4202ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
4302ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
4402ac6454SAndrew Thompson #include <dev/usb/usb_generic.h>
4502ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h>
4602ac6454SAndrew Thompson #include <dev/usb/usb_util.h>
4702ac6454SAndrew Thompson 
4802ac6454SAndrew Thompson #include <dev/usb/usb_controller.h>
4902ac6454SAndrew Thompson #include <dev/usb/usb_bus.h>
5002ac6454SAndrew Thompson 
5102ac6454SAndrew Thompson #include <sys/filio.h>
5202ac6454SAndrew Thompson #include <sys/ttycom.h>
5302ac6454SAndrew Thompson #include <sys/syscallsubr.h>
5402ac6454SAndrew Thompson 
5502ac6454SAndrew Thompson #include <machine/stdarg.h>
5602ac6454SAndrew Thompson 
578755859aSAndrew Thompson #if USB_HAVE_UGEN
588755859aSAndrew Thompson 
5902ac6454SAndrew Thompson #if USB_DEBUG
6002ac6454SAndrew Thompson static int usb2_fifo_debug = 0;
6102ac6454SAndrew Thompson 
629360ae40SAndrew Thompson SYSCTL_NODE(_hw_usb, OID_AUTO, dev, CTLFLAG_RW, 0, "USB device");
639360ae40SAndrew Thompson SYSCTL_INT(_hw_usb_dev, OID_AUTO, debug, CTLFLAG_RW,
6402ac6454SAndrew Thompson     &usb2_fifo_debug, 0, "Debug Level");
6502ac6454SAndrew Thompson #endif
6602ac6454SAndrew Thompson 
6702ac6454SAndrew Thompson #if ((__FreeBSD_version >= 700001) || (__FreeBSD_version == 0) || \
6802ac6454SAndrew Thompson      ((__FreeBSD_version >= 600034) && (__FreeBSD_version < 700000)))
6902ac6454SAndrew Thompson #define	USB_UCRED struct ucred *ucred,
7002ac6454SAndrew Thompson #else
7102ac6454SAndrew Thompson #define	USB_UCRED
7202ac6454SAndrew Thompson #endif
7302ac6454SAndrew Thompson 
7402ac6454SAndrew Thompson /* prototypes */
7502ac6454SAndrew Thompson 
76760bc48eSAndrew Thompson static int	usb2_fifo_open(struct usb_cdev_privdata *,
77760bc48eSAndrew Thompson 		    struct usb_fifo *, int);
78760bc48eSAndrew Thompson static void	usb2_fifo_close(struct usb_fifo *, int);
7902ac6454SAndrew Thompson static void	usb2_dev_init(void *);
8002ac6454SAndrew Thompson static void	usb2_dev_init_post(void *);
8102ac6454SAndrew Thompson static void	usb2_dev_uninit(void *);
82760bc48eSAndrew Thompson static int	usb2_fifo_uiomove(struct usb_fifo *, void *, int,
8302ac6454SAndrew Thompson 		    struct uio *);
84760bc48eSAndrew Thompson static void	usb2_fifo_check_methods(struct usb_fifo_methods *);
85760bc48eSAndrew Thompson static struct	usb_fifo *usb2_fifo_alloc(void);
86760bc48eSAndrew Thompson static struct	usb_pipe *usb2_dev_get_pipe(struct usb_device *, uint8_t,
87f5f145baSAndrew Thompson 		    uint8_t);
88760bc48eSAndrew Thompson static void	usb2_loc_fill(struct usb_fs_privdata *,
89760bc48eSAndrew Thompson 		    struct usb_cdev_privdata *);
90ee3e3ff5SAndrew Thompson static void	usb2_close(void *);
91760bc48eSAndrew Thompson static usb2_error_t usb2_ref_device(struct usb_cdev_privdata *, int);
92760bc48eSAndrew Thompson static usb2_error_t usb2_usb_ref_device(struct usb_cdev_privdata *);
93760bc48eSAndrew Thompson static void	usb2_unref_device(struct usb_cdev_privdata *);
9402ac6454SAndrew Thompson 
95ee3e3ff5SAndrew Thompson static d_open_t usb2_open;
9602ac6454SAndrew Thompson static d_ioctl_t usb2_ioctl;
97ee3e3ff5SAndrew Thompson static d_read_t usb2_read;
98ee3e3ff5SAndrew Thompson static d_write_t usb2_write;
99ee3e3ff5SAndrew Thompson static d_poll_t usb2_poll;
10002ac6454SAndrew Thompson 
101ee3e3ff5SAndrew Thompson static d_ioctl_t usb2_static_ioctl;
10202ac6454SAndrew Thompson 
10302ac6454SAndrew Thompson static usb2_fifo_open_t usb2_fifo_dummy_open;
10402ac6454SAndrew Thompson static usb2_fifo_close_t usb2_fifo_dummy_close;
10502ac6454SAndrew Thompson static usb2_fifo_ioctl_t usb2_fifo_dummy_ioctl;
10602ac6454SAndrew Thompson static usb2_fifo_cmd_t usb2_fifo_dummy_cmd;
10702ac6454SAndrew Thompson 
108ee3e3ff5SAndrew Thompson /* character device structure used for devices (/dev/ugenX.Y and /dev/uXXX) */
109ee3e3ff5SAndrew Thompson struct cdevsw usb2_devsw = {
11002ac6454SAndrew Thompson 	.d_version = D_VERSION,
111ee3e3ff5SAndrew Thompson 	.d_open = usb2_open,
11202ac6454SAndrew Thompson 	.d_ioctl = usb2_ioctl,
113ee3e3ff5SAndrew Thompson 	.d_name = "usbdev",
11402ac6454SAndrew Thompson 	.d_flags = D_TRACKCLOSE,
115ee3e3ff5SAndrew Thompson 	.d_read = usb2_read,
116ee3e3ff5SAndrew Thompson 	.d_write = usb2_write,
117ee3e3ff5SAndrew Thompson 	.d_poll = usb2_poll
11802ac6454SAndrew Thompson };
11902ac6454SAndrew Thompson 
120ee3e3ff5SAndrew Thompson static struct cdev* usb2_dev = NULL;
121ee3e3ff5SAndrew Thompson 
122ee3e3ff5SAndrew Thompson /* character device structure used for /dev/usb */
123ee3e3ff5SAndrew Thompson struct cdevsw usb2_static_devsw = {
124ee3e3ff5SAndrew Thompson 	.d_version = D_VERSION,
125ee3e3ff5SAndrew Thompson 	.d_ioctl = usb2_static_ioctl,
126ee3e3ff5SAndrew Thompson 	.d_name = "usb"
12702ac6454SAndrew Thompson };
12802ac6454SAndrew Thompson 
129760bc48eSAndrew Thompson static TAILQ_HEAD(, usb_symlink) usb2_sym_head;
13002ac6454SAndrew Thompson static struct sx usb2_sym_lock;
13102ac6454SAndrew Thompson 
13202ac6454SAndrew Thompson struct mtx usb2_ref_lock;
13302ac6454SAndrew Thompson 
13402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
135ee3e3ff5SAndrew Thompson  *	usb2_loc_fill
13602ac6454SAndrew Thompson  *
137760bc48eSAndrew Thompson  * This is used to fill out a usb_cdev_privdata structure based on the
138760bc48eSAndrew Thompson  * device's address as contained in usb_fs_privdata.
13902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
140ee3e3ff5SAndrew Thompson static void
141760bc48eSAndrew Thompson usb2_loc_fill(struct usb_fs_privdata* pd, struct usb_cdev_privdata *cpd)
14202ac6454SAndrew Thompson {
143ee3e3ff5SAndrew Thompson 	cpd->bus_index = pd->bus_index;
144ee3e3ff5SAndrew Thompson 	cpd->dev_index = pd->dev_index;
145ee3e3ff5SAndrew Thompson 	cpd->ep_addr = pd->ep_addr;
146ee3e3ff5SAndrew Thompson 	cpd->fifo_index = pd->fifo_index;
14702ac6454SAndrew Thompson }
14802ac6454SAndrew Thompson 
14902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
15002ac6454SAndrew Thompson  *	usb2_ref_device
15102ac6454SAndrew Thompson  *
15202ac6454SAndrew Thompson  * This function is used to atomically refer an USB device by its
15302ac6454SAndrew Thompson  * device location. If this function returns success the USB device
15402ac6454SAndrew Thompson  * will not dissappear until the USB device is unreferenced.
15502ac6454SAndrew Thompson  *
15602ac6454SAndrew Thompson  * Return values:
15702ac6454SAndrew Thompson  *  0: Success, refcount incremented on the given USB device.
15802ac6454SAndrew Thompson  *  Else: Failure.
15902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
16002ac6454SAndrew Thompson usb2_error_t
161760bc48eSAndrew Thompson usb2_ref_device(struct usb_cdev_privdata* cpd, int need_uref)
16202ac6454SAndrew Thompson {
163760bc48eSAndrew Thompson 	struct usb_fifo **ppf;
164760bc48eSAndrew Thompson 	struct usb_fifo *f;
16502ac6454SAndrew Thompson 
166ee3e3ff5SAndrew Thompson 	DPRINTFN(2, "usb2_ref_device, cpd=%p need uref=%d\n", cpd, need_uref);
16702ac6454SAndrew Thompson 
16802ac6454SAndrew Thompson 	mtx_lock(&usb2_ref_lock);
169ee3e3ff5SAndrew Thompson 	cpd->bus = devclass_get_softc(usb2_devclass_ptr, cpd->bus_index);
170ee3e3ff5SAndrew Thompson 	if (cpd->bus == NULL) {
171ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "no bus at %u\n", cpd->bus_index);
17202ac6454SAndrew Thompson 		goto error;
17302ac6454SAndrew Thompson 	}
174ee3e3ff5SAndrew Thompson 	cpd->udev = cpd->bus->devices[cpd->dev_index];
175ee3e3ff5SAndrew Thompson 	if (cpd->udev == NULL) {
176ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "no device at %u\n", cpd->dev_index);
17702ac6454SAndrew Thompson 		goto error;
17802ac6454SAndrew Thompson 	}
179ee3e3ff5SAndrew Thompson 	if (cpd->udev->refcount == USB_DEV_REF_MAX) {
18002ac6454SAndrew Thompson 		DPRINTFN(2, "no dev ref\n");
18102ac6454SAndrew Thompson 		goto error;
18202ac6454SAndrew Thompson 	}
1830ed53d45SAndrew Thompson 	if (need_uref) {
1840ed53d45SAndrew Thompson 		DPRINTFN(2, "ref udev - needed\n");
1850ed53d45SAndrew Thompson 		cpd->udev->refcount++;
1860ed53d45SAndrew Thompson 		cpd->is_uref = 1;
1870ed53d45SAndrew Thompson 
1880ed53d45SAndrew Thompson 		mtx_unlock(&usb2_ref_lock);
1890ed53d45SAndrew Thompson 
1900ed53d45SAndrew Thompson 		/*
1910ed53d45SAndrew Thompson 		 * We need to grab the sx-lock before grabbing the
1920ed53d45SAndrew Thompson 		 * FIFO refs to avoid deadlock at detach!
1930ed53d45SAndrew Thompson 		 */
1940ed53d45SAndrew Thompson 		sx_xlock(cpd->udev->default_sx + 1);
1950ed53d45SAndrew Thompson 
1960ed53d45SAndrew Thompson 		mtx_lock(&usb2_ref_lock);
1970ed53d45SAndrew Thompson 	}
1980ed53d45SAndrew Thompson 
19902ac6454SAndrew Thompson 	/* check if we are doing an open */
200ee3e3ff5SAndrew Thompson 	if (cpd->fflags == 0) {
20102ac6454SAndrew Thompson 		/* set defaults */
202ee3e3ff5SAndrew Thompson 		cpd->txfifo = NULL;
203ee3e3ff5SAndrew Thompson 		cpd->rxfifo = NULL;
204ee3e3ff5SAndrew Thompson 		cpd->is_write = 0;
205ee3e3ff5SAndrew Thompson 		cpd->is_read = 0;
206ee3e3ff5SAndrew Thompson 		cpd->is_usbfs = 0;
20702ac6454SAndrew Thompson 	} else {
20802ac6454SAndrew Thompson 		/* initialise "is_usbfs" flag */
209ee3e3ff5SAndrew Thompson 		cpd->is_usbfs = 0;
21002ac6454SAndrew Thompson 
21102ac6454SAndrew Thompson 		/* check for write */
212ee3e3ff5SAndrew Thompson 		if (cpd->fflags & FWRITE) {
213ee3e3ff5SAndrew Thompson 			ppf = cpd->udev->fifo;
214ee3e3ff5SAndrew Thompson 			f = ppf[cpd->fifo_index + USB_FIFO_TX];
215ee3e3ff5SAndrew Thompson 			cpd->txfifo = f;
216ee3e3ff5SAndrew Thompson 			cpd->is_write = 1;	/* ref */
217ee3e3ff5SAndrew Thompson 			if (f == NULL || f->refcount == USB_FIFO_REF_MAX)
21802ac6454SAndrew Thompson 				goto error;
2197214348fSAndrew Thompson 			if (f->curr_cpd != cpd)
2207214348fSAndrew Thompson 				goto error;
22102ac6454SAndrew Thompson 			/* check if USB-FS is active */
22202ac6454SAndrew Thompson 			if (f->fs_ep_max != 0) {
223ee3e3ff5SAndrew Thompson 				cpd->is_usbfs = 1;
22402ac6454SAndrew Thompson 			}
22502ac6454SAndrew Thompson 		} else {
226ee3e3ff5SAndrew Thompson 			cpd->txfifo = NULL;
227ee3e3ff5SAndrew Thompson 			cpd->is_write = 0;	/* no ref */
22802ac6454SAndrew Thompson 		}
22902ac6454SAndrew Thompson 
23002ac6454SAndrew Thompson 		/* check for read */
231ee3e3ff5SAndrew Thompson 		if (cpd->fflags & FREAD) {
232ee3e3ff5SAndrew Thompson 			ppf = cpd->udev->fifo;
233ee3e3ff5SAndrew Thompson 			f = ppf[cpd->fifo_index + USB_FIFO_RX];
234ee3e3ff5SAndrew Thompson 			cpd->rxfifo = f;
235ee3e3ff5SAndrew Thompson 			cpd->is_read = 1;	/* ref */
236ee3e3ff5SAndrew Thompson 			if (f == NULL || f->refcount == USB_FIFO_REF_MAX)
23702ac6454SAndrew Thompson 				goto error;
2387214348fSAndrew Thompson 			if (f->curr_cpd != cpd)
2397214348fSAndrew Thompson 				goto error;
24002ac6454SAndrew Thompson 			/* check if USB-FS is active */
24102ac6454SAndrew Thompson 			if (f->fs_ep_max != 0) {
242ee3e3ff5SAndrew Thompson 				cpd->is_usbfs = 1;
24302ac6454SAndrew Thompson 			}
24402ac6454SAndrew Thompson 		} else {
245ee3e3ff5SAndrew Thompson 			cpd->rxfifo = NULL;
246ee3e3ff5SAndrew Thompson 			cpd->is_read = 0;	/* no ref */
24702ac6454SAndrew Thompson 		}
24802ac6454SAndrew Thompson 	}
24902ac6454SAndrew Thompson 
25002ac6454SAndrew Thompson 	/* when everything is OK we increment the refcounts */
251ee3e3ff5SAndrew Thompson 	if (cpd->is_write) {
25202ac6454SAndrew Thompson 		DPRINTFN(2, "ref write\n");
253ee3e3ff5SAndrew Thompson 		cpd->txfifo->refcount++;
25402ac6454SAndrew Thompson 	}
255ee3e3ff5SAndrew Thompson 	if (cpd->is_read) {
25602ac6454SAndrew Thompson 		DPRINTFN(2, "ref read\n");
257ee3e3ff5SAndrew Thompson 		cpd->rxfifo->refcount++;
25802ac6454SAndrew Thompson 	}
25902ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
26002ac6454SAndrew Thompson 
261ee3e3ff5SAndrew Thompson 	if (cpd->is_uref) {
26202ac6454SAndrew Thompson 		mtx_lock(&Giant);	/* XXX */
26302ac6454SAndrew Thompson 	}
26402ac6454SAndrew Thompson 	return (0);
26502ac6454SAndrew Thompson 
26602ac6454SAndrew Thompson error:
2670ed53d45SAndrew Thompson 	if (cpd->is_uref) {
2680ed53d45SAndrew Thompson 		sx_unlock(cpd->udev->default_sx + 1);
2690ed53d45SAndrew Thompson 		if (--(cpd->udev->refcount) == 0) {
2700ed53d45SAndrew Thompson 			usb2_cv_signal(cpd->udev->default_cv + 1);
2710ed53d45SAndrew Thompson 		}
2720ed53d45SAndrew Thompson 		cpd->is_uref = 0;
2730ed53d45SAndrew Thompson 	}
27402ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
27502ac6454SAndrew Thompson 	DPRINTFN(2, "fail\n");
27602ac6454SAndrew Thompson 	return (USB_ERR_INVAL);
27702ac6454SAndrew Thompson }
27802ac6454SAndrew Thompson 
27902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
2800ed53d45SAndrew Thompson  *	usb2_usb_ref_device
28102ac6454SAndrew Thompson  *
28202ac6454SAndrew Thompson  * This function is used to upgrade an USB reference to include the
28302ac6454SAndrew Thompson  * USB device reference on a USB location.
28402ac6454SAndrew Thompson  *
28502ac6454SAndrew Thompson  * Return values:
28602ac6454SAndrew Thompson  *  0: Success, refcount incremented on the given USB device.
28702ac6454SAndrew Thompson  *  Else: Failure.
28802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
28902ac6454SAndrew Thompson static usb2_error_t
290760bc48eSAndrew Thompson usb2_usb_ref_device(struct usb_cdev_privdata *cpd)
29102ac6454SAndrew Thompson {
29202ac6454SAndrew Thompson 	/*
29302ac6454SAndrew Thompson 	 * Check if we already got an USB reference on this location:
29402ac6454SAndrew Thompson 	 */
2950ed53d45SAndrew Thompson 	if (cpd->is_uref)
29602ac6454SAndrew Thompson 		return (0);		/* success */
29702ac6454SAndrew Thompson 
29802ac6454SAndrew Thompson 	/*
2990ed53d45SAndrew Thompson 	 * To avoid deadlock at detach we need to drop the FIFO ref
3000ed53d45SAndrew Thompson 	 * and re-acquire a new ref!
30102ac6454SAndrew Thompson 	 */
3020ed53d45SAndrew Thompson 	usb2_unref_device(cpd);
30302ac6454SAndrew Thompson 
3040ed53d45SAndrew Thompson 	return (usb2_ref_device(cpd, 1 /* need uref */));
30502ac6454SAndrew Thompson }
30602ac6454SAndrew Thompson 
30702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
30802ac6454SAndrew Thompson  *	usb2_unref_device
30902ac6454SAndrew Thompson  *
31002ac6454SAndrew Thompson  * This function will release the reference count by one unit for the
31102ac6454SAndrew Thompson  * given USB device.
31202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
31302ac6454SAndrew Thompson void
314760bc48eSAndrew Thompson usb2_unref_device(struct usb_cdev_privdata *cpd)
31502ac6454SAndrew Thompson {
316ee3e3ff5SAndrew Thompson 	if (cpd->is_uref) {
31702ac6454SAndrew Thompson 		mtx_unlock(&Giant);	/* XXX */
318ee3e3ff5SAndrew Thompson 		sx_unlock(cpd->udev->default_sx + 1);
31902ac6454SAndrew Thompson 	}
32002ac6454SAndrew Thompson 	mtx_lock(&usb2_ref_lock);
321ee3e3ff5SAndrew Thompson 	if (cpd->is_read) {
322ee3e3ff5SAndrew Thompson 		if (--(cpd->rxfifo->refcount) == 0) {
323ee3e3ff5SAndrew Thompson 			usb2_cv_signal(&cpd->rxfifo->cv_drain);
32402ac6454SAndrew Thompson 		}
325ee3e3ff5SAndrew Thompson 		cpd->is_read = 0;
32602ac6454SAndrew Thompson 	}
327ee3e3ff5SAndrew Thompson 	if (cpd->is_write) {
328ee3e3ff5SAndrew Thompson 		if (--(cpd->txfifo->refcount) == 0) {
329ee3e3ff5SAndrew Thompson 			usb2_cv_signal(&cpd->txfifo->cv_drain);
33002ac6454SAndrew Thompson 		}
331ee3e3ff5SAndrew Thompson 		cpd->is_write = 0;
33202ac6454SAndrew Thompson 	}
333ee3e3ff5SAndrew Thompson 	if (cpd->is_uref) {
334ee3e3ff5SAndrew Thompson 		if (--(cpd->udev->refcount) == 0) {
335ee3e3ff5SAndrew Thompson 			usb2_cv_signal(cpd->udev->default_cv + 1);
33602ac6454SAndrew Thompson 		}
337ee3e3ff5SAndrew Thompson 		cpd->is_uref = 0;
33802ac6454SAndrew Thompson 	}
33902ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
34002ac6454SAndrew Thompson }
34102ac6454SAndrew Thompson 
342760bc48eSAndrew Thompson static struct usb_fifo *
34302ac6454SAndrew Thompson usb2_fifo_alloc(void)
34402ac6454SAndrew Thompson {
345760bc48eSAndrew Thompson 	struct usb_fifo *f;
34602ac6454SAndrew Thompson 
34702ac6454SAndrew Thompson 	f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO);
34802ac6454SAndrew Thompson 	if (f) {
34902ac6454SAndrew Thompson 		usb2_cv_init(&f->cv_io, "FIFO-IO");
35002ac6454SAndrew Thompson 		usb2_cv_init(&f->cv_drain, "FIFO-DRAIN");
35102ac6454SAndrew Thompson 		f->refcount = 1;
35202ac6454SAndrew Thompson 	}
35302ac6454SAndrew Thompson 	return (f);
35402ac6454SAndrew Thompson }
35502ac6454SAndrew Thompson 
35602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
35702ac6454SAndrew Thompson  *	usb2_fifo_create
35802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
35902ac6454SAndrew Thompson static int
360760bc48eSAndrew Thompson usb2_fifo_create(struct usb_cdev_privdata *cpd)
36102ac6454SAndrew Thompson {
362760bc48eSAndrew Thompson 	struct usb_device *udev = cpd->udev;
363760bc48eSAndrew Thompson 	struct usb_fifo *f;
364760bc48eSAndrew Thompson 	struct usb_pipe *pipe;
36502ac6454SAndrew Thompson 	uint8_t n;
36602ac6454SAndrew Thompson 	uint8_t is_tx;
36702ac6454SAndrew Thompson 	uint8_t is_rx;
36802ac6454SAndrew Thompson 	uint8_t no_null;
36902ac6454SAndrew Thompson 	uint8_t is_busy;
370ee3e3ff5SAndrew Thompson 	int ep = cpd->ep_addr;
37102ac6454SAndrew Thompson 
372ee3e3ff5SAndrew Thompson 	is_tx = (cpd->fflags & FWRITE) ? 1 : 0;
373ee3e3ff5SAndrew Thompson 	is_rx = (cpd->fflags & FREAD) ? 1 : 0;
37402ac6454SAndrew Thompson 	no_null = 1;
37502ac6454SAndrew Thompson 	is_busy = 0;
37602ac6454SAndrew Thompson 
377ee3e3ff5SAndrew Thompson 	/* Preallocated FIFO */
378ee3e3ff5SAndrew Thompson 	if (ep < 0) {
379ee3e3ff5SAndrew Thompson 		DPRINTFN(5, "Preallocated FIFO\n");
380ee3e3ff5SAndrew Thompson 		if (is_tx) {
381ee3e3ff5SAndrew Thompson 			f = udev->fifo[cpd->fifo_index + USB_FIFO_TX];
382ee3e3ff5SAndrew Thompson 			if (f == NULL)
383ee3e3ff5SAndrew Thompson 				return (EINVAL);
384ee3e3ff5SAndrew Thompson 			cpd->txfifo = f;
385ee3e3ff5SAndrew Thompson 		}
386ee3e3ff5SAndrew Thompson 		if (is_rx) {
387ee3e3ff5SAndrew Thompson 			f = udev->fifo[cpd->fifo_index + USB_FIFO_RX];
388ee3e3ff5SAndrew Thompson 			if (f == NULL)
389ee3e3ff5SAndrew Thompson 				return (EINVAL);
390ee3e3ff5SAndrew Thompson 			cpd->rxfifo = f;
391ee3e3ff5SAndrew Thompson 		}
392ee3e3ff5SAndrew Thompson 		return (0);
393ee3e3ff5SAndrew Thompson 	}
39402ac6454SAndrew Thompson 
395ee3e3ff5SAndrew Thompson 	KASSERT(ep >= 0 && ep <= 15, ("endpoint %d out of range", ep));
396ee3e3ff5SAndrew Thompson 
397ee3e3ff5SAndrew Thompson 	/* search for a free FIFO slot */
398ee3e3ff5SAndrew Thompson 	DPRINTFN(5, "Endpoint device, searching for 0x%02x\n", ep);
39902ac6454SAndrew Thompson 	for (n = 0;; n += 2) {
40002ac6454SAndrew Thompson 
40102ac6454SAndrew Thompson 		if (n == USB_FIFO_MAX) {
40202ac6454SAndrew Thompson 			if (no_null) {
40302ac6454SAndrew Thompson 				no_null = 0;
40402ac6454SAndrew Thompson 				n = 0;
40502ac6454SAndrew Thompson 			} else {
40602ac6454SAndrew Thompson 				/* end of FIFOs reached */
407ee3e3ff5SAndrew Thompson 				DPRINTFN(5, "out of FIFOs\n");
40802ac6454SAndrew Thompson 				return (ENOMEM);
40902ac6454SAndrew Thompson 			}
41002ac6454SAndrew Thompson 		}
41102ac6454SAndrew Thompson 		/* Check for TX FIFO */
41202ac6454SAndrew Thompson 		if (is_tx) {
41302ac6454SAndrew Thompson 			f = udev->fifo[n + USB_FIFO_TX];
41402ac6454SAndrew Thompson 			if (f != NULL) {
415ee3e3ff5SAndrew Thompson 				if (f->dev_ep_index != ep) {
41602ac6454SAndrew Thompson 					/* wrong endpoint index */
41702ac6454SAndrew Thompson 					continue;
41802ac6454SAndrew Thompson 				}
4197214348fSAndrew Thompson 				if (f->curr_cpd != NULL) {
42002ac6454SAndrew Thompson 					/* FIFO is opened */
42102ac6454SAndrew Thompson 					is_busy = 1;
42202ac6454SAndrew Thompson 					continue;
42302ac6454SAndrew Thompson 				}
42402ac6454SAndrew Thompson 			} else if (no_null) {
42502ac6454SAndrew Thompson 				continue;
42602ac6454SAndrew Thompson 			}
42702ac6454SAndrew Thompson 		}
42802ac6454SAndrew Thompson 		/* Check for RX FIFO */
42902ac6454SAndrew Thompson 		if (is_rx) {
43002ac6454SAndrew Thompson 			f = udev->fifo[n + USB_FIFO_RX];
43102ac6454SAndrew Thompson 			if (f != NULL) {
432ee3e3ff5SAndrew Thompson 				if (f->dev_ep_index != ep) {
43302ac6454SAndrew Thompson 					/* wrong endpoint index */
43402ac6454SAndrew Thompson 					continue;
43502ac6454SAndrew Thompson 				}
4367214348fSAndrew Thompson 				if (f->curr_cpd != NULL) {
43702ac6454SAndrew Thompson 					/* FIFO is opened */
43802ac6454SAndrew Thompson 					is_busy = 1;
43902ac6454SAndrew Thompson 					continue;
44002ac6454SAndrew Thompson 				}
44102ac6454SAndrew Thompson 			} else if (no_null) {
44202ac6454SAndrew Thompson 				continue;
44302ac6454SAndrew Thompson 			}
44402ac6454SAndrew Thompson 		}
44502ac6454SAndrew Thompson 		break;
44602ac6454SAndrew Thompson 	}
44702ac6454SAndrew Thompson 
44802ac6454SAndrew Thompson 	if (no_null == 0) {
449ee3e3ff5SAndrew Thompson 		if (ep >= (USB_EP_MAX / 2)) {
45002ac6454SAndrew Thompson 			/* we don't create any endpoints in this range */
4517214348fSAndrew Thompson 			DPRINTFN(5, "ep out of range\n");
45202ac6454SAndrew Thompson 			return (is_busy ? EBUSY : EINVAL);
45302ac6454SAndrew Thompson 		}
45402ac6454SAndrew Thompson 	}
4557214348fSAndrew Thompson 
4567214348fSAndrew Thompson 	if ((ep != 0) && is_busy) {
4577214348fSAndrew Thompson 		/*
4587214348fSAndrew Thompson 		 * Only the default control endpoint is allowed to be
4597214348fSAndrew Thompson 		 * opened multiple times!
4607214348fSAndrew Thompson 		 */
4617214348fSAndrew Thompson 		DPRINTFN(5, "busy\n");
4627214348fSAndrew Thompson 		return (EBUSY);
4637214348fSAndrew Thompson 	}
4647214348fSAndrew Thompson 
46502ac6454SAndrew Thompson 	/* Check TX FIFO */
46602ac6454SAndrew Thompson 	if (is_tx &&
46702ac6454SAndrew Thompson 	    (udev->fifo[n + USB_FIFO_TX] == NULL)) {
468f35aaff0SAndrew Thompson 		pipe = usb2_dev_get_pipe(udev, ep, USB_FIFO_TX);
469f5f145baSAndrew Thompson 		DPRINTFN(5, "dev_get_pipe(%d, 0x%x)\n", ep, USB_FIFO_TX);
47002ac6454SAndrew Thompson 		if (pipe == NULL) {
471ee3e3ff5SAndrew Thompson 			DPRINTFN(5, "dev_get_pipe returned NULL\n");
47202ac6454SAndrew Thompson 			return (EINVAL);
47302ac6454SAndrew Thompson 		}
47402ac6454SAndrew Thompson 		f = usb2_fifo_alloc();
47502ac6454SAndrew Thompson 		if (f == NULL) {
476ee3e3ff5SAndrew Thompson 			DPRINTFN(5, "could not alloc tx fifo\n");
47702ac6454SAndrew Thompson 			return (ENOMEM);
47802ac6454SAndrew Thompson 		}
47902ac6454SAndrew Thompson 		/* update some fields */
48002ac6454SAndrew Thompson 		f->fifo_index = n + USB_FIFO_TX;
481ee3e3ff5SAndrew Thompson 		f->dev_ep_index = ep;
48202ac6454SAndrew Thompson 		f->priv_mtx = udev->default_mtx;
48302ac6454SAndrew Thompson 		f->priv_sc0 = pipe;
48402ac6454SAndrew Thompson 		f->methods = &usb2_ugen_methods;
485f5f145baSAndrew Thompson 		f->iface_index = pipe->iface_index;
48602ac6454SAndrew Thompson 		f->udev = udev;
48702ac6454SAndrew Thompson 		mtx_lock(&usb2_ref_lock);
48802ac6454SAndrew Thompson 		udev->fifo[n + USB_FIFO_TX] = f;
48902ac6454SAndrew Thompson 		mtx_unlock(&usb2_ref_lock);
49002ac6454SAndrew Thompson 	}
49102ac6454SAndrew Thompson 	/* Check RX FIFO */
49202ac6454SAndrew Thompson 	if (is_rx &&
49302ac6454SAndrew Thompson 	    (udev->fifo[n + USB_FIFO_RX] == NULL)) {
49402ac6454SAndrew Thompson 
495f35aaff0SAndrew Thompson 		pipe = usb2_dev_get_pipe(udev, ep, USB_FIFO_RX);
496f5f145baSAndrew Thompson 		DPRINTFN(5, "dev_get_pipe(%d, 0x%x)\n", ep, USB_FIFO_RX);
49702ac6454SAndrew Thompson 		if (pipe == NULL) {
498ee3e3ff5SAndrew Thompson 			DPRINTFN(5, "dev_get_pipe returned NULL\n");
49902ac6454SAndrew Thompson 			return (EINVAL);
50002ac6454SAndrew Thompson 		}
50102ac6454SAndrew Thompson 		f = usb2_fifo_alloc();
50202ac6454SAndrew Thompson 		if (f == NULL) {
503ee3e3ff5SAndrew Thompson 			DPRINTFN(5, "could not alloc rx fifo\n");
50402ac6454SAndrew Thompson 			return (ENOMEM);
50502ac6454SAndrew Thompson 		}
50602ac6454SAndrew Thompson 		/* update some fields */
50702ac6454SAndrew Thompson 		f->fifo_index = n + USB_FIFO_RX;
508ee3e3ff5SAndrew Thompson 		f->dev_ep_index = ep;
50902ac6454SAndrew Thompson 		f->priv_mtx = udev->default_mtx;
51002ac6454SAndrew Thompson 		f->priv_sc0 = pipe;
51102ac6454SAndrew Thompson 		f->methods = &usb2_ugen_methods;
512f5f145baSAndrew Thompson 		f->iface_index = pipe->iface_index;
51302ac6454SAndrew Thompson 		f->udev = udev;
51402ac6454SAndrew Thompson 		mtx_lock(&usb2_ref_lock);
51502ac6454SAndrew Thompson 		udev->fifo[n + USB_FIFO_RX] = f;
51602ac6454SAndrew Thompson 		mtx_unlock(&usb2_ref_lock);
51702ac6454SAndrew Thompson 	}
51802ac6454SAndrew Thompson 	if (is_tx) {
519ee3e3ff5SAndrew Thompson 		cpd->txfifo = udev->fifo[n + USB_FIFO_TX];
52002ac6454SAndrew Thompson 	}
52102ac6454SAndrew Thompson 	if (is_rx) {
522ee3e3ff5SAndrew Thompson 		cpd->rxfifo = udev->fifo[n + USB_FIFO_RX];
52302ac6454SAndrew Thompson 	}
524ee3e3ff5SAndrew Thompson 	/* fill out fifo index */
525ee3e3ff5SAndrew Thompson 	DPRINTFN(5, "fifo index = %d\n", n);
526ee3e3ff5SAndrew Thompson 	cpd->fifo_index = n;
52702ac6454SAndrew Thompson 
52802ac6454SAndrew Thompson 	/* complete */
52902ac6454SAndrew Thompson 
53002ac6454SAndrew Thompson 	return (0);
53102ac6454SAndrew Thompson }
53202ac6454SAndrew Thompson 
53302ac6454SAndrew Thompson void
534760bc48eSAndrew Thompson usb2_fifo_free(struct usb_fifo *f)
53502ac6454SAndrew Thompson {
53602ac6454SAndrew Thompson 	uint8_t n;
53702ac6454SAndrew Thompson 
53802ac6454SAndrew Thompson 	if (f == NULL) {
53902ac6454SAndrew Thompson 		/* be NULL safe */
54002ac6454SAndrew Thompson 		return;
54102ac6454SAndrew Thompson 	}
54202ac6454SAndrew Thompson 	/* destroy symlink devices, if any */
54302ac6454SAndrew Thompson 	for (n = 0; n != 2; n++) {
54402ac6454SAndrew Thompson 		if (f->symlink[n]) {
54502ac6454SAndrew Thompson 			usb2_free_symlink(f->symlink[n]);
54602ac6454SAndrew Thompson 			f->symlink[n] = NULL;
54702ac6454SAndrew Thompson 		}
54802ac6454SAndrew Thompson 	}
54902ac6454SAndrew Thompson 	mtx_lock(&usb2_ref_lock);
55002ac6454SAndrew Thompson 
55102ac6454SAndrew Thompson 	/* delink ourselves to stop calls from userland */
55202ac6454SAndrew Thompson 	if ((f->fifo_index < USB_FIFO_MAX) &&
55302ac6454SAndrew Thompson 	    (f->udev != NULL) &&
55402ac6454SAndrew Thompson 	    (f->udev->fifo[f->fifo_index] == f)) {
55502ac6454SAndrew Thompson 		f->udev->fifo[f->fifo_index] = NULL;
55602ac6454SAndrew Thompson 	} else {
55702ac6454SAndrew Thompson 		DPRINTFN(0, "USB FIFO %p has not been linked!\n", f);
55802ac6454SAndrew Thompson 	}
55902ac6454SAndrew Thompson 
56002ac6454SAndrew Thompson 	/* decrease refcount */
56102ac6454SAndrew Thompson 	f->refcount--;
56202ac6454SAndrew Thompson 	/* prevent any write flush */
56302ac6454SAndrew Thompson 	f->flag_iserror = 1;
56402ac6454SAndrew Thompson 	/* need to wait until all callers have exited */
56502ac6454SAndrew Thompson 	while (f->refcount != 0) {
56602ac6454SAndrew Thompson 		mtx_unlock(&usb2_ref_lock);	/* avoid LOR */
56702ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
56802ac6454SAndrew Thompson 		/* get I/O thread out of any sleep state */
56902ac6454SAndrew Thompson 		if (f->flag_sleeping) {
57002ac6454SAndrew Thompson 			f->flag_sleeping = 0;
57102ac6454SAndrew Thompson 			usb2_cv_broadcast(&f->cv_io);
57202ac6454SAndrew Thompson 		}
57302ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
57402ac6454SAndrew Thompson 		mtx_lock(&usb2_ref_lock);
57502ac6454SAndrew Thompson 
57602ac6454SAndrew Thompson 		/* wait for sync */
57702ac6454SAndrew Thompson 		usb2_cv_wait(&f->cv_drain, &usb2_ref_lock);
57802ac6454SAndrew Thompson 	}
57902ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
58002ac6454SAndrew Thompson 
58102ac6454SAndrew Thompson 	/* take care of closing the device here, if any */
582ee3e3ff5SAndrew Thompson 	usb2_fifo_close(f, 0);
58302ac6454SAndrew Thompson 
58402ac6454SAndrew Thompson 	usb2_cv_destroy(&f->cv_io);
58502ac6454SAndrew Thompson 	usb2_cv_destroy(&f->cv_drain);
58602ac6454SAndrew Thompson 
58702ac6454SAndrew Thompson 	free(f, M_USBDEV);
58802ac6454SAndrew Thompson }
58902ac6454SAndrew Thompson 
590760bc48eSAndrew Thompson static struct usb_pipe *
591760bc48eSAndrew Thompson usb2_dev_get_pipe(struct usb_device *udev, uint8_t ep_index, uint8_t dir)
59202ac6454SAndrew Thompson {
593760bc48eSAndrew Thompson 	struct usb_pipe *pipe;
59402ac6454SAndrew Thompson 	uint8_t ep_dir;
59502ac6454SAndrew Thompson 
59602ac6454SAndrew Thompson 	if (ep_index == 0) {
59702ac6454SAndrew Thompson 		pipe = &udev->default_pipe;
59802ac6454SAndrew Thompson 	} else {
59902ac6454SAndrew Thompson 		if (dir == USB_FIFO_RX) {
600f29a0724SAndrew Thompson 			if (udev->flags.usb_mode == USB_MODE_HOST) {
60102ac6454SAndrew Thompson 				ep_dir = UE_DIR_IN;
60202ac6454SAndrew Thompson 			} else {
60302ac6454SAndrew Thompson 				ep_dir = UE_DIR_OUT;
60402ac6454SAndrew Thompson 			}
60502ac6454SAndrew Thompson 		} else {
606f29a0724SAndrew Thompson 			if (udev->flags.usb_mode == USB_MODE_HOST) {
60702ac6454SAndrew Thompson 				ep_dir = UE_DIR_OUT;
60802ac6454SAndrew Thompson 			} else {
60902ac6454SAndrew Thompson 				ep_dir = UE_DIR_IN;
61002ac6454SAndrew Thompson 			}
61102ac6454SAndrew Thompson 		}
61202ac6454SAndrew Thompson 		pipe = usb2_get_pipe_by_addr(udev, ep_index | ep_dir);
61302ac6454SAndrew Thompson 	}
61402ac6454SAndrew Thompson 
61502ac6454SAndrew Thompson 	if (pipe == NULL) {
61602ac6454SAndrew Thompson 		/* if the pipe does not exist then return */
61702ac6454SAndrew Thompson 		return (NULL);
61802ac6454SAndrew Thompson 	}
61902ac6454SAndrew Thompson 	if (pipe->edesc == NULL) {
62002ac6454SAndrew Thompson 		/* invalid pipe */
62102ac6454SAndrew Thompson 		return (NULL);
62202ac6454SAndrew Thompson 	}
62302ac6454SAndrew Thompson 	return (pipe);			/* success */
62402ac6454SAndrew Thompson }
62502ac6454SAndrew Thompson 
62602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
62702ac6454SAndrew Thompson  *	usb2_fifo_open
62802ac6454SAndrew Thompson  *
62902ac6454SAndrew Thompson  * Returns:
63002ac6454SAndrew Thompson  * 0: Success
63102ac6454SAndrew Thompson  * Else: Failure
63202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
63302ac6454SAndrew Thompson static int
634760bc48eSAndrew Thompson usb2_fifo_open(struct usb_cdev_privdata *cpd,
635760bc48eSAndrew Thompson     struct usb_fifo *f, int fflags)
63602ac6454SAndrew Thompson {
63702ac6454SAndrew Thompson 	int err;
63802ac6454SAndrew Thompson 
63902ac6454SAndrew Thompson 	if (f == NULL) {
64002ac6454SAndrew Thompson 		/* no FIFO there */
64102ac6454SAndrew Thompson 		DPRINTFN(2, "no FIFO\n");
64202ac6454SAndrew Thompson 		return (ENXIO);
64302ac6454SAndrew Thompson 	}
64402ac6454SAndrew Thompson 	/* remove FWRITE and FREAD flags */
64502ac6454SAndrew Thompson 	fflags &= ~(FWRITE | FREAD);
64602ac6454SAndrew Thompson 
64702ac6454SAndrew Thompson 	/* set correct file flags */
64802ac6454SAndrew Thompson 	if ((f->fifo_index & 1) == USB_FIFO_TX) {
64902ac6454SAndrew Thompson 		fflags |= FWRITE;
65002ac6454SAndrew Thompson 	} else {
65102ac6454SAndrew Thompson 		fflags |= FREAD;
65202ac6454SAndrew Thompson 	}
65302ac6454SAndrew Thompson 
65402ac6454SAndrew Thompson 	/* check if we are already opened */
65502ac6454SAndrew Thompson 	/* we don't need any locks when checking this variable */
6567214348fSAndrew Thompson 	if (f->curr_cpd != NULL) {
65702ac6454SAndrew Thompson 		err = EBUSY;
65802ac6454SAndrew Thompson 		goto done;
65902ac6454SAndrew Thompson 	}
660ee3e3ff5SAndrew Thompson 
6617214348fSAndrew Thompson 	/* reset short flag before open */
6627214348fSAndrew Thompson 	f->flag_short = 0;
6637214348fSAndrew Thompson 
66402ac6454SAndrew Thompson 	/* call open method */
665ee3e3ff5SAndrew Thompson 	err = (f->methods->f_open) (f, fflags);
66602ac6454SAndrew Thompson 	if (err) {
66702ac6454SAndrew Thompson 		goto done;
66802ac6454SAndrew Thompson 	}
66902ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
67002ac6454SAndrew Thompson 
67102ac6454SAndrew Thompson 	/* reset sleep flag */
67202ac6454SAndrew Thompson 	f->flag_sleeping = 0;
67302ac6454SAndrew Thompson 
67402ac6454SAndrew Thompson 	/* reset error flag */
67502ac6454SAndrew Thompson 	f->flag_iserror = 0;
67602ac6454SAndrew Thompson 
67702ac6454SAndrew Thompson 	/* reset complete flag */
67802ac6454SAndrew Thompson 	f->flag_iscomplete = 0;
67902ac6454SAndrew Thompson 
68002ac6454SAndrew Thompson 	/* reset select flag */
68102ac6454SAndrew Thompson 	f->flag_isselect = 0;
68202ac6454SAndrew Thompson 
68302ac6454SAndrew Thompson 	/* reset flushing flag */
68402ac6454SAndrew Thompson 	f->flag_flushing = 0;
68502ac6454SAndrew Thompson 
68602ac6454SAndrew Thompson 	/* reset ASYNC proc flag */
68702ac6454SAndrew Thompson 	f->async_p = NULL;
68802ac6454SAndrew Thompson 
68902ac6454SAndrew Thompson 	mtx_lock(&usb2_ref_lock);
6907214348fSAndrew Thompson 	/* flag the fifo as opened to prevent others */
6917214348fSAndrew Thompson 	f->curr_cpd = cpd;
69202ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
69302ac6454SAndrew Thompson 
69402ac6454SAndrew Thompson 	/* reset queue */
69502ac6454SAndrew Thompson 	usb2_fifo_reset(f);
69602ac6454SAndrew Thompson 
69702ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
69802ac6454SAndrew Thompson done:
69902ac6454SAndrew Thompson 	return (err);
70002ac6454SAndrew Thompson }
70102ac6454SAndrew Thompson 
70202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
70302ac6454SAndrew Thompson  *	usb2_fifo_reset
70402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
70502ac6454SAndrew Thompson void
706760bc48eSAndrew Thompson usb2_fifo_reset(struct usb_fifo *f)
70702ac6454SAndrew Thompson {
708760bc48eSAndrew Thompson 	struct usb_mbuf *m;
70902ac6454SAndrew Thompson 
71002ac6454SAndrew Thompson 	if (f == NULL) {
71102ac6454SAndrew Thompson 		return;
71202ac6454SAndrew Thompson 	}
71302ac6454SAndrew Thompson 	while (1) {
71402ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
71502ac6454SAndrew Thompson 		if (m) {
71602ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->free_q, m);
71702ac6454SAndrew Thompson 		} else {
71802ac6454SAndrew Thompson 			break;
71902ac6454SAndrew Thompson 		}
72002ac6454SAndrew Thompson 	}
72102ac6454SAndrew Thompson }
72202ac6454SAndrew Thompson 
72302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
72402ac6454SAndrew Thompson  *	usb2_fifo_close
72502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
72602ac6454SAndrew Thompson static void
727760bc48eSAndrew Thompson usb2_fifo_close(struct usb_fifo *f, int fflags)
72802ac6454SAndrew Thompson {
72902ac6454SAndrew Thompson 	int err;
73002ac6454SAndrew Thompson 
73102ac6454SAndrew Thompson 	/* check if we are not opened */
7327214348fSAndrew Thompson 	if (f->curr_cpd == NULL) {
73302ac6454SAndrew Thompson 		/* nothing to do - already closed */
73402ac6454SAndrew Thompson 		return;
73502ac6454SAndrew Thompson 	}
73602ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
73702ac6454SAndrew Thompson 
7387214348fSAndrew Thompson 	/* clear current cdev private data pointer */
7397214348fSAndrew Thompson 	f->curr_cpd = NULL;
74002ac6454SAndrew Thompson 
74102ac6454SAndrew Thompson 	/* check if we are selected */
74202ac6454SAndrew Thompson 	if (f->flag_isselect) {
74302ac6454SAndrew Thompson 		selwakeup(&f->selinfo);
74402ac6454SAndrew Thompson 		f->flag_isselect = 0;
74502ac6454SAndrew Thompson 	}
74602ac6454SAndrew Thompson 	/* check if a thread wants SIGIO */
74702ac6454SAndrew Thompson 	if (f->async_p != NULL) {
74802ac6454SAndrew Thompson 		PROC_LOCK(f->async_p);
74902ac6454SAndrew Thompson 		psignal(f->async_p, SIGIO);
75002ac6454SAndrew Thompson 		PROC_UNLOCK(f->async_p);
75102ac6454SAndrew Thompson 		f->async_p = NULL;
75202ac6454SAndrew Thompson 	}
75302ac6454SAndrew Thompson 	/* remove FWRITE and FREAD flags */
75402ac6454SAndrew Thompson 	fflags &= ~(FWRITE | FREAD);
75502ac6454SAndrew Thompson 
75602ac6454SAndrew Thompson 	/* flush written data, if any */
75702ac6454SAndrew Thompson 	if ((f->fifo_index & 1) == USB_FIFO_TX) {
75802ac6454SAndrew Thompson 
75902ac6454SAndrew Thompson 		if (!f->flag_iserror) {
76002ac6454SAndrew Thompson 
76102ac6454SAndrew Thompson 			/* set flushing flag */
76202ac6454SAndrew Thompson 			f->flag_flushing = 1;
76302ac6454SAndrew Thompson 
76402ac6454SAndrew Thompson 			/* start write transfer, if not already started */
76502ac6454SAndrew Thompson 			(f->methods->f_start_write) (f);
76602ac6454SAndrew Thompson 
76702ac6454SAndrew Thompson 			/* check if flushed already */
76802ac6454SAndrew Thompson 			while (f->flag_flushing &&
76902ac6454SAndrew Thompson 			    (!f->flag_iserror)) {
77002ac6454SAndrew Thompson 				/* wait until all data has been written */
77102ac6454SAndrew Thompson 				f->flag_sleeping = 1;
77202ac6454SAndrew Thompson 				err = usb2_cv_wait_sig(&f->cv_io, f->priv_mtx);
77302ac6454SAndrew Thompson 				if (err) {
77402ac6454SAndrew Thompson 					DPRINTF("signal received\n");
77502ac6454SAndrew Thompson 					break;
77602ac6454SAndrew Thompson 				}
77702ac6454SAndrew Thompson 			}
77802ac6454SAndrew Thompson 		}
77902ac6454SAndrew Thompson 		fflags |= FWRITE;
78002ac6454SAndrew Thompson 
78102ac6454SAndrew Thompson 		/* stop write transfer, if not already stopped */
78202ac6454SAndrew Thompson 		(f->methods->f_stop_write) (f);
78302ac6454SAndrew Thompson 	} else {
78402ac6454SAndrew Thompson 		fflags |= FREAD;
78502ac6454SAndrew Thompson 
78602ac6454SAndrew Thompson 		/* stop write transfer, if not already stopped */
78702ac6454SAndrew Thompson 		(f->methods->f_stop_read) (f);
78802ac6454SAndrew Thompson 	}
78902ac6454SAndrew Thompson 
79002ac6454SAndrew Thompson 	/* check if we are sleeping */
79102ac6454SAndrew Thompson 	if (f->flag_sleeping) {
79202ac6454SAndrew Thompson 		DPRINTFN(2, "Sleeping at close!\n");
79302ac6454SAndrew Thompson 	}
79402ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
79502ac6454SAndrew Thompson 
79602ac6454SAndrew Thompson 	/* call close method */
797ee3e3ff5SAndrew Thompson 	(f->methods->f_close) (f, fflags);
79802ac6454SAndrew Thompson 
79902ac6454SAndrew Thompson 	DPRINTF("closed\n");
80002ac6454SAndrew Thompson }
80102ac6454SAndrew Thompson 
80202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
803ee3e3ff5SAndrew Thompson  *	usb2_open - cdev callback
80402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
80502ac6454SAndrew Thompson static int
806ee3e3ff5SAndrew Thompson usb2_open(struct cdev *dev, int fflags, int devtype, struct thread *td)
80702ac6454SAndrew Thompson {
808760bc48eSAndrew Thompson 	struct usb_fs_privdata* pd = (struct usb_fs_privdata*)dev->si_drv1;
809760bc48eSAndrew Thompson 	struct usb_cdev_privdata *cpd;
810ee3e3ff5SAndrew Thompson 	int err, ep;
81102ac6454SAndrew Thompson 
812cba30496SAndrew Thompson 	DPRINTFN(2, "%s fflags=0x%08x\n", dev->si_name, fflags);
81302ac6454SAndrew Thompson 
814ee3e3ff5SAndrew Thompson 	KASSERT(fflags & (FREAD|FWRITE), ("invalid open flags"));
815ee3e3ff5SAndrew Thompson 	if (((fflags & FREAD) && !(pd->mode & FREAD)) ||
816ee3e3ff5SAndrew Thompson 	    ((fflags & FWRITE) && !(pd->mode & FWRITE))) {
817ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "access mode not supported\n");
81802ac6454SAndrew Thompson 		return (EPERM);
81902ac6454SAndrew Thompson 	}
820ee3e3ff5SAndrew Thompson 
821ee3e3ff5SAndrew Thompson 	cpd = malloc(sizeof(*cpd), M_USBDEV, M_WAITOK | M_ZERO);
822ee3e3ff5SAndrew Thompson 	ep = cpd->ep_addr = pd->ep_addr;
823ee3e3ff5SAndrew Thompson 
824ee3e3ff5SAndrew Thompson 	usb2_loc_fill(pd, cpd);
825ee3e3ff5SAndrew Thompson 	err = usb2_ref_device(cpd, 1);
82602ac6454SAndrew Thompson 	if (err) {
82702ac6454SAndrew Thompson 		DPRINTFN(2, "cannot ref device\n");
828ee3e3ff5SAndrew Thompson 		free(cpd, M_USBDEV);
82902ac6454SAndrew Thompson 		return (ENXIO);
83002ac6454SAndrew Thompson 	}
831ee3e3ff5SAndrew Thompson 	cpd->fflags = fflags;	/* access mode for open lifetime */
83202ac6454SAndrew Thompson 
83302ac6454SAndrew Thompson 	/* create FIFOs, if any */
834ee3e3ff5SAndrew Thompson 	err = usb2_fifo_create(cpd);
83502ac6454SAndrew Thompson 	/* check for error */
83602ac6454SAndrew Thompson 	if (err) {
837ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "cannot create fifo\n");
838ee3e3ff5SAndrew Thompson 		usb2_unref_device(cpd);
839ee3e3ff5SAndrew Thompson 		free(cpd, M_USBDEV);
84002ac6454SAndrew Thompson 		return (err);
84102ac6454SAndrew Thompson 	}
84202ac6454SAndrew Thompson 	if (fflags & FREAD) {
8437214348fSAndrew Thompson 		err = usb2_fifo_open(cpd, cpd->rxfifo, fflags);
84402ac6454SAndrew Thompson 		if (err) {
84502ac6454SAndrew Thompson 			DPRINTFN(2, "read open failed\n");
846ee3e3ff5SAndrew Thompson 			usb2_unref_device(cpd);
847ee3e3ff5SAndrew Thompson 			free(cpd, M_USBDEV);
84802ac6454SAndrew Thompson 			return (err);
84902ac6454SAndrew Thompson 		}
85002ac6454SAndrew Thompson 	}
85102ac6454SAndrew Thompson 	if (fflags & FWRITE) {
8527214348fSAndrew Thompson 		err = usb2_fifo_open(cpd, cpd->txfifo, fflags);
85302ac6454SAndrew Thompson 		if (err) {
85402ac6454SAndrew Thompson 			DPRINTFN(2, "write open failed\n");
85502ac6454SAndrew Thompson 			if (fflags & FREAD) {
856ee3e3ff5SAndrew Thompson 				usb2_fifo_close(cpd->rxfifo, fflags);
85702ac6454SAndrew Thompson 			}
858ee3e3ff5SAndrew Thompson 			usb2_unref_device(cpd);
859ee3e3ff5SAndrew Thompson 			free(cpd, M_USBDEV);
86002ac6454SAndrew Thompson 			return (err);
86102ac6454SAndrew Thompson 		}
86202ac6454SAndrew Thompson 	}
863ee3e3ff5SAndrew Thompson 	usb2_unref_device(cpd);
864ee3e3ff5SAndrew Thompson 	devfs_set_cdevpriv(cpd, usb2_close);
86502ac6454SAndrew Thompson 
866ee3e3ff5SAndrew Thompson 	return (0);
86702ac6454SAndrew Thompson }
86802ac6454SAndrew Thompson 
86902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
87002ac6454SAndrew Thompson  *	usb2_close - cdev callback
87102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
87202ac6454SAndrew Thompson static void
873ee3e3ff5SAndrew Thompson usb2_close(void *arg)
87402ac6454SAndrew Thompson {
875760bc48eSAndrew Thompson 	struct usb_cdev_privdata *cpd = arg;
876ee3e3ff5SAndrew Thompson 	int err;
87702ac6454SAndrew Thompson 
8787214348fSAndrew Thompson 	DPRINTFN(2, "cpd=%p\n", cpd);
879ee3e3ff5SAndrew Thompson 
880ee3e3ff5SAndrew Thompson 	err = usb2_ref_device(cpd, 1);
881ee3e3ff5SAndrew Thompson 	if (err) {
882ee3e3ff5SAndrew Thompson 		free(cpd, M_USBDEV);
88302ac6454SAndrew Thompson 		return;
88402ac6454SAndrew Thompson 	}
885ee3e3ff5SAndrew Thompson 	if (cpd->fflags & FREAD) {
886ee3e3ff5SAndrew Thompson 		usb2_fifo_close(cpd->rxfifo, cpd->fflags);
88702ac6454SAndrew Thompson 	}
888ee3e3ff5SAndrew Thompson 	if (cpd->fflags & FWRITE) {
889ee3e3ff5SAndrew Thompson 		usb2_fifo_close(cpd->txfifo, cpd->fflags);
89002ac6454SAndrew Thompson 	}
891ee3e3ff5SAndrew Thompson 
892ee3e3ff5SAndrew Thompson 	usb2_unref_device(cpd);
893ee3e3ff5SAndrew Thompson 	free(cpd, M_USBDEV);
89402ac6454SAndrew Thompson 	return;
89502ac6454SAndrew Thompson }
89602ac6454SAndrew Thompson 
89702ac6454SAndrew Thompson static void
89802ac6454SAndrew Thompson usb2_dev_init(void *arg)
89902ac6454SAndrew Thompson {
90002ac6454SAndrew Thompson 	mtx_init(&usb2_ref_lock, "USB ref mutex", NULL, MTX_DEF);
90102ac6454SAndrew Thompson 	sx_init(&usb2_sym_lock, "USB sym mutex");
90202ac6454SAndrew Thompson 	TAILQ_INIT(&usb2_sym_head);
90302ac6454SAndrew Thompson 
90402ac6454SAndrew Thompson 	/* check the UGEN methods */
90502ac6454SAndrew Thompson 	usb2_fifo_check_methods(&usb2_ugen_methods);
90602ac6454SAndrew Thompson }
90702ac6454SAndrew Thompson 
90802ac6454SAndrew Thompson SYSINIT(usb2_dev_init, SI_SUB_KLD, SI_ORDER_FIRST, usb2_dev_init, NULL);
90902ac6454SAndrew Thompson 
91002ac6454SAndrew Thompson static void
91102ac6454SAndrew Thompson usb2_dev_init_post(void *arg)
91202ac6454SAndrew Thompson {
91302ac6454SAndrew Thompson 	/*
914ee3e3ff5SAndrew Thompson 	 * Create /dev/usb - this is needed for usbconfig(8), which
915ee3e3ff5SAndrew Thompson 	 * needs a well-known device name to access.
91602ac6454SAndrew Thompson 	 */
917ee3e3ff5SAndrew Thompson 	usb2_dev = make_dev(&usb2_static_devsw, 0, UID_ROOT, GID_OPERATOR,
918ee3e3ff5SAndrew Thompson 	    0644, USB_DEVICE_NAME);
91902ac6454SAndrew Thompson 	if (usb2_dev == NULL) {
92002ac6454SAndrew Thompson 		DPRINTFN(0, "Could not create usb bus device!\n");
92102ac6454SAndrew Thompson 	}
92202ac6454SAndrew Thompson }
92302ac6454SAndrew Thompson 
92402ac6454SAndrew Thompson SYSINIT(usb2_dev_init_post, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, usb2_dev_init_post, NULL);
92502ac6454SAndrew Thompson 
92602ac6454SAndrew Thompson static void
92702ac6454SAndrew Thompson usb2_dev_uninit(void *arg)
92802ac6454SAndrew Thompson {
929ee3e3ff5SAndrew Thompson 	if (usb2_dev != NULL) {
93002ac6454SAndrew Thompson 		destroy_dev(usb2_dev);
93102ac6454SAndrew Thompson 		usb2_dev = NULL;
932ee3e3ff5SAndrew Thompson 
93302ac6454SAndrew Thompson 	}
93402ac6454SAndrew Thompson 	mtx_destroy(&usb2_ref_lock);
93502ac6454SAndrew Thompson 	sx_destroy(&usb2_sym_lock);
93602ac6454SAndrew Thompson }
93702ac6454SAndrew Thompson 
93802ac6454SAndrew Thompson SYSUNINIT(usb2_dev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb2_dev_uninit, NULL);
93902ac6454SAndrew Thompson 
94002ac6454SAndrew Thompson static int
941760bc48eSAndrew Thompson usb2_ioctl_f_sub(struct usb_fifo *f, u_long cmd, void *addr,
94202ac6454SAndrew Thompson     struct thread *td)
94302ac6454SAndrew Thompson {
94402ac6454SAndrew Thompson 	int error = 0;
94502ac6454SAndrew Thompson 
94602ac6454SAndrew Thompson 	switch (cmd) {
94702ac6454SAndrew Thompson 	case FIODTYPE:
94802ac6454SAndrew Thompson 		*(int *)addr = 0;	/* character device */
94902ac6454SAndrew Thompson 		break;
95002ac6454SAndrew Thompson 
95102ac6454SAndrew Thompson 	case FIONBIO:
95202ac6454SAndrew Thompson 		/* handled by upper FS layer */
95302ac6454SAndrew Thompson 		break;
95402ac6454SAndrew Thompson 
95502ac6454SAndrew Thompson 	case FIOASYNC:
95602ac6454SAndrew Thompson 		if (*(int *)addr) {
95702ac6454SAndrew Thompson 			if (f->async_p != NULL) {
95802ac6454SAndrew Thompson 				error = EBUSY;
95902ac6454SAndrew Thompson 				break;
96002ac6454SAndrew Thompson 			}
96102ac6454SAndrew Thompson 			f->async_p = USB_TD_GET_PROC(td);
96202ac6454SAndrew Thompson 		} else {
96302ac6454SAndrew Thompson 			f->async_p = NULL;
96402ac6454SAndrew Thompson 		}
96502ac6454SAndrew Thompson 		break;
96602ac6454SAndrew Thompson 
96702ac6454SAndrew Thompson 		/* XXX this is not the most general solution */
96802ac6454SAndrew Thompson 	case TIOCSPGRP:
96902ac6454SAndrew Thompson 		if (f->async_p == NULL) {
97002ac6454SAndrew Thompson 			error = EINVAL;
97102ac6454SAndrew Thompson 			break;
97202ac6454SAndrew Thompson 		}
97302ac6454SAndrew Thompson 		if (*(int *)addr != USB_PROC_GET_GID(f->async_p)) {
97402ac6454SAndrew Thompson 			error = EPERM;
97502ac6454SAndrew Thompson 			break;
97602ac6454SAndrew Thompson 		}
97702ac6454SAndrew Thompson 		break;
97802ac6454SAndrew Thompson 	default:
97902ac6454SAndrew Thompson 		return (ENOIOCTL);
98002ac6454SAndrew Thompson 	}
981cba30496SAndrew Thompson 	DPRINTFN(3, "cmd 0x%lx = %d\n", cmd, error);
98202ac6454SAndrew Thompson 	return (error);
98302ac6454SAndrew Thompson }
98402ac6454SAndrew Thompson 
985ee3e3ff5SAndrew Thompson /*------------------------------------------------------------------------*
986ee3e3ff5SAndrew Thompson  *	usb2_ioctl - cdev callback
987ee3e3ff5SAndrew Thompson  *------------------------------------------------------------------------*/
98802ac6454SAndrew Thompson static int
989ee3e3ff5SAndrew Thompson usb2_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int fflag, struct thread* td)
99002ac6454SAndrew Thompson {
991760bc48eSAndrew Thompson 	struct usb_cdev_privdata* cpd;
992760bc48eSAndrew Thompson 	struct usb_fifo *f;
99302ac6454SAndrew Thompson 	int fflags;
99402ac6454SAndrew Thompson 	int err;
99502ac6454SAndrew Thompson 
996cba30496SAndrew Thompson 	DPRINTFN(2, "cmd=0x%lx\n", cmd);
997cba30496SAndrew Thompson 
998ee3e3ff5SAndrew Thompson 	err = devfs_get_cdevpriv((void **)&cpd);
999ee3e3ff5SAndrew Thompson 	if (err != 0)
1000ee3e3ff5SAndrew Thompson 		return (err);
1001ee3e3ff5SAndrew Thompson 
1002f5f145baSAndrew Thompson 	/*
1003f5f145baSAndrew Thompson 	 * Performance optimistaion: We try to check for IOCTL's that
1004f5f145baSAndrew Thompson 	 * don't need the USB reference first. Then we grab the USB
1005f5f145baSAndrew Thompson 	 * reference if we need it!
1006f5f145baSAndrew Thompson 	 */
1007f5f145baSAndrew Thompson 	err = usb2_ref_device(cpd, 0 /* no uref */ );
100802ac6454SAndrew Thompson 	if (err) {
100902ac6454SAndrew Thompson 		return (ENXIO);
101002ac6454SAndrew Thompson 	}
1011ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
101202ac6454SAndrew Thompson 
101302ac6454SAndrew Thompson 	f = NULL;			/* set default value */
101402ac6454SAndrew Thompson 	err = ENOIOCTL;			/* set default value */
101502ac6454SAndrew Thompson 
101602ac6454SAndrew Thompson 	if (fflags & FWRITE) {
1017ee3e3ff5SAndrew Thompson 		f = cpd->txfifo;
101802ac6454SAndrew Thompson 		err = usb2_ioctl_f_sub(f, cmd, addr, td);
101902ac6454SAndrew Thompson 	}
102002ac6454SAndrew Thompson 	if (fflags & FREAD) {
1021ee3e3ff5SAndrew Thompson 		f = cpd->rxfifo;
102202ac6454SAndrew Thompson 		err = usb2_ioctl_f_sub(f, cmd, addr, td);
102302ac6454SAndrew Thompson 	}
1024ee3e3ff5SAndrew Thompson 	KASSERT(f != NULL, ("fifo not found"));
102502ac6454SAndrew Thompson 	if (err == ENOIOCTL) {
1026ee3e3ff5SAndrew Thompson 		err = (f->methods->f_ioctl) (f, cmd, addr, fflags);
1027cba30496SAndrew Thompson 		DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err);
102802ac6454SAndrew Thompson 		if (err == ENOIOCTL) {
10290ed53d45SAndrew Thompson 			if (usb2_usb_ref_device(cpd)) {
103002ac6454SAndrew Thompson 				err = ENXIO;
103102ac6454SAndrew Thompson 				goto done;
103202ac6454SAndrew Thompson 			}
1033ee3e3ff5SAndrew Thompson 			err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags);
1034cba30496SAndrew Thompson 			DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err);
103502ac6454SAndrew Thompson 		}
103602ac6454SAndrew Thompson 	}
103702ac6454SAndrew Thompson 	if (err == ENOIOCTL) {
103802ac6454SAndrew Thompson 		err = ENOTTY;
103902ac6454SAndrew Thompson 	}
104002ac6454SAndrew Thompson done:
1041ee3e3ff5SAndrew Thompson 	usb2_unref_device(cpd);
104202ac6454SAndrew Thompson 	return (err);
104302ac6454SAndrew Thompson }
104402ac6454SAndrew Thompson 
104502ac6454SAndrew Thompson /* ARGSUSED */
104602ac6454SAndrew Thompson static int
1047ee3e3ff5SAndrew Thompson usb2_poll(struct cdev* dev, int events, struct thread* td)
104802ac6454SAndrew Thompson {
1049760bc48eSAndrew Thompson 	struct usb_cdev_privdata* cpd;
1050760bc48eSAndrew Thompson 	struct usb_fifo *f;
1051760bc48eSAndrew Thompson 	struct usb_mbuf *m;
10528a93629eSAndrew Thompson 	int fflags, revents;
105302ac6454SAndrew Thompson 
10548a93629eSAndrew Thompson 	if (devfs_get_cdevpriv((void **)&cpd) != 0 ||
10558a93629eSAndrew Thompson 	    usb2_ref_device(cpd, 0) != 0)
10568a93629eSAndrew Thompson 		return (events &
10578a93629eSAndrew Thompson 		    (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM));
1058ee3e3ff5SAndrew Thompson 
1059ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
106002ac6454SAndrew Thompson 
106102ac6454SAndrew Thompson 	/* Figure out who needs service */
1062ee3e3ff5SAndrew Thompson 	revents = 0;
106302ac6454SAndrew Thompson 	if ((events & (POLLOUT | POLLWRNORM)) &&
106402ac6454SAndrew Thompson 	    (fflags & FWRITE)) {
106502ac6454SAndrew Thompson 
1066ee3e3ff5SAndrew Thompson 		f = cpd->txfifo;
106702ac6454SAndrew Thompson 
106802ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
106902ac6454SAndrew Thompson 
1070ee3e3ff5SAndrew Thompson 		if (!cpd->is_usbfs) {
107102ac6454SAndrew Thompson 			if (f->flag_iserror) {
107202ac6454SAndrew Thompson 				/* we got an error */
107302ac6454SAndrew Thompson 				m = (void *)1;
107402ac6454SAndrew Thompson 			} else {
107502ac6454SAndrew Thompson 				if (f->queue_data == NULL) {
107602ac6454SAndrew Thompson 					/*
107702ac6454SAndrew Thompson 					 * start write transfer, if not
107802ac6454SAndrew Thompson 					 * already started
107902ac6454SAndrew Thompson 					 */
108002ac6454SAndrew Thompson 					(f->methods->f_start_write) (f);
108102ac6454SAndrew Thompson 				}
108202ac6454SAndrew Thompson 				/* check if any packets are available */
108302ac6454SAndrew Thompson 				USB_IF_POLL(&f->free_q, m);
108402ac6454SAndrew Thompson 			}
108502ac6454SAndrew Thompson 		} else {
108602ac6454SAndrew Thompson 			if (f->flag_iscomplete) {
108702ac6454SAndrew Thompson 				m = (void *)1;
108802ac6454SAndrew Thompson 			} else {
108902ac6454SAndrew Thompson 				m = NULL;
109002ac6454SAndrew Thompson 			}
109102ac6454SAndrew Thompson 		}
109202ac6454SAndrew Thompson 
109302ac6454SAndrew Thompson 		if (m) {
109402ac6454SAndrew Thompson 			revents |= events & (POLLOUT | POLLWRNORM);
109502ac6454SAndrew Thompson 		} else {
109602ac6454SAndrew Thompson 			f->flag_isselect = 1;
109702ac6454SAndrew Thompson 			selrecord(td, &f->selinfo);
109802ac6454SAndrew Thompson 		}
109902ac6454SAndrew Thompson 
110002ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
110102ac6454SAndrew Thompson 	}
110202ac6454SAndrew Thompson 	if ((events & (POLLIN | POLLRDNORM)) &&
110302ac6454SAndrew Thompson 	    (fflags & FREAD)) {
110402ac6454SAndrew Thompson 
1105ee3e3ff5SAndrew Thompson 		f = cpd->rxfifo;
110602ac6454SAndrew Thompson 
110702ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
110802ac6454SAndrew Thompson 
1109ee3e3ff5SAndrew Thompson 		if (!cpd->is_usbfs) {
111002ac6454SAndrew Thompson 			if (f->flag_iserror) {
111102ac6454SAndrew Thompson 				/* we have and error */
111202ac6454SAndrew Thompson 				m = (void *)1;
111302ac6454SAndrew Thompson 			} else {
111402ac6454SAndrew Thompson 				if (f->queue_data == NULL) {
111502ac6454SAndrew Thompson 					/*
111602ac6454SAndrew Thompson 					 * start read transfer, if not
111702ac6454SAndrew Thompson 					 * already started
111802ac6454SAndrew Thompson 					 */
111902ac6454SAndrew Thompson 					(f->methods->f_start_read) (f);
112002ac6454SAndrew Thompson 				}
112102ac6454SAndrew Thompson 				/* check if any packets are available */
112202ac6454SAndrew Thompson 				USB_IF_POLL(&f->used_q, m);
112302ac6454SAndrew Thompson 			}
112402ac6454SAndrew Thompson 		} else {
112502ac6454SAndrew Thompson 			if (f->flag_iscomplete) {
112602ac6454SAndrew Thompson 				m = (void *)1;
112702ac6454SAndrew Thompson 			} else {
112802ac6454SAndrew Thompson 				m = NULL;
112902ac6454SAndrew Thompson 			}
113002ac6454SAndrew Thompson 		}
113102ac6454SAndrew Thompson 
113202ac6454SAndrew Thompson 		if (m) {
113302ac6454SAndrew Thompson 			revents |= events & (POLLIN | POLLRDNORM);
113402ac6454SAndrew Thompson 		} else {
113502ac6454SAndrew Thompson 			f->flag_isselect = 1;
113602ac6454SAndrew Thompson 			selrecord(td, &f->selinfo);
113702ac6454SAndrew Thompson 
1138ee3e3ff5SAndrew Thompson 			if (!cpd->is_usbfs) {
113902ac6454SAndrew Thompson 				/* start reading data */
114002ac6454SAndrew Thompson 				(f->methods->f_start_read) (f);
114102ac6454SAndrew Thompson 			}
114202ac6454SAndrew Thompson 		}
114302ac6454SAndrew Thompson 
114402ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
114502ac6454SAndrew Thompson 	}
1146ee3e3ff5SAndrew Thompson 	usb2_unref_device(cpd);
114702ac6454SAndrew Thompson 	return (revents);
114802ac6454SAndrew Thompson }
114902ac6454SAndrew Thompson 
115002ac6454SAndrew Thompson static int
1151ee3e3ff5SAndrew Thompson usb2_read(struct cdev *dev, struct uio *uio, int ioflag)
115202ac6454SAndrew Thompson {
1153760bc48eSAndrew Thompson 	struct usb_cdev_privdata* cpd;
1154760bc48eSAndrew Thompson 	struct usb_fifo *f;
1155760bc48eSAndrew Thompson 	struct usb_mbuf *m;
115602ac6454SAndrew Thompson 	int fflags;
115702ac6454SAndrew Thompson 	int resid;
115802ac6454SAndrew Thompson 	int io_len;
115902ac6454SAndrew Thompson 	int err;
116002ac6454SAndrew Thompson 	uint8_t tr_data = 0;
116102ac6454SAndrew Thompson 
1162ee3e3ff5SAndrew Thompson 	err = devfs_get_cdevpriv((void **)&cpd);
1163ee3e3ff5SAndrew Thompson 	if (err != 0)
1164ee3e3ff5SAndrew Thompson 		return (err);
116502ac6454SAndrew Thompson 
1166ee3e3ff5SAndrew Thompson 	err = usb2_ref_device(cpd, 0 /* no uref */ );
116702ac6454SAndrew Thompson 	if (err) {
116802ac6454SAndrew Thompson 		return (ENXIO);
116902ac6454SAndrew Thompson 	}
1170ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
1171ee3e3ff5SAndrew Thompson 
1172ee3e3ff5SAndrew Thompson 	f = cpd->rxfifo;
117302ac6454SAndrew Thompson 	if (f == NULL) {
117402ac6454SAndrew Thompson 		/* should not happen */
117502ac6454SAndrew Thompson 		return (EPERM);
117602ac6454SAndrew Thompson 	}
117702ac6454SAndrew Thompson 
1178ee3e3ff5SAndrew Thompson 	resid = uio->uio_resid;
117902ac6454SAndrew Thompson 
118002ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
118102ac6454SAndrew Thompson 
118202ac6454SAndrew Thompson 	/* check for permanent read error */
118302ac6454SAndrew Thompson 	if (f->flag_iserror) {
118402ac6454SAndrew Thompson 		err = EIO;
118502ac6454SAndrew Thompson 		goto done;
118602ac6454SAndrew Thompson 	}
118702ac6454SAndrew Thompson 	/* check if USB-FS interface is active */
1188ee3e3ff5SAndrew Thompson 	if (cpd->is_usbfs) {
118902ac6454SAndrew Thompson 		/*
119002ac6454SAndrew Thompson 		 * The queue is used for events that should be
119102ac6454SAndrew Thompson 		 * retrieved using the "USB_FS_COMPLETE" ioctl.
119202ac6454SAndrew Thompson 		 */
119302ac6454SAndrew Thompson 		err = EINVAL;
119402ac6454SAndrew Thompson 		goto done;
119502ac6454SAndrew Thompson 	}
119602ac6454SAndrew Thompson 	while (uio->uio_resid > 0) {
119702ac6454SAndrew Thompson 
119802ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
119902ac6454SAndrew Thompson 
120002ac6454SAndrew Thompson 		if (m == NULL) {
120102ac6454SAndrew Thompson 
120202ac6454SAndrew Thompson 			/* start read transfer, if not already started */
120302ac6454SAndrew Thompson 
120402ac6454SAndrew Thompson 			(f->methods->f_start_read) (f);
120502ac6454SAndrew Thompson 
120607532e49SAndrew Thompson 			if (ioflag & IO_NDELAY) {
120702ac6454SAndrew Thompson 				if (tr_data) {
120802ac6454SAndrew Thompson 					/* return length before error */
120902ac6454SAndrew Thompson 					break;
121002ac6454SAndrew Thompson 				}
121102ac6454SAndrew Thompson 				err = EWOULDBLOCK;
121202ac6454SAndrew Thompson 				break;
121302ac6454SAndrew Thompson 			}
121402ac6454SAndrew Thompson 			DPRINTF("sleeping\n");
121502ac6454SAndrew Thompson 
121602ac6454SAndrew Thompson 			err = usb2_fifo_wait(f);
121702ac6454SAndrew Thompson 			if (err) {
121802ac6454SAndrew Thompson 				break;
121902ac6454SAndrew Thompson 			}
122002ac6454SAndrew Thompson 			continue;
122102ac6454SAndrew Thompson 		}
122202ac6454SAndrew Thompson 		if (f->methods->f_filter_read) {
122302ac6454SAndrew Thompson 			/*
122402ac6454SAndrew Thompson 			 * Sometimes it is convenient to process data at the
122502ac6454SAndrew Thompson 			 * expense of a userland process instead of a kernel
122602ac6454SAndrew Thompson 			 * process.
122702ac6454SAndrew Thompson 			 */
122802ac6454SAndrew Thompson 			(f->methods->f_filter_read) (f, m);
122902ac6454SAndrew Thompson 		}
123002ac6454SAndrew Thompson 		tr_data = 1;
123102ac6454SAndrew Thompson 
123202ac6454SAndrew Thompson 		io_len = MIN(m->cur_data_len, uio->uio_resid);
123302ac6454SAndrew Thompson 
123402ac6454SAndrew Thompson 		DPRINTFN(2, "transfer %d bytes from %p\n",
123502ac6454SAndrew Thompson 		    io_len, m->cur_data_ptr);
123602ac6454SAndrew Thompson 
123702ac6454SAndrew Thompson 		err = usb2_fifo_uiomove(f,
123802ac6454SAndrew Thompson 		    m->cur_data_ptr, io_len, uio);
123902ac6454SAndrew Thompson 
124002ac6454SAndrew Thompson 		m->cur_data_len -= io_len;
124102ac6454SAndrew Thompson 		m->cur_data_ptr += io_len;
124202ac6454SAndrew Thompson 
124302ac6454SAndrew Thompson 		if (m->cur_data_len == 0) {
124402ac6454SAndrew Thompson 
124502ac6454SAndrew Thompson 			uint8_t last_packet;
124602ac6454SAndrew Thompson 
124702ac6454SAndrew Thompson 			last_packet = m->last_packet;
124802ac6454SAndrew Thompson 
124902ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->free_q, m);
125002ac6454SAndrew Thompson 
125102ac6454SAndrew Thompson 			if (last_packet) {
125202ac6454SAndrew Thompson 				/* keep framing */
125302ac6454SAndrew Thompson 				break;
125402ac6454SAndrew Thompson 			}
125502ac6454SAndrew Thompson 		} else {
125602ac6454SAndrew Thompson 			USB_IF_PREPEND(&f->used_q, m);
125702ac6454SAndrew Thompson 		}
125802ac6454SAndrew Thompson 
125902ac6454SAndrew Thompson 		if (err) {
126002ac6454SAndrew Thompson 			break;
126102ac6454SAndrew Thompson 		}
126202ac6454SAndrew Thompson 	}
126302ac6454SAndrew Thompson done:
126402ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
126502ac6454SAndrew Thompson 
1266ee3e3ff5SAndrew Thompson 	usb2_unref_device(cpd);
126702ac6454SAndrew Thompson 
126802ac6454SAndrew Thompson 	return (err);
126902ac6454SAndrew Thompson }
127002ac6454SAndrew Thompson 
127102ac6454SAndrew Thompson static int
1272ee3e3ff5SAndrew Thompson usb2_write(struct cdev *dev, struct uio *uio, int ioflag)
127302ac6454SAndrew Thompson {
1274760bc48eSAndrew Thompson 	struct usb_cdev_privdata* cpd;
1275760bc48eSAndrew Thompson 	struct usb_fifo *f;
1276760bc48eSAndrew Thompson 	struct usb_mbuf *m;
127702ac6454SAndrew Thompson 	int fflags;
127802ac6454SAndrew Thompson 	int resid;
127902ac6454SAndrew Thompson 	int io_len;
128002ac6454SAndrew Thompson 	int err;
128102ac6454SAndrew Thompson 	uint8_t tr_data = 0;
128202ac6454SAndrew Thompson 
128302ac6454SAndrew Thompson 	DPRINTFN(2, "\n");
128402ac6454SAndrew Thompson 
1285ee3e3ff5SAndrew Thompson 	err = devfs_get_cdevpriv((void **)&cpd);
1286ee3e3ff5SAndrew Thompson 	if (err != 0)
1287ee3e3ff5SAndrew Thompson 		return (err);
128802ac6454SAndrew Thompson 
1289ee3e3ff5SAndrew Thompson 	err = usb2_ref_device(cpd, 0 /* no uref */ );
129002ac6454SAndrew Thompson 	if (err) {
129102ac6454SAndrew Thompson 		return (ENXIO);
129202ac6454SAndrew Thompson 	}
1293ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
1294ee3e3ff5SAndrew Thompson 
1295ee3e3ff5SAndrew Thompson 	f = cpd->txfifo;
129602ac6454SAndrew Thompson 	if (f == NULL) {
129702ac6454SAndrew Thompson 		/* should not happen */
1298ee3e3ff5SAndrew Thompson 		usb2_unref_device(cpd);
129902ac6454SAndrew Thompson 		return (EPERM);
130002ac6454SAndrew Thompson 	}
130102ac6454SAndrew Thompson 	resid = uio->uio_resid;
130202ac6454SAndrew Thompson 
130302ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
130402ac6454SAndrew Thompson 
130502ac6454SAndrew Thompson 	/* check for permanent write error */
130602ac6454SAndrew Thompson 	if (f->flag_iserror) {
130702ac6454SAndrew Thompson 		err = EIO;
130802ac6454SAndrew Thompson 		goto done;
130902ac6454SAndrew Thompson 	}
131002ac6454SAndrew Thompson 	/* check if USB-FS interface is active */
1311ee3e3ff5SAndrew Thompson 	if (cpd->is_usbfs) {
131202ac6454SAndrew Thompson 		/*
131302ac6454SAndrew Thompson 		 * The queue is used for events that should be
131402ac6454SAndrew Thompson 		 * retrieved using the "USB_FS_COMPLETE" ioctl.
131502ac6454SAndrew Thompson 		 */
131602ac6454SAndrew Thompson 		err = EINVAL;
131702ac6454SAndrew Thompson 		goto done;
131802ac6454SAndrew Thompson 	}
131902ac6454SAndrew Thompson 	if (f->queue_data == NULL) {
132002ac6454SAndrew Thompson 		/* start write transfer, if not already started */
132102ac6454SAndrew Thompson 		(f->methods->f_start_write) (f);
132202ac6454SAndrew Thompson 	}
132302ac6454SAndrew Thompson 	/* we allow writing zero length data */
132402ac6454SAndrew Thompson 	do {
132502ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->free_q, m);
132602ac6454SAndrew Thompson 
132702ac6454SAndrew Thompson 		if (m == NULL) {
132802ac6454SAndrew Thompson 
132907532e49SAndrew Thompson 			if (ioflag & IO_NDELAY) {
133002ac6454SAndrew Thompson 				if (tr_data) {
133102ac6454SAndrew Thompson 					/* return length before error */
133202ac6454SAndrew Thompson 					break;
133302ac6454SAndrew Thompson 				}
133402ac6454SAndrew Thompson 				err = EWOULDBLOCK;
133502ac6454SAndrew Thompson 				break;
133602ac6454SAndrew Thompson 			}
133702ac6454SAndrew Thompson 			DPRINTF("sleeping\n");
133802ac6454SAndrew Thompson 
133902ac6454SAndrew Thompson 			err = usb2_fifo_wait(f);
134002ac6454SAndrew Thompson 			if (err) {
134102ac6454SAndrew Thompson 				break;
134202ac6454SAndrew Thompson 			}
134302ac6454SAndrew Thompson 			continue;
134402ac6454SAndrew Thompson 		}
134502ac6454SAndrew Thompson 		tr_data = 1;
134602ac6454SAndrew Thompson 
134702ac6454SAndrew Thompson 		USB_MBUF_RESET(m);
134802ac6454SAndrew Thompson 
134902ac6454SAndrew Thompson 		io_len = MIN(m->cur_data_len, uio->uio_resid);
135002ac6454SAndrew Thompson 
135102ac6454SAndrew Thompson 		m->cur_data_len = io_len;
135202ac6454SAndrew Thompson 
135302ac6454SAndrew Thompson 		DPRINTFN(2, "transfer %d bytes to %p\n",
135402ac6454SAndrew Thompson 		    io_len, m->cur_data_ptr);
135502ac6454SAndrew Thompson 
135602ac6454SAndrew Thompson 		err = usb2_fifo_uiomove(f,
135702ac6454SAndrew Thompson 		    m->cur_data_ptr, io_len, uio);
135802ac6454SAndrew Thompson 
135902ac6454SAndrew Thompson 		if (err) {
136002ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->free_q, m);
136102ac6454SAndrew Thompson 			break;
136202ac6454SAndrew Thompson 		}
136302ac6454SAndrew Thompson 		if (f->methods->f_filter_write) {
136402ac6454SAndrew Thompson 			/*
136502ac6454SAndrew Thompson 			 * Sometimes it is convenient to process data at the
136602ac6454SAndrew Thompson 			 * expense of a userland process instead of a kernel
136702ac6454SAndrew Thompson 			 * process.
136802ac6454SAndrew Thompson 			 */
136902ac6454SAndrew Thompson 			(f->methods->f_filter_write) (f, m);
137002ac6454SAndrew Thompson 		}
137102ac6454SAndrew Thompson 		USB_IF_ENQUEUE(&f->used_q, m);
137202ac6454SAndrew Thompson 
137302ac6454SAndrew Thompson 		(f->methods->f_start_write) (f);
137402ac6454SAndrew Thompson 
137502ac6454SAndrew Thompson 	} while (uio->uio_resid > 0);
137602ac6454SAndrew Thompson done:
137702ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
137802ac6454SAndrew Thompson 
1379ee3e3ff5SAndrew Thompson 	usb2_unref_device(cpd);
138002ac6454SAndrew Thompson 
1381ee3e3ff5SAndrew Thompson 	return (err);
1382ee3e3ff5SAndrew Thompson }
138302ac6454SAndrew Thompson 
1384ee3e3ff5SAndrew Thompson int
1385ee3e3ff5SAndrew Thompson usb2_static_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
1386ee3e3ff5SAndrew Thompson     struct thread *td)
1387ee3e3ff5SAndrew Thompson {
1388ee3e3ff5SAndrew Thompson 	union {
1389760bc48eSAndrew Thompson 		struct usb_read_dir *urd;
1390ee3e3ff5SAndrew Thompson 		void* data;
1391ee3e3ff5SAndrew Thompson 	} u;
1392ee3e3ff5SAndrew Thompson 	int err = ENOTTY;
1393ee3e3ff5SAndrew Thompson 
1394ee3e3ff5SAndrew Thompson 	u.data = data;
1395ee3e3ff5SAndrew Thompson 	switch (cmd) {
1396ee3e3ff5SAndrew Thompson 		case USB_READ_DIR:
1397ee3e3ff5SAndrew Thompson 			err = usb2_read_symlink(u.urd->urd_data,
1398ee3e3ff5SAndrew Thompson 			    u.urd->urd_startentry, u.urd->urd_maxlen);
1399ee3e3ff5SAndrew Thompson 			break;
1400ee3e3ff5SAndrew Thompson 		case USB_DEV_QUIRK_GET:
1401ee3e3ff5SAndrew Thompson 		case USB_QUIRK_NAME_GET:
1402ee3e3ff5SAndrew Thompson 		case USB_DEV_QUIRK_ADD:
1403ee3e3ff5SAndrew Thompson 		case USB_DEV_QUIRK_REMOVE:
1404ee3e3ff5SAndrew Thompson 			err = usb2_quirk_ioctl_p(cmd, data, fflag, td);
1405ee3e3ff5SAndrew Thompson 			break;
1406ee3e3ff5SAndrew Thompson 		case USB_GET_TEMPLATE:
1407ee3e3ff5SAndrew Thompson 			*(int *)data = usb2_template;
1408ee3e3ff5SAndrew Thompson 			break;
1409ee3e3ff5SAndrew Thompson 		case USB_SET_TEMPLATE:
141050230f98SAndrew Thompson 			err = priv_check(curthread, PRIV_DRIVER);
1411ee3e3ff5SAndrew Thompson 			if (err)
1412ee3e3ff5SAndrew Thompson 				break;
1413ee3e3ff5SAndrew Thompson 			usb2_template = *(int *)data;
1414ee3e3ff5SAndrew Thompson 			break;
1415ee3e3ff5SAndrew Thompson 	}
141602ac6454SAndrew Thompson 	return (err);
141702ac6454SAndrew Thompson }
141802ac6454SAndrew Thompson 
141902ac6454SAndrew Thompson static int
1420760bc48eSAndrew Thompson usb2_fifo_uiomove(struct usb_fifo *f, void *cp,
142102ac6454SAndrew Thompson     int n, struct uio *uio)
142202ac6454SAndrew Thompson {
142302ac6454SAndrew Thompson 	int error;
142402ac6454SAndrew Thompson 
142502ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
142602ac6454SAndrew Thompson 
142702ac6454SAndrew Thompson 	/*
142802ac6454SAndrew Thompson 	 * "uiomove()" can sleep so one needs to make a wrapper,
142902ac6454SAndrew Thompson 	 * exiting the mutex and checking things:
143002ac6454SAndrew Thompson 	 */
143102ac6454SAndrew Thompson 	error = uiomove(cp, n, uio);
143202ac6454SAndrew Thompson 
143302ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
143402ac6454SAndrew Thompson 
143502ac6454SAndrew Thompson 	return (error);
143602ac6454SAndrew Thompson }
143702ac6454SAndrew Thompson 
143802ac6454SAndrew Thompson int
1439760bc48eSAndrew Thompson usb2_fifo_wait(struct usb_fifo *f)
144002ac6454SAndrew Thompson {
144102ac6454SAndrew Thompson 	int err;
144202ac6454SAndrew Thompson 
144302ac6454SAndrew Thompson 	mtx_assert(f->priv_mtx, MA_OWNED);
144402ac6454SAndrew Thompson 
144502ac6454SAndrew Thompson 	if (f->flag_iserror) {
144602ac6454SAndrew Thompson 		/* we are gone */
144702ac6454SAndrew Thompson 		return (EIO);
144802ac6454SAndrew Thompson 	}
144902ac6454SAndrew Thompson 	f->flag_sleeping = 1;
145002ac6454SAndrew Thompson 
145102ac6454SAndrew Thompson 	err = usb2_cv_wait_sig(&f->cv_io, f->priv_mtx);
145202ac6454SAndrew Thompson 
145302ac6454SAndrew Thompson 	if (f->flag_iserror) {
145402ac6454SAndrew Thompson 		/* we are gone */
145502ac6454SAndrew Thompson 		err = EIO;
145602ac6454SAndrew Thompson 	}
145702ac6454SAndrew Thompson 	return (err);
145802ac6454SAndrew Thompson }
145902ac6454SAndrew Thompson 
146002ac6454SAndrew Thompson void
1461760bc48eSAndrew Thompson usb2_fifo_signal(struct usb_fifo *f)
146202ac6454SAndrew Thompson {
146302ac6454SAndrew Thompson 	if (f->flag_sleeping) {
146402ac6454SAndrew Thompson 		f->flag_sleeping = 0;
146502ac6454SAndrew Thompson 		usb2_cv_broadcast(&f->cv_io);
146602ac6454SAndrew Thompson 	}
146702ac6454SAndrew Thompson }
146802ac6454SAndrew Thompson 
146902ac6454SAndrew Thompson void
1470760bc48eSAndrew Thompson usb2_fifo_wakeup(struct usb_fifo *f)
147102ac6454SAndrew Thompson {
147202ac6454SAndrew Thompson 	usb2_fifo_signal(f);
147302ac6454SAndrew Thompson 
147402ac6454SAndrew Thompson 	if (f->flag_isselect) {
147502ac6454SAndrew Thompson 		selwakeup(&f->selinfo);
147602ac6454SAndrew Thompson 		f->flag_isselect = 0;
147702ac6454SAndrew Thompson 	}
147802ac6454SAndrew Thompson 	if (f->async_p != NULL) {
147902ac6454SAndrew Thompson 		PROC_LOCK(f->async_p);
148002ac6454SAndrew Thompson 		psignal(f->async_p, SIGIO);
148102ac6454SAndrew Thompson 		PROC_UNLOCK(f->async_p);
148202ac6454SAndrew Thompson 	}
148302ac6454SAndrew Thompson }
148402ac6454SAndrew Thompson 
148502ac6454SAndrew Thompson static int
1486760bc48eSAndrew Thompson usb2_fifo_dummy_open(struct usb_fifo *fifo, int fflags)
148702ac6454SAndrew Thompson {
148802ac6454SAndrew Thompson 	return (0);
148902ac6454SAndrew Thompson }
149002ac6454SAndrew Thompson 
149102ac6454SAndrew Thompson static void
1492760bc48eSAndrew Thompson usb2_fifo_dummy_close(struct usb_fifo *fifo, int fflags)
149302ac6454SAndrew Thompson {
149402ac6454SAndrew Thompson 	return;
149502ac6454SAndrew Thompson }
149602ac6454SAndrew Thompson 
149702ac6454SAndrew Thompson static int
1498760bc48eSAndrew Thompson usb2_fifo_dummy_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
149902ac6454SAndrew Thompson {
150002ac6454SAndrew Thompson 	return (ENOIOCTL);
150102ac6454SAndrew Thompson }
150202ac6454SAndrew Thompson 
150302ac6454SAndrew Thompson static void
1504760bc48eSAndrew Thompson usb2_fifo_dummy_cmd(struct usb_fifo *fifo)
150502ac6454SAndrew Thompson {
150602ac6454SAndrew Thompson 	fifo->flag_flushing = 0;	/* not flushing */
150702ac6454SAndrew Thompson }
150802ac6454SAndrew Thompson 
150902ac6454SAndrew Thompson static void
1510760bc48eSAndrew Thompson usb2_fifo_check_methods(struct usb_fifo_methods *pm)
151102ac6454SAndrew Thompson {
151202ac6454SAndrew Thompson 	/* check that all callback functions are OK */
151302ac6454SAndrew Thompson 
151402ac6454SAndrew Thompson 	if (pm->f_open == NULL)
151502ac6454SAndrew Thompson 		pm->f_open = &usb2_fifo_dummy_open;
151602ac6454SAndrew Thompson 
151702ac6454SAndrew Thompson 	if (pm->f_close == NULL)
151802ac6454SAndrew Thompson 		pm->f_close = &usb2_fifo_dummy_close;
151902ac6454SAndrew Thompson 
152002ac6454SAndrew Thompson 	if (pm->f_ioctl == NULL)
152102ac6454SAndrew Thompson 		pm->f_ioctl = &usb2_fifo_dummy_ioctl;
152202ac6454SAndrew Thompson 
152302ac6454SAndrew Thompson 	if (pm->f_ioctl_post == NULL)
152402ac6454SAndrew Thompson 		pm->f_ioctl_post = &usb2_fifo_dummy_ioctl;
152502ac6454SAndrew Thompson 
152602ac6454SAndrew Thompson 	if (pm->f_start_read == NULL)
152702ac6454SAndrew Thompson 		pm->f_start_read = &usb2_fifo_dummy_cmd;
152802ac6454SAndrew Thompson 
152902ac6454SAndrew Thompson 	if (pm->f_stop_read == NULL)
153002ac6454SAndrew Thompson 		pm->f_stop_read = &usb2_fifo_dummy_cmd;
153102ac6454SAndrew Thompson 
153202ac6454SAndrew Thompson 	if (pm->f_start_write == NULL)
153302ac6454SAndrew Thompson 		pm->f_start_write = &usb2_fifo_dummy_cmd;
153402ac6454SAndrew Thompson 
153502ac6454SAndrew Thompson 	if (pm->f_stop_write == NULL)
153602ac6454SAndrew Thompson 		pm->f_stop_write = &usb2_fifo_dummy_cmd;
153702ac6454SAndrew Thompson }
153802ac6454SAndrew Thompson 
153902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
154002ac6454SAndrew Thompson  *	usb2_fifo_attach
154102ac6454SAndrew Thompson  *
154202ac6454SAndrew Thompson  * The following function will create a duplex FIFO.
154302ac6454SAndrew Thompson  *
154402ac6454SAndrew Thompson  * Return values:
154502ac6454SAndrew Thompson  * 0: Success.
154602ac6454SAndrew Thompson  * Else: Failure.
154702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
154802ac6454SAndrew Thompson int
1549760bc48eSAndrew Thompson usb2_fifo_attach(struct usb_device *udev, void *priv_sc,
1550760bc48eSAndrew Thompson     struct mtx *priv_mtx, struct usb_fifo_methods *pm,
1551760bc48eSAndrew Thompson     struct usb_fifo_sc *f_sc, uint16_t unit, uint16_t subunit,
1552ee3e3ff5SAndrew Thompson     uint8_t iface_index, uid_t uid, gid_t gid, int mode)
155302ac6454SAndrew Thompson {
1554760bc48eSAndrew Thompson 	struct usb_fifo *f_tx;
1555760bc48eSAndrew Thompson 	struct usb_fifo *f_rx;
1556ee3e3ff5SAndrew Thompson 	char devname[32];
155702ac6454SAndrew Thompson 	uint8_t n;
1558760bc48eSAndrew Thompson 	struct usb_fs_privdata* pd;
155902ac6454SAndrew Thompson 
156002ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_TX] = NULL;
156102ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_RX] = NULL;
156202ac6454SAndrew Thompson 
156302ac6454SAndrew Thompson 	if (pm == NULL)
156402ac6454SAndrew Thompson 		return (EINVAL);
156502ac6454SAndrew Thompson 
156602ac6454SAndrew Thompson 	/* check the methods */
156702ac6454SAndrew Thompson 	usb2_fifo_check_methods(pm);
156802ac6454SAndrew Thompson 
156902ac6454SAndrew Thompson 	if (priv_mtx == NULL)
157002ac6454SAndrew Thompson 		priv_mtx = &Giant;
157102ac6454SAndrew Thompson 
157202ac6454SAndrew Thompson 	/* search for a free FIFO slot */
157302ac6454SAndrew Thompson 	for (n = 0;; n += 2) {
157402ac6454SAndrew Thompson 
157502ac6454SAndrew Thompson 		if (n == USB_FIFO_MAX) {
157602ac6454SAndrew Thompson 			/* end of FIFOs reached */
157702ac6454SAndrew Thompson 			return (ENOMEM);
157802ac6454SAndrew Thompson 		}
157902ac6454SAndrew Thompson 		/* Check for TX FIFO */
158002ac6454SAndrew Thompson 		if (udev->fifo[n + USB_FIFO_TX] != NULL) {
158102ac6454SAndrew Thompson 			continue;
158202ac6454SAndrew Thompson 		}
158302ac6454SAndrew Thompson 		/* Check for RX FIFO */
158402ac6454SAndrew Thompson 		if (udev->fifo[n + USB_FIFO_RX] != NULL) {
158502ac6454SAndrew Thompson 			continue;
158602ac6454SAndrew Thompson 		}
158702ac6454SAndrew Thompson 		break;
158802ac6454SAndrew Thompson 	}
158902ac6454SAndrew Thompson 
159002ac6454SAndrew Thompson 	f_tx = usb2_fifo_alloc();
159102ac6454SAndrew Thompson 	f_rx = usb2_fifo_alloc();
159202ac6454SAndrew Thompson 
159302ac6454SAndrew Thompson 	if ((f_tx == NULL) || (f_rx == NULL)) {
159402ac6454SAndrew Thompson 		usb2_fifo_free(f_tx);
159502ac6454SAndrew Thompson 		usb2_fifo_free(f_rx);
159602ac6454SAndrew Thompson 		return (ENOMEM);
159702ac6454SAndrew Thompson 	}
159802ac6454SAndrew Thompson 	/* initialise FIFO structures */
159902ac6454SAndrew Thompson 
160002ac6454SAndrew Thompson 	f_tx->fifo_index = n + USB_FIFO_TX;
16012989a677SAndrew Thompson 	f_tx->dev_ep_index = -1;
160202ac6454SAndrew Thompson 	f_tx->priv_mtx = priv_mtx;
160302ac6454SAndrew Thompson 	f_tx->priv_sc0 = priv_sc;
160402ac6454SAndrew Thompson 	f_tx->methods = pm;
160502ac6454SAndrew Thompson 	f_tx->iface_index = iface_index;
160602ac6454SAndrew Thompson 	f_tx->udev = udev;
160702ac6454SAndrew Thompson 
160802ac6454SAndrew Thompson 	f_rx->fifo_index = n + USB_FIFO_RX;
16092989a677SAndrew Thompson 	f_rx->dev_ep_index = -1;
161002ac6454SAndrew Thompson 	f_rx->priv_mtx = priv_mtx;
161102ac6454SAndrew Thompson 	f_rx->priv_sc0 = priv_sc;
161202ac6454SAndrew Thompson 	f_rx->methods = pm;
161302ac6454SAndrew Thompson 	f_rx->iface_index = iface_index;
161402ac6454SAndrew Thompson 	f_rx->udev = udev;
161502ac6454SAndrew Thompson 
161602ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_TX] = f_tx;
161702ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_RX] = f_rx;
161802ac6454SAndrew Thompson 
161902ac6454SAndrew Thompson 	mtx_lock(&usb2_ref_lock);
162002ac6454SAndrew Thompson 	udev->fifo[f_tx->fifo_index] = f_tx;
162102ac6454SAndrew Thompson 	udev->fifo[f_rx->fifo_index] = f_rx;
162202ac6454SAndrew Thompson 	mtx_unlock(&usb2_ref_lock);
162302ac6454SAndrew Thompson 
162402ac6454SAndrew Thompson 	for (n = 0; n != 4; n++) {
162502ac6454SAndrew Thompson 
162602ac6454SAndrew Thompson 		if (pm->basename[n] == NULL) {
162702ac6454SAndrew Thompson 			continue;
162802ac6454SAndrew Thompson 		}
162902ac6454SAndrew Thompson 		if (subunit == 0xFFFF) {
1630ee3e3ff5SAndrew Thompson 			if (snprintf(devname, sizeof(devname),
163102ac6454SAndrew Thompson 			    "%s%u%s", pm->basename[n],
163202ac6454SAndrew Thompson 			    unit, pm->postfix[n] ?
163302ac6454SAndrew Thompson 			    pm->postfix[n] : "")) {
163402ac6454SAndrew Thompson 				/* ignore */
163502ac6454SAndrew Thompson 			}
163602ac6454SAndrew Thompson 		} else {
1637ee3e3ff5SAndrew Thompson 			if (snprintf(devname, sizeof(devname),
163802ac6454SAndrew Thompson 			    "%s%u.%u%s", pm->basename[n],
163902ac6454SAndrew Thompson 			    unit, subunit, pm->postfix[n] ?
164002ac6454SAndrew Thompson 			    pm->postfix[n] : "")) {
164102ac6454SAndrew Thompson 				/* ignore */
164202ac6454SAndrew Thompson 			}
164302ac6454SAndrew Thompson 		}
164402ac6454SAndrew Thompson 
164502ac6454SAndrew Thompson 		/*
164602ac6454SAndrew Thompson 		 * Distribute the symbolic links into two FIFO structures:
164702ac6454SAndrew Thompson 		 */
164802ac6454SAndrew Thompson 		if (n & 1) {
164902ac6454SAndrew Thompson 			f_rx->symlink[n / 2] =
1650ee3e3ff5SAndrew Thompson 			    usb2_alloc_symlink(devname);
165102ac6454SAndrew Thompson 		} else {
165202ac6454SAndrew Thompson 			f_tx->symlink[n / 2] =
1653ee3e3ff5SAndrew Thompson 			    usb2_alloc_symlink(devname);
165402ac6454SAndrew Thompson 		}
1655ee3e3ff5SAndrew Thompson 
1656ee3e3ff5SAndrew Thompson 		/*
1657ee3e3ff5SAndrew Thompson 		 * Initialize device private data - this is used to find the
1658ee3e3ff5SAndrew Thompson 		 * actual USB device itself.
1659ee3e3ff5SAndrew Thompson 		 */
1660760bc48eSAndrew Thompson 		pd = malloc(sizeof(struct usb_fs_privdata), M_USBDEV, M_WAITOK | M_ZERO);
1661ee3e3ff5SAndrew Thompson 		pd->bus_index = device_get_unit(udev->bus->bdev);
1662ee3e3ff5SAndrew Thompson 		pd->dev_index = udev->device_index;
1663ee3e3ff5SAndrew Thompson 		pd->ep_addr = -1;	/* not an endpoint */
16647214348fSAndrew Thompson 		pd->fifo_index = f_tx->fifo_index & f_rx->fifo_index;
1665ee3e3ff5SAndrew Thompson 		pd->mode = FREAD|FWRITE;
1666ee3e3ff5SAndrew Thompson 
1667ee3e3ff5SAndrew Thompson 		/* Now, create the device itself */
1668ee3e3ff5SAndrew Thompson 		f_sc->dev = make_dev(&usb2_devsw, 0, uid, gid, mode,
1669ee3e3ff5SAndrew Thompson 		    devname);
16707214348fSAndrew Thompson 		/* XXX setting si_drv1 and creating the device is not atomic! */
1671ee3e3ff5SAndrew Thompson 		f_sc->dev->si_drv1 = pd;
167202ac6454SAndrew Thompson 	}
167302ac6454SAndrew Thompson 
167402ac6454SAndrew Thompson 	DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx);
167502ac6454SAndrew Thompson 	return (0);
167602ac6454SAndrew Thompson }
167702ac6454SAndrew Thompson 
167802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
167902ac6454SAndrew Thompson  *	usb2_fifo_alloc_buffer
168002ac6454SAndrew Thompson  *
168102ac6454SAndrew Thompson  * Return values:
168202ac6454SAndrew Thompson  * 0: Success
168302ac6454SAndrew Thompson  * Else failure
168402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
168502ac6454SAndrew Thompson int
1686760bc48eSAndrew Thompson usb2_fifo_alloc_buffer(struct usb_fifo *f, usb2_size_t bufsize,
168702ac6454SAndrew Thompson     uint16_t nbuf)
168802ac6454SAndrew Thompson {
168902ac6454SAndrew Thompson 	usb2_fifo_free_buffer(f);
169002ac6454SAndrew Thompson 
169102ac6454SAndrew Thompson 	/* allocate an endpoint */
169202ac6454SAndrew Thompson 	f->free_q.ifq_maxlen = nbuf;
169302ac6454SAndrew Thompson 	f->used_q.ifq_maxlen = nbuf;
169402ac6454SAndrew Thompson 
169502ac6454SAndrew Thompson 	f->queue_data = usb2_alloc_mbufs(
169602ac6454SAndrew Thompson 	    M_USBDEV, &f->free_q, bufsize, nbuf);
169702ac6454SAndrew Thompson 
169802ac6454SAndrew Thompson 	if ((f->queue_data == NULL) && bufsize && nbuf) {
169902ac6454SAndrew Thompson 		return (ENOMEM);
170002ac6454SAndrew Thompson 	}
170102ac6454SAndrew Thompson 	return (0);			/* success */
170202ac6454SAndrew Thompson }
170302ac6454SAndrew Thompson 
170402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
170502ac6454SAndrew Thompson  *	usb2_fifo_free_buffer
170602ac6454SAndrew Thompson  *
170702ac6454SAndrew Thompson  * This function will free the buffers associated with a FIFO. This
170802ac6454SAndrew Thompson  * function can be called multiple times in a row.
170902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
171002ac6454SAndrew Thompson void
1711760bc48eSAndrew Thompson usb2_fifo_free_buffer(struct usb_fifo *f)
171202ac6454SAndrew Thompson {
171302ac6454SAndrew Thompson 	if (f->queue_data) {
171402ac6454SAndrew Thompson 		/* free old buffer */
171502ac6454SAndrew Thompson 		free(f->queue_data, M_USBDEV);
171602ac6454SAndrew Thompson 		f->queue_data = NULL;
171702ac6454SAndrew Thompson 	}
171802ac6454SAndrew Thompson 	/* reset queues */
171902ac6454SAndrew Thompson 
172002ac6454SAndrew Thompson 	bzero(&f->free_q, sizeof(f->free_q));
172102ac6454SAndrew Thompson 	bzero(&f->used_q, sizeof(f->used_q));
172202ac6454SAndrew Thompson }
172302ac6454SAndrew Thompson 
1724ee3e3ff5SAndrew Thompson static void
1725ee3e3ff5SAndrew Thompson usb2_fifo_cleanup(void* ptr)
1726ee3e3ff5SAndrew Thompson {
1727ee3e3ff5SAndrew Thompson 	free(ptr, M_USBDEV);
1728ee3e3ff5SAndrew Thompson }
1729ee3e3ff5SAndrew Thompson 
173002ac6454SAndrew Thompson void
1731760bc48eSAndrew Thompson usb2_fifo_detach(struct usb_fifo_sc *f_sc)
173202ac6454SAndrew Thompson {
173302ac6454SAndrew Thompson 	if (f_sc == NULL) {
173402ac6454SAndrew Thompson 		return;
173502ac6454SAndrew Thompson 	}
173602ac6454SAndrew Thompson 	usb2_fifo_free(f_sc->fp[USB_FIFO_TX]);
173702ac6454SAndrew Thompson 	usb2_fifo_free(f_sc->fp[USB_FIFO_RX]);
173802ac6454SAndrew Thompson 
173902ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_TX] = NULL;
174002ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_RX] = NULL;
174102ac6454SAndrew Thompson 
1742ee3e3ff5SAndrew Thompson 	if (f_sc->dev != NULL) {
17437214348fSAndrew Thompson 		destroy_dev_sched_cb(f_sc->dev,
17447214348fSAndrew Thompson 		    usb2_fifo_cleanup, f_sc->dev->si_drv1);
17457214348fSAndrew Thompson 		f_sc->dev = NULL;
1746ee3e3ff5SAndrew Thompson 	}
1747ee3e3ff5SAndrew Thompson 
174802ac6454SAndrew Thompson 	DPRINTFN(2, "detached %p\n", f_sc);
174902ac6454SAndrew Thompson }
175002ac6454SAndrew Thompson 
1751578d0effSAndrew Thompson usb2_size_t
1752760bc48eSAndrew Thompson usb2_fifo_put_bytes_max(struct usb_fifo *f)
175302ac6454SAndrew Thompson {
1754760bc48eSAndrew Thompson 	struct usb_mbuf *m;
1755578d0effSAndrew Thompson 	usb2_size_t len;
175602ac6454SAndrew Thompson 
175702ac6454SAndrew Thompson 	USB_IF_POLL(&f->free_q, m);
175802ac6454SAndrew Thompson 
175902ac6454SAndrew Thompson 	if (m) {
176002ac6454SAndrew Thompson 		len = m->max_data_len;
176102ac6454SAndrew Thompson 	} else {
176202ac6454SAndrew Thompson 		len = 0;
176302ac6454SAndrew Thompson 	}
176402ac6454SAndrew Thompson 	return (len);
176502ac6454SAndrew Thompson }
176602ac6454SAndrew Thompson 
176702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
176802ac6454SAndrew Thompson  *	usb2_fifo_put_data
176902ac6454SAndrew Thompson  *
177002ac6454SAndrew Thompson  * what:
177102ac6454SAndrew Thompson  *  0 - normal operation
177202ac6454SAndrew Thompson  *  1 - set last packet flag to enforce framing
177302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
177402ac6454SAndrew Thompson void
1775760bc48eSAndrew Thompson usb2_fifo_put_data(struct usb_fifo *f, struct usb_page_cache *pc,
1776578d0effSAndrew Thompson     usb2_frlength_t offset, usb2_frlength_t len, uint8_t what)
177702ac6454SAndrew Thompson {
1778760bc48eSAndrew Thompson 	struct usb_mbuf *m;
1779578d0effSAndrew Thompson 	usb2_frlength_t io_len;
178002ac6454SAndrew Thompson 
178102ac6454SAndrew Thompson 	while (len || (what == 1)) {
178202ac6454SAndrew Thompson 
178302ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->free_q, m);
178402ac6454SAndrew Thompson 
178502ac6454SAndrew Thompson 		if (m) {
178602ac6454SAndrew Thompson 			USB_MBUF_RESET(m);
178702ac6454SAndrew Thompson 
178802ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
178902ac6454SAndrew Thompson 
179002ac6454SAndrew Thompson 			usb2_copy_out(pc, offset, m->cur_data_ptr, io_len);
179102ac6454SAndrew Thompson 
179202ac6454SAndrew Thompson 			m->cur_data_len = io_len;
179302ac6454SAndrew Thompson 			offset += io_len;
179402ac6454SAndrew Thompson 			len -= io_len;
179502ac6454SAndrew Thompson 
179602ac6454SAndrew Thompson 			if ((len == 0) && (what == 1)) {
179702ac6454SAndrew Thompson 				m->last_packet = 1;
179802ac6454SAndrew Thompson 			}
179902ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->used_q, m);
180002ac6454SAndrew Thompson 
180102ac6454SAndrew Thompson 			usb2_fifo_wakeup(f);
180202ac6454SAndrew Thompson 
180302ac6454SAndrew Thompson 			if ((len == 0) || (what == 1)) {
180402ac6454SAndrew Thompson 				break;
180502ac6454SAndrew Thompson 			}
180602ac6454SAndrew Thompson 		} else {
180702ac6454SAndrew Thompson 			break;
180802ac6454SAndrew Thompson 		}
180902ac6454SAndrew Thompson 	}
181002ac6454SAndrew Thompson }
181102ac6454SAndrew Thompson 
181202ac6454SAndrew Thompson void
1813760bc48eSAndrew Thompson usb2_fifo_put_data_linear(struct usb_fifo *f, void *ptr,
1814578d0effSAndrew Thompson     usb2_size_t len, uint8_t what)
181502ac6454SAndrew Thompson {
1816760bc48eSAndrew Thompson 	struct usb_mbuf *m;
1817578d0effSAndrew Thompson 	usb2_size_t io_len;
181802ac6454SAndrew Thompson 
181902ac6454SAndrew Thompson 	while (len || (what == 1)) {
182002ac6454SAndrew Thompson 
182102ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->free_q, m);
182202ac6454SAndrew Thompson 
182302ac6454SAndrew Thompson 		if (m) {
182402ac6454SAndrew Thompson 			USB_MBUF_RESET(m);
182502ac6454SAndrew Thompson 
182602ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
182702ac6454SAndrew Thompson 
182802ac6454SAndrew Thompson 			bcopy(ptr, m->cur_data_ptr, io_len);
182902ac6454SAndrew Thompson 
183002ac6454SAndrew Thompson 			m->cur_data_len = io_len;
183102ac6454SAndrew Thompson 			ptr = USB_ADD_BYTES(ptr, io_len);
183202ac6454SAndrew Thompson 			len -= io_len;
183302ac6454SAndrew Thompson 
183402ac6454SAndrew Thompson 			if ((len == 0) && (what == 1)) {
183502ac6454SAndrew Thompson 				m->last_packet = 1;
183602ac6454SAndrew Thompson 			}
183702ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->used_q, m);
183802ac6454SAndrew Thompson 
183902ac6454SAndrew Thompson 			usb2_fifo_wakeup(f);
184002ac6454SAndrew Thompson 
184102ac6454SAndrew Thompson 			if ((len == 0) || (what == 1)) {
184202ac6454SAndrew Thompson 				break;
184302ac6454SAndrew Thompson 			}
184402ac6454SAndrew Thompson 		} else {
184502ac6454SAndrew Thompson 			break;
184602ac6454SAndrew Thompson 		}
184702ac6454SAndrew Thompson 	}
184802ac6454SAndrew Thompson }
184902ac6454SAndrew Thompson 
185002ac6454SAndrew Thompson uint8_t
1851760bc48eSAndrew Thompson usb2_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb2_size_t len)
185202ac6454SAndrew Thompson {
1853760bc48eSAndrew Thompson 	struct usb_mbuf *m;
185402ac6454SAndrew Thompson 
185502ac6454SAndrew Thompson 	USB_IF_DEQUEUE(&f->free_q, m);
185602ac6454SAndrew Thompson 
185702ac6454SAndrew Thompson 	if (m) {
185802ac6454SAndrew Thompson 		m->cur_data_len = len;
185902ac6454SAndrew Thompson 		m->cur_data_ptr = ptr;
186002ac6454SAndrew Thompson 		USB_IF_ENQUEUE(&f->used_q, m);
186102ac6454SAndrew Thompson 		usb2_fifo_wakeup(f);
186202ac6454SAndrew Thompson 		return (1);
186302ac6454SAndrew Thompson 	}
186402ac6454SAndrew Thompson 	return (0);
186502ac6454SAndrew Thompson }
186602ac6454SAndrew Thompson 
186702ac6454SAndrew Thompson void
1868760bc48eSAndrew Thompson usb2_fifo_put_data_error(struct usb_fifo *f)
186902ac6454SAndrew Thompson {
187002ac6454SAndrew Thompson 	f->flag_iserror = 1;
187102ac6454SAndrew Thompson 	usb2_fifo_wakeup(f);
187202ac6454SAndrew Thompson }
187302ac6454SAndrew Thompson 
187402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
187502ac6454SAndrew Thompson  *	usb2_fifo_get_data
187602ac6454SAndrew Thompson  *
187702ac6454SAndrew Thompson  * what:
187802ac6454SAndrew Thompson  *  0 - normal operation
1879760bc48eSAndrew Thompson  *  1 - only get one "usb_mbuf"
188002ac6454SAndrew Thompson  *
188102ac6454SAndrew Thompson  * returns:
188202ac6454SAndrew Thompson  *  0 - no more data
188302ac6454SAndrew Thompson  *  1 - data in buffer
188402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
188502ac6454SAndrew Thompson uint8_t
1886760bc48eSAndrew Thompson usb2_fifo_get_data(struct usb_fifo *f, struct usb_page_cache *pc,
1887578d0effSAndrew Thompson     usb2_frlength_t offset, usb2_frlength_t len, usb2_frlength_t *actlen,
188802ac6454SAndrew Thompson     uint8_t what)
188902ac6454SAndrew Thompson {
1890760bc48eSAndrew Thompson 	struct usb_mbuf *m;
1891578d0effSAndrew Thompson 	usb2_frlength_t io_len;
189202ac6454SAndrew Thompson 	uint8_t tr_data = 0;
189302ac6454SAndrew Thompson 
189402ac6454SAndrew Thompson 	actlen[0] = 0;
189502ac6454SAndrew Thompson 
189602ac6454SAndrew Thompson 	while (1) {
189702ac6454SAndrew Thompson 
189802ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
189902ac6454SAndrew Thompson 
190002ac6454SAndrew Thompson 		if (m) {
190102ac6454SAndrew Thompson 
190202ac6454SAndrew Thompson 			tr_data = 1;
190302ac6454SAndrew Thompson 
190402ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
190502ac6454SAndrew Thompson 
190602ac6454SAndrew Thompson 			usb2_copy_in(pc, offset, m->cur_data_ptr, io_len);
190702ac6454SAndrew Thompson 
190802ac6454SAndrew Thompson 			len -= io_len;
190902ac6454SAndrew Thompson 			offset += io_len;
191002ac6454SAndrew Thompson 			actlen[0] += io_len;
191102ac6454SAndrew Thompson 			m->cur_data_ptr += io_len;
191202ac6454SAndrew Thompson 			m->cur_data_len -= io_len;
191302ac6454SAndrew Thompson 
191402ac6454SAndrew Thompson 			if ((m->cur_data_len == 0) || (what == 1)) {
191502ac6454SAndrew Thompson 				USB_IF_ENQUEUE(&f->free_q, m);
191602ac6454SAndrew Thompson 
191702ac6454SAndrew Thompson 				usb2_fifo_wakeup(f);
191802ac6454SAndrew Thompson 
191902ac6454SAndrew Thompson 				if (what == 1) {
192002ac6454SAndrew Thompson 					break;
192102ac6454SAndrew Thompson 				}
192202ac6454SAndrew Thompson 			} else {
192302ac6454SAndrew Thompson 				USB_IF_PREPEND(&f->used_q, m);
192402ac6454SAndrew Thompson 			}
192502ac6454SAndrew Thompson 		} else {
192602ac6454SAndrew Thompson 
192702ac6454SAndrew Thompson 			if (tr_data) {
192802ac6454SAndrew Thompson 				/* wait for data to be written out */
192902ac6454SAndrew Thompson 				break;
193002ac6454SAndrew Thompson 			}
193102ac6454SAndrew Thompson 			if (f->flag_flushing) {
19327214348fSAndrew Thompson 				/* check if we should send a short packet */
19337214348fSAndrew Thompson 				if (f->flag_short != 0) {
19347214348fSAndrew Thompson 					f->flag_short = 0;
19357214348fSAndrew Thompson 					tr_data = 1;
19367214348fSAndrew Thompson 					break;
19377214348fSAndrew Thompson 				}
19387214348fSAndrew Thompson 				/* flushing complete */
193902ac6454SAndrew Thompson 				f->flag_flushing = 0;
194002ac6454SAndrew Thompson 				usb2_fifo_wakeup(f);
194102ac6454SAndrew Thompson 			}
194202ac6454SAndrew Thompson 			break;
194302ac6454SAndrew Thompson 		}
194402ac6454SAndrew Thompson 		if (len == 0) {
194502ac6454SAndrew Thompson 			break;
194602ac6454SAndrew Thompson 		}
194702ac6454SAndrew Thompson 	}
194802ac6454SAndrew Thompson 	return (tr_data);
194902ac6454SAndrew Thompson }
195002ac6454SAndrew Thompson 
195102ac6454SAndrew Thompson uint8_t
1952760bc48eSAndrew Thompson usb2_fifo_get_data_linear(struct usb_fifo *f, void *ptr,
1953578d0effSAndrew Thompson     usb2_size_t len, usb2_size_t *actlen, uint8_t what)
195402ac6454SAndrew Thompson {
1955760bc48eSAndrew Thompson 	struct usb_mbuf *m;
1956578d0effSAndrew Thompson 	usb2_size_t io_len;
195702ac6454SAndrew Thompson 	uint8_t tr_data = 0;
195802ac6454SAndrew Thompson 
195902ac6454SAndrew Thompson 	actlen[0] = 0;
196002ac6454SAndrew Thompson 
196102ac6454SAndrew Thompson 	while (1) {
196202ac6454SAndrew Thompson 
196302ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
196402ac6454SAndrew Thompson 
196502ac6454SAndrew Thompson 		if (m) {
196602ac6454SAndrew Thompson 
196702ac6454SAndrew Thompson 			tr_data = 1;
196802ac6454SAndrew Thompson 
196902ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
197002ac6454SAndrew Thompson 
197102ac6454SAndrew Thompson 			bcopy(m->cur_data_ptr, ptr, io_len);
197202ac6454SAndrew Thompson 
197302ac6454SAndrew Thompson 			len -= io_len;
197402ac6454SAndrew Thompson 			ptr = USB_ADD_BYTES(ptr, io_len);
197502ac6454SAndrew Thompson 			actlen[0] += io_len;
197602ac6454SAndrew Thompson 			m->cur_data_ptr += io_len;
197702ac6454SAndrew Thompson 			m->cur_data_len -= io_len;
197802ac6454SAndrew Thompson 
197902ac6454SAndrew Thompson 			if ((m->cur_data_len == 0) || (what == 1)) {
198002ac6454SAndrew Thompson 				USB_IF_ENQUEUE(&f->free_q, m);
198102ac6454SAndrew Thompson 
198202ac6454SAndrew Thompson 				usb2_fifo_wakeup(f);
198302ac6454SAndrew Thompson 
198402ac6454SAndrew Thompson 				if (what == 1) {
198502ac6454SAndrew Thompson 					break;
198602ac6454SAndrew Thompson 				}
198702ac6454SAndrew Thompson 			} else {
198802ac6454SAndrew Thompson 				USB_IF_PREPEND(&f->used_q, m);
198902ac6454SAndrew Thompson 			}
199002ac6454SAndrew Thompson 		} else {
199102ac6454SAndrew Thompson 
199202ac6454SAndrew Thompson 			if (tr_data) {
199302ac6454SAndrew Thompson 				/* wait for data to be written out */
199402ac6454SAndrew Thompson 				break;
199502ac6454SAndrew Thompson 			}
199602ac6454SAndrew Thompson 			if (f->flag_flushing) {
19977214348fSAndrew Thompson 				/* check if we should send a short packet */
19987214348fSAndrew Thompson 				if (f->flag_short != 0) {
19997214348fSAndrew Thompson 					f->flag_short = 0;
20007214348fSAndrew Thompson 					tr_data = 1;
20017214348fSAndrew Thompson 					break;
20027214348fSAndrew Thompson 				}
20037214348fSAndrew Thompson 				/* flushing complete */
200402ac6454SAndrew Thompson 				f->flag_flushing = 0;
200502ac6454SAndrew Thompson 				usb2_fifo_wakeup(f);
200602ac6454SAndrew Thompson 			}
200702ac6454SAndrew Thompson 			break;
200802ac6454SAndrew Thompson 		}
200902ac6454SAndrew Thompson 		if (len == 0) {
201002ac6454SAndrew Thompson 			break;
201102ac6454SAndrew Thompson 		}
201202ac6454SAndrew Thompson 	}
201302ac6454SAndrew Thompson 	return (tr_data);
201402ac6454SAndrew Thompson }
201502ac6454SAndrew Thompson 
201602ac6454SAndrew Thompson uint8_t
2017760bc48eSAndrew Thompson usb2_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb2_size_t *plen)
201802ac6454SAndrew Thompson {
2019760bc48eSAndrew Thompson 	struct usb_mbuf *m;
202002ac6454SAndrew Thompson 
202102ac6454SAndrew Thompson 	USB_IF_POLL(&f->used_q, m);
202202ac6454SAndrew Thompson 
202302ac6454SAndrew Thompson 	if (m) {
202402ac6454SAndrew Thompson 		*plen = m->cur_data_len;
202502ac6454SAndrew Thompson 		*pptr = m->cur_data_ptr;
202602ac6454SAndrew Thompson 
202702ac6454SAndrew Thompson 		return (1);
202802ac6454SAndrew Thompson 	}
202902ac6454SAndrew Thompson 	return (0);
203002ac6454SAndrew Thompson }
203102ac6454SAndrew Thompson 
203202ac6454SAndrew Thompson void
2033760bc48eSAndrew Thompson usb2_fifo_get_data_error(struct usb_fifo *f)
203402ac6454SAndrew Thompson {
203502ac6454SAndrew Thompson 	f->flag_iserror = 1;
203602ac6454SAndrew Thompson 	usb2_fifo_wakeup(f);
203702ac6454SAndrew Thompson }
203802ac6454SAndrew Thompson 
203902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
204002ac6454SAndrew Thompson  *	usb2_alloc_symlink
204102ac6454SAndrew Thompson  *
204202ac6454SAndrew Thompson  * Return values:
204302ac6454SAndrew Thompson  * NULL: Failure
204402ac6454SAndrew Thompson  * Else: Pointer to symlink entry
204502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
2046760bc48eSAndrew Thompson struct usb_symlink *
2047ee3e3ff5SAndrew Thompson usb2_alloc_symlink(const char *target)
204802ac6454SAndrew Thompson {
2049760bc48eSAndrew Thompson 	struct usb_symlink *ps;
205002ac6454SAndrew Thompson 
205102ac6454SAndrew Thompson 	ps = malloc(sizeof(*ps), M_USBDEV, M_WAITOK);
205202ac6454SAndrew Thompson 	if (ps == NULL) {
205302ac6454SAndrew Thompson 		return (ps);
205402ac6454SAndrew Thompson 	}
2055ee3e3ff5SAndrew Thompson 	/* XXX no longer needed */
2056ee3e3ff5SAndrew Thompson 	strlcpy(ps->src_path, target, sizeof(ps->src_path));
2057ee3e3ff5SAndrew Thompson 	ps->src_len = strlen(ps->src_path);
205802ac6454SAndrew Thompson 	strlcpy(ps->dst_path, target, sizeof(ps->dst_path));
205902ac6454SAndrew Thompson 	ps->dst_len = strlen(ps->dst_path);
206002ac6454SAndrew Thompson 
206102ac6454SAndrew Thompson 	sx_xlock(&usb2_sym_lock);
206202ac6454SAndrew Thompson 	TAILQ_INSERT_TAIL(&usb2_sym_head, ps, sym_entry);
206302ac6454SAndrew Thompson 	sx_unlock(&usb2_sym_lock);
206402ac6454SAndrew Thompson 	return (ps);
206502ac6454SAndrew Thompson }
206602ac6454SAndrew Thompson 
206702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
206802ac6454SAndrew Thompson  *	usb2_free_symlink
206902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
207002ac6454SAndrew Thompson void
2071760bc48eSAndrew Thompson usb2_free_symlink(struct usb_symlink *ps)
207202ac6454SAndrew Thompson {
207302ac6454SAndrew Thompson 	if (ps == NULL) {
207402ac6454SAndrew Thompson 		return;
207502ac6454SAndrew Thompson 	}
207602ac6454SAndrew Thompson 	sx_xlock(&usb2_sym_lock);
207702ac6454SAndrew Thompson 	TAILQ_REMOVE(&usb2_sym_head, ps, sym_entry);
207802ac6454SAndrew Thompson 	sx_unlock(&usb2_sym_lock);
207902ac6454SAndrew Thompson 
208002ac6454SAndrew Thompson 	free(ps, M_USBDEV);
208102ac6454SAndrew Thompson }
208202ac6454SAndrew Thompson 
208302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
208402ac6454SAndrew Thompson  *	usb2_read_symlink
208502ac6454SAndrew Thompson  *
208602ac6454SAndrew Thompson  * Return value:
208702ac6454SAndrew Thompson  * 0: Success
208802ac6454SAndrew Thompson  * Else: Failure
208902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
209002ac6454SAndrew Thompson int
209102ac6454SAndrew Thompson usb2_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len)
209202ac6454SAndrew Thompson {
2093760bc48eSAndrew Thompson 	struct usb_symlink *ps;
209402ac6454SAndrew Thompson 	uint32_t temp;
209502ac6454SAndrew Thompson 	uint32_t delta = 0;
209602ac6454SAndrew Thompson 	uint8_t len;
209702ac6454SAndrew Thompson 	int error = 0;
209802ac6454SAndrew Thompson 
209902ac6454SAndrew Thompson 	sx_xlock(&usb2_sym_lock);
210002ac6454SAndrew Thompson 
210102ac6454SAndrew Thompson 	TAILQ_FOREACH(ps, &usb2_sym_head, sym_entry) {
210202ac6454SAndrew Thompson 
210302ac6454SAndrew Thompson 		/*
210402ac6454SAndrew Thompson 		 * Compute total length of source and destination symlink
210502ac6454SAndrew Thompson 		 * strings pluss one length byte and two NUL bytes:
210602ac6454SAndrew Thompson 		 */
210702ac6454SAndrew Thompson 		temp = ps->src_len + ps->dst_len + 3;
210802ac6454SAndrew Thompson 
210902ac6454SAndrew Thompson 		if (temp > 255) {
211002ac6454SAndrew Thompson 			/*
211102ac6454SAndrew Thompson 			 * Skip entry because this length cannot fit
211202ac6454SAndrew Thompson 			 * into one byte:
211302ac6454SAndrew Thompson 			 */
211402ac6454SAndrew Thompson 			continue;
211502ac6454SAndrew Thompson 		}
211602ac6454SAndrew Thompson 		if (startentry != 0) {
211702ac6454SAndrew Thompson 			/* decrement read offset */
211802ac6454SAndrew Thompson 			startentry--;
211902ac6454SAndrew Thompson 			continue;
212002ac6454SAndrew Thompson 		}
212102ac6454SAndrew Thompson 		if (temp > user_len) {
212202ac6454SAndrew Thompson 			/* out of buffer space */
212302ac6454SAndrew Thompson 			break;
212402ac6454SAndrew Thompson 		}
212502ac6454SAndrew Thompson 		len = temp;
212602ac6454SAndrew Thompson 
212702ac6454SAndrew Thompson 		/* copy out total length */
212802ac6454SAndrew Thompson 
212902ac6454SAndrew Thompson 		error = copyout(&len,
213002ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
213102ac6454SAndrew Thompson 		if (error) {
213202ac6454SAndrew Thompson 			break;
213302ac6454SAndrew Thompson 		}
213402ac6454SAndrew Thompson 		delta += 1;
213502ac6454SAndrew Thompson 
213602ac6454SAndrew Thompson 		/* copy out source string */
213702ac6454SAndrew Thompson 
213802ac6454SAndrew Thompson 		error = copyout(ps->src_path,
213902ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), ps->src_len);
214002ac6454SAndrew Thompson 		if (error) {
214102ac6454SAndrew Thompson 			break;
214202ac6454SAndrew Thompson 		}
214302ac6454SAndrew Thompson 		len = 0;
214402ac6454SAndrew Thompson 		delta += ps->src_len;
214502ac6454SAndrew Thompson 		error = copyout(&len,
214602ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
214702ac6454SAndrew Thompson 		if (error) {
214802ac6454SAndrew Thompson 			break;
214902ac6454SAndrew Thompson 		}
215002ac6454SAndrew Thompson 		delta += 1;
215102ac6454SAndrew Thompson 
215202ac6454SAndrew Thompson 		/* copy out destination string */
215302ac6454SAndrew Thompson 
215402ac6454SAndrew Thompson 		error = copyout(ps->dst_path,
215502ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), ps->dst_len);
215602ac6454SAndrew Thompson 		if (error) {
215702ac6454SAndrew Thompson 			break;
215802ac6454SAndrew Thompson 		}
215902ac6454SAndrew Thompson 		len = 0;
216002ac6454SAndrew Thompson 		delta += ps->dst_len;
216102ac6454SAndrew Thompson 		error = copyout(&len,
216202ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
216302ac6454SAndrew Thompson 		if (error) {
216402ac6454SAndrew Thompson 			break;
216502ac6454SAndrew Thompson 		}
216602ac6454SAndrew Thompson 		delta += 1;
216702ac6454SAndrew Thompson 
216802ac6454SAndrew Thompson 		user_len -= temp;
216902ac6454SAndrew Thompson 	}
217002ac6454SAndrew Thompson 
217102ac6454SAndrew Thompson 	/* a zero length entry indicates the end */
217202ac6454SAndrew Thompson 
217302ac6454SAndrew Thompson 	if ((user_len != 0) && (error == 0)) {
217402ac6454SAndrew Thompson 
217502ac6454SAndrew Thompson 		len = 0;
217602ac6454SAndrew Thompson 
217702ac6454SAndrew Thompson 		error = copyout(&len,
217802ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
217902ac6454SAndrew Thompson 	}
218002ac6454SAndrew Thompson 	sx_unlock(&usb2_sym_lock);
218102ac6454SAndrew Thompson 	return (error);
218202ac6454SAndrew Thompson }
21837214348fSAndrew Thompson 
21847214348fSAndrew Thompson void
2185760bc48eSAndrew Thompson usb2_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff)
21867214348fSAndrew Thompson {
21877214348fSAndrew Thompson 	if (f == NULL)
21887214348fSAndrew Thompson 		return;
21897214348fSAndrew Thompson 
21907214348fSAndrew Thompson 	/* send a Zero Length Packet, ZLP, before close */
21917214348fSAndrew Thompson 	f->flag_short = onoff;
21927214348fSAndrew Thompson }
21938755859aSAndrew Thompson #endif	/* USB_HAVE_UGEN */
2194