xref: /freebsd/sys/dev/usb/usb_dev.c (revision 91cd92400fc6fa107e8b2b60140d2675e9f78020)
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  *
27a593f6b8SAndrew Thompson  * usb_dev.c - An abstraction layer for creating devices under /dev/...
2802ac6454SAndrew Thompson  */
2902ac6454SAndrew Thompson 
30ed6d949aSAndrew Thompson #include <sys/stdint.h>
31ed6d949aSAndrew Thompson #include <sys/stddef.h>
32ed6d949aSAndrew Thompson #include <sys/param.h>
33ed6d949aSAndrew Thompson #include <sys/queue.h>
34ed6d949aSAndrew Thompson #include <sys/types.h>
35ed6d949aSAndrew Thompson #include <sys/systm.h>
36ed6d949aSAndrew Thompson #include <sys/kernel.h>
37ed6d949aSAndrew Thompson #include <sys/bus.h>
38ed6d949aSAndrew Thompson #include <sys/linker_set.h>
39ed6d949aSAndrew Thompson #include <sys/module.h>
40ed6d949aSAndrew Thompson #include <sys/lock.h>
41ed6d949aSAndrew Thompson #include <sys/mutex.h>
42ed6d949aSAndrew Thompson #include <sys/condvar.h>
43ed6d949aSAndrew Thompson #include <sys/sysctl.h>
44ed6d949aSAndrew Thompson #include <sys/sx.h>
45ed6d949aSAndrew Thompson #include <sys/unistd.h>
46ed6d949aSAndrew Thompson #include <sys/callout.h>
47ed6d949aSAndrew Thompson #include <sys/malloc.h>
48ed6d949aSAndrew Thompson #include <sys/priv.h>
49ed6d949aSAndrew Thompson #include <sys/vnode.h>
50ed6d949aSAndrew Thompson #include <sys/conf.h>
51ed6d949aSAndrew Thompson #include <sys/fcntl.h>
52ed6d949aSAndrew Thompson 
5302ac6454SAndrew Thompson #include <dev/usb/usb.h>
5402ac6454SAndrew Thompson #include <dev/usb/usb_ioctl.h>
55ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
56ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
5702ac6454SAndrew Thompson 
58a593f6b8SAndrew Thompson #define	USB_DEBUG_VAR usb_fifo_debug
5902ac6454SAndrew Thompson 
6002ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
6102ac6454SAndrew Thompson #include <dev/usb/usb_dev.h>
62ed6d949aSAndrew Thompson #include <dev/usb/usb_mbuf.h>
6302ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
6402ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
6502ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
6602ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
6702ac6454SAndrew Thompson #include <dev/usb/usb_generic.h>
6802ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h>
6902ac6454SAndrew Thompson #include <dev/usb/usb_util.h>
7002ac6454SAndrew Thompson 
7102ac6454SAndrew Thompson #include <dev/usb/usb_controller.h>
7202ac6454SAndrew Thompson #include <dev/usb/usb_bus.h>
7302ac6454SAndrew Thompson 
7402ac6454SAndrew Thompson #include <sys/filio.h>
7502ac6454SAndrew Thompson #include <sys/ttycom.h>
7602ac6454SAndrew Thompson #include <sys/syscallsubr.h>
7702ac6454SAndrew Thompson 
7802ac6454SAndrew Thompson #include <machine/stdarg.h>
7902ac6454SAndrew Thompson 
808755859aSAndrew Thompson #if USB_HAVE_UGEN
818755859aSAndrew Thompson 
82ed6d949aSAndrew Thompson #ifdef USB_DEBUG
83a593f6b8SAndrew Thompson static int usb_fifo_debug = 0;
8402ac6454SAndrew Thompson 
859360ae40SAndrew Thompson SYSCTL_NODE(_hw_usb, OID_AUTO, dev, CTLFLAG_RW, 0, "USB device");
869360ae40SAndrew Thompson SYSCTL_INT(_hw_usb_dev, OID_AUTO, debug, CTLFLAG_RW,
87a593f6b8SAndrew Thompson     &usb_fifo_debug, 0, "Debug Level");
88c13fd8d4SAndrew Thompson 
89c13fd8d4SAndrew Thompson TUNABLE_INT("hw.usb.dev.debug", &usb_fifo_debug);
9002ac6454SAndrew Thompson #endif
9102ac6454SAndrew Thompson 
9202ac6454SAndrew Thompson #if ((__FreeBSD_version >= 700001) || (__FreeBSD_version == 0) || \
9302ac6454SAndrew Thompson      ((__FreeBSD_version >= 600034) && (__FreeBSD_version < 700000)))
9402ac6454SAndrew Thompson #define	USB_UCRED struct ucred *ucred,
9502ac6454SAndrew Thompson #else
9602ac6454SAndrew Thompson #define	USB_UCRED
9702ac6454SAndrew Thompson #endif
9802ac6454SAndrew Thompson 
9902ac6454SAndrew Thompson /* prototypes */
10002ac6454SAndrew Thompson 
101a593f6b8SAndrew Thompson static int	usb_fifo_open(struct usb_cdev_privdata *,
102760bc48eSAndrew Thompson 		    struct usb_fifo *, int);
103a593f6b8SAndrew Thompson static void	usb_fifo_close(struct usb_fifo *, int);
104a593f6b8SAndrew Thompson static void	usb_dev_init(void *);
105a593f6b8SAndrew Thompson static void	usb_dev_init_post(void *);
106a593f6b8SAndrew Thompson static void	usb_dev_uninit(void *);
107a593f6b8SAndrew Thompson static int	usb_fifo_uiomove(struct usb_fifo *, void *, int,
10802ac6454SAndrew Thompson 		    struct uio *);
109a593f6b8SAndrew Thompson static void	usb_fifo_check_methods(struct usb_fifo_methods *);
110a593f6b8SAndrew Thompson static struct	usb_fifo *usb_fifo_alloc(void);
111a593f6b8SAndrew Thompson static struct	usb_endpoint *usb_dev_get_ep(struct usb_device *, uint8_t,
112f5f145baSAndrew Thompson 		    uint8_t);
113a593f6b8SAndrew Thompson static void	usb_loc_fill(struct usb_fs_privdata *,
114760bc48eSAndrew Thompson 		    struct usb_cdev_privdata *);
115a593f6b8SAndrew Thompson static void	usb_close(void *);
116a593f6b8SAndrew Thompson static usb_error_t usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *, int);
117a593f6b8SAndrew Thompson static usb_error_t usb_usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *);
118a593f6b8SAndrew Thompson static void	usb_unref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *);
11902ac6454SAndrew Thompson 
120a593f6b8SAndrew Thompson static d_open_t usb_open;
121a593f6b8SAndrew Thompson static d_ioctl_t usb_ioctl;
122a593f6b8SAndrew Thompson static d_read_t usb_read;
123a593f6b8SAndrew Thompson static d_write_t usb_write;
124a593f6b8SAndrew Thompson static d_poll_t usb_poll;
12502ac6454SAndrew Thompson 
126a593f6b8SAndrew Thompson static d_ioctl_t usb_static_ioctl;
12702ac6454SAndrew Thompson 
128a593f6b8SAndrew Thompson static usb_fifo_open_t usb_fifo_dummy_open;
129a593f6b8SAndrew Thompson static usb_fifo_close_t usb_fifo_dummy_close;
130a593f6b8SAndrew Thompson static usb_fifo_ioctl_t usb_fifo_dummy_ioctl;
131a593f6b8SAndrew Thompson static usb_fifo_cmd_t usb_fifo_dummy_cmd;
13202ac6454SAndrew Thompson 
133ee3e3ff5SAndrew Thompson /* character device structure used for devices (/dev/ugenX.Y and /dev/uXXX) */
134a593f6b8SAndrew Thompson struct cdevsw usb_devsw = {
13502ac6454SAndrew Thompson 	.d_version = D_VERSION,
136a593f6b8SAndrew Thompson 	.d_open = usb_open,
137a593f6b8SAndrew Thompson 	.d_ioctl = usb_ioctl,
138ee3e3ff5SAndrew Thompson 	.d_name = "usbdev",
13902ac6454SAndrew Thompson 	.d_flags = D_TRACKCLOSE,
140a593f6b8SAndrew Thompson 	.d_read = usb_read,
141a593f6b8SAndrew Thompson 	.d_write = usb_write,
142a593f6b8SAndrew Thompson 	.d_poll = usb_poll
14302ac6454SAndrew Thompson };
14402ac6454SAndrew Thompson 
145a593f6b8SAndrew Thompson static struct cdev* usb_dev = NULL;
146ee3e3ff5SAndrew Thompson 
147ee3e3ff5SAndrew Thompson /* character device structure used for /dev/usb */
148a593f6b8SAndrew Thompson static struct cdevsw usb_static_devsw = {
149ee3e3ff5SAndrew Thompson 	.d_version = D_VERSION,
150a593f6b8SAndrew Thompson 	.d_ioctl = usb_static_ioctl,
151ee3e3ff5SAndrew Thompson 	.d_name = "usb"
15202ac6454SAndrew Thompson };
15302ac6454SAndrew Thompson 
154a593f6b8SAndrew Thompson static TAILQ_HEAD(, usb_symlink) usb_sym_head;
155a593f6b8SAndrew Thompson static struct sx usb_sym_lock;
15602ac6454SAndrew Thompson 
157a593f6b8SAndrew Thompson struct mtx usb_ref_lock;
15802ac6454SAndrew Thompson 
15902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
160a593f6b8SAndrew Thompson  *	usb_loc_fill
16102ac6454SAndrew Thompson  *
162760bc48eSAndrew Thompson  * This is used to fill out a usb_cdev_privdata structure based on the
163760bc48eSAndrew Thompson  * device's address as contained in usb_fs_privdata.
16402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
165ee3e3ff5SAndrew Thompson static void
166a593f6b8SAndrew Thompson usb_loc_fill(struct usb_fs_privdata* pd, struct usb_cdev_privdata *cpd)
16702ac6454SAndrew Thompson {
168ee3e3ff5SAndrew Thompson 	cpd->bus_index = pd->bus_index;
169ee3e3ff5SAndrew Thompson 	cpd->dev_index = pd->dev_index;
170ee3e3ff5SAndrew Thompson 	cpd->ep_addr = pd->ep_addr;
171ee3e3ff5SAndrew Thompson 	cpd->fifo_index = pd->fifo_index;
17202ac6454SAndrew Thompson }
17302ac6454SAndrew Thompson 
17402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
175a593f6b8SAndrew Thompson  *	usb_ref_device
17602ac6454SAndrew Thompson  *
17702ac6454SAndrew Thompson  * This function is used to atomically refer an USB device by its
17802ac6454SAndrew Thompson  * device location. If this function returns success the USB device
17902ac6454SAndrew Thompson  * will not dissappear until the USB device is unreferenced.
18002ac6454SAndrew Thompson  *
18102ac6454SAndrew Thompson  * Return values:
18202ac6454SAndrew Thompson  *  0: Success, refcount incremented on the given USB device.
18302ac6454SAndrew Thompson  *  Else: Failure.
18402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
185e0a69b51SAndrew Thompson usb_error_t
186a593f6b8SAndrew Thompson usb_ref_device(struct usb_cdev_privdata *cpd,
187e13e19faSAndrew Thompson     struct usb_cdev_refdata *crd, int need_uref)
18802ac6454SAndrew Thompson {
189760bc48eSAndrew Thompson 	struct usb_fifo **ppf;
190760bc48eSAndrew Thompson 	struct usb_fifo *f;
19102ac6454SAndrew Thompson 
192e13e19faSAndrew Thompson 	DPRINTFN(2, "cpd=%p need uref=%d\n", cpd, need_uref);
193e13e19faSAndrew Thompson 
194e13e19faSAndrew Thompson 	/* clear all refs */
195e13e19faSAndrew Thompson 	memset(crd, 0, sizeof(*crd));
19602ac6454SAndrew Thompson 
197a593f6b8SAndrew Thompson 	mtx_lock(&usb_ref_lock);
198a593f6b8SAndrew Thompson 	cpd->bus = devclass_get_softc(usb_devclass_ptr, cpd->bus_index);
199ee3e3ff5SAndrew Thompson 	if (cpd->bus == NULL) {
200ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "no bus at %u\n", cpd->bus_index);
20102ac6454SAndrew Thompson 		goto error;
20202ac6454SAndrew Thompson 	}
203ee3e3ff5SAndrew Thompson 	cpd->udev = cpd->bus->devices[cpd->dev_index];
204ee3e3ff5SAndrew Thompson 	if (cpd->udev == NULL) {
205ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "no device at %u\n", cpd->dev_index);
20602ac6454SAndrew Thompson 		goto error;
20702ac6454SAndrew Thompson 	}
208ee3e3ff5SAndrew Thompson 	if (cpd->udev->refcount == USB_DEV_REF_MAX) {
20902ac6454SAndrew Thompson 		DPRINTFN(2, "no dev ref\n");
21002ac6454SAndrew Thompson 		goto error;
21102ac6454SAndrew Thompson 	}
2120ed53d45SAndrew Thompson 	if (need_uref) {
2130ed53d45SAndrew Thompson 		DPRINTFN(2, "ref udev - needed\n");
2140ed53d45SAndrew Thompson 		cpd->udev->refcount++;
2150ed53d45SAndrew Thompson 
216a593f6b8SAndrew Thompson 		mtx_unlock(&usb_ref_lock);
2170ed53d45SAndrew Thompson 
2180ed53d45SAndrew Thompson 		/*
2190ed53d45SAndrew Thompson 		 * We need to grab the sx-lock before grabbing the
2200ed53d45SAndrew Thompson 		 * FIFO refs to avoid deadlock at detach!
2210ed53d45SAndrew Thompson 		 */
222cb18f7d1SAlfred Perlstein 		usbd_enum_lock(cpd->udev);
2230ed53d45SAndrew Thompson 
224a593f6b8SAndrew Thompson 		mtx_lock(&usb_ref_lock);
225a488edb5SAndrew Thompson 
226a488edb5SAndrew Thompson 		/*
227a488edb5SAndrew Thompson 		 * Set "is_uref" after grabbing the default SX lock
228a488edb5SAndrew Thompson 		 */
229e13e19faSAndrew Thompson 		crd->is_uref = 1;
2300ed53d45SAndrew Thompson 	}
2310ed53d45SAndrew Thompson 
23202ac6454SAndrew Thompson 	/* check if we are doing an open */
233ee3e3ff5SAndrew Thompson 	if (cpd->fflags == 0) {
234e13e19faSAndrew Thompson 		/* use zero defaults */
23502ac6454SAndrew Thompson 	} else {
23602ac6454SAndrew Thompson 		/* check for write */
237ee3e3ff5SAndrew Thompson 		if (cpd->fflags & FWRITE) {
238ee3e3ff5SAndrew Thompson 			ppf = cpd->udev->fifo;
239ee3e3ff5SAndrew Thompson 			f = ppf[cpd->fifo_index + USB_FIFO_TX];
240e13e19faSAndrew Thompson 			crd->txfifo = f;
241e13e19faSAndrew Thompson 			crd->is_write = 1;	/* ref */
242ee3e3ff5SAndrew Thompson 			if (f == NULL || f->refcount == USB_FIFO_REF_MAX)
24302ac6454SAndrew Thompson 				goto error;
2447214348fSAndrew Thompson 			if (f->curr_cpd != cpd)
2457214348fSAndrew Thompson 				goto error;
24602ac6454SAndrew Thompson 			/* check if USB-FS is active */
24702ac6454SAndrew Thompson 			if (f->fs_ep_max != 0) {
248e13e19faSAndrew Thompson 				crd->is_usbfs = 1;
24902ac6454SAndrew Thompson 			}
25002ac6454SAndrew Thompson 		}
25102ac6454SAndrew Thompson 
25202ac6454SAndrew Thompson 		/* check for read */
253ee3e3ff5SAndrew Thompson 		if (cpd->fflags & FREAD) {
254ee3e3ff5SAndrew Thompson 			ppf = cpd->udev->fifo;
255ee3e3ff5SAndrew Thompson 			f = ppf[cpd->fifo_index + USB_FIFO_RX];
256e13e19faSAndrew Thompson 			crd->rxfifo = f;
257e13e19faSAndrew Thompson 			crd->is_read = 1;	/* ref */
258ee3e3ff5SAndrew Thompson 			if (f == NULL || f->refcount == USB_FIFO_REF_MAX)
25902ac6454SAndrew Thompson 				goto error;
2607214348fSAndrew Thompson 			if (f->curr_cpd != cpd)
2617214348fSAndrew Thompson 				goto error;
26202ac6454SAndrew Thompson 			/* check if USB-FS is active */
26302ac6454SAndrew Thompson 			if (f->fs_ep_max != 0) {
264e13e19faSAndrew Thompson 				crd->is_usbfs = 1;
26502ac6454SAndrew Thompson 			}
26602ac6454SAndrew Thompson 		}
26702ac6454SAndrew Thompson 	}
26802ac6454SAndrew Thompson 
26902ac6454SAndrew Thompson 	/* when everything is OK we increment the refcounts */
270e13e19faSAndrew Thompson 	if (crd->is_write) {
27102ac6454SAndrew Thompson 		DPRINTFN(2, "ref write\n");
272e13e19faSAndrew Thompson 		crd->txfifo->refcount++;
27302ac6454SAndrew Thompson 	}
274e13e19faSAndrew Thompson 	if (crd->is_read) {
27502ac6454SAndrew Thompson 		DPRINTFN(2, "ref read\n");
276e13e19faSAndrew Thompson 		crd->rxfifo->refcount++;
27702ac6454SAndrew Thompson 	}
278a593f6b8SAndrew Thompson 	mtx_unlock(&usb_ref_lock);
27902ac6454SAndrew Thompson 
28002ac6454SAndrew Thompson 	return (0);
28102ac6454SAndrew Thompson 
28202ac6454SAndrew Thompson error:
283e13e19faSAndrew Thompson 	if (crd->is_uref) {
284cb18f7d1SAlfred Perlstein 		usbd_enum_unlock(cpd->udev);
285cb18f7d1SAlfred Perlstein 
2860ed53d45SAndrew Thompson 		if (--(cpd->udev->refcount) == 0) {
287*91cd9240SAndrew Thompson 			cv_signal(&cpd->udev->ref_cv);
2880ed53d45SAndrew Thompson 		}
2890ed53d45SAndrew Thompson 	}
290a593f6b8SAndrew Thompson 	mtx_unlock(&usb_ref_lock);
29102ac6454SAndrew Thompson 	DPRINTFN(2, "fail\n");
29202ac6454SAndrew Thompson 	return (USB_ERR_INVAL);
29302ac6454SAndrew Thompson }
29402ac6454SAndrew Thompson 
29502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
296a593f6b8SAndrew Thompson  *	usb_usb_ref_device
29702ac6454SAndrew Thompson  *
29802ac6454SAndrew Thompson  * This function is used to upgrade an USB reference to include the
29902ac6454SAndrew Thompson  * USB device reference on a USB location.
30002ac6454SAndrew Thompson  *
30102ac6454SAndrew Thompson  * Return values:
30202ac6454SAndrew Thompson  *  0: Success, refcount incremented on the given USB device.
30302ac6454SAndrew Thompson  *  Else: Failure.
30402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
305e0a69b51SAndrew Thompson static usb_error_t
306a593f6b8SAndrew Thompson usb_usb_ref_device(struct usb_cdev_privdata *cpd,
307e13e19faSAndrew Thompson     struct usb_cdev_refdata *crd)
30802ac6454SAndrew Thompson {
30902ac6454SAndrew Thompson 	/*
31002ac6454SAndrew Thompson 	 * Check if we already got an USB reference on this location:
31102ac6454SAndrew Thompson 	 */
312e13e19faSAndrew Thompson 	if (crd->is_uref)
31302ac6454SAndrew Thompson 		return (0);		/* success */
31402ac6454SAndrew Thompson 
31502ac6454SAndrew Thompson 	/*
3160ed53d45SAndrew Thompson 	 * To avoid deadlock at detach we need to drop the FIFO ref
3170ed53d45SAndrew Thompson 	 * and re-acquire a new ref!
31802ac6454SAndrew Thompson 	 */
319a593f6b8SAndrew Thompson 	usb_unref_device(cpd, crd);
32002ac6454SAndrew Thompson 
321a593f6b8SAndrew Thompson 	return (usb_ref_device(cpd, crd, 1 /* need uref */));
32202ac6454SAndrew Thompson }
32302ac6454SAndrew Thompson 
32402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
325a593f6b8SAndrew Thompson  *	usb_unref_device
32602ac6454SAndrew Thompson  *
32702ac6454SAndrew Thompson  * This function will release the reference count by one unit for the
32802ac6454SAndrew Thompson  * given USB device.
32902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
33002ac6454SAndrew Thompson void
331a593f6b8SAndrew Thompson usb_unref_device(struct usb_cdev_privdata *cpd,
332e13e19faSAndrew Thompson     struct usb_cdev_refdata *crd)
33302ac6454SAndrew Thompson {
334a488edb5SAndrew Thompson 
335e13e19faSAndrew Thompson 	DPRINTFN(2, "cpd=%p is_uref=%d\n", cpd, crd->is_uref);
336a488edb5SAndrew Thompson 
337cb18f7d1SAlfred Perlstein 	if (crd->is_uref)
338cb18f7d1SAlfred Perlstein 		usbd_enum_unlock(cpd->udev);
339cb18f7d1SAlfred Perlstein 
340a593f6b8SAndrew Thompson 	mtx_lock(&usb_ref_lock);
341e13e19faSAndrew Thompson 	if (crd->is_read) {
342e13e19faSAndrew Thompson 		if (--(crd->rxfifo->refcount) == 0) {
3438437751dSAndrew Thompson 			cv_signal(&crd->rxfifo->cv_drain);
34402ac6454SAndrew Thompson 		}
345e13e19faSAndrew Thompson 		crd->is_read = 0;
34602ac6454SAndrew Thompson 	}
347e13e19faSAndrew Thompson 	if (crd->is_write) {
348e13e19faSAndrew Thompson 		if (--(crd->txfifo->refcount) == 0) {
3498437751dSAndrew Thompson 			cv_signal(&crd->txfifo->cv_drain);
35002ac6454SAndrew Thompson 		}
351e13e19faSAndrew Thompson 		crd->is_write = 0;
35202ac6454SAndrew Thompson 	}
353e13e19faSAndrew Thompson 	if (crd->is_uref) {
354ee3e3ff5SAndrew Thompson 		if (--(cpd->udev->refcount) == 0) {
355*91cd9240SAndrew Thompson 			cv_signal(&cpd->udev->ref_cv);
35602ac6454SAndrew Thompson 		}
357e13e19faSAndrew Thompson 		crd->is_uref = 0;
35802ac6454SAndrew Thompson 	}
359a593f6b8SAndrew Thompson 	mtx_unlock(&usb_ref_lock);
36002ac6454SAndrew Thompson }
36102ac6454SAndrew Thompson 
362760bc48eSAndrew Thompson static struct usb_fifo *
363a593f6b8SAndrew Thompson usb_fifo_alloc(void)
36402ac6454SAndrew Thompson {
365760bc48eSAndrew Thompson 	struct usb_fifo *f;
36602ac6454SAndrew Thompson 
36702ac6454SAndrew Thompson 	f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO);
36802ac6454SAndrew Thompson 	if (f) {
3698437751dSAndrew Thompson 		cv_init(&f->cv_io, "FIFO-IO");
3708437751dSAndrew Thompson 		cv_init(&f->cv_drain, "FIFO-DRAIN");
37102ac6454SAndrew Thompson 		f->refcount = 1;
37202ac6454SAndrew Thompson 	}
37302ac6454SAndrew Thompson 	return (f);
37402ac6454SAndrew Thompson }
37502ac6454SAndrew Thompson 
37602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
377a593f6b8SAndrew Thompson  *	usb_fifo_create
37802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
37902ac6454SAndrew Thompson static int
380a593f6b8SAndrew Thompson usb_fifo_create(struct usb_cdev_privdata *cpd,
381e13e19faSAndrew Thompson     struct usb_cdev_refdata *crd)
38202ac6454SAndrew Thompson {
383760bc48eSAndrew Thompson 	struct usb_device *udev = cpd->udev;
384760bc48eSAndrew Thompson 	struct usb_fifo *f;
385ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep;
38602ac6454SAndrew Thompson 	uint8_t n;
38702ac6454SAndrew Thompson 	uint8_t is_tx;
38802ac6454SAndrew Thompson 	uint8_t is_rx;
38902ac6454SAndrew Thompson 	uint8_t no_null;
39002ac6454SAndrew Thompson 	uint8_t is_busy;
391ae60fdfbSAndrew Thompson 	int e = cpd->ep_addr;
39202ac6454SAndrew Thompson 
393ee3e3ff5SAndrew Thompson 	is_tx = (cpd->fflags & FWRITE) ? 1 : 0;
394ee3e3ff5SAndrew Thompson 	is_rx = (cpd->fflags & FREAD) ? 1 : 0;
39502ac6454SAndrew Thompson 	no_null = 1;
39602ac6454SAndrew Thompson 	is_busy = 0;
39702ac6454SAndrew Thompson 
398ee3e3ff5SAndrew Thompson 	/* Preallocated FIFO */
399ae60fdfbSAndrew Thompson 	if (e < 0) {
400ee3e3ff5SAndrew Thompson 		DPRINTFN(5, "Preallocated FIFO\n");
401ee3e3ff5SAndrew Thompson 		if (is_tx) {
402ee3e3ff5SAndrew Thompson 			f = udev->fifo[cpd->fifo_index + USB_FIFO_TX];
403ee3e3ff5SAndrew Thompson 			if (f == NULL)
404ee3e3ff5SAndrew Thompson 				return (EINVAL);
405e13e19faSAndrew Thompson 			crd->txfifo = f;
406ee3e3ff5SAndrew Thompson 		}
407ee3e3ff5SAndrew Thompson 		if (is_rx) {
408ee3e3ff5SAndrew Thompson 			f = udev->fifo[cpd->fifo_index + USB_FIFO_RX];
409ee3e3ff5SAndrew Thompson 			if (f == NULL)
410ee3e3ff5SAndrew Thompson 				return (EINVAL);
411e13e19faSAndrew Thompson 			crd->rxfifo = f;
412ee3e3ff5SAndrew Thompson 		}
413ee3e3ff5SAndrew Thompson 		return (0);
414ee3e3ff5SAndrew Thompson 	}
41502ac6454SAndrew Thompson 
416ae60fdfbSAndrew Thompson 	KASSERT(e >= 0 && e <= 15, ("endpoint %d out of range", e));
417ee3e3ff5SAndrew Thompson 
418ee3e3ff5SAndrew Thompson 	/* search for a free FIFO slot */
419ae60fdfbSAndrew Thompson 	DPRINTFN(5, "Endpoint device, searching for 0x%02x\n", e);
42002ac6454SAndrew Thompson 	for (n = 0;; n += 2) {
42102ac6454SAndrew Thompson 
42202ac6454SAndrew Thompson 		if (n == USB_FIFO_MAX) {
42302ac6454SAndrew Thompson 			if (no_null) {
42402ac6454SAndrew Thompson 				no_null = 0;
42502ac6454SAndrew Thompson 				n = 0;
42602ac6454SAndrew Thompson 			} else {
42702ac6454SAndrew Thompson 				/* end of FIFOs reached */
428ee3e3ff5SAndrew Thompson 				DPRINTFN(5, "out of FIFOs\n");
42902ac6454SAndrew Thompson 				return (ENOMEM);
43002ac6454SAndrew Thompson 			}
43102ac6454SAndrew Thompson 		}
43202ac6454SAndrew Thompson 		/* Check for TX FIFO */
43302ac6454SAndrew Thompson 		if (is_tx) {
43402ac6454SAndrew Thompson 			f = udev->fifo[n + USB_FIFO_TX];
43502ac6454SAndrew Thompson 			if (f != NULL) {
436ae60fdfbSAndrew Thompson 				if (f->dev_ep_index != e) {
43702ac6454SAndrew Thompson 					/* wrong endpoint index */
43802ac6454SAndrew Thompson 					continue;
43902ac6454SAndrew Thompson 				}
4407214348fSAndrew Thompson 				if (f->curr_cpd != NULL) {
44102ac6454SAndrew Thompson 					/* FIFO is opened */
44202ac6454SAndrew Thompson 					is_busy = 1;
44302ac6454SAndrew Thompson 					continue;
44402ac6454SAndrew Thompson 				}
44502ac6454SAndrew Thompson 			} else if (no_null) {
44602ac6454SAndrew Thompson 				continue;
44702ac6454SAndrew Thompson 			}
44802ac6454SAndrew Thompson 		}
44902ac6454SAndrew Thompson 		/* Check for RX FIFO */
45002ac6454SAndrew Thompson 		if (is_rx) {
45102ac6454SAndrew Thompson 			f = udev->fifo[n + USB_FIFO_RX];
45202ac6454SAndrew Thompson 			if (f != NULL) {
453ae60fdfbSAndrew Thompson 				if (f->dev_ep_index != e) {
45402ac6454SAndrew Thompson 					/* wrong endpoint index */
45502ac6454SAndrew Thompson 					continue;
45602ac6454SAndrew Thompson 				}
4577214348fSAndrew Thompson 				if (f->curr_cpd != NULL) {
45802ac6454SAndrew Thompson 					/* FIFO is opened */
45902ac6454SAndrew Thompson 					is_busy = 1;
46002ac6454SAndrew Thompson 					continue;
46102ac6454SAndrew Thompson 				}
46202ac6454SAndrew Thompson 			} else if (no_null) {
46302ac6454SAndrew Thompson 				continue;
46402ac6454SAndrew Thompson 			}
46502ac6454SAndrew Thompson 		}
46602ac6454SAndrew Thompson 		break;
46702ac6454SAndrew Thompson 	}
46802ac6454SAndrew Thompson 
46902ac6454SAndrew Thompson 	if (no_null == 0) {
470ae60fdfbSAndrew Thompson 		if (e >= (USB_EP_MAX / 2)) {
47102ac6454SAndrew Thompson 			/* we don't create any endpoints in this range */
4727214348fSAndrew Thompson 			DPRINTFN(5, "ep out of range\n");
47302ac6454SAndrew Thompson 			return (is_busy ? EBUSY : EINVAL);
47402ac6454SAndrew Thompson 		}
47502ac6454SAndrew Thompson 	}
4767214348fSAndrew Thompson 
477ae60fdfbSAndrew Thompson 	if ((e != 0) && is_busy) {
4787214348fSAndrew Thompson 		/*
4797214348fSAndrew Thompson 		 * Only the default control endpoint is allowed to be
4807214348fSAndrew Thompson 		 * opened multiple times!
4817214348fSAndrew Thompson 		 */
4827214348fSAndrew Thompson 		DPRINTFN(5, "busy\n");
4837214348fSAndrew Thompson 		return (EBUSY);
4847214348fSAndrew Thompson 	}
4857214348fSAndrew Thompson 
48602ac6454SAndrew Thompson 	/* Check TX FIFO */
48702ac6454SAndrew Thompson 	if (is_tx &&
48802ac6454SAndrew Thompson 	    (udev->fifo[n + USB_FIFO_TX] == NULL)) {
489a593f6b8SAndrew Thompson 		ep = usb_dev_get_ep(udev, e, USB_FIFO_TX);
490ae60fdfbSAndrew Thompson 		DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_TX);
491ae60fdfbSAndrew Thompson 		if (ep == NULL) {
492ae60fdfbSAndrew Thompson 			DPRINTFN(5, "dev_get_endpoint returned NULL\n");
49302ac6454SAndrew Thompson 			return (EINVAL);
49402ac6454SAndrew Thompson 		}
495a593f6b8SAndrew Thompson 		f = usb_fifo_alloc();
49602ac6454SAndrew Thompson 		if (f == NULL) {
497ee3e3ff5SAndrew Thompson 			DPRINTFN(5, "could not alloc tx fifo\n");
49802ac6454SAndrew Thompson 			return (ENOMEM);
49902ac6454SAndrew Thompson 		}
50002ac6454SAndrew Thompson 		/* update some fields */
50102ac6454SAndrew Thompson 		f->fifo_index = n + USB_FIFO_TX;
502ae60fdfbSAndrew Thompson 		f->dev_ep_index = e;
503*91cd9240SAndrew Thompson 		f->priv_mtx = &udev->device_mtx;
504ae60fdfbSAndrew Thompson 		f->priv_sc0 = ep;
505a593f6b8SAndrew Thompson 		f->methods = &usb_ugen_methods;
506ae60fdfbSAndrew Thompson 		f->iface_index = ep->iface_index;
50702ac6454SAndrew Thompson 		f->udev = udev;
508a593f6b8SAndrew Thompson 		mtx_lock(&usb_ref_lock);
50902ac6454SAndrew Thompson 		udev->fifo[n + USB_FIFO_TX] = f;
510a593f6b8SAndrew Thompson 		mtx_unlock(&usb_ref_lock);
51102ac6454SAndrew Thompson 	}
51202ac6454SAndrew Thompson 	/* Check RX FIFO */
51302ac6454SAndrew Thompson 	if (is_rx &&
51402ac6454SAndrew Thompson 	    (udev->fifo[n + USB_FIFO_RX] == NULL)) {
51502ac6454SAndrew Thompson 
516a593f6b8SAndrew Thompson 		ep = usb_dev_get_ep(udev, e, USB_FIFO_RX);
517ae60fdfbSAndrew Thompson 		DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_RX);
518ae60fdfbSAndrew Thompson 		if (ep == NULL) {
519ae60fdfbSAndrew Thompson 			DPRINTFN(5, "dev_get_endpoint returned NULL\n");
52002ac6454SAndrew Thompson 			return (EINVAL);
52102ac6454SAndrew Thompson 		}
522a593f6b8SAndrew Thompson 		f = usb_fifo_alloc();
52302ac6454SAndrew Thompson 		if (f == NULL) {
524ee3e3ff5SAndrew Thompson 			DPRINTFN(5, "could not alloc rx fifo\n");
52502ac6454SAndrew Thompson 			return (ENOMEM);
52602ac6454SAndrew Thompson 		}
52702ac6454SAndrew Thompson 		/* update some fields */
52802ac6454SAndrew Thompson 		f->fifo_index = n + USB_FIFO_RX;
529ae60fdfbSAndrew Thompson 		f->dev_ep_index = e;
530*91cd9240SAndrew Thompson 		f->priv_mtx = &udev->device_mtx;
531ae60fdfbSAndrew Thompson 		f->priv_sc0 = ep;
532a593f6b8SAndrew Thompson 		f->methods = &usb_ugen_methods;
533ae60fdfbSAndrew Thompson 		f->iface_index = ep->iface_index;
53402ac6454SAndrew Thompson 		f->udev = udev;
535a593f6b8SAndrew Thompson 		mtx_lock(&usb_ref_lock);
53602ac6454SAndrew Thompson 		udev->fifo[n + USB_FIFO_RX] = f;
537a593f6b8SAndrew Thompson 		mtx_unlock(&usb_ref_lock);
53802ac6454SAndrew Thompson 	}
53902ac6454SAndrew Thompson 	if (is_tx) {
540e13e19faSAndrew Thompson 		crd->txfifo = udev->fifo[n + USB_FIFO_TX];
54102ac6454SAndrew Thompson 	}
54202ac6454SAndrew Thompson 	if (is_rx) {
543e13e19faSAndrew Thompson 		crd->rxfifo = udev->fifo[n + USB_FIFO_RX];
54402ac6454SAndrew Thompson 	}
545ee3e3ff5SAndrew Thompson 	/* fill out fifo index */
546ee3e3ff5SAndrew Thompson 	DPRINTFN(5, "fifo index = %d\n", n);
547ee3e3ff5SAndrew Thompson 	cpd->fifo_index = n;
54802ac6454SAndrew Thompson 
54902ac6454SAndrew Thompson 	/* complete */
55002ac6454SAndrew Thompson 
55102ac6454SAndrew Thompson 	return (0);
55202ac6454SAndrew Thompson }
55302ac6454SAndrew Thompson 
55402ac6454SAndrew Thompson void
555a593f6b8SAndrew Thompson usb_fifo_free(struct usb_fifo *f)
55602ac6454SAndrew Thompson {
55702ac6454SAndrew Thompson 	uint8_t n;
55802ac6454SAndrew Thompson 
55902ac6454SAndrew Thompson 	if (f == NULL) {
56002ac6454SAndrew Thompson 		/* be NULL safe */
56102ac6454SAndrew Thompson 		return;
56202ac6454SAndrew Thompson 	}
56302ac6454SAndrew Thompson 	/* destroy symlink devices, if any */
56402ac6454SAndrew Thompson 	for (n = 0; n != 2; n++) {
56502ac6454SAndrew Thompson 		if (f->symlink[n]) {
566a593f6b8SAndrew Thompson 			usb_free_symlink(f->symlink[n]);
56702ac6454SAndrew Thompson 			f->symlink[n] = NULL;
56802ac6454SAndrew Thompson 		}
56902ac6454SAndrew Thompson 	}
570a593f6b8SAndrew Thompson 	mtx_lock(&usb_ref_lock);
57102ac6454SAndrew Thompson 
57202ac6454SAndrew Thompson 	/* delink ourselves to stop calls from userland */
57302ac6454SAndrew Thompson 	if ((f->fifo_index < USB_FIFO_MAX) &&
57402ac6454SAndrew Thompson 	    (f->udev != NULL) &&
57502ac6454SAndrew Thompson 	    (f->udev->fifo[f->fifo_index] == f)) {
57602ac6454SAndrew Thompson 		f->udev->fifo[f->fifo_index] = NULL;
57702ac6454SAndrew Thompson 	} else {
578767cb2e2SAndrew Thompson 		DPRINTFN(0, "USB FIFO %p has not been linked\n", f);
57902ac6454SAndrew Thompson 	}
58002ac6454SAndrew Thompson 
58102ac6454SAndrew Thompson 	/* decrease refcount */
58202ac6454SAndrew Thompson 	f->refcount--;
58302ac6454SAndrew Thompson 	/* prevent any write flush */
58402ac6454SAndrew Thompson 	f->flag_iserror = 1;
58502ac6454SAndrew Thompson 	/* need to wait until all callers have exited */
58602ac6454SAndrew Thompson 	while (f->refcount != 0) {
587a593f6b8SAndrew Thompson 		mtx_unlock(&usb_ref_lock);	/* avoid LOR */
58802ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
58902ac6454SAndrew Thompson 		/* get I/O thread out of any sleep state */
59002ac6454SAndrew Thompson 		if (f->flag_sleeping) {
59102ac6454SAndrew Thompson 			f->flag_sleeping = 0;
5928437751dSAndrew Thompson 			cv_broadcast(&f->cv_io);
59302ac6454SAndrew Thompson 		}
59402ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
595a593f6b8SAndrew Thompson 		mtx_lock(&usb_ref_lock);
59602ac6454SAndrew Thompson 
59702ac6454SAndrew Thompson 		/* wait for sync */
598a593f6b8SAndrew Thompson 		cv_wait(&f->cv_drain, &usb_ref_lock);
59902ac6454SAndrew Thompson 	}
600a593f6b8SAndrew Thompson 	mtx_unlock(&usb_ref_lock);
60102ac6454SAndrew Thompson 
60202ac6454SAndrew Thompson 	/* take care of closing the device here, if any */
603a593f6b8SAndrew Thompson 	usb_fifo_close(f, 0);
60402ac6454SAndrew Thompson 
6058437751dSAndrew Thompson 	cv_destroy(&f->cv_io);
6068437751dSAndrew Thompson 	cv_destroy(&f->cv_drain);
60702ac6454SAndrew Thompson 
60802ac6454SAndrew Thompson 	free(f, M_USBDEV);
60902ac6454SAndrew Thompson }
61002ac6454SAndrew Thompson 
611ae60fdfbSAndrew Thompson static struct usb_endpoint *
612a593f6b8SAndrew Thompson usb_dev_get_ep(struct usb_device *udev, uint8_t ep_index, uint8_t dir)
61302ac6454SAndrew Thompson {
614ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep;
61502ac6454SAndrew Thompson 	uint8_t ep_dir;
61602ac6454SAndrew Thompson 
61702ac6454SAndrew Thompson 	if (ep_index == 0) {
618ae60fdfbSAndrew Thompson 		ep = &udev->default_ep;
61902ac6454SAndrew Thompson 	} else {
62002ac6454SAndrew Thompson 		if (dir == USB_FIFO_RX) {
621f29a0724SAndrew Thompson 			if (udev->flags.usb_mode == USB_MODE_HOST) {
62202ac6454SAndrew Thompson 				ep_dir = UE_DIR_IN;
62302ac6454SAndrew Thompson 			} else {
62402ac6454SAndrew Thompson 				ep_dir = UE_DIR_OUT;
62502ac6454SAndrew Thompson 			}
62602ac6454SAndrew Thompson 		} else {
627f29a0724SAndrew Thompson 			if (udev->flags.usb_mode == USB_MODE_HOST) {
62802ac6454SAndrew Thompson 				ep_dir = UE_DIR_OUT;
62902ac6454SAndrew Thompson 			} else {
63002ac6454SAndrew Thompson 				ep_dir = UE_DIR_IN;
63102ac6454SAndrew Thompson 			}
63202ac6454SAndrew Thompson 		}
633a593f6b8SAndrew Thompson 		ep = usbd_get_ep_by_addr(udev, ep_index | ep_dir);
63402ac6454SAndrew Thompson 	}
63502ac6454SAndrew Thompson 
636ae60fdfbSAndrew Thompson 	if (ep == NULL) {
637ae60fdfbSAndrew Thompson 		/* if the endpoint does not exist then return */
63802ac6454SAndrew Thompson 		return (NULL);
63902ac6454SAndrew Thompson 	}
640ae60fdfbSAndrew Thompson 	if (ep->edesc == NULL) {
641ae60fdfbSAndrew Thompson 		/* invalid endpoint */
64202ac6454SAndrew Thompson 		return (NULL);
64302ac6454SAndrew Thompson 	}
644ae60fdfbSAndrew Thompson 	return (ep);			/* success */
64502ac6454SAndrew Thompson }
64602ac6454SAndrew Thompson 
64702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
648a593f6b8SAndrew Thompson  *	usb_fifo_open
64902ac6454SAndrew Thompson  *
65002ac6454SAndrew Thompson  * Returns:
65102ac6454SAndrew Thompson  * 0: Success
65202ac6454SAndrew Thompson  * Else: Failure
65302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
65402ac6454SAndrew Thompson static int
655a593f6b8SAndrew Thompson usb_fifo_open(struct usb_cdev_privdata *cpd,
656760bc48eSAndrew Thompson     struct usb_fifo *f, int fflags)
65702ac6454SAndrew Thompson {
65802ac6454SAndrew Thompson 	int err;
65902ac6454SAndrew Thompson 
66002ac6454SAndrew Thompson 	if (f == NULL) {
66102ac6454SAndrew Thompson 		/* no FIFO there */
66202ac6454SAndrew Thompson 		DPRINTFN(2, "no FIFO\n");
66302ac6454SAndrew Thompson 		return (ENXIO);
66402ac6454SAndrew Thompson 	}
66502ac6454SAndrew Thompson 	/* remove FWRITE and FREAD flags */
66602ac6454SAndrew Thompson 	fflags &= ~(FWRITE | FREAD);
66702ac6454SAndrew Thompson 
66802ac6454SAndrew Thompson 	/* set correct file flags */
66902ac6454SAndrew Thompson 	if ((f->fifo_index & 1) == USB_FIFO_TX) {
67002ac6454SAndrew Thompson 		fflags |= FWRITE;
67102ac6454SAndrew Thompson 	} else {
67202ac6454SAndrew Thompson 		fflags |= FREAD;
67302ac6454SAndrew Thompson 	}
67402ac6454SAndrew Thompson 
67502ac6454SAndrew Thompson 	/* check if we are already opened */
67602ac6454SAndrew Thompson 	/* we don't need any locks when checking this variable */
6777214348fSAndrew Thompson 	if (f->curr_cpd != NULL) {
67802ac6454SAndrew Thompson 		err = EBUSY;
67902ac6454SAndrew Thompson 		goto done;
68002ac6454SAndrew Thompson 	}
681ee3e3ff5SAndrew Thompson 
6827214348fSAndrew Thompson 	/* reset short flag before open */
6837214348fSAndrew Thompson 	f->flag_short = 0;
6847214348fSAndrew Thompson 
68502ac6454SAndrew Thompson 	/* call open method */
686ee3e3ff5SAndrew Thompson 	err = (f->methods->f_open) (f, fflags);
68702ac6454SAndrew Thompson 	if (err) {
68802ac6454SAndrew Thompson 		goto done;
68902ac6454SAndrew Thompson 	}
69002ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
69102ac6454SAndrew Thompson 
69202ac6454SAndrew Thompson 	/* reset sleep flag */
69302ac6454SAndrew Thompson 	f->flag_sleeping = 0;
69402ac6454SAndrew Thompson 
69502ac6454SAndrew Thompson 	/* reset error flag */
69602ac6454SAndrew Thompson 	f->flag_iserror = 0;
69702ac6454SAndrew Thompson 
69802ac6454SAndrew Thompson 	/* reset complete flag */
69902ac6454SAndrew Thompson 	f->flag_iscomplete = 0;
70002ac6454SAndrew Thompson 
70102ac6454SAndrew Thompson 	/* reset select flag */
70202ac6454SAndrew Thompson 	f->flag_isselect = 0;
70302ac6454SAndrew Thompson 
70402ac6454SAndrew Thompson 	/* reset flushing flag */
70502ac6454SAndrew Thompson 	f->flag_flushing = 0;
70602ac6454SAndrew Thompson 
70702ac6454SAndrew Thompson 	/* reset ASYNC proc flag */
70802ac6454SAndrew Thompson 	f->async_p = NULL;
70902ac6454SAndrew Thompson 
710a593f6b8SAndrew Thompson 	mtx_lock(&usb_ref_lock);
7117214348fSAndrew Thompson 	/* flag the fifo as opened to prevent others */
7127214348fSAndrew Thompson 	f->curr_cpd = cpd;
713a593f6b8SAndrew Thompson 	mtx_unlock(&usb_ref_lock);
71402ac6454SAndrew Thompson 
71502ac6454SAndrew Thompson 	/* reset queue */
716a593f6b8SAndrew Thompson 	usb_fifo_reset(f);
71702ac6454SAndrew Thompson 
71802ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
71902ac6454SAndrew Thompson done:
72002ac6454SAndrew Thompson 	return (err);
72102ac6454SAndrew Thompson }
72202ac6454SAndrew Thompson 
72302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
724a593f6b8SAndrew Thompson  *	usb_fifo_reset
72502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
72602ac6454SAndrew Thompson void
727a593f6b8SAndrew Thompson usb_fifo_reset(struct usb_fifo *f)
72802ac6454SAndrew Thompson {
729760bc48eSAndrew Thompson 	struct usb_mbuf *m;
73002ac6454SAndrew Thompson 
73102ac6454SAndrew Thompson 	if (f == NULL) {
73202ac6454SAndrew Thompson 		return;
73302ac6454SAndrew Thompson 	}
73402ac6454SAndrew Thompson 	while (1) {
73502ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
73602ac6454SAndrew Thompson 		if (m) {
73702ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->free_q, m);
73802ac6454SAndrew Thompson 		} else {
73902ac6454SAndrew Thompson 			break;
74002ac6454SAndrew Thompson 		}
74102ac6454SAndrew Thompson 	}
742bd73b187SAlfred Perlstein 	/* reset have fragment flag */
743bd73b187SAlfred Perlstein 	f->flag_have_fragment = 0;
74402ac6454SAndrew Thompson }
74502ac6454SAndrew Thompson 
74602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
747a593f6b8SAndrew Thompson  *	usb_fifo_close
74802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
74902ac6454SAndrew Thompson static void
750a593f6b8SAndrew Thompson usb_fifo_close(struct usb_fifo *f, int fflags)
75102ac6454SAndrew Thompson {
75202ac6454SAndrew Thompson 	int err;
75302ac6454SAndrew Thompson 
75402ac6454SAndrew Thompson 	/* check if we are not opened */
7557214348fSAndrew Thompson 	if (f->curr_cpd == NULL) {
75602ac6454SAndrew Thompson 		/* nothing to do - already closed */
75702ac6454SAndrew Thompson 		return;
75802ac6454SAndrew Thompson 	}
75902ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
76002ac6454SAndrew Thompson 
7617214348fSAndrew Thompson 	/* clear current cdev private data pointer */
7627214348fSAndrew Thompson 	f->curr_cpd = NULL;
76302ac6454SAndrew Thompson 
76402ac6454SAndrew Thompson 	/* check if we are selected */
76502ac6454SAndrew Thompson 	if (f->flag_isselect) {
76602ac6454SAndrew Thompson 		selwakeup(&f->selinfo);
76702ac6454SAndrew Thompson 		f->flag_isselect = 0;
76802ac6454SAndrew Thompson 	}
76902ac6454SAndrew Thompson 	/* check if a thread wants SIGIO */
77002ac6454SAndrew Thompson 	if (f->async_p != NULL) {
77102ac6454SAndrew Thompson 		PROC_LOCK(f->async_p);
77202ac6454SAndrew Thompson 		psignal(f->async_p, SIGIO);
77302ac6454SAndrew Thompson 		PROC_UNLOCK(f->async_p);
77402ac6454SAndrew Thompson 		f->async_p = NULL;
77502ac6454SAndrew Thompson 	}
77602ac6454SAndrew Thompson 	/* remove FWRITE and FREAD flags */
77702ac6454SAndrew Thompson 	fflags &= ~(FWRITE | FREAD);
77802ac6454SAndrew Thompson 
77902ac6454SAndrew Thompson 	/* flush written data, if any */
78002ac6454SAndrew Thompson 	if ((f->fifo_index & 1) == USB_FIFO_TX) {
78102ac6454SAndrew Thompson 
78202ac6454SAndrew Thompson 		if (!f->flag_iserror) {
78302ac6454SAndrew Thompson 
78402ac6454SAndrew Thompson 			/* set flushing flag */
78502ac6454SAndrew Thompson 			f->flag_flushing = 1;
78602ac6454SAndrew Thompson 
787bd73b187SAlfred Perlstein 			/* get the last packet in */
788bd73b187SAlfred Perlstein 			if (f->flag_have_fragment) {
789bd73b187SAlfred Perlstein 				struct usb_mbuf *m;
790bd73b187SAlfred Perlstein 				f->flag_have_fragment = 0;
791bd73b187SAlfred Perlstein 				USB_IF_DEQUEUE(&f->free_q, m);
792bd73b187SAlfred Perlstein 				if (m) {
793bd73b187SAlfred Perlstein 					USB_IF_ENQUEUE(&f->used_q, m);
794bd73b187SAlfred Perlstein 				}
795bd73b187SAlfred Perlstein 			}
796bd73b187SAlfred Perlstein 
79702ac6454SAndrew Thompson 			/* start write transfer, if not already started */
79802ac6454SAndrew Thompson 			(f->methods->f_start_write) (f);
79902ac6454SAndrew Thompson 
80002ac6454SAndrew Thompson 			/* check if flushed already */
80102ac6454SAndrew Thompson 			while (f->flag_flushing &&
80202ac6454SAndrew Thompson 			    (!f->flag_iserror)) {
80302ac6454SAndrew Thompson 				/* wait until all data has been written */
80402ac6454SAndrew Thompson 				f->flag_sleeping = 1;
8058437751dSAndrew Thompson 				err = cv_wait_sig(&f->cv_io, f->priv_mtx);
80602ac6454SAndrew Thompson 				if (err) {
80702ac6454SAndrew Thompson 					DPRINTF("signal received\n");
80802ac6454SAndrew Thompson 					break;
80902ac6454SAndrew Thompson 				}
81002ac6454SAndrew Thompson 			}
81102ac6454SAndrew Thompson 		}
81202ac6454SAndrew Thompson 		fflags |= FWRITE;
81302ac6454SAndrew Thompson 
81402ac6454SAndrew Thompson 		/* stop write transfer, if not already stopped */
81502ac6454SAndrew Thompson 		(f->methods->f_stop_write) (f);
81602ac6454SAndrew Thompson 	} else {
81702ac6454SAndrew Thompson 		fflags |= FREAD;
81802ac6454SAndrew Thompson 
81902ac6454SAndrew Thompson 		/* stop write transfer, if not already stopped */
82002ac6454SAndrew Thompson 		(f->methods->f_stop_read) (f);
82102ac6454SAndrew Thompson 	}
82202ac6454SAndrew Thompson 
82302ac6454SAndrew Thompson 	/* check if we are sleeping */
82402ac6454SAndrew Thompson 	if (f->flag_sleeping) {
82502ac6454SAndrew Thompson 		DPRINTFN(2, "Sleeping at close!\n");
82602ac6454SAndrew Thompson 	}
82702ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
82802ac6454SAndrew Thompson 
82902ac6454SAndrew Thompson 	/* call close method */
830ee3e3ff5SAndrew Thompson 	(f->methods->f_close) (f, fflags);
83102ac6454SAndrew Thompson 
83202ac6454SAndrew Thompson 	DPRINTF("closed\n");
83302ac6454SAndrew Thompson }
83402ac6454SAndrew Thompson 
83502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
836a593f6b8SAndrew Thompson  *	usb_open - cdev callback
83702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
83802ac6454SAndrew Thompson static int
839a593f6b8SAndrew Thompson usb_open(struct cdev *dev, int fflags, int devtype, struct thread *td)
84002ac6454SAndrew Thompson {
841760bc48eSAndrew Thompson 	struct usb_fs_privdata* pd = (struct usb_fs_privdata*)dev->si_drv1;
842e13e19faSAndrew Thompson 	struct usb_cdev_refdata refs;
843760bc48eSAndrew Thompson 	struct usb_cdev_privdata *cpd;
844ee3e3ff5SAndrew Thompson 	int err, ep;
84502ac6454SAndrew Thompson 
846cba30496SAndrew Thompson 	DPRINTFN(2, "%s fflags=0x%08x\n", dev->si_name, fflags);
84702ac6454SAndrew Thompson 
848ee3e3ff5SAndrew Thompson 	KASSERT(fflags & (FREAD|FWRITE), ("invalid open flags"));
849ee3e3ff5SAndrew Thompson 	if (((fflags & FREAD) && !(pd->mode & FREAD)) ||
850ee3e3ff5SAndrew Thompson 	    ((fflags & FWRITE) && !(pd->mode & FWRITE))) {
851ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "access mode not supported\n");
85202ac6454SAndrew Thompson 		return (EPERM);
85302ac6454SAndrew Thompson 	}
854ee3e3ff5SAndrew Thompson 
855ee3e3ff5SAndrew Thompson 	cpd = malloc(sizeof(*cpd), M_USBDEV, M_WAITOK | M_ZERO);
856ee3e3ff5SAndrew Thompson 	ep = cpd->ep_addr = pd->ep_addr;
857ee3e3ff5SAndrew Thompson 
858a593f6b8SAndrew Thompson 	usb_loc_fill(pd, cpd);
859a593f6b8SAndrew Thompson 	err = usb_ref_device(cpd, &refs, 1);
86002ac6454SAndrew Thompson 	if (err) {
86102ac6454SAndrew Thompson 		DPRINTFN(2, "cannot ref device\n");
862ee3e3ff5SAndrew Thompson 		free(cpd, M_USBDEV);
86302ac6454SAndrew Thompson 		return (ENXIO);
86402ac6454SAndrew Thompson 	}
865ee3e3ff5SAndrew Thompson 	cpd->fflags = fflags;	/* access mode for open lifetime */
86602ac6454SAndrew Thompson 
86702ac6454SAndrew Thompson 	/* create FIFOs, if any */
868a593f6b8SAndrew Thompson 	err = usb_fifo_create(cpd, &refs);
86902ac6454SAndrew Thompson 	/* check for error */
87002ac6454SAndrew Thompson 	if (err) {
871ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "cannot create fifo\n");
872a593f6b8SAndrew Thompson 		usb_unref_device(cpd, &refs);
873ee3e3ff5SAndrew Thompson 		free(cpd, M_USBDEV);
87402ac6454SAndrew Thompson 		return (err);
87502ac6454SAndrew Thompson 	}
87602ac6454SAndrew Thompson 	if (fflags & FREAD) {
877a593f6b8SAndrew Thompson 		err = usb_fifo_open(cpd, refs.rxfifo, fflags);
87802ac6454SAndrew Thompson 		if (err) {
87902ac6454SAndrew Thompson 			DPRINTFN(2, "read open failed\n");
880a593f6b8SAndrew Thompson 			usb_unref_device(cpd, &refs);
881ee3e3ff5SAndrew Thompson 			free(cpd, M_USBDEV);
88202ac6454SAndrew Thompson 			return (err);
88302ac6454SAndrew Thompson 		}
88402ac6454SAndrew Thompson 	}
88502ac6454SAndrew Thompson 	if (fflags & FWRITE) {
886a593f6b8SAndrew Thompson 		err = usb_fifo_open(cpd, refs.txfifo, fflags);
88702ac6454SAndrew Thompson 		if (err) {
88802ac6454SAndrew Thompson 			DPRINTFN(2, "write open failed\n");
88902ac6454SAndrew Thompson 			if (fflags & FREAD) {
890a593f6b8SAndrew Thompson 				usb_fifo_close(refs.rxfifo, fflags);
89102ac6454SAndrew Thompson 			}
892a593f6b8SAndrew Thompson 			usb_unref_device(cpd, &refs);
893ee3e3ff5SAndrew Thompson 			free(cpd, M_USBDEV);
89402ac6454SAndrew Thompson 			return (err);
89502ac6454SAndrew Thompson 		}
89602ac6454SAndrew Thompson 	}
897a593f6b8SAndrew Thompson 	usb_unref_device(cpd, &refs);
898a593f6b8SAndrew Thompson 	devfs_set_cdevpriv(cpd, usb_close);
89902ac6454SAndrew Thompson 
900ee3e3ff5SAndrew Thompson 	return (0);
90102ac6454SAndrew Thompson }
90202ac6454SAndrew Thompson 
90302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
904a593f6b8SAndrew Thompson  *	usb_close - cdev callback
90502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
90602ac6454SAndrew Thompson static void
907a593f6b8SAndrew Thompson usb_close(void *arg)
90802ac6454SAndrew Thompson {
909e13e19faSAndrew Thompson 	struct usb_cdev_refdata refs;
910760bc48eSAndrew Thompson 	struct usb_cdev_privdata *cpd = arg;
911ee3e3ff5SAndrew Thompson 	int err;
91202ac6454SAndrew Thompson 
9137214348fSAndrew Thompson 	DPRINTFN(2, "cpd=%p\n", cpd);
914ee3e3ff5SAndrew Thompson 
915a593f6b8SAndrew Thompson 	err = usb_ref_device(cpd, &refs, 1);
916ee3e3ff5SAndrew Thompson 	if (err) {
917ee3e3ff5SAndrew Thompson 		free(cpd, M_USBDEV);
91802ac6454SAndrew Thompson 		return;
91902ac6454SAndrew Thompson 	}
920ee3e3ff5SAndrew Thompson 	if (cpd->fflags & FREAD) {
921a593f6b8SAndrew Thompson 		usb_fifo_close(refs.rxfifo, cpd->fflags);
92202ac6454SAndrew Thompson 	}
923ee3e3ff5SAndrew Thompson 	if (cpd->fflags & FWRITE) {
924a593f6b8SAndrew Thompson 		usb_fifo_close(refs.txfifo, cpd->fflags);
92502ac6454SAndrew Thompson 	}
926ee3e3ff5SAndrew Thompson 
927a593f6b8SAndrew Thompson 	usb_unref_device(cpd, &refs);
928ee3e3ff5SAndrew Thompson 	free(cpd, M_USBDEV);
92902ac6454SAndrew Thompson 	return;
93002ac6454SAndrew Thompson }
93102ac6454SAndrew Thompson 
93202ac6454SAndrew Thompson static void
933a593f6b8SAndrew Thompson usb_dev_init(void *arg)
93402ac6454SAndrew Thompson {
935a593f6b8SAndrew Thompson 	mtx_init(&usb_ref_lock, "USB ref mutex", NULL, MTX_DEF);
936a593f6b8SAndrew Thompson 	sx_init(&usb_sym_lock, "USB sym mutex");
937a593f6b8SAndrew Thompson 	TAILQ_INIT(&usb_sym_head);
93802ac6454SAndrew Thompson 
93902ac6454SAndrew Thompson 	/* check the UGEN methods */
940a593f6b8SAndrew Thompson 	usb_fifo_check_methods(&usb_ugen_methods);
94102ac6454SAndrew Thompson }
94202ac6454SAndrew Thompson 
943a593f6b8SAndrew Thompson SYSINIT(usb_dev_init, SI_SUB_KLD, SI_ORDER_FIRST, usb_dev_init, NULL);
94402ac6454SAndrew Thompson 
94502ac6454SAndrew Thompson static void
946a593f6b8SAndrew Thompson usb_dev_init_post(void *arg)
94702ac6454SAndrew Thompson {
94802ac6454SAndrew Thompson 	/*
949ee3e3ff5SAndrew Thompson 	 * Create /dev/usb - this is needed for usbconfig(8), which
950ee3e3ff5SAndrew Thompson 	 * needs a well-known device name to access.
95102ac6454SAndrew Thompson 	 */
952a593f6b8SAndrew Thompson 	usb_dev = make_dev(&usb_static_devsw, 0, UID_ROOT, GID_OPERATOR,
953ee3e3ff5SAndrew Thompson 	    0644, USB_DEVICE_NAME);
954a593f6b8SAndrew Thompson 	if (usb_dev == NULL) {
955767cb2e2SAndrew Thompson 		DPRINTFN(0, "Could not create usb bus device\n");
95602ac6454SAndrew Thompson 	}
95702ac6454SAndrew Thompson }
95802ac6454SAndrew Thompson 
959a593f6b8SAndrew Thompson SYSINIT(usb_dev_init_post, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, usb_dev_init_post, NULL);
96002ac6454SAndrew Thompson 
96102ac6454SAndrew Thompson static void
962a593f6b8SAndrew Thompson usb_dev_uninit(void *arg)
96302ac6454SAndrew Thompson {
964a593f6b8SAndrew Thompson 	if (usb_dev != NULL) {
965a593f6b8SAndrew Thompson 		destroy_dev(usb_dev);
966a593f6b8SAndrew Thompson 		usb_dev = NULL;
967ee3e3ff5SAndrew Thompson 
96802ac6454SAndrew Thompson 	}
969a593f6b8SAndrew Thompson 	mtx_destroy(&usb_ref_lock);
970a593f6b8SAndrew Thompson 	sx_destroy(&usb_sym_lock);
97102ac6454SAndrew Thompson }
97202ac6454SAndrew Thompson 
973a593f6b8SAndrew Thompson SYSUNINIT(usb_dev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb_dev_uninit, NULL);
97402ac6454SAndrew Thompson 
97502ac6454SAndrew Thompson static int
976a593f6b8SAndrew Thompson usb_ioctl_f_sub(struct usb_fifo *f, u_long cmd, void *addr,
97702ac6454SAndrew Thompson     struct thread *td)
97802ac6454SAndrew Thompson {
97902ac6454SAndrew Thompson 	int error = 0;
98002ac6454SAndrew Thompson 
98102ac6454SAndrew Thompson 	switch (cmd) {
98202ac6454SAndrew Thompson 	case FIODTYPE:
98302ac6454SAndrew Thompson 		*(int *)addr = 0;	/* character device */
98402ac6454SAndrew Thompson 		break;
98502ac6454SAndrew Thompson 
98602ac6454SAndrew Thompson 	case FIONBIO:
98702ac6454SAndrew Thompson 		/* handled by upper FS layer */
98802ac6454SAndrew Thompson 		break;
98902ac6454SAndrew Thompson 
99002ac6454SAndrew Thompson 	case FIOASYNC:
99102ac6454SAndrew Thompson 		if (*(int *)addr) {
99202ac6454SAndrew Thompson 			if (f->async_p != NULL) {
99302ac6454SAndrew Thompson 				error = EBUSY;
99402ac6454SAndrew Thompson 				break;
99502ac6454SAndrew Thompson 			}
99602ac6454SAndrew Thompson 			f->async_p = USB_TD_GET_PROC(td);
99702ac6454SAndrew Thompson 		} else {
99802ac6454SAndrew Thompson 			f->async_p = NULL;
99902ac6454SAndrew Thompson 		}
100002ac6454SAndrew Thompson 		break;
100102ac6454SAndrew Thompson 
100202ac6454SAndrew Thompson 		/* XXX this is not the most general solution */
100302ac6454SAndrew Thompson 	case TIOCSPGRP:
100402ac6454SAndrew Thompson 		if (f->async_p == NULL) {
100502ac6454SAndrew Thompson 			error = EINVAL;
100602ac6454SAndrew Thompson 			break;
100702ac6454SAndrew Thompson 		}
100802ac6454SAndrew Thompson 		if (*(int *)addr != USB_PROC_GET_GID(f->async_p)) {
100902ac6454SAndrew Thompson 			error = EPERM;
101002ac6454SAndrew Thompson 			break;
101102ac6454SAndrew Thompson 		}
101202ac6454SAndrew Thompson 		break;
101302ac6454SAndrew Thompson 	default:
101402ac6454SAndrew Thompson 		return (ENOIOCTL);
101502ac6454SAndrew Thompson 	}
1016cba30496SAndrew Thompson 	DPRINTFN(3, "cmd 0x%lx = %d\n", cmd, error);
101702ac6454SAndrew Thompson 	return (error);
101802ac6454SAndrew Thompson }
101902ac6454SAndrew Thompson 
1020ee3e3ff5SAndrew Thompson /*------------------------------------------------------------------------*
1021a593f6b8SAndrew Thompson  *	usb_ioctl - cdev callback
1022ee3e3ff5SAndrew Thompson  *------------------------------------------------------------------------*/
102302ac6454SAndrew Thompson static int
1024a593f6b8SAndrew Thompson usb_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int fflag, struct thread* td)
102502ac6454SAndrew Thompson {
1026e13e19faSAndrew Thompson 	struct usb_cdev_refdata refs;
1027760bc48eSAndrew Thompson 	struct usb_cdev_privdata* cpd;
1028760bc48eSAndrew Thompson 	struct usb_fifo *f;
102902ac6454SAndrew Thompson 	int fflags;
103002ac6454SAndrew Thompson 	int err;
103102ac6454SAndrew Thompson 
1032cba30496SAndrew Thompson 	DPRINTFN(2, "cmd=0x%lx\n", cmd);
1033cba30496SAndrew Thompson 
1034ee3e3ff5SAndrew Thompson 	err = devfs_get_cdevpriv((void **)&cpd);
1035ee3e3ff5SAndrew Thompson 	if (err != 0)
1036ee3e3ff5SAndrew Thompson 		return (err);
1037ee3e3ff5SAndrew Thompson 
1038f5f145baSAndrew Thompson 	/*
1039e13e19faSAndrew Thompson 	 * Performance optimisation: We try to check for IOCTL's that
1040f5f145baSAndrew Thompson 	 * don't need the USB reference first. Then we grab the USB
1041f5f145baSAndrew Thompson 	 * reference if we need it!
1042f5f145baSAndrew Thompson 	 */
1043a593f6b8SAndrew Thompson 	err = usb_ref_device(cpd, &refs, 0 /* no uref */ );
1044cb18f7d1SAlfred Perlstein 	if (err)
104502ac6454SAndrew Thompson 		return (ENXIO);
1046cb18f7d1SAlfred Perlstein 
1047ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
104802ac6454SAndrew Thompson 
104902ac6454SAndrew Thompson 	f = NULL;			/* set default value */
105002ac6454SAndrew Thompson 	err = ENOIOCTL;			/* set default value */
105102ac6454SAndrew Thompson 
105202ac6454SAndrew Thompson 	if (fflags & FWRITE) {
1053e13e19faSAndrew Thompson 		f = refs.txfifo;
1054a593f6b8SAndrew Thompson 		err = usb_ioctl_f_sub(f, cmd, addr, td);
105502ac6454SAndrew Thompson 	}
105602ac6454SAndrew Thompson 	if (fflags & FREAD) {
1057e13e19faSAndrew Thompson 		f = refs.rxfifo;
1058a593f6b8SAndrew Thompson 		err = usb_ioctl_f_sub(f, cmd, addr, td);
105902ac6454SAndrew Thompson 	}
1060ee3e3ff5SAndrew Thompson 	KASSERT(f != NULL, ("fifo not found"));
106102ac6454SAndrew Thompson 	if (err == ENOIOCTL) {
1062ee3e3ff5SAndrew Thompson 		err = (f->methods->f_ioctl) (f, cmd, addr, fflags);
1063cba30496SAndrew Thompson 		DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err);
106402ac6454SAndrew Thompson 		if (err == ENOIOCTL) {
1065a593f6b8SAndrew Thompson 			if (usb_usb_ref_device(cpd, &refs)) {
106602ac6454SAndrew Thompson 				err = ENXIO;
106702ac6454SAndrew Thompson 				goto done;
106802ac6454SAndrew Thompson 			}
1069ee3e3ff5SAndrew Thompson 			err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags);
1070cba30496SAndrew Thompson 			DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err);
107102ac6454SAndrew Thompson 		}
107202ac6454SAndrew Thompson 	}
107302ac6454SAndrew Thompson 	if (err == ENOIOCTL) {
107402ac6454SAndrew Thompson 		err = ENOTTY;
107502ac6454SAndrew Thompson 	}
107602ac6454SAndrew Thompson done:
1077a593f6b8SAndrew Thompson 	usb_unref_device(cpd, &refs);
107802ac6454SAndrew Thompson 	return (err);
107902ac6454SAndrew Thompson }
108002ac6454SAndrew Thompson 
108102ac6454SAndrew Thompson /* ARGSUSED */
108202ac6454SAndrew Thompson static int
1083a593f6b8SAndrew Thompson usb_poll(struct cdev* dev, int events, struct thread* td)
108402ac6454SAndrew Thompson {
1085e13e19faSAndrew Thompson 	struct usb_cdev_refdata refs;
1086760bc48eSAndrew Thompson 	struct usb_cdev_privdata* cpd;
1087760bc48eSAndrew Thompson 	struct usb_fifo *f;
1088760bc48eSAndrew Thompson 	struct usb_mbuf *m;
10898a93629eSAndrew Thompson 	int fflags, revents;
109002ac6454SAndrew Thompson 
10918a93629eSAndrew Thompson 	if (devfs_get_cdevpriv((void **)&cpd) != 0 ||
1092a593f6b8SAndrew Thompson 	    usb_ref_device(cpd, &refs, 0) != 0)
10938a93629eSAndrew Thompson 		return (events &
10948a93629eSAndrew Thompson 		    (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM));
1095ee3e3ff5SAndrew Thompson 
1096ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
109702ac6454SAndrew Thompson 
109802ac6454SAndrew Thompson 	/* Figure out who needs service */
1099ee3e3ff5SAndrew Thompson 	revents = 0;
110002ac6454SAndrew Thompson 	if ((events & (POLLOUT | POLLWRNORM)) &&
110102ac6454SAndrew Thompson 	    (fflags & FWRITE)) {
110202ac6454SAndrew Thompson 
1103e13e19faSAndrew Thompson 		f = refs.txfifo;
110402ac6454SAndrew Thompson 
110502ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
110602ac6454SAndrew Thompson 
1107e13e19faSAndrew Thompson 		if (!refs.is_usbfs) {
110802ac6454SAndrew Thompson 			if (f->flag_iserror) {
110902ac6454SAndrew Thompson 				/* we got an error */
111002ac6454SAndrew Thompson 				m = (void *)1;
111102ac6454SAndrew Thompson 			} else {
111202ac6454SAndrew Thompson 				if (f->queue_data == NULL) {
111302ac6454SAndrew Thompson 					/*
111402ac6454SAndrew Thompson 					 * start write transfer, if not
111502ac6454SAndrew Thompson 					 * already started
111602ac6454SAndrew Thompson 					 */
111702ac6454SAndrew Thompson 					(f->methods->f_start_write) (f);
111802ac6454SAndrew Thompson 				}
111902ac6454SAndrew Thompson 				/* check if any packets are available */
112002ac6454SAndrew Thompson 				USB_IF_POLL(&f->free_q, m);
112102ac6454SAndrew Thompson 			}
112202ac6454SAndrew Thompson 		} else {
112302ac6454SAndrew Thompson 			if (f->flag_iscomplete) {
112402ac6454SAndrew Thompson 				m = (void *)1;
112502ac6454SAndrew Thompson 			} else {
112602ac6454SAndrew Thompson 				m = NULL;
112702ac6454SAndrew Thompson 			}
112802ac6454SAndrew Thompson 		}
112902ac6454SAndrew Thompson 
113002ac6454SAndrew Thompson 		if (m) {
113102ac6454SAndrew Thompson 			revents |= events & (POLLOUT | POLLWRNORM);
113202ac6454SAndrew Thompson 		} else {
113302ac6454SAndrew Thompson 			f->flag_isselect = 1;
113402ac6454SAndrew Thompson 			selrecord(td, &f->selinfo);
113502ac6454SAndrew Thompson 		}
113602ac6454SAndrew Thompson 
113702ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
113802ac6454SAndrew Thompson 	}
113902ac6454SAndrew Thompson 	if ((events & (POLLIN | POLLRDNORM)) &&
114002ac6454SAndrew Thompson 	    (fflags & FREAD)) {
114102ac6454SAndrew Thompson 
1142e13e19faSAndrew Thompson 		f = refs.rxfifo;
114302ac6454SAndrew Thompson 
114402ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
114502ac6454SAndrew Thompson 
1146e13e19faSAndrew Thompson 		if (!refs.is_usbfs) {
114702ac6454SAndrew Thompson 			if (f->flag_iserror) {
114802ac6454SAndrew Thompson 				/* we have and error */
114902ac6454SAndrew Thompson 				m = (void *)1;
115002ac6454SAndrew Thompson 			} else {
115102ac6454SAndrew Thompson 				if (f->queue_data == NULL) {
115202ac6454SAndrew Thompson 					/*
115302ac6454SAndrew Thompson 					 * start read transfer, if not
115402ac6454SAndrew Thompson 					 * already started
115502ac6454SAndrew Thompson 					 */
115602ac6454SAndrew Thompson 					(f->methods->f_start_read) (f);
115702ac6454SAndrew Thompson 				}
115802ac6454SAndrew Thompson 				/* check if any packets are available */
115902ac6454SAndrew Thompson 				USB_IF_POLL(&f->used_q, m);
116002ac6454SAndrew Thompson 			}
116102ac6454SAndrew Thompson 		} else {
116202ac6454SAndrew Thompson 			if (f->flag_iscomplete) {
116302ac6454SAndrew Thompson 				m = (void *)1;
116402ac6454SAndrew Thompson 			} else {
116502ac6454SAndrew Thompson 				m = NULL;
116602ac6454SAndrew Thompson 			}
116702ac6454SAndrew Thompson 		}
116802ac6454SAndrew Thompson 
116902ac6454SAndrew Thompson 		if (m) {
117002ac6454SAndrew Thompson 			revents |= events & (POLLIN | POLLRDNORM);
117102ac6454SAndrew Thompson 		} else {
117202ac6454SAndrew Thompson 			f->flag_isselect = 1;
117302ac6454SAndrew Thompson 			selrecord(td, &f->selinfo);
117402ac6454SAndrew Thompson 
1175e13e19faSAndrew Thompson 			if (!refs.is_usbfs) {
117602ac6454SAndrew Thompson 				/* start reading data */
117702ac6454SAndrew Thompson 				(f->methods->f_start_read) (f);
117802ac6454SAndrew Thompson 			}
117902ac6454SAndrew Thompson 		}
118002ac6454SAndrew Thompson 
118102ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
118202ac6454SAndrew Thompson 	}
1183a593f6b8SAndrew Thompson 	usb_unref_device(cpd, &refs);
118402ac6454SAndrew Thompson 	return (revents);
118502ac6454SAndrew Thompson }
118602ac6454SAndrew Thompson 
118702ac6454SAndrew Thompson static int
1188a593f6b8SAndrew Thompson usb_read(struct cdev *dev, struct uio *uio, int ioflag)
118902ac6454SAndrew Thompson {
1190e13e19faSAndrew Thompson 	struct usb_cdev_refdata refs;
1191760bc48eSAndrew Thompson 	struct usb_cdev_privdata* cpd;
1192760bc48eSAndrew Thompson 	struct usb_fifo *f;
1193760bc48eSAndrew Thompson 	struct usb_mbuf *m;
119402ac6454SAndrew Thompson 	int fflags;
119502ac6454SAndrew Thompson 	int resid;
119602ac6454SAndrew Thompson 	int io_len;
119702ac6454SAndrew Thompson 	int err;
119802ac6454SAndrew Thompson 	uint8_t tr_data = 0;
119902ac6454SAndrew Thompson 
1200ee3e3ff5SAndrew Thompson 	err = devfs_get_cdevpriv((void **)&cpd);
1201ee3e3ff5SAndrew Thompson 	if (err != 0)
1202ee3e3ff5SAndrew Thompson 		return (err);
120302ac6454SAndrew Thompson 
1204a593f6b8SAndrew Thompson 	err = usb_ref_device(cpd, &refs, 0 /* no uref */ );
120502ac6454SAndrew Thompson 	if (err) {
120602ac6454SAndrew Thompson 		return (ENXIO);
120702ac6454SAndrew Thompson 	}
1208ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
1209ee3e3ff5SAndrew Thompson 
1210e13e19faSAndrew Thompson 	f = refs.rxfifo;
121102ac6454SAndrew Thompson 	if (f == NULL) {
121202ac6454SAndrew Thompson 		/* should not happen */
1213a593f6b8SAndrew Thompson 		usb_unref_device(cpd, &refs);
121402ac6454SAndrew Thompson 		return (EPERM);
121502ac6454SAndrew Thompson 	}
121602ac6454SAndrew Thompson 
1217ee3e3ff5SAndrew Thompson 	resid = uio->uio_resid;
121802ac6454SAndrew Thompson 
121902ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
122002ac6454SAndrew Thompson 
122102ac6454SAndrew Thompson 	/* check for permanent read error */
122202ac6454SAndrew Thompson 	if (f->flag_iserror) {
122302ac6454SAndrew Thompson 		err = EIO;
122402ac6454SAndrew Thompson 		goto done;
122502ac6454SAndrew Thompson 	}
122602ac6454SAndrew Thompson 	/* check if USB-FS interface is active */
1227e13e19faSAndrew Thompson 	if (refs.is_usbfs) {
122802ac6454SAndrew Thompson 		/*
122902ac6454SAndrew Thompson 		 * The queue is used for events that should be
123002ac6454SAndrew Thompson 		 * retrieved using the "USB_FS_COMPLETE" ioctl.
123102ac6454SAndrew Thompson 		 */
123202ac6454SAndrew Thompson 		err = EINVAL;
123302ac6454SAndrew Thompson 		goto done;
123402ac6454SAndrew Thompson 	}
123502ac6454SAndrew Thompson 	while (uio->uio_resid > 0) {
123602ac6454SAndrew Thompson 
123702ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
123802ac6454SAndrew Thompson 
123902ac6454SAndrew Thompson 		if (m == NULL) {
124002ac6454SAndrew Thompson 
124102ac6454SAndrew Thompson 			/* start read transfer, if not already started */
124202ac6454SAndrew Thompson 
124302ac6454SAndrew Thompson 			(f->methods->f_start_read) (f);
124402ac6454SAndrew Thompson 
124507532e49SAndrew Thompson 			if (ioflag & IO_NDELAY) {
124602ac6454SAndrew Thompson 				if (tr_data) {
124702ac6454SAndrew Thompson 					/* return length before error */
124802ac6454SAndrew Thompson 					break;
124902ac6454SAndrew Thompson 				}
125002ac6454SAndrew Thompson 				err = EWOULDBLOCK;
125102ac6454SAndrew Thompson 				break;
125202ac6454SAndrew Thompson 			}
125302ac6454SAndrew Thompson 			DPRINTF("sleeping\n");
125402ac6454SAndrew Thompson 
1255a593f6b8SAndrew Thompson 			err = usb_fifo_wait(f);
125602ac6454SAndrew Thompson 			if (err) {
125702ac6454SAndrew Thompson 				break;
125802ac6454SAndrew Thompson 			}
125902ac6454SAndrew Thompson 			continue;
126002ac6454SAndrew Thompson 		}
126102ac6454SAndrew Thompson 		if (f->methods->f_filter_read) {
126202ac6454SAndrew Thompson 			/*
126302ac6454SAndrew Thompson 			 * Sometimes it is convenient to process data at the
126402ac6454SAndrew Thompson 			 * expense of a userland process instead of a kernel
126502ac6454SAndrew Thompson 			 * process.
126602ac6454SAndrew Thompson 			 */
126702ac6454SAndrew Thompson 			(f->methods->f_filter_read) (f, m);
126802ac6454SAndrew Thompson 		}
126902ac6454SAndrew Thompson 		tr_data = 1;
127002ac6454SAndrew Thompson 
127102ac6454SAndrew Thompson 		io_len = MIN(m->cur_data_len, uio->uio_resid);
127202ac6454SAndrew Thompson 
127302ac6454SAndrew Thompson 		DPRINTFN(2, "transfer %d bytes from %p\n",
127402ac6454SAndrew Thompson 		    io_len, m->cur_data_ptr);
127502ac6454SAndrew Thompson 
1276a593f6b8SAndrew Thompson 		err = usb_fifo_uiomove(f,
127702ac6454SAndrew Thompson 		    m->cur_data_ptr, io_len, uio);
127802ac6454SAndrew Thompson 
127902ac6454SAndrew Thompson 		m->cur_data_len -= io_len;
128002ac6454SAndrew Thompson 		m->cur_data_ptr += io_len;
128102ac6454SAndrew Thompson 
128202ac6454SAndrew Thompson 		if (m->cur_data_len == 0) {
128302ac6454SAndrew Thompson 
128402ac6454SAndrew Thompson 			uint8_t last_packet;
128502ac6454SAndrew Thompson 
128602ac6454SAndrew Thompson 			last_packet = m->last_packet;
128702ac6454SAndrew Thompson 
128802ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->free_q, m);
128902ac6454SAndrew Thompson 
129002ac6454SAndrew Thompson 			if (last_packet) {
129102ac6454SAndrew Thompson 				/* keep framing */
129202ac6454SAndrew Thompson 				break;
129302ac6454SAndrew Thompson 			}
129402ac6454SAndrew Thompson 		} else {
129502ac6454SAndrew Thompson 			USB_IF_PREPEND(&f->used_q, m);
129602ac6454SAndrew Thompson 		}
129702ac6454SAndrew Thompson 
129802ac6454SAndrew Thompson 		if (err) {
129902ac6454SAndrew Thompson 			break;
130002ac6454SAndrew Thompson 		}
130102ac6454SAndrew Thompson 	}
130202ac6454SAndrew Thompson done:
130302ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
130402ac6454SAndrew Thompson 
1305a593f6b8SAndrew Thompson 	usb_unref_device(cpd, &refs);
130602ac6454SAndrew Thompson 
130702ac6454SAndrew Thompson 	return (err);
130802ac6454SAndrew Thompson }
130902ac6454SAndrew Thompson 
131002ac6454SAndrew Thompson static int
1311a593f6b8SAndrew Thompson usb_write(struct cdev *dev, struct uio *uio, int ioflag)
131202ac6454SAndrew Thompson {
1313e13e19faSAndrew Thompson 	struct usb_cdev_refdata refs;
1314760bc48eSAndrew Thompson 	struct usb_cdev_privdata* cpd;
1315760bc48eSAndrew Thompson 	struct usb_fifo *f;
1316760bc48eSAndrew Thompson 	struct usb_mbuf *m;
1317bd73b187SAlfred Perlstein 	uint8_t *pdata;
131802ac6454SAndrew Thompson 	int fflags;
131902ac6454SAndrew Thompson 	int resid;
132002ac6454SAndrew Thompson 	int io_len;
132102ac6454SAndrew Thompson 	int err;
132202ac6454SAndrew Thompson 	uint8_t tr_data = 0;
132302ac6454SAndrew Thompson 
132402ac6454SAndrew Thompson 	DPRINTFN(2, "\n");
132502ac6454SAndrew Thompson 
1326ee3e3ff5SAndrew Thompson 	err = devfs_get_cdevpriv((void **)&cpd);
1327ee3e3ff5SAndrew Thompson 	if (err != 0)
1328ee3e3ff5SAndrew Thompson 		return (err);
132902ac6454SAndrew Thompson 
1330a593f6b8SAndrew Thompson 	err = usb_ref_device(cpd, &refs, 0 /* no uref */ );
133102ac6454SAndrew Thompson 	if (err) {
133202ac6454SAndrew Thompson 		return (ENXIO);
133302ac6454SAndrew Thompson 	}
1334ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
1335ee3e3ff5SAndrew Thompson 
1336e13e19faSAndrew Thompson 	f = refs.txfifo;
133702ac6454SAndrew Thompson 	if (f == NULL) {
133802ac6454SAndrew Thompson 		/* should not happen */
1339a593f6b8SAndrew Thompson 		usb_unref_device(cpd, &refs);
134002ac6454SAndrew Thompson 		return (EPERM);
134102ac6454SAndrew Thompson 	}
134202ac6454SAndrew Thompson 	resid = uio->uio_resid;
134302ac6454SAndrew Thompson 
134402ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
134502ac6454SAndrew Thompson 
134602ac6454SAndrew Thompson 	/* check for permanent write error */
134702ac6454SAndrew Thompson 	if (f->flag_iserror) {
134802ac6454SAndrew Thompson 		err = EIO;
134902ac6454SAndrew Thompson 		goto done;
135002ac6454SAndrew Thompson 	}
135102ac6454SAndrew Thompson 	/* check if USB-FS interface is active */
1352e13e19faSAndrew Thompson 	if (refs.is_usbfs) {
135302ac6454SAndrew Thompson 		/*
135402ac6454SAndrew Thompson 		 * The queue is used for events that should be
135502ac6454SAndrew Thompson 		 * retrieved using the "USB_FS_COMPLETE" ioctl.
135602ac6454SAndrew Thompson 		 */
135702ac6454SAndrew Thompson 		err = EINVAL;
135802ac6454SAndrew Thompson 		goto done;
135902ac6454SAndrew Thompson 	}
136002ac6454SAndrew Thompson 	if (f->queue_data == NULL) {
136102ac6454SAndrew Thompson 		/* start write transfer, if not already started */
136202ac6454SAndrew Thompson 		(f->methods->f_start_write) (f);
136302ac6454SAndrew Thompson 	}
136402ac6454SAndrew Thompson 	/* we allow writing zero length data */
136502ac6454SAndrew Thompson 	do {
136602ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->free_q, m);
136702ac6454SAndrew Thompson 
136802ac6454SAndrew Thompson 		if (m == NULL) {
136902ac6454SAndrew Thompson 
137007532e49SAndrew Thompson 			if (ioflag & IO_NDELAY) {
137102ac6454SAndrew Thompson 				if (tr_data) {
137202ac6454SAndrew Thompson 					/* return length before error */
137302ac6454SAndrew Thompson 					break;
137402ac6454SAndrew Thompson 				}
137502ac6454SAndrew Thompson 				err = EWOULDBLOCK;
137602ac6454SAndrew Thompson 				break;
137702ac6454SAndrew Thompson 			}
137802ac6454SAndrew Thompson 			DPRINTF("sleeping\n");
137902ac6454SAndrew Thompson 
1380a593f6b8SAndrew Thompson 			err = usb_fifo_wait(f);
138102ac6454SAndrew Thompson 			if (err) {
138202ac6454SAndrew Thompson 				break;
138302ac6454SAndrew Thompson 			}
138402ac6454SAndrew Thompson 			continue;
138502ac6454SAndrew Thompson 		}
138602ac6454SAndrew Thompson 		tr_data = 1;
138702ac6454SAndrew Thompson 
1388bd73b187SAlfred Perlstein 		if (f->flag_have_fragment == 0) {
138902ac6454SAndrew Thompson 			USB_MBUF_RESET(m);
1390bd73b187SAlfred Perlstein 			io_len = m->cur_data_len;
1391bd73b187SAlfred Perlstein 			pdata = m->cur_data_ptr;
1392bd73b187SAlfred Perlstein 			if (io_len > uio->uio_resid)
1393bd73b187SAlfred Perlstein 				io_len = uio->uio_resid;
139402ac6454SAndrew Thompson 			m->cur_data_len = io_len;
1395bd73b187SAlfred Perlstein 		} else {
1396bd73b187SAlfred Perlstein 			io_len = m->max_data_len - m->cur_data_len;
1397bd73b187SAlfred Perlstein 			pdata = m->cur_data_ptr + m->cur_data_len;
1398bd73b187SAlfred Perlstein 			if (io_len > uio->uio_resid)
1399bd73b187SAlfred Perlstein 				io_len = uio->uio_resid;
1400bd73b187SAlfred Perlstein 			m->cur_data_len += io_len;
1401bd73b187SAlfred Perlstein 		}
140202ac6454SAndrew Thompson 
140302ac6454SAndrew Thompson 		DPRINTFN(2, "transfer %d bytes to %p\n",
1404bd73b187SAlfred Perlstein 		    io_len, pdata);
140502ac6454SAndrew Thompson 
1406bd73b187SAlfred Perlstein 		err = usb_fifo_uiomove(f, pdata, io_len, uio);
140702ac6454SAndrew Thompson 
140802ac6454SAndrew Thompson 		if (err) {
1409bd73b187SAlfred Perlstein 			f->flag_have_fragment = 0;
141002ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->free_q, m);
141102ac6454SAndrew Thompson 			break;
141202ac6454SAndrew Thompson 		}
1413bd73b187SAlfred Perlstein 
1414bd73b187SAlfred Perlstein 		/* check if the buffer is ready to be transmitted */
1415bd73b187SAlfred Perlstein 
1416bd73b187SAlfred Perlstein 		if ((f->flag_write_defrag == 0) ||
1417bd73b187SAlfred Perlstein 		    (m->cur_data_len == m->max_data_len)) {
1418bd73b187SAlfred Perlstein 			f->flag_have_fragment = 0;
1419bd73b187SAlfred Perlstein 
142002ac6454SAndrew Thompson 			/*
1421bd73b187SAlfred Perlstein 			 * Check for write filter:
1422bd73b187SAlfred Perlstein 			 *
1423bd73b187SAlfred Perlstein 			 * Sometimes it is convenient to process data
1424bd73b187SAlfred Perlstein 			 * at the expense of a userland process
1425bd73b187SAlfred Perlstein 			 * instead of a kernel process.
142602ac6454SAndrew Thompson 			 */
1427bd73b187SAlfred Perlstein 			if (f->methods->f_filter_write) {
142802ac6454SAndrew Thompson 				(f->methods->f_filter_write) (f, m);
142902ac6454SAndrew Thompson 			}
1430bd73b187SAlfred Perlstein 
1431bd73b187SAlfred Perlstein 			/* Put USB mbuf in the used queue */
143202ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->used_q, m);
143302ac6454SAndrew Thompson 
1434bd73b187SAlfred Perlstein 			/* Start writing data, if not already started */
143502ac6454SAndrew Thompson 			(f->methods->f_start_write) (f);
1436bd73b187SAlfred Perlstein 		} else {
1437bd73b187SAlfred Perlstein 			/* Wait for more data or close */
1438bd73b187SAlfred Perlstein 			f->flag_have_fragment = 1;
1439bd73b187SAlfred Perlstein 			USB_IF_PREPEND(&f->free_q, m);
1440bd73b187SAlfred Perlstein 		}
144102ac6454SAndrew Thompson 
144202ac6454SAndrew Thompson 	} while (uio->uio_resid > 0);
144302ac6454SAndrew Thompson done:
144402ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
144502ac6454SAndrew Thompson 
1446a593f6b8SAndrew Thompson 	usb_unref_device(cpd, &refs);
144702ac6454SAndrew Thompson 
1448ee3e3ff5SAndrew Thompson 	return (err);
1449ee3e3ff5SAndrew Thompson }
145002ac6454SAndrew Thompson 
1451ee3e3ff5SAndrew Thompson int
1452a593f6b8SAndrew Thompson usb_static_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
1453ee3e3ff5SAndrew Thompson     struct thread *td)
1454ee3e3ff5SAndrew Thompson {
1455ee3e3ff5SAndrew Thompson 	union {
1456760bc48eSAndrew Thompson 		struct usb_read_dir *urd;
1457ee3e3ff5SAndrew Thompson 		void* data;
1458ee3e3ff5SAndrew Thompson 	} u;
1459ee3e3ff5SAndrew Thompson 	int err = ENOTTY;
1460ee3e3ff5SAndrew Thompson 
1461ee3e3ff5SAndrew Thompson 	u.data = data;
1462ee3e3ff5SAndrew Thompson 	switch (cmd) {
1463ee3e3ff5SAndrew Thompson 		case USB_READ_DIR:
1464a593f6b8SAndrew Thompson 			err = usb_read_symlink(u.urd->urd_data,
1465ee3e3ff5SAndrew Thompson 			    u.urd->urd_startentry, u.urd->urd_maxlen);
1466ee3e3ff5SAndrew Thompson 			break;
1467ee3e3ff5SAndrew Thompson 		case USB_DEV_QUIRK_GET:
1468ee3e3ff5SAndrew Thompson 		case USB_QUIRK_NAME_GET:
1469ee3e3ff5SAndrew Thompson 		case USB_DEV_QUIRK_ADD:
1470ee3e3ff5SAndrew Thompson 		case USB_DEV_QUIRK_REMOVE:
1471a593f6b8SAndrew Thompson 			err = usb_quirk_ioctl_p(cmd, data, fflag, td);
1472ee3e3ff5SAndrew Thompson 			break;
1473ee3e3ff5SAndrew Thompson 		case USB_GET_TEMPLATE:
1474a593f6b8SAndrew Thompson 			*(int *)data = usb_template;
1475ee3e3ff5SAndrew Thompson 			break;
1476ee3e3ff5SAndrew Thompson 		case USB_SET_TEMPLATE:
147750230f98SAndrew Thompson 			err = priv_check(curthread, PRIV_DRIVER);
1478ee3e3ff5SAndrew Thompson 			if (err)
1479ee3e3ff5SAndrew Thompson 				break;
1480a593f6b8SAndrew Thompson 			usb_template = *(int *)data;
1481ee3e3ff5SAndrew Thompson 			break;
1482ee3e3ff5SAndrew Thompson 	}
148302ac6454SAndrew Thompson 	return (err);
148402ac6454SAndrew Thompson }
148502ac6454SAndrew Thompson 
148602ac6454SAndrew Thompson static int
1487a593f6b8SAndrew Thompson usb_fifo_uiomove(struct usb_fifo *f, void *cp,
148802ac6454SAndrew Thompson     int n, struct uio *uio)
148902ac6454SAndrew Thompson {
149002ac6454SAndrew Thompson 	int error;
149102ac6454SAndrew Thompson 
149202ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
149302ac6454SAndrew Thompson 
149402ac6454SAndrew Thompson 	/*
149502ac6454SAndrew Thompson 	 * "uiomove()" can sleep so one needs to make a wrapper,
149602ac6454SAndrew Thompson 	 * exiting the mutex and checking things:
149702ac6454SAndrew Thompson 	 */
149802ac6454SAndrew Thompson 	error = uiomove(cp, n, uio);
149902ac6454SAndrew Thompson 
150002ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
150102ac6454SAndrew Thompson 
150202ac6454SAndrew Thompson 	return (error);
150302ac6454SAndrew Thompson }
150402ac6454SAndrew Thompson 
150502ac6454SAndrew Thompson int
1506a593f6b8SAndrew Thompson usb_fifo_wait(struct usb_fifo *f)
150702ac6454SAndrew Thompson {
150802ac6454SAndrew Thompson 	int err;
150902ac6454SAndrew Thompson 
151002ac6454SAndrew Thompson 	mtx_assert(f->priv_mtx, MA_OWNED);
151102ac6454SAndrew Thompson 
151202ac6454SAndrew Thompson 	if (f->flag_iserror) {
151302ac6454SAndrew Thompson 		/* we are gone */
151402ac6454SAndrew Thompson 		return (EIO);
151502ac6454SAndrew Thompson 	}
151602ac6454SAndrew Thompson 	f->flag_sleeping = 1;
151702ac6454SAndrew Thompson 
15188437751dSAndrew Thompson 	err = cv_wait_sig(&f->cv_io, f->priv_mtx);
151902ac6454SAndrew Thompson 
152002ac6454SAndrew Thompson 	if (f->flag_iserror) {
152102ac6454SAndrew Thompson 		/* we are gone */
152202ac6454SAndrew Thompson 		err = EIO;
152302ac6454SAndrew Thompson 	}
152402ac6454SAndrew Thompson 	return (err);
152502ac6454SAndrew Thompson }
152602ac6454SAndrew Thompson 
152702ac6454SAndrew Thompson void
1528a593f6b8SAndrew Thompson usb_fifo_signal(struct usb_fifo *f)
152902ac6454SAndrew Thompson {
153002ac6454SAndrew Thompson 	if (f->flag_sleeping) {
153102ac6454SAndrew Thompson 		f->flag_sleeping = 0;
15328437751dSAndrew Thompson 		cv_broadcast(&f->cv_io);
153302ac6454SAndrew Thompson 	}
153402ac6454SAndrew Thompson }
153502ac6454SAndrew Thompson 
153602ac6454SAndrew Thompson void
1537a593f6b8SAndrew Thompson usb_fifo_wakeup(struct usb_fifo *f)
153802ac6454SAndrew Thompson {
1539a593f6b8SAndrew Thompson 	usb_fifo_signal(f);
154002ac6454SAndrew Thompson 
154102ac6454SAndrew Thompson 	if (f->flag_isselect) {
154202ac6454SAndrew Thompson 		selwakeup(&f->selinfo);
154302ac6454SAndrew Thompson 		f->flag_isselect = 0;
154402ac6454SAndrew Thompson 	}
154502ac6454SAndrew Thompson 	if (f->async_p != NULL) {
154602ac6454SAndrew Thompson 		PROC_LOCK(f->async_p);
154702ac6454SAndrew Thompson 		psignal(f->async_p, SIGIO);
154802ac6454SAndrew Thompson 		PROC_UNLOCK(f->async_p);
154902ac6454SAndrew Thompson 	}
155002ac6454SAndrew Thompson }
155102ac6454SAndrew Thompson 
155202ac6454SAndrew Thompson static int
1553a593f6b8SAndrew Thompson usb_fifo_dummy_open(struct usb_fifo *fifo, int fflags)
155402ac6454SAndrew Thompson {
155502ac6454SAndrew Thompson 	return (0);
155602ac6454SAndrew Thompson }
155702ac6454SAndrew Thompson 
155802ac6454SAndrew Thompson static void
1559a593f6b8SAndrew Thompson usb_fifo_dummy_close(struct usb_fifo *fifo, int fflags)
156002ac6454SAndrew Thompson {
156102ac6454SAndrew Thompson 	return;
156202ac6454SAndrew Thompson }
156302ac6454SAndrew Thompson 
156402ac6454SAndrew Thompson static int
1565a593f6b8SAndrew Thompson usb_fifo_dummy_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
156602ac6454SAndrew Thompson {
156702ac6454SAndrew Thompson 	return (ENOIOCTL);
156802ac6454SAndrew Thompson }
156902ac6454SAndrew Thompson 
157002ac6454SAndrew Thompson static void
1571a593f6b8SAndrew Thompson usb_fifo_dummy_cmd(struct usb_fifo *fifo)
157202ac6454SAndrew Thompson {
157302ac6454SAndrew Thompson 	fifo->flag_flushing = 0;	/* not flushing */
157402ac6454SAndrew Thompson }
157502ac6454SAndrew Thompson 
157602ac6454SAndrew Thompson static void
1577a593f6b8SAndrew Thompson usb_fifo_check_methods(struct usb_fifo_methods *pm)
157802ac6454SAndrew Thompson {
157902ac6454SAndrew Thompson 	/* check that all callback functions are OK */
158002ac6454SAndrew Thompson 
158102ac6454SAndrew Thompson 	if (pm->f_open == NULL)
1582a593f6b8SAndrew Thompson 		pm->f_open = &usb_fifo_dummy_open;
158302ac6454SAndrew Thompson 
158402ac6454SAndrew Thompson 	if (pm->f_close == NULL)
1585a593f6b8SAndrew Thompson 		pm->f_close = &usb_fifo_dummy_close;
158602ac6454SAndrew Thompson 
158702ac6454SAndrew Thompson 	if (pm->f_ioctl == NULL)
1588a593f6b8SAndrew Thompson 		pm->f_ioctl = &usb_fifo_dummy_ioctl;
158902ac6454SAndrew Thompson 
159002ac6454SAndrew Thompson 	if (pm->f_ioctl_post == NULL)
1591a593f6b8SAndrew Thompson 		pm->f_ioctl_post = &usb_fifo_dummy_ioctl;
159202ac6454SAndrew Thompson 
159302ac6454SAndrew Thompson 	if (pm->f_start_read == NULL)
1594a593f6b8SAndrew Thompson 		pm->f_start_read = &usb_fifo_dummy_cmd;
159502ac6454SAndrew Thompson 
159602ac6454SAndrew Thompson 	if (pm->f_stop_read == NULL)
1597a593f6b8SAndrew Thompson 		pm->f_stop_read = &usb_fifo_dummy_cmd;
159802ac6454SAndrew Thompson 
159902ac6454SAndrew Thompson 	if (pm->f_start_write == NULL)
1600a593f6b8SAndrew Thompson 		pm->f_start_write = &usb_fifo_dummy_cmd;
160102ac6454SAndrew Thompson 
160202ac6454SAndrew Thompson 	if (pm->f_stop_write == NULL)
1603a593f6b8SAndrew Thompson 		pm->f_stop_write = &usb_fifo_dummy_cmd;
160402ac6454SAndrew Thompson }
160502ac6454SAndrew Thompson 
160602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1607a593f6b8SAndrew Thompson  *	usb_fifo_attach
160802ac6454SAndrew Thompson  *
160902ac6454SAndrew Thompson  * The following function will create a duplex FIFO.
161002ac6454SAndrew Thompson  *
161102ac6454SAndrew Thompson  * Return values:
161202ac6454SAndrew Thompson  * 0: Success.
161302ac6454SAndrew Thompson  * Else: Failure.
161402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
161502ac6454SAndrew Thompson int
1616a593f6b8SAndrew Thompson usb_fifo_attach(struct usb_device *udev, void *priv_sc,
1617760bc48eSAndrew Thompson     struct mtx *priv_mtx, struct usb_fifo_methods *pm,
1618760bc48eSAndrew Thompson     struct usb_fifo_sc *f_sc, uint16_t unit, uint16_t subunit,
1619ee3e3ff5SAndrew Thompson     uint8_t iface_index, uid_t uid, gid_t gid, int mode)
162002ac6454SAndrew Thompson {
1621760bc48eSAndrew Thompson 	struct usb_fifo *f_tx;
1622760bc48eSAndrew Thompson 	struct usb_fifo *f_rx;
1623ee3e3ff5SAndrew Thompson 	char devname[32];
162402ac6454SAndrew Thompson 	uint8_t n;
1625760bc48eSAndrew Thompson 	struct usb_fs_privdata* pd;
162602ac6454SAndrew Thompson 
162702ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_TX] = NULL;
162802ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_RX] = NULL;
162902ac6454SAndrew Thompson 
163002ac6454SAndrew Thompson 	if (pm == NULL)
163102ac6454SAndrew Thompson 		return (EINVAL);
163202ac6454SAndrew Thompson 
163302ac6454SAndrew Thompson 	/* check the methods */
1634a593f6b8SAndrew Thompson 	usb_fifo_check_methods(pm);
163502ac6454SAndrew Thompson 
163602ac6454SAndrew Thompson 	if (priv_mtx == NULL)
163702ac6454SAndrew Thompson 		priv_mtx = &Giant;
163802ac6454SAndrew Thompson 
163902ac6454SAndrew Thompson 	/* search for a free FIFO slot */
164002ac6454SAndrew Thompson 	for (n = 0;; n += 2) {
164102ac6454SAndrew Thompson 
164202ac6454SAndrew Thompson 		if (n == USB_FIFO_MAX) {
164302ac6454SAndrew Thompson 			/* end of FIFOs reached */
164402ac6454SAndrew Thompson 			return (ENOMEM);
164502ac6454SAndrew Thompson 		}
164602ac6454SAndrew Thompson 		/* Check for TX FIFO */
164702ac6454SAndrew Thompson 		if (udev->fifo[n + USB_FIFO_TX] != NULL) {
164802ac6454SAndrew Thompson 			continue;
164902ac6454SAndrew Thompson 		}
165002ac6454SAndrew Thompson 		/* Check for RX FIFO */
165102ac6454SAndrew Thompson 		if (udev->fifo[n + USB_FIFO_RX] != NULL) {
165202ac6454SAndrew Thompson 			continue;
165302ac6454SAndrew Thompson 		}
165402ac6454SAndrew Thompson 		break;
165502ac6454SAndrew Thompson 	}
165602ac6454SAndrew Thompson 
1657a593f6b8SAndrew Thompson 	f_tx = usb_fifo_alloc();
1658a593f6b8SAndrew Thompson 	f_rx = usb_fifo_alloc();
165902ac6454SAndrew Thompson 
166002ac6454SAndrew Thompson 	if ((f_tx == NULL) || (f_rx == NULL)) {
1661a593f6b8SAndrew Thompson 		usb_fifo_free(f_tx);
1662a593f6b8SAndrew Thompson 		usb_fifo_free(f_rx);
166302ac6454SAndrew Thompson 		return (ENOMEM);
166402ac6454SAndrew Thompson 	}
166502ac6454SAndrew Thompson 	/* initialise FIFO structures */
166602ac6454SAndrew Thompson 
166702ac6454SAndrew Thompson 	f_tx->fifo_index = n + USB_FIFO_TX;
16682989a677SAndrew Thompson 	f_tx->dev_ep_index = -1;
166902ac6454SAndrew Thompson 	f_tx->priv_mtx = priv_mtx;
167002ac6454SAndrew Thompson 	f_tx->priv_sc0 = priv_sc;
167102ac6454SAndrew Thompson 	f_tx->methods = pm;
167202ac6454SAndrew Thompson 	f_tx->iface_index = iface_index;
167302ac6454SAndrew Thompson 	f_tx->udev = udev;
167402ac6454SAndrew Thompson 
167502ac6454SAndrew Thompson 	f_rx->fifo_index = n + USB_FIFO_RX;
16762989a677SAndrew Thompson 	f_rx->dev_ep_index = -1;
167702ac6454SAndrew Thompson 	f_rx->priv_mtx = priv_mtx;
167802ac6454SAndrew Thompson 	f_rx->priv_sc0 = priv_sc;
167902ac6454SAndrew Thompson 	f_rx->methods = pm;
168002ac6454SAndrew Thompson 	f_rx->iface_index = iface_index;
168102ac6454SAndrew Thompson 	f_rx->udev = udev;
168202ac6454SAndrew Thompson 
168302ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_TX] = f_tx;
168402ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_RX] = f_rx;
168502ac6454SAndrew Thompson 
1686a593f6b8SAndrew Thompson 	mtx_lock(&usb_ref_lock);
168702ac6454SAndrew Thompson 	udev->fifo[f_tx->fifo_index] = f_tx;
168802ac6454SAndrew Thompson 	udev->fifo[f_rx->fifo_index] = f_rx;
1689a593f6b8SAndrew Thompson 	mtx_unlock(&usb_ref_lock);
169002ac6454SAndrew Thompson 
169102ac6454SAndrew Thompson 	for (n = 0; n != 4; n++) {
169202ac6454SAndrew Thompson 
169302ac6454SAndrew Thompson 		if (pm->basename[n] == NULL) {
169402ac6454SAndrew Thompson 			continue;
169502ac6454SAndrew Thompson 		}
169602ac6454SAndrew Thompson 		if (subunit == 0xFFFF) {
1697ee3e3ff5SAndrew Thompson 			if (snprintf(devname, sizeof(devname),
169802ac6454SAndrew Thompson 			    "%s%u%s", pm->basename[n],
169902ac6454SAndrew Thompson 			    unit, pm->postfix[n] ?
170002ac6454SAndrew Thompson 			    pm->postfix[n] : "")) {
170102ac6454SAndrew Thompson 				/* ignore */
170202ac6454SAndrew Thompson 			}
170302ac6454SAndrew Thompson 		} else {
1704ee3e3ff5SAndrew Thompson 			if (snprintf(devname, sizeof(devname),
170502ac6454SAndrew Thompson 			    "%s%u.%u%s", pm->basename[n],
170602ac6454SAndrew Thompson 			    unit, subunit, pm->postfix[n] ?
170702ac6454SAndrew Thompson 			    pm->postfix[n] : "")) {
170802ac6454SAndrew Thompson 				/* ignore */
170902ac6454SAndrew Thompson 			}
171002ac6454SAndrew Thompson 		}
171102ac6454SAndrew Thompson 
171202ac6454SAndrew Thompson 		/*
171302ac6454SAndrew Thompson 		 * Distribute the symbolic links into two FIFO structures:
171402ac6454SAndrew Thompson 		 */
171502ac6454SAndrew Thompson 		if (n & 1) {
171602ac6454SAndrew Thompson 			f_rx->symlink[n / 2] =
1717a593f6b8SAndrew Thompson 			    usb_alloc_symlink(devname);
171802ac6454SAndrew Thompson 		} else {
171902ac6454SAndrew Thompson 			f_tx->symlink[n / 2] =
1720a593f6b8SAndrew Thompson 			    usb_alloc_symlink(devname);
172102ac6454SAndrew Thompson 		}
1722ee3e3ff5SAndrew Thompson 
1723ee3e3ff5SAndrew Thompson 		/*
1724ee3e3ff5SAndrew Thompson 		 * Initialize device private data - this is used to find the
1725ee3e3ff5SAndrew Thompson 		 * actual USB device itself.
1726ee3e3ff5SAndrew Thompson 		 */
1727760bc48eSAndrew Thompson 		pd = malloc(sizeof(struct usb_fs_privdata), M_USBDEV, M_WAITOK | M_ZERO);
1728ee3e3ff5SAndrew Thompson 		pd->bus_index = device_get_unit(udev->bus->bdev);
1729ee3e3ff5SAndrew Thompson 		pd->dev_index = udev->device_index;
1730ee3e3ff5SAndrew Thompson 		pd->ep_addr = -1;	/* not an endpoint */
17317214348fSAndrew Thompson 		pd->fifo_index = f_tx->fifo_index & f_rx->fifo_index;
1732ee3e3ff5SAndrew Thompson 		pd->mode = FREAD|FWRITE;
1733ee3e3ff5SAndrew Thompson 
1734ee3e3ff5SAndrew Thompson 		/* Now, create the device itself */
1735a593f6b8SAndrew Thompson 		f_sc->dev = make_dev(&usb_devsw, 0, uid, gid, mode,
1736ee3e3ff5SAndrew Thompson 		    devname);
17377214348fSAndrew Thompson 		/* XXX setting si_drv1 and creating the device is not atomic! */
1738ee3e3ff5SAndrew Thompson 		f_sc->dev->si_drv1 = pd;
173902ac6454SAndrew Thompson 	}
174002ac6454SAndrew Thompson 
174102ac6454SAndrew Thompson 	DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx);
174202ac6454SAndrew Thompson 	return (0);
174302ac6454SAndrew Thompson }
174402ac6454SAndrew Thompson 
174502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1746a593f6b8SAndrew Thompson  *	usb_fifo_alloc_buffer
174702ac6454SAndrew Thompson  *
174802ac6454SAndrew Thompson  * Return values:
174902ac6454SAndrew Thompson  * 0: Success
175002ac6454SAndrew Thompson  * Else failure
175102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
175202ac6454SAndrew Thompson int
1753a593f6b8SAndrew Thompson usb_fifo_alloc_buffer(struct usb_fifo *f, usb_size_t bufsize,
175402ac6454SAndrew Thompson     uint16_t nbuf)
175502ac6454SAndrew Thompson {
1756a593f6b8SAndrew Thompson 	usb_fifo_free_buffer(f);
175702ac6454SAndrew Thompson 
175802ac6454SAndrew Thompson 	/* allocate an endpoint */
175902ac6454SAndrew Thompson 	f->free_q.ifq_maxlen = nbuf;
176002ac6454SAndrew Thompson 	f->used_q.ifq_maxlen = nbuf;
176102ac6454SAndrew Thompson 
1762a593f6b8SAndrew Thompson 	f->queue_data = usb_alloc_mbufs(
176302ac6454SAndrew Thompson 	    M_USBDEV, &f->free_q, bufsize, nbuf);
176402ac6454SAndrew Thompson 
176502ac6454SAndrew Thompson 	if ((f->queue_data == NULL) && bufsize && nbuf) {
176602ac6454SAndrew Thompson 		return (ENOMEM);
176702ac6454SAndrew Thompson 	}
176802ac6454SAndrew Thompson 	return (0);			/* success */
176902ac6454SAndrew Thompson }
177002ac6454SAndrew Thompson 
177102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1772a593f6b8SAndrew Thompson  *	usb_fifo_free_buffer
177302ac6454SAndrew Thompson  *
177402ac6454SAndrew Thompson  * This function will free the buffers associated with a FIFO. This
177502ac6454SAndrew Thompson  * function can be called multiple times in a row.
177602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
177702ac6454SAndrew Thompson void
1778a593f6b8SAndrew Thompson usb_fifo_free_buffer(struct usb_fifo *f)
177902ac6454SAndrew Thompson {
178002ac6454SAndrew Thompson 	if (f->queue_data) {
178102ac6454SAndrew Thompson 		/* free old buffer */
178202ac6454SAndrew Thompson 		free(f->queue_data, M_USBDEV);
178302ac6454SAndrew Thompson 		f->queue_data = NULL;
178402ac6454SAndrew Thompson 	}
178502ac6454SAndrew Thompson 	/* reset queues */
178602ac6454SAndrew Thompson 
178702ac6454SAndrew Thompson 	bzero(&f->free_q, sizeof(f->free_q));
178802ac6454SAndrew Thompson 	bzero(&f->used_q, sizeof(f->used_q));
178902ac6454SAndrew Thompson }
179002ac6454SAndrew Thompson 
1791ee3e3ff5SAndrew Thompson static void
1792a593f6b8SAndrew Thompson usb_fifo_cleanup(void* ptr)
1793ee3e3ff5SAndrew Thompson {
1794ee3e3ff5SAndrew Thompson 	free(ptr, M_USBDEV);
1795ee3e3ff5SAndrew Thompson }
1796ee3e3ff5SAndrew Thompson 
179702ac6454SAndrew Thompson void
1798a593f6b8SAndrew Thompson usb_fifo_detach(struct usb_fifo_sc *f_sc)
179902ac6454SAndrew Thompson {
180002ac6454SAndrew Thompson 	if (f_sc == NULL) {
180102ac6454SAndrew Thompson 		return;
180202ac6454SAndrew Thompson 	}
1803a593f6b8SAndrew Thompson 	usb_fifo_free(f_sc->fp[USB_FIFO_TX]);
1804a593f6b8SAndrew Thompson 	usb_fifo_free(f_sc->fp[USB_FIFO_RX]);
180502ac6454SAndrew Thompson 
180602ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_TX] = NULL;
180702ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_RX] = NULL;
180802ac6454SAndrew Thompson 
1809ee3e3ff5SAndrew Thompson 	if (f_sc->dev != NULL) {
18107214348fSAndrew Thompson 		destroy_dev_sched_cb(f_sc->dev,
1811a593f6b8SAndrew Thompson 		    usb_fifo_cleanup, f_sc->dev->si_drv1);
18127214348fSAndrew Thompson 		f_sc->dev = NULL;
1813ee3e3ff5SAndrew Thompson 	}
1814ee3e3ff5SAndrew Thompson 
181502ac6454SAndrew Thompson 	DPRINTFN(2, "detached %p\n", f_sc);
181602ac6454SAndrew Thompson }
181702ac6454SAndrew Thompson 
1818f9cb546cSAndrew Thompson usb_size_t
1819a593f6b8SAndrew Thompson usb_fifo_put_bytes_max(struct usb_fifo *f)
182002ac6454SAndrew Thompson {
1821760bc48eSAndrew Thompson 	struct usb_mbuf *m;
1822f9cb546cSAndrew Thompson 	usb_size_t len;
182302ac6454SAndrew Thompson 
182402ac6454SAndrew Thompson 	USB_IF_POLL(&f->free_q, m);
182502ac6454SAndrew Thompson 
182602ac6454SAndrew Thompson 	if (m) {
182702ac6454SAndrew Thompson 		len = m->max_data_len;
182802ac6454SAndrew Thompson 	} else {
182902ac6454SAndrew Thompson 		len = 0;
183002ac6454SAndrew Thompson 	}
183102ac6454SAndrew Thompson 	return (len);
183202ac6454SAndrew Thompson }
183302ac6454SAndrew Thompson 
183402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1835a593f6b8SAndrew Thompson  *	usb_fifo_put_data
183602ac6454SAndrew Thompson  *
183702ac6454SAndrew Thompson  * what:
183802ac6454SAndrew Thompson  *  0 - normal operation
183902ac6454SAndrew Thompson  *  1 - set last packet flag to enforce framing
184002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
184102ac6454SAndrew Thompson void
1842a593f6b8SAndrew Thompson usb_fifo_put_data(struct usb_fifo *f, struct usb_page_cache *pc,
1843e0a69b51SAndrew Thompson     usb_frlength_t offset, usb_frlength_t len, uint8_t what)
184402ac6454SAndrew Thompson {
1845760bc48eSAndrew Thompson 	struct usb_mbuf *m;
1846e0a69b51SAndrew Thompson 	usb_frlength_t io_len;
184702ac6454SAndrew Thompson 
184802ac6454SAndrew Thompson 	while (len || (what == 1)) {
184902ac6454SAndrew Thompson 
185002ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->free_q, m);
185102ac6454SAndrew Thompson 
185202ac6454SAndrew Thompson 		if (m) {
185302ac6454SAndrew Thompson 			USB_MBUF_RESET(m);
185402ac6454SAndrew Thompson 
185502ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
185602ac6454SAndrew Thompson 
1857a593f6b8SAndrew Thompson 			usbd_copy_out(pc, offset, m->cur_data_ptr, io_len);
185802ac6454SAndrew Thompson 
185902ac6454SAndrew Thompson 			m->cur_data_len = io_len;
186002ac6454SAndrew Thompson 			offset += io_len;
186102ac6454SAndrew Thompson 			len -= io_len;
186202ac6454SAndrew Thompson 
186302ac6454SAndrew Thompson 			if ((len == 0) && (what == 1)) {
186402ac6454SAndrew Thompson 				m->last_packet = 1;
186502ac6454SAndrew Thompson 			}
186602ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->used_q, m);
186702ac6454SAndrew Thompson 
1868a593f6b8SAndrew Thompson 			usb_fifo_wakeup(f);
186902ac6454SAndrew Thompson 
187002ac6454SAndrew Thompson 			if ((len == 0) || (what == 1)) {
187102ac6454SAndrew Thompson 				break;
187202ac6454SAndrew Thompson 			}
187302ac6454SAndrew Thompson 		} else {
187402ac6454SAndrew Thompson 			break;
187502ac6454SAndrew Thompson 		}
187602ac6454SAndrew Thompson 	}
187702ac6454SAndrew Thompson }
187802ac6454SAndrew Thompson 
187902ac6454SAndrew Thompson void
1880a593f6b8SAndrew Thompson usb_fifo_put_data_linear(struct usb_fifo *f, void *ptr,
1881f9cb546cSAndrew Thompson     usb_size_t len, uint8_t what)
188202ac6454SAndrew Thompson {
1883760bc48eSAndrew Thompson 	struct usb_mbuf *m;
1884f9cb546cSAndrew Thompson 	usb_size_t io_len;
188502ac6454SAndrew Thompson 
188602ac6454SAndrew Thompson 	while (len || (what == 1)) {
188702ac6454SAndrew Thompson 
188802ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->free_q, m);
188902ac6454SAndrew Thompson 
189002ac6454SAndrew Thompson 		if (m) {
189102ac6454SAndrew Thompson 			USB_MBUF_RESET(m);
189202ac6454SAndrew Thompson 
189302ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
189402ac6454SAndrew Thompson 
189502ac6454SAndrew Thompson 			bcopy(ptr, m->cur_data_ptr, io_len);
189602ac6454SAndrew Thompson 
189702ac6454SAndrew Thompson 			m->cur_data_len = io_len;
189802ac6454SAndrew Thompson 			ptr = USB_ADD_BYTES(ptr, io_len);
189902ac6454SAndrew Thompson 			len -= io_len;
190002ac6454SAndrew Thompson 
190102ac6454SAndrew Thompson 			if ((len == 0) && (what == 1)) {
190202ac6454SAndrew Thompson 				m->last_packet = 1;
190302ac6454SAndrew Thompson 			}
190402ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->used_q, m);
190502ac6454SAndrew Thompson 
1906a593f6b8SAndrew Thompson 			usb_fifo_wakeup(f);
190702ac6454SAndrew Thompson 
190802ac6454SAndrew Thompson 			if ((len == 0) || (what == 1)) {
190902ac6454SAndrew Thompson 				break;
191002ac6454SAndrew Thompson 			}
191102ac6454SAndrew Thompson 		} else {
191202ac6454SAndrew Thompson 			break;
191302ac6454SAndrew Thompson 		}
191402ac6454SAndrew Thompson 	}
191502ac6454SAndrew Thompson }
191602ac6454SAndrew Thompson 
191702ac6454SAndrew Thompson uint8_t
1918a593f6b8SAndrew Thompson usb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len)
191902ac6454SAndrew Thompson {
1920760bc48eSAndrew Thompson 	struct usb_mbuf *m;
192102ac6454SAndrew Thompson 
192202ac6454SAndrew Thompson 	USB_IF_DEQUEUE(&f->free_q, m);
192302ac6454SAndrew Thompson 
192402ac6454SAndrew Thompson 	if (m) {
192502ac6454SAndrew Thompson 		m->cur_data_len = len;
192602ac6454SAndrew Thompson 		m->cur_data_ptr = ptr;
192702ac6454SAndrew Thompson 		USB_IF_ENQUEUE(&f->used_q, m);
1928a593f6b8SAndrew Thompson 		usb_fifo_wakeup(f);
192902ac6454SAndrew Thompson 		return (1);
193002ac6454SAndrew Thompson 	}
193102ac6454SAndrew Thompson 	return (0);
193202ac6454SAndrew Thompson }
193302ac6454SAndrew Thompson 
193402ac6454SAndrew Thompson void
1935a593f6b8SAndrew Thompson usb_fifo_put_data_error(struct usb_fifo *f)
193602ac6454SAndrew Thompson {
193702ac6454SAndrew Thompson 	f->flag_iserror = 1;
1938a593f6b8SAndrew Thompson 	usb_fifo_wakeup(f);
193902ac6454SAndrew Thompson }
194002ac6454SAndrew Thompson 
194102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1942a593f6b8SAndrew Thompson  *	usb_fifo_get_data
194302ac6454SAndrew Thompson  *
194402ac6454SAndrew Thompson  * what:
194502ac6454SAndrew Thompson  *  0 - normal operation
1946760bc48eSAndrew Thompson  *  1 - only get one "usb_mbuf"
194702ac6454SAndrew Thompson  *
194802ac6454SAndrew Thompson  * returns:
194902ac6454SAndrew Thompson  *  0 - no more data
195002ac6454SAndrew Thompson  *  1 - data in buffer
195102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
195202ac6454SAndrew Thompson uint8_t
1953a593f6b8SAndrew Thompson usb_fifo_get_data(struct usb_fifo *f, struct usb_page_cache *pc,
1954e0a69b51SAndrew Thompson     usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen,
195502ac6454SAndrew Thompson     uint8_t what)
195602ac6454SAndrew Thompson {
1957760bc48eSAndrew Thompson 	struct usb_mbuf *m;
1958e0a69b51SAndrew Thompson 	usb_frlength_t io_len;
195902ac6454SAndrew Thompson 	uint8_t tr_data = 0;
196002ac6454SAndrew Thompson 
196102ac6454SAndrew Thompson 	actlen[0] = 0;
196202ac6454SAndrew Thompson 
196302ac6454SAndrew Thompson 	while (1) {
196402ac6454SAndrew Thompson 
196502ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
196602ac6454SAndrew Thompson 
196702ac6454SAndrew Thompson 		if (m) {
196802ac6454SAndrew Thompson 
196902ac6454SAndrew Thompson 			tr_data = 1;
197002ac6454SAndrew Thompson 
197102ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
197202ac6454SAndrew Thompson 
1973a593f6b8SAndrew Thompson 			usbd_copy_in(pc, offset, m->cur_data_ptr, io_len);
197402ac6454SAndrew Thompson 
197502ac6454SAndrew Thompson 			len -= io_len;
197602ac6454SAndrew Thompson 			offset += io_len;
197702ac6454SAndrew Thompson 			actlen[0] += io_len;
197802ac6454SAndrew Thompson 			m->cur_data_ptr += io_len;
197902ac6454SAndrew Thompson 			m->cur_data_len -= io_len;
198002ac6454SAndrew Thompson 
198102ac6454SAndrew Thompson 			if ((m->cur_data_len == 0) || (what == 1)) {
198202ac6454SAndrew Thompson 				USB_IF_ENQUEUE(&f->free_q, m);
198302ac6454SAndrew Thompson 
1984a593f6b8SAndrew Thompson 				usb_fifo_wakeup(f);
198502ac6454SAndrew Thompson 
198602ac6454SAndrew Thompson 				if (what == 1) {
198702ac6454SAndrew Thompson 					break;
198802ac6454SAndrew Thompson 				}
198902ac6454SAndrew Thompson 			} else {
199002ac6454SAndrew Thompson 				USB_IF_PREPEND(&f->used_q, m);
199102ac6454SAndrew Thompson 			}
199202ac6454SAndrew Thompson 		} else {
199302ac6454SAndrew Thompson 
199402ac6454SAndrew Thompson 			if (tr_data) {
199502ac6454SAndrew Thompson 				/* wait for data to be written out */
199602ac6454SAndrew Thompson 				break;
199702ac6454SAndrew Thompson 			}
199802ac6454SAndrew Thompson 			if (f->flag_flushing) {
19997214348fSAndrew Thompson 				/* check if we should send a short packet */
20007214348fSAndrew Thompson 				if (f->flag_short != 0) {
20017214348fSAndrew Thompson 					f->flag_short = 0;
20027214348fSAndrew Thompson 					tr_data = 1;
20037214348fSAndrew Thompson 					break;
20047214348fSAndrew Thompson 				}
20057214348fSAndrew Thompson 				/* flushing complete */
200602ac6454SAndrew Thompson 				f->flag_flushing = 0;
2007a593f6b8SAndrew Thompson 				usb_fifo_wakeup(f);
200802ac6454SAndrew Thompson 			}
200902ac6454SAndrew Thompson 			break;
201002ac6454SAndrew Thompson 		}
201102ac6454SAndrew Thompson 		if (len == 0) {
201202ac6454SAndrew Thompson 			break;
201302ac6454SAndrew Thompson 		}
201402ac6454SAndrew Thompson 	}
201502ac6454SAndrew Thompson 	return (tr_data);
201602ac6454SAndrew Thompson }
201702ac6454SAndrew Thompson 
201802ac6454SAndrew Thompson uint8_t
2019a593f6b8SAndrew Thompson usb_fifo_get_data_linear(struct usb_fifo *f, void *ptr,
2020f9cb546cSAndrew Thompson     usb_size_t len, usb_size_t *actlen, uint8_t what)
202102ac6454SAndrew Thompson {
2022760bc48eSAndrew Thompson 	struct usb_mbuf *m;
2023f9cb546cSAndrew Thompson 	usb_size_t io_len;
202402ac6454SAndrew Thompson 	uint8_t tr_data = 0;
202502ac6454SAndrew Thompson 
202602ac6454SAndrew Thompson 	actlen[0] = 0;
202702ac6454SAndrew Thompson 
202802ac6454SAndrew Thompson 	while (1) {
202902ac6454SAndrew Thompson 
203002ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
203102ac6454SAndrew Thompson 
203202ac6454SAndrew Thompson 		if (m) {
203302ac6454SAndrew Thompson 
203402ac6454SAndrew Thompson 			tr_data = 1;
203502ac6454SAndrew Thompson 
203602ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
203702ac6454SAndrew Thompson 
203802ac6454SAndrew Thompson 			bcopy(m->cur_data_ptr, ptr, io_len);
203902ac6454SAndrew Thompson 
204002ac6454SAndrew Thompson 			len -= io_len;
204102ac6454SAndrew Thompson 			ptr = USB_ADD_BYTES(ptr, io_len);
204202ac6454SAndrew Thompson 			actlen[0] += io_len;
204302ac6454SAndrew Thompson 			m->cur_data_ptr += io_len;
204402ac6454SAndrew Thompson 			m->cur_data_len -= io_len;
204502ac6454SAndrew Thompson 
204602ac6454SAndrew Thompson 			if ((m->cur_data_len == 0) || (what == 1)) {
204702ac6454SAndrew Thompson 				USB_IF_ENQUEUE(&f->free_q, m);
204802ac6454SAndrew Thompson 
2049a593f6b8SAndrew Thompson 				usb_fifo_wakeup(f);
205002ac6454SAndrew Thompson 
205102ac6454SAndrew Thompson 				if (what == 1) {
205202ac6454SAndrew Thompson 					break;
205302ac6454SAndrew Thompson 				}
205402ac6454SAndrew Thompson 			} else {
205502ac6454SAndrew Thompson 				USB_IF_PREPEND(&f->used_q, m);
205602ac6454SAndrew Thompson 			}
205702ac6454SAndrew Thompson 		} else {
205802ac6454SAndrew Thompson 
205902ac6454SAndrew Thompson 			if (tr_data) {
206002ac6454SAndrew Thompson 				/* wait for data to be written out */
206102ac6454SAndrew Thompson 				break;
206202ac6454SAndrew Thompson 			}
206302ac6454SAndrew Thompson 			if (f->flag_flushing) {
20647214348fSAndrew Thompson 				/* check if we should send a short packet */
20657214348fSAndrew Thompson 				if (f->flag_short != 0) {
20667214348fSAndrew Thompson 					f->flag_short = 0;
20677214348fSAndrew Thompson 					tr_data = 1;
20687214348fSAndrew Thompson 					break;
20697214348fSAndrew Thompson 				}
20707214348fSAndrew Thompson 				/* flushing complete */
207102ac6454SAndrew Thompson 				f->flag_flushing = 0;
2072a593f6b8SAndrew Thompson 				usb_fifo_wakeup(f);
207302ac6454SAndrew Thompson 			}
207402ac6454SAndrew Thompson 			break;
207502ac6454SAndrew Thompson 		}
207602ac6454SAndrew Thompson 		if (len == 0) {
207702ac6454SAndrew Thompson 			break;
207802ac6454SAndrew Thompson 		}
207902ac6454SAndrew Thompson 	}
208002ac6454SAndrew Thompson 	return (tr_data);
208102ac6454SAndrew Thompson }
208202ac6454SAndrew Thompson 
208302ac6454SAndrew Thompson uint8_t
2084a593f6b8SAndrew Thompson usb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen)
208502ac6454SAndrew Thompson {
2086760bc48eSAndrew Thompson 	struct usb_mbuf *m;
208702ac6454SAndrew Thompson 
208802ac6454SAndrew Thompson 	USB_IF_POLL(&f->used_q, m);
208902ac6454SAndrew Thompson 
209002ac6454SAndrew Thompson 	if (m) {
209102ac6454SAndrew Thompson 		*plen = m->cur_data_len;
209202ac6454SAndrew Thompson 		*pptr = m->cur_data_ptr;
209302ac6454SAndrew Thompson 
209402ac6454SAndrew Thompson 		return (1);
209502ac6454SAndrew Thompson 	}
209602ac6454SAndrew Thompson 	return (0);
209702ac6454SAndrew Thompson }
209802ac6454SAndrew Thompson 
209902ac6454SAndrew Thompson void
2100a593f6b8SAndrew Thompson usb_fifo_get_data_error(struct usb_fifo *f)
210102ac6454SAndrew Thompson {
210202ac6454SAndrew Thompson 	f->flag_iserror = 1;
2103a593f6b8SAndrew Thompson 	usb_fifo_wakeup(f);
210402ac6454SAndrew Thompson }
210502ac6454SAndrew Thompson 
210602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
2107a593f6b8SAndrew Thompson  *	usb_alloc_symlink
210802ac6454SAndrew Thompson  *
210902ac6454SAndrew Thompson  * Return values:
211002ac6454SAndrew Thompson  * NULL: Failure
211102ac6454SAndrew Thompson  * Else: Pointer to symlink entry
211202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
2113760bc48eSAndrew Thompson struct usb_symlink *
2114a593f6b8SAndrew Thompson usb_alloc_symlink(const char *target)
211502ac6454SAndrew Thompson {
2116760bc48eSAndrew Thompson 	struct usb_symlink *ps;
211702ac6454SAndrew Thompson 
211802ac6454SAndrew Thompson 	ps = malloc(sizeof(*ps), M_USBDEV, M_WAITOK);
211902ac6454SAndrew Thompson 	if (ps == NULL) {
212002ac6454SAndrew Thompson 		return (ps);
212102ac6454SAndrew Thompson 	}
2122ee3e3ff5SAndrew Thompson 	/* XXX no longer needed */
2123ee3e3ff5SAndrew Thompson 	strlcpy(ps->src_path, target, sizeof(ps->src_path));
2124ee3e3ff5SAndrew Thompson 	ps->src_len = strlen(ps->src_path);
212502ac6454SAndrew Thompson 	strlcpy(ps->dst_path, target, sizeof(ps->dst_path));
212602ac6454SAndrew Thompson 	ps->dst_len = strlen(ps->dst_path);
212702ac6454SAndrew Thompson 
2128a593f6b8SAndrew Thompson 	sx_xlock(&usb_sym_lock);
2129a593f6b8SAndrew Thompson 	TAILQ_INSERT_TAIL(&usb_sym_head, ps, sym_entry);
2130a593f6b8SAndrew Thompson 	sx_unlock(&usb_sym_lock);
213102ac6454SAndrew Thompson 	return (ps);
213202ac6454SAndrew Thompson }
213302ac6454SAndrew Thompson 
213402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
2135a593f6b8SAndrew Thompson  *	usb_free_symlink
213602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
213702ac6454SAndrew Thompson void
2138a593f6b8SAndrew Thompson usb_free_symlink(struct usb_symlink *ps)
213902ac6454SAndrew Thompson {
214002ac6454SAndrew Thompson 	if (ps == NULL) {
214102ac6454SAndrew Thompson 		return;
214202ac6454SAndrew Thompson 	}
2143a593f6b8SAndrew Thompson 	sx_xlock(&usb_sym_lock);
2144a593f6b8SAndrew Thompson 	TAILQ_REMOVE(&usb_sym_head, ps, sym_entry);
2145a593f6b8SAndrew Thompson 	sx_unlock(&usb_sym_lock);
214602ac6454SAndrew Thompson 
214702ac6454SAndrew Thompson 	free(ps, M_USBDEV);
214802ac6454SAndrew Thompson }
214902ac6454SAndrew Thompson 
215002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
2151a593f6b8SAndrew Thompson  *	usb_read_symlink
215202ac6454SAndrew Thompson  *
215302ac6454SAndrew Thompson  * Return value:
215402ac6454SAndrew Thompson  * 0: Success
215502ac6454SAndrew Thompson  * Else: Failure
215602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
215702ac6454SAndrew Thompson int
2158a593f6b8SAndrew Thompson usb_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len)
215902ac6454SAndrew Thompson {
2160760bc48eSAndrew Thompson 	struct usb_symlink *ps;
216102ac6454SAndrew Thompson 	uint32_t temp;
216202ac6454SAndrew Thompson 	uint32_t delta = 0;
216302ac6454SAndrew Thompson 	uint8_t len;
216402ac6454SAndrew Thompson 	int error = 0;
216502ac6454SAndrew Thompson 
2166a593f6b8SAndrew Thompson 	sx_xlock(&usb_sym_lock);
216702ac6454SAndrew Thompson 
2168a593f6b8SAndrew Thompson 	TAILQ_FOREACH(ps, &usb_sym_head, sym_entry) {
216902ac6454SAndrew Thompson 
217002ac6454SAndrew Thompson 		/*
217102ac6454SAndrew Thompson 		 * Compute total length of source and destination symlink
217202ac6454SAndrew Thompson 		 * strings pluss one length byte and two NUL bytes:
217302ac6454SAndrew Thompson 		 */
217402ac6454SAndrew Thompson 		temp = ps->src_len + ps->dst_len + 3;
217502ac6454SAndrew Thompson 
217602ac6454SAndrew Thompson 		if (temp > 255) {
217702ac6454SAndrew Thompson 			/*
217802ac6454SAndrew Thompson 			 * Skip entry because this length cannot fit
217902ac6454SAndrew Thompson 			 * into one byte:
218002ac6454SAndrew Thompson 			 */
218102ac6454SAndrew Thompson 			continue;
218202ac6454SAndrew Thompson 		}
218302ac6454SAndrew Thompson 		if (startentry != 0) {
218402ac6454SAndrew Thompson 			/* decrement read offset */
218502ac6454SAndrew Thompson 			startentry--;
218602ac6454SAndrew Thompson 			continue;
218702ac6454SAndrew Thompson 		}
218802ac6454SAndrew Thompson 		if (temp > user_len) {
218902ac6454SAndrew Thompson 			/* out of buffer space */
219002ac6454SAndrew Thompson 			break;
219102ac6454SAndrew Thompson 		}
219202ac6454SAndrew Thompson 		len = temp;
219302ac6454SAndrew Thompson 
219402ac6454SAndrew Thompson 		/* copy out total length */
219502ac6454SAndrew Thompson 
219602ac6454SAndrew Thompson 		error = copyout(&len,
219702ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
219802ac6454SAndrew Thompson 		if (error) {
219902ac6454SAndrew Thompson 			break;
220002ac6454SAndrew Thompson 		}
220102ac6454SAndrew Thompson 		delta += 1;
220202ac6454SAndrew Thompson 
220302ac6454SAndrew Thompson 		/* copy out source string */
220402ac6454SAndrew Thompson 
220502ac6454SAndrew Thompson 		error = copyout(ps->src_path,
220602ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), ps->src_len);
220702ac6454SAndrew Thompson 		if (error) {
220802ac6454SAndrew Thompson 			break;
220902ac6454SAndrew Thompson 		}
221002ac6454SAndrew Thompson 		len = 0;
221102ac6454SAndrew Thompson 		delta += ps->src_len;
221202ac6454SAndrew Thompson 		error = copyout(&len,
221302ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
221402ac6454SAndrew Thompson 		if (error) {
221502ac6454SAndrew Thompson 			break;
221602ac6454SAndrew Thompson 		}
221702ac6454SAndrew Thompson 		delta += 1;
221802ac6454SAndrew Thompson 
221902ac6454SAndrew Thompson 		/* copy out destination string */
222002ac6454SAndrew Thompson 
222102ac6454SAndrew Thompson 		error = copyout(ps->dst_path,
222202ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), ps->dst_len);
222302ac6454SAndrew Thompson 		if (error) {
222402ac6454SAndrew Thompson 			break;
222502ac6454SAndrew Thompson 		}
222602ac6454SAndrew Thompson 		len = 0;
222702ac6454SAndrew Thompson 		delta += ps->dst_len;
222802ac6454SAndrew Thompson 		error = copyout(&len,
222902ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
223002ac6454SAndrew Thompson 		if (error) {
223102ac6454SAndrew Thompson 			break;
223202ac6454SAndrew Thompson 		}
223302ac6454SAndrew Thompson 		delta += 1;
223402ac6454SAndrew Thompson 
223502ac6454SAndrew Thompson 		user_len -= temp;
223602ac6454SAndrew Thompson 	}
223702ac6454SAndrew Thompson 
223802ac6454SAndrew Thompson 	/* a zero length entry indicates the end */
223902ac6454SAndrew Thompson 
224002ac6454SAndrew Thompson 	if ((user_len != 0) && (error == 0)) {
224102ac6454SAndrew Thompson 
224202ac6454SAndrew Thompson 		len = 0;
224302ac6454SAndrew Thompson 
224402ac6454SAndrew Thompson 		error = copyout(&len,
224502ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
224602ac6454SAndrew Thompson 	}
2247a593f6b8SAndrew Thompson 	sx_unlock(&usb_sym_lock);
224802ac6454SAndrew Thompson 	return (error);
224902ac6454SAndrew Thompson }
22507214348fSAndrew Thompson 
22517214348fSAndrew Thompson void
2252a593f6b8SAndrew Thompson usb_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff)
22537214348fSAndrew Thompson {
22547214348fSAndrew Thompson 	if (f == NULL)
22557214348fSAndrew Thompson 		return;
22567214348fSAndrew Thompson 
22577214348fSAndrew Thompson 	/* send a Zero Length Packet, ZLP, before close */
22587214348fSAndrew Thompson 	f->flag_short = onoff;
22597214348fSAndrew Thompson }
2260ed6d949aSAndrew Thompson 
2261bd73b187SAlfred Perlstein void
2262bd73b187SAlfred Perlstein usb_fifo_set_write_defrag(struct usb_fifo *f, uint8_t onoff)
2263bd73b187SAlfred Perlstein {
2264bd73b187SAlfred Perlstein 	if (f == NULL)
2265bd73b187SAlfred Perlstein 		return;
2266bd73b187SAlfred Perlstein 
2267bd73b187SAlfred Perlstein 	/* defrag written data */
2268bd73b187SAlfred Perlstein 	f->flag_write_defrag = onoff;
2269bd73b187SAlfred Perlstein 	/* reset defrag state */
2270bd73b187SAlfred Perlstein 	f->flag_have_fragment = 0;
2271bd73b187SAlfred Perlstein }
2272bd73b187SAlfred Perlstein 
2273ed6d949aSAndrew Thompson void *
2274ed6d949aSAndrew Thompson usb_fifo_softc(struct usb_fifo *f)
2275ed6d949aSAndrew Thompson {
2276ed6d949aSAndrew Thompson 	return (f->priv_sc0);
2277ed6d949aSAndrew Thompson }
22788755859aSAndrew Thompson #endif	/* USB_HAVE_UGEN */
2279