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