xref: /freebsd/sys/dev/usb/usb_dev.c (revision 71625ec9ad2a9bc8c09784fbd23b759830e0ee5f)
102ac6454SAndrew Thompson /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
49b077d72SHans Petter Selasky  * Copyright (c) 2006-2023 Hans Petter Selasky
502ac6454SAndrew Thompson  *
602ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
702ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
802ac6454SAndrew Thompson  * are met:
902ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
1002ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1102ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1202ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1302ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1402ac6454SAndrew Thompson  *
1502ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1602ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1702ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1802ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1902ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2002ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2102ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2202ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2302ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2402ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2502ac6454SAndrew Thompson  * SUCH DAMAGE.
2602ac6454SAndrew Thompson  *
2702ac6454SAndrew Thompson  *
28a593f6b8SAndrew Thompson  * usb_dev.c - An abstraction layer for creating devices under /dev/...
2902ac6454SAndrew Thompson  */
3002ac6454SAndrew Thompson 
31d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE
32d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE
33d2b99310SHans Petter Selasky #else
3445b48cbcSBrooks Davis #ifdef COMPAT_FREEBSD32
3545b48cbcSBrooks Davis #include <sys/abi_compat.h>
3645b48cbcSBrooks Davis #endif
37ed6d949aSAndrew Thompson #include <sys/stdint.h>
38ed6d949aSAndrew Thompson #include <sys/stddef.h>
39ed6d949aSAndrew Thompson #include <sys/param.h>
40ed6d949aSAndrew Thompson #include <sys/queue.h>
41ed6d949aSAndrew Thompson #include <sys/types.h>
42ed6d949aSAndrew Thompson #include <sys/systm.h>
43ed6d949aSAndrew Thompson #include <sys/kernel.h>
44ed6d949aSAndrew Thompson #include <sys/bus.h>
45ed6d949aSAndrew Thompson #include <sys/module.h>
46ed6d949aSAndrew Thompson #include <sys/lock.h>
47ed6d949aSAndrew Thompson #include <sys/mutex.h>
48ed6d949aSAndrew Thompson #include <sys/condvar.h>
49ed6d949aSAndrew Thompson #include <sys/sysctl.h>
50ed6d949aSAndrew Thompson #include <sys/sx.h>
51ed6d949aSAndrew Thompson #include <sys/unistd.h>
52ed6d949aSAndrew Thompson #include <sys/callout.h>
53ed6d949aSAndrew Thompson #include <sys/malloc.h>
54ed6d949aSAndrew Thompson #include <sys/priv.h>
55ed6d949aSAndrew Thompson #include <sys/vnode.h>
56ed6d949aSAndrew Thompson #include <sys/conf.h>
57ed6d949aSAndrew Thompson #include <sys/fcntl.h>
58ed6d949aSAndrew Thompson 
5902ac6454SAndrew Thompson #include <dev/usb/usb.h>
6002ac6454SAndrew Thompson #include <dev/usb/usb_ioctl.h>
61ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
62ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
6302ac6454SAndrew Thompson 
64a593f6b8SAndrew Thompson #define	USB_DEBUG_VAR usb_fifo_debug
6502ac6454SAndrew Thompson 
6602ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
6702ac6454SAndrew Thompson #include <dev/usb/usb_dev.h>
68ed6d949aSAndrew Thompson #include <dev/usb/usb_mbuf.h>
6902ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
7002ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
7102ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
7202ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
7302ac6454SAndrew Thompson #include <dev/usb/usb_generic.h>
7402ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h>
7502ac6454SAndrew Thompson #include <dev/usb/usb_util.h>
7602ac6454SAndrew Thompson 
7702ac6454SAndrew Thompson #include <dev/usb/usb_controller.h>
7802ac6454SAndrew Thompson #include <dev/usb/usb_bus.h>
7902ac6454SAndrew Thompson 
8002ac6454SAndrew Thompson #include <sys/filio.h>
8102ac6454SAndrew Thompson #include <sys/ttycom.h>
8202ac6454SAndrew Thompson #include <sys/syscallsubr.h>
8302ac6454SAndrew Thompson 
8402ac6454SAndrew Thompson #include <machine/stdarg.h>
85d2b99310SHans Petter Selasky #endif			/* USB_GLOBAL_INCLUDE_FILE */
8602ac6454SAndrew Thompson 
878755859aSAndrew Thompson #if USB_HAVE_UGEN
888755859aSAndrew Thompson 
89ed6d949aSAndrew Thompson #ifdef USB_DEBUG
90a593f6b8SAndrew Thompson static int usb_fifo_debug = 0;
9102ac6454SAndrew Thompson 
92f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, dev, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
93f8d2b1f3SPawel Biernacki     "USB device");
94af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb_dev, OID_AUTO, debug, CTLFLAG_RWTUN,
95a593f6b8SAndrew Thompson     &usb_fifo_debug, 0, "Debug Level");
9602ac6454SAndrew Thompson #endif
9702ac6454SAndrew Thompson 
9802ac6454SAndrew Thompson #define	USB_UCRED struct ucred *ucred,
9902ac6454SAndrew Thompson 
10002ac6454SAndrew Thompson /* prototypes */
10102ac6454SAndrew Thompson 
102a593f6b8SAndrew Thompson static int	usb_fifo_open(struct usb_cdev_privdata *,
103760bc48eSAndrew Thompson 		    struct usb_fifo *, int);
104a593f6b8SAndrew Thompson static void	usb_fifo_close(struct usb_fifo *, int);
105a593f6b8SAndrew Thompson static void	usb_dev_init(void *);
106a593f6b8SAndrew Thompson static void	usb_dev_init_post(void *);
107a593f6b8SAndrew Thompson static void	usb_dev_uninit(void *);
108a593f6b8SAndrew Thompson static int	usb_fifo_uiomove(struct usb_fifo *, void *, int,
10902ac6454SAndrew Thompson 		    struct uio *);
110a593f6b8SAndrew Thompson static void	usb_fifo_check_methods(struct usb_fifo_methods *);
111e2723934SHans Petter Selasky static struct	usb_fifo *usb_fifo_alloc(struct mtx *);
112a593f6b8SAndrew Thompson static struct	usb_endpoint *usb_dev_get_ep(struct usb_device *, uint8_t,
113f5f145baSAndrew Thompson 		    uint8_t);
114a593f6b8SAndrew Thompson static void	usb_loc_fill(struct usb_fs_privdata *,
115760bc48eSAndrew Thompson 		    struct usb_cdev_privdata *);
116a593f6b8SAndrew Thompson static void	usb_close(void *);
117a593f6b8SAndrew Thompson static usb_error_t usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *, int);
118a593f6b8SAndrew Thompson static usb_error_t usb_usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *);
119a593f6b8SAndrew Thompson static void	usb_unref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *);
12002ac6454SAndrew Thompson 
121a593f6b8SAndrew Thompson static d_open_t usb_open;
122a593f6b8SAndrew Thompson static d_ioctl_t usb_ioctl;
123a593f6b8SAndrew Thompson static d_read_t usb_read;
124a593f6b8SAndrew Thompson static d_write_t usb_write;
125a593f6b8SAndrew Thompson static d_poll_t usb_poll;
1260b6d54d4SHans Petter Selasky static d_kqfilter_t usb_kqfilter;
12702ac6454SAndrew Thompson 
128a593f6b8SAndrew Thompson static d_ioctl_t usb_static_ioctl;
12902ac6454SAndrew Thompson 
130a593f6b8SAndrew Thompson static usb_fifo_open_t usb_fifo_dummy_open;
131a593f6b8SAndrew Thompson static usb_fifo_close_t usb_fifo_dummy_close;
132a593f6b8SAndrew Thompson static usb_fifo_ioctl_t usb_fifo_dummy_ioctl;
133a593f6b8SAndrew Thompson static usb_fifo_cmd_t usb_fifo_dummy_cmd;
13402ac6454SAndrew Thompson 
135ee3e3ff5SAndrew Thompson /* character device structure used for devices (/dev/ugenX.Y and /dev/uXXX) */
136a593f6b8SAndrew Thompson struct cdevsw usb_devsw = {
13702ac6454SAndrew Thompson 	.d_version = D_VERSION,
138a593f6b8SAndrew Thompson 	.d_open = usb_open,
139a593f6b8SAndrew Thompson 	.d_ioctl = usb_ioctl,
140ee3e3ff5SAndrew Thompson 	.d_name = "usbdev",
14102ac6454SAndrew Thompson 	.d_flags = D_TRACKCLOSE,
142a593f6b8SAndrew Thompson 	.d_read = usb_read,
143a593f6b8SAndrew Thompson 	.d_write = usb_write,
1440b6d54d4SHans Petter Selasky 	.d_poll = usb_poll,
1450b6d54d4SHans Petter Selasky 	.d_kqfilter = usb_kqfilter,
14602ac6454SAndrew Thompson };
14702ac6454SAndrew Thompson 
148a593f6b8SAndrew Thompson static struct cdev* usb_dev = NULL;
149ee3e3ff5SAndrew Thompson 
150ee3e3ff5SAndrew Thompson /* character device structure used for /dev/usb */
151a593f6b8SAndrew Thompson static struct cdevsw usb_static_devsw = {
152ee3e3ff5SAndrew Thompson 	.d_version = D_VERSION,
153a593f6b8SAndrew Thompson 	.d_ioctl = usb_static_ioctl,
154ee3e3ff5SAndrew Thompson 	.d_name = "usb"
15502ac6454SAndrew Thompson };
15602ac6454SAndrew Thompson 
157a593f6b8SAndrew Thompson static TAILQ_HEAD(, usb_symlink) usb_sym_head;
158a593f6b8SAndrew Thompson static struct sx usb_sym_lock;
15902ac6454SAndrew Thompson 
160a593f6b8SAndrew Thompson struct mtx usb_ref_lock;
16102ac6454SAndrew Thompson 
16202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
163a593f6b8SAndrew Thompson  *	usb_loc_fill
16402ac6454SAndrew Thompson  *
165760bc48eSAndrew Thompson  * This is used to fill out a usb_cdev_privdata structure based on the
166760bc48eSAndrew Thompson  * device's address as contained in usb_fs_privdata.
16702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
168ee3e3ff5SAndrew Thompson static void
usb_loc_fill(struct usb_fs_privdata * pd,struct usb_cdev_privdata * cpd)169a593f6b8SAndrew Thompson usb_loc_fill(struct usb_fs_privdata* pd, struct usb_cdev_privdata *cpd)
17002ac6454SAndrew Thompson {
171ee3e3ff5SAndrew Thompson 	cpd->bus_index = pd->bus_index;
172ee3e3ff5SAndrew Thompson 	cpd->dev_index = pd->dev_index;
173ee3e3ff5SAndrew Thompson 	cpd->ep_addr = pd->ep_addr;
174ee3e3ff5SAndrew Thompson 	cpd->fifo_index = pd->fifo_index;
17502ac6454SAndrew Thompson }
17602ac6454SAndrew Thompson 
17702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
178a593f6b8SAndrew Thompson  *	usb_ref_device
17902ac6454SAndrew Thompson  *
18002ac6454SAndrew Thompson  * This function is used to atomically refer an USB device by its
18102ac6454SAndrew Thompson  * device location. If this function returns success the USB device
18220733245SPedro F. Giffuni  * will not disappear until the USB device is unreferenced.
18302ac6454SAndrew Thompson  *
18402ac6454SAndrew Thompson  * Return values:
18502ac6454SAndrew Thompson  *  0: Success, refcount incremented on the given USB device.
18602ac6454SAndrew Thompson  *  Else: Failure.
18702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1888019e7e7SAndrew Thompson static usb_error_t
usb_ref_device(struct usb_cdev_privdata * cpd,struct usb_cdev_refdata * crd,int need_uref)189a593f6b8SAndrew Thompson usb_ref_device(struct usb_cdev_privdata *cpd,
190e13e19faSAndrew Thompson     struct usb_cdev_refdata *crd, int need_uref)
19102ac6454SAndrew Thompson {
192760bc48eSAndrew Thompson 	struct usb_fifo **ppf;
193760bc48eSAndrew Thompson 	struct usb_fifo *f;
19402ac6454SAndrew Thompson 
195e13e19faSAndrew Thompson 	DPRINTFN(2, "cpd=%p need uref=%d\n", cpd, need_uref);
196e13e19faSAndrew Thompson 
197e13e19faSAndrew Thompson 	/* clear all refs */
198e13e19faSAndrew Thompson 	memset(crd, 0, sizeof(*crd));
19902ac6454SAndrew Thompson 
200a593f6b8SAndrew Thompson 	mtx_lock(&usb_ref_lock);
201a593f6b8SAndrew Thompson 	cpd->bus = devclass_get_softc(usb_devclass_ptr, cpd->bus_index);
202ee3e3ff5SAndrew Thompson 	if (cpd->bus == NULL) {
203ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "no bus at %u\n", cpd->bus_index);
20402ac6454SAndrew Thompson 		goto error;
20502ac6454SAndrew Thompson 	}
206ee3e3ff5SAndrew Thompson 	cpd->udev = cpd->bus->devices[cpd->dev_index];
207ee3e3ff5SAndrew Thompson 	if (cpd->udev == NULL) {
208ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "no device at %u\n", cpd->dev_index);
20902ac6454SAndrew Thompson 		goto error;
21002ac6454SAndrew Thompson 	}
211f97da128SHans Petter Selasky 	if (cpd->udev->state == USB_STATE_DETACHED &&
212f97da128SHans Petter Selasky 	    (need_uref != 2)) {
213f97da128SHans Petter Selasky 		DPRINTFN(2, "device is detached\n");
214f97da128SHans Petter Selasky 		goto error;
215f97da128SHans Petter Selasky 	}
216d008478eSHans Petter Selasky 	if (need_uref) {
217d008478eSHans Petter Selasky 		DPRINTFN(2, "ref udev - needed\n");
218d008478eSHans Petter Selasky 
219ee3e3ff5SAndrew Thompson 		if (cpd->udev->refcount == USB_DEV_REF_MAX) {
22002ac6454SAndrew Thompson 			DPRINTFN(2, "no dev ref\n");
22102ac6454SAndrew Thompson 			goto error;
22202ac6454SAndrew Thompson 		}
2230ed53d45SAndrew Thompson 		cpd->udev->refcount++;
2240ed53d45SAndrew Thompson 
225a593f6b8SAndrew Thompson 		mtx_unlock(&usb_ref_lock);
2260ed53d45SAndrew Thompson 
2270ed53d45SAndrew Thompson 		/*
228a18a7a41SHans Petter Selasky 		 * We need to grab the enumeration SX-lock before
229a18a7a41SHans Petter Selasky 		 * grabbing the FIFO refs to avoid deadlock at detach!
2300ed53d45SAndrew Thompson 		 */
23110aab8b6SHans Petter Selasky 		crd->do_unlock = usbd_enum_lock_sig(cpd->udev);
2320ed53d45SAndrew Thompson 
233a593f6b8SAndrew Thompson 		mtx_lock(&usb_ref_lock);
234a488edb5SAndrew Thompson 
235a488edb5SAndrew Thompson 		/*
236a488edb5SAndrew Thompson 		 * Set "is_uref" after grabbing the default SX lock
237a488edb5SAndrew Thompson 		 */
238e13e19faSAndrew Thompson 		crd->is_uref = 1;
23910aab8b6SHans Petter Selasky 
24010aab8b6SHans Petter Selasky 		/* check for signal */
24110aab8b6SHans Petter Selasky 		if (crd->do_unlock > 1) {
24210aab8b6SHans Petter Selasky 			crd->do_unlock = 0;
24310aab8b6SHans Petter Selasky 			goto error;
24410aab8b6SHans Petter Selasky 		}
2450ed53d45SAndrew Thompson 	}
2460ed53d45SAndrew Thompson 
24702ac6454SAndrew Thompson 	/* check if we are doing an open */
248ee3e3ff5SAndrew Thompson 	if (cpd->fflags == 0) {
249e13e19faSAndrew Thompson 		/* use zero defaults */
25002ac6454SAndrew Thompson 	} else {
25102ac6454SAndrew Thompson 		/* check for write */
252ee3e3ff5SAndrew Thompson 		if (cpd->fflags & FWRITE) {
253ee3e3ff5SAndrew Thompson 			ppf = cpd->udev->fifo;
254ee3e3ff5SAndrew Thompson 			f = ppf[cpd->fifo_index + USB_FIFO_TX];
255e13e19faSAndrew Thompson 			crd->txfifo = f;
256e13e19faSAndrew Thompson 			crd->is_write = 1;	/* ref */
257ee3e3ff5SAndrew Thompson 			if (f == NULL || f->refcount == USB_FIFO_REF_MAX)
25802ac6454SAndrew Thompson 				goto error;
2597214348fSAndrew Thompson 			if (f->curr_cpd != cpd)
2607214348fSAndrew Thompson 				goto error;
26102ac6454SAndrew Thompson 			/* check if USB-FS is active */
26202ac6454SAndrew Thompson 			if (f->fs_ep_max != 0) {
263e13e19faSAndrew Thompson 				crd->is_usbfs = 1;
26402ac6454SAndrew Thompson 			}
26502ac6454SAndrew Thompson 		}
26602ac6454SAndrew Thompson 
26702ac6454SAndrew Thompson 		/* check for read */
268ee3e3ff5SAndrew Thompson 		if (cpd->fflags & FREAD) {
269ee3e3ff5SAndrew Thompson 			ppf = cpd->udev->fifo;
270ee3e3ff5SAndrew Thompson 			f = ppf[cpd->fifo_index + USB_FIFO_RX];
271e13e19faSAndrew Thompson 			crd->rxfifo = f;
272e13e19faSAndrew Thompson 			crd->is_read = 1;	/* ref */
273ee3e3ff5SAndrew Thompson 			if (f == NULL || f->refcount == USB_FIFO_REF_MAX)
27402ac6454SAndrew Thompson 				goto error;
2757214348fSAndrew Thompson 			if (f->curr_cpd != cpd)
2767214348fSAndrew Thompson 				goto error;
27702ac6454SAndrew Thompson 			/* check if USB-FS is active */
27802ac6454SAndrew Thompson 			if (f->fs_ep_max != 0) {
279e13e19faSAndrew Thompson 				crd->is_usbfs = 1;
28002ac6454SAndrew Thompson 			}
28102ac6454SAndrew Thompson 		}
28202ac6454SAndrew Thompson 	}
28302ac6454SAndrew Thompson 
28402ac6454SAndrew Thompson 	/* when everything is OK we increment the refcounts */
285e13e19faSAndrew Thompson 	if (crd->is_write) {
28602ac6454SAndrew Thompson 		DPRINTFN(2, "ref write\n");
287e13e19faSAndrew Thompson 		crd->txfifo->refcount++;
28802ac6454SAndrew Thompson 	}
289e13e19faSAndrew Thompson 	if (crd->is_read) {
29002ac6454SAndrew Thompson 		DPRINTFN(2, "ref read\n");
291e13e19faSAndrew Thompson 		crd->rxfifo->refcount++;
29202ac6454SAndrew Thompson 	}
293a593f6b8SAndrew Thompson 	mtx_unlock(&usb_ref_lock);
29402ac6454SAndrew Thompson 
29502ac6454SAndrew Thompson 	return (0);
29602ac6454SAndrew Thompson 
29702ac6454SAndrew Thompson error:
298a18a7a41SHans Petter Selasky 	if (crd->do_unlock)
299cb18f7d1SAlfred Perlstein 		usbd_enum_unlock(cpd->udev);
300cb18f7d1SAlfred Perlstein 
301a18a7a41SHans Petter Selasky 	if (crd->is_uref) {
302b78e84d1SHans Petter Selasky 		if (--(cpd->udev->refcount) == 0)
303d008478eSHans Petter Selasky 			cv_broadcast(&cpd->udev->ref_cv);
3040ed53d45SAndrew Thompson 	}
305a593f6b8SAndrew Thompson 	mtx_unlock(&usb_ref_lock);
30602ac6454SAndrew Thompson 	DPRINTFN(2, "fail\n");
307b06d477bSHans Petter Selasky 
308b06d477bSHans Petter Selasky 	/* clear all refs */
309b06d477bSHans Petter Selasky 	memset(crd, 0, sizeof(*crd));
310b06d477bSHans Petter Selasky 
31102ac6454SAndrew Thompson 	return (USB_ERR_INVAL);
31202ac6454SAndrew Thompson }
31302ac6454SAndrew Thompson 
31402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
315a593f6b8SAndrew Thompson  *	usb_usb_ref_device
31602ac6454SAndrew Thompson  *
31702ac6454SAndrew Thompson  * This function is used to upgrade an USB reference to include the
31802ac6454SAndrew Thompson  * USB device reference on a USB location.
31902ac6454SAndrew Thompson  *
32002ac6454SAndrew Thompson  * Return values:
32102ac6454SAndrew Thompson  *  0: Success, refcount incremented on the given USB device.
32202ac6454SAndrew Thompson  *  Else: Failure.
32302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
324e0a69b51SAndrew Thompson static usb_error_t
usb_usb_ref_device(struct usb_cdev_privdata * cpd,struct usb_cdev_refdata * crd)325a593f6b8SAndrew Thompson usb_usb_ref_device(struct usb_cdev_privdata *cpd,
326e13e19faSAndrew Thompson     struct usb_cdev_refdata *crd)
32702ac6454SAndrew Thompson {
32802ac6454SAndrew Thompson 	/*
32902ac6454SAndrew Thompson 	 * Check if we already got an USB reference on this location:
33002ac6454SAndrew Thompson 	 */
331e13e19faSAndrew Thompson 	if (crd->is_uref)
33202ac6454SAndrew Thompson 		return (0);		/* success */
33302ac6454SAndrew Thompson 
33402ac6454SAndrew Thompson 	/*
3350ed53d45SAndrew Thompson 	 * To avoid deadlock at detach we need to drop the FIFO ref
3360ed53d45SAndrew Thompson 	 * and re-acquire a new ref!
33702ac6454SAndrew Thompson 	 */
338a593f6b8SAndrew Thompson 	usb_unref_device(cpd, crd);
33902ac6454SAndrew Thompson 
340a593f6b8SAndrew Thompson 	return (usb_ref_device(cpd, crd, 1 /* need uref */));
34102ac6454SAndrew Thompson }
34202ac6454SAndrew Thompson 
34302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
344a593f6b8SAndrew Thompson  *	usb_unref_device
34502ac6454SAndrew Thompson  *
34602ac6454SAndrew Thompson  * This function will release the reference count by one unit for the
34702ac6454SAndrew Thompson  * given USB device.
34802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
3498019e7e7SAndrew Thompson static void
usb_unref_device(struct usb_cdev_privdata * cpd,struct usb_cdev_refdata * crd)350a593f6b8SAndrew Thompson usb_unref_device(struct usb_cdev_privdata *cpd,
351e13e19faSAndrew Thompson     struct usb_cdev_refdata *crd)
35202ac6454SAndrew Thompson {
353a488edb5SAndrew Thompson 
354e13e19faSAndrew Thompson 	DPRINTFN(2, "cpd=%p is_uref=%d\n", cpd, crd->is_uref);
355a488edb5SAndrew Thompson 
356a18a7a41SHans Petter Selasky 	if (crd->do_unlock)
357cb18f7d1SAlfred Perlstein 		usbd_enum_unlock(cpd->udev);
358cb18f7d1SAlfred Perlstein 
359a593f6b8SAndrew Thompson 	mtx_lock(&usb_ref_lock);
360e13e19faSAndrew Thompson 	if (crd->is_read) {
361e13e19faSAndrew Thompson 		if (--(crd->rxfifo->refcount) == 0) {
3628437751dSAndrew Thompson 			cv_signal(&crd->rxfifo->cv_drain);
36302ac6454SAndrew Thompson 		}
364e13e19faSAndrew Thompson 		crd->is_read = 0;
36502ac6454SAndrew Thompson 	}
366e13e19faSAndrew Thompson 	if (crd->is_write) {
367e13e19faSAndrew Thompson 		if (--(crd->txfifo->refcount) == 0) {
3688437751dSAndrew Thompson 			cv_signal(&crd->txfifo->cv_drain);
36902ac6454SAndrew Thompson 		}
370e13e19faSAndrew Thompson 		crd->is_write = 0;
37102ac6454SAndrew Thompson 	}
372e13e19faSAndrew Thompson 	if (crd->is_uref) {
373e13e19faSAndrew Thompson 		crd->is_uref = 0;
374b78e84d1SHans Petter Selasky 		if (--(cpd->udev->refcount) == 0)
375d008478eSHans Petter Selasky 			cv_broadcast(&cpd->udev->ref_cv);
37602ac6454SAndrew Thompson 	}
377a593f6b8SAndrew Thompson 	mtx_unlock(&usb_ref_lock);
37802ac6454SAndrew Thompson }
37902ac6454SAndrew Thompson 
380760bc48eSAndrew Thompson static struct usb_fifo *
usb_fifo_alloc(struct mtx * mtx)381e2723934SHans Petter Selasky usb_fifo_alloc(struct mtx *mtx)
38202ac6454SAndrew Thompson {
383760bc48eSAndrew Thompson 	struct usb_fifo *f;
38402ac6454SAndrew Thompson 
38502ac6454SAndrew Thompson 	f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO);
3868437751dSAndrew Thompson 	cv_init(&f->cv_io, "FIFO-IO");
3878437751dSAndrew Thompson 	cv_init(&f->cv_drain, "FIFO-DRAIN");
3889b077d72SHans Petter Selasky 	sx_init(&f->fs_fastpath_lock, "FIFO-FP");
389e2723934SHans Petter Selasky 	f->priv_mtx = mtx;
39002ac6454SAndrew Thompson 	f->refcount = 1;
391e2723934SHans Petter Selasky 	knlist_init_mtx(&f->selinfo.si_note, mtx);
39202ac6454SAndrew Thompson 	return (f);
39302ac6454SAndrew Thompson }
39402ac6454SAndrew Thompson 
39502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
396a593f6b8SAndrew Thompson  *	usb_fifo_create
39702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
39802ac6454SAndrew Thompson static int
usb_fifo_create(struct usb_cdev_privdata * cpd,struct usb_cdev_refdata * crd)399a593f6b8SAndrew Thompson usb_fifo_create(struct usb_cdev_privdata *cpd,
400e13e19faSAndrew Thompson     struct usb_cdev_refdata *crd)
40102ac6454SAndrew Thompson {
402760bc48eSAndrew Thompson 	struct usb_device *udev = cpd->udev;
403760bc48eSAndrew Thompson 	struct usb_fifo *f;
404ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep;
40502ac6454SAndrew Thompson 	uint8_t n;
40602ac6454SAndrew Thompson 	uint8_t is_tx;
40702ac6454SAndrew Thompson 	uint8_t is_rx;
40802ac6454SAndrew Thompson 	uint8_t no_null;
40902ac6454SAndrew Thompson 	uint8_t is_busy;
410ae60fdfbSAndrew Thompson 	int e = cpd->ep_addr;
41102ac6454SAndrew Thompson 
412ee3e3ff5SAndrew Thompson 	is_tx = (cpd->fflags & FWRITE) ? 1 : 0;
413ee3e3ff5SAndrew Thompson 	is_rx = (cpd->fflags & FREAD) ? 1 : 0;
41402ac6454SAndrew Thompson 	no_null = 1;
41502ac6454SAndrew Thompson 	is_busy = 0;
41602ac6454SAndrew Thompson 
417ee3e3ff5SAndrew Thompson 	/* Preallocated FIFO */
418ae60fdfbSAndrew Thompson 	if (e < 0) {
419ee3e3ff5SAndrew Thompson 		DPRINTFN(5, "Preallocated FIFO\n");
420ee3e3ff5SAndrew Thompson 		if (is_tx) {
421ee3e3ff5SAndrew Thompson 			f = udev->fifo[cpd->fifo_index + USB_FIFO_TX];
422ee3e3ff5SAndrew Thompson 			if (f == NULL)
423ee3e3ff5SAndrew Thompson 				return (EINVAL);
424e13e19faSAndrew Thompson 			crd->txfifo = f;
425ee3e3ff5SAndrew Thompson 		}
426ee3e3ff5SAndrew Thompson 		if (is_rx) {
427ee3e3ff5SAndrew Thompson 			f = udev->fifo[cpd->fifo_index + USB_FIFO_RX];
428ee3e3ff5SAndrew Thompson 			if (f == NULL)
429ee3e3ff5SAndrew Thompson 				return (EINVAL);
430e13e19faSAndrew Thompson 			crd->rxfifo = f;
431ee3e3ff5SAndrew Thompson 		}
432ee3e3ff5SAndrew Thompson 		return (0);
433ee3e3ff5SAndrew Thompson 	}
43402ac6454SAndrew Thompson 
435ae60fdfbSAndrew Thompson 	KASSERT(e >= 0 && e <= 15, ("endpoint %d out of range", e));
436ee3e3ff5SAndrew Thompson 
437ee3e3ff5SAndrew Thompson 	/* search for a free FIFO slot */
438ae60fdfbSAndrew Thompson 	DPRINTFN(5, "Endpoint device, searching for 0x%02x\n", e);
43902ac6454SAndrew Thompson 	for (n = 0;; n += 2) {
44002ac6454SAndrew Thompson 		if (n == USB_FIFO_MAX) {
44102ac6454SAndrew Thompson 			if (no_null) {
44202ac6454SAndrew Thompson 				no_null = 0;
44302ac6454SAndrew Thompson 				n = 0;
44402ac6454SAndrew Thompson 			} else {
44502ac6454SAndrew Thompson 				/* end of FIFOs reached */
446ee3e3ff5SAndrew Thompson 				DPRINTFN(5, "out of FIFOs\n");
44702ac6454SAndrew Thompson 				return (ENOMEM);
44802ac6454SAndrew Thompson 			}
44902ac6454SAndrew Thompson 		}
45002ac6454SAndrew Thompson 		/* Check for TX FIFO */
45102ac6454SAndrew Thompson 		if (is_tx) {
45202ac6454SAndrew Thompson 			f = udev->fifo[n + USB_FIFO_TX];
45302ac6454SAndrew Thompson 			if (f != NULL) {
454ae60fdfbSAndrew Thompson 				if (f->dev_ep_index != e) {
45502ac6454SAndrew Thompson 					/* wrong endpoint index */
45602ac6454SAndrew Thompson 					continue;
45702ac6454SAndrew Thompson 				}
4587214348fSAndrew Thompson 				if (f->curr_cpd != NULL) {
45902ac6454SAndrew Thompson 					/* FIFO is opened */
46002ac6454SAndrew Thompson 					is_busy = 1;
46102ac6454SAndrew Thompson 					continue;
46202ac6454SAndrew Thompson 				}
46302ac6454SAndrew Thompson 			} else if (no_null) {
46402ac6454SAndrew Thompson 				continue;
46502ac6454SAndrew Thompson 			}
46602ac6454SAndrew Thompson 		}
46702ac6454SAndrew Thompson 		/* Check for RX FIFO */
46802ac6454SAndrew Thompson 		if (is_rx) {
46902ac6454SAndrew Thompson 			f = udev->fifo[n + USB_FIFO_RX];
47002ac6454SAndrew Thompson 			if (f != NULL) {
471ae60fdfbSAndrew Thompson 				if (f->dev_ep_index != e) {
47202ac6454SAndrew Thompson 					/* wrong endpoint index */
47302ac6454SAndrew Thompson 					continue;
47402ac6454SAndrew Thompson 				}
4757214348fSAndrew Thompson 				if (f->curr_cpd != NULL) {
47602ac6454SAndrew Thompson 					/* FIFO is opened */
47702ac6454SAndrew Thompson 					is_busy = 1;
47802ac6454SAndrew Thompson 					continue;
47902ac6454SAndrew Thompson 				}
48002ac6454SAndrew Thompson 			} else if (no_null) {
48102ac6454SAndrew Thompson 				continue;
48202ac6454SAndrew Thompson 			}
48302ac6454SAndrew Thompson 		}
48402ac6454SAndrew Thompson 		break;
48502ac6454SAndrew Thompson 	}
48602ac6454SAndrew Thompson 
48702ac6454SAndrew Thompson 	if (no_null == 0) {
488ae60fdfbSAndrew Thompson 		if (e >= (USB_EP_MAX / 2)) {
48902ac6454SAndrew Thompson 			/* we don't create any endpoints in this range */
4907214348fSAndrew Thompson 			DPRINTFN(5, "ep out of range\n");
49102ac6454SAndrew Thompson 			return (is_busy ? EBUSY : EINVAL);
49202ac6454SAndrew Thompson 		}
49302ac6454SAndrew Thompson 	}
4947214348fSAndrew Thompson 
495ae60fdfbSAndrew Thompson 	if ((e != 0) && is_busy) {
4967214348fSAndrew Thompson 		/*
4977214348fSAndrew Thompson 		 * Only the default control endpoint is allowed to be
4987214348fSAndrew Thompson 		 * opened multiple times!
4997214348fSAndrew Thompson 		 */
5007214348fSAndrew Thompson 		DPRINTFN(5, "busy\n");
5017214348fSAndrew Thompson 		return (EBUSY);
5027214348fSAndrew Thompson 	}
5037214348fSAndrew Thompson 
50402ac6454SAndrew Thompson 	/* Check TX FIFO */
50502ac6454SAndrew Thompson 	if (is_tx &&
50602ac6454SAndrew Thompson 	    (udev->fifo[n + USB_FIFO_TX] == NULL)) {
507a593f6b8SAndrew Thompson 		ep = usb_dev_get_ep(udev, e, USB_FIFO_TX);
508ae60fdfbSAndrew Thompson 		DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_TX);
509ae60fdfbSAndrew Thompson 		if (ep == NULL) {
510ae60fdfbSAndrew Thompson 			DPRINTFN(5, "dev_get_endpoint returned NULL\n");
51102ac6454SAndrew Thompson 			return (EINVAL);
51202ac6454SAndrew Thompson 		}
513e2723934SHans Petter Selasky 		f = usb_fifo_alloc(&udev->device_mtx);
51402ac6454SAndrew Thompson 		if (f == NULL) {
515ee3e3ff5SAndrew Thompson 			DPRINTFN(5, "could not alloc tx fifo\n");
51602ac6454SAndrew Thompson 			return (ENOMEM);
51702ac6454SAndrew Thompson 		}
51802ac6454SAndrew Thompson 		/* update some fields */
51902ac6454SAndrew Thompson 		f->fifo_index = n + USB_FIFO_TX;
520ae60fdfbSAndrew Thompson 		f->dev_ep_index = e;
521ae60fdfbSAndrew Thompson 		f->priv_sc0 = ep;
522a593f6b8SAndrew Thompson 		f->methods = &usb_ugen_methods;
523ae60fdfbSAndrew Thompson 		f->iface_index = ep->iface_index;
52402ac6454SAndrew Thompson 		f->udev = udev;
525a593f6b8SAndrew Thompson 		mtx_lock(&usb_ref_lock);
52602ac6454SAndrew Thompson 		udev->fifo[n + USB_FIFO_TX] = f;
527a593f6b8SAndrew Thompson 		mtx_unlock(&usb_ref_lock);
52802ac6454SAndrew Thompson 	}
52902ac6454SAndrew Thompson 	/* Check RX FIFO */
53002ac6454SAndrew Thompson 	if (is_rx &&
53102ac6454SAndrew Thompson 	    (udev->fifo[n + USB_FIFO_RX] == NULL)) {
532a593f6b8SAndrew Thompson 		ep = usb_dev_get_ep(udev, e, USB_FIFO_RX);
533ae60fdfbSAndrew Thompson 		DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_RX);
534ae60fdfbSAndrew Thompson 		if (ep == NULL) {
535ae60fdfbSAndrew Thompson 			DPRINTFN(5, "dev_get_endpoint returned NULL\n");
53602ac6454SAndrew Thompson 			return (EINVAL);
53702ac6454SAndrew Thompson 		}
538e2723934SHans Petter Selasky 		f = usb_fifo_alloc(&udev->device_mtx);
53902ac6454SAndrew Thompson 		if (f == NULL) {
540ee3e3ff5SAndrew Thompson 			DPRINTFN(5, "could not alloc rx fifo\n");
54102ac6454SAndrew Thompson 			return (ENOMEM);
54202ac6454SAndrew Thompson 		}
54302ac6454SAndrew Thompson 		/* update some fields */
54402ac6454SAndrew Thompson 		f->fifo_index = n + USB_FIFO_RX;
545ae60fdfbSAndrew Thompson 		f->dev_ep_index = e;
546ae60fdfbSAndrew Thompson 		f->priv_sc0 = ep;
547a593f6b8SAndrew Thompson 		f->methods = &usb_ugen_methods;
548ae60fdfbSAndrew Thompson 		f->iface_index = ep->iface_index;
54902ac6454SAndrew Thompson 		f->udev = udev;
550a593f6b8SAndrew Thompson 		mtx_lock(&usb_ref_lock);
55102ac6454SAndrew Thompson 		udev->fifo[n + USB_FIFO_RX] = f;
552a593f6b8SAndrew Thompson 		mtx_unlock(&usb_ref_lock);
55302ac6454SAndrew Thompson 	}
55402ac6454SAndrew Thompson 	if (is_tx) {
555e13e19faSAndrew Thompson 		crd->txfifo = udev->fifo[n + USB_FIFO_TX];
55602ac6454SAndrew Thompson 	}
55702ac6454SAndrew Thompson 	if (is_rx) {
558e13e19faSAndrew Thompson 		crd->rxfifo = udev->fifo[n + USB_FIFO_RX];
55902ac6454SAndrew Thompson 	}
560ee3e3ff5SAndrew Thompson 	/* fill out fifo index */
561ee3e3ff5SAndrew Thompson 	DPRINTFN(5, "fifo index = %d\n", n);
562ee3e3ff5SAndrew Thompson 	cpd->fifo_index = n;
56302ac6454SAndrew Thompson 
56402ac6454SAndrew Thompson 	/* complete */
56502ac6454SAndrew Thompson 
56602ac6454SAndrew Thompson 	return (0);
56702ac6454SAndrew Thompson }
56802ac6454SAndrew Thompson 
56902ac6454SAndrew Thompson void
usb_fifo_free(struct usb_fifo * f)570a593f6b8SAndrew Thompson usb_fifo_free(struct usb_fifo *f)
57102ac6454SAndrew Thompson {
57202ac6454SAndrew Thompson 	uint8_t n;
57302ac6454SAndrew Thompson 
57402ac6454SAndrew Thompson 	if (f == NULL) {
57502ac6454SAndrew Thompson 		/* be NULL safe */
57602ac6454SAndrew Thompson 		return;
57702ac6454SAndrew Thompson 	}
57802ac6454SAndrew Thompson 	/* destroy symlink devices, if any */
57902ac6454SAndrew Thompson 	for (n = 0; n != 2; n++) {
58002ac6454SAndrew Thompson 		if (f->symlink[n]) {
581a593f6b8SAndrew Thompson 			usb_free_symlink(f->symlink[n]);
58202ac6454SAndrew Thompson 			f->symlink[n] = NULL;
58302ac6454SAndrew Thompson 		}
58402ac6454SAndrew Thompson 	}
585a593f6b8SAndrew Thompson 	mtx_lock(&usb_ref_lock);
58602ac6454SAndrew Thompson 
58702ac6454SAndrew Thompson 	/* delink ourselves to stop calls from userland */
58802ac6454SAndrew Thompson 	if ((f->fifo_index < USB_FIFO_MAX) &&
58902ac6454SAndrew Thompson 	    (f->udev != NULL) &&
59002ac6454SAndrew Thompson 	    (f->udev->fifo[f->fifo_index] == f)) {
59102ac6454SAndrew Thompson 		f->udev->fifo[f->fifo_index] = NULL;
59202ac6454SAndrew Thompson 	} else {
593767cb2e2SAndrew Thompson 		DPRINTFN(0, "USB FIFO %p has not been linked\n", f);
59402ac6454SAndrew Thompson 	}
59502ac6454SAndrew Thompson 
59602ac6454SAndrew Thompson 	/* decrease refcount */
59702ac6454SAndrew Thompson 	f->refcount--;
59802ac6454SAndrew Thompson 	/* need to wait until all callers have exited */
59902ac6454SAndrew Thompson 	while (f->refcount != 0) {
600a593f6b8SAndrew Thompson 		mtx_unlock(&usb_ref_lock);	/* avoid LOR */
60102ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
602b78e84d1SHans Petter Selasky 		/* prevent write flush, if any */
603b78e84d1SHans Petter Selasky 		f->flag_iserror = 1;
60402ac6454SAndrew Thompson 		/* get I/O thread out of any sleep state */
60502ac6454SAndrew Thompson 		if (f->flag_sleeping) {
60602ac6454SAndrew Thompson 			f->flag_sleeping = 0;
6078437751dSAndrew Thompson 			cv_broadcast(&f->cv_io);
60802ac6454SAndrew Thompson 		}
60902ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
610a593f6b8SAndrew Thompson 		mtx_lock(&usb_ref_lock);
61102ac6454SAndrew Thompson 
612c7d8c1c6SHans Petter Selasky 		/*
613c7d8c1c6SHans Petter Selasky 		 * Check if the "f->refcount" variable reached zero
614c7d8c1c6SHans Petter Selasky 		 * during the unlocked time before entering wait:
615c7d8c1c6SHans Petter Selasky 		 */
616c7d8c1c6SHans Petter Selasky 		if (f->refcount == 0)
617c7d8c1c6SHans Petter Selasky 			break;
618c7d8c1c6SHans Petter Selasky 
61902ac6454SAndrew Thompson 		/* wait for sync */
620a593f6b8SAndrew Thompson 		cv_wait(&f->cv_drain, &usb_ref_lock);
62102ac6454SAndrew Thompson 	}
622a593f6b8SAndrew Thompson 	mtx_unlock(&usb_ref_lock);
62302ac6454SAndrew Thompson 
62402ac6454SAndrew Thompson 	/* take care of closing the device here, if any */
625a593f6b8SAndrew Thompson 	usb_fifo_close(f, 0);
62602ac6454SAndrew Thompson 
6278437751dSAndrew Thompson 	cv_destroy(&f->cv_io);
6288437751dSAndrew Thompson 	cv_destroy(&f->cv_drain);
6299b077d72SHans Petter Selasky 	sx_destroy(&f->fs_fastpath_lock);
63002ac6454SAndrew Thompson 
631a6609003SHans Petter Selasky 	knlist_clear(&f->selinfo.si_note, 0);
632a6609003SHans Petter Selasky 	seldrain(&f->selinfo);
633e2723934SHans Petter Selasky 	knlist_destroy(&f->selinfo.si_note);
634e2723934SHans Petter Selasky 
63502ac6454SAndrew Thompson 	free(f, M_USBDEV);
63602ac6454SAndrew Thompson }
63702ac6454SAndrew Thompson 
638ae60fdfbSAndrew Thompson static struct usb_endpoint *
usb_dev_get_ep(struct usb_device * udev,uint8_t ep_index,uint8_t dir)639a593f6b8SAndrew Thompson usb_dev_get_ep(struct usb_device *udev, uint8_t ep_index, uint8_t dir)
64002ac6454SAndrew Thompson {
641ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep;
64202ac6454SAndrew Thompson 	uint8_t ep_dir;
64302ac6454SAndrew Thompson 
64402ac6454SAndrew Thompson 	if (ep_index == 0) {
6455b3bb704SAndrew Thompson 		ep = &udev->ctrl_ep;
64602ac6454SAndrew Thompson 	} else {
64702ac6454SAndrew Thompson 		if (dir == USB_FIFO_RX) {
648f29a0724SAndrew Thompson 			if (udev->flags.usb_mode == USB_MODE_HOST) {
64902ac6454SAndrew Thompson 				ep_dir = UE_DIR_IN;
65002ac6454SAndrew Thompson 			} else {
65102ac6454SAndrew Thompson 				ep_dir = UE_DIR_OUT;
65202ac6454SAndrew Thompson 			}
65302ac6454SAndrew Thompson 		} else {
654f29a0724SAndrew Thompson 			if (udev->flags.usb_mode == USB_MODE_HOST) {
65502ac6454SAndrew Thompson 				ep_dir = UE_DIR_OUT;
65602ac6454SAndrew Thompson 			} else {
65702ac6454SAndrew Thompson 				ep_dir = UE_DIR_IN;
65802ac6454SAndrew Thompson 			}
65902ac6454SAndrew Thompson 		}
660a593f6b8SAndrew Thompson 		ep = usbd_get_ep_by_addr(udev, ep_index | ep_dir);
66102ac6454SAndrew Thompson 	}
66202ac6454SAndrew Thompson 
663ae60fdfbSAndrew Thompson 	if (ep == NULL) {
664ae60fdfbSAndrew Thompson 		/* if the endpoint does not exist then return */
66502ac6454SAndrew Thompson 		return (NULL);
66602ac6454SAndrew Thompson 	}
667ae60fdfbSAndrew Thompson 	if (ep->edesc == NULL) {
668ae60fdfbSAndrew Thompson 		/* invalid endpoint */
66902ac6454SAndrew Thompson 		return (NULL);
67002ac6454SAndrew Thompson 	}
671ae60fdfbSAndrew Thompson 	return (ep);			/* success */
67202ac6454SAndrew Thompson }
67302ac6454SAndrew Thompson 
67402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
675a593f6b8SAndrew Thompson  *	usb_fifo_open
67602ac6454SAndrew Thompson  *
67702ac6454SAndrew Thompson  * Returns:
67802ac6454SAndrew Thompson  * 0: Success
67902ac6454SAndrew Thompson  * Else: Failure
68002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
68102ac6454SAndrew Thompson static int
usb_fifo_open(struct usb_cdev_privdata * cpd,struct usb_fifo * f,int fflags)682a593f6b8SAndrew Thompson usb_fifo_open(struct usb_cdev_privdata *cpd,
683760bc48eSAndrew Thompson     struct usb_fifo *f, int fflags)
68402ac6454SAndrew Thompson {
68502ac6454SAndrew Thompson 	int err;
68602ac6454SAndrew Thompson 
68702ac6454SAndrew Thompson 	if (f == NULL) {
68802ac6454SAndrew Thompson 		/* no FIFO there */
68902ac6454SAndrew Thompson 		DPRINTFN(2, "no FIFO\n");
69002ac6454SAndrew Thompson 		return (ENXIO);
69102ac6454SAndrew Thompson 	}
69202ac6454SAndrew Thompson 	/* remove FWRITE and FREAD flags */
69302ac6454SAndrew Thompson 	fflags &= ~(FWRITE | FREAD);
69402ac6454SAndrew Thompson 
69502ac6454SAndrew Thompson 	/* set correct file flags */
69602ac6454SAndrew Thompson 	if ((f->fifo_index & 1) == USB_FIFO_TX) {
69702ac6454SAndrew Thompson 		fflags |= FWRITE;
69802ac6454SAndrew Thompson 	} else {
69902ac6454SAndrew Thompson 		fflags |= FREAD;
70002ac6454SAndrew Thompson 	}
70102ac6454SAndrew Thompson 
70202ac6454SAndrew Thompson 	/* check if we are already opened */
70302ac6454SAndrew Thompson 	/* we don't need any locks when checking this variable */
7047214348fSAndrew Thompson 	if (f->curr_cpd != NULL) {
70502ac6454SAndrew Thompson 		err = EBUSY;
70602ac6454SAndrew Thompson 		goto done;
70702ac6454SAndrew Thompson 	}
708ee3e3ff5SAndrew Thompson 
7097214348fSAndrew Thompson 	/* reset short flag before open */
7107214348fSAndrew Thompson 	f->flag_short = 0;
7117214348fSAndrew Thompson 
71202ac6454SAndrew Thompson 	/* call open method */
713ee3e3ff5SAndrew Thompson 	err = (f->methods->f_open) (f, fflags);
71402ac6454SAndrew Thompson 	if (err) {
71502ac6454SAndrew Thompson 		goto done;
71602ac6454SAndrew Thompson 	}
71702ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
71802ac6454SAndrew Thompson 
71902ac6454SAndrew Thompson 	/* reset sleep flag */
72002ac6454SAndrew Thompson 	f->flag_sleeping = 0;
72102ac6454SAndrew Thompson 
72202ac6454SAndrew Thompson 	/* reset error flag */
72302ac6454SAndrew Thompson 	f->flag_iserror = 0;
72402ac6454SAndrew Thompson 
72502ac6454SAndrew Thompson 	/* reset complete flag */
72602ac6454SAndrew Thompson 	f->flag_iscomplete = 0;
72702ac6454SAndrew Thompson 
72802ac6454SAndrew Thompson 	/* reset select flag */
72902ac6454SAndrew Thompson 	f->flag_isselect = 0;
73002ac6454SAndrew Thompson 
73102ac6454SAndrew Thompson 	/* reset flushing flag */
73202ac6454SAndrew Thompson 	f->flag_flushing = 0;
73302ac6454SAndrew Thompson 
73402ac6454SAndrew Thompson 	/* reset ASYNC proc flag */
73502ac6454SAndrew Thompson 	f->async_p = NULL;
73602ac6454SAndrew Thompson 
737a593f6b8SAndrew Thompson 	mtx_lock(&usb_ref_lock);
7387214348fSAndrew Thompson 	/* flag the fifo as opened to prevent others */
7397214348fSAndrew Thompson 	f->curr_cpd = cpd;
740a593f6b8SAndrew Thompson 	mtx_unlock(&usb_ref_lock);
74102ac6454SAndrew Thompson 
74202ac6454SAndrew Thompson 	/* reset queue */
743a593f6b8SAndrew Thompson 	usb_fifo_reset(f);
74402ac6454SAndrew Thompson 
74502ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
74602ac6454SAndrew Thompson done:
74702ac6454SAndrew Thompson 	return (err);
74802ac6454SAndrew Thompson }
74902ac6454SAndrew Thompson 
75002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
751a593f6b8SAndrew Thompson  *	usb_fifo_reset
75202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
75302ac6454SAndrew Thompson void
usb_fifo_reset(struct usb_fifo * f)754a593f6b8SAndrew Thompson usb_fifo_reset(struct usb_fifo *f)
75502ac6454SAndrew Thompson {
756760bc48eSAndrew Thompson 	struct usb_mbuf *m;
75702ac6454SAndrew Thompson 
75802ac6454SAndrew Thompson 	if (f == NULL) {
75902ac6454SAndrew Thompson 		return;
76002ac6454SAndrew Thompson 	}
76102ac6454SAndrew Thompson 	while (1) {
76202ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
76302ac6454SAndrew Thompson 		if (m) {
76402ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->free_q, m);
76502ac6454SAndrew Thompson 		} else {
76602ac6454SAndrew Thompson 			break;
76702ac6454SAndrew Thompson 		}
76802ac6454SAndrew Thompson 	}
769bd73b187SAlfred Perlstein 	/* reset have fragment flag */
770bd73b187SAlfred Perlstein 	f->flag_have_fragment = 0;
77102ac6454SAndrew Thompson }
77202ac6454SAndrew Thompson 
77302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
774a593f6b8SAndrew Thompson  *	usb_fifo_close
77502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
77602ac6454SAndrew Thompson static void
usb_fifo_close(struct usb_fifo * f,int fflags)777a593f6b8SAndrew Thompson usb_fifo_close(struct usb_fifo *f, int fflags)
77802ac6454SAndrew Thompson {
77902ac6454SAndrew Thompson 	int err;
78002ac6454SAndrew Thompson 
78102ac6454SAndrew Thompson 	/* check if we are not opened */
7827214348fSAndrew Thompson 	if (f->curr_cpd == NULL) {
78302ac6454SAndrew Thompson 		/* nothing to do - already closed */
78402ac6454SAndrew Thompson 		return;
78502ac6454SAndrew Thompson 	}
78602ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
78702ac6454SAndrew Thompson 
7887214348fSAndrew Thompson 	/* clear current cdev private data pointer */
7890b6d54d4SHans Petter Selasky 	mtx_lock(&usb_ref_lock);
7907214348fSAndrew Thompson 	f->curr_cpd = NULL;
7910b6d54d4SHans Petter Selasky 	mtx_unlock(&usb_ref_lock);
7920b6d54d4SHans Petter Selasky 
7930b6d54d4SHans Petter Selasky 	/* check if we are watched by kevent */
7940b6d54d4SHans Petter Selasky 	KNOTE_LOCKED(&f->selinfo.si_note, 0);
79502ac6454SAndrew Thompson 
79602ac6454SAndrew Thompson 	/* check if we are selected */
79702ac6454SAndrew Thompson 	if (f->flag_isselect) {
79802ac6454SAndrew Thompson 		selwakeup(&f->selinfo);
79902ac6454SAndrew Thompson 		f->flag_isselect = 0;
80002ac6454SAndrew Thompson 	}
80102ac6454SAndrew Thompson 	/* check if a thread wants SIGIO */
80202ac6454SAndrew Thompson 	if (f->async_p != NULL) {
80302ac6454SAndrew Thompson 		PROC_LOCK(f->async_p);
8048451d0ddSKip Macy 		kern_psignal(f->async_p, SIGIO);
80502ac6454SAndrew Thompson 		PROC_UNLOCK(f->async_p);
80602ac6454SAndrew Thompson 		f->async_p = NULL;
80702ac6454SAndrew Thompson 	}
80802ac6454SAndrew Thompson 	/* remove FWRITE and FREAD flags */
80902ac6454SAndrew Thompson 	fflags &= ~(FWRITE | FREAD);
81002ac6454SAndrew Thompson 
81102ac6454SAndrew Thompson 	/* flush written data, if any */
81202ac6454SAndrew Thompson 	if ((f->fifo_index & 1) == USB_FIFO_TX) {
81302ac6454SAndrew Thompson 		if (!f->flag_iserror) {
81402ac6454SAndrew Thompson 			/* set flushing flag */
81502ac6454SAndrew Thompson 			f->flag_flushing = 1;
81602ac6454SAndrew Thompson 
817bd73b187SAlfred Perlstein 			/* get the last packet in */
818bd73b187SAlfred Perlstein 			if (f->flag_have_fragment) {
819bd73b187SAlfred Perlstein 				struct usb_mbuf *m;
820bd73b187SAlfred Perlstein 				f->flag_have_fragment = 0;
821bd73b187SAlfred Perlstein 				USB_IF_DEQUEUE(&f->free_q, m);
822bd73b187SAlfred Perlstein 				if (m) {
823bd73b187SAlfred Perlstein 					USB_IF_ENQUEUE(&f->used_q, m);
824bd73b187SAlfred Perlstein 				}
825bd73b187SAlfred Perlstein 			}
826bd73b187SAlfred Perlstein 
82702ac6454SAndrew Thompson 			/* start write transfer, if not already started */
82802ac6454SAndrew Thompson 			(f->methods->f_start_write) (f);
82902ac6454SAndrew Thompson 
83002ac6454SAndrew Thompson 			/* check if flushed already */
83102ac6454SAndrew Thompson 			while (f->flag_flushing &&
83202ac6454SAndrew Thompson 			    (!f->flag_iserror)) {
83302ac6454SAndrew Thompson 				/* wait until all data has been written */
83402ac6454SAndrew Thompson 				f->flag_sleeping = 1;
835b6630e50SHans Petter Selasky 				err = cv_timedwait_sig(&f->cv_io, f->priv_mtx,
836b6630e50SHans Petter Selasky 				    USB_MS_TO_TICKS(USB_DEFAULT_TIMEOUT));
83702ac6454SAndrew Thompson 				if (err) {
83802ac6454SAndrew Thompson 					DPRINTF("signal received\n");
83902ac6454SAndrew Thompson 					break;
84002ac6454SAndrew Thompson 				}
84102ac6454SAndrew Thompson 			}
84202ac6454SAndrew Thompson 		}
84302ac6454SAndrew Thompson 		fflags |= FWRITE;
84402ac6454SAndrew Thompson 
84502ac6454SAndrew Thompson 		/* stop write transfer, if not already stopped */
84602ac6454SAndrew Thompson 		(f->methods->f_stop_write) (f);
84702ac6454SAndrew Thompson 	} else {
84802ac6454SAndrew Thompson 		fflags |= FREAD;
84902ac6454SAndrew Thompson 
85002ac6454SAndrew Thompson 		/* stop write transfer, if not already stopped */
85102ac6454SAndrew Thompson 		(f->methods->f_stop_read) (f);
85202ac6454SAndrew Thompson 	}
85302ac6454SAndrew Thompson 
85402ac6454SAndrew Thompson 	/* check if we are sleeping */
85502ac6454SAndrew Thompson 	if (f->flag_sleeping) {
85602ac6454SAndrew Thompson 		DPRINTFN(2, "Sleeping at close!\n");
85702ac6454SAndrew Thompson 	}
85802ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
85902ac6454SAndrew Thompson 
86002ac6454SAndrew Thompson 	/* call close method */
861ee3e3ff5SAndrew Thompson 	(f->methods->f_close) (f, fflags);
86202ac6454SAndrew Thompson 
86302ac6454SAndrew Thompson 	DPRINTF("closed\n");
86402ac6454SAndrew Thompson }
86502ac6454SAndrew Thompson 
86602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
867a593f6b8SAndrew Thompson  *	usb_open - cdev callback
86802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
86902ac6454SAndrew Thompson static int
usb_open(struct cdev * dev,int fflags,int devtype,struct thread * td)870a593f6b8SAndrew Thompson usb_open(struct cdev *dev, int fflags, int devtype, struct thread *td)
87102ac6454SAndrew Thompson {
872760bc48eSAndrew Thompson 	struct usb_fs_privdata* pd = (struct usb_fs_privdata*)dev->si_drv1;
873e13e19faSAndrew Thompson 	struct usb_cdev_refdata refs;
874760bc48eSAndrew Thompson 	struct usb_cdev_privdata *cpd;
875151ba793SAlexander Kabaev 	int err;
87602ac6454SAndrew Thompson 
8777870adb6SEd Schouten 	DPRINTFN(2, "%s fflags=0x%08x\n", devtoname(dev), fflags);
87802ac6454SAndrew Thompson 
879ee3e3ff5SAndrew Thompson 	KASSERT(fflags & (FREAD|FWRITE), ("invalid open flags"));
880ee3e3ff5SAndrew Thompson 	if (((fflags & FREAD) && !(pd->mode & FREAD)) ||
881ee3e3ff5SAndrew Thompson 	    ((fflags & FWRITE) && !(pd->mode & FWRITE))) {
882ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "access mode not supported\n");
88302ac6454SAndrew Thompson 		return (EPERM);
88402ac6454SAndrew Thompson 	}
885ee3e3ff5SAndrew Thompson 
886ee3e3ff5SAndrew Thompson 	cpd = malloc(sizeof(*cpd), M_USBDEV, M_WAITOK | M_ZERO);
887ee3e3ff5SAndrew Thompson 
888a593f6b8SAndrew Thompson 	usb_loc_fill(pd, cpd);
889a593f6b8SAndrew Thompson 	err = usb_ref_device(cpd, &refs, 1);
89002ac6454SAndrew Thompson 	if (err) {
89102ac6454SAndrew Thompson 		DPRINTFN(2, "cannot ref device\n");
892ee3e3ff5SAndrew Thompson 		free(cpd, M_USBDEV);
89302ac6454SAndrew Thompson 		return (ENXIO);
89402ac6454SAndrew Thompson 	}
895ee3e3ff5SAndrew Thompson 	cpd->fflags = fflags;	/* access mode for open lifetime */
89602ac6454SAndrew Thompson 
89702ac6454SAndrew Thompson 	/* create FIFOs, if any */
898a593f6b8SAndrew Thompson 	err = usb_fifo_create(cpd, &refs);
89902ac6454SAndrew Thompson 	/* check for error */
90002ac6454SAndrew Thompson 	if (err) {
901ee3e3ff5SAndrew Thompson 		DPRINTFN(2, "cannot create fifo\n");
902a593f6b8SAndrew Thompson 		usb_unref_device(cpd, &refs);
903ee3e3ff5SAndrew Thompson 		free(cpd, M_USBDEV);
90402ac6454SAndrew Thompson 		return (err);
90502ac6454SAndrew Thompson 	}
90602ac6454SAndrew Thompson 	if (fflags & FREAD) {
907a593f6b8SAndrew Thompson 		err = usb_fifo_open(cpd, refs.rxfifo, fflags);
90802ac6454SAndrew Thompson 		if (err) {
90902ac6454SAndrew Thompson 			DPRINTFN(2, "read open failed\n");
910a593f6b8SAndrew Thompson 			usb_unref_device(cpd, &refs);
911ee3e3ff5SAndrew Thompson 			free(cpd, M_USBDEV);
91202ac6454SAndrew Thompson 			return (err);
91302ac6454SAndrew Thompson 		}
91402ac6454SAndrew Thompson 	}
91502ac6454SAndrew Thompson 	if (fflags & FWRITE) {
916a593f6b8SAndrew Thompson 		err = usb_fifo_open(cpd, refs.txfifo, fflags);
91702ac6454SAndrew Thompson 		if (err) {
91802ac6454SAndrew Thompson 			DPRINTFN(2, "write open failed\n");
91902ac6454SAndrew Thompson 			if (fflags & FREAD) {
920a593f6b8SAndrew Thompson 				usb_fifo_close(refs.rxfifo, fflags);
92102ac6454SAndrew Thompson 			}
922a593f6b8SAndrew Thompson 			usb_unref_device(cpd, &refs);
923ee3e3ff5SAndrew Thompson 			free(cpd, M_USBDEV);
92402ac6454SAndrew Thompson 			return (err);
92502ac6454SAndrew Thompson 		}
92602ac6454SAndrew Thompson 	}
927a593f6b8SAndrew Thompson 	usb_unref_device(cpd, &refs);
928a593f6b8SAndrew Thompson 	devfs_set_cdevpriv(cpd, usb_close);
92902ac6454SAndrew Thompson 
930ee3e3ff5SAndrew Thompson 	return (0);
93102ac6454SAndrew Thompson }
93202ac6454SAndrew Thompson 
93302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
934a593f6b8SAndrew Thompson  *	usb_close - cdev callback
93502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
93602ac6454SAndrew Thompson static void
usb_close(void * arg)937a593f6b8SAndrew Thompson usb_close(void *arg)
93802ac6454SAndrew Thompson {
939e13e19faSAndrew Thompson 	struct usb_cdev_refdata refs;
940760bc48eSAndrew Thompson 	struct usb_cdev_privdata *cpd = arg;
941ee3e3ff5SAndrew Thompson 	int err;
94202ac6454SAndrew Thompson 
9437214348fSAndrew Thompson 	DPRINTFN(2, "cpd=%p\n", cpd);
944ee3e3ff5SAndrew Thompson 
945f97da128SHans Petter Selasky 	err = usb_ref_device(cpd, &refs,
946f97da128SHans Petter Selasky 	    2 /* uref and allow detached state */);
947f97da128SHans Petter Selasky 	if (err) {
9485c8c627bSHans Petter Selasky 		DPRINTFN(2, "Cannot grab USB reference when "
949f97da128SHans Petter Selasky 		    "closing USB file handle\n");
950fbaee0f1SHans Petter Selasky 		goto done;
95102ac6454SAndrew Thompson 	}
952ee3e3ff5SAndrew Thompson 	if (cpd->fflags & FREAD) {
953a593f6b8SAndrew Thompson 		usb_fifo_close(refs.rxfifo, cpd->fflags);
95402ac6454SAndrew Thompson 	}
955ee3e3ff5SAndrew Thompson 	if (cpd->fflags & FWRITE) {
956a593f6b8SAndrew Thompson 		usb_fifo_close(refs.txfifo, cpd->fflags);
95702ac6454SAndrew Thompson 	}
958a593f6b8SAndrew Thompson 	usb_unref_device(cpd, &refs);
959fbaee0f1SHans Petter Selasky done:
960ee3e3ff5SAndrew Thompson 	free(cpd, M_USBDEV);
96102ac6454SAndrew Thompson }
96202ac6454SAndrew Thompson 
96302ac6454SAndrew Thompson static void
usb_dev_init(void * arg)964a593f6b8SAndrew Thompson usb_dev_init(void *arg)
96502ac6454SAndrew Thompson {
966a593f6b8SAndrew Thompson 	mtx_init(&usb_ref_lock, "USB ref mutex", NULL, MTX_DEF);
967a593f6b8SAndrew Thompson 	sx_init(&usb_sym_lock, "USB sym mutex");
968a593f6b8SAndrew Thompson 	TAILQ_INIT(&usb_sym_head);
96902ac6454SAndrew Thompson 
97002ac6454SAndrew Thompson 	/* check the UGEN methods */
971a593f6b8SAndrew Thompson 	usb_fifo_check_methods(&usb_ugen_methods);
97202ac6454SAndrew Thompson }
97302ac6454SAndrew Thompson 
974a593f6b8SAndrew Thompson SYSINIT(usb_dev_init, SI_SUB_KLD, SI_ORDER_FIRST, usb_dev_init, NULL);
97502ac6454SAndrew Thompson 
97602ac6454SAndrew Thompson static void
usb_dev_init_post(void * arg)977a593f6b8SAndrew Thompson usb_dev_init_post(void *arg)
97802ac6454SAndrew Thompson {
97902ac6454SAndrew Thompson 	/*
980ee3e3ff5SAndrew Thompson 	 * Create /dev/usb - this is needed for usbconfig(8), which
981ee3e3ff5SAndrew Thompson 	 * needs a well-known device name to access.
98202ac6454SAndrew Thompson 	 */
983a593f6b8SAndrew Thompson 	usb_dev = make_dev(&usb_static_devsw, 0, UID_ROOT, GID_OPERATOR,
984ee3e3ff5SAndrew Thompson 	    0644, USB_DEVICE_NAME);
985a593f6b8SAndrew Thompson 	if (usb_dev == NULL) {
986767cb2e2SAndrew Thompson 		DPRINTFN(0, "Could not create usb bus device\n");
98702ac6454SAndrew Thompson 	}
98802ac6454SAndrew Thompson }
98902ac6454SAndrew Thompson 
990a593f6b8SAndrew Thompson SYSINIT(usb_dev_init_post, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, usb_dev_init_post, NULL);
99102ac6454SAndrew Thompson 
99202ac6454SAndrew Thompson static void
usb_dev_uninit(void * arg)993a593f6b8SAndrew Thompson usb_dev_uninit(void *arg)
99402ac6454SAndrew Thompson {
995a593f6b8SAndrew Thompson 	if (usb_dev != NULL) {
996a593f6b8SAndrew Thompson 		destroy_dev(usb_dev);
997a593f6b8SAndrew Thompson 		usb_dev = NULL;
99802ac6454SAndrew Thompson 	}
999a593f6b8SAndrew Thompson 	mtx_destroy(&usb_ref_lock);
1000a593f6b8SAndrew Thompson 	sx_destroy(&usb_sym_lock);
100102ac6454SAndrew Thompson }
100202ac6454SAndrew Thompson 
1003a593f6b8SAndrew Thompson SYSUNINIT(usb_dev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb_dev_uninit, NULL);
100402ac6454SAndrew Thompson 
100502ac6454SAndrew Thompson static int
usb_ioctl_f_sub(struct usb_fifo * f,u_long cmd,void * addr,struct thread * td)1006a593f6b8SAndrew Thompson usb_ioctl_f_sub(struct usb_fifo *f, u_long cmd, void *addr,
100702ac6454SAndrew Thompson     struct thread *td)
100802ac6454SAndrew Thompson {
100902ac6454SAndrew Thompson 	int error = 0;
101002ac6454SAndrew Thompson 
101102ac6454SAndrew Thompson 	switch (cmd) {
101202ac6454SAndrew Thompson 	case FIODTYPE:
101302ac6454SAndrew Thompson 		*(int *)addr = 0;	/* character device */
101402ac6454SAndrew Thompson 		break;
101502ac6454SAndrew Thompson 
101602ac6454SAndrew Thompson 	case FIONBIO:
101702ac6454SAndrew Thompson 		/* handled by upper FS layer */
101802ac6454SAndrew Thompson 		break;
101902ac6454SAndrew Thompson 
102002ac6454SAndrew Thompson 	case FIOASYNC:
102102ac6454SAndrew Thompson 		if (*(int *)addr) {
102202ac6454SAndrew Thompson 			if (f->async_p != NULL) {
102302ac6454SAndrew Thompson 				error = EBUSY;
102402ac6454SAndrew Thompson 				break;
102502ac6454SAndrew Thompson 			}
102602ac6454SAndrew Thompson 			f->async_p = USB_TD_GET_PROC(td);
102702ac6454SAndrew Thompson 		} else {
102802ac6454SAndrew Thompson 			f->async_p = NULL;
102902ac6454SAndrew Thompson 		}
103002ac6454SAndrew Thompson 		break;
103102ac6454SAndrew Thompson 
103202ac6454SAndrew Thompson 		/* XXX this is not the most general solution */
103302ac6454SAndrew Thompson 	case TIOCSPGRP:
103402ac6454SAndrew Thompson 		if (f->async_p == NULL) {
103502ac6454SAndrew Thompson 			error = EINVAL;
103602ac6454SAndrew Thompson 			break;
103702ac6454SAndrew Thompson 		}
103802ac6454SAndrew Thompson 		if (*(int *)addr != USB_PROC_GET_GID(f->async_p)) {
103902ac6454SAndrew Thompson 			error = EPERM;
104002ac6454SAndrew Thompson 			break;
104102ac6454SAndrew Thompson 		}
104202ac6454SAndrew Thompson 		break;
104302ac6454SAndrew Thompson 	default:
104402ac6454SAndrew Thompson 		return (ENOIOCTL);
104502ac6454SAndrew Thompson 	}
1046cba30496SAndrew Thompson 	DPRINTFN(3, "cmd 0x%lx = %d\n", cmd, error);
104702ac6454SAndrew Thompson 	return (error);
104802ac6454SAndrew Thompson }
104902ac6454SAndrew Thompson 
1050ee3e3ff5SAndrew Thompson /*------------------------------------------------------------------------*
1051a593f6b8SAndrew Thompson  *	usb_ioctl - cdev callback
1052ee3e3ff5SAndrew Thompson  *------------------------------------------------------------------------*/
105302ac6454SAndrew Thompson static int
usb_ioctl(struct cdev * dev,u_long cmd,caddr_t addr,int fflag,struct thread * td)1054a593f6b8SAndrew Thompson usb_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int fflag, struct thread* td)
105502ac6454SAndrew Thompson {
1056e13e19faSAndrew Thompson 	struct usb_cdev_refdata refs;
1057760bc48eSAndrew Thompson 	struct usb_cdev_privdata* cpd;
1058760bc48eSAndrew Thompson 	struct usb_fifo *f;
105902ac6454SAndrew Thompson 	int fflags;
106002ac6454SAndrew Thompson 	int err;
106102ac6454SAndrew Thompson 
1062cba30496SAndrew Thompson 	DPRINTFN(2, "cmd=0x%lx\n", cmd);
1063cba30496SAndrew Thompson 
1064ee3e3ff5SAndrew Thompson 	err = devfs_get_cdevpriv((void **)&cpd);
1065ee3e3ff5SAndrew Thompson 	if (err != 0)
1066ee3e3ff5SAndrew Thompson 		return (err);
1067ee3e3ff5SAndrew Thompson 
1068f5f145baSAndrew Thompson 	/*
1069e13e19faSAndrew Thompson 	 * Performance optimisation: We try to check for IOCTL's that
1070f5f145baSAndrew Thompson 	 * don't need the USB reference first. Then we grab the USB
1071f5f145baSAndrew Thompson 	 * reference if we need it!
1072f5f145baSAndrew Thompson 	 */
1073a593f6b8SAndrew Thompson 	err = usb_ref_device(cpd, &refs, 0 /* no uref */ );
1074cb18f7d1SAlfred Perlstein 	if (err)
107502ac6454SAndrew Thompson 		return (ENXIO);
1076cb18f7d1SAlfred Perlstein 
1077ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
107802ac6454SAndrew Thompson 
107902ac6454SAndrew Thompson 	f = NULL;			/* set default value */
108002ac6454SAndrew Thompson 	err = ENOIOCTL;			/* set default value */
108102ac6454SAndrew Thompson 
108202ac6454SAndrew Thompson 	if (fflags & FWRITE) {
1083e13e19faSAndrew Thompson 		f = refs.txfifo;
1084a593f6b8SAndrew Thompson 		err = usb_ioctl_f_sub(f, cmd, addr, td);
108502ac6454SAndrew Thompson 	}
108602ac6454SAndrew Thompson 	if (fflags & FREAD) {
1087e13e19faSAndrew Thompson 		f = refs.rxfifo;
1088a593f6b8SAndrew Thompson 		err = usb_ioctl_f_sub(f, cmd, addr, td);
108902ac6454SAndrew Thompson 	}
1090ee3e3ff5SAndrew Thompson 	KASSERT(f != NULL, ("fifo not found"));
10918f9750b7SHans Petter Selasky 	if (err != ENOIOCTL)
10928f9750b7SHans Petter Selasky 		goto done;
10938f9750b7SHans Petter Selasky 
1094ee3e3ff5SAndrew Thompson 	err = (f->methods->f_ioctl) (f, cmd, addr, fflags);
10958f9750b7SHans Petter Selasky 
1096cba30496SAndrew Thompson 	DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err);
10978f9750b7SHans Petter Selasky 
10988f9750b7SHans Petter Selasky 	if (err != ENOIOCTL)
10998f9750b7SHans Petter Selasky 		goto done;
11008f9750b7SHans Petter Selasky 
1101a593f6b8SAndrew Thompson 	if (usb_usb_ref_device(cpd, &refs)) {
1102b06d477bSHans Petter Selasky 		/* we lost the reference */
1103b06d477bSHans Petter Selasky 		return (ENXIO);
110402ac6454SAndrew Thompson 	}
11058f9750b7SHans Petter Selasky 
1106ee3e3ff5SAndrew Thompson 	err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags);
11078f9750b7SHans Petter Selasky 
1108cba30496SAndrew Thompson 	DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err);
11098f9750b7SHans Petter Selasky 
11108f9750b7SHans Petter Selasky 	if (err == ENOIOCTL)
111102ac6454SAndrew Thompson 		err = ENOTTY;
11128f9750b7SHans Petter Selasky 
11138f9750b7SHans Petter Selasky 	if (err)
11148f9750b7SHans Petter Selasky 		goto done;
11158f9750b7SHans Petter Selasky 
11168f9750b7SHans Petter Selasky 	/* Wait for re-enumeration, if any */
11178f9750b7SHans Petter Selasky 
1118d6f4a9f9SHans Petter Selasky 	while (f->udev->re_enumerate_wait != USB_RE_ENUM_DONE) {
11198f9750b7SHans Petter Selasky 		usb_unref_device(cpd, &refs);
11208f9750b7SHans Petter Selasky 
11218f9750b7SHans Petter Selasky 		usb_pause_mtx(NULL, hz / 128);
11228f9750b7SHans Petter Selasky 
1123d64e9217SHans Petter Selasky 		while (usb_ref_device(cpd, &refs, 1 /* need uref */)) {
1124d64e9217SHans Petter Selasky 			if (usb_ref_device(cpd, &refs, 0)) {
1125b06d477bSHans Petter Selasky 				/* device no longer exists */
1126b06d477bSHans Petter Selasky 				return (ENXIO);
112702ac6454SAndrew Thompson 			}
1128d64e9217SHans Petter Selasky 			usb_unref_device(cpd, &refs);
1129d64e9217SHans Petter Selasky 			usb_pause_mtx(NULL, hz / 128);
1130d64e9217SHans Petter Selasky 		}
11318f9750b7SHans Petter Selasky 	}
11328f9750b7SHans Petter Selasky 
113302ac6454SAndrew Thompson done:
1134a593f6b8SAndrew Thompson 	usb_unref_device(cpd, &refs);
113502ac6454SAndrew Thompson 	return (err);
113602ac6454SAndrew Thompson }
113702ac6454SAndrew Thompson 
11380b6d54d4SHans Petter Selasky static void
usb_filter_detach(struct knote * kn)11390b6d54d4SHans Petter Selasky usb_filter_detach(struct knote *kn)
11400b6d54d4SHans Petter Selasky {
11410b6d54d4SHans Petter Selasky 	struct usb_fifo *f = kn->kn_hook;
11420b6d54d4SHans Petter Selasky 	knlist_remove(&f->selinfo.si_note, kn, 0);
11430b6d54d4SHans Petter Selasky }
11440b6d54d4SHans Petter Selasky 
11450b6d54d4SHans Petter Selasky static int
usb_filter_write(struct knote * kn,long hint)11460b6d54d4SHans Petter Selasky usb_filter_write(struct knote *kn, long hint)
11470b6d54d4SHans Petter Selasky {
11480b6d54d4SHans Petter Selasky 	struct usb_cdev_privdata* cpd;
11490b6d54d4SHans Petter Selasky 	struct usb_fifo *f;
11500b6d54d4SHans Petter Selasky 	struct usb_mbuf *m;
11510b6d54d4SHans Petter Selasky 
11520b6d54d4SHans Petter Selasky 	DPRINTFN(2, "\n");
11530b6d54d4SHans Petter Selasky 
11540b6d54d4SHans Petter Selasky 	f = kn->kn_hook;
11550b6d54d4SHans Petter Selasky 
11560eb8d462SHans Petter Selasky 	USB_MTX_ASSERT(f->priv_mtx, MA_OWNED);
11570b6d54d4SHans Petter Selasky 
11580b6d54d4SHans Petter Selasky 	cpd = f->curr_cpd;
11590b6d54d4SHans Petter Selasky 	if (cpd == NULL) {
11600b6d54d4SHans Petter Selasky 		m = (void *)1;
11610b6d54d4SHans Petter Selasky 	} else if (f->fs_ep_max == 0) {
11620b6d54d4SHans Petter Selasky 		if (f->flag_iserror) {
11630b6d54d4SHans Petter Selasky 			/* we got an error */
11640b6d54d4SHans Petter Selasky 			m = (void *)1;
11650b6d54d4SHans Petter Selasky 		} else {
11660b6d54d4SHans Petter Selasky 			if (f->queue_data == NULL) {
11670b6d54d4SHans Petter Selasky 				/*
11680b6d54d4SHans Petter Selasky 				 * start write transfer, if not
11690b6d54d4SHans Petter Selasky 				 * already started
11700b6d54d4SHans Petter Selasky 				 */
11710b6d54d4SHans Petter Selasky 				(f->methods->f_start_write) (f);
11720b6d54d4SHans Petter Selasky 			}
11730b6d54d4SHans Petter Selasky 			/* check if any packets are available */
11740b6d54d4SHans Petter Selasky 			USB_IF_POLL(&f->free_q, m);
11750b6d54d4SHans Petter Selasky 		}
11760b6d54d4SHans Petter Selasky 	} else {
11770b6d54d4SHans Petter Selasky 		if (f->flag_iscomplete) {
11780b6d54d4SHans Petter Selasky 			m = (void *)1;
11790b6d54d4SHans Petter Selasky 		} else {
11800b6d54d4SHans Petter Selasky 			m = NULL;
11810b6d54d4SHans Petter Selasky 		}
11820b6d54d4SHans Petter Selasky 	}
11830b6d54d4SHans Petter Selasky 	return (m ? 1 : 0);
11840b6d54d4SHans Petter Selasky }
11850b6d54d4SHans Petter Selasky 
11860b6d54d4SHans Petter Selasky static int
usb_filter_read(struct knote * kn,long hint)11870b6d54d4SHans Petter Selasky usb_filter_read(struct knote *kn, long hint)
11880b6d54d4SHans Petter Selasky {
11890b6d54d4SHans Petter Selasky 	struct usb_cdev_privdata* cpd;
11900b6d54d4SHans Petter Selasky 	struct usb_fifo *f;
11910b6d54d4SHans Petter Selasky 	struct usb_mbuf *m;
11920b6d54d4SHans Petter Selasky 
11930b6d54d4SHans Petter Selasky 	DPRINTFN(2, "\n");
11940b6d54d4SHans Petter Selasky 
11950b6d54d4SHans Petter Selasky 	f = kn->kn_hook;
11960b6d54d4SHans Petter Selasky 
11970eb8d462SHans Petter Selasky 	USB_MTX_ASSERT(f->priv_mtx, MA_OWNED);
11980b6d54d4SHans Petter Selasky 
11990b6d54d4SHans Petter Selasky 	cpd = f->curr_cpd;
12000b6d54d4SHans Petter Selasky 	if (cpd == NULL) {
12010b6d54d4SHans Petter Selasky 		m = (void *)1;
12020b6d54d4SHans Petter Selasky 	} else if (f->fs_ep_max == 0) {
12030b6d54d4SHans Petter Selasky 		if (f->flag_iserror) {
12040b6d54d4SHans Petter Selasky 			/* we have an error */
12050b6d54d4SHans Petter Selasky 			m = (void *)1;
12060b6d54d4SHans Petter Selasky 		} else {
12070b6d54d4SHans Petter Selasky 			if (f->queue_data == NULL) {
12080b6d54d4SHans Petter Selasky 				/*
12090b6d54d4SHans Petter Selasky 				 * start read transfer, if not
12100b6d54d4SHans Petter Selasky 				 * already started
12110b6d54d4SHans Petter Selasky 				 */
12120b6d54d4SHans Petter Selasky 				(f->methods->f_start_read) (f);
12130b6d54d4SHans Petter Selasky 			}
12140b6d54d4SHans Petter Selasky 			/* check if any packets are available */
12150b6d54d4SHans Petter Selasky 			USB_IF_POLL(&f->used_q, m);
12160b6d54d4SHans Petter Selasky 
12170b6d54d4SHans Petter Selasky 			/* start reading data, if any */
12180b6d54d4SHans Petter Selasky 			if (m == NULL)
12190b6d54d4SHans Petter Selasky 				(f->methods->f_start_read) (f);
12200b6d54d4SHans Petter Selasky 		}
12210b6d54d4SHans Petter Selasky 	} else {
12220b6d54d4SHans Petter Selasky 		if (f->flag_iscomplete) {
12230b6d54d4SHans Petter Selasky 			m = (void *)1;
12240b6d54d4SHans Petter Selasky 		} else {
12250b6d54d4SHans Petter Selasky 			m = NULL;
12260b6d54d4SHans Petter Selasky 		}
12270b6d54d4SHans Petter Selasky 	}
12280b6d54d4SHans Petter Selasky 	return (m ? 1 : 0);
12290b6d54d4SHans Petter Selasky }
12300b6d54d4SHans Petter Selasky 
12310b6d54d4SHans Petter Selasky static struct filterops usb_filtops_write = {
12320b6d54d4SHans Petter Selasky 	.f_isfd = 1,
12330b6d54d4SHans Petter Selasky 	.f_detach = usb_filter_detach,
12340b6d54d4SHans Petter Selasky 	.f_event = usb_filter_write,
12350b6d54d4SHans Petter Selasky };
12360b6d54d4SHans Petter Selasky 
12370b6d54d4SHans Petter Selasky static struct filterops usb_filtops_read = {
12380b6d54d4SHans Petter Selasky 	.f_isfd = 1,
12390b6d54d4SHans Petter Selasky 	.f_detach = usb_filter_detach,
12400b6d54d4SHans Petter Selasky 	.f_event = usb_filter_read,
12410b6d54d4SHans Petter Selasky };
12420b6d54d4SHans Petter Selasky 
12430b6d54d4SHans Petter Selasky /* ARGSUSED */
12440b6d54d4SHans Petter Selasky static int
usb_kqfilter(struct cdev * dev,struct knote * kn)12450b6d54d4SHans Petter Selasky usb_kqfilter(struct cdev* dev, struct knote *kn)
12460b6d54d4SHans Petter Selasky {
12470b6d54d4SHans Petter Selasky 	struct usb_cdev_refdata refs;
12480b6d54d4SHans Petter Selasky 	struct usb_cdev_privdata* cpd;
12490b6d54d4SHans Petter Selasky 	struct usb_fifo *f;
12500b6d54d4SHans Petter Selasky 	int fflags;
12510b6d54d4SHans Petter Selasky 	int err = EINVAL;
12520b6d54d4SHans Petter Selasky 
12530b6d54d4SHans Petter Selasky 	DPRINTFN(2, "\n");
12540b6d54d4SHans Petter Selasky 
12550b6d54d4SHans Petter Selasky 	if (devfs_get_cdevpriv((void **)&cpd) != 0 ||
12560b6d54d4SHans Petter Selasky 	    usb_ref_device(cpd, &refs, 0) != 0)
12570b6d54d4SHans Petter Selasky 		return (ENXIO);
12580b6d54d4SHans Petter Selasky 
12590b6d54d4SHans Petter Selasky 	fflags = cpd->fflags;
12600b6d54d4SHans Petter Selasky 
12610b6d54d4SHans Petter Selasky 	/* Figure out who needs service */
12620b6d54d4SHans Petter Selasky 	switch (kn->kn_filter) {
12630b6d54d4SHans Petter Selasky 	case EVFILT_WRITE:
12640b6d54d4SHans Petter Selasky 		if (fflags & FWRITE) {
12650b6d54d4SHans Petter Selasky 			f = refs.txfifo;
12660b6d54d4SHans Petter Selasky 			kn->kn_fop = &usb_filtops_write;
12670b6d54d4SHans Petter Selasky 			err = 0;
12680b6d54d4SHans Petter Selasky 		}
12690b6d54d4SHans Petter Selasky 		break;
12700b6d54d4SHans Petter Selasky 	case EVFILT_READ:
12710b6d54d4SHans Petter Selasky 		if (fflags & FREAD) {
12720b6d54d4SHans Petter Selasky 			f = refs.rxfifo;
12730b6d54d4SHans Petter Selasky 			kn->kn_fop = &usb_filtops_read;
12740b6d54d4SHans Petter Selasky 			err = 0;
12750b6d54d4SHans Petter Selasky 		}
12760b6d54d4SHans Petter Selasky 		break;
12770b6d54d4SHans Petter Selasky 	default:
12780b6d54d4SHans Petter Selasky 		err = EOPNOTSUPP;
12790b6d54d4SHans Petter Selasky 		break;
12800b6d54d4SHans Petter Selasky 	}
12810b6d54d4SHans Petter Selasky 
12820b6d54d4SHans Petter Selasky 	if (err == 0) {
12830b6d54d4SHans Petter Selasky 		kn->kn_hook = f;
12840b6d54d4SHans Petter Selasky 		mtx_lock(f->priv_mtx);
12850b6d54d4SHans Petter Selasky 		knlist_add(&f->selinfo.si_note, kn, 1);
12860b6d54d4SHans Petter Selasky 		mtx_unlock(f->priv_mtx);
12870b6d54d4SHans Petter Selasky 	}
12880b6d54d4SHans Petter Selasky 
12890b6d54d4SHans Petter Selasky 	usb_unref_device(cpd, &refs);
12900b6d54d4SHans Petter Selasky 	return (err);
12910b6d54d4SHans Petter Selasky }
12920b6d54d4SHans Petter Selasky 
129302ac6454SAndrew Thompson /* ARGSUSED */
129402ac6454SAndrew Thompson static int
usb_poll(struct cdev * dev,int events,struct thread * td)1295a593f6b8SAndrew Thompson usb_poll(struct cdev* dev, int events, struct thread* td)
129602ac6454SAndrew Thompson {
1297e13e19faSAndrew Thompson 	struct usb_cdev_refdata refs;
1298760bc48eSAndrew Thompson 	struct usb_cdev_privdata* cpd;
1299760bc48eSAndrew Thompson 	struct usb_fifo *f;
1300760bc48eSAndrew Thompson 	struct usb_mbuf *m;
13018a93629eSAndrew Thompson 	int fflags, revents;
130202ac6454SAndrew Thompson 
13038a93629eSAndrew Thompson 	if (devfs_get_cdevpriv((void **)&cpd) != 0 ||
1304a593f6b8SAndrew Thompson 	    usb_ref_device(cpd, &refs, 0) != 0)
13058a93629eSAndrew Thompson 		return (events &
13068a93629eSAndrew Thompson 		    (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM));
1307ee3e3ff5SAndrew Thompson 
1308ee3e3ff5SAndrew Thompson 	fflags = cpd->fflags;
130902ac6454SAndrew Thompson 
131002ac6454SAndrew Thompson 	/* Figure out who needs service */
1311ee3e3ff5SAndrew Thompson 	revents = 0;
131202ac6454SAndrew Thompson 	if ((events & (POLLOUT | POLLWRNORM)) &&
131302ac6454SAndrew Thompson 	    (fflags & FWRITE)) {
1314e13e19faSAndrew Thompson 		f = refs.txfifo;
131502ac6454SAndrew Thompson 
131602ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
131702ac6454SAndrew Thompson 
1318e13e19faSAndrew Thompson 		if (!refs.is_usbfs) {
131902ac6454SAndrew Thompson 			if (f->flag_iserror) {
132002ac6454SAndrew Thompson 				/* we got an error */
132102ac6454SAndrew Thompson 				m = (void *)1;
132202ac6454SAndrew Thompson 			} else {
132302ac6454SAndrew Thompson 				if (f->queue_data == NULL) {
132402ac6454SAndrew Thompson 					/*
132502ac6454SAndrew Thompson 					 * start write transfer, if not
132602ac6454SAndrew Thompson 					 * already started
132702ac6454SAndrew Thompson 					 */
132802ac6454SAndrew Thompson 					(f->methods->f_start_write) (f);
132902ac6454SAndrew Thompson 				}
133002ac6454SAndrew Thompson 				/* check if any packets are available */
133102ac6454SAndrew Thompson 				USB_IF_POLL(&f->free_q, m);
133202ac6454SAndrew Thompson 			}
133302ac6454SAndrew Thompson 		} else {
133402ac6454SAndrew Thompson 			if (f->flag_iscomplete) {
133502ac6454SAndrew Thompson 				m = (void *)1;
133602ac6454SAndrew Thompson 			} else {
133702ac6454SAndrew Thompson 				m = NULL;
133802ac6454SAndrew Thompson 			}
133902ac6454SAndrew Thompson 		}
134002ac6454SAndrew Thompson 
134102ac6454SAndrew Thompson 		if (m) {
134202ac6454SAndrew Thompson 			revents |= events & (POLLOUT | POLLWRNORM);
134302ac6454SAndrew Thompson 		} else {
134402ac6454SAndrew Thompson 			f->flag_isselect = 1;
134502ac6454SAndrew Thompson 			selrecord(td, &f->selinfo);
134602ac6454SAndrew Thompson 		}
134702ac6454SAndrew Thompson 
134802ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
134902ac6454SAndrew Thompson 	}
135002ac6454SAndrew Thompson 	if ((events & (POLLIN | POLLRDNORM)) &&
135102ac6454SAndrew Thompson 	    (fflags & FREAD)) {
1352e13e19faSAndrew Thompson 		f = refs.rxfifo;
135302ac6454SAndrew Thompson 
135402ac6454SAndrew Thompson 		mtx_lock(f->priv_mtx);
135502ac6454SAndrew Thompson 
1356e13e19faSAndrew Thompson 		if (!refs.is_usbfs) {
135702ac6454SAndrew Thompson 			if (f->flag_iserror) {
13580b6d54d4SHans Petter Selasky 				/* we have an error */
135902ac6454SAndrew Thompson 				m = (void *)1;
136002ac6454SAndrew Thompson 			} else {
136102ac6454SAndrew Thompson 				if (f->queue_data == NULL) {
136202ac6454SAndrew Thompson 					/*
136302ac6454SAndrew Thompson 					 * start read transfer, if not
136402ac6454SAndrew Thompson 					 * already started
136502ac6454SAndrew Thompson 					 */
136602ac6454SAndrew Thompson 					(f->methods->f_start_read) (f);
136702ac6454SAndrew Thompson 				}
136802ac6454SAndrew Thompson 				/* check if any packets are available */
136902ac6454SAndrew Thompson 				USB_IF_POLL(&f->used_q, m);
137002ac6454SAndrew Thompson 			}
137102ac6454SAndrew Thompson 		} else {
137202ac6454SAndrew Thompson 			if (f->flag_iscomplete) {
137302ac6454SAndrew Thompson 				m = (void *)1;
137402ac6454SAndrew Thompson 			} else {
137502ac6454SAndrew Thompson 				m = NULL;
137602ac6454SAndrew Thompson 			}
137702ac6454SAndrew Thompson 		}
137802ac6454SAndrew Thompson 
137902ac6454SAndrew Thompson 		if (m) {
138002ac6454SAndrew Thompson 			revents |= events & (POLLIN | POLLRDNORM);
138102ac6454SAndrew Thompson 		} else {
138202ac6454SAndrew Thompson 			f->flag_isselect = 1;
138302ac6454SAndrew Thompson 			selrecord(td, &f->selinfo);
138402ac6454SAndrew Thompson 
1385e13e19faSAndrew Thompson 			if (!refs.is_usbfs) {
138602ac6454SAndrew Thompson 				/* start reading data */
138702ac6454SAndrew Thompson 				(f->methods->f_start_read) (f);
138802ac6454SAndrew Thompson 			}
138902ac6454SAndrew Thompson 		}
139002ac6454SAndrew Thompson 
139102ac6454SAndrew Thompson 		mtx_unlock(f->priv_mtx);
139202ac6454SAndrew Thompson 	}
1393a593f6b8SAndrew Thompson 	usb_unref_device(cpd, &refs);
139402ac6454SAndrew Thompson 	return (revents);
139502ac6454SAndrew Thompson }
139602ac6454SAndrew Thompson 
139702ac6454SAndrew Thompson static int
usb_read(struct cdev * dev,struct uio * uio,int ioflag)1398a593f6b8SAndrew Thompson usb_read(struct cdev *dev, struct uio *uio, int ioflag)
139902ac6454SAndrew Thompson {
1400e13e19faSAndrew Thompson 	struct usb_cdev_refdata refs;
1401760bc48eSAndrew Thompson 	struct usb_cdev_privdata* cpd;
1402760bc48eSAndrew Thompson 	struct usb_fifo *f;
1403760bc48eSAndrew Thompson 	struct usb_mbuf *m;
140402ac6454SAndrew Thompson 	int io_len;
140502ac6454SAndrew Thompson 	int err;
140602ac6454SAndrew Thompson 	uint8_t tr_data = 0;
140702ac6454SAndrew Thompson 
1408ee3e3ff5SAndrew Thompson 	err = devfs_get_cdevpriv((void **)&cpd);
1409ee3e3ff5SAndrew Thompson 	if (err != 0)
1410ee3e3ff5SAndrew Thompson 		return (err);
141102ac6454SAndrew Thompson 
1412a593f6b8SAndrew Thompson 	err = usb_ref_device(cpd, &refs, 0 /* no uref */ );
1413b06d477bSHans Petter Selasky 	if (err)
141402ac6454SAndrew Thompson 		return (ENXIO);
1415b06d477bSHans Petter Selasky 
1416e13e19faSAndrew Thompson 	f = refs.rxfifo;
141702ac6454SAndrew Thompson 	if (f == NULL) {
141802ac6454SAndrew Thompson 		/* should not happen */
1419a593f6b8SAndrew Thompson 		usb_unref_device(cpd, &refs);
142002ac6454SAndrew Thompson 		return (EPERM);
142102ac6454SAndrew Thompson 	}
142202ac6454SAndrew Thompson 
142302ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
142402ac6454SAndrew Thompson 
142502ac6454SAndrew Thompson 	/* check for permanent read error */
142602ac6454SAndrew Thompson 	if (f->flag_iserror) {
142702ac6454SAndrew Thompson 		err = EIO;
142802ac6454SAndrew Thompson 		goto done;
142902ac6454SAndrew Thompson 	}
143002ac6454SAndrew Thompson 	/* check if USB-FS interface is active */
1431e13e19faSAndrew Thompson 	if (refs.is_usbfs) {
143202ac6454SAndrew Thompson 		/*
143302ac6454SAndrew Thompson 		 * The queue is used for events that should be
143402ac6454SAndrew Thompson 		 * retrieved using the "USB_FS_COMPLETE" ioctl.
143502ac6454SAndrew Thompson 		 */
143602ac6454SAndrew Thompson 		err = EINVAL;
143702ac6454SAndrew Thompson 		goto done;
143802ac6454SAndrew Thompson 	}
143902ac6454SAndrew Thompson 	while (uio->uio_resid > 0) {
144002ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
144102ac6454SAndrew Thompson 
144202ac6454SAndrew Thompson 		if (m == NULL) {
144302ac6454SAndrew Thompson 			/* start read transfer, if not already started */
144402ac6454SAndrew Thompson 
144502ac6454SAndrew Thompson 			(f->methods->f_start_read) (f);
144602ac6454SAndrew Thompson 
144707532e49SAndrew Thompson 			if (ioflag & IO_NDELAY) {
144802ac6454SAndrew Thompson 				if (tr_data) {
144902ac6454SAndrew Thompson 					/* return length before error */
145002ac6454SAndrew Thompson 					break;
145102ac6454SAndrew Thompson 				}
145202ac6454SAndrew Thompson 				err = EWOULDBLOCK;
145302ac6454SAndrew Thompson 				break;
145402ac6454SAndrew Thompson 			}
145502ac6454SAndrew Thompson 			DPRINTF("sleeping\n");
145602ac6454SAndrew Thompson 
1457a593f6b8SAndrew Thompson 			err = usb_fifo_wait(f);
145802ac6454SAndrew Thompson 			if (err) {
145902ac6454SAndrew Thompson 				break;
146002ac6454SAndrew Thompson 			}
146102ac6454SAndrew Thompson 			continue;
146202ac6454SAndrew Thompson 		}
146302ac6454SAndrew Thompson 		if (f->methods->f_filter_read) {
146402ac6454SAndrew Thompson 			/*
146502ac6454SAndrew Thompson 			 * Sometimes it is convenient to process data at the
146602ac6454SAndrew Thompson 			 * expense of a userland process instead of a kernel
146702ac6454SAndrew Thompson 			 * process.
146802ac6454SAndrew Thompson 			 */
146902ac6454SAndrew Thompson 			(f->methods->f_filter_read) (f, m);
147002ac6454SAndrew Thompson 		}
147102ac6454SAndrew Thompson 		tr_data = 1;
147202ac6454SAndrew Thompson 
147302ac6454SAndrew Thompson 		io_len = MIN(m->cur_data_len, uio->uio_resid);
147402ac6454SAndrew Thompson 
147502ac6454SAndrew Thompson 		DPRINTFN(2, "transfer %d bytes from %p\n",
147602ac6454SAndrew Thompson 		    io_len, m->cur_data_ptr);
147702ac6454SAndrew Thompson 
1478a593f6b8SAndrew Thompson 		err = usb_fifo_uiomove(f,
147902ac6454SAndrew Thompson 		    m->cur_data_ptr, io_len, uio);
148002ac6454SAndrew Thompson 
148102ac6454SAndrew Thompson 		m->cur_data_len -= io_len;
148202ac6454SAndrew Thompson 		m->cur_data_ptr += io_len;
148302ac6454SAndrew Thompson 
148402ac6454SAndrew Thompson 		if (m->cur_data_len == 0) {
148502ac6454SAndrew Thompson 			uint8_t last_packet;
148602ac6454SAndrew Thompson 
148702ac6454SAndrew Thompson 			last_packet = m->last_packet;
148802ac6454SAndrew Thompson 
148902ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->free_q, m);
149002ac6454SAndrew Thompson 
149102ac6454SAndrew Thompson 			if (last_packet) {
149202ac6454SAndrew Thompson 				/* keep framing */
149302ac6454SAndrew Thompson 				break;
149402ac6454SAndrew Thompson 			}
149502ac6454SAndrew Thompson 		} else {
149602ac6454SAndrew Thompson 			USB_IF_PREPEND(&f->used_q, m);
149702ac6454SAndrew Thompson 		}
149802ac6454SAndrew Thompson 
149902ac6454SAndrew Thompson 		if (err) {
150002ac6454SAndrew Thompson 			break;
150102ac6454SAndrew Thompson 		}
150202ac6454SAndrew Thompson 	}
150302ac6454SAndrew Thompson done:
150402ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
150502ac6454SAndrew Thompson 
1506a593f6b8SAndrew Thompson 	usb_unref_device(cpd, &refs);
150702ac6454SAndrew Thompson 
150802ac6454SAndrew Thompson 	return (err);
150902ac6454SAndrew Thompson }
151002ac6454SAndrew Thompson 
151102ac6454SAndrew Thompson static int
usb_write(struct cdev * dev,struct uio * uio,int ioflag)1512a593f6b8SAndrew Thompson usb_write(struct cdev *dev, struct uio *uio, int ioflag)
151302ac6454SAndrew Thompson {
1514e13e19faSAndrew Thompson 	struct usb_cdev_refdata refs;
1515760bc48eSAndrew Thompson 	struct usb_cdev_privdata* cpd;
1516760bc48eSAndrew Thompson 	struct usb_fifo *f;
1517760bc48eSAndrew Thompson 	struct usb_mbuf *m;
1518bd73b187SAlfred Perlstein 	uint8_t *pdata;
151902ac6454SAndrew Thompson 	int io_len;
152002ac6454SAndrew Thompson 	int err;
152102ac6454SAndrew Thompson 	uint8_t tr_data = 0;
152202ac6454SAndrew Thompson 
152302ac6454SAndrew Thompson 	DPRINTFN(2, "\n");
152402ac6454SAndrew Thompson 
1525ee3e3ff5SAndrew Thompson 	err = devfs_get_cdevpriv((void **)&cpd);
1526ee3e3ff5SAndrew Thompson 	if (err != 0)
1527ee3e3ff5SAndrew Thompson 		return (err);
152802ac6454SAndrew Thompson 
1529a593f6b8SAndrew Thompson 	err = usb_ref_device(cpd, &refs, 0 /* no uref */ );
1530b06d477bSHans Petter Selasky 	if (err)
153102ac6454SAndrew Thompson 		return (ENXIO);
1532b06d477bSHans Petter Selasky 
1533e13e19faSAndrew Thompson 	f = refs.txfifo;
153402ac6454SAndrew Thompson 	if (f == NULL) {
153502ac6454SAndrew Thompson 		/* should not happen */
1536a593f6b8SAndrew Thompson 		usb_unref_device(cpd, &refs);
153702ac6454SAndrew Thompson 		return (EPERM);
153802ac6454SAndrew Thompson 	}
153902ac6454SAndrew Thompson 
154002ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
154102ac6454SAndrew Thompson 
154202ac6454SAndrew Thompson 	/* check for permanent write error */
154302ac6454SAndrew Thompson 	if (f->flag_iserror) {
154402ac6454SAndrew Thompson 		err = EIO;
154502ac6454SAndrew Thompson 		goto done;
154602ac6454SAndrew Thompson 	}
154702ac6454SAndrew Thompson 	/* check if USB-FS interface is active */
1548e13e19faSAndrew Thompson 	if (refs.is_usbfs) {
154902ac6454SAndrew Thompson 		/*
155002ac6454SAndrew Thompson 		 * The queue is used for events that should be
155102ac6454SAndrew Thompson 		 * retrieved using the "USB_FS_COMPLETE" ioctl.
155202ac6454SAndrew Thompson 		 */
155302ac6454SAndrew Thompson 		err = EINVAL;
155402ac6454SAndrew Thompson 		goto done;
155502ac6454SAndrew Thompson 	}
155602ac6454SAndrew Thompson 	if (f->queue_data == NULL) {
155702ac6454SAndrew Thompson 		/* start write transfer, if not already started */
155802ac6454SAndrew Thompson 		(f->methods->f_start_write) (f);
155902ac6454SAndrew Thompson 	}
156002ac6454SAndrew Thompson 	/* we allow writing zero length data */
156102ac6454SAndrew Thompson 	do {
156202ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->free_q, m);
156302ac6454SAndrew Thompson 
156402ac6454SAndrew Thompson 		if (m == NULL) {
156507532e49SAndrew Thompson 			if (ioflag & IO_NDELAY) {
156602ac6454SAndrew Thompson 				if (tr_data) {
156702ac6454SAndrew Thompson 					/* return length before error */
156802ac6454SAndrew Thompson 					break;
156902ac6454SAndrew Thompson 				}
157002ac6454SAndrew Thompson 				err = EWOULDBLOCK;
157102ac6454SAndrew Thompson 				break;
157202ac6454SAndrew Thompson 			}
157302ac6454SAndrew Thompson 			DPRINTF("sleeping\n");
157402ac6454SAndrew Thompson 
1575a593f6b8SAndrew Thompson 			err = usb_fifo_wait(f);
157602ac6454SAndrew Thompson 			if (err) {
157702ac6454SAndrew Thompson 				break;
157802ac6454SAndrew Thompson 			}
157902ac6454SAndrew Thompson 			continue;
158002ac6454SAndrew Thompson 		}
158102ac6454SAndrew Thompson 		tr_data = 1;
158202ac6454SAndrew Thompson 
1583bd73b187SAlfred Perlstein 		if (f->flag_have_fragment == 0) {
158402ac6454SAndrew Thompson 			USB_MBUF_RESET(m);
1585bd73b187SAlfred Perlstein 			io_len = m->cur_data_len;
1586bd73b187SAlfred Perlstein 			pdata = m->cur_data_ptr;
1587bd73b187SAlfred Perlstein 			if (io_len > uio->uio_resid)
1588bd73b187SAlfred Perlstein 				io_len = uio->uio_resid;
158902ac6454SAndrew Thompson 			m->cur_data_len = io_len;
1590bd73b187SAlfred Perlstein 		} else {
1591bd73b187SAlfred Perlstein 			io_len = m->max_data_len - m->cur_data_len;
1592bd73b187SAlfred Perlstein 			pdata = m->cur_data_ptr + m->cur_data_len;
1593bd73b187SAlfred Perlstein 			if (io_len > uio->uio_resid)
1594bd73b187SAlfred Perlstein 				io_len = uio->uio_resid;
1595bd73b187SAlfred Perlstein 			m->cur_data_len += io_len;
1596bd73b187SAlfred Perlstein 		}
159702ac6454SAndrew Thompson 
159802ac6454SAndrew Thompson 		DPRINTFN(2, "transfer %d bytes to %p\n",
1599bd73b187SAlfred Perlstein 		    io_len, pdata);
160002ac6454SAndrew Thompson 
1601bd73b187SAlfred Perlstein 		err = usb_fifo_uiomove(f, pdata, io_len, uio);
160202ac6454SAndrew Thompson 
160302ac6454SAndrew Thompson 		if (err) {
1604bd73b187SAlfred Perlstein 			f->flag_have_fragment = 0;
160502ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->free_q, m);
160602ac6454SAndrew Thompson 			break;
160702ac6454SAndrew Thompson 		}
1608bd73b187SAlfred Perlstein 
1609bd73b187SAlfred Perlstein 		/* check if the buffer is ready to be transmitted */
1610bd73b187SAlfred Perlstein 
1611bd73b187SAlfred Perlstein 		if ((f->flag_write_defrag == 0) ||
1612bd73b187SAlfred Perlstein 		    (m->cur_data_len == m->max_data_len)) {
1613bd73b187SAlfred Perlstein 			f->flag_have_fragment = 0;
1614bd73b187SAlfred Perlstein 
161502ac6454SAndrew Thompson 			/*
1616bd73b187SAlfred Perlstein 			 * Check for write filter:
1617bd73b187SAlfred Perlstein 			 *
1618bd73b187SAlfred Perlstein 			 * Sometimes it is convenient to process data
1619bd73b187SAlfred Perlstein 			 * at the expense of a userland process
1620bd73b187SAlfred Perlstein 			 * instead of a kernel process.
162102ac6454SAndrew Thompson 			 */
1622bd73b187SAlfred Perlstein 			if (f->methods->f_filter_write) {
162302ac6454SAndrew Thompson 				(f->methods->f_filter_write) (f, m);
162402ac6454SAndrew Thompson 			}
1625bd73b187SAlfred Perlstein 
1626bd73b187SAlfred Perlstein 			/* Put USB mbuf in the used queue */
162702ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->used_q, m);
162802ac6454SAndrew Thompson 
1629bd73b187SAlfred Perlstein 			/* Start writing data, if not already started */
163002ac6454SAndrew Thompson 			(f->methods->f_start_write) (f);
1631bd73b187SAlfred Perlstein 		} else {
1632bd73b187SAlfred Perlstein 			/* Wait for more data or close */
1633bd73b187SAlfred Perlstein 			f->flag_have_fragment = 1;
1634bd73b187SAlfred Perlstein 			USB_IF_PREPEND(&f->free_q, m);
1635bd73b187SAlfred Perlstein 		}
163602ac6454SAndrew Thompson 
163702ac6454SAndrew Thompson 	} while (uio->uio_resid > 0);
163802ac6454SAndrew Thompson done:
163902ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
164002ac6454SAndrew Thompson 
1641a593f6b8SAndrew Thompson 	usb_unref_device(cpd, &refs);
164202ac6454SAndrew Thompson 
1643ee3e3ff5SAndrew Thompson 	return (err);
1644ee3e3ff5SAndrew Thompson }
164502ac6454SAndrew Thompson 
1646ee3e3ff5SAndrew Thompson int
usb_static_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)1647a593f6b8SAndrew Thompson usb_static_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
1648ee3e3ff5SAndrew Thompson     struct thread *td)
1649ee3e3ff5SAndrew Thompson {
1650ee3e3ff5SAndrew Thompson 	union {
1651760bc48eSAndrew Thompson 		struct usb_read_dir *urd;
165245b48cbcSBrooks Davis #ifdef COMPAT_FREEBSD32
165345b48cbcSBrooks Davis 		struct usb_read_dir32 *urd32;
165445b48cbcSBrooks Davis #endif
1655ee3e3ff5SAndrew Thompson 		void* data;
1656ee3e3ff5SAndrew Thompson 	} u;
1657ce4092bdSHans Petter Selasky 	int err;
1658ee3e3ff5SAndrew Thompson 
1659ee3e3ff5SAndrew Thompson 	u.data = data;
1660ee3e3ff5SAndrew Thompson 	switch (cmd) {
1661ee3e3ff5SAndrew Thompson 		case USB_READ_DIR:
1662a593f6b8SAndrew Thompson 			err = usb_read_symlink(u.urd->urd_data,
1663ee3e3ff5SAndrew Thompson 			    u.urd->urd_startentry, u.urd->urd_maxlen);
1664ee3e3ff5SAndrew Thompson 			break;
166545b48cbcSBrooks Davis #ifdef COMPAT_FREEBSD32
166645b48cbcSBrooks Davis 		case USB_READ_DIR32:
166745b48cbcSBrooks Davis 			err = usb_read_symlink(PTRIN(u.urd32->urd_data),
166845b48cbcSBrooks Davis 			    u.urd32->urd_startentry, u.urd32->urd_maxlen);
166945b48cbcSBrooks Davis 			break;
167045b48cbcSBrooks Davis #endif
1671ee3e3ff5SAndrew Thompson 		case USB_DEV_QUIRK_GET:
1672ee3e3ff5SAndrew Thompson 		case USB_QUIRK_NAME_GET:
1673ee3e3ff5SAndrew Thompson 		case USB_DEV_QUIRK_ADD:
1674ee3e3ff5SAndrew Thompson 		case USB_DEV_QUIRK_REMOVE:
1675a593f6b8SAndrew Thompson 			err = usb_quirk_ioctl_p(cmd, data, fflag, td);
1676ee3e3ff5SAndrew Thompson 			break;
1677ee3e3ff5SAndrew Thompson 		case USB_GET_TEMPLATE:
1678a593f6b8SAndrew Thompson 			*(int *)data = usb_template;
1679ce4092bdSHans Petter Selasky 			err = 0;
1680ee3e3ff5SAndrew Thompson 			break;
1681ee3e3ff5SAndrew Thompson 		case USB_SET_TEMPLATE:
168250230f98SAndrew Thompson 			err = priv_check(curthread, PRIV_DRIVER);
1683ee3e3ff5SAndrew Thompson 			if (err)
1684ee3e3ff5SAndrew Thompson 				break;
1685a593f6b8SAndrew Thompson 			usb_template = *(int *)data;
1686ee3e3ff5SAndrew Thompson 			break;
1687ce4092bdSHans Petter Selasky 		default:
1688ce4092bdSHans Petter Selasky 			err = ENOTTY;
1689ce4092bdSHans Petter Selasky 			break;
1690ee3e3ff5SAndrew Thompson 	}
169102ac6454SAndrew Thompson 	return (err);
169202ac6454SAndrew Thompson }
169302ac6454SAndrew Thompson 
169402ac6454SAndrew Thompson static int
usb_fifo_uiomove(struct usb_fifo * f,void * cp,int n,struct uio * uio)1695a593f6b8SAndrew Thompson usb_fifo_uiomove(struct usb_fifo *f, void *cp,
169602ac6454SAndrew Thompson     int n, struct uio *uio)
169702ac6454SAndrew Thompson {
169802ac6454SAndrew Thompson 	int error;
169902ac6454SAndrew Thompson 
170002ac6454SAndrew Thompson 	mtx_unlock(f->priv_mtx);
170102ac6454SAndrew Thompson 
170202ac6454SAndrew Thompson 	/*
170302ac6454SAndrew Thompson 	 * "uiomove()" can sleep so one needs to make a wrapper,
170402ac6454SAndrew Thompson 	 * exiting the mutex and checking things:
170502ac6454SAndrew Thompson 	 */
170602ac6454SAndrew Thompson 	error = uiomove(cp, n, uio);
170702ac6454SAndrew Thompson 
170802ac6454SAndrew Thompson 	mtx_lock(f->priv_mtx);
170902ac6454SAndrew Thompson 
171002ac6454SAndrew Thompson 	return (error);
171102ac6454SAndrew Thompson }
171202ac6454SAndrew Thompson 
171302ac6454SAndrew Thompson int
usb_fifo_wait(struct usb_fifo * f)1714a593f6b8SAndrew Thompson usb_fifo_wait(struct usb_fifo *f)
171502ac6454SAndrew Thompson {
171602ac6454SAndrew Thompson 	int err;
171702ac6454SAndrew Thompson 
17180eb8d462SHans Petter Selasky 	USB_MTX_ASSERT(f->priv_mtx, MA_OWNED);
171902ac6454SAndrew Thompson 
172002ac6454SAndrew Thompson 	if (f->flag_iserror) {
172102ac6454SAndrew Thompson 		/* we are gone */
172202ac6454SAndrew Thompson 		return (EIO);
172302ac6454SAndrew Thompson 	}
172402ac6454SAndrew Thompson 	f->flag_sleeping = 1;
172502ac6454SAndrew Thompson 
17268437751dSAndrew Thompson 	err = cv_wait_sig(&f->cv_io, f->priv_mtx);
172702ac6454SAndrew Thompson 
172802ac6454SAndrew Thompson 	if (f->flag_iserror) {
172902ac6454SAndrew Thompson 		/* we are gone */
173002ac6454SAndrew Thompson 		err = EIO;
173102ac6454SAndrew Thompson 	}
173202ac6454SAndrew Thompson 	return (err);
173302ac6454SAndrew Thompson }
173402ac6454SAndrew Thompson 
173502ac6454SAndrew Thompson void
usb_fifo_signal(struct usb_fifo * f)1736a593f6b8SAndrew Thompson usb_fifo_signal(struct usb_fifo *f)
173702ac6454SAndrew Thompson {
173802ac6454SAndrew Thompson 	if (f->flag_sleeping) {
173902ac6454SAndrew Thompson 		f->flag_sleeping = 0;
17408437751dSAndrew Thompson 		cv_broadcast(&f->cv_io);
174102ac6454SAndrew Thompson 	}
174202ac6454SAndrew Thompson }
174302ac6454SAndrew Thompson 
174402ac6454SAndrew Thompson void
usb_fifo_wakeup(struct usb_fifo * f)1745a593f6b8SAndrew Thompson usb_fifo_wakeup(struct usb_fifo *f)
174602ac6454SAndrew Thompson {
1747a593f6b8SAndrew Thompson 	usb_fifo_signal(f);
174802ac6454SAndrew Thompson 
17490b6d54d4SHans Petter Selasky 	KNOTE_LOCKED(&f->selinfo.si_note, 0);
17500b6d54d4SHans Petter Selasky 
175102ac6454SAndrew Thompson 	if (f->flag_isselect) {
175202ac6454SAndrew Thompson 		selwakeup(&f->selinfo);
175302ac6454SAndrew Thompson 		f->flag_isselect = 0;
175402ac6454SAndrew Thompson 	}
175502ac6454SAndrew Thompson 	if (f->async_p != NULL) {
175602ac6454SAndrew Thompson 		PROC_LOCK(f->async_p);
17578451d0ddSKip Macy 		kern_psignal(f->async_p, SIGIO);
175802ac6454SAndrew Thompson 		PROC_UNLOCK(f->async_p);
175902ac6454SAndrew Thompson 	}
176002ac6454SAndrew Thompson }
176102ac6454SAndrew Thompson 
176202ac6454SAndrew Thompson static int
usb_fifo_dummy_open(struct usb_fifo * fifo,int fflags)1763a593f6b8SAndrew Thompson usb_fifo_dummy_open(struct usb_fifo *fifo, int fflags)
176402ac6454SAndrew Thompson {
176502ac6454SAndrew Thompson 	return (0);
176602ac6454SAndrew Thompson }
176702ac6454SAndrew Thompson 
176802ac6454SAndrew Thompson static void
usb_fifo_dummy_close(struct usb_fifo * fifo,int fflags)1769a593f6b8SAndrew Thompson usb_fifo_dummy_close(struct usb_fifo *fifo, int fflags)
177002ac6454SAndrew Thompson {
177102ac6454SAndrew Thompson 	return;
177202ac6454SAndrew Thompson }
177302ac6454SAndrew Thompson 
177402ac6454SAndrew Thompson static int
usb_fifo_dummy_ioctl(struct usb_fifo * fifo,u_long cmd,void * addr,int fflags)1775a593f6b8SAndrew Thompson usb_fifo_dummy_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
177602ac6454SAndrew Thompson {
177702ac6454SAndrew Thompson 	return (ENOIOCTL);
177802ac6454SAndrew Thompson }
177902ac6454SAndrew Thompson 
178002ac6454SAndrew Thompson static void
usb_fifo_dummy_cmd(struct usb_fifo * fifo)1781a593f6b8SAndrew Thompson usb_fifo_dummy_cmd(struct usb_fifo *fifo)
178202ac6454SAndrew Thompson {
178302ac6454SAndrew Thompson 	fifo->flag_flushing = 0;	/* not flushing */
178402ac6454SAndrew Thompson }
178502ac6454SAndrew Thompson 
178602ac6454SAndrew Thompson static void
usb_fifo_check_methods(struct usb_fifo_methods * pm)1787a593f6b8SAndrew Thompson usb_fifo_check_methods(struct usb_fifo_methods *pm)
178802ac6454SAndrew Thompson {
178902ac6454SAndrew Thompson 	/* check that all callback functions are OK */
179002ac6454SAndrew Thompson 
179102ac6454SAndrew Thompson 	if (pm->f_open == NULL)
1792a593f6b8SAndrew Thompson 		pm->f_open = &usb_fifo_dummy_open;
179302ac6454SAndrew Thompson 
179402ac6454SAndrew Thompson 	if (pm->f_close == NULL)
1795a593f6b8SAndrew Thompson 		pm->f_close = &usb_fifo_dummy_close;
179602ac6454SAndrew Thompson 
179702ac6454SAndrew Thompson 	if (pm->f_ioctl == NULL)
1798a593f6b8SAndrew Thompson 		pm->f_ioctl = &usb_fifo_dummy_ioctl;
179902ac6454SAndrew Thompson 
180002ac6454SAndrew Thompson 	if (pm->f_ioctl_post == NULL)
1801a593f6b8SAndrew Thompson 		pm->f_ioctl_post = &usb_fifo_dummy_ioctl;
180202ac6454SAndrew Thompson 
180302ac6454SAndrew Thompson 	if (pm->f_start_read == NULL)
1804a593f6b8SAndrew Thompson 		pm->f_start_read = &usb_fifo_dummy_cmd;
180502ac6454SAndrew Thompson 
180602ac6454SAndrew Thompson 	if (pm->f_stop_read == NULL)
1807a593f6b8SAndrew Thompson 		pm->f_stop_read = &usb_fifo_dummy_cmd;
180802ac6454SAndrew Thompson 
180902ac6454SAndrew Thompson 	if (pm->f_start_write == NULL)
1810a593f6b8SAndrew Thompson 		pm->f_start_write = &usb_fifo_dummy_cmd;
181102ac6454SAndrew Thompson 
181202ac6454SAndrew Thompson 	if (pm->f_stop_write == NULL)
1813a593f6b8SAndrew Thompson 		pm->f_stop_write = &usb_fifo_dummy_cmd;
181402ac6454SAndrew Thompson }
181502ac6454SAndrew Thompson 
181602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1817a593f6b8SAndrew Thompson  *	usb_fifo_attach
181802ac6454SAndrew Thompson  *
181902ac6454SAndrew Thompson  * The following function will create a duplex FIFO.
182002ac6454SAndrew Thompson  *
182102ac6454SAndrew Thompson  * Return values:
182202ac6454SAndrew Thompson  * 0: Success.
182302ac6454SAndrew Thompson  * Else: Failure.
182402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
182502ac6454SAndrew Thompson int
usb_fifo_attach(struct usb_device * udev,void * priv_sc,struct mtx * priv_mtx,struct usb_fifo_methods * pm,struct usb_fifo_sc * f_sc,uint16_t unit,int16_t subunit,uint8_t iface_index,uid_t uid,gid_t gid,int mode)1826a593f6b8SAndrew Thompson usb_fifo_attach(struct usb_device *udev, void *priv_sc,
1827760bc48eSAndrew Thompson     struct mtx *priv_mtx, struct usb_fifo_methods *pm,
18286d917491SHans Petter Selasky     struct usb_fifo_sc *f_sc, uint16_t unit, int16_t subunit,
1829ee3e3ff5SAndrew Thompson     uint8_t iface_index, uid_t uid, gid_t gid, int mode)
183002ac6454SAndrew Thompson {
1831760bc48eSAndrew Thompson 	struct usb_fifo *f_tx;
1832760bc48eSAndrew Thompson 	struct usb_fifo *f_rx;
1833ee3e3ff5SAndrew Thompson 	char devname[32];
183402ac6454SAndrew Thompson 	uint8_t n;
183502ac6454SAndrew Thompson 
183602ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_TX] = NULL;
183702ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_RX] = NULL;
183802ac6454SAndrew Thompson 
183902ac6454SAndrew Thompson 	if (pm == NULL)
184002ac6454SAndrew Thompson 		return (EINVAL);
184102ac6454SAndrew Thompson 
184202ac6454SAndrew Thompson 	/* check the methods */
1843a593f6b8SAndrew Thompson 	usb_fifo_check_methods(pm);
184402ac6454SAndrew Thompson 
184502ac6454SAndrew Thompson 	if (priv_mtx == NULL)
184602ac6454SAndrew Thompson 		priv_mtx = &Giant;
184702ac6454SAndrew Thompson 
184802ac6454SAndrew Thompson 	/* search for a free FIFO slot */
184902ac6454SAndrew Thompson 	for (n = 0;; n += 2) {
185002ac6454SAndrew Thompson 		if (n == USB_FIFO_MAX) {
185102ac6454SAndrew Thompson 			/* end of FIFOs reached */
185202ac6454SAndrew Thompson 			return (ENOMEM);
185302ac6454SAndrew Thompson 		}
185402ac6454SAndrew Thompson 		/* Check for TX FIFO */
185502ac6454SAndrew Thompson 		if (udev->fifo[n + USB_FIFO_TX] != NULL) {
185602ac6454SAndrew Thompson 			continue;
185702ac6454SAndrew Thompson 		}
185802ac6454SAndrew Thompson 		/* Check for RX FIFO */
185902ac6454SAndrew Thompson 		if (udev->fifo[n + USB_FIFO_RX] != NULL) {
186002ac6454SAndrew Thompson 			continue;
186102ac6454SAndrew Thompson 		}
186202ac6454SAndrew Thompson 		break;
186302ac6454SAndrew Thompson 	}
186402ac6454SAndrew Thompson 
1865e2723934SHans Petter Selasky 	f_tx = usb_fifo_alloc(priv_mtx);
1866e2723934SHans Petter Selasky 	f_rx = usb_fifo_alloc(priv_mtx);
186702ac6454SAndrew Thompson 
186802ac6454SAndrew Thompson 	if ((f_tx == NULL) || (f_rx == NULL)) {
1869a593f6b8SAndrew Thompson 		usb_fifo_free(f_tx);
1870a593f6b8SAndrew Thompson 		usb_fifo_free(f_rx);
187102ac6454SAndrew Thompson 		return (ENOMEM);
187202ac6454SAndrew Thompson 	}
187302ac6454SAndrew Thompson 	/* initialise FIFO structures */
187402ac6454SAndrew Thompson 
187502ac6454SAndrew Thompson 	f_tx->fifo_index = n + USB_FIFO_TX;
18762989a677SAndrew Thompson 	f_tx->dev_ep_index = -1;
187702ac6454SAndrew Thompson 	f_tx->priv_sc0 = priv_sc;
187802ac6454SAndrew Thompson 	f_tx->methods = pm;
187902ac6454SAndrew Thompson 	f_tx->iface_index = iface_index;
188002ac6454SAndrew Thompson 	f_tx->udev = udev;
188102ac6454SAndrew Thompson 
188202ac6454SAndrew Thompson 	f_rx->fifo_index = n + USB_FIFO_RX;
18832989a677SAndrew Thompson 	f_rx->dev_ep_index = -1;
188402ac6454SAndrew Thompson 	f_rx->priv_sc0 = priv_sc;
188502ac6454SAndrew Thompson 	f_rx->methods = pm;
188602ac6454SAndrew Thompson 	f_rx->iface_index = iface_index;
188702ac6454SAndrew Thompson 	f_rx->udev = udev;
188802ac6454SAndrew Thompson 
188902ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_TX] = f_tx;
189002ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_RX] = f_rx;
189102ac6454SAndrew Thompson 
1892a593f6b8SAndrew Thompson 	mtx_lock(&usb_ref_lock);
189302ac6454SAndrew Thompson 	udev->fifo[f_tx->fifo_index] = f_tx;
189402ac6454SAndrew Thompson 	udev->fifo[f_rx->fifo_index] = f_rx;
1895a593f6b8SAndrew Thompson 	mtx_unlock(&usb_ref_lock);
189602ac6454SAndrew Thompson 
189702ac6454SAndrew Thompson 	for (n = 0; n != 4; n++) {
189802ac6454SAndrew Thompson 		if (pm->basename[n] == NULL) {
189902ac6454SAndrew Thompson 			continue;
190002ac6454SAndrew Thompson 		}
19016d917491SHans Petter Selasky 		if (subunit < 0) {
1902ee3e3ff5SAndrew Thompson 			if (snprintf(devname, sizeof(devname),
190302ac6454SAndrew Thompson 			    "%s%u%s", pm->basename[n],
190402ac6454SAndrew Thompson 			    unit, pm->postfix[n] ?
190502ac6454SAndrew Thompson 			    pm->postfix[n] : "")) {
190602ac6454SAndrew Thompson 				/* ignore */
190702ac6454SAndrew Thompson 			}
190802ac6454SAndrew Thompson 		} else {
1909ee3e3ff5SAndrew Thompson 			if (snprintf(devname, sizeof(devname),
19106d917491SHans Petter Selasky 			    "%s%u.%d%s", pm->basename[n],
191102ac6454SAndrew Thompson 			    unit, subunit, pm->postfix[n] ?
191202ac6454SAndrew Thompson 			    pm->postfix[n] : "")) {
191302ac6454SAndrew Thompson 				/* ignore */
191402ac6454SAndrew Thompson 			}
191502ac6454SAndrew Thompson 		}
191602ac6454SAndrew Thompson 
191702ac6454SAndrew Thompson 		/*
191802ac6454SAndrew Thompson 		 * Distribute the symbolic links into two FIFO structures:
191902ac6454SAndrew Thompson 		 */
192002ac6454SAndrew Thompson 		if (n & 1) {
192102ac6454SAndrew Thompson 			f_rx->symlink[n / 2] =
1922a593f6b8SAndrew Thompson 			    usb_alloc_symlink(devname);
192302ac6454SAndrew Thompson 		} else {
192402ac6454SAndrew Thompson 			f_tx->symlink[n / 2] =
1925a593f6b8SAndrew Thompson 			    usb_alloc_symlink(devname);
192602ac6454SAndrew Thompson 		}
1927ee3e3ff5SAndrew Thompson 
19282ffd5fdcSHans Petter Selasky 		/* Create the device */
19292ffd5fdcSHans Petter Selasky 		f_sc->dev = usb_make_dev(udev, devname, -1,
19302ffd5fdcSHans Petter Selasky 		    f_tx->fifo_index & f_rx->fifo_index,
19312ffd5fdcSHans Petter Selasky 		    FREAD|FWRITE, uid, gid, mode);
193202ac6454SAndrew Thompson 	}
193302ac6454SAndrew Thompson 
193402ac6454SAndrew Thompson 	DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx);
193502ac6454SAndrew Thompson 	return (0);
193602ac6454SAndrew Thompson }
193702ac6454SAndrew Thompson 
193802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1939a593f6b8SAndrew Thompson  *	usb_fifo_alloc_buffer
194002ac6454SAndrew Thompson  *
194102ac6454SAndrew Thompson  * Return values:
194202ac6454SAndrew Thompson  * 0: Success
194302ac6454SAndrew Thompson  * Else failure
194402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
194502ac6454SAndrew Thompson int
usb_fifo_alloc_buffer(struct usb_fifo * f,usb_size_t bufsize,uint16_t nbuf)1946a593f6b8SAndrew Thompson usb_fifo_alloc_buffer(struct usb_fifo *f, usb_size_t bufsize,
194702ac6454SAndrew Thompson     uint16_t nbuf)
194802ac6454SAndrew Thompson {
1949aad0c65dSHans Petter Selasky 	struct usb_ifqueue temp_q = {};
1950aad0c65dSHans Petter Selasky 	void *queue_data;
1951aad0c65dSHans Petter Selasky 
1952a593f6b8SAndrew Thompson 	usb_fifo_free_buffer(f);
195302ac6454SAndrew Thompson 
1954aad0c65dSHans Petter Selasky 	temp_q.ifq_maxlen = nbuf;
195502ac6454SAndrew Thompson 
1956aad0c65dSHans Petter Selasky 	queue_data = usb_alloc_mbufs(
1957aad0c65dSHans Petter Selasky 	    M_USBDEV, &temp_q, bufsize, nbuf);
195802ac6454SAndrew Thompson 
1959aad0c65dSHans Petter Selasky 	if (queue_data == NULL && bufsize != 0 && nbuf != 0)
196002ac6454SAndrew Thompson 		return (ENOMEM);
1961aad0c65dSHans Petter Selasky 
1962aad0c65dSHans Petter Selasky 	mtx_lock(f->priv_mtx);
1963aad0c65dSHans Petter Selasky 
1964aad0c65dSHans Petter Selasky 	/*
1965aad0c65dSHans Petter Selasky 	 * Setup queues and sizes under lock to avoid early use by
1966aad0c65dSHans Petter Selasky 	 * concurrent FIFO access:
1967aad0c65dSHans Petter Selasky 	 */
1968aad0c65dSHans Petter Selasky 	f->free_q = temp_q;
1969aad0c65dSHans Petter Selasky 	f->used_q.ifq_maxlen = nbuf;
1970aad0c65dSHans Petter Selasky 	f->queue_data = queue_data;
1971aad0c65dSHans Petter Selasky 	mtx_unlock(f->priv_mtx);
1972aad0c65dSHans Petter Selasky 
197302ac6454SAndrew Thompson 	return (0);			/* success */
197402ac6454SAndrew Thompson }
197502ac6454SAndrew Thompson 
197602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1977a593f6b8SAndrew Thompson  *	usb_fifo_free_buffer
197802ac6454SAndrew Thompson  *
197902ac6454SAndrew Thompson  * This function will free the buffers associated with a FIFO. This
198002ac6454SAndrew Thompson  * function can be called multiple times in a row.
198102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
198202ac6454SAndrew Thompson void
usb_fifo_free_buffer(struct usb_fifo * f)1983a593f6b8SAndrew Thompson usb_fifo_free_buffer(struct usb_fifo *f)
198402ac6454SAndrew Thompson {
1985aad0c65dSHans Petter Selasky 	void *queue_data;
198602ac6454SAndrew Thompson 
1987aad0c65dSHans Petter Selasky 	mtx_lock(f->priv_mtx);
1988aad0c65dSHans Petter Selasky 
1989aad0c65dSHans Petter Selasky 	/* Get and clear pointer to free, if any. */
1990aad0c65dSHans Petter Selasky 	queue_data = f->queue_data;
1991aad0c65dSHans Petter Selasky 	f->queue_data = NULL;
1992aad0c65dSHans Petter Selasky 
1993aad0c65dSHans Petter Selasky 	/*
1994aad0c65dSHans Petter Selasky 	 * Reset queues under lock to avoid use of freed buffers by
1995aad0c65dSHans Petter Selasky 	 * concurrent FIFO activity:
1996aad0c65dSHans Petter Selasky 	 */
1997271ae033SHans Petter Selasky 	memset(&f->free_q, 0, sizeof(f->free_q));
1998271ae033SHans Petter Selasky 	memset(&f->used_q, 0, sizeof(f->used_q));
1999aad0c65dSHans Petter Selasky 	mtx_unlock(f->priv_mtx);
2000aad0c65dSHans Petter Selasky 
2001aad0c65dSHans Petter Selasky 	/* Free old buffer, if any. */
2002aad0c65dSHans Petter Selasky 	free(queue_data, M_USBDEV);
200302ac6454SAndrew Thompson }
200402ac6454SAndrew Thompson 
200502ac6454SAndrew Thompson void
usb_fifo_detach(struct usb_fifo_sc * f_sc)2006a593f6b8SAndrew Thompson usb_fifo_detach(struct usb_fifo_sc *f_sc)
200702ac6454SAndrew Thompson {
200802ac6454SAndrew Thompson 	if (f_sc == NULL) {
200902ac6454SAndrew Thompson 		return;
201002ac6454SAndrew Thompson 	}
2011a593f6b8SAndrew Thompson 	usb_fifo_free(f_sc->fp[USB_FIFO_TX]);
2012a593f6b8SAndrew Thompson 	usb_fifo_free(f_sc->fp[USB_FIFO_RX]);
201302ac6454SAndrew Thompson 
201402ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_TX] = NULL;
201502ac6454SAndrew Thompson 	f_sc->fp[USB_FIFO_RX] = NULL;
201602ac6454SAndrew Thompson 
20172ffd5fdcSHans Petter Selasky 	usb_destroy_dev(f_sc->dev);
20182ffd5fdcSHans Petter Selasky 
20197214348fSAndrew Thompson 	f_sc->dev = NULL;
2020ee3e3ff5SAndrew Thompson 
202102ac6454SAndrew Thompson 	DPRINTFN(2, "detached %p\n", f_sc);
202202ac6454SAndrew Thompson }
202302ac6454SAndrew Thompson 
2024f9cb546cSAndrew Thompson usb_size_t
usb_fifo_put_bytes_max(struct usb_fifo * f)2025a593f6b8SAndrew Thompson usb_fifo_put_bytes_max(struct usb_fifo *f)
202602ac6454SAndrew Thompson {
2027760bc48eSAndrew Thompson 	struct usb_mbuf *m;
2028f9cb546cSAndrew Thompson 	usb_size_t len;
202902ac6454SAndrew Thompson 
203002ac6454SAndrew Thompson 	USB_IF_POLL(&f->free_q, m);
203102ac6454SAndrew Thompson 
203202ac6454SAndrew Thompson 	if (m) {
203302ac6454SAndrew Thompson 		len = m->max_data_len;
203402ac6454SAndrew Thompson 	} else {
203502ac6454SAndrew Thompson 		len = 0;
203602ac6454SAndrew Thompson 	}
203702ac6454SAndrew Thompson 	return (len);
203802ac6454SAndrew Thompson }
203902ac6454SAndrew Thompson 
204002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
2041a593f6b8SAndrew Thompson  *	usb_fifo_put_data
204202ac6454SAndrew Thompson  *
204302ac6454SAndrew Thompson  * what:
204402ac6454SAndrew Thompson  *  0 - normal operation
204502ac6454SAndrew Thompson  *  1 - set last packet flag to enforce framing
204602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
204702ac6454SAndrew Thompson void
usb_fifo_put_data(struct usb_fifo * f,struct usb_page_cache * pc,usb_frlength_t offset,usb_frlength_t len,uint8_t what)2048a593f6b8SAndrew Thompson usb_fifo_put_data(struct usb_fifo *f, struct usb_page_cache *pc,
2049e0a69b51SAndrew Thompson     usb_frlength_t offset, usb_frlength_t len, uint8_t what)
205002ac6454SAndrew Thompson {
2051760bc48eSAndrew Thompson 	struct usb_mbuf *m;
2052e0a69b51SAndrew Thompson 	usb_frlength_t io_len;
205302ac6454SAndrew Thompson 
205402ac6454SAndrew Thompson 	while (len || (what == 1)) {
205502ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->free_q, m);
205602ac6454SAndrew Thompson 
205702ac6454SAndrew Thompson 		if (m) {
205802ac6454SAndrew Thompson 			USB_MBUF_RESET(m);
205902ac6454SAndrew Thompson 
206002ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
206102ac6454SAndrew Thompson 
2062a593f6b8SAndrew Thompson 			usbd_copy_out(pc, offset, m->cur_data_ptr, io_len);
206302ac6454SAndrew Thompson 
206402ac6454SAndrew Thompson 			m->cur_data_len = io_len;
206502ac6454SAndrew Thompson 			offset += io_len;
206602ac6454SAndrew Thompson 			len -= io_len;
206702ac6454SAndrew Thompson 
206802ac6454SAndrew Thompson 			if ((len == 0) && (what == 1)) {
206902ac6454SAndrew Thompson 				m->last_packet = 1;
207002ac6454SAndrew Thompson 			}
207102ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->used_q, m);
207202ac6454SAndrew Thompson 
2073a593f6b8SAndrew Thompson 			usb_fifo_wakeup(f);
207402ac6454SAndrew Thompson 
207502ac6454SAndrew Thompson 			if ((len == 0) || (what == 1)) {
207602ac6454SAndrew Thompson 				break;
207702ac6454SAndrew Thompson 			}
207802ac6454SAndrew Thompson 		} else {
207902ac6454SAndrew Thompson 			break;
208002ac6454SAndrew Thompson 		}
208102ac6454SAndrew Thompson 	}
208202ac6454SAndrew Thompson }
208302ac6454SAndrew Thompson 
208402ac6454SAndrew Thompson void
usb_fifo_put_data_linear(struct usb_fifo * f,void * ptr,usb_size_t len,uint8_t what)2085a593f6b8SAndrew Thompson usb_fifo_put_data_linear(struct usb_fifo *f, void *ptr,
2086f9cb546cSAndrew Thompson     usb_size_t len, uint8_t what)
208702ac6454SAndrew Thompson {
2088760bc48eSAndrew Thompson 	struct usb_mbuf *m;
2089f9cb546cSAndrew Thompson 	usb_size_t io_len;
209002ac6454SAndrew Thompson 
209102ac6454SAndrew Thompson 	while (len || (what == 1)) {
209202ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->free_q, m);
209302ac6454SAndrew Thompson 
209402ac6454SAndrew Thompson 		if (m) {
209502ac6454SAndrew Thompson 			USB_MBUF_RESET(m);
209602ac6454SAndrew Thompson 
209702ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
209802ac6454SAndrew Thompson 
2099271ae033SHans Petter Selasky 			memcpy(m->cur_data_ptr, ptr, io_len);
210002ac6454SAndrew Thompson 
210102ac6454SAndrew Thompson 			m->cur_data_len = io_len;
210202ac6454SAndrew Thompson 			ptr = USB_ADD_BYTES(ptr, io_len);
210302ac6454SAndrew Thompson 			len -= io_len;
210402ac6454SAndrew Thompson 
210502ac6454SAndrew Thompson 			if ((len == 0) && (what == 1)) {
210602ac6454SAndrew Thompson 				m->last_packet = 1;
210702ac6454SAndrew Thompson 			}
210802ac6454SAndrew Thompson 			USB_IF_ENQUEUE(&f->used_q, m);
210902ac6454SAndrew Thompson 
2110a593f6b8SAndrew Thompson 			usb_fifo_wakeup(f);
211102ac6454SAndrew Thompson 
211202ac6454SAndrew Thompson 			if ((len == 0) || (what == 1)) {
211302ac6454SAndrew Thompson 				break;
211402ac6454SAndrew Thompson 			}
211502ac6454SAndrew Thompson 		} else {
211602ac6454SAndrew Thompson 			break;
211702ac6454SAndrew Thompson 		}
211802ac6454SAndrew Thompson 	}
211902ac6454SAndrew Thompson }
212002ac6454SAndrew Thompson 
212102ac6454SAndrew Thompson uint8_t
usb_fifo_put_data_buffer(struct usb_fifo * f,void * ptr,usb_size_t len)2122a593f6b8SAndrew Thompson usb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len)
212302ac6454SAndrew Thompson {
2124760bc48eSAndrew Thompson 	struct usb_mbuf *m;
212502ac6454SAndrew Thompson 
212602ac6454SAndrew Thompson 	USB_IF_DEQUEUE(&f->free_q, m);
212702ac6454SAndrew Thompson 
212802ac6454SAndrew Thompson 	if (m) {
212902ac6454SAndrew Thompson 		m->cur_data_len = len;
213002ac6454SAndrew Thompson 		m->cur_data_ptr = ptr;
213102ac6454SAndrew Thompson 		USB_IF_ENQUEUE(&f->used_q, m);
2132a593f6b8SAndrew Thompson 		usb_fifo_wakeup(f);
213302ac6454SAndrew Thompson 		return (1);
213402ac6454SAndrew Thompson 	}
213502ac6454SAndrew Thompson 	return (0);
213602ac6454SAndrew Thompson }
213702ac6454SAndrew Thompson 
213802ac6454SAndrew Thompson void
usb_fifo_put_data_error(struct usb_fifo * f)2139a593f6b8SAndrew Thompson usb_fifo_put_data_error(struct usb_fifo *f)
214002ac6454SAndrew Thompson {
214102ac6454SAndrew Thompson 	f->flag_iserror = 1;
2142a593f6b8SAndrew Thompson 	usb_fifo_wakeup(f);
214302ac6454SAndrew Thompson }
214402ac6454SAndrew Thompson 
214502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
2146a593f6b8SAndrew Thompson  *	usb_fifo_get_data
214702ac6454SAndrew Thompson  *
214802ac6454SAndrew Thompson  * what:
214902ac6454SAndrew Thompson  *  0 - normal operation
2150760bc48eSAndrew Thompson  *  1 - only get one "usb_mbuf"
215102ac6454SAndrew Thompson  *
215202ac6454SAndrew Thompson  * returns:
215302ac6454SAndrew Thompson  *  0 - no more data
215402ac6454SAndrew Thompson  *  1 - data in buffer
215502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
215602ac6454SAndrew Thompson uint8_t
usb_fifo_get_data(struct usb_fifo * f,struct usb_page_cache * pc,usb_frlength_t offset,usb_frlength_t len,usb_frlength_t * actlen,uint8_t what)2157a593f6b8SAndrew Thompson usb_fifo_get_data(struct usb_fifo *f, struct usb_page_cache *pc,
2158e0a69b51SAndrew Thompson     usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen,
215902ac6454SAndrew Thompson     uint8_t what)
216002ac6454SAndrew Thompson {
2161760bc48eSAndrew Thompson 	struct usb_mbuf *m;
2162e0a69b51SAndrew Thompson 	usb_frlength_t io_len;
216302ac6454SAndrew Thompson 	uint8_t tr_data = 0;
216402ac6454SAndrew Thompson 
216502ac6454SAndrew Thompson 	actlen[0] = 0;
216602ac6454SAndrew Thompson 
216702ac6454SAndrew Thompson 	while (1) {
216802ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
216902ac6454SAndrew Thompson 
217002ac6454SAndrew Thompson 		if (m) {
217102ac6454SAndrew Thompson 			tr_data = 1;
217202ac6454SAndrew Thompson 
217302ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
217402ac6454SAndrew Thompson 
2175a593f6b8SAndrew Thompson 			usbd_copy_in(pc, offset, m->cur_data_ptr, io_len);
217602ac6454SAndrew Thompson 
217702ac6454SAndrew Thompson 			len -= io_len;
217802ac6454SAndrew Thompson 			offset += io_len;
217902ac6454SAndrew Thompson 			actlen[0] += io_len;
218002ac6454SAndrew Thompson 			m->cur_data_ptr += io_len;
218102ac6454SAndrew Thompson 			m->cur_data_len -= io_len;
218202ac6454SAndrew Thompson 
218302ac6454SAndrew Thompson 			if ((m->cur_data_len == 0) || (what == 1)) {
218402ac6454SAndrew Thompson 				USB_IF_ENQUEUE(&f->free_q, m);
218502ac6454SAndrew Thompson 
2186a593f6b8SAndrew Thompson 				usb_fifo_wakeup(f);
218702ac6454SAndrew Thompson 
218802ac6454SAndrew Thompson 				if (what == 1) {
218902ac6454SAndrew Thompson 					break;
219002ac6454SAndrew Thompson 				}
219102ac6454SAndrew Thompson 			} else {
219202ac6454SAndrew Thompson 				USB_IF_PREPEND(&f->used_q, m);
219302ac6454SAndrew Thompson 			}
219402ac6454SAndrew Thompson 		} else {
219502ac6454SAndrew Thompson 			if (tr_data) {
219602ac6454SAndrew Thompson 				/* wait for data to be written out */
219702ac6454SAndrew Thompson 				break;
219802ac6454SAndrew Thompson 			}
219902ac6454SAndrew Thompson 			if (f->flag_flushing) {
22007214348fSAndrew Thompson 				/* check if we should send a short packet */
22017214348fSAndrew Thompson 				if (f->flag_short != 0) {
22027214348fSAndrew Thompson 					f->flag_short = 0;
22037214348fSAndrew Thompson 					tr_data = 1;
22047214348fSAndrew Thompson 					break;
22057214348fSAndrew Thompson 				}
22067214348fSAndrew Thompson 				/* flushing complete */
220702ac6454SAndrew Thompson 				f->flag_flushing = 0;
2208a593f6b8SAndrew Thompson 				usb_fifo_wakeup(f);
220902ac6454SAndrew Thompson 			}
221002ac6454SAndrew Thompson 			break;
221102ac6454SAndrew Thompson 		}
221202ac6454SAndrew Thompson 		if (len == 0) {
221302ac6454SAndrew Thompson 			break;
221402ac6454SAndrew Thompson 		}
221502ac6454SAndrew Thompson 	}
221602ac6454SAndrew Thompson 	return (tr_data);
221702ac6454SAndrew Thompson }
221802ac6454SAndrew Thompson 
221902ac6454SAndrew Thompson uint8_t
usb_fifo_get_data_linear(struct usb_fifo * f,void * ptr,usb_size_t len,usb_size_t * actlen,uint8_t what)2220a593f6b8SAndrew Thompson usb_fifo_get_data_linear(struct usb_fifo *f, void *ptr,
2221f9cb546cSAndrew Thompson     usb_size_t len, usb_size_t *actlen, uint8_t what)
222202ac6454SAndrew Thompson {
2223760bc48eSAndrew Thompson 	struct usb_mbuf *m;
2224f9cb546cSAndrew Thompson 	usb_size_t io_len;
222502ac6454SAndrew Thompson 	uint8_t tr_data = 0;
222602ac6454SAndrew Thompson 
222702ac6454SAndrew Thompson 	actlen[0] = 0;
222802ac6454SAndrew Thompson 
222902ac6454SAndrew Thompson 	while (1) {
223002ac6454SAndrew Thompson 		USB_IF_DEQUEUE(&f->used_q, m);
223102ac6454SAndrew Thompson 
223202ac6454SAndrew Thompson 		if (m) {
223302ac6454SAndrew Thompson 			tr_data = 1;
223402ac6454SAndrew Thompson 
223502ac6454SAndrew Thompson 			io_len = MIN(len, m->cur_data_len);
223602ac6454SAndrew Thompson 
2237271ae033SHans Petter Selasky 			memcpy(ptr, m->cur_data_ptr, io_len);
223802ac6454SAndrew Thompson 
223902ac6454SAndrew Thompson 			len -= io_len;
224002ac6454SAndrew Thompson 			ptr = USB_ADD_BYTES(ptr, io_len);
224102ac6454SAndrew Thompson 			actlen[0] += io_len;
224202ac6454SAndrew Thompson 			m->cur_data_ptr += io_len;
224302ac6454SAndrew Thompson 			m->cur_data_len -= io_len;
224402ac6454SAndrew Thompson 
224502ac6454SAndrew Thompson 			if ((m->cur_data_len == 0) || (what == 1)) {
224602ac6454SAndrew Thompson 				USB_IF_ENQUEUE(&f->free_q, m);
224702ac6454SAndrew Thompson 
2248a593f6b8SAndrew Thompson 				usb_fifo_wakeup(f);
224902ac6454SAndrew Thompson 
225002ac6454SAndrew Thompson 				if (what == 1) {
225102ac6454SAndrew Thompson 					break;
225202ac6454SAndrew Thompson 				}
225302ac6454SAndrew Thompson 			} else {
225402ac6454SAndrew Thompson 				USB_IF_PREPEND(&f->used_q, m);
225502ac6454SAndrew Thompson 			}
225602ac6454SAndrew Thompson 		} else {
225702ac6454SAndrew Thompson 			if (tr_data) {
225802ac6454SAndrew Thompson 				/* wait for data to be written out */
225902ac6454SAndrew Thompson 				break;
226002ac6454SAndrew Thompson 			}
226102ac6454SAndrew Thompson 			if (f->flag_flushing) {
22627214348fSAndrew Thompson 				/* check if we should send a short packet */
22637214348fSAndrew Thompson 				if (f->flag_short != 0) {
22647214348fSAndrew Thompson 					f->flag_short = 0;
22657214348fSAndrew Thompson 					tr_data = 1;
22667214348fSAndrew Thompson 					break;
22677214348fSAndrew Thompson 				}
22687214348fSAndrew Thompson 				/* flushing complete */
226902ac6454SAndrew Thompson 				f->flag_flushing = 0;
2270a593f6b8SAndrew Thompson 				usb_fifo_wakeup(f);
227102ac6454SAndrew Thompson 			}
227202ac6454SAndrew Thompson 			break;
227302ac6454SAndrew Thompson 		}
227402ac6454SAndrew Thompson 		if (len == 0) {
227502ac6454SAndrew Thompson 			break;
227602ac6454SAndrew Thompson 		}
227702ac6454SAndrew Thompson 	}
227802ac6454SAndrew Thompson 	return (tr_data);
227902ac6454SAndrew Thompson }
228002ac6454SAndrew Thompson 
228102ac6454SAndrew Thompson uint8_t
usb_fifo_get_data_buffer(struct usb_fifo * f,void ** pptr,usb_size_t * plen)2282a593f6b8SAndrew Thompson usb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen)
228302ac6454SAndrew Thompson {
2284760bc48eSAndrew Thompson 	struct usb_mbuf *m;
228502ac6454SAndrew Thompson 
228602ac6454SAndrew Thompson 	USB_IF_POLL(&f->used_q, m);
228702ac6454SAndrew Thompson 
228802ac6454SAndrew Thompson 	if (m) {
228902ac6454SAndrew Thompson 		*plen = m->cur_data_len;
229002ac6454SAndrew Thompson 		*pptr = m->cur_data_ptr;
229102ac6454SAndrew Thompson 
229202ac6454SAndrew Thompson 		return (1);
229302ac6454SAndrew Thompson 	}
229402ac6454SAndrew Thompson 	return (0);
229502ac6454SAndrew Thompson }
229602ac6454SAndrew Thompson 
229702ac6454SAndrew Thompson void
usb_fifo_get_data_error(struct usb_fifo * f)2298a593f6b8SAndrew Thompson usb_fifo_get_data_error(struct usb_fifo *f)
229902ac6454SAndrew Thompson {
230002ac6454SAndrew Thompson 	f->flag_iserror = 1;
2301a593f6b8SAndrew Thompson 	usb_fifo_wakeup(f);
230202ac6454SAndrew Thompson }
230302ac6454SAndrew Thompson 
230402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
2305a593f6b8SAndrew Thompson  *	usb_alloc_symlink
230602ac6454SAndrew Thompson  *
230702ac6454SAndrew Thompson  * Return values:
230802ac6454SAndrew Thompson  * NULL: Failure
230902ac6454SAndrew Thompson  * Else: Pointer to symlink entry
231002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
2311760bc48eSAndrew Thompson struct usb_symlink *
usb_alloc_symlink(const char * target)2312a593f6b8SAndrew Thompson usb_alloc_symlink(const char *target)
231302ac6454SAndrew Thompson {
2314760bc48eSAndrew Thompson 	struct usb_symlink *ps;
231502ac6454SAndrew Thompson 
231602ac6454SAndrew Thompson 	ps = malloc(sizeof(*ps), M_USBDEV, M_WAITOK);
2317ee3e3ff5SAndrew Thompson 	/* XXX no longer needed */
2318ee3e3ff5SAndrew Thompson 	strlcpy(ps->src_path, target, sizeof(ps->src_path));
2319ee3e3ff5SAndrew Thompson 	ps->src_len = strlen(ps->src_path);
232002ac6454SAndrew Thompson 	strlcpy(ps->dst_path, target, sizeof(ps->dst_path));
232102ac6454SAndrew Thompson 	ps->dst_len = strlen(ps->dst_path);
232202ac6454SAndrew Thompson 
2323a593f6b8SAndrew Thompson 	sx_xlock(&usb_sym_lock);
2324a593f6b8SAndrew Thompson 	TAILQ_INSERT_TAIL(&usb_sym_head, ps, sym_entry);
2325a593f6b8SAndrew Thompson 	sx_unlock(&usb_sym_lock);
232602ac6454SAndrew Thompson 	return (ps);
232702ac6454SAndrew Thompson }
232802ac6454SAndrew Thompson 
232902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
2330a593f6b8SAndrew Thompson  *	usb_free_symlink
233102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
233202ac6454SAndrew Thompson void
usb_free_symlink(struct usb_symlink * ps)2333a593f6b8SAndrew Thompson usb_free_symlink(struct usb_symlink *ps)
233402ac6454SAndrew Thompson {
233502ac6454SAndrew Thompson 	if (ps == NULL) {
233602ac6454SAndrew Thompson 		return;
233702ac6454SAndrew Thompson 	}
2338a593f6b8SAndrew Thompson 	sx_xlock(&usb_sym_lock);
2339a593f6b8SAndrew Thompson 	TAILQ_REMOVE(&usb_sym_head, ps, sym_entry);
2340a593f6b8SAndrew Thompson 	sx_unlock(&usb_sym_lock);
234102ac6454SAndrew Thompson 
234202ac6454SAndrew Thompson 	free(ps, M_USBDEV);
234302ac6454SAndrew Thompson }
234402ac6454SAndrew Thompson 
234502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
2346a593f6b8SAndrew Thompson  *	usb_read_symlink
234702ac6454SAndrew Thompson  *
234802ac6454SAndrew Thompson  * Return value:
234902ac6454SAndrew Thompson  * 0: Success
235002ac6454SAndrew Thompson  * Else: Failure
235102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
235202ac6454SAndrew Thompson int
usb_read_symlink(uint8_t * user_ptr,uint32_t startentry,uint32_t user_len)2353a593f6b8SAndrew Thompson usb_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len)
235402ac6454SAndrew Thompson {
2355760bc48eSAndrew Thompson 	struct usb_symlink *ps;
235602ac6454SAndrew Thompson 	uint32_t temp;
235702ac6454SAndrew Thompson 	uint32_t delta = 0;
235802ac6454SAndrew Thompson 	uint8_t len;
235902ac6454SAndrew Thompson 	int error = 0;
236002ac6454SAndrew Thompson 
2361a593f6b8SAndrew Thompson 	sx_xlock(&usb_sym_lock);
236202ac6454SAndrew Thompson 
2363a593f6b8SAndrew Thompson 	TAILQ_FOREACH(ps, &usb_sym_head, sym_entry) {
236402ac6454SAndrew Thompson 		/*
236502ac6454SAndrew Thompson 		 * Compute total length of source and destination symlink
236602ac6454SAndrew Thompson 		 * strings pluss one length byte and two NUL bytes:
236702ac6454SAndrew Thompson 		 */
236802ac6454SAndrew Thompson 		temp = ps->src_len + ps->dst_len + 3;
236902ac6454SAndrew Thompson 
237002ac6454SAndrew Thompson 		if (temp > 255) {
237102ac6454SAndrew Thompson 			/*
237202ac6454SAndrew Thompson 			 * Skip entry because this length cannot fit
237302ac6454SAndrew Thompson 			 * into one byte:
237402ac6454SAndrew Thompson 			 */
237502ac6454SAndrew Thompson 			continue;
237602ac6454SAndrew Thompson 		}
237702ac6454SAndrew Thompson 		if (startentry != 0) {
237802ac6454SAndrew Thompson 			/* decrement read offset */
237902ac6454SAndrew Thompson 			startentry--;
238002ac6454SAndrew Thompson 			continue;
238102ac6454SAndrew Thompson 		}
238202ac6454SAndrew Thompson 		if (temp > user_len) {
238302ac6454SAndrew Thompson 			/* out of buffer space */
238402ac6454SAndrew Thompson 			break;
238502ac6454SAndrew Thompson 		}
238602ac6454SAndrew Thompson 		len = temp;
238702ac6454SAndrew Thompson 
238802ac6454SAndrew Thompson 		/* copy out total length */
238902ac6454SAndrew Thompson 
239002ac6454SAndrew Thompson 		error = copyout(&len,
239102ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
239202ac6454SAndrew Thompson 		if (error) {
239302ac6454SAndrew Thompson 			break;
239402ac6454SAndrew Thompson 		}
239502ac6454SAndrew Thompson 		delta += 1;
239602ac6454SAndrew Thompson 
239702ac6454SAndrew Thompson 		/* copy out source string */
239802ac6454SAndrew Thompson 
239902ac6454SAndrew Thompson 		error = copyout(ps->src_path,
240002ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), ps->src_len);
240102ac6454SAndrew Thompson 		if (error) {
240202ac6454SAndrew Thompson 			break;
240302ac6454SAndrew Thompson 		}
240402ac6454SAndrew Thompson 		len = 0;
240502ac6454SAndrew Thompson 		delta += ps->src_len;
240602ac6454SAndrew Thompson 		error = copyout(&len,
240702ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
240802ac6454SAndrew Thompson 		if (error) {
240902ac6454SAndrew Thompson 			break;
241002ac6454SAndrew Thompson 		}
241102ac6454SAndrew Thompson 		delta += 1;
241202ac6454SAndrew Thompson 
241302ac6454SAndrew Thompson 		/* copy out destination string */
241402ac6454SAndrew Thompson 
241502ac6454SAndrew Thompson 		error = copyout(ps->dst_path,
241602ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), ps->dst_len);
241702ac6454SAndrew Thompson 		if (error) {
241802ac6454SAndrew Thompson 			break;
241902ac6454SAndrew Thompson 		}
242002ac6454SAndrew Thompson 		len = 0;
242102ac6454SAndrew Thompson 		delta += ps->dst_len;
242202ac6454SAndrew Thompson 		error = copyout(&len,
242302ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
242402ac6454SAndrew Thompson 		if (error) {
242502ac6454SAndrew Thompson 			break;
242602ac6454SAndrew Thompson 		}
242702ac6454SAndrew Thompson 		delta += 1;
242802ac6454SAndrew Thompson 
242902ac6454SAndrew Thompson 		user_len -= temp;
243002ac6454SAndrew Thompson 	}
243102ac6454SAndrew Thompson 
243202ac6454SAndrew Thompson 	/* a zero length entry indicates the end */
243302ac6454SAndrew Thompson 
243402ac6454SAndrew Thompson 	if ((user_len != 0) && (error == 0)) {
243502ac6454SAndrew Thompson 		len = 0;
243602ac6454SAndrew Thompson 
243702ac6454SAndrew Thompson 		error = copyout(&len,
243802ac6454SAndrew Thompson 		    USB_ADD_BYTES(user_ptr, delta), 1);
243902ac6454SAndrew Thompson 	}
2440a593f6b8SAndrew Thompson 	sx_unlock(&usb_sym_lock);
244102ac6454SAndrew Thompson 	return (error);
244202ac6454SAndrew Thompson }
24437214348fSAndrew Thompson 
24447214348fSAndrew Thompson void
usb_fifo_set_close_zlp(struct usb_fifo * f,uint8_t onoff)2445a593f6b8SAndrew Thompson usb_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff)
24467214348fSAndrew Thompson {
24477214348fSAndrew Thompson 	if (f == NULL)
24487214348fSAndrew Thompson 		return;
24497214348fSAndrew Thompson 
24507214348fSAndrew Thompson 	/* send a Zero Length Packet, ZLP, before close */
24517214348fSAndrew Thompson 	f->flag_short = onoff;
24527214348fSAndrew Thompson }
2453ed6d949aSAndrew Thompson 
2454bd73b187SAlfred Perlstein void
usb_fifo_set_write_defrag(struct usb_fifo * f,uint8_t onoff)2455bd73b187SAlfred Perlstein usb_fifo_set_write_defrag(struct usb_fifo *f, uint8_t onoff)
2456bd73b187SAlfred Perlstein {
2457bd73b187SAlfred Perlstein 	if (f == NULL)
2458bd73b187SAlfred Perlstein 		return;
2459bd73b187SAlfred Perlstein 
2460bd73b187SAlfred Perlstein 	/* defrag written data */
2461bd73b187SAlfred Perlstein 	f->flag_write_defrag = onoff;
2462bd73b187SAlfred Perlstein 	/* reset defrag state */
2463bd73b187SAlfred Perlstein 	f->flag_have_fragment = 0;
2464bd73b187SAlfred Perlstein }
2465bd73b187SAlfred Perlstein 
2466ed6d949aSAndrew Thompson void *
usb_fifo_softc(struct usb_fifo * f)2467ed6d949aSAndrew Thompson usb_fifo_softc(struct usb_fifo *f)
2468ed6d949aSAndrew Thompson {
2469ed6d949aSAndrew Thompson 	return (f->priv_sc0);
2470ed6d949aSAndrew Thompson }
24718755859aSAndrew Thompson #endif	/* USB_HAVE_UGEN */
2472