xref: /third_party/FreeBSD/sys/dev/usb/usb_dev.c (revision f9f848fa)
1f9f848faSopenharmony_ci/*-
2f9f848faSopenharmony_ci * SPDX-License-Identifier: BSD-2-Clause
3f9f848faSopenharmony_ci *
4f9f848faSopenharmony_ci * Copyright (c) 2006-2023 Hans Petter Selasky
5f9f848faSopenharmony_ci *
6f9f848faSopenharmony_ci * Redistribution and use in source and binary forms, with or without
7f9f848faSopenharmony_ci * modification, are permitted provided that the following conditions
8f9f848faSopenharmony_ci * are met:
9f9f848faSopenharmony_ci * 1. Redistributions of source code must retain the above copyright
10f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer.
11f9f848faSopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
12f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
13f9f848faSopenharmony_ci *    documentation and/or other materials provided with the distribution.
14f9f848faSopenharmony_ci *
15f9f848faSopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16f9f848faSopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17f9f848faSopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18f9f848faSopenharmony_ci * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19f9f848faSopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20f9f848faSopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21f9f848faSopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22f9f848faSopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23f9f848faSopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24f9f848faSopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25f9f848faSopenharmony_ci * SUCH DAMAGE.
26f9f848faSopenharmony_ci *
27f9f848faSopenharmony_ci *
28f9f848faSopenharmony_ci * usb_dev.c - An abstraction layer for creating devices under /dev/...
29f9f848faSopenharmony_ci */
30f9f848faSopenharmony_ci
31f9f848faSopenharmony_ci#include "implementation/global_implementation.h"
32f9f848faSopenharmony_ci#include "fs/driver.h"
33f9f848faSopenharmony_ci#include "fs/file.h"
34f9f848faSopenharmony_ci#include <unistd.h>
35f9f848faSopenharmony_ci
36f9f848faSopenharmony_ci#undef USB_DEBUG_VAR
37f9f848faSopenharmony_ci#define	USB_DEBUG_VAR usb_fifo_debug
38f9f848faSopenharmony_ci
39f9f848faSopenharmony_ci#if USB_HAVE_UGEN
40f9f848faSopenharmony_ci
41f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
42f9f848faSopenharmony_cistatic int usb_fifo_debug = 0;
43f9f848faSopenharmony_ci#endif
44f9f848faSopenharmony_ci
45f9f848faSopenharmony_ci/* prototypes */
46f9f848faSopenharmony_ci
47f9f848faSopenharmony_cistatic int	usb_fifo_open(struct usb_cdev_privdata *,
48f9f848faSopenharmony_ci		    struct usb_fifo *, int);
49f9f848faSopenharmony_cistatic void	usb_fifo_close(struct usb_fifo *, int);
50f9f848faSopenharmony_cistatic void	usb_fifo_check_methods(struct usb_fifo_methods *);
51f9f848faSopenharmony_cistatic struct	usb_fifo *usb_fifo_alloc(struct mtx *);
52f9f848faSopenharmony_cistatic struct	usb_endpoint *usb_dev_get_ep(struct usb_device *, uint8_t,
53f9f848faSopenharmony_ci		    uint8_t);
54f9f848faSopenharmony_cistatic void	usb_loc_fill(struct usb_fs_privdata *,
55f9f848faSopenharmony_ci		    struct usb_cdev_privdata *);
56f9f848faSopenharmony_cistatic usb_error_t usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *, int);
57f9f848faSopenharmony_cistatic usb_error_t usb_usb_ref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *);
58f9f848faSopenharmony_cistatic void	usb_unref_device(struct usb_cdev_privdata *, struct usb_cdev_refdata *);
59f9f848faSopenharmony_ci
60f9f848faSopenharmony_cistatic int usb_open(struct file *filep);
61f9f848faSopenharmony_cistatic int usb_close(struct file *filep);
62f9f848faSopenharmony_cistatic int usb_ioctl(struct file *filep, int cmd, unsigned long arg);
63f9f848faSopenharmony_cistatic ssize_t usb_read(struct file *filep, char *buffer, size_t buflen);
64f9f848faSopenharmony_cistatic ssize_t usb_write(struct file *filep, const char *buffer, size_t buflen);
65f9f848faSopenharmony_cistatic int usb_poll(struct file *filep, poll_table *fds);
66f9f848faSopenharmony_ci
67f9f848faSopenharmony_cistatic usb_fifo_open_t usb_fifo_dummy_open;
68f9f848faSopenharmony_cistatic usb_fifo_close_t usb_fifo_dummy_close;
69f9f848faSopenharmony_cistatic usb_fifo_ioctl_t usb_fifo_dummy_ioctl;
70f9f848faSopenharmony_cistatic usb_fifo_cmd_t usb_fifo_dummy_cmd;
71f9f848faSopenharmony_ci
72f9f848faSopenharmony_ci/* character device structure used for devices (/dev/ugenX.Y and /dev/uXXX) */
73f9f848faSopenharmony_cistruct file_operations_vfs usb_devsw = {
74f9f848faSopenharmony_ci	.open = usb_open,
75f9f848faSopenharmony_ci	.close = usb_close,
76f9f848faSopenharmony_ci	.ioctl = usb_ioctl,
77f9f848faSopenharmony_ci	.read = usb_read,
78f9f848faSopenharmony_ci	.write = usb_write,
79f9f848faSopenharmony_ci	.poll = usb_poll,
80f9f848faSopenharmony_ci	.mmap = NULL,
81f9f848faSopenharmony_ci};
82f9f848faSopenharmony_ci
83f9f848faSopenharmony_cistatic TAILQ_HEAD(, usb_symlink) usb_sym_head;
84f9f848faSopenharmony_cistatic struct sx usb_sym_lock;
85f9f848faSopenharmony_ci
86f9f848faSopenharmony_cistruct mtx usb_ref_lock;
87f9f848faSopenharmony_ci
88f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
89f9f848faSopenharmony_ci *	usb_loc_fill
90f9f848faSopenharmony_ci *
91f9f848faSopenharmony_ci * This is used to fill out a usb_cdev_privdata structure based on the
92f9f848faSopenharmony_ci * device's address as contained in usb_fs_privdata.
93f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
94f9f848faSopenharmony_cistatic void
95f9f848faSopenharmony_ciusb_loc_fill(struct usb_fs_privdata* pd, struct usb_cdev_privdata *cpd)
96f9f848faSopenharmony_ci{
97f9f848faSopenharmony_ci	cpd->bus_index = pd->bus_index;
98f9f848faSopenharmony_ci	cpd->dev_index = pd->dev_index;
99f9f848faSopenharmony_ci	cpd->ep_addr = pd->ep_addr;
100f9f848faSopenharmony_ci	cpd->fifo_index = pd->fifo_index;
101f9f848faSopenharmony_ci}
102f9f848faSopenharmony_ci
103f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
104f9f848faSopenharmony_ci *	usb_ref_device
105f9f848faSopenharmony_ci *
106f9f848faSopenharmony_ci * This function is used to atomically refer an USB device by its
107f9f848faSopenharmony_ci * device location. If this function returns success the USB device
108f9f848faSopenharmony_ci * will not disappear until the USB device is unreferenced.
109f9f848faSopenharmony_ci *
110f9f848faSopenharmony_ci * Return values:
111f9f848faSopenharmony_ci *  0: Success, refcount incremented on the given USB device.
112f9f848faSopenharmony_ci *  Else: Failure.
113f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
114f9f848faSopenharmony_cistatic usb_error_t
115f9f848faSopenharmony_ciusb_ref_device(struct usb_cdev_privdata *cpd,
116f9f848faSopenharmony_ci    struct usb_cdev_refdata *crd, int need_uref)
117f9f848faSopenharmony_ci{
118f9f848faSopenharmony_ci	struct usb_fifo **ppf = NULL;
119f9f848faSopenharmony_ci	struct usb_fifo *f = NULL;
120f9f848faSopenharmony_ci
121f9f848faSopenharmony_ci	DPRINTFN(2, "cpd=%p need uref=%d\n", cpd, need_uref);
122f9f848faSopenharmony_ci
123f9f848faSopenharmony_ci	/* clear all refs */
124f9f848faSopenharmony_ci	(void)memset_s(crd, sizeof(*crd), 0, sizeof(*crd));
125f9f848faSopenharmony_ci
126f9f848faSopenharmony_ci	mtx_lock(&usb_ref_lock);
127f9f848faSopenharmony_ci	cpd->bus = devclass_get_softc(usb_devclass_ptr, cpd->bus_index);
128f9f848faSopenharmony_ci	if (cpd->bus == NULL) {
129f9f848faSopenharmony_ci		DPRINTFN(2, "no bus at %u\n", cpd->bus_index);
130f9f848faSopenharmony_ci		goto error;
131f9f848faSopenharmony_ci	}
132f9f848faSopenharmony_ci	cpd->udev = cpd->bus->devices[cpd->dev_index];
133f9f848faSopenharmony_ci	if (cpd->udev == NULL) {
134f9f848faSopenharmony_ci		DPRINTFN(2, "no device at %u\n", cpd->dev_index);
135f9f848faSopenharmony_ci		goto error;
136f9f848faSopenharmony_ci	}
137f9f848faSopenharmony_ci
138f9f848faSopenharmony_ci	if (cpd->udev->state == USB_STATE_DETACHED &&
139f9f848faSopenharmony_ci	    (need_uref != 2)) {
140f9f848faSopenharmony_ci		DPRINTFN(2, "device is detached\n");
141f9f848faSopenharmony_ci		goto error;
142f9f848faSopenharmony_ci	}
143f9f848faSopenharmony_ci	if (need_uref) {
144f9f848faSopenharmony_ci		DPRINTFN(2, "ref udev - needed\n");
145f9f848faSopenharmony_ci
146f9f848faSopenharmony_ci		if (cpd->udev->refcount == USB_DEV_REF_MAX) {
147f9f848faSopenharmony_ci			DPRINTFN(2, "no dev ref\n");
148f9f848faSopenharmony_ci			goto error;
149f9f848faSopenharmony_ci		}
150f9f848faSopenharmony_ci		cpd->udev->refcount++;
151f9f848faSopenharmony_ci
152f9f848faSopenharmony_ci		mtx_unlock(&usb_ref_lock);
153f9f848faSopenharmony_ci
154f9f848faSopenharmony_ci		/*
155f9f848faSopenharmony_ci		 * We need to grab the enumeration SX-lock before
156f9f848faSopenharmony_ci		 * grabbing the FIFO refs to avoid deadlock at detach!
157f9f848faSopenharmony_ci		 */
158f9f848faSopenharmony_ci		crd->do_unlock = usbd_enum_lock(cpd->udev);
159f9f848faSopenharmony_ci
160f9f848faSopenharmony_ci		mtx_lock(&usb_ref_lock);
161f9f848faSopenharmony_ci
162f9f848faSopenharmony_ci		/*
163f9f848faSopenharmony_ci		 * Set "is_uref" after grabbing the default SX lock
164f9f848faSopenharmony_ci		 */
165f9f848faSopenharmony_ci		crd->is_uref = 1;
166f9f848faSopenharmony_ci
167f9f848faSopenharmony_ci		/* check for signal */
168f9f848faSopenharmony_ci		if (crd->do_unlock > 1) {
169f9f848faSopenharmony_ci			crd->do_unlock = 0;
170f9f848faSopenharmony_ci			goto error;
171f9f848faSopenharmony_ci		}
172f9f848faSopenharmony_ci	}
173f9f848faSopenharmony_ci
174f9f848faSopenharmony_ci	/* check if we are doing an open */
175f9f848faSopenharmony_ci	if (cpd->fflags == 0) {
176f9f848faSopenharmony_ci		/* use zero defaults */
177f9f848faSopenharmony_ci	} else {
178f9f848faSopenharmony_ci		/* check for write */
179f9f848faSopenharmony_ci		if ((unsigned int)cpd->fflags & FWRITE) {
180f9f848faSopenharmony_ci			ppf = cpd->udev->fifo;
181f9f848faSopenharmony_ci			f = ppf[cpd->fifo_index + USB_FIFO_TX];
182f9f848faSopenharmony_ci			crd->txfifo = f;
183f9f848faSopenharmony_ci			crd->is_write = 1;	/* ref */
184f9f848faSopenharmony_ci			if (f == NULL || f->refcount == USB_FIFO_REF_MAX)
185f9f848faSopenharmony_ci				goto error;
186f9f848faSopenharmony_ci			if (f->curr_cpd != cpd)
187f9f848faSopenharmony_ci				goto error;
188f9f848faSopenharmony_ci
189f9f848faSopenharmony_ci			/* check if USB-FS is active */
190f9f848faSopenharmony_ci			if (f->fs_ep_max != 0) {
191f9f848faSopenharmony_ci				crd->is_usbfs = 1;
192f9f848faSopenharmony_ci			}
193f9f848faSopenharmony_ci		}
194f9f848faSopenharmony_ci
195f9f848faSopenharmony_ci		/* check for read */
196f9f848faSopenharmony_ci		if ((unsigned int)cpd->fflags & FREAD) {
197f9f848faSopenharmony_ci			ppf = cpd->udev->fifo;
198f9f848faSopenharmony_ci			f = ppf[cpd->fifo_index + USB_FIFO_RX];
199f9f848faSopenharmony_ci			crd->rxfifo = f;
200f9f848faSopenharmony_ci			crd->is_read = 1;	/* ref */
201f9f848faSopenharmony_ci			if (f == NULL || f->refcount == USB_FIFO_REF_MAX)
202f9f848faSopenharmony_ci				goto error;
203f9f848faSopenharmony_ci			if (f->curr_cpd != cpd)
204f9f848faSopenharmony_ci				goto error;
205f9f848faSopenharmony_ci
206f9f848faSopenharmony_ci			/* check if USB-FS is active */
207f9f848faSopenharmony_ci			if (f->fs_ep_max != 0) {
208f9f848faSopenharmony_ci				crd->is_usbfs = 1;
209f9f848faSopenharmony_ci			}
210f9f848faSopenharmony_ci		}
211f9f848faSopenharmony_ci	}
212f9f848faSopenharmony_ci
213f9f848faSopenharmony_ci	/* when everything is OK we increment the refcounts */
214f9f848faSopenharmony_ci	if (crd->is_write) {
215f9f848faSopenharmony_ci		DPRINTFN(2, "ref write\n");
216f9f848faSopenharmony_ci		crd->txfifo->refcount++;
217f9f848faSopenharmony_ci	}
218f9f848faSopenharmony_ci	if (crd->is_read) {
219f9f848faSopenharmony_ci		DPRINTFN(2, "ref read\n");
220f9f848faSopenharmony_ci		crd->rxfifo->refcount++;
221f9f848faSopenharmony_ci	}
222f9f848faSopenharmony_ci	mtx_unlock(&usb_ref_lock);
223f9f848faSopenharmony_ci
224f9f848faSopenharmony_ci	return (0);
225f9f848faSopenharmony_ci
226f9f848faSopenharmony_cierror:
227f9f848faSopenharmony_ci	if (crd->do_unlock)
228f9f848faSopenharmony_ci		usbd_enum_unlock(cpd->udev);
229f9f848faSopenharmony_ci
230f9f848faSopenharmony_ci	if (crd->is_uref) {
231f9f848faSopenharmony_ci		if (cpd->udev && --(cpd->udev->refcount) == 0)
232f9f848faSopenharmony_ci			cv_broadcast(&cpd->udev->ref_cv);
233f9f848faSopenharmony_ci	}
234f9f848faSopenharmony_ci	mtx_unlock(&usb_ref_lock);
235f9f848faSopenharmony_ci	DPRINTFN(2, "fail\n");
236f9f848faSopenharmony_ci
237f9f848faSopenharmony_ci	/* clear all refs */
238f9f848faSopenharmony_ci	memset(crd, 0, sizeof(*crd));
239f9f848faSopenharmony_ci
240f9f848faSopenharmony_ci	return (USB_ERR_INVAL);
241f9f848faSopenharmony_ci}
242f9f848faSopenharmony_ci
243f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
244f9f848faSopenharmony_ci *	usb_usb_ref_device
245f9f848faSopenharmony_ci *
246f9f848faSopenharmony_ci * This function is used to upgrade an USB reference to include the
247f9f848faSopenharmony_ci * USB device reference on a USB location.
248f9f848faSopenharmony_ci *
249f9f848faSopenharmony_ci * Return values:
250f9f848faSopenharmony_ci *  0: Success, refcount incremented on the given USB device.
251f9f848faSopenharmony_ci *  Else: Failure.
252f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
253f9f848faSopenharmony_cistatic usb_error_t
254f9f848faSopenharmony_ciusb_usb_ref_device(struct usb_cdev_privdata *cpd,
255f9f848faSopenharmony_ci    struct usb_cdev_refdata *crd)
256f9f848faSopenharmony_ci{
257f9f848faSopenharmony_ci	/*
258f9f848faSopenharmony_ci	 * Check if we already got an USB reference on this location:
259f9f848faSopenharmony_ci	 */
260f9f848faSopenharmony_ci	if (crd->is_uref)
261f9f848faSopenharmony_ci		return (0);		/* success */
262f9f848faSopenharmony_ci
263f9f848faSopenharmony_ci	/*
264f9f848faSopenharmony_ci	 * To avoid deadlock at detach we need to drop the FIFO ref
265f9f848faSopenharmony_ci	 * and re-acquire a new ref!
266f9f848faSopenharmony_ci	 */
267f9f848faSopenharmony_ci	usb_unref_device(cpd, crd);
268f9f848faSopenharmony_ci
269f9f848faSopenharmony_ci	return (usb_ref_device(cpd, crd, 1 /* need uref */));
270f9f848faSopenharmony_ci}
271f9f848faSopenharmony_ci
272f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
273f9f848faSopenharmony_ci *	usb_unref_device
274f9f848faSopenharmony_ci *
275f9f848faSopenharmony_ci * This function will release the reference count by one unit for the
276f9f848faSopenharmony_ci * given USB device.
277f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
278f9f848faSopenharmony_cistatic void
279f9f848faSopenharmony_ciusb_unref_device(struct usb_cdev_privdata *cpd,
280f9f848faSopenharmony_ci    struct usb_cdev_refdata *crd)
281f9f848faSopenharmony_ci{
282f9f848faSopenharmony_ci
283f9f848faSopenharmony_ci	DPRINTFN(2, "cpd=%p is_uref=%d\n", cpd, crd->is_uref);
284f9f848faSopenharmony_ci
285f9f848faSopenharmony_ci	if (crd->do_unlock)
286f9f848faSopenharmony_ci		usbd_enum_unlock(cpd->udev);
287f9f848faSopenharmony_ci
288f9f848faSopenharmony_ci	mtx_lock(&usb_ref_lock);
289f9f848faSopenharmony_ci	if (crd->is_read) {
290f9f848faSopenharmony_ci		if (--(crd->rxfifo->refcount) == 0) {
291f9f848faSopenharmony_ci			cv_signal(&crd->rxfifo->cv_drain);
292f9f848faSopenharmony_ci		}
293f9f848faSopenharmony_ci		crd->is_read = 0;
294f9f848faSopenharmony_ci	}
295f9f848faSopenharmony_ci	if (crd->is_write) {
296f9f848faSopenharmony_ci		if (--(crd->txfifo->refcount) == 0) {
297f9f848faSopenharmony_ci			cv_signal(&crd->txfifo->cv_drain);
298f9f848faSopenharmony_ci		}
299f9f848faSopenharmony_ci		crd->is_write = 0;
300f9f848faSopenharmony_ci	}
301f9f848faSopenharmony_ci	if (crd->is_uref) {
302f9f848faSopenharmony_ci		crd->is_uref = 0;
303f9f848faSopenharmony_ci		if (--(cpd->udev->refcount) == 0)
304f9f848faSopenharmony_ci			cv_broadcast(&cpd->udev->ref_cv);
305f9f848faSopenharmony_ci	}
306f9f848faSopenharmony_ci	mtx_unlock(&usb_ref_lock);
307f9f848faSopenharmony_ci}
308f9f848faSopenharmony_ci
309f9f848faSopenharmony_cistatic struct usb_fifo *
310f9f848faSopenharmony_ciusb_fifo_alloc(struct mtx *mtx)
311f9f848faSopenharmony_ci{
312f9f848faSopenharmony_ci	struct usb_fifo *f;
313f9f848faSopenharmony_ci
314f9f848faSopenharmony_ci	f = bsd_malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO);
315f9f848faSopenharmony_ci	if (f != NULL) {
316f9f848faSopenharmony_ci		cv_init(&f->cv_io, "FIFO-IO");
317f9f848faSopenharmony_ci		cv_init(&f->cv_drain, "FIFO-DRAIN");
318f9f848faSopenharmony_ci		f->priv_mtx = mtx;
319f9f848faSopenharmony_ci		f->refcount = 1;
320f9f848faSopenharmony_ci		mtx_init(mtx, 0, 0, MTX_RECURSE);
321f9f848faSopenharmony_ci	}
322f9f848faSopenharmony_ci	return (f);
323f9f848faSopenharmony_ci}
324f9f848faSopenharmony_ci
325f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
326f9f848faSopenharmony_ci *	usb_fifo_create
327f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
328f9f848faSopenharmony_cistatic int
329f9f848faSopenharmony_ciusb_fifo_create(struct usb_cdev_privdata *cpd,
330f9f848faSopenharmony_ci    struct usb_cdev_refdata *crd)
331f9f848faSopenharmony_ci{
332f9f848faSopenharmony_ci	struct usb_device *udev = cpd->udev;
333f9f848faSopenharmony_ci	struct usb_fifo *f = NULL;
334f9f848faSopenharmony_ci	struct usb_endpoint *ep = NULL;
335f9f848faSopenharmony_ci	uint8_t n;
336f9f848faSopenharmony_ci	uint8_t is_tx;
337f9f848faSopenharmony_ci	uint8_t is_rx;
338f9f848faSopenharmony_ci	uint8_t no_null;
339f9f848faSopenharmony_ci	uint8_t is_busy;
340f9f848faSopenharmony_ci	int e = cpd->ep_addr;
341f9f848faSopenharmony_ci
342f9f848faSopenharmony_ci	is_tx = ((unsigned int)cpd->fflags & FWRITE) ? 1 : 0;
343f9f848faSopenharmony_ci	is_rx = ((unsigned int)cpd->fflags & FREAD) ? 1 : 0;
344f9f848faSopenharmony_ci	no_null = 1;
345f9f848faSopenharmony_ci	is_busy = 0;
346f9f848faSopenharmony_ci
347f9f848faSopenharmony_ci	/* Preallocated FIFO */
348f9f848faSopenharmony_ci	if (e < 0) {
349f9f848faSopenharmony_ci		DPRINTFN(5, "Preallocated FIFO\n");
350f9f848faSopenharmony_ci		if (is_tx) {
351f9f848faSopenharmony_ci			f = udev->fifo[cpd->fifo_index + USB_FIFO_TX];
352f9f848faSopenharmony_ci			if (f == NULL)
353f9f848faSopenharmony_ci				return (EINVAL);
354f9f848faSopenharmony_ci			crd->txfifo = f;
355f9f848faSopenharmony_ci		}
356f9f848faSopenharmony_ci		if (is_rx) {
357f9f848faSopenharmony_ci			f = udev->fifo[cpd->fifo_index + USB_FIFO_RX];
358f9f848faSopenharmony_ci			if (f == NULL)
359f9f848faSopenharmony_ci				return (EINVAL);
360f9f848faSopenharmony_ci			crd->rxfifo = f;
361f9f848faSopenharmony_ci		}
362f9f848faSopenharmony_ci		return (0);
363f9f848faSopenharmony_ci	}
364f9f848faSopenharmony_ci
365f9f848faSopenharmony_ci	KASSERT(e >= 0 && e <= 15, ("endpoint %d out of range", e));
366f9f848faSopenharmony_ci
367f9f848faSopenharmony_ci	/* search for a free FIFO slot */
368f9f848faSopenharmony_ci	DPRINTFN(5, "Endpoint device, searching for 0x%02x\n", e);
369f9f848faSopenharmony_ci	for (n = 0;; n += 2) {
370f9f848faSopenharmony_ci		if (n == USB_FIFO_MAX) {
371f9f848faSopenharmony_ci			if (no_null) {
372f9f848faSopenharmony_ci				no_null = 0;
373f9f848faSopenharmony_ci				n = 0;
374f9f848faSopenharmony_ci			} else {
375f9f848faSopenharmony_ci				/* end of FIFOs reached */
376f9f848faSopenharmony_ci				DPRINTFN(5, "out of FIFOs\n");
377f9f848faSopenharmony_ci				return (ENOMEM);
378f9f848faSopenharmony_ci			}
379f9f848faSopenharmony_ci		}
380f9f848faSopenharmony_ci		/* Check for TX FIFO */
381f9f848faSopenharmony_ci		if (is_tx) {
382f9f848faSopenharmony_ci			f = udev->fifo[n + USB_FIFO_TX];
383f9f848faSopenharmony_ci			if (f != NULL) {
384f9f848faSopenharmony_ci				if (f->dev_ep_index != e) {
385f9f848faSopenharmony_ci					/* wrong endpoint index */
386f9f848faSopenharmony_ci					continue;
387f9f848faSopenharmony_ci				}
388f9f848faSopenharmony_ci				if (f->curr_cpd != NULL) {
389f9f848faSopenharmony_ci					/* FIFO is opened */
390f9f848faSopenharmony_ci					is_busy = 1;
391f9f848faSopenharmony_ci					continue;
392f9f848faSopenharmony_ci				}
393f9f848faSopenharmony_ci			} else if (no_null) {
394f9f848faSopenharmony_ci				continue;
395f9f848faSopenharmony_ci			}
396f9f848faSopenharmony_ci		}
397f9f848faSopenharmony_ci		/* Check for RX FIFO */
398f9f848faSopenharmony_ci		if (is_rx) {
399f9f848faSopenharmony_ci			f = udev->fifo[n + USB_FIFO_RX];
400f9f848faSopenharmony_ci			if (f != NULL) {
401f9f848faSopenharmony_ci				if (f->dev_ep_index != e) {
402f9f848faSopenharmony_ci					/* wrong endpoint index */
403f9f848faSopenharmony_ci					continue;
404f9f848faSopenharmony_ci				}
405f9f848faSopenharmony_ci				if (f->curr_cpd != NULL) {
406f9f848faSopenharmony_ci					/* FIFO is opened */
407f9f848faSopenharmony_ci					is_busy = 1;
408f9f848faSopenharmony_ci					continue;
409f9f848faSopenharmony_ci				}
410f9f848faSopenharmony_ci			} else if (no_null) {
411f9f848faSopenharmony_ci				continue;
412f9f848faSopenharmony_ci			}
413f9f848faSopenharmony_ci		}
414f9f848faSopenharmony_ci		break;
415f9f848faSopenharmony_ci	}
416f9f848faSopenharmony_ci
417f9f848faSopenharmony_ci	if (no_null == 0) {
418f9f848faSopenharmony_ci		if (e >= (USB_EP_MAX / 2)) {
419f9f848faSopenharmony_ci			/* we don't create any endpoints in this range */
420f9f848faSopenharmony_ci			DPRINTFN(5, "ep out of range\n");
421f9f848faSopenharmony_ci			return (is_busy ? EBUSY : EINVAL);
422f9f848faSopenharmony_ci		}
423f9f848faSopenharmony_ci	}
424f9f848faSopenharmony_ci
425f9f848faSopenharmony_ci	if ((e != 0) && is_busy) {
426f9f848faSopenharmony_ci		/*
427f9f848faSopenharmony_ci		 * Only the default control endpoint is allowed to be
428f9f848faSopenharmony_ci		 * opened multiple times!
429f9f848faSopenharmony_ci		 */
430f9f848faSopenharmony_ci		DPRINTFN(5, "busy\n");
431f9f848faSopenharmony_ci		return (EBUSY);
432f9f848faSopenharmony_ci	}
433f9f848faSopenharmony_ci
434f9f848faSopenharmony_ci	/* Check TX FIFO */
435f9f848faSopenharmony_ci	if (is_tx &&
436f9f848faSopenharmony_ci	    (udev->fifo[n + USB_FIFO_TX] == NULL)) {
437f9f848faSopenharmony_ci		ep = usb_dev_get_ep(udev, e, USB_FIFO_TX);
438f9f848faSopenharmony_ci		DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_TX);
439f9f848faSopenharmony_ci		if (ep == NULL) {
440f9f848faSopenharmony_ci			DPRINTFN(5, "dev_get_endpoint returned NULL\n");
441f9f848faSopenharmony_ci			return (EINVAL);
442f9f848faSopenharmony_ci		}
443f9f848faSopenharmony_ci		f = usb_fifo_alloc(&udev->device_mtx);
444f9f848faSopenharmony_ci		if (f == NULL) {
445f9f848faSopenharmony_ci			DPRINTFN(5, "could not alloc tx fifo\n");
446f9f848faSopenharmony_ci			return (ENOMEM);
447f9f848faSopenharmony_ci		}
448f9f848faSopenharmony_ci		/* update some fields */
449f9f848faSopenharmony_ci		f->fifo_index = n + USB_FIFO_TX;
450f9f848faSopenharmony_ci		f->dev_ep_index = e;
451f9f848faSopenharmony_ci		f->priv_sc0 = ep;
452f9f848faSopenharmony_ci		f->methods = &usb_ugen_methods;
453f9f848faSopenharmony_ci		f->iface_index = ep->iface_index;
454f9f848faSopenharmony_ci		f->udev = udev;
455f9f848faSopenharmony_ci		mtx_lock(&usb_ref_lock);
456f9f848faSopenharmony_ci		udev->fifo[n + USB_FIFO_TX] = f;
457f9f848faSopenharmony_ci		mtx_unlock(&usb_ref_lock);
458f9f848faSopenharmony_ci	}
459f9f848faSopenharmony_ci	/* Check RX FIFO */
460f9f848faSopenharmony_ci	if (is_rx &&
461f9f848faSopenharmony_ci	    (udev->fifo[n + USB_FIFO_RX] == NULL)) {
462f9f848faSopenharmony_ci		ep = usb_dev_get_ep(udev, e, USB_FIFO_RX);
463f9f848faSopenharmony_ci		DPRINTFN(5, "dev_get_endpoint(%d, 0x%x)\n", e, USB_FIFO_RX);
464f9f848faSopenharmony_ci		if (ep == NULL) {
465f9f848faSopenharmony_ci			DPRINTFN(5, "dev_get_endpoint returned NULL\n");
466f9f848faSopenharmony_ci			return (EINVAL);
467f9f848faSopenharmony_ci		}
468f9f848faSopenharmony_ci		f = usb_fifo_alloc(&udev->device_mtx);
469f9f848faSopenharmony_ci		if (f == NULL) {
470f9f848faSopenharmony_ci			DPRINTFN(5, "could not alloc rx fifo\n");
471f9f848faSopenharmony_ci			return (ENOMEM);
472f9f848faSopenharmony_ci		}
473f9f848faSopenharmony_ci		/* update some fields */
474f9f848faSopenharmony_ci		f->fifo_index = n + USB_FIFO_RX;
475f9f848faSopenharmony_ci		f->dev_ep_index = e;
476f9f848faSopenharmony_ci		f->priv_sc0 = ep;
477f9f848faSopenharmony_ci		f->methods = &usb_ugen_methods;
478f9f848faSopenharmony_ci		f->iface_index = ep->iface_index;
479f9f848faSopenharmony_ci		f->udev = udev;
480f9f848faSopenharmony_ci		mtx_lock(&usb_ref_lock);
481f9f848faSopenharmony_ci		udev->fifo[n + USB_FIFO_RX] = f;
482f9f848faSopenharmony_ci		mtx_unlock(&usb_ref_lock);
483f9f848faSopenharmony_ci	}
484f9f848faSopenharmony_ci	if (is_tx) {
485f9f848faSopenharmony_ci		crd->txfifo = udev->fifo[n + USB_FIFO_TX];
486f9f848faSopenharmony_ci	}
487f9f848faSopenharmony_ci	if (is_rx) {
488f9f848faSopenharmony_ci		crd->rxfifo = udev->fifo[n + USB_FIFO_RX];
489f9f848faSopenharmony_ci	}
490f9f848faSopenharmony_ci	/* fill out fifo index */
491f9f848faSopenharmony_ci	DPRINTFN(5, "fifo index = %d\n", n);
492f9f848faSopenharmony_ci	cpd->fifo_index = n;
493f9f848faSopenharmony_ci
494f9f848faSopenharmony_ci	/* complete */
495f9f848faSopenharmony_ci
496f9f848faSopenharmony_ci	return (0);
497f9f848faSopenharmony_ci}
498f9f848faSopenharmony_ci
499f9f848faSopenharmony_civoid
500f9f848faSopenharmony_ciusb_fifo_free(struct usb_fifo *f)
501f9f848faSopenharmony_ci{
502f9f848faSopenharmony_ci	uint8_t n;
503f9f848faSopenharmony_ci
504f9f848faSopenharmony_ci	if (f == NULL) {
505f9f848faSopenharmony_ci		/* be NULL safe */
506f9f848faSopenharmony_ci		return;
507f9f848faSopenharmony_ci	}
508f9f848faSopenharmony_ci	/* destroy symlink devices, if any */
509f9f848faSopenharmony_ci	for (n = 0; n != 2; n++) {
510f9f848faSopenharmony_ci		if (f->symlink[n]) {
511f9f848faSopenharmony_ci			usb_free_symlink(f->symlink[n]);
512f9f848faSopenharmony_ci			f->symlink[n] = NULL;
513f9f848faSopenharmony_ci		}
514f9f848faSopenharmony_ci	}
515f9f848faSopenharmony_ci	mtx_lock(&usb_ref_lock);
516f9f848faSopenharmony_ci
517f9f848faSopenharmony_ci	/* delink ourselves to stop calls from userland */
518f9f848faSopenharmony_ci	if ((f->fifo_index < USB_FIFO_MAX) &&
519f9f848faSopenharmony_ci	    (f->udev != NULL) &&
520f9f848faSopenharmony_ci	    (f->udev->fifo[f->fifo_index] == f)) {
521f9f848faSopenharmony_ci		f->udev->fifo[f->fifo_index] = NULL;
522f9f848faSopenharmony_ci	} else {
523f9f848faSopenharmony_ci		DPRINTFN(0, "USB FIFO %p has not been linked\n", f);
524f9f848faSopenharmony_ci	}
525f9f848faSopenharmony_ci
526f9f848faSopenharmony_ci	/* decrease refcount */
527f9f848faSopenharmony_ci	f->refcount--;
528f9f848faSopenharmony_ci	/* need to wait until all callers have exited */
529f9f848faSopenharmony_ci	while (f->refcount != 0) {
530f9f848faSopenharmony_ci		mtx_unlock(&usb_ref_lock);	/* avoid LOR */
531f9f848faSopenharmony_ci		mtx_lock(f->priv_mtx);
532f9f848faSopenharmony_ci		/* prevent write flush, if any */
533f9f848faSopenharmony_ci		f->flag_iserror = 1;
534f9f848faSopenharmony_ci		/* get I/O thread out of any sleep state */
535f9f848faSopenharmony_ci		if (f->flag_sleeping) {
536f9f848faSopenharmony_ci			f->flag_sleeping = 0;
537f9f848faSopenharmony_ci			cv_broadcast(&f->cv_io);
538f9f848faSopenharmony_ci		}
539f9f848faSopenharmony_ci		mtx_unlock(f->priv_mtx);
540f9f848faSopenharmony_ci		mtx_lock(&usb_ref_lock);
541f9f848faSopenharmony_ci
542f9f848faSopenharmony_ci		/*
543f9f848faSopenharmony_ci		 * Check if the "f->refcount" variable reached zero
544f9f848faSopenharmony_ci		 * during the unlocked time before entering wait:
545f9f848faSopenharmony_ci		 */
546f9f848faSopenharmony_ci		if (f->refcount == 0)
547f9f848faSopenharmony_ci			break;
548f9f848faSopenharmony_ci
549f9f848faSopenharmony_ci		/* wait for sync */
550f9f848faSopenharmony_ci		cv_wait(&f->cv_drain, &usb_ref_lock);
551f9f848faSopenharmony_ci	}
552f9f848faSopenharmony_ci	mtx_unlock(&usb_ref_lock);
553f9f848faSopenharmony_ci
554f9f848faSopenharmony_ci	/* take care of closing the device here, if any */
555f9f848faSopenharmony_ci	usb_fifo_close(f, 0);
556f9f848faSopenharmony_ci
557f9f848faSopenharmony_ci	cv_destroy(&f->cv_io);
558f9f848faSopenharmony_ci	cv_destroy(&f->cv_drain);
559f9f848faSopenharmony_ci
560f9f848faSopenharmony_ci	bsd_free(f, M_USBDEV);
561f9f848faSopenharmony_ci}
562f9f848faSopenharmony_ci
563f9f848faSopenharmony_cistatic struct usb_endpoint *
564f9f848faSopenharmony_ciusb_dev_get_ep(struct usb_device *udev, uint8_t ep_index, uint8_t dir)
565f9f848faSopenharmony_ci{
566f9f848faSopenharmony_ci	struct usb_endpoint *ep = NULL;
567f9f848faSopenharmony_ci	uint8_t ep_dir;
568f9f848faSopenharmony_ci
569f9f848faSopenharmony_ci	if (ep_index == 0) {
570f9f848faSopenharmony_ci		ep = &udev->ctrl_ep;
571f9f848faSopenharmony_ci	} else {
572f9f848faSopenharmony_ci		if (dir == USB_FIFO_RX) {
573f9f848faSopenharmony_ci			if (udev->flags.usb_mode == USB_MODE_HOST) {
574f9f848faSopenharmony_ci				ep_dir = UE_DIR_IN;
575f9f848faSopenharmony_ci			} else {
576f9f848faSopenharmony_ci				ep_dir = UE_DIR_OUT;
577f9f848faSopenharmony_ci			}
578f9f848faSopenharmony_ci		} else {
579f9f848faSopenharmony_ci			if (udev->flags.usb_mode == USB_MODE_HOST) {
580f9f848faSopenharmony_ci				ep_dir = UE_DIR_OUT;
581f9f848faSopenharmony_ci			} else {
582f9f848faSopenharmony_ci				ep_dir = UE_DIR_IN;
583f9f848faSopenharmony_ci			}
584f9f848faSopenharmony_ci		}
585f9f848faSopenharmony_ci		ep = usbd_get_ep_by_addr(udev, ep_index | ep_dir);
586f9f848faSopenharmony_ci	}
587f9f848faSopenharmony_ci
588f9f848faSopenharmony_ci	if (ep == NULL) {
589f9f848faSopenharmony_ci		/* if the endpoint does not exist then return */
590f9f848faSopenharmony_ci		return (NULL);
591f9f848faSopenharmony_ci	}
592f9f848faSopenharmony_ci	if (ep->edesc == NULL) {
593f9f848faSopenharmony_ci		/* invalid endpoint */
594f9f848faSopenharmony_ci		return (NULL);
595f9f848faSopenharmony_ci	}
596f9f848faSopenharmony_ci	return (ep);			/* success */
597f9f848faSopenharmony_ci}
598f9f848faSopenharmony_ci
599f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
600f9f848faSopenharmony_ci *	usb_fifo_open
601f9f848faSopenharmony_ci *
602f9f848faSopenharmony_ci * Returns:
603f9f848faSopenharmony_ci * 0: Success
604f9f848faSopenharmony_ci * Else: Failure
605f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
606f9f848faSopenharmony_cistatic int
607f9f848faSopenharmony_ciusb_fifo_open(struct usb_cdev_privdata *cpd,
608f9f848faSopenharmony_ci    struct usb_fifo *f, int fflags)
609f9f848faSopenharmony_ci{
610f9f848faSopenharmony_ci	int err;
611f9f848faSopenharmony_ci
612f9f848faSopenharmony_ci	if (f == NULL) {
613f9f848faSopenharmony_ci		/* no FIFO there */
614f9f848faSopenharmony_ci		DPRINTFN(2, "no FIFO\n");
615f9f848faSopenharmony_ci		return (ENXIO);
616f9f848faSopenharmony_ci	}
617f9f848faSopenharmony_ci	/* remove FWRITE and FREAD flags */
618f9f848faSopenharmony_ci	fflags = (unsigned int)fflags & (~(FWRITE | FREAD));
619f9f848faSopenharmony_ci
620f9f848faSopenharmony_ci	/* set correct file flags */
621f9f848faSopenharmony_ci	if ((f->fifo_index & 1) == USB_FIFO_TX) {
622f9f848faSopenharmony_ci		fflags = (unsigned int)fflags | FWRITE;
623f9f848faSopenharmony_ci	} else {
624f9f848faSopenharmony_ci		fflags = (unsigned int)fflags | FREAD;
625f9f848faSopenharmony_ci	}
626f9f848faSopenharmony_ci
627f9f848faSopenharmony_ci	/* check if we are already opened */
628f9f848faSopenharmony_ci	/* we don't need any locks when checking this variable */
629f9f848faSopenharmony_ci	if (f->curr_cpd != NULL) {
630f9f848faSopenharmony_ci		err = EBUSY;
631f9f848faSopenharmony_ci		goto done;
632f9f848faSopenharmony_ci	}
633f9f848faSopenharmony_ci
634f9f848faSopenharmony_ci	/* reset short flag before open */
635f9f848faSopenharmony_ci	f->flag_short = 0;
636f9f848faSopenharmony_ci
637f9f848faSopenharmony_ci	/* call open method */
638f9f848faSopenharmony_ci	err = (f->methods->f_open) (f, fflags);
639f9f848faSopenharmony_ci	if (err) {
640f9f848faSopenharmony_ci		goto done;
641f9f848faSopenharmony_ci	}
642f9f848faSopenharmony_ci	mtx_lock(f->priv_mtx);
643f9f848faSopenharmony_ci
644f9f848faSopenharmony_ci	/* reset sleep flag */
645f9f848faSopenharmony_ci	f->flag_sleeping = 0;
646f9f848faSopenharmony_ci
647f9f848faSopenharmony_ci	/* reset error flag */
648f9f848faSopenharmony_ci	f->flag_iserror = 0;
649f9f848faSopenharmony_ci
650f9f848faSopenharmony_ci	/* reset complete flag */
651f9f848faSopenharmony_ci	f->flag_iscomplete = 0;
652f9f848faSopenharmony_ci
653f9f848faSopenharmony_ci	/* reset select flag */
654f9f848faSopenharmony_ci	f->flag_isselect = 0;
655f9f848faSopenharmony_ci
656f9f848faSopenharmony_ci	/* reset flushing flag */
657f9f848faSopenharmony_ci	f->flag_flushing = 0;
658f9f848faSopenharmony_ci
659f9f848faSopenharmony_ci	/* reset ASYNC proc flag */
660f9f848faSopenharmony_ci	f->async_p = NULL;
661f9f848faSopenharmony_ci
662f9f848faSopenharmony_ci	mtx_lock(&usb_ref_lock);
663f9f848faSopenharmony_ci	/* flag the fifo as opened to prevent others */
664f9f848faSopenharmony_ci	f->curr_cpd = cpd;
665f9f848faSopenharmony_ci	mtx_unlock(&usb_ref_lock);
666f9f848faSopenharmony_ci
667f9f848faSopenharmony_ci	/* reset queue */
668f9f848faSopenharmony_ci	usb_fifo_reset(f);
669f9f848faSopenharmony_ci
670f9f848faSopenharmony_ci	mtx_unlock(f->priv_mtx);
671f9f848faSopenharmony_cidone:
672f9f848faSopenharmony_ci	return (err);
673f9f848faSopenharmony_ci}
674f9f848faSopenharmony_ci
675f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
676f9f848faSopenharmony_ci *	usb_fifo_reset
677f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
678f9f848faSopenharmony_civoid
679f9f848faSopenharmony_ciusb_fifo_reset(struct usb_fifo *f)
680f9f848faSopenharmony_ci{
681f9f848faSopenharmony_ci	struct usb_mbuf *m = NULL;
682f9f848faSopenharmony_ci
683f9f848faSopenharmony_ci	if (f == NULL) {
684f9f848faSopenharmony_ci		return;
685f9f848faSopenharmony_ci	}
686f9f848faSopenharmony_ci	while (1) {
687f9f848faSopenharmony_ci		USB_IF_DEQUEUE(&f->used_q, m);
688f9f848faSopenharmony_ci		if (m) {
689f9f848faSopenharmony_ci			USB_IF_ENQUEUE(&f->free_q, m);
690f9f848faSopenharmony_ci		} else {
691f9f848faSopenharmony_ci			break;
692f9f848faSopenharmony_ci		}
693f9f848faSopenharmony_ci	}
694f9f848faSopenharmony_ci	/* reset have fragment flag */
695f9f848faSopenharmony_ci	f->flag_have_fragment = 0;
696f9f848faSopenharmony_ci}
697f9f848faSopenharmony_ci
698f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
699f9f848faSopenharmony_ci *	usb_fifo_close
700f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
701f9f848faSopenharmony_cistatic void
702f9f848faSopenharmony_ciusb_fifo_close(struct usb_fifo *f, int fflags)
703f9f848faSopenharmony_ci{
704f9f848faSopenharmony_ci	int err;
705f9f848faSopenharmony_ci
706f9f848faSopenharmony_ci	/* check if we are not opened */
707f9f848faSopenharmony_ci	if (f->curr_cpd == NULL) {
708f9f848faSopenharmony_ci		/* nothing to do - already closed */
709f9f848faSopenharmony_ci		return;
710f9f848faSopenharmony_ci	}
711f9f848faSopenharmony_ci	mtx_lock(f->priv_mtx);
712f9f848faSopenharmony_ci
713f9f848faSopenharmony_ci	/* clear current cdev private data pointer */
714f9f848faSopenharmony_ci	mtx_lock(&usb_ref_lock);
715f9f848faSopenharmony_ci	f->curr_cpd = NULL;
716f9f848faSopenharmony_ci	mtx_unlock(&usb_ref_lock);
717f9f848faSopenharmony_ci
718f9f848faSopenharmony_ci	/* remove FWRITE and FREAD flags */
719f9f848faSopenharmony_ci	fflags = (unsigned int)fflags & (~(FWRITE | FREAD));
720f9f848faSopenharmony_ci
721f9f848faSopenharmony_ci	/* flush written data, if any */
722f9f848faSopenharmony_ci	if ((f->fifo_index & 1) == USB_FIFO_TX) {
723f9f848faSopenharmony_ci		if (!f->flag_iserror) {
724f9f848faSopenharmony_ci			/* set flushing flag */
725f9f848faSopenharmony_ci			f->flag_flushing = 1;
726f9f848faSopenharmony_ci
727f9f848faSopenharmony_ci			/* get the last packet in */
728f9f848faSopenharmony_ci			if (f->flag_have_fragment) {
729f9f848faSopenharmony_ci				struct usb_mbuf *m = NULL;
730f9f848faSopenharmony_ci				f->flag_have_fragment = 0;
731f9f848faSopenharmony_ci				USB_IF_DEQUEUE(&f->free_q, m);
732f9f848faSopenharmony_ci				if (m) {
733f9f848faSopenharmony_ci					USB_IF_ENQUEUE(&f->used_q, m);
734f9f848faSopenharmony_ci				}
735f9f848faSopenharmony_ci			}
736f9f848faSopenharmony_ci
737f9f848faSopenharmony_ci			/* start write transfer, if not already started */
738f9f848faSopenharmony_ci			(f->methods->f_start_write) (f);
739f9f848faSopenharmony_ci
740f9f848faSopenharmony_ci			/* check if flushed already */
741f9f848faSopenharmony_ci			while (f->flag_flushing &&
742f9f848faSopenharmony_ci			    (!f->flag_iserror)) {
743f9f848faSopenharmony_ci				/* wait until all data has been written */
744f9f848faSopenharmony_ci				f->flag_sleeping = 1;
745f9f848faSopenharmony_ci				err = cv_timedwait(&f->cv_io, f->priv_mtx,
746f9f848faSopenharmony_ci					USB_MS_TO_TICKS(USB_DEFAULT_TIMEOUT));
747f9f848faSopenharmony_ci				if (err) {
748f9f848faSopenharmony_ci					DPRINTF("signal received\n");
749f9f848faSopenharmony_ci					break;
750f9f848faSopenharmony_ci				}
751f9f848faSopenharmony_ci			}
752f9f848faSopenharmony_ci		}
753f9f848faSopenharmony_ci		fflags = (unsigned int)fflags | FWRITE;
754f9f848faSopenharmony_ci
755f9f848faSopenharmony_ci		/* stop write transfer, if not already stopped */
756f9f848faSopenharmony_ci		(f->methods->f_stop_write) (f);
757f9f848faSopenharmony_ci	} else {
758f9f848faSopenharmony_ci		fflags = (unsigned int)fflags | FREAD;
759f9f848faSopenharmony_ci
760f9f848faSopenharmony_ci		/* stop write transfer, if not already stopped */
761f9f848faSopenharmony_ci		(f->methods->f_stop_read) (f);
762f9f848faSopenharmony_ci	}
763f9f848faSopenharmony_ci
764f9f848faSopenharmony_ci	/* check if we are sleeping */
765f9f848faSopenharmony_ci	if (f->flag_sleeping) {
766f9f848faSopenharmony_ci		DPRINTFN(2, "Sleeping at close!\n");
767f9f848faSopenharmony_ci	}
768f9f848faSopenharmony_ci	mtx_unlock(f->priv_mtx);
769f9f848faSopenharmony_ci
770f9f848faSopenharmony_ci	/* call close method */
771f9f848faSopenharmony_ci	(f->methods->f_close) (f, fflags);
772f9f848faSopenharmony_ci
773f9f848faSopenharmony_ci	DPRINTF("closed\n");
774f9f848faSopenharmony_ci}
775f9f848faSopenharmony_ci
776f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
777f9f848faSopenharmony_ci *	usb_open - cdev callback
778f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
779f9f848faSopenharmony_cistatic int
780f9f848faSopenharmony_ciusb_open(struct file *filep)
781f9f848faSopenharmony_ci{
782f9f848faSopenharmony_ci	struct drv_data* drvData = (struct drv_data* )filep->f_vnode->data;
783f9f848faSopenharmony_ci	struct usb_fs_privdata* pd = (struct usb_fs_privdata* )drvData->priv;
784f9f848faSopenharmony_ci	struct usb_cdev_refdata refs;
785f9f848faSopenharmony_ci	struct usb_cdev_privdata *cpd = NULL;
786f9f848faSopenharmony_ci	int err;
787f9f848faSopenharmony_ci	int fflags;
788f9f848faSopenharmony_ci
789f9f848faSopenharmony_ci	DPRINTFN(2, "%s fflags=0x%08x\n", filep->f_path, fflags);
790f9f848faSopenharmony_ci
791f9f848faSopenharmony_ci	if (((unsigned int)filep->f_oflags & O_ACCMODE) == O_RDWR) {
792f9f848faSopenharmony_ci		fflags = FREAD | FWRITE;
793f9f848faSopenharmony_ci	} else if (((unsigned int)filep->f_oflags & O_ACCMODE) == O_WRONLY) {
794f9f848faSopenharmony_ci		fflags = FWRITE;
795f9f848faSopenharmony_ci	} else {
796f9f848faSopenharmony_ci		fflags = FREAD;
797f9f848faSopenharmony_ci	}
798f9f848faSopenharmony_ci
799f9f848faSopenharmony_ci	cpd = bsd_malloc(sizeof(*cpd), M_USBDEV, M_WAITOK | M_ZERO);
800f9f848faSopenharmony_ci	if (cpd == NULL) {
801f9f848faSopenharmony_ci		return (-ENOMEM);
802f9f848faSopenharmony_ci	}
803f9f848faSopenharmony_ci
804f9f848faSopenharmony_ci	usb_loc_fill(pd, cpd);
805f9f848faSopenharmony_ci	err = usb_ref_device(cpd, &refs, 1);
806f9f848faSopenharmony_ci	if (err) {
807f9f848faSopenharmony_ci		DPRINTFN(2, "cannot ref device\n");
808f9f848faSopenharmony_ci		bsd_free(cpd, M_USBDEV);
809f9f848faSopenharmony_ci		return (-ENXIO);
810f9f848faSopenharmony_ci	}
811f9f848faSopenharmony_ci	cpd->fflags = fflags;	/* access mode for open lifetime */
812f9f848faSopenharmony_ci
813f9f848faSopenharmony_ci	/* create FIFOs, if any */
814f9f848faSopenharmony_ci	err = usb_fifo_create(cpd, &refs);
815f9f848faSopenharmony_ci	/* check for error */
816f9f848faSopenharmony_ci	if (err) {
817f9f848faSopenharmony_ci		DPRINTFN(2, "cannot create fifo\n");
818f9f848faSopenharmony_ci		usb_unref_device(cpd, &refs);
819f9f848faSopenharmony_ci		bsd_free(cpd, M_USBDEV);
820f9f848faSopenharmony_ci		return (-err);
821f9f848faSopenharmony_ci	}
822f9f848faSopenharmony_ci	if ((unsigned int)fflags & FREAD) {
823f9f848faSopenharmony_ci		err = usb_fifo_open(cpd, refs.rxfifo, fflags);
824f9f848faSopenharmony_ci		if (err) {
825f9f848faSopenharmony_ci			DPRINTFN(2, "read open failed\n");
826f9f848faSopenharmony_ci			usb_unref_device(cpd, &refs);
827f9f848faSopenharmony_ci			bsd_free(cpd, M_USBDEV);
828f9f848faSopenharmony_ci			return (-err);
829f9f848faSopenharmony_ci		}
830f9f848faSopenharmony_ci	}
831f9f848faSopenharmony_ci	if ((unsigned int)fflags & FWRITE) {
832f9f848faSopenharmony_ci		err = usb_fifo_open(cpd, refs.txfifo, fflags);
833f9f848faSopenharmony_ci		if (err) {
834f9f848faSopenharmony_ci			DPRINTFN(2, "write open failed\n");
835f9f848faSopenharmony_ci			if ((unsigned int)fflags & FREAD) {
836f9f848faSopenharmony_ci				usb_fifo_close(refs.rxfifo, fflags);
837f9f848faSopenharmony_ci			}
838f9f848faSopenharmony_ci			usb_unref_device(cpd, &refs);
839f9f848faSopenharmony_ci			bsd_free(cpd, M_USBDEV);
840f9f848faSopenharmony_ci			return (-err);
841f9f848faSopenharmony_ci
842f9f848faSopenharmony_ci		}
843f9f848faSopenharmony_ci	}
844f9f848faSopenharmony_ci	usb_unref_device(cpd, &refs);
845f9f848faSopenharmony_ci	filep->f_priv = cpd;
846f9f848faSopenharmony_ci
847f9f848faSopenharmony_ci	return (0);
848f9f848faSopenharmony_ci}
849f9f848faSopenharmony_ci
850f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
851f9f848faSopenharmony_ci *	usb_close - cdev callback
852f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
853f9f848faSopenharmony_cistatic int
854f9f848faSopenharmony_ciusb_close(struct file *filep)
855f9f848faSopenharmony_ci{
856f9f848faSopenharmony_ci	struct usb_cdev_refdata refs;
857f9f848faSopenharmony_ci	struct usb_cdev_privdata *cpd = (struct usb_cdev_privdata *)filep->f_priv;
858f9f848faSopenharmony_ci	int err;
859f9f848faSopenharmony_ci
860f9f848faSopenharmony_ci	DPRINTFN(2, "cpd=%p\n", cpd);
861f9f848faSopenharmony_ci
862f9f848faSopenharmony_ci	err = usb_ref_device(cpd, &refs,
863f9f848faSopenharmony_ci	    2 /* uref and allow detached state */);
864f9f848faSopenharmony_ci	if (err) {
865f9f848faSopenharmony_ci		DPRINTFN(2, "Cannot grab USB reference when "
866f9f848faSopenharmony_ci		    "closing USB file handle\n");
867f9f848faSopenharmony_ci		return (-ENXIO);
868f9f848faSopenharmony_ci	}
869f9f848faSopenharmony_ci	if ((unsigned int)cpd->fflags & FREAD) {
870f9f848faSopenharmony_ci		usb_fifo_close(refs.rxfifo, cpd->fflags);
871f9f848faSopenharmony_ci	}
872f9f848faSopenharmony_ci	if ((unsigned int)cpd->fflags & FWRITE) {
873f9f848faSopenharmony_ci		usb_fifo_close(refs.txfifo, cpd->fflags);
874f9f848faSopenharmony_ci	}
875f9f848faSopenharmony_ci	usb_unref_device(cpd, &refs);
876f9f848faSopenharmony_ci
877f9f848faSopenharmony_ci	bsd_free(cpd, M_USBDEV);
878f9f848faSopenharmony_ci	return (0);
879f9f848faSopenharmony_ci}
880f9f848faSopenharmony_ci
881f9f848faSopenharmony_civoid
882f9f848faSopenharmony_ciusb_dev_init(void *arg)
883f9f848faSopenharmony_ci{
884f9f848faSopenharmony_ci	int ret;
885f9f848faSopenharmony_ci	mtx_init(&usb_ref_lock, "USB ref mutex", NULL, MTX_DEF);
886f9f848faSopenharmony_ci	ret = mkdir(USB_DEVICE_DIR, DEFAULT_DIR_MODE);
887f9f848faSopenharmony_ci	if (ret < 0) {
888f9f848faSopenharmony_ci		usb_err("usb mkdir error! ret = %d, errono = %d\n", ret, get_errno());
889f9f848faSopenharmony_ci	}
890f9f848faSopenharmony_ci
891f9f848faSopenharmony_ci	sx_init(&usb_sym_lock, "USB sym mutex");
892f9f848faSopenharmony_ci	TAILQ_INIT(&usb_sym_head);
893f9f848faSopenharmony_ci
894f9f848faSopenharmony_ci	/* check the UGEN methods */
895f9f848faSopenharmony_ci	usb_fifo_check_methods(&usb_ugen_methods);
896f9f848faSopenharmony_ci}
897f9f848faSopenharmony_ci
898f9f848faSopenharmony_civoid
899f9f848faSopenharmony_ciusb_dev_uninit(void *arg)
900f9f848faSopenharmony_ci{
901f9f848faSopenharmony_ci	int ret;
902f9f848faSopenharmony_ci	mtx_destroy(&usb_ref_lock);
903f9f848faSopenharmony_ci	sx_destroy(&usb_sym_lock);
904f9f848faSopenharmony_ci	ret = rmdir(USB_DEVICE_DIR);
905f9f848faSopenharmony_ci	if (ret < 0) {
906f9f848faSopenharmony_ci		usb_err("usb rmdir error! ret = %d, errono = %d\n", ret, get_errno());
907f9f848faSopenharmony_ci	}
908f9f848faSopenharmony_ci
909f9f848faSopenharmony_ci}
910f9f848faSopenharmony_ci
911f9f848faSopenharmony_cistatic int
912f9f848faSopenharmony_ciusb_ioctl_f_sub(struct usb_fifo *f, u_long cmd, const void *addr,
913f9f848faSopenharmony_ci    struct thread *td)
914f9f848faSopenharmony_ci{
915f9f848faSopenharmony_ci	int error = 0;
916f9f848faSopenharmony_ci	int data;
917f9f848faSopenharmony_ci
918f9f848faSopenharmony_ci	switch (cmd) {
919f9f848faSopenharmony_ci	case FIONBIO:
920f9f848faSopenharmony_ci		/* handled by upper FS layer */
921f9f848faSopenharmony_ci		break;
922f9f848faSopenharmony_ci
923f9f848faSopenharmony_ci	case FIOASYNC:
924f9f848faSopenharmony_ci		error = copyin((const void *)addr, &data, sizeof(data));
925f9f848faSopenharmony_ci		if (data) {
926f9f848faSopenharmony_ci			if (f->async_p != NULL) {
927f9f848faSopenharmony_ci				error = EBUSY;
928f9f848faSopenharmony_ci				break;
929f9f848faSopenharmony_ci			}
930f9f848faSopenharmony_ci			f->async_p = USB_TD_GET_PROC(td);
931f9f848faSopenharmony_ci		} else {
932f9f848faSopenharmony_ci			f->async_p = NULL;
933f9f848faSopenharmony_ci		}
934f9f848faSopenharmony_ci		break;
935f9f848faSopenharmony_ci
936f9f848faSopenharmony_ci		/* XXX this is not the most general solution */
937f9f848faSopenharmony_ci	case TIOCSPGRP:
938f9f848faSopenharmony_ci		if (f->async_p == NULL) {
939f9f848faSopenharmony_ci			error = EINVAL;
940f9f848faSopenharmony_ci			break;
941f9f848faSopenharmony_ci		}
942f9f848faSopenharmony_ci		error = copyin((const void *)addr, &data, sizeof(data));
943f9f848faSopenharmony_ci		if (data != USB_PROC_GET_GID(f->async_p)) {
944f9f848faSopenharmony_ci			error = EPERM;
945f9f848faSopenharmony_ci			break;
946f9f848faSopenharmony_ci		}
947f9f848faSopenharmony_ci		break;
948f9f848faSopenharmony_ci	default:
949f9f848faSopenharmony_ci		return (ENOIOCTL);
950f9f848faSopenharmony_ci	}
951f9f848faSopenharmony_ci	DPRINTFN(3, "cmd 0x%lx = %d\n", cmd, error);
952f9f848faSopenharmony_ci	return (error);
953f9f848faSopenharmony_ci}
954f9f848faSopenharmony_ci
955f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
956f9f848faSopenharmony_ci *	usb_ioctl - cdev callback
957f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
958f9f848faSopenharmony_cistatic int
959f9f848faSopenharmony_ciusb_ioctl(struct file *filep, int cmd, unsigned long arg)
960f9f848faSopenharmony_ci{
961f9f848faSopenharmony_ci	struct usb_cdev_refdata refs;
962f9f848faSopenharmony_ci	struct usb_cdev_privdata *cpd = (struct usb_cdev_privdata *)filep->f_priv;
963f9f848faSopenharmony_ci	struct usb_fifo *f = NULL;
964f9f848faSopenharmony_ci	int fflags;
965f9f848faSopenharmony_ci	int err;
966f9f848faSopenharmony_ci	caddr_t addr = (caddr_t)(UINTPTR)arg;
967f9f848faSopenharmony_ci
968f9f848faSopenharmony_ci	DPRINTFN(2, "cmd=0x%lx\n", cmd);
969f9f848faSopenharmony_ci
970f9f848faSopenharmony_ci	/*
971f9f848faSopenharmony_ci	 * Performance optimisation: We try to check for IOCTL's that
972f9f848faSopenharmony_ci	 * don't need the USB reference first. Then we grab the USB
973f9f848faSopenharmony_ci	 * reference if we need it!
974f9f848faSopenharmony_ci	 */
975f9f848faSopenharmony_ci	err = usb_ref_device(cpd, &refs, 0 /* no uref */ );
976f9f848faSopenharmony_ci	if (err)
977f9f848faSopenharmony_ci		return (-ENXIO);
978f9f848faSopenharmony_ci
979f9f848faSopenharmony_ci	fflags = cpd->fflags;
980f9f848faSopenharmony_ci
981f9f848faSopenharmony_ci	f = NULL;			/* set default value */
982f9f848faSopenharmony_ci	err = ENOIOCTL;			/* set default value */
983f9f848faSopenharmony_ci
984f9f848faSopenharmony_ci	if ((unsigned int)fflags & FWRITE) {
985f9f848faSopenharmony_ci		f = refs.txfifo;
986f9f848faSopenharmony_ci		err = usb_ioctl_f_sub(f, cmd, addr, NULL);
987f9f848faSopenharmony_ci	}
988f9f848faSopenharmony_ci	if ((unsigned int)fflags & FREAD) {
989f9f848faSopenharmony_ci		f = refs.rxfifo;
990f9f848faSopenharmony_ci		err = usb_ioctl_f_sub(f, cmd, addr, NULL);
991f9f848faSopenharmony_ci	}
992f9f848faSopenharmony_ci	KASSERT(f != NULL, ("fifo not found"));
993f9f848faSopenharmony_ci	if (err != ENOIOCTL)
994f9f848faSopenharmony_ci		goto done;
995f9f848faSopenharmony_ci
996f9f848faSopenharmony_ci	err = (f->methods->f_ioctl) (f, cmd, addr, fflags);
997f9f848faSopenharmony_ci
998f9f848faSopenharmony_ci	DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err);
999f9f848faSopenharmony_ci
1000f9f848faSopenharmony_ci	if (err != ENOIOCTL)
1001f9f848faSopenharmony_ci		goto done;
1002f9f848faSopenharmony_ci
1003f9f848faSopenharmony_ci	if (usb_usb_ref_device(cpd, &refs)) {
1004f9f848faSopenharmony_ci		/* we lost the reference */
1005f9f848faSopenharmony_ci		return (-ENXIO);
1006f9f848faSopenharmony_ci	}
1007f9f848faSopenharmony_ci
1008f9f848faSopenharmony_ci	err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags);
1009f9f848faSopenharmony_ci
1010f9f848faSopenharmony_ci	DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err);
1011f9f848faSopenharmony_ci
1012f9f848faSopenharmony_ci	if (err == ENOIOCTL)
1013f9f848faSopenharmony_ci		err = ENOTTY;
1014f9f848faSopenharmony_ci
1015f9f848faSopenharmony_ci	if (err)
1016f9f848faSopenharmony_ci		goto done;
1017f9f848faSopenharmony_ci
1018f9f848faSopenharmony_ci	/* Wait for re-enumeration, if any */
1019f9f848faSopenharmony_ci
1020f9f848faSopenharmony_ci	while (f->udev->re_enumerate_wait != USB_RE_ENUM_DONE) {
1021f9f848faSopenharmony_ci		usb_unref_device(cpd, &refs);
1022f9f848faSopenharmony_ci
1023f9f848faSopenharmony_ci		usb_pause_mtx(NULL, hz / 128);
1024f9f848faSopenharmony_ci
1025f9f848faSopenharmony_ci		while (usb_ref_device(cpd, &refs, 1 /* need uref */)) {
1026f9f848faSopenharmony_ci			if (usb_ref_device(cpd, &refs, 0)) {
1027f9f848faSopenharmony_ci				/* device no longer exists */
1028f9f848faSopenharmony_ci				return (-ENXIO);
1029f9f848faSopenharmony_ci			}
1030f9f848faSopenharmony_ci			usb_unref_device(cpd, &refs);
1031f9f848faSopenharmony_ci			usb_pause_mtx(NULL, hz / 128);
1032f9f848faSopenharmony_ci		}
1033f9f848faSopenharmony_ci	}
1034f9f848faSopenharmony_ci
1035f9f848faSopenharmony_cidone:
1036f9f848faSopenharmony_ci	usb_unref_device(cpd, &refs);
1037f9f848faSopenharmony_ci	return (-err);
1038f9f848faSopenharmony_ci}
1039f9f848faSopenharmony_ci
1040f9f848faSopenharmony_ci/* ARGSUSED */
1041f9f848faSopenharmony_cistatic int
1042f9f848faSopenharmony_ciusb_poll(struct file *filep, poll_table *fds)
1043f9f848faSopenharmony_ci{
1044f9f848faSopenharmony_ci	struct usb_cdev_refdata refs;
1045f9f848faSopenharmony_ci	struct usb_cdev_privdata *cpd = (struct usb_cdev_privdata *)filep->f_priv;
1046f9f848faSopenharmony_ci	struct usb_fifo *f = NULL;
1047f9f848faSopenharmony_ci	struct usb_mbuf *m = NULL;
1048f9f848faSopenharmony_ci	int fflags, revents;
1049f9f848faSopenharmony_ci	pollevent_t events = fds->key;
1050f9f848faSopenharmony_ci
1051f9f848faSopenharmony_ci	if (usb_ref_device(cpd, &refs, 0) != 0)
1052f9f848faSopenharmony_ci		return (events &
1053f9f848faSopenharmony_ci		    (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM));
1054f9f848faSopenharmony_ci
1055f9f848faSopenharmony_ci	fflags = cpd->fflags;
1056f9f848faSopenharmony_ci
1057f9f848faSopenharmony_ci	/* Figure out who needs service */
1058f9f848faSopenharmony_ci	revents = 0;
1059f9f848faSopenharmony_ci	if ((events & (POLLOUT | POLLWRNORM)) &&
1060f9f848faSopenharmony_ci	    ((unsigned int)fflags & FWRITE)) {
1061f9f848faSopenharmony_ci		f = refs.txfifo;
1062f9f848faSopenharmony_ci
1063f9f848faSopenharmony_ci		mtx_lock(f->priv_mtx);
1064f9f848faSopenharmony_ci
1065f9f848faSopenharmony_ci		if (!refs.is_usbfs) {
1066f9f848faSopenharmony_ci			if (f->flag_iserror) {
1067f9f848faSopenharmony_ci				/* we got an error */
1068f9f848faSopenharmony_ci				m = (void *)1;
1069f9f848faSopenharmony_ci			} else {
1070f9f848faSopenharmony_ci				if (f->queue_data == NULL) {
1071f9f848faSopenharmony_ci					/*
1072f9f848faSopenharmony_ci					 * start write transfer, if not
1073f9f848faSopenharmony_ci					 * already started
1074f9f848faSopenharmony_ci					 */
1075f9f848faSopenharmony_ci					(f->methods->f_start_write) (f);
1076f9f848faSopenharmony_ci				}
1077f9f848faSopenharmony_ci				/* check if any packets are available */
1078f9f848faSopenharmony_ci				USB_IF_POLL(&f->free_q, m);
1079f9f848faSopenharmony_ci			}
1080f9f848faSopenharmony_ci		} else {
1081f9f848faSopenharmony_ci			if (f->flag_iscomplete) {
1082f9f848faSopenharmony_ci				m = (void *)1;
1083f9f848faSopenharmony_ci			} else {
1084f9f848faSopenharmony_ci				m = NULL;
1085f9f848faSopenharmony_ci			}
1086f9f848faSopenharmony_ci		}
1087f9f848faSopenharmony_ci
1088f9f848faSopenharmony_ci		if (m) {
1089f9f848faSopenharmony_ci			revents = (unsigned int)revents | (events & (POLLOUT | POLLWRNORM));
1090f9f848faSopenharmony_ci		} else {
1091f9f848faSopenharmony_ci			f->flag_isselect = 1;
1092f9f848faSopenharmony_ci		}
1093f9f848faSopenharmony_ci
1094f9f848faSopenharmony_ci		mtx_unlock(f->priv_mtx);
1095f9f848faSopenharmony_ci	}
1096f9f848faSopenharmony_ci	if ((events & (POLLIN | POLLRDNORM)) &&
1097f9f848faSopenharmony_ci	    ((unsigned int)fflags & FREAD)) {
1098f9f848faSopenharmony_ci		f = refs.rxfifo;
1099f9f848faSopenharmony_ci
1100f9f848faSopenharmony_ci		mtx_lock(f->priv_mtx);
1101f9f848faSopenharmony_ci
1102f9f848faSopenharmony_ci		if (!refs.is_usbfs) {
1103f9f848faSopenharmony_ci			if (f->flag_iserror) {
1104f9f848faSopenharmony_ci				/* we have an error */
1105f9f848faSopenharmony_ci				m = (void *)1;
1106f9f848faSopenharmony_ci			} else {
1107f9f848faSopenharmony_ci				if (f->queue_data == NULL) {
1108f9f848faSopenharmony_ci					/*
1109f9f848faSopenharmony_ci					 * start read transfer, if not
1110f9f848faSopenharmony_ci					 * already started
1111f9f848faSopenharmony_ci					 */
1112f9f848faSopenharmony_ci
1113f9f848faSopenharmony_ci					(f->methods->f_start_read) (f);
1114f9f848faSopenharmony_ci				}
1115f9f848faSopenharmony_ci
1116f9f848faSopenharmony_ci				/* check if any packets are available */
1117f9f848faSopenharmony_ci				USB_IF_POLL(&f->used_q, m);
1118f9f848faSopenharmony_ci			}
1119f9f848faSopenharmony_ci		} else {
1120f9f848faSopenharmony_ci			if (f->flag_iscomplete) {
1121f9f848faSopenharmony_ci				m = (void *)1;
1122f9f848faSopenharmony_ci			} else {
1123f9f848faSopenharmony_ci				m = NULL;
1124f9f848faSopenharmony_ci			}
1125f9f848faSopenharmony_ci		}
1126f9f848faSopenharmony_ci
1127f9f848faSopenharmony_ci		if (m) {
1128f9f848faSopenharmony_ci			revents = (unsigned int)revents | (events & (POLLIN | POLLRDNORM));
1129f9f848faSopenharmony_ci		} else {
1130f9f848faSopenharmony_ci			f->flag_isselect = 1;
1131f9f848faSopenharmony_ci
1132f9f848faSopenharmony_ci			if (!refs.is_usbfs) {
1133f9f848faSopenharmony_ci
1134f9f848faSopenharmony_ci				/* start reading data */
1135f9f848faSopenharmony_ci				(f->methods->f_start_read) (f);
1136f9f848faSopenharmony_ci			}
1137f9f848faSopenharmony_ci		}
1138f9f848faSopenharmony_ci		mtx_unlock(f->priv_mtx);
1139f9f848faSopenharmony_ci	}
1140f9f848faSopenharmony_ci	usb_unref_device(cpd, &refs);
1141f9f848faSopenharmony_ci
1142f9f848faSopenharmony_ci	return (revents);
1143f9f848faSopenharmony_ci}
1144f9f848faSopenharmony_ci
1145f9f848faSopenharmony_cistatic int
1146f9f848faSopenharmony_ciusb_read(struct file *filep, char *buffer, size_t buflen)
1147f9f848faSopenharmony_ci{
1148f9f848faSopenharmony_ci	struct usb_cdev_refdata refs;
1149f9f848faSopenharmony_ci	struct usb_cdev_privdata *cpd = (struct usb_cdev_privdata *)filep->f_priv;
1150f9f848faSopenharmony_ci	struct usb_fifo *f = NULL;
1151f9f848faSopenharmony_ci	struct usb_mbuf *m = NULL;
1152f9f848faSopenharmony_ci	int resid;
1153f9f848faSopenharmony_ci	int io_len;
1154f9f848faSopenharmony_ci	int err;
1155f9f848faSopenharmony_ci
1156f9f848faSopenharmony_ci	err = usb_ref_device(cpd, &refs, 0 /* no uref */ );
1157f9f848faSopenharmony_ci	if (err)
1158f9f848faSopenharmony_ci		return (-ENXIO);
1159f9f848faSopenharmony_ci
1160f9f848faSopenharmony_ci	f = refs.rxfifo;
1161f9f848faSopenharmony_ci	if (f == NULL) {
1162f9f848faSopenharmony_ci		/* should not happen */
1163f9f848faSopenharmony_ci		usb_unref_device(cpd, &refs);
1164f9f848faSopenharmony_ci		return (-EPERM);
1165f9f848faSopenharmony_ci	}
1166f9f848faSopenharmony_ci
1167f9f848faSopenharmony_ci	resid = buflen;
1168f9f848faSopenharmony_ci
1169f9f848faSopenharmony_ci	mtx_lock(f->priv_mtx);
1170f9f848faSopenharmony_ci
1171f9f848faSopenharmony_ci	/* check for permanent read error */
1172f9f848faSopenharmony_ci	if (f->flag_iserror) {
1173f9f848faSopenharmony_ci		err = EIO;
1174f9f848faSopenharmony_ci		goto done;
1175f9f848faSopenharmony_ci	}
1176f9f848faSopenharmony_ci	/* check if USB-FS interface is active */
1177f9f848faSopenharmony_ci	if (refs.is_usbfs) {
1178f9f848faSopenharmony_ci		/*
1179f9f848faSopenharmony_ci		 * The queue is used for events that should be
1180f9f848faSopenharmony_ci		 * retrieved using the "USB_FS_COMPLETE" ioctl.
1181f9f848faSopenharmony_ci		 */
1182f9f848faSopenharmony_ci		err = EINVAL;
1183f9f848faSopenharmony_ci		goto done;
1184f9f848faSopenharmony_ci	}
1185f9f848faSopenharmony_ci
1186f9f848faSopenharmony_ci	while (resid > 0) {
1187f9f848faSopenharmony_ci		USB_IF_DEQUEUE(&f->used_q, m);
1188f9f848faSopenharmony_ci
1189f9f848faSopenharmony_ci		if (m == NULL) {
1190f9f848faSopenharmony_ci			/* start read transfer, if not already started */
1191f9f848faSopenharmony_ci
1192f9f848faSopenharmony_ci			(f->methods->f_start_read) (f);
1193f9f848faSopenharmony_ci
1194f9f848faSopenharmony_ci			DPRINTF("sleeping\n");
1195f9f848faSopenharmony_ci
1196f9f848faSopenharmony_ci			err = usb_fifo_wait(f);
1197f9f848faSopenharmony_ci			if (err) {
1198f9f848faSopenharmony_ci				break;
1199f9f848faSopenharmony_ci			}
1200f9f848faSopenharmony_ci			continue;
1201f9f848faSopenharmony_ci		}
1202f9f848faSopenharmony_ci		if (f->methods->f_filter_read) {
1203f9f848faSopenharmony_ci			/*
1204f9f848faSopenharmony_ci			 * Sometimes it is convenient to process data at the
1205f9f848faSopenharmony_ci			 * expense of a userland process instead of a kernel
1206f9f848faSopenharmony_ci			 * process.
1207f9f848faSopenharmony_ci			 */
1208f9f848faSopenharmony_ci			(f->methods->f_filter_read) (f, m);
1209f9f848faSopenharmony_ci		}
1210f9f848faSopenharmony_ci
1211f9f848faSopenharmony_ci		io_len = MIN(m->cur_data_len, resid);
1212f9f848faSopenharmony_ci
1213f9f848faSopenharmony_ci		DPRINTFN(2, "transfer %d bytes from %p\n",
1214f9f848faSopenharmony_ci		    io_len, m->cur_data_ptr);
1215f9f848faSopenharmony_ci
1216f9f848faSopenharmony_ci		err = copyout((const void *)m->cur_data_ptr, buffer, io_len);
1217f9f848faSopenharmony_ci		if (err) {
1218f9f848faSopenharmony_ci			break;
1219f9f848faSopenharmony_ci		}
1220f9f848faSopenharmony_ci
1221f9f848faSopenharmony_ci		m->cur_data_len -= io_len;
1222f9f848faSopenharmony_ci		m->cur_data_ptr += io_len;
1223f9f848faSopenharmony_ci
1224f9f848faSopenharmony_ci		if (m->cur_data_len == 0) {
1225f9f848faSopenharmony_ci			uint8_t last_packet;
1226f9f848faSopenharmony_ci
1227f9f848faSopenharmony_ci			last_packet = m->last_packet;
1228f9f848faSopenharmony_ci
1229f9f848faSopenharmony_ci			USB_IF_ENQUEUE(&f->free_q, m);
1230f9f848faSopenharmony_ci
1231f9f848faSopenharmony_ci			if (last_packet) {
1232f9f848faSopenharmony_ci				/* keep framing */
1233f9f848faSopenharmony_ci				break;
1234f9f848faSopenharmony_ci			}
1235f9f848faSopenharmony_ci		} else {
1236f9f848faSopenharmony_ci			USB_IF_PREPEND(&f->used_q, m);
1237f9f848faSopenharmony_ci		}
1238f9f848faSopenharmony_ci
1239f9f848faSopenharmony_ci		if (err) {
1240f9f848faSopenharmony_ci			break;
1241f9f848faSopenharmony_ci		}
1242f9f848faSopenharmony_ci		resid -= io_len;
1243f9f848faSopenharmony_ci	}
1244f9f848faSopenharmony_cidone:
1245f9f848faSopenharmony_ci	mtx_unlock(f->priv_mtx);
1246f9f848faSopenharmony_ci
1247f9f848faSopenharmony_ci	usb_unref_device(cpd, &refs);
1248f9f848faSopenharmony_ci
1249f9f848faSopenharmony_ci	return (-err);
1250f9f848faSopenharmony_ci}
1251f9f848faSopenharmony_ci
1252f9f848faSopenharmony_cistatic int
1253f9f848faSopenharmony_ciusb_write(struct file *filep, const char *buffer, size_t buflen)
1254f9f848faSopenharmony_ci{
1255f9f848faSopenharmony_ci	struct usb_cdev_refdata refs;
1256f9f848faSopenharmony_ci	struct usb_cdev_privdata *cpd = (struct usb_cdev_privdata *)filep->f_priv;
1257f9f848faSopenharmony_ci	struct usb_fifo *f = NULL;
1258f9f848faSopenharmony_ci	struct usb_mbuf *m = NULL;
1259f9f848faSopenharmony_ci	uint8_t *pdata = NULL;
1260f9f848faSopenharmony_ci	int resid;
1261f9f848faSopenharmony_ci	int io_len;
1262f9f848faSopenharmony_ci	int err;
1263f9f848faSopenharmony_ci
1264f9f848faSopenharmony_ci	DPRINTFN(2, "\n");
1265f9f848faSopenharmony_ci
1266f9f848faSopenharmony_ci	err = usb_ref_device(cpd, &refs, 0 /* no uref */ );
1267f9f848faSopenharmony_ci	if (err)
1268f9f848faSopenharmony_ci		return (-ENXIO);
1269f9f848faSopenharmony_ci
1270f9f848faSopenharmony_ci	f = refs.txfifo;
1271f9f848faSopenharmony_ci	if (f == NULL) {
1272f9f848faSopenharmony_ci		/* should not happen */
1273f9f848faSopenharmony_ci		usb_unref_device(cpd, &refs);
1274f9f848faSopenharmony_ci		return (-EPERM);
1275f9f848faSopenharmony_ci	}
1276f9f848faSopenharmony_ci
1277f9f848faSopenharmony_ci	resid = buflen;
1278f9f848faSopenharmony_ci
1279f9f848faSopenharmony_ci	mtx_lock(f->priv_mtx);
1280f9f848faSopenharmony_ci
1281f9f848faSopenharmony_ci	/* check for permanent write error */
1282f9f848faSopenharmony_ci	if (f->flag_iserror) {
1283f9f848faSopenharmony_ci		err = EIO;
1284f9f848faSopenharmony_ci		goto done;
1285f9f848faSopenharmony_ci	}
1286f9f848faSopenharmony_ci	/* check if USB-FS interface is active */
1287f9f848faSopenharmony_ci	if (refs.is_usbfs) {
1288f9f848faSopenharmony_ci		/*
1289f9f848faSopenharmony_ci		 * The queue is used for events that should be
1290f9f848faSopenharmony_ci		 * retrieved using the "USB_FS_COMPLETE" ioctl.
1291f9f848faSopenharmony_ci		 */
1292f9f848faSopenharmony_ci		err = EINVAL;
1293f9f848faSopenharmony_ci		goto done;
1294f9f848faSopenharmony_ci	}
1295f9f848faSopenharmony_ci	if (f->queue_data == NULL) {
1296f9f848faSopenharmony_ci		/* start write transfer, if not already started */
1297f9f848faSopenharmony_ci		(f->methods->f_start_write) (f);
1298f9f848faSopenharmony_ci	}
1299f9f848faSopenharmony_ci	/* we allow writing zero length data */
1300f9f848faSopenharmony_ci	do {
1301f9f848faSopenharmony_ci		USB_IF_DEQUEUE(&f->free_q, m);
1302f9f848faSopenharmony_ci
1303f9f848faSopenharmony_ci		if (m == NULL) {
1304f9f848faSopenharmony_ci			DPRINTF("sleeping\n");
1305f9f848faSopenharmony_ci
1306f9f848faSopenharmony_ci			err = usb_fifo_wait(f);
1307f9f848faSopenharmony_ci			if (err) {
1308f9f848faSopenharmony_ci				break;
1309f9f848faSopenharmony_ci			}
1310f9f848faSopenharmony_ci			continue;
1311f9f848faSopenharmony_ci		}
1312f9f848faSopenharmony_ci
1313f9f848faSopenharmony_ci		if (f->flag_have_fragment == 0) {
1314f9f848faSopenharmony_ci			USB_MBUF_RESET(m);
1315f9f848faSopenharmony_ci			io_len = m->cur_data_len;
1316f9f848faSopenharmony_ci			pdata = m->cur_data_ptr;
1317f9f848faSopenharmony_ci			if (io_len > resid)
1318f9f848faSopenharmony_ci				io_len = resid;
1319f9f848faSopenharmony_ci			m->cur_data_len = io_len;
1320f9f848faSopenharmony_ci		} else {
1321f9f848faSopenharmony_ci			io_len = m->max_data_len - m->cur_data_len;
1322f9f848faSopenharmony_ci			pdata = m->cur_data_ptr + m->cur_data_len;
1323f9f848faSopenharmony_ci			if (io_len > resid)
1324f9f848faSopenharmony_ci				io_len = resid;
1325f9f848faSopenharmony_ci			m->cur_data_len += io_len;
1326f9f848faSopenharmony_ci		}
1327f9f848faSopenharmony_ci
1328f9f848faSopenharmony_ci		DPRINTFN(2, "transfer %d bytes to %p\n",
1329f9f848faSopenharmony_ci		    io_len, pdata);
1330f9f848faSopenharmony_ci
1331f9f848faSopenharmony_ci		err = copyin(buffer, pdata, io_len);
1332f9f848faSopenharmony_ci		if (err) {
1333f9f848faSopenharmony_ci			f->flag_have_fragment = 0;
1334f9f848faSopenharmony_ci			USB_IF_ENQUEUE(&f->free_q, m);
1335f9f848faSopenharmony_ci			break;
1336f9f848faSopenharmony_ci		}
1337f9f848faSopenharmony_ci
1338f9f848faSopenharmony_ci		/* check if the buffer is ready to be transmitted */
1339f9f848faSopenharmony_ci
1340f9f848faSopenharmony_ci		if ((f->flag_write_defrag == 0) ||
1341f9f848faSopenharmony_ci		    (m->cur_data_len == m->max_data_len)) {
1342f9f848faSopenharmony_ci			f->flag_have_fragment = 0;
1343f9f848faSopenharmony_ci
1344f9f848faSopenharmony_ci			/*
1345f9f848faSopenharmony_ci			 * Check for write filter:
1346f9f848faSopenharmony_ci			 *
1347f9f848faSopenharmony_ci			 * Sometimes it is convenient to process data
1348f9f848faSopenharmony_ci			 * at the expense of a userland process
1349f9f848faSopenharmony_ci			 * instead of a kernel process.
1350f9f848faSopenharmony_ci			 */
1351f9f848faSopenharmony_ci			if (f->methods->f_filter_write) {
1352f9f848faSopenharmony_ci				(f->methods->f_filter_write) (f, m);
1353f9f848faSopenharmony_ci			}
1354f9f848faSopenharmony_ci
1355f9f848faSopenharmony_ci			/* Put USB mbuf in the used queue */
1356f9f848faSopenharmony_ci			USB_IF_ENQUEUE(&f->used_q, m);
1357f9f848faSopenharmony_ci
1358f9f848faSopenharmony_ci			/* Start writing data, if not already started */
1359f9f848faSopenharmony_ci			(f->methods->f_start_write) (f);
1360f9f848faSopenharmony_ci		} else {
1361f9f848faSopenharmony_ci			/* Wait for more data or close */
1362f9f848faSopenharmony_ci			f->flag_have_fragment = 1;
1363f9f848faSopenharmony_ci			USB_IF_PREPEND(&f->free_q, m);
1364f9f848faSopenharmony_ci		}
1365f9f848faSopenharmony_ci		resid -= io_len;
1366f9f848faSopenharmony_ci	} while (resid > 0);
1367f9f848faSopenharmony_cidone:
1368f9f848faSopenharmony_ci	mtx_unlock(f->priv_mtx);
1369f9f848faSopenharmony_ci
1370f9f848faSopenharmony_ci	usb_unref_device(cpd, &refs);
1371f9f848faSopenharmony_ci
1372f9f848faSopenharmony_ci	return (-err);
1373f9f848faSopenharmony_ci}
1374f9f848faSopenharmony_ci
1375f9f848faSopenharmony_ciint
1376f9f848faSopenharmony_ciusb_fifo_wait(struct usb_fifo *f)
1377f9f848faSopenharmony_ci{
1378f9f848faSopenharmony_ci	int err;
1379f9f848faSopenharmony_ci
1380f9f848faSopenharmony_ci	mtx_assert(f->priv_mtx, MA_OWNED);
1381f9f848faSopenharmony_ci
1382f9f848faSopenharmony_ci	if (f->flag_iserror) {
1383f9f848faSopenharmony_ci		/* we are gone */
1384f9f848faSopenharmony_ci		return (EIO);
1385f9f848faSopenharmony_ci	}
1386f9f848faSopenharmony_ci	f->flag_sleeping = 1;
1387f9f848faSopenharmony_ci
1388f9f848faSopenharmony_ci	err = cv_wait(&f->cv_io, f->priv_mtx);
1389f9f848faSopenharmony_ci
1390f9f848faSopenharmony_ci	if (f->flag_iserror) {
1391f9f848faSopenharmony_ci		/* we are gone */
1392f9f848faSopenharmony_ci		err = EIO;
1393f9f848faSopenharmony_ci	}
1394f9f848faSopenharmony_ci	return (err);
1395f9f848faSopenharmony_ci}
1396f9f848faSopenharmony_ci
1397f9f848faSopenharmony_civoid
1398f9f848faSopenharmony_ciusb_fifo_signal(struct usb_fifo *f)
1399f9f848faSopenharmony_ci{
1400f9f848faSopenharmony_ci	if (f->flag_sleeping) {
1401f9f848faSopenharmony_ci		f->flag_sleeping = 0;
1402f9f848faSopenharmony_ci		cv_broadcast(&f->cv_io);
1403f9f848faSopenharmony_ci	}
1404f9f848faSopenharmony_ci}
1405f9f848faSopenharmony_ci
1406f9f848faSopenharmony_civoid
1407f9f848faSopenharmony_ciusb_fifo_wakeup(struct usb_fifo *f)
1408f9f848faSopenharmony_ci{
1409f9f848faSopenharmony_ci	usb_fifo_signal(f);
1410f9f848faSopenharmony_ci}
1411f9f848faSopenharmony_ci
1412f9f848faSopenharmony_cistatic int
1413f9f848faSopenharmony_ciusb_fifo_dummy_open(struct usb_fifo *fifo, int fflags)
1414f9f848faSopenharmony_ci{
1415f9f848faSopenharmony_ci	return (0);
1416f9f848faSopenharmony_ci}
1417f9f848faSopenharmony_ci
1418f9f848faSopenharmony_cistatic void
1419f9f848faSopenharmony_ciusb_fifo_dummy_close(struct usb_fifo *fifo, int fflags)
1420f9f848faSopenharmony_ci{
1421f9f848faSopenharmony_ci	return;
1422f9f848faSopenharmony_ci}
1423f9f848faSopenharmony_ci
1424f9f848faSopenharmony_cistatic int
1425f9f848faSopenharmony_ciusb_fifo_dummy_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
1426f9f848faSopenharmony_ci{
1427f9f848faSopenharmony_ci	return (ENOIOCTL);
1428f9f848faSopenharmony_ci}
1429f9f848faSopenharmony_ci
1430f9f848faSopenharmony_cistatic void
1431f9f848faSopenharmony_ciusb_fifo_dummy_cmd(struct usb_fifo *fifo)
1432f9f848faSopenharmony_ci{
1433f9f848faSopenharmony_ci	fifo->flag_flushing = 0;	/* not flushing */
1434f9f848faSopenharmony_ci}
1435f9f848faSopenharmony_ci
1436f9f848faSopenharmony_cistatic void
1437f9f848faSopenharmony_ciusb_fifo_check_methods(struct usb_fifo_methods *pm)
1438f9f848faSopenharmony_ci{
1439f9f848faSopenharmony_ci	/* check that all callback functions are OK */
1440f9f848faSopenharmony_ci
1441f9f848faSopenharmony_ci	if (pm->f_open == NULL)
1442f9f848faSopenharmony_ci		pm->f_open = &usb_fifo_dummy_open;
1443f9f848faSopenharmony_ci
1444f9f848faSopenharmony_ci	if (pm->f_close == NULL)
1445f9f848faSopenharmony_ci		pm->f_close = &usb_fifo_dummy_close;
1446f9f848faSopenharmony_ci
1447f9f848faSopenharmony_ci	if (pm->f_ioctl == NULL)
1448f9f848faSopenharmony_ci		pm->f_ioctl = &usb_fifo_dummy_ioctl;
1449f9f848faSopenharmony_ci
1450f9f848faSopenharmony_ci	if (pm->f_ioctl_post == NULL)
1451f9f848faSopenharmony_ci		pm->f_ioctl_post = &usb_fifo_dummy_ioctl;
1452f9f848faSopenharmony_ci
1453f9f848faSopenharmony_ci	if (pm->f_start_read == NULL)
1454f9f848faSopenharmony_ci		pm->f_start_read = &usb_fifo_dummy_cmd;
1455f9f848faSopenharmony_ci
1456f9f848faSopenharmony_ci	if (pm->f_stop_read == NULL)
1457f9f848faSopenharmony_ci		pm->f_stop_read = &usb_fifo_dummy_cmd;
1458f9f848faSopenharmony_ci
1459f9f848faSopenharmony_ci	if (pm->f_start_write == NULL)
1460f9f848faSopenharmony_ci		pm->f_start_write = &usb_fifo_dummy_cmd;
1461f9f848faSopenharmony_ci
1462f9f848faSopenharmony_ci	if (pm->f_stop_write == NULL)
1463f9f848faSopenharmony_ci		pm->f_stop_write = &usb_fifo_dummy_cmd;
1464f9f848faSopenharmony_ci}
1465f9f848faSopenharmony_ci
1466f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
1467f9f848faSopenharmony_ci *	usb_fifo_attach
1468f9f848faSopenharmony_ci *
1469f9f848faSopenharmony_ci * The following function will create a duplex FIFO.
1470f9f848faSopenharmony_ci *
1471f9f848faSopenharmony_ci * Return values:
1472f9f848faSopenharmony_ci * 0: Success.
1473f9f848faSopenharmony_ci * Else: Failure.
1474f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
1475f9f848faSopenharmony_ciint
1476f9f848faSopenharmony_ciusb_fifo_attach(struct usb_device *udev, void *priv_sc,
1477f9f848faSopenharmony_ci    struct mtx *priv_mtx, struct usb_fifo_methods *pm,
1478f9f848faSopenharmony_ci    struct usb_fifo_sc *f_sc, uint16_t unit, int16_t subunit,
1479f9f848faSopenharmony_ci    uint8_t iface_index, uid_t uid, gid_t gid, int mode)
1480f9f848faSopenharmony_ci{
1481f9f848faSopenharmony_ci	struct usb_fifo *f_tx = NULL;
1482f9f848faSopenharmony_ci	struct usb_fifo *f_rx = NULL;
1483f9f848faSopenharmony_ci	char devname[32];
1484f9f848faSopenharmony_ci	uint8_t n;
1485f9f848faSopenharmony_ci
1486f9f848faSopenharmony_ci	f_sc->fp[USB_FIFO_TX] = NULL;
1487f9f848faSopenharmony_ci	f_sc->fp[USB_FIFO_RX] = NULL;
1488f9f848faSopenharmony_ci
1489f9f848faSopenharmony_ci	if (pm == NULL)
1490f9f848faSopenharmony_ci		return (EINVAL);
1491f9f848faSopenharmony_ci
1492f9f848faSopenharmony_ci	/* check the methods */
1493f9f848faSopenharmony_ci	usb_fifo_check_methods(pm);
1494f9f848faSopenharmony_ci
1495f9f848faSopenharmony_ci	if (priv_mtx == NULL)
1496f9f848faSopenharmony_ci		priv_mtx = &Giant;
1497f9f848faSopenharmony_ci
1498f9f848faSopenharmony_ci	/* search for a free FIFO slot */
1499f9f848faSopenharmony_ci	for (n = 0;; n += 2) {
1500f9f848faSopenharmony_ci		if (n == USB_FIFO_MAX) {
1501f9f848faSopenharmony_ci			/* end of FIFOs reached */
1502f9f848faSopenharmony_ci			return (ENOMEM);
1503f9f848faSopenharmony_ci		}
1504f9f848faSopenharmony_ci		/* Check for TX FIFO */
1505f9f848faSopenharmony_ci		if (udev->fifo[n + USB_FIFO_TX] != NULL) {
1506f9f848faSopenharmony_ci			continue;
1507f9f848faSopenharmony_ci		}
1508f9f848faSopenharmony_ci		/* Check for RX FIFO */
1509f9f848faSopenharmony_ci		if (udev->fifo[n + USB_FIFO_RX] != NULL) {
1510f9f848faSopenharmony_ci			continue;
1511f9f848faSopenharmony_ci		}
1512f9f848faSopenharmony_ci		break;
1513f9f848faSopenharmony_ci	}
1514f9f848faSopenharmony_ci
1515f9f848faSopenharmony_ci	f_tx = usb_fifo_alloc(priv_mtx);
1516f9f848faSopenharmony_ci	f_rx = usb_fifo_alloc(priv_mtx);
1517f9f848faSopenharmony_ci
1518f9f848faSopenharmony_ci	if ((f_tx == NULL) || (f_rx == NULL)) {
1519f9f848faSopenharmony_ci		usb_fifo_free(f_tx);
1520f9f848faSopenharmony_ci		usb_fifo_free(f_rx);
1521f9f848faSopenharmony_ci		return (ENOMEM);
1522f9f848faSopenharmony_ci	}
1523f9f848faSopenharmony_ci	/* initialise FIFO structures */
1524f9f848faSopenharmony_ci
1525f9f848faSopenharmony_ci	f_tx->fifo_index = n + USB_FIFO_TX;
1526f9f848faSopenharmony_ci	f_tx->dev_ep_index = -1;
1527f9f848faSopenharmony_ci	f_tx->priv_sc0 = priv_sc;
1528f9f848faSopenharmony_ci	f_tx->methods = pm;
1529f9f848faSopenharmony_ci	f_tx->iface_index = iface_index;
1530f9f848faSopenharmony_ci	f_tx->udev = udev;
1531f9f848faSopenharmony_ci
1532f9f848faSopenharmony_ci	f_rx->fifo_index = n + USB_FIFO_RX;
1533f9f848faSopenharmony_ci	f_rx->dev_ep_index = -1;
1534f9f848faSopenharmony_ci	f_rx->priv_sc0 = priv_sc;
1535f9f848faSopenharmony_ci	f_rx->methods = pm;
1536f9f848faSopenharmony_ci	f_rx->iface_index = iface_index;
1537f9f848faSopenharmony_ci	f_rx->udev = udev;
1538f9f848faSopenharmony_ci
1539f9f848faSopenharmony_ci	f_sc->fp[USB_FIFO_TX] = f_tx;
1540f9f848faSopenharmony_ci	f_sc->fp[USB_FIFO_RX] = f_rx;
1541f9f848faSopenharmony_ci
1542f9f848faSopenharmony_ci	mtx_lock(&usb_ref_lock);
1543f9f848faSopenharmony_ci	udev->fifo[f_tx->fifo_index] = f_tx;
1544f9f848faSopenharmony_ci	udev->fifo[f_rx->fifo_index] = f_rx;
1545f9f848faSopenharmony_ci	mtx_unlock(&usb_ref_lock);
1546f9f848faSopenharmony_ci
1547f9f848faSopenharmony_ci	for (n = 0; n != 4; n++) {
1548f9f848faSopenharmony_ci		if (pm->basename[n] == NULL) {
1549f9f848faSopenharmony_ci			continue;
1550f9f848faSopenharmony_ci		}
1551f9f848faSopenharmony_ci		if (subunit < 0) {
1552f9f848faSopenharmony_ci			if (snprintf_s(devname, sizeof(devname), sizeof(devname) - 1,
1553f9f848faSopenharmony_ci			    "%s%u%s", pm->basename[n],
1554f9f848faSopenharmony_ci			    unit, pm->postfix[n] ?
1555f9f848faSopenharmony_ci			    pm->postfix[n] : "")) {
1556f9f848faSopenharmony_ci				/* ignore */
1557f9f848faSopenharmony_ci			}
1558f9f848faSopenharmony_ci		} else {
1559f9f848faSopenharmony_ci			if (snprintf_s(devname, sizeof(devname), sizeof(devname) - 1,
1560f9f848faSopenharmony_ci			    "%s%u.%d%s", pm->basename[n],
1561f9f848faSopenharmony_ci			    unit, subunit, pm->postfix[n] ?
1562f9f848faSopenharmony_ci			    pm->postfix[n] : "")) {
1563f9f848faSopenharmony_ci				/* ignore */
1564f9f848faSopenharmony_ci			}
1565f9f848faSopenharmony_ci		}
1566f9f848faSopenharmony_ci
1567f9f848faSopenharmony_ci		/*
1568f9f848faSopenharmony_ci		 * Distribute the symbolic links into two FIFO structures:
1569f9f848faSopenharmony_ci		 */
1570f9f848faSopenharmony_ci		if (n & 1) {
1571f9f848faSopenharmony_ci			f_rx->symlink[n / 2] =
1572f9f848faSopenharmony_ci			    usb_alloc_symlink(devname);
1573f9f848faSopenharmony_ci		} else {
1574f9f848faSopenharmony_ci			f_tx->symlink[n / 2] =
1575f9f848faSopenharmony_ci			    usb_alloc_symlink(devname);
1576f9f848faSopenharmony_ci		}
1577f9f848faSopenharmony_ci
1578f9f848faSopenharmony_ci		/* Create the device */
1579f9f848faSopenharmony_ci		f_sc->dev = usb_make_dev(udev, devname, -1,
1580f9f848faSopenharmony_ci		    f_tx->fifo_index & f_rx->fifo_index,
1581f9f848faSopenharmony_ci		    FREAD|FWRITE, uid, gid, mode);
1582f9f848faSopenharmony_ci	}
1583f9f848faSopenharmony_ci
1584f9f848faSopenharmony_ci	DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx);
1585f9f848faSopenharmony_ci	return (0);
1586f9f848faSopenharmony_ci}
1587f9f848faSopenharmony_ci
1588f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
1589f9f848faSopenharmony_ci *	usb_fifo_alloc_buffer
1590f9f848faSopenharmony_ci *
1591f9f848faSopenharmony_ci * Return values:
1592f9f848faSopenharmony_ci * 0: Success
1593f9f848faSopenharmony_ci * Else failure
1594f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
1595f9f848faSopenharmony_ciint
1596f9f848faSopenharmony_ciusb_fifo_alloc_buffer(struct usb_fifo *f, usb_size_t bufsize,
1597f9f848faSopenharmony_ci    uint16_t nbuf)
1598f9f848faSopenharmony_ci{
1599f9f848faSopenharmony_ci	struct usb_ifqueue temp_q = {};
1600f9f848faSopenharmony_ci	void *queue_data;
1601f9f848faSopenharmony_ci
1602f9f848faSopenharmony_ci	usb_fifo_free_buffer(f);
1603f9f848faSopenharmony_ci
1604f9f848faSopenharmony_ci	temp_q.ifq_maxlen = nbuf;
1605f9f848faSopenharmony_ci
1606f9f848faSopenharmony_ci	queue_data = usb_alloc_mbufs(
1607f9f848faSopenharmony_ci	    M_USBDEV, &temp_q, bufsize, nbuf);
1608f9f848faSopenharmony_ci
1609f9f848faSopenharmony_ci	if (queue_data == NULL && bufsize != 0 && nbuf != 0)
1610f9f848faSopenharmony_ci		return (ENOMEM);
1611f9f848faSopenharmony_ci
1612f9f848faSopenharmony_ci	mtx_lock(f->priv_mtx);
1613f9f848faSopenharmony_ci
1614f9f848faSopenharmony_ci	/*
1615f9f848faSopenharmony_ci	 * Setup queues and sizes under lock to avoid early use by
1616f9f848faSopenharmony_ci	 * concurrent FIFO access:
1617f9f848faSopenharmony_ci	 */
1618f9f848faSopenharmony_ci	f->free_q = temp_q;
1619f9f848faSopenharmony_ci	f->used_q.ifq_maxlen = nbuf;
1620f9f848faSopenharmony_ci	f->queue_data = queue_data;
1621f9f848faSopenharmony_ci	mtx_unlock(f->priv_mtx);
1622f9f848faSopenharmony_ci
1623f9f848faSopenharmony_ci	return (0);			/* success */
1624f9f848faSopenharmony_ci}
1625f9f848faSopenharmony_ci
1626f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
1627f9f848faSopenharmony_ci *	usb_fifo_free_buffer
1628f9f848faSopenharmony_ci *
1629f9f848faSopenharmony_ci * This function will free the buffers associated with a FIFO. This
1630f9f848faSopenharmony_ci * function can be called multiple times in a row.
1631f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
1632f9f848faSopenharmony_civoid
1633f9f848faSopenharmony_ciusb_fifo_free_buffer(struct usb_fifo *f)
1634f9f848faSopenharmony_ci{
1635f9f848faSopenharmony_ci	void *queue_data;
1636f9f848faSopenharmony_ci
1637f9f848faSopenharmony_ci	mtx_lock(f->priv_mtx);
1638f9f848faSopenharmony_ci
1639f9f848faSopenharmony_ci	/* Get and clear pointer to free, if any. */
1640f9f848faSopenharmony_ci	queue_data = f->queue_data;
1641f9f848faSopenharmony_ci	f->queue_data = NULL;
1642f9f848faSopenharmony_ci
1643f9f848faSopenharmony_ci	/*
1644f9f848faSopenharmony_ci	 * Reset queues under lock to avoid use of freed buffers by
1645f9f848faSopenharmony_ci	 * concurrent FIFO activity:
1646f9f848faSopenharmony_ci	 */
1647f9f848faSopenharmony_ci	memset(&f->free_q, 0, sizeof(f->free_q));
1648f9f848faSopenharmony_ci	memset(&f->used_q, 0, sizeof(f->used_q));
1649f9f848faSopenharmony_ci	mtx_unlock(f->priv_mtx);
1650f9f848faSopenharmony_ci
1651f9f848faSopenharmony_ci	/* Free old buffer, if any. */
1652f9f848faSopenharmony_ci	bsd_free(queue_data, M_USBDEV);
1653f9f848faSopenharmony_ci}
1654f9f848faSopenharmony_ci
1655f9f848faSopenharmony_civoid
1656f9f848faSopenharmony_ciusb_fifo_detach(struct usb_fifo_sc *f_sc)
1657f9f848faSopenharmony_ci{
1658f9f848faSopenharmony_ci	if (f_sc == NULL) {
1659f9f848faSopenharmony_ci		return;
1660f9f848faSopenharmony_ci	}
1661f9f848faSopenharmony_ci	usb_fifo_free(f_sc->fp[USB_FIFO_TX]);
1662f9f848faSopenharmony_ci	usb_fifo_free(f_sc->fp[USB_FIFO_RX]);
1663f9f848faSopenharmony_ci
1664f9f848faSopenharmony_ci	f_sc->fp[USB_FIFO_TX] = NULL;
1665f9f848faSopenharmony_ci	f_sc->fp[USB_FIFO_RX] = NULL;
1666f9f848faSopenharmony_ci
1667f9f848faSopenharmony_ci	usb_destroy_dev(f_sc->dev);
1668f9f848faSopenharmony_ci
1669f9f848faSopenharmony_ci	f_sc->dev = NULL;
1670f9f848faSopenharmony_ci
1671f9f848faSopenharmony_ci	DPRINTFN(2, "detached %p\n", f_sc);
1672f9f848faSopenharmony_ci}
1673f9f848faSopenharmony_ci
1674f9f848faSopenharmony_ciusb_size_t
1675f9f848faSopenharmony_ciusb_fifo_put_bytes_max(struct usb_fifo *f)
1676f9f848faSopenharmony_ci{
1677f9f848faSopenharmony_ci	struct usb_mbuf *m = NULL;
1678f9f848faSopenharmony_ci	usb_size_t len;
1679f9f848faSopenharmony_ci
1680f9f848faSopenharmony_ci	USB_IF_POLL(&f->free_q, m);
1681f9f848faSopenharmony_ci
1682f9f848faSopenharmony_ci	if (m) {
1683f9f848faSopenharmony_ci		len = m->max_data_len;
1684f9f848faSopenharmony_ci	} else {
1685f9f848faSopenharmony_ci		len = 0;
1686f9f848faSopenharmony_ci	}
1687f9f848faSopenharmony_ci	return (len);
1688f9f848faSopenharmony_ci}
1689f9f848faSopenharmony_ci
1690f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
1691f9f848faSopenharmony_ci *	usb_fifo_put_data
1692f9f848faSopenharmony_ci *
1693f9f848faSopenharmony_ci * what:
1694f9f848faSopenharmony_ci *  0 - normal operation
1695f9f848faSopenharmony_ci *  1 - set last packet flag to enforce framing
1696f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
1697f9f848faSopenharmony_civoid
1698f9f848faSopenharmony_ciusb_fifo_put_data(struct usb_fifo *f, struct usb_page_cache *pc,
1699f9f848faSopenharmony_ci    usb_frlength_t offset, usb_frlength_t len, uint8_t what)
1700f9f848faSopenharmony_ci{
1701f9f848faSopenharmony_ci	struct usb_mbuf *m = NULL;
1702f9f848faSopenharmony_ci	usb_frlength_t io_len;
1703f9f848faSopenharmony_ci
1704f9f848faSopenharmony_ci	while (len || (what == 1)) {
1705f9f848faSopenharmony_ci		USB_IF_DEQUEUE(&f->free_q, m);
1706f9f848faSopenharmony_ci
1707f9f848faSopenharmony_ci		if (m) {
1708f9f848faSopenharmony_ci			USB_MBUF_RESET(m);
1709f9f848faSopenharmony_ci
1710f9f848faSopenharmony_ci			io_len = MIN(len, m->cur_data_len);
1711f9f848faSopenharmony_ci
1712f9f848faSopenharmony_ci			usbd_copy_out(pc, offset, m->cur_data_ptr, io_len);
1713f9f848faSopenharmony_ci
1714f9f848faSopenharmony_ci			m->cur_data_len = io_len;
1715f9f848faSopenharmony_ci			offset += io_len;
1716f9f848faSopenharmony_ci			len -= io_len;
1717f9f848faSopenharmony_ci
1718f9f848faSopenharmony_ci			if ((len == 0) && (what == 1)) {
1719f9f848faSopenharmony_ci				m->last_packet = 1;
1720f9f848faSopenharmony_ci			}
1721f9f848faSopenharmony_ci			USB_IF_ENQUEUE(&f->used_q, m);
1722f9f848faSopenharmony_ci
1723f9f848faSopenharmony_ci			usb_fifo_wakeup(f);
1724f9f848faSopenharmony_ci
1725f9f848faSopenharmony_ci			if ((len == 0) || (what == 1)) {
1726f9f848faSopenharmony_ci				break;
1727f9f848faSopenharmony_ci			}
1728f9f848faSopenharmony_ci		} else {
1729f9f848faSopenharmony_ci			break;
1730f9f848faSopenharmony_ci		}
1731f9f848faSopenharmony_ci	}
1732f9f848faSopenharmony_ci}
1733f9f848faSopenharmony_ci
1734f9f848faSopenharmony_civoid
1735f9f848faSopenharmony_ciusb_fifo_put_data_linear(struct usb_fifo *f, void *ptr,
1736f9f848faSopenharmony_ci    usb_size_t len, uint8_t what)
1737f9f848faSopenharmony_ci{
1738f9f848faSopenharmony_ci	struct usb_mbuf *m = NULL;
1739f9f848faSopenharmony_ci	usb_size_t io_len;
1740f9f848faSopenharmony_ci	int error;
1741f9f848faSopenharmony_ci
1742f9f848faSopenharmony_ci	while (len || (what == 1)) {
1743f9f848faSopenharmony_ci		USB_IF_DEQUEUE(&f->free_q, m);
1744f9f848faSopenharmony_ci
1745f9f848faSopenharmony_ci		if (m) {
1746f9f848faSopenharmony_ci			USB_MBUF_RESET(m);
1747f9f848faSopenharmony_ci
1748f9f848faSopenharmony_ci			io_len = MIN(len, m->cur_data_len);
1749f9f848faSopenharmony_ci
1750f9f848faSopenharmony_ci			error = memcpy_s(m->cur_data_ptr, io_len, ptr, io_len);
1751f9f848faSopenharmony_ci			if (error != EOK) {
1752f9f848faSopenharmony_ci				break;
1753f9f848faSopenharmony_ci			}
1754f9f848faSopenharmony_ci
1755f9f848faSopenharmony_ci			m->cur_data_len = io_len;
1756f9f848faSopenharmony_ci			ptr = USB_ADD_BYTES(ptr, io_len);
1757f9f848faSopenharmony_ci			len -= io_len;
1758f9f848faSopenharmony_ci
1759f9f848faSopenharmony_ci			if ((len == 0) && (what == 1)) {
1760f9f848faSopenharmony_ci				m->last_packet = 1;
1761f9f848faSopenharmony_ci			}
1762f9f848faSopenharmony_ci			USB_IF_ENQUEUE(&f->used_q, m);
1763f9f848faSopenharmony_ci
1764f9f848faSopenharmony_ci			usb_fifo_wakeup(f);
1765f9f848faSopenharmony_ci
1766f9f848faSopenharmony_ci			if ((len == 0) || (what == 1)) {
1767f9f848faSopenharmony_ci				break;
1768f9f848faSopenharmony_ci			}
1769f9f848faSopenharmony_ci		} else {
1770f9f848faSopenharmony_ci			break;
1771f9f848faSopenharmony_ci		}
1772f9f848faSopenharmony_ci	}
1773f9f848faSopenharmony_ci}
1774f9f848faSopenharmony_ci
1775f9f848faSopenharmony_ciuint8_t
1776f9f848faSopenharmony_ciusb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len)
1777f9f848faSopenharmony_ci{
1778f9f848faSopenharmony_ci	struct usb_mbuf *m = NULL;
1779f9f848faSopenharmony_ci
1780f9f848faSopenharmony_ci	USB_IF_DEQUEUE(&f->free_q, m);
1781f9f848faSopenharmony_ci
1782f9f848faSopenharmony_ci	if (m) {
1783f9f848faSopenharmony_ci		m->cur_data_len = len;
1784f9f848faSopenharmony_ci		m->cur_data_ptr = ptr;
1785f9f848faSopenharmony_ci		USB_IF_ENQUEUE(&f->used_q, m);
1786f9f848faSopenharmony_ci		usb_fifo_wakeup(f);
1787f9f848faSopenharmony_ci		return (1);
1788f9f848faSopenharmony_ci	}
1789f9f848faSopenharmony_ci	return (0);
1790f9f848faSopenharmony_ci}
1791f9f848faSopenharmony_ci
1792f9f848faSopenharmony_civoid
1793f9f848faSopenharmony_ciusb_fifo_put_data_error(struct usb_fifo *f)
1794f9f848faSopenharmony_ci{
1795f9f848faSopenharmony_ci	f->flag_iserror = 1;
1796f9f848faSopenharmony_ci	usb_fifo_wakeup(f);
1797f9f848faSopenharmony_ci}
1798f9f848faSopenharmony_ci
1799f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
1800f9f848faSopenharmony_ci *	usb_fifo_get_data
1801f9f848faSopenharmony_ci *
1802f9f848faSopenharmony_ci * what:
1803f9f848faSopenharmony_ci *  0 - normal operation
1804f9f848faSopenharmony_ci *  1 - only get one "usb_mbuf"
1805f9f848faSopenharmony_ci *
1806f9f848faSopenharmony_ci * returns:
1807f9f848faSopenharmony_ci *  0 - no more data
1808f9f848faSopenharmony_ci *  1 - data in buffer
1809f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
1810f9f848faSopenharmony_ciuint8_t
1811f9f848faSopenharmony_ciusb_fifo_get_data(struct usb_fifo *f, struct usb_page_cache *pc,
1812f9f848faSopenharmony_ci    usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen,
1813f9f848faSopenharmony_ci    uint8_t what)
1814f9f848faSopenharmony_ci{
1815f9f848faSopenharmony_ci	struct usb_mbuf *m = NULL;
1816f9f848faSopenharmony_ci	usb_frlength_t io_len;
1817f9f848faSopenharmony_ci	uint8_t tr_data = 0;
1818f9f848faSopenharmony_ci
1819f9f848faSopenharmony_ci	actlen[0] = 0;
1820f9f848faSopenharmony_ci
1821f9f848faSopenharmony_ci	while (1) {
1822f9f848faSopenharmony_ci		USB_IF_DEQUEUE(&f->used_q, m);
1823f9f848faSopenharmony_ci
1824f9f848faSopenharmony_ci		if (m) {
1825f9f848faSopenharmony_ci			tr_data = 1;
1826f9f848faSopenharmony_ci
1827f9f848faSopenharmony_ci			io_len = MIN(len, m->cur_data_len);
1828f9f848faSopenharmony_ci
1829f9f848faSopenharmony_ci			usbd_copy_in(pc, offset, m->cur_data_ptr, io_len);
1830f9f848faSopenharmony_ci
1831f9f848faSopenharmony_ci			len -= io_len;
1832f9f848faSopenharmony_ci			offset += io_len;
1833f9f848faSopenharmony_ci			actlen[0] += io_len;
1834f9f848faSopenharmony_ci			m->cur_data_ptr += io_len;
1835f9f848faSopenharmony_ci			m->cur_data_len -= io_len;
1836f9f848faSopenharmony_ci
1837f9f848faSopenharmony_ci			if ((m->cur_data_len == 0) || (what == 1)) {
1838f9f848faSopenharmony_ci				USB_IF_ENQUEUE(&f->free_q, m);
1839f9f848faSopenharmony_ci
1840f9f848faSopenharmony_ci				usb_fifo_wakeup(f);
1841f9f848faSopenharmony_ci
1842f9f848faSopenharmony_ci				if (what == 1) {
1843f9f848faSopenharmony_ci					break;
1844f9f848faSopenharmony_ci				}
1845f9f848faSopenharmony_ci			} else {
1846f9f848faSopenharmony_ci				USB_IF_PREPEND(&f->used_q, m);
1847f9f848faSopenharmony_ci			}
1848f9f848faSopenharmony_ci		} else {
1849f9f848faSopenharmony_ci			if (tr_data) {
1850f9f848faSopenharmony_ci				/* wait for data to be written out */
1851f9f848faSopenharmony_ci				break;
1852f9f848faSopenharmony_ci			}
1853f9f848faSopenharmony_ci			if (f->flag_flushing) {
1854f9f848faSopenharmony_ci				/* check if we should send a short packet */
1855f9f848faSopenharmony_ci				if (f->flag_short != 0) {
1856f9f848faSopenharmony_ci					f->flag_short = 0;
1857f9f848faSopenharmony_ci					tr_data = 1;
1858f9f848faSopenharmony_ci					break;
1859f9f848faSopenharmony_ci				}
1860f9f848faSopenharmony_ci				/* flushing complete */
1861f9f848faSopenharmony_ci				f->flag_flushing = 0;
1862f9f848faSopenharmony_ci				usb_fifo_wakeup(f);
1863f9f848faSopenharmony_ci			}
1864f9f848faSopenharmony_ci			break;
1865f9f848faSopenharmony_ci		}
1866f9f848faSopenharmony_ci		if (len == 0) {
1867f9f848faSopenharmony_ci			break;
1868f9f848faSopenharmony_ci		}
1869f9f848faSopenharmony_ci	}
1870f9f848faSopenharmony_ci	return (tr_data);
1871f9f848faSopenharmony_ci}
1872f9f848faSopenharmony_ci
1873f9f848faSopenharmony_ciuint8_t
1874f9f848faSopenharmony_ciusb_fifo_get_data_linear(struct usb_fifo *f, void *ptr,
1875f9f848faSopenharmony_ci    usb_size_t len, usb_size_t *actlen, uint8_t what)
1876f9f848faSopenharmony_ci{
1877f9f848faSopenharmony_ci	struct usb_mbuf *m = NULL;
1878f9f848faSopenharmony_ci	usb_size_t io_len;
1879f9f848faSopenharmony_ci	uint8_t tr_data = 0;
1880f9f848faSopenharmony_ci	int error;
1881f9f848faSopenharmony_ci
1882f9f848faSopenharmony_ci	actlen[0] = 0;
1883f9f848faSopenharmony_ci
1884f9f848faSopenharmony_ci	while (1) {
1885f9f848faSopenharmony_ci		USB_IF_DEQUEUE(&f->used_q, m);
1886f9f848faSopenharmony_ci
1887f9f848faSopenharmony_ci		if (m) {
1888f9f848faSopenharmony_ci			tr_data = 1;
1889f9f848faSopenharmony_ci
1890f9f848faSopenharmony_ci			io_len = MIN(len, m->cur_data_len);
1891f9f848faSopenharmony_ci
1892f9f848faSopenharmony_ci			error = memcpy_s(ptr, io_len, m->cur_data_ptr, io_len);
1893f9f848faSopenharmony_ci			if (error != EOK) {
1894f9f848faSopenharmony_ci				break;
1895f9f848faSopenharmony_ci			}
1896f9f848faSopenharmony_ci
1897f9f848faSopenharmony_ci			len -= io_len;
1898f9f848faSopenharmony_ci			ptr = USB_ADD_BYTES(ptr, io_len);
1899f9f848faSopenharmony_ci			actlen[0] += io_len;
1900f9f848faSopenharmony_ci			m->cur_data_ptr += io_len;
1901f9f848faSopenharmony_ci			m->cur_data_len -= io_len;
1902f9f848faSopenharmony_ci
1903f9f848faSopenharmony_ci			if ((m->cur_data_len == 0) || (what == 1)) {
1904f9f848faSopenharmony_ci				USB_IF_ENQUEUE(&f->free_q, m);
1905f9f848faSopenharmony_ci
1906f9f848faSopenharmony_ci				usb_fifo_wakeup(f);
1907f9f848faSopenharmony_ci
1908f9f848faSopenharmony_ci				if (what == 1) {
1909f9f848faSopenharmony_ci					break;
1910f9f848faSopenharmony_ci				}
1911f9f848faSopenharmony_ci			} else {
1912f9f848faSopenharmony_ci				USB_IF_PREPEND(&f->used_q, m);
1913f9f848faSopenharmony_ci			}
1914f9f848faSopenharmony_ci		} else {
1915f9f848faSopenharmony_ci			if (tr_data) {
1916f9f848faSopenharmony_ci				/* wait for data to be written out */
1917f9f848faSopenharmony_ci				break;
1918f9f848faSopenharmony_ci			}
1919f9f848faSopenharmony_ci			if (f->flag_flushing) {
1920f9f848faSopenharmony_ci				/* check if we should send a short packet */
1921f9f848faSopenharmony_ci				if (f->flag_short != 0) {
1922f9f848faSopenharmony_ci					f->flag_short = 0;
1923f9f848faSopenharmony_ci					tr_data = 1;
1924f9f848faSopenharmony_ci					break;
1925f9f848faSopenharmony_ci				}
1926f9f848faSopenharmony_ci				/* flushing complete */
1927f9f848faSopenharmony_ci				f->flag_flushing = 0;
1928f9f848faSopenharmony_ci				usb_fifo_wakeup(f);
1929f9f848faSopenharmony_ci			}
1930f9f848faSopenharmony_ci			break;
1931f9f848faSopenharmony_ci		}
1932f9f848faSopenharmony_ci		if (len == 0) {
1933f9f848faSopenharmony_ci			break;
1934f9f848faSopenharmony_ci		}
1935f9f848faSopenharmony_ci	}
1936f9f848faSopenharmony_ci	return (tr_data);
1937f9f848faSopenharmony_ci}
1938f9f848faSopenharmony_ci
1939f9f848faSopenharmony_ciuint8_t
1940f9f848faSopenharmony_ciusb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen)
1941f9f848faSopenharmony_ci{
1942f9f848faSopenharmony_ci	struct usb_mbuf *m = NULL;
1943f9f848faSopenharmony_ci
1944f9f848faSopenharmony_ci	USB_IF_POLL(&f->used_q, m);
1945f9f848faSopenharmony_ci
1946f9f848faSopenharmony_ci	if (m) {
1947f9f848faSopenharmony_ci		*plen = m->cur_data_len;
1948f9f848faSopenharmony_ci		*pptr = m->cur_data_ptr;
1949f9f848faSopenharmony_ci
1950f9f848faSopenharmony_ci		return (1);
1951f9f848faSopenharmony_ci	}
1952f9f848faSopenharmony_ci	return (0);
1953f9f848faSopenharmony_ci}
1954f9f848faSopenharmony_ci
1955f9f848faSopenharmony_civoid
1956f9f848faSopenharmony_ciusb_fifo_get_data_error(struct usb_fifo *f)
1957f9f848faSopenharmony_ci{
1958f9f848faSopenharmony_ci	f->flag_iserror = 1;
1959f9f848faSopenharmony_ci	usb_fifo_wakeup(f);
1960f9f848faSopenharmony_ci}
1961f9f848faSopenharmony_ci
1962f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
1963f9f848faSopenharmony_ci *	usb_alloc_symlink
1964f9f848faSopenharmony_ci *
1965f9f848faSopenharmony_ci * Return values:
1966f9f848faSopenharmony_ci * NULL: Failure
1967f9f848faSopenharmony_ci * Else: Pointer to symlink entry
1968f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
1969f9f848faSopenharmony_cistruct usb_symlink *
1970f9f848faSopenharmony_ciusb_alloc_symlink(const char *target)
1971f9f848faSopenharmony_ci{
1972f9f848faSopenharmony_ci	struct usb_symlink *ps = NULL;
1973f9f848faSopenharmony_ci
1974f9f848faSopenharmony_ci	ps = bsd_malloc(sizeof(*ps), M_USBDEV, M_WAITOK);
1975f9f848faSopenharmony_ci	if (ps == NULL) {
1976f9f848faSopenharmony_ci		return (ps);
1977f9f848faSopenharmony_ci	}
1978f9f848faSopenharmony_ci	/* XXX no longer needed */
1979f9f848faSopenharmony_ci	strlcpy(ps->src_path, target, sizeof(ps->src_path));
1980f9f848faSopenharmony_ci	ps->src_len = strlen(ps->src_path);
1981f9f848faSopenharmony_ci	strlcpy(ps->dst_path, target, sizeof(ps->dst_path));
1982f9f848faSopenharmony_ci	ps->dst_len = strlen(ps->dst_path);
1983f9f848faSopenharmony_ci
1984f9f848faSopenharmony_ci	sx_xlock(&usb_sym_lock);
1985f9f848faSopenharmony_ci	TAILQ_INSERT_TAIL(&usb_sym_head, ps, sym_entry);
1986f9f848faSopenharmony_ci	sx_xunlock(&usb_sym_lock);
1987f9f848faSopenharmony_ci	return (ps);
1988f9f848faSopenharmony_ci}
1989f9f848faSopenharmony_ci
1990f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
1991f9f848faSopenharmony_ci *	usb_free_symlink
1992f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
1993f9f848faSopenharmony_civoid
1994f9f848faSopenharmony_ciusb_free_symlink(struct usb_symlink *ps)
1995f9f848faSopenharmony_ci{
1996f9f848faSopenharmony_ci	if (ps == NULL) {
1997f9f848faSopenharmony_ci		return;
1998f9f848faSopenharmony_ci	}
1999f9f848faSopenharmony_ci	sx_xlock(&usb_sym_lock);
2000f9f848faSopenharmony_ci	TAILQ_REMOVE(&usb_sym_head, ps, sym_entry);
2001f9f848faSopenharmony_ci	sx_xunlock(&usb_sym_lock);
2002f9f848faSopenharmony_ci
2003f9f848faSopenharmony_ci	bsd_free(ps, M_USBDEV);
2004f9f848faSopenharmony_ci}
2005f9f848faSopenharmony_ci
2006f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
2007f9f848faSopenharmony_ci *	usb_read_symlink
2008f9f848faSopenharmony_ci *
2009f9f848faSopenharmony_ci * Return value:
2010f9f848faSopenharmony_ci * 0: Success
2011f9f848faSopenharmony_ci * Else: Failure
2012f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
2013f9f848faSopenharmony_ciint
2014f9f848faSopenharmony_ciusb_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len)
2015f9f848faSopenharmony_ci{
2016f9f848faSopenharmony_ci	struct usb_symlink *ps;
2017f9f848faSopenharmony_ci	uint32_t temp;
2018f9f848faSopenharmony_ci	uint32_t delta = 0;
2019f9f848faSopenharmony_ci	uint8_t len;
2020f9f848faSopenharmony_ci	int error = 0;
2021f9f848faSopenharmony_ci
2022f9f848faSopenharmony_ci	sx_xlock(&usb_sym_lock);
2023f9f848faSopenharmony_ci
2024f9f848faSopenharmony_ci	TAILQ_FOREACH(ps, &usb_sym_head, sym_entry) {
2025f9f848faSopenharmony_ci		/*
2026f9f848faSopenharmony_ci		 * Compute total length of source and destination symlink
2027f9f848faSopenharmony_ci		 * strings pluss one length byte and two NUL bytes:
2028f9f848faSopenharmony_ci		 */
2029f9f848faSopenharmony_ci		temp = ps->src_len + ps->dst_len + 3;
2030f9f848faSopenharmony_ci
2031f9f848faSopenharmony_ci		if (temp > 255) {
2032f9f848faSopenharmony_ci			/*
2033f9f848faSopenharmony_ci			 * Skip entry because this length cannot fit
2034f9f848faSopenharmony_ci			 * into one byte:
2035f9f848faSopenharmony_ci			 */
2036f9f848faSopenharmony_ci			continue;
2037f9f848faSopenharmony_ci		}
2038f9f848faSopenharmony_ci		if (startentry != 0) {
2039f9f848faSopenharmony_ci			/* decrement read offset */
2040f9f848faSopenharmony_ci			startentry--;
2041f9f848faSopenharmony_ci			continue;
2042f9f848faSopenharmony_ci		}
2043f9f848faSopenharmony_ci		if (temp > user_len) {
2044f9f848faSopenharmony_ci			/* out of buffer space */
2045f9f848faSopenharmony_ci			break;
2046f9f848faSopenharmony_ci		}
2047f9f848faSopenharmony_ci		len = temp;
2048f9f848faSopenharmony_ci
2049f9f848faSopenharmony_ci		/* copy out total length */
2050f9f848faSopenharmony_ci
2051f9f848faSopenharmony_ci		error = copyout(&len,
2052f9f848faSopenharmony_ci		    USB_ADD_BYTES(user_ptr, delta), 1);
2053f9f848faSopenharmony_ci		if (error) {
2054f9f848faSopenharmony_ci			break;
2055f9f848faSopenharmony_ci		}
2056f9f848faSopenharmony_ci		delta += 1;
2057f9f848faSopenharmony_ci
2058f9f848faSopenharmony_ci		/* copy out source string */
2059f9f848faSopenharmony_ci
2060f9f848faSopenharmony_ci		error = copyout(ps->src_path,
2061f9f848faSopenharmony_ci		    USB_ADD_BYTES(user_ptr, delta), ps->src_len);
2062f9f848faSopenharmony_ci		if (error) {
2063f9f848faSopenharmony_ci			break;
2064f9f848faSopenharmony_ci		}
2065f9f848faSopenharmony_ci		len = 0;
2066f9f848faSopenharmony_ci		delta += ps->src_len;
2067f9f848faSopenharmony_ci		error = copyout(&len,
2068f9f848faSopenharmony_ci		    USB_ADD_BYTES(user_ptr, delta), 1);
2069f9f848faSopenharmony_ci		if (error) {
2070f9f848faSopenharmony_ci			break;
2071f9f848faSopenharmony_ci		}
2072f9f848faSopenharmony_ci		delta += 1;
2073f9f848faSopenharmony_ci
2074f9f848faSopenharmony_ci		/* copy out destination string */
2075f9f848faSopenharmony_ci
2076f9f848faSopenharmony_ci		error = copyout(ps->dst_path,
2077f9f848faSopenharmony_ci		    USB_ADD_BYTES(user_ptr, delta), ps->dst_len);
2078f9f848faSopenharmony_ci		if (error) {
2079f9f848faSopenharmony_ci			break;
2080f9f848faSopenharmony_ci		}
2081f9f848faSopenharmony_ci		len = 0;
2082f9f848faSopenharmony_ci		delta += ps->dst_len;
2083f9f848faSopenharmony_ci		error = copyout(&len,
2084f9f848faSopenharmony_ci		    USB_ADD_BYTES(user_ptr, delta), 1);
2085f9f848faSopenharmony_ci		if (error) {
2086f9f848faSopenharmony_ci			break;
2087f9f848faSopenharmony_ci		}
2088f9f848faSopenharmony_ci		delta += 1;
2089f9f848faSopenharmony_ci
2090f9f848faSopenharmony_ci		user_len -= temp;
2091f9f848faSopenharmony_ci	}
2092f9f848faSopenharmony_ci
2093f9f848faSopenharmony_ci	/* a zero length entry indicates the end */
2094f9f848faSopenharmony_ci
2095f9f848faSopenharmony_ci	if ((user_len != 0) && (error == 0)) {
2096f9f848faSopenharmony_ci		len = 0;
2097f9f848faSopenharmony_ci
2098f9f848faSopenharmony_ci		error = copyout(&len,
2099f9f848faSopenharmony_ci		    USB_ADD_BYTES(user_ptr, delta), 1);
2100f9f848faSopenharmony_ci	}
2101f9f848faSopenharmony_ci	sx_xunlock(&usb_sym_lock);
2102f9f848faSopenharmony_ci	return (error);
2103f9f848faSopenharmony_ci}
2104f9f848faSopenharmony_ci
2105f9f848faSopenharmony_civoid
2106f9f848faSopenharmony_ciusb_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff)
2107f9f848faSopenharmony_ci{
2108f9f848faSopenharmony_ci	if (f == NULL)
2109f9f848faSopenharmony_ci		return;
2110f9f848faSopenharmony_ci
2111f9f848faSopenharmony_ci	/* send a Zero Length Packet, ZLP, before close */
2112f9f848faSopenharmony_ci	f->flag_short = onoff;
2113f9f848faSopenharmony_ci}
2114f9f848faSopenharmony_ci
2115f9f848faSopenharmony_civoid
2116f9f848faSopenharmony_ciusb_fifo_set_write_defrag(struct usb_fifo *f, uint8_t onoff)
2117f9f848faSopenharmony_ci{
2118f9f848faSopenharmony_ci	if (f == NULL)
2119f9f848faSopenharmony_ci		return;
2120f9f848faSopenharmony_ci
2121f9f848faSopenharmony_ci	/* defrag written data */
2122f9f848faSopenharmony_ci	f->flag_write_defrag = onoff;
2123f9f848faSopenharmony_ci	/* reset defrag state */
2124f9f848faSopenharmony_ci	f->flag_have_fragment = 0;
2125f9f848faSopenharmony_ci}
2126f9f848faSopenharmony_ci
2127f9f848faSopenharmony_civoid *
2128f9f848faSopenharmony_ciusb_fifo_softc(struct usb_fifo *f)
2129f9f848faSopenharmony_ci{
2130f9f848faSopenharmony_ci	return (f->priv_sc0);
2131f9f848faSopenharmony_ci}
2132f9f848faSopenharmony_ci#endif	/* USB_HAVE_UGEN */
2133