xref: /dragonfly/sys/bus/u4b/storage/urio.c (revision 2b3f93ea)
112bd3c8bSSascha Wildner /*-
212bd3c8bSSascha Wildner  * Copyright (c) 2000 Iwasa Kazmi
312bd3c8bSSascha Wildner  * All rights reserved.
412bd3c8bSSascha Wildner  *
512bd3c8bSSascha Wildner  * Redistribution and use in source and binary forms, with or without
612bd3c8bSSascha Wildner  * modification, are permitted provided that the following conditions
712bd3c8bSSascha Wildner  * are met:
812bd3c8bSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
912bd3c8bSSascha Wildner  *    notice, this list of conditions, and the following disclaimer.
1012bd3c8bSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
1112bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
1212bd3c8bSSascha Wildner  *    documentation and/or other materials provided with the distribution.
1312bd3c8bSSascha Wildner  *
1412bd3c8bSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1512bd3c8bSSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1612bd3c8bSSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1712bd3c8bSSascha Wildner  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
1812bd3c8bSSascha Wildner  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1912bd3c8bSSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2012bd3c8bSSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2112bd3c8bSSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2212bd3c8bSSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2312bd3c8bSSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2412bd3c8bSSascha Wildner  * SUCH DAMAGE.
2512bd3c8bSSascha Wildner  *
2612bd3c8bSSascha Wildner  * This code is based on ugen.c and ulpt.c developed by Lennart Augustsson.
2712bd3c8bSSascha Wildner  * This code includes software developed by the NetBSD Foundation, Inc. and
2812bd3c8bSSascha Wildner  * its contributors.
2999c3594eSSascha Wildner  *
3099c3594eSSascha Wildner  * $FreeBSD: head/sys/dev/usb/storage/urio.c 246128 2013-01-30 18:01:20Z sbz $
3112bd3c8bSSascha Wildner  */
3212bd3c8bSSascha Wildner 
3312bd3c8bSSascha Wildner /*
3412bd3c8bSSascha Wildner  * 2000/3/24  added NetBSD/OpenBSD support (from Alex Nemirovsky)
3512bd3c8bSSascha Wildner  * 2000/3/07  use two bulk-pipe handles for read and write (Dirk)
3612bd3c8bSSascha Wildner  * 2000/3/06  change major number(143), and copyright header
3712bd3c8bSSascha Wildner  *            some fix for 4.0 (Dirk)
3812bd3c8bSSascha Wildner  * 2000/3/05  codes for FreeBSD 4.x - CURRENT (Thanks to Dirk-Willem van Gulik)
3912bd3c8bSSascha Wildner  * 2000/3/01  remove retry code from urioioctl()
4012bd3c8bSSascha Wildner  *            change method of bulk transfer (no interrupt)
4112bd3c8bSSascha Wildner  * 2000/2/28  small fixes for new rio_usb.h
4212bd3c8bSSascha Wildner  * 2000/2/24  first version.
4312bd3c8bSSascha Wildner  */
4412bd3c8bSSascha Wildner 
4512bd3c8bSSascha Wildner #include <sys/stdint.h>
4612bd3c8bSSascha Wildner #include <sys/param.h>
4712bd3c8bSSascha Wildner #include <sys/queue.h>
4812bd3c8bSSascha Wildner #include <sys/types.h>
4912bd3c8bSSascha Wildner #include <sys/systm.h>
5012bd3c8bSSascha Wildner #include <sys/kernel.h>
5112bd3c8bSSascha Wildner #include <sys/bus.h>
5212bd3c8bSSascha Wildner #include <sys/module.h>
5312bd3c8bSSascha Wildner #include <sys/lock.h>
5412bd3c8bSSascha Wildner #include <sys/condvar.h>
5512bd3c8bSSascha Wildner #include <sys/sysctl.h>
5612bd3c8bSSascha Wildner #include <sys/unistd.h>
5712bd3c8bSSascha Wildner #include <sys/callout.h>
5812bd3c8bSSascha Wildner #include <sys/malloc.h>
59*2b3f93eaSMatthew Dillon #include <sys/caps.h>
6012bd3c8bSSascha Wildner #include <sys/conf.h>
6112bd3c8bSSascha Wildner #include <sys/fcntl.h>
6212bd3c8bSSascha Wildner 
6399c3594eSSascha Wildner #include <bus/u4b/usb.h>
6499c3594eSSascha Wildner #include <bus/u4b/usbdi.h>
6512bd3c8bSSascha Wildner #include "usbdevs.h"
6612bd3c8bSSascha Wildner 
6799c3594eSSascha Wildner #include <bus/u4b/usb_ioctl.h>
6899c3594eSSascha Wildner #include <bus/u4b/usb_generic.h>
6912bd3c8bSSascha Wildner 
7012bd3c8bSSascha Wildner #define	USB_DEBUG_VAR urio_debug
7199c3594eSSascha Wildner #include <bus/u4b/usb_debug.h>
7212bd3c8bSSascha Wildner 
7399c3594eSSascha Wildner #include <bus/u4b/storage/rio500_usb.h>
7412bd3c8bSSascha Wildner 
7512bd3c8bSSascha Wildner #ifdef USB_DEBUG
7612bd3c8bSSascha Wildner static int urio_debug = 0;
7712bd3c8bSSascha Wildner 
7812bd3c8bSSascha Wildner static SYSCTL_NODE(_hw_usb, OID_AUTO, urio, CTLFLAG_RW, 0, "USB urio");
79a9b765b7SSascha Wildner SYSCTL_INT(_hw_usb_urio, OID_AUTO, debug, CTLFLAG_RW,
8012bd3c8bSSascha Wildner     &urio_debug, 0, "urio debug level");
8112bd3c8bSSascha Wildner #endif
8212bd3c8bSSascha Wildner 
8312bd3c8bSSascha Wildner #define	URIO_T_WR     0
8412bd3c8bSSascha Wildner #define	URIO_T_RD     1
8512bd3c8bSSascha Wildner #define	URIO_T_WR_CS  2
8612bd3c8bSSascha Wildner #define	URIO_T_RD_CS  3
8712bd3c8bSSascha Wildner #define	URIO_T_MAX    4
8812bd3c8bSSascha Wildner 
8912bd3c8bSSascha Wildner #define	URIO_BSIZE	(1<<12)		/* bytes */
9012bd3c8bSSascha Wildner #define	URIO_IFQ_MAXLEN      2		/* units */
9112bd3c8bSSascha Wildner 
9212bd3c8bSSascha Wildner struct urio_softc {
9312bd3c8bSSascha Wildner 	struct usb_fifo_sc sc_fifo;
9499c3594eSSascha Wildner 	struct lock sc_lock;
9512bd3c8bSSascha Wildner 
9612bd3c8bSSascha Wildner 	struct usb_device *sc_udev;
9712bd3c8bSSascha Wildner 	struct usb_xfer *sc_xfer[URIO_T_MAX];
9812bd3c8bSSascha Wildner 
9912bd3c8bSSascha Wildner 	uint8_t	sc_flags;
10012bd3c8bSSascha Wildner #define	URIO_FLAG_READ_STALL    0x01	/* read transfer stalled */
10112bd3c8bSSascha Wildner #define	URIO_FLAG_WRITE_STALL   0x02	/* write transfer stalled */
10212bd3c8bSSascha Wildner 
10312bd3c8bSSascha Wildner 	uint8_t	sc_name[16];
10412bd3c8bSSascha Wildner };
10512bd3c8bSSascha Wildner 
10612bd3c8bSSascha Wildner /* prototypes */
10712bd3c8bSSascha Wildner 
10812bd3c8bSSascha Wildner static device_probe_t urio_probe;
10912bd3c8bSSascha Wildner static device_attach_t urio_attach;
11012bd3c8bSSascha Wildner static device_detach_t urio_detach;
11112bd3c8bSSascha Wildner 
11212bd3c8bSSascha Wildner static usb_callback_t urio_write_callback;
11312bd3c8bSSascha Wildner static usb_callback_t urio_write_clear_stall_callback;
11412bd3c8bSSascha Wildner static usb_callback_t urio_read_callback;
11512bd3c8bSSascha Wildner static usb_callback_t urio_read_clear_stall_callback;
11612bd3c8bSSascha Wildner 
11712bd3c8bSSascha Wildner static usb_fifo_close_t urio_close;
11812bd3c8bSSascha Wildner static usb_fifo_cmd_t urio_start_read;
11912bd3c8bSSascha Wildner static usb_fifo_cmd_t urio_start_write;
12012bd3c8bSSascha Wildner static usb_fifo_cmd_t urio_stop_read;
12112bd3c8bSSascha Wildner static usb_fifo_cmd_t urio_stop_write;
12212bd3c8bSSascha Wildner static usb_fifo_ioctl_t urio_ioctl;
12312bd3c8bSSascha Wildner static usb_fifo_open_t urio_open;
12412bd3c8bSSascha Wildner 
12512bd3c8bSSascha Wildner static struct usb_fifo_methods urio_fifo_methods = {
12612bd3c8bSSascha Wildner 	.f_close = &urio_close,
12712bd3c8bSSascha Wildner 	.f_ioctl = &urio_ioctl,
12812bd3c8bSSascha Wildner 	.f_open = &urio_open,
12912bd3c8bSSascha Wildner 	.f_start_read = &urio_start_read,
13012bd3c8bSSascha Wildner 	.f_start_write = &urio_start_write,
13112bd3c8bSSascha Wildner 	.f_stop_read = &urio_stop_read,
13212bd3c8bSSascha Wildner 	.f_stop_write = &urio_stop_write,
13312bd3c8bSSascha Wildner 	.basename[0] = "urio",
13412bd3c8bSSascha Wildner };
13512bd3c8bSSascha Wildner 
13612bd3c8bSSascha Wildner static const struct usb_config urio_config[URIO_T_MAX] = {
13712bd3c8bSSascha Wildner 	[URIO_T_WR] = {
13812bd3c8bSSascha Wildner 		.type = UE_BULK,
13912bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
14012bd3c8bSSascha Wildner 		.direction = UE_DIR_OUT,
14112bd3c8bSSascha Wildner 		.bufsize = URIO_BSIZE,
14212bd3c8bSSascha Wildner 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,.proxy_buffer = 1,},
14312bd3c8bSSascha Wildner 		.callback = &urio_write_callback,
14412bd3c8bSSascha Wildner 	},
14512bd3c8bSSascha Wildner 
14612bd3c8bSSascha Wildner 	[URIO_T_RD] = {
14712bd3c8bSSascha Wildner 		.type = UE_BULK,
14812bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
14912bd3c8bSSascha Wildner 		.direction = UE_DIR_IN,
15012bd3c8bSSascha Wildner 		.bufsize = URIO_BSIZE,
15112bd3c8bSSascha Wildner 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1,},
15212bd3c8bSSascha Wildner 		.callback = &urio_read_callback,
15312bd3c8bSSascha Wildner 	},
15412bd3c8bSSascha Wildner 
15512bd3c8bSSascha Wildner 	[URIO_T_WR_CS] = {
15612bd3c8bSSascha Wildner 		.type = UE_CONTROL,
15712bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
15812bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
15912bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
16012bd3c8bSSascha Wildner 		.callback = &urio_write_clear_stall_callback,
16112bd3c8bSSascha Wildner 		.timeout = 1000,	/* 1 second */
16212bd3c8bSSascha Wildner 		.interval = 50,	/* 50ms */
16312bd3c8bSSascha Wildner 	},
16412bd3c8bSSascha Wildner 
16512bd3c8bSSascha Wildner 	[URIO_T_RD_CS] = {
16612bd3c8bSSascha Wildner 		.type = UE_CONTROL,
16712bd3c8bSSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
16812bd3c8bSSascha Wildner 		.direction = UE_DIR_ANY,
16912bd3c8bSSascha Wildner 		.bufsize = sizeof(struct usb_device_request),
17012bd3c8bSSascha Wildner 		.callback = &urio_read_clear_stall_callback,
17112bd3c8bSSascha Wildner 		.timeout = 1000,	/* 1 second */
17212bd3c8bSSascha Wildner 		.interval = 50,	/* 50ms */
17312bd3c8bSSascha Wildner 	},
17412bd3c8bSSascha Wildner };
17512bd3c8bSSascha Wildner 
17612bd3c8bSSascha Wildner static devclass_t urio_devclass;
17712bd3c8bSSascha Wildner 
17812bd3c8bSSascha Wildner static device_method_t urio_methods[] = {
17912bd3c8bSSascha Wildner 	/* Device interface */
18012bd3c8bSSascha Wildner 	DEVMETHOD(device_probe, urio_probe),
18112bd3c8bSSascha Wildner 	DEVMETHOD(device_attach, urio_attach),
18212bd3c8bSSascha Wildner 	DEVMETHOD(device_detach, urio_detach),
18399c3594eSSascha Wildner 
184d3c9c58eSSascha Wildner 	DEVMETHOD_END
18512bd3c8bSSascha Wildner };
18612bd3c8bSSascha Wildner 
18712bd3c8bSSascha Wildner static driver_t urio_driver = {
18812bd3c8bSSascha Wildner 	.name = "urio",
18912bd3c8bSSascha Wildner 	.methods = urio_methods,
19012bd3c8bSSascha Wildner 	.size = sizeof(struct urio_softc),
19112bd3c8bSSascha Wildner };
19212bd3c8bSSascha Wildner 
19312bd3c8bSSascha Wildner static const STRUCT_USB_HOST_ID urio_devs[] = {
19412bd3c8bSSascha Wildner 	{USB_VPI(USB_VENDOR_DIAMOND, USB_PRODUCT_DIAMOND_RIO500USB, 0)},
19512bd3c8bSSascha Wildner 	{USB_VPI(USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_RIO600USB, 0)},
19612bd3c8bSSascha Wildner 	{USB_VPI(USB_VENDOR_DIAMOND2, USB_PRODUCT_DIAMOND2_RIO800USB, 0)},
19712bd3c8bSSascha Wildner };
19812bd3c8bSSascha Wildner 
19915130067Szrj DRIVER_MODULE(urio, uhub, urio_driver, urio_devclass, NULL, NULL);
20015130067Szrj MODULE_DEPEND(urio, usb, 1, 1, 1);
20115130067Szrj MODULE_VERSION(urio, 1);
20215130067Szrj 
20312bd3c8bSSascha Wildner static int
urio_probe(device_t dev)20412bd3c8bSSascha Wildner urio_probe(device_t dev)
20512bd3c8bSSascha Wildner {
20612bd3c8bSSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
20712bd3c8bSSascha Wildner 
20812bd3c8bSSascha Wildner 	if (uaa->usb_mode != USB_MODE_HOST)
20912bd3c8bSSascha Wildner 		return (ENXIO);
21012bd3c8bSSascha Wildner 	if (uaa->info.bConfigIndex != 0)
21112bd3c8bSSascha Wildner 		return (ENXIO);
21212bd3c8bSSascha Wildner 	if (uaa->info.bIfaceIndex != 0)
21312bd3c8bSSascha Wildner 		return (ENXIO);
21412bd3c8bSSascha Wildner 
21512bd3c8bSSascha Wildner 	return (usbd_lookup_id_by_uaa(urio_devs, sizeof(urio_devs), uaa));
21612bd3c8bSSascha Wildner }
21712bd3c8bSSascha Wildner 
21812bd3c8bSSascha Wildner static int
urio_attach(device_t dev)21912bd3c8bSSascha Wildner urio_attach(device_t dev)
22012bd3c8bSSascha Wildner {
22112bd3c8bSSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
22212bd3c8bSSascha Wildner 	struct urio_softc *sc = device_get_softc(dev);
22312bd3c8bSSascha Wildner 	int error;
22412bd3c8bSSascha Wildner 
22512bd3c8bSSascha Wildner 	device_set_usb_desc(dev);
22612bd3c8bSSascha Wildner 
22712bd3c8bSSascha Wildner 	sc->sc_udev = uaa->device;
22812bd3c8bSSascha Wildner 
22999c3594eSSascha Wildner 	lockinit(&sc->sc_lock, "urio lock", 0, LK_CANRECURSE);
23012bd3c8bSSascha Wildner 
23199c3594eSSascha Wildner 	ksnprintf(sc->sc_name, sizeof(sc->sc_name),
23212bd3c8bSSascha Wildner 	    "%s", device_get_nameunit(dev));
23312bd3c8bSSascha Wildner 
23412bd3c8bSSascha Wildner 	error = usbd_transfer_setup(uaa->device,
23512bd3c8bSSascha Wildner 	    &uaa->info.bIfaceIndex, sc->sc_xfer,
23699c3594eSSascha Wildner 	    urio_config, URIO_T_MAX, sc, &sc->sc_lock);
23712bd3c8bSSascha Wildner 
23812bd3c8bSSascha Wildner 	if (error) {
23912bd3c8bSSascha Wildner 		DPRINTF("error=%s\n", usbd_errstr(error));
24012bd3c8bSSascha Wildner 		goto detach;
24112bd3c8bSSascha Wildner 	}
24212bd3c8bSSascha Wildner 
24399c3594eSSascha Wildner 	error = usb_fifo_attach(uaa->device, sc, &sc->sc_lock,
24412bd3c8bSSascha Wildner 	    &urio_fifo_methods, &sc->sc_fifo,
245a41b1dd4SMarkus Pfeiffer 	    device_get_unit(dev), -1, uaa->info.bIfaceIndex,
24612bd3c8bSSascha Wildner 	    UID_ROOT, GID_OPERATOR, 0644);
24712bd3c8bSSascha Wildner 	if (error) {
24812bd3c8bSSascha Wildner 		goto detach;
24912bd3c8bSSascha Wildner 	}
25012bd3c8bSSascha Wildner 	return (0);			/* success */
25112bd3c8bSSascha Wildner 
25212bd3c8bSSascha Wildner detach:
25312bd3c8bSSascha Wildner 	urio_detach(dev);
25412bd3c8bSSascha Wildner 	return (ENOMEM);		/* failure */
25512bd3c8bSSascha Wildner }
25612bd3c8bSSascha Wildner 
25712bd3c8bSSascha Wildner static void
urio_write_callback(struct usb_xfer * xfer,usb_error_t error)25812bd3c8bSSascha Wildner urio_write_callback(struct usb_xfer *xfer, usb_error_t error)
25912bd3c8bSSascha Wildner {
26012bd3c8bSSascha Wildner 	struct urio_softc *sc = usbd_xfer_softc(xfer);
26112bd3c8bSSascha Wildner 	struct usb_fifo *f = sc->sc_fifo.fp[USB_FIFO_TX];
26212bd3c8bSSascha Wildner 	struct usb_page_cache *pc;
26312bd3c8bSSascha Wildner 	uint32_t actlen;
26412bd3c8bSSascha Wildner 
26512bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
26612bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
26712bd3c8bSSascha Wildner 	case USB_ST_SETUP:
26812bd3c8bSSascha Wildner 		if (sc->sc_flags & URIO_FLAG_WRITE_STALL) {
26912bd3c8bSSascha Wildner 			usbd_transfer_start(sc->sc_xfer[URIO_T_WR_CS]);
27012bd3c8bSSascha Wildner 			return;
27112bd3c8bSSascha Wildner 		}
27212bd3c8bSSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
27312bd3c8bSSascha Wildner 		if (usb_fifo_get_data(f, pc, 0,
27412bd3c8bSSascha Wildner 		    usbd_xfer_max_len(xfer), &actlen, 0)) {
27512bd3c8bSSascha Wildner 
27612bd3c8bSSascha Wildner 			usbd_xfer_set_frame_len(xfer, 0, actlen);
27712bd3c8bSSascha Wildner 			usbd_transfer_submit(xfer);
27812bd3c8bSSascha Wildner 		}
27912bd3c8bSSascha Wildner 		return;
28012bd3c8bSSascha Wildner 
28112bd3c8bSSascha Wildner 	default:			/* Error */
28212bd3c8bSSascha Wildner 		if (error != USB_ERR_CANCELLED) {
28312bd3c8bSSascha Wildner 			/* try to clear stall first */
28412bd3c8bSSascha Wildner 			sc->sc_flags |= URIO_FLAG_WRITE_STALL;
28512bd3c8bSSascha Wildner 			usbd_transfer_start(sc->sc_xfer[URIO_T_WR_CS]);
28612bd3c8bSSascha Wildner 		}
28712bd3c8bSSascha Wildner 		return;
28812bd3c8bSSascha Wildner 	}
28912bd3c8bSSascha Wildner }
29012bd3c8bSSascha Wildner 
29112bd3c8bSSascha Wildner static void
urio_write_clear_stall_callback(struct usb_xfer * xfer,usb_error_t error)29212bd3c8bSSascha Wildner urio_write_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error)
29312bd3c8bSSascha Wildner {
29412bd3c8bSSascha Wildner 	struct urio_softc *sc = usbd_xfer_softc(xfer);
29512bd3c8bSSascha Wildner 	struct usb_xfer *xfer_other = sc->sc_xfer[URIO_T_WR];
29612bd3c8bSSascha Wildner 
29712bd3c8bSSascha Wildner 	if (usbd_clear_stall_callback(xfer, xfer_other)) {
29812bd3c8bSSascha Wildner 		DPRINTF("stall cleared\n");
29912bd3c8bSSascha Wildner 		sc->sc_flags &= ~URIO_FLAG_WRITE_STALL;
30012bd3c8bSSascha Wildner 		usbd_transfer_start(xfer_other);
30112bd3c8bSSascha Wildner 	}
30212bd3c8bSSascha Wildner }
30312bd3c8bSSascha Wildner 
30412bd3c8bSSascha Wildner static void
urio_read_callback(struct usb_xfer * xfer,usb_error_t error)30512bd3c8bSSascha Wildner urio_read_callback(struct usb_xfer *xfer, usb_error_t error)
30612bd3c8bSSascha Wildner {
30712bd3c8bSSascha Wildner 	struct urio_softc *sc = usbd_xfer_softc(xfer);
30812bd3c8bSSascha Wildner 	struct usb_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX];
30912bd3c8bSSascha Wildner 	struct usb_page_cache *pc;
31012bd3c8bSSascha Wildner 	int actlen;
31112bd3c8bSSascha Wildner 
31212bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
31312bd3c8bSSascha Wildner 
31412bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
31512bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
31612bd3c8bSSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
31712bd3c8bSSascha Wildner 		usb_fifo_put_data(f, pc, 0, actlen, 1);
31812bd3c8bSSascha Wildner 
31912bd3c8bSSascha Wildner 	case USB_ST_SETUP:
32012bd3c8bSSascha Wildner 		if (sc->sc_flags & URIO_FLAG_READ_STALL) {
32112bd3c8bSSascha Wildner 			usbd_transfer_start(sc->sc_xfer[URIO_T_RD_CS]);
32212bd3c8bSSascha Wildner 			return;
32312bd3c8bSSascha Wildner 		}
32412bd3c8bSSascha Wildner 		if (usb_fifo_put_bytes_max(f) != 0) {
32512bd3c8bSSascha Wildner 			usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
32612bd3c8bSSascha Wildner 			usbd_transfer_submit(xfer);
32712bd3c8bSSascha Wildner 		}
32812bd3c8bSSascha Wildner 		return;
32912bd3c8bSSascha Wildner 
33012bd3c8bSSascha Wildner 	default:			/* Error */
33112bd3c8bSSascha Wildner 		if (error != USB_ERR_CANCELLED) {
33212bd3c8bSSascha Wildner 			/* try to clear stall first */
33312bd3c8bSSascha Wildner 			sc->sc_flags |= URIO_FLAG_READ_STALL;
33412bd3c8bSSascha Wildner 			usbd_transfer_start(sc->sc_xfer[URIO_T_RD_CS]);
33512bd3c8bSSascha Wildner 		}
33612bd3c8bSSascha Wildner 		return;
33712bd3c8bSSascha Wildner 	}
33812bd3c8bSSascha Wildner }
33912bd3c8bSSascha Wildner 
34012bd3c8bSSascha Wildner static void
urio_read_clear_stall_callback(struct usb_xfer * xfer,usb_error_t error)34112bd3c8bSSascha Wildner urio_read_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error)
34212bd3c8bSSascha Wildner {
34312bd3c8bSSascha Wildner 	struct urio_softc *sc = usbd_xfer_softc(xfer);
34412bd3c8bSSascha Wildner 	struct usb_xfer *xfer_other = sc->sc_xfer[URIO_T_RD];
34512bd3c8bSSascha Wildner 
34612bd3c8bSSascha Wildner 	if (usbd_clear_stall_callback(xfer, xfer_other)) {
34712bd3c8bSSascha Wildner 		DPRINTF("stall cleared\n");
34812bd3c8bSSascha Wildner 		sc->sc_flags &= ~URIO_FLAG_READ_STALL;
34912bd3c8bSSascha Wildner 		usbd_transfer_start(xfer_other);
35012bd3c8bSSascha Wildner 	}
35112bd3c8bSSascha Wildner }
35212bd3c8bSSascha Wildner 
35312bd3c8bSSascha Wildner static void
urio_start_read(struct usb_fifo * fifo)35412bd3c8bSSascha Wildner urio_start_read(struct usb_fifo *fifo)
35512bd3c8bSSascha Wildner {
35612bd3c8bSSascha Wildner 	struct urio_softc *sc = usb_fifo_softc(fifo);
35712bd3c8bSSascha Wildner 
35812bd3c8bSSascha Wildner 	usbd_transfer_start(sc->sc_xfer[URIO_T_RD]);
35912bd3c8bSSascha Wildner }
36012bd3c8bSSascha Wildner 
36112bd3c8bSSascha Wildner static void
urio_stop_read(struct usb_fifo * fifo)36212bd3c8bSSascha Wildner urio_stop_read(struct usb_fifo *fifo)
36312bd3c8bSSascha Wildner {
36412bd3c8bSSascha Wildner 	struct urio_softc *sc = usb_fifo_softc(fifo);
36512bd3c8bSSascha Wildner 
36612bd3c8bSSascha Wildner 	usbd_transfer_stop(sc->sc_xfer[URIO_T_RD_CS]);
36712bd3c8bSSascha Wildner 	usbd_transfer_stop(sc->sc_xfer[URIO_T_RD]);
36812bd3c8bSSascha Wildner }
36912bd3c8bSSascha Wildner 
37012bd3c8bSSascha Wildner static void
urio_start_write(struct usb_fifo * fifo)37112bd3c8bSSascha Wildner urio_start_write(struct usb_fifo *fifo)
37212bd3c8bSSascha Wildner {
37312bd3c8bSSascha Wildner 	struct urio_softc *sc = usb_fifo_softc(fifo);
37412bd3c8bSSascha Wildner 
37512bd3c8bSSascha Wildner 	usbd_transfer_start(sc->sc_xfer[URIO_T_WR]);
37612bd3c8bSSascha Wildner }
37712bd3c8bSSascha Wildner 
37812bd3c8bSSascha Wildner static void
urio_stop_write(struct usb_fifo * fifo)37912bd3c8bSSascha Wildner urio_stop_write(struct usb_fifo *fifo)
38012bd3c8bSSascha Wildner {
38112bd3c8bSSascha Wildner 	struct urio_softc *sc = usb_fifo_softc(fifo);
38212bd3c8bSSascha Wildner 
38312bd3c8bSSascha Wildner 	usbd_transfer_stop(sc->sc_xfer[URIO_T_WR_CS]);
38412bd3c8bSSascha Wildner 	usbd_transfer_stop(sc->sc_xfer[URIO_T_WR]);
38512bd3c8bSSascha Wildner }
38612bd3c8bSSascha Wildner 
38712bd3c8bSSascha Wildner static int
urio_open(struct usb_fifo * fifo,int fflags)38812bd3c8bSSascha Wildner urio_open(struct usb_fifo *fifo, int fflags)
38912bd3c8bSSascha Wildner {
39012bd3c8bSSascha Wildner 	struct urio_softc *sc = usb_fifo_softc(fifo);
39112bd3c8bSSascha Wildner 
39212bd3c8bSSascha Wildner 	if (fflags & FREAD) {
39312bd3c8bSSascha Wildner 		/* clear stall first */
39499c3594eSSascha Wildner 		lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
39512bd3c8bSSascha Wildner 		sc->sc_flags |= URIO_FLAG_READ_STALL;
39699c3594eSSascha Wildner 		lockmgr(&sc->sc_lock, LK_RELEASE);
39712bd3c8bSSascha Wildner 
39812bd3c8bSSascha Wildner 		if (usb_fifo_alloc_buffer(fifo,
39912bd3c8bSSascha Wildner 		    usbd_xfer_max_len(sc->sc_xfer[URIO_T_RD]),
40012bd3c8bSSascha Wildner 		    URIO_IFQ_MAXLEN)) {
40112bd3c8bSSascha Wildner 			return (ENOMEM);
40212bd3c8bSSascha Wildner 		}
40312bd3c8bSSascha Wildner 	}
40412bd3c8bSSascha Wildner 	if (fflags & FWRITE) {
40512bd3c8bSSascha Wildner 		/* clear stall first */
40612bd3c8bSSascha Wildner 		sc->sc_flags |= URIO_FLAG_WRITE_STALL;
40712bd3c8bSSascha Wildner 
40812bd3c8bSSascha Wildner 		if (usb_fifo_alloc_buffer(fifo,
40912bd3c8bSSascha Wildner 		    usbd_xfer_max_len(sc->sc_xfer[URIO_T_WR]),
41012bd3c8bSSascha Wildner 		    URIO_IFQ_MAXLEN)) {
41112bd3c8bSSascha Wildner 			return (ENOMEM);
41212bd3c8bSSascha Wildner 		}
41312bd3c8bSSascha Wildner 	}
41412bd3c8bSSascha Wildner 	return (0);			/* success */
41512bd3c8bSSascha Wildner }
41612bd3c8bSSascha Wildner 
41712bd3c8bSSascha Wildner static void
urio_close(struct usb_fifo * fifo,int fflags)41812bd3c8bSSascha Wildner urio_close(struct usb_fifo *fifo, int fflags)
41912bd3c8bSSascha Wildner {
42012bd3c8bSSascha Wildner 	if (fflags & (FREAD | FWRITE)) {
42112bd3c8bSSascha Wildner 		usb_fifo_free_buffer(fifo);
42212bd3c8bSSascha Wildner 	}
42312bd3c8bSSascha Wildner }
42412bd3c8bSSascha Wildner 
42512bd3c8bSSascha Wildner static int
urio_ioctl(struct usb_fifo * fifo,u_long cmd,void * addr,int fflags)42612bd3c8bSSascha Wildner urio_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr,
42712bd3c8bSSascha Wildner     int fflags)
42812bd3c8bSSascha Wildner {
42912bd3c8bSSascha Wildner 	struct usb_ctl_request ur;
43012bd3c8bSSascha Wildner 	struct RioCommand *rio_cmd;
43112bd3c8bSSascha Wildner 	int error;
43212bd3c8bSSascha Wildner 
43312bd3c8bSSascha Wildner 	switch (cmd) {
43412bd3c8bSSascha Wildner 	case RIO_RECV_COMMAND:
43512bd3c8bSSascha Wildner 		if (!(fflags & FWRITE)) {
43612bd3c8bSSascha Wildner 			error = EPERM;
43712bd3c8bSSascha Wildner 			goto done;
43812bd3c8bSSascha Wildner 		}
43912bd3c8bSSascha Wildner 		memset(&ur, 0, sizeof(ur));
44012bd3c8bSSascha Wildner 		rio_cmd = addr;
44112bd3c8bSSascha Wildner 		ur.ucr_request.bmRequestType =
44212bd3c8bSSascha Wildner 		    rio_cmd->requesttype | UT_READ_VENDOR_DEVICE;
44312bd3c8bSSascha Wildner 		break;
44412bd3c8bSSascha Wildner 
44512bd3c8bSSascha Wildner 	case RIO_SEND_COMMAND:
44612bd3c8bSSascha Wildner 		if (!(fflags & FWRITE)) {
44712bd3c8bSSascha Wildner 			error = EPERM;
44812bd3c8bSSascha Wildner 			goto done;
44912bd3c8bSSascha Wildner 		}
45012bd3c8bSSascha Wildner 		memset(&ur, 0, sizeof(ur));
45112bd3c8bSSascha Wildner 		rio_cmd = addr;
45212bd3c8bSSascha Wildner 		ur.ucr_request.bmRequestType =
45312bd3c8bSSascha Wildner 		    rio_cmd->requesttype | UT_WRITE_VENDOR_DEVICE;
45412bd3c8bSSascha Wildner 		break;
45512bd3c8bSSascha Wildner 
45612bd3c8bSSascha Wildner 	default:
45712bd3c8bSSascha Wildner 		error = EINVAL;
45812bd3c8bSSascha Wildner 		goto done;
45912bd3c8bSSascha Wildner 	}
46012bd3c8bSSascha Wildner 
46112bd3c8bSSascha Wildner 	DPRINTFN(2, "Sending command\n");
46212bd3c8bSSascha Wildner 
46312bd3c8bSSascha Wildner 	/* Send rio control message */
46412bd3c8bSSascha Wildner 	ur.ucr_request.bRequest = rio_cmd->request;
46512bd3c8bSSascha Wildner 	USETW(ur.ucr_request.wValue, rio_cmd->value);
46612bd3c8bSSascha Wildner 	USETW(ur.ucr_request.wIndex, rio_cmd->index);
46712bd3c8bSSascha Wildner 	USETW(ur.ucr_request.wLength, rio_cmd->length);
46812bd3c8bSSascha Wildner 	ur.ucr_data = rio_cmd->buffer;
46912bd3c8bSSascha Wildner 
47012bd3c8bSSascha Wildner 	/* reuse generic USB code */
47112bd3c8bSSascha Wildner 	error = ugen_do_request(fifo, &ur);
47212bd3c8bSSascha Wildner 
47312bd3c8bSSascha Wildner done:
47412bd3c8bSSascha Wildner 	return (error);
47512bd3c8bSSascha Wildner }
47612bd3c8bSSascha Wildner 
47712bd3c8bSSascha Wildner static int
urio_detach(device_t dev)47812bd3c8bSSascha Wildner urio_detach(device_t dev)
47912bd3c8bSSascha Wildner {
48012bd3c8bSSascha Wildner 	struct urio_softc *sc = device_get_softc(dev);
48112bd3c8bSSascha Wildner 
48212bd3c8bSSascha Wildner 	DPRINTF("\n");
48312bd3c8bSSascha Wildner 
48412bd3c8bSSascha Wildner 	usb_fifo_detach(&sc->sc_fifo);
48512bd3c8bSSascha Wildner 
48612bd3c8bSSascha Wildner 	usbd_transfer_unsetup(sc->sc_xfer, URIO_T_MAX);
48712bd3c8bSSascha Wildner 
48899c3594eSSascha Wildner 	lockuninit(&sc->sc_lock);
48912bd3c8bSSascha Wildner 
49012bd3c8bSSascha Wildner 	return (0);
49112bd3c8bSSascha Wildner }
492