diff --git a/sys/dev/usb2/image/uscanner2.c b/sys/dev/usb2/image/uscanner2.c index 4ce13a834c36..46c8193e3acc 100644 --- a/sys/dev/usb2/image/uscanner2.c +++ b/sys/dev/usb2/image/uscanner2.c @@ -1,638 +1,637 @@ /* $NetBSD: uscanner.c,v 1.30 2002/07/11 21:14:36 augustss Exp$ */ /* Also already merged from NetBSD: * $NetBSD: uscanner.c,v 1.33 2002/09/23 05:51:24 simonb Exp $ */ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology * and Nick Hibma (n_hibma@qubesoft.com). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #define USB_DEBUG_VAR uscanner_debug #include #include #include -#include #include #include #include #include #include #include #if USB_DEBUG static int uscanner_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, uscanner, CTLFLAG_RW, 0, "USB uscanner"); SYSCTL_INT(_hw_usb2_uscanner, OID_AUTO, debug, CTLFLAG_RW, &uscanner_debug, 0, "uscanner debug level"); #endif /* * uscanner transfers macros definition. */ #define USCANNER_BSIZE (1 << 15) #define USCANNER_IFQ_MAXLEN 2 #define USCANNER_N_TRANSFER 4 /* * Transfers stallings handling flags definition. */ #define USCANNER_FLAG_READ_STALL 0x01 #define USCANNER_FLAG_WRITE_STALL 0x02 /* * uscanner_info flags definition. */ #define USCANNER_FLAG_KEEP_OPEN 0x04 struct uscanner_softc { struct usb2_fifo_sc sc_fifo; struct mtx sc_mtx; struct usb2_xfer *sc_xfer[USCANNER_N_TRANSFER]; uint8_t sc_flags; /* Used to prevent stalls */ }; /* * Prototypes for driver handling routines (sorted by use). */ static device_probe_t uscanner_probe; static device_attach_t uscanner_attach; static device_detach_t uscanner_detach; /* * Prototypes for xfer transfer callbacks. */ static usb2_callback_t uscanner_read_callback; static usb2_callback_t uscanner_read_clear_stall_callback; static usb2_callback_t uscanner_write_callback; static usb2_callback_t uscanner_write_clear_stall_callback; /* * Prototypes for the character device handling routines. */ static usb2_fifo_close_t uscanner_close; static usb2_fifo_cmd_t uscanner_start_read; static usb2_fifo_cmd_t uscanner_start_write; static usb2_fifo_cmd_t uscanner_stop_read; static usb2_fifo_cmd_t uscanner_stop_write; static usb2_fifo_open_t uscanner_open; static struct usb2_fifo_methods uscanner_fifo_methods = { .f_close = &uscanner_close, .f_open = &uscanner_open, .f_start_read = &uscanner_start_read, .f_start_write = &uscanner_start_write, .f_stop_read = &uscanner_stop_read, .f_stop_write = &uscanner_stop_write, .basename[0] = "uscanner", }; /* * xfer transfers array. Resolve-stalling callbacks are marked as control * transfers. */ static const struct usb2_config uscanner_config[USCANNER_N_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = USCANNER_BSIZE, .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.proxy_buffer = 1,.force_short_xfer = 1,}, .mh.callback = &uscanner_write_callback, }, [1] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = USCANNER_BSIZE, .mh.flags = {.pipe_bof = 1,.proxy_buffer = 1,.short_xfer_ok = 1,}, .mh.callback = &uscanner_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &uscanner_write_clear_stall_callback, .mh.timeout = 1000, .mh.interval = 50, /* 50ms */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &uscanner_read_clear_stall_callback, .mh.timeout = 1000, .mh.interval = 50, /* 50ms */ }, }; static devclass_t uscanner_devclass; static device_method_t uscanner_methods[] = { DEVMETHOD(device_probe, uscanner_probe), DEVMETHOD(device_attach, uscanner_attach), DEVMETHOD(device_detach, uscanner_detach), {0, 0} }; static driver_t uscanner_driver = { .name = "uscanner", .methods = uscanner_methods, .size = sizeof(struct uscanner_softc), }; DRIVER_MODULE(uscanner, ushub, uscanner_driver, uscanner_devclass, NULL, 0); MODULE_DEPEND(uscanner, usb2_image, 1, 1, 1); MODULE_DEPEND(uscanner, usb2_core, 1, 1, 1); /* * USB scanners device IDs */ static const struct usb2_device_id uscanner_devs[] = { /* Acer */ {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_320U, 0)}, {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640U, 0)}, {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640BT, 0)}, {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_620U, 0)}, {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_1240U, 0)}, {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_C310U, 0)}, {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_4300U, 0)}, /* AGFA */ {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1236U, 0)}, {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U, 0)}, {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U2, 0)}, {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANTOUCH, 0)}, {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE40, 0)}, {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE50, 0)}, {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE20, 0)}, {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE25, 0)}, {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE26, 0)}, {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE52, 0)}, /* Avision */ {USB_VPI(USB_VENDOR_AVISION, USB_PRODUCT_AVISION_1200U, 0)}, /* Canon */ {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_N656U, 0)}, {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_N676U, 0)}, {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_N1220U, 0)}, {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_D660U, 0)}, {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_N1240U, 0)}, {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_LIDE25, 0)}, /* Epson */ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_636, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_610, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1200, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1240, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1250, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1270, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1600, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1640, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_640U, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1650, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1660, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1670, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1260, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_RX425, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3200, USCANNER_FLAG_KEEP_OPEN)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9700F, USCANNER_FLAG_KEEP_OPEN)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_CX5400, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX7400, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9300UF, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_2480, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3500, USCANNER_FLAG_KEEP_OPEN)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3590, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4200, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4800, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4990, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_5000, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_6000, 0)}, {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX8400, 0)}, /* HP */ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_2200C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_3300C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_3400CSE, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4100C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4200C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4300C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4470C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4670V, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_S20, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_5200C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_5300C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_5400C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_6200C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_6300C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_82x0C, 0)}, /* Kye */ {USB_VPI(USB_VENDOR_KYE, USB_PRODUCT_KYE_VIVIDPRO, 0)}, /* Microtek */ {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_X6U, 0)}, {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX, 0)}, {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX2, 0)}, {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_C6, 0)}, {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL, 0)}, {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL2, 0)}, {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6UL, 0)}, /* Minolta */ {USB_VPI(USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_5400, 0)}, /* Mustek */ {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CU, 0)}, {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200F, 0)}, {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200TA, 0)}, {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600USB, 0)}, {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600CU, 0)}, {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USB, 0)}, {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200UB, 0)}, {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USBPLUS, 0)}, {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CUPLUS, 0)}, /* National */ {USB_VPI(USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW1200, 0)}, {USB_VPI(USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW2400, 0)}, /* Nikon */ {USB_VPI(USB_VENDOR_NIKON, USB_PRODUCT_NIKON_LS40, 0)}, /* Primax */ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2X300, 0)}, {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E300, 0)}, {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2300, 0)}, {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E3002, 0)}, {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_9600, 0)}, {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_600U, 0)}, {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_6200, 0)}, {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_19200, 0)}, {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_1200U, 0)}, {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G600, 0)}, {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_636I, 0)}, {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2600, 0)}, {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E600, 0)}, /* Scanlogic */ {USB_VPI(USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_336CX, 0)}, /* Ultima */ {USB_VPI(USB_VENDOR_ULTIMA, USB_PRODUCT_ULTIMA_1200UBPLUS, 0)}, /* UMAX */ {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1220U, 0)}, {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1236U, 0)}, {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2000U, 0)}, {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2100U, 0)}, {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2200U, 0)}, {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA3400, 0)}, /* Visioneer */ {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_3000, 0)}, {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_5300, 0)}, {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_7600, 0)}, {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6100, 0)}, {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6200, 0)}, {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8100, 0)}, {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8600, 0)} }; /* * uscanner device probing method. */ static int uscanner_probe(device_t dev) { struct usb2_attach_arg *uaa; DPRINTFN(11, "\n"); uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } /* Give other class drivers a chance for multifunctional scanners. */ if (uaa->use_generic == 0) { return (ENXIO); } return (usb2_lookup_id_by_uaa(uscanner_devs, sizeof(uscanner_devs), uaa)); } /* * uscanner device attaching method. */ static int uscanner_attach(device_t dev) { struct usb2_attach_arg *uaa; struct uscanner_softc *sc; int unit; int error; uaa = device_get_ivars(dev); sc = device_get_softc(dev); unit = device_get_unit(dev); /* * A first path softc structure filling. sc_fifo and * sc_xfer are initialised later. */ sc->sc_flags = USB_GET_DRIVER_INFO(uaa); mtx_init(&sc->sc_mtx, "uscanner mutex", NULL, MTX_DEF | MTX_RECURSE); /* * Announce the device: */ device_set_usb2_desc(dev); /* * Setup the transfer. */ if ((error = usb2_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, uscanner_config, USCANNER_N_TRANSFER, sc, &sc->sc_mtx))) { device_printf(dev, "could not setup transfers, " "error=%s\n", usb2_errstr(error)); goto detach; } /* set interface permissions */ usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644); error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, &uscanner_fifo_methods, &sc->sc_fifo, unit, 0 - 1, uaa->info.bIfaceIndex); if (error) { goto detach; } return (0); detach: uscanner_detach(dev); return (ENOMEM); } /* * uscanner device detaching method. */ static int uscanner_detach(device_t dev) { struct uscanner_softc *sc; sc = device_get_softc(dev); usb2_fifo_detach(&sc->sc_fifo); usb2_transfer_unsetup(sc->sc_xfer, USCANNER_N_TRANSFER); mtx_destroy(&sc->sc_mtx); return (0); } /* * Reading callback. Implemented as an "in" bulk transfer. */ static void uscanner_read_callback(struct usb2_xfer *xfer) { struct uscanner_softc *sc; struct usb2_fifo *f; sc = xfer->priv_sc; f = sc->sc_fifo.fp[USB_FIFO_RX]; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: usb2_fifo_put_data(f, xfer->frbuffers, 0, xfer->actlen, 1); case USB_ST_SETUP: /* * If reading is in stall, just jump to clear stall callback and * solve the situation. */ if (sc->sc_flags & USCANNER_FLAG_READ_STALL) { usb2_transfer_start(sc->sc_xfer[3]); break; } if (usb2_fifo_put_bytes_max(f) != 0) { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } break; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { sc->sc_flags |= USCANNER_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer[3]); } break; } } /* * Removing stall on reading callback. */ static void uscanner_read_clear_stall_callback(struct usb2_xfer *xfer) { struct uscanner_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[1]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~USCANNER_FLAG_READ_STALL; usb2_transfer_start(xfer_other); } } /* * Writing callback. Implemented as an "out" bulk transfer. */ static void uscanner_write_callback(struct usb2_xfer *xfer) { struct uscanner_softc *sc; struct usb2_fifo *f; uint32_t actlen; sc = xfer->priv_sc; f = sc->sc_fifo.fp[USB_FIFO_TX]; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: /* * If writing is in stall, just jump to clear stall callback and * solve the situation. */ if (sc->sc_flags & USCANNER_FLAG_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[2]); break; } /* * Write datas, setup and perform hardware transfer. */ if (usb2_fifo_get_data(f, xfer->frbuffers, 0, xfer->max_data_length, &actlen, 0)) { xfer->frlengths[0] = actlen; usb2_start_hardware(xfer); } break; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { sc->sc_flags |= USCANNER_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[2]); } break; } } /* * Removing stall on writing callback. */ static void uscanner_write_clear_stall_callback(struct usb2_xfer *xfer) { struct uscanner_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~USCANNER_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); } } /* * uscanner character device opening method. */ static int uscanner_open(struct usb2_fifo *fifo, int fflags, struct thread *td) { struct uscanner_softc *sc; sc = fifo->priv_sc0; if (!(sc->sc_flags & USCANNER_FLAG_KEEP_OPEN)) { if (fflags & FWRITE) { sc->sc_flags |= USCANNER_FLAG_WRITE_STALL; } if (fflags & FREAD) { sc->sc_flags |= USCANNER_FLAG_READ_STALL; } } if (fflags & FREAD) { if (usb2_fifo_alloc_buffer(fifo, sc->sc_xfer[1]->max_data_length, USCANNER_IFQ_MAXLEN)) { return (ENOMEM); } } if (fflags & FWRITE) { if (usb2_fifo_alloc_buffer(fifo, sc->sc_xfer[0]->max_data_length, USCANNER_IFQ_MAXLEN)) { return (ENOMEM); } } return (0); } static void uscanner_close(struct usb2_fifo *fifo, int fflags, struct thread *td) { if (fflags & (FREAD | FWRITE)) { usb2_fifo_free_buffer(fifo); } } /* * uscanner character device start reading method. */ static void uscanner_start_read(struct usb2_fifo *fifo) { struct uscanner_softc *sc; sc = fifo->priv_sc0; usb2_transfer_start(sc->sc_xfer[1]); } /* * uscanner character device start writing method. */ static void uscanner_start_write(struct usb2_fifo *fifo) { struct uscanner_softc *sc; sc = fifo->priv_sc0; usb2_transfer_start(sc->sc_xfer[0]); } /* * uscanner character device stop reading method. */ static void uscanner_stop_read(struct usb2_fifo *fifo) { struct uscanner_softc *sc; sc = fifo->priv_sc0; usb2_transfer_stop(sc->sc_xfer[3]); usb2_transfer_stop(sc->sc_xfer[1]); } /* * uscanner character device stop writing method. */ static void uscanner_stop_write(struct usb2_fifo *fifo) { struct uscanner_softc *sc; sc = fifo->priv_sc0; usb2_transfer_stop(sc->sc_xfer[2]); usb2_transfer_stop(sc->sc_xfer[0]); } diff --git a/sys/dev/usb2/misc/ufm2.c b/sys/dev/usb2/misc/ufm2.c index 376b6304be85..c37fdf659c24 100644 --- a/sys/dev/usb2/misc/ufm2.c +++ b/sys/dev/usb2/misc/ufm2.c @@ -1,337 +1,336 @@ /*- * Copyright (c) 2001 M. Warner Losh * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This code is based on ugen.c and ulpt.c developed by Lennart Augustsson. * This code includes software developed by the NetBSD Foundation, Inc. and * its contributors. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #define USB_DEBUG_VAR usb2_debug #include #include #include -#include #include #include #include #include #include #include #define UFM_CMD0 0x00 #define UFM_CMD_SET_FREQ 0x01 #define UFM_CMD2 0x02 struct ufm_softc { struct usb2_fifo_sc sc_fifo; struct mtx sc_mtx; struct usb2_device *sc_udev; uint32_t sc_unit; uint32_t sc_freq; uint8_t sc_name[16]; }; /* prototypes */ static device_probe_t ufm_probe; static device_attach_t ufm_attach; static device_detach_t ufm_detach; static usb2_fifo_ioctl_t ufm_ioctl; static usb2_fifo_open_t ufm_open; static struct usb2_fifo_methods ufm_fifo_methods = { .f_ioctl = &ufm_ioctl, .f_open = &ufm_open, .basename[0] = "ufm", }; static int ufm_do_req(struct ufm_softc *, uint8_t, uint16_t, uint16_t, uint8_t *); static int ufm_set_freq(struct ufm_softc *, void *); static int ufm_get_freq(struct ufm_softc *, void *); static int ufm_start(struct ufm_softc *, void *); static int ufm_stop(struct ufm_softc *, void *); static int ufm_get_stat(struct ufm_softc *, void *); static devclass_t ufm_devclass; static device_method_t ufm_methods[] = { DEVMETHOD(device_probe, ufm_probe), DEVMETHOD(device_attach, ufm_attach), DEVMETHOD(device_detach, ufm_detach), {0, 0} }; static driver_t ufm_driver = { .name = "ufm", .methods = ufm_methods, .size = sizeof(struct ufm_softc), }; MODULE_DEPEND(ufm, usb2_misc, 1, 1, 1); DRIVER_MODULE(ufm, ushub, ufm_driver, ufm_devclass, NULL, 0); MODULE_DEPEND(ufm, usb2_core, 1, 1, 1); static int ufm_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if ((uaa->info.idVendor == USB_VENDOR_CYPRESS) && (uaa->info.idProduct == USB_PRODUCT_CYPRESS_FMRADIO)) { return (0); } return (ENXIO); } static int ufm_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct ufm_softc *sc = device_get_softc(dev); int error; if (sc == NULL) { return (ENOMEM); } sc->sc_udev = uaa->device; sc->sc_unit = device_get_unit(dev); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); mtx_init(&sc->sc_mtx, "ufm lock", NULL, MTX_DEF | MTX_RECURSE); device_set_usb2_desc(dev); /* set interface permissions */ usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644); error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, &ufm_fifo_methods, &sc->sc_fifo, device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex); if (error) { goto detach; } return (0); /* success */ detach: ufm_detach(dev); return (ENXIO); } static int ufm_detach(device_t dev) { struct ufm_softc *sc = device_get_softc(dev); usb2_fifo_detach(&sc->sc_fifo); mtx_destroy(&sc->sc_mtx); return (0); } static int ufm_open(struct usb2_fifo *dev, int fflags, struct thread *td) { if ((fflags & (FWRITE | FREAD)) != (FWRITE | FREAD)) { return (EACCES); } return (0); } static int ufm_do_req(struct ufm_softc *sc, uint8_t request, uint16_t value, uint16_t index, uint8_t *retbuf) { int error; struct usb2_device_request req; uint8_t buf[1]; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = request; USETW(req.wValue, value); USETW(req.wIndex, index); USETW(req.wLength, 1); error = usb2_do_request(sc->sc_udev, NULL, &req, buf); if (retbuf) { *retbuf = buf[0]; } if (error) { return (ENXIO); } return (0); } static int ufm_set_freq(struct ufm_softc *sc, void *addr) { int freq = *(int *)addr; /* * Freq now is in Hz. We need to convert it to the frequency * that the radio wants. This frequency is 10.7MHz above * the actual frequency. We then need to convert to * units of 12.5kHz. We add one to the IFM to make rounding * easier. */ mtx_lock(&sc->sc_mtx); sc->sc_freq = freq; mtx_unlock(&sc->sc_mtx); freq = (freq + 10700001) / 12500; /* This appears to set the frequency */ if (ufm_do_req(sc, UFM_CMD_SET_FREQ, freq >> 8, freq, NULL) != 0) { return (EIO); } /* Not sure what this does */ if (ufm_do_req(sc, UFM_CMD0, 0x96, 0xb7, NULL) != 0) { return (EIO); } return (0); } static int ufm_get_freq(struct ufm_softc *sc, void *addr) { int *valp = (int *)addr; mtx_lock(&sc->sc_mtx); *valp = sc->sc_freq; mtx_unlock(&sc->sc_mtx); return (0); } static int ufm_start(struct ufm_softc *sc, void *addr) { uint8_t ret; if (ufm_do_req(sc, UFM_CMD0, 0x00, 0xc7, &ret)) { return (EIO); } if (ufm_do_req(sc, UFM_CMD2, 0x01, 0x00, &ret)) { return (EIO); } if (ret & 0x1) { return (EIO); } return (0); } static int ufm_stop(struct ufm_softc *sc, void *addr) { if (ufm_do_req(sc, UFM_CMD0, 0x16, 0x1C, NULL)) { return (EIO); } if (ufm_do_req(sc, UFM_CMD2, 0x00, 0x00, NULL)) { return (EIO); } return (0); } static int ufm_get_stat(struct ufm_softc *sc, void *addr) { uint8_t ret; /* * Note, there's a 240ms settle time before the status * will be valid, so sleep that amount. */ mtx_lock(&sc->sc_mtx); usb2_pause_mtx(&sc->sc_mtx, USB_MS_HZ / 4); mtx_unlock(&sc->sc_mtx); if (ufm_do_req(sc, UFM_CMD0, 0x00, 0x24, &ret)) { return (EIO); } *(int *)addr = ret; return (0); } static int ufm_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr, int fflags, struct thread *td) { struct ufm_softc *sc = fifo->priv_sc0; int error = 0; switch (cmd) { case FM_SET_FREQ: error = ufm_set_freq(sc, addr); break; case FM_GET_FREQ: error = ufm_get_freq(sc, addr); break; case FM_START: error = ufm_start(sc, addr); break; case FM_STOP: error = ufm_stop(sc, addr); break; case FM_GET_STAT: error = ufm_get_stat(sc, addr); break; default: error = ENOTTY; break; } return (error); } diff --git a/sys/dev/usb2/storage/urio2.c b/sys/dev/usb2/storage/urio2.c index 2007e153fd64..ff4d819e5290 100644 --- a/sys/dev/usb2/storage/urio2.c +++ b/sys/dev/usb2/storage/urio2.c @@ -1,484 +1,483 @@ /*- * Copyright (c) 2000 Iwasa Kazmi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This code is based on ugen.c and ulpt.c developed by Lennart Augustsson. * This code includes software developed by the NetBSD Foundation, Inc. and * its contributors. */ #include __FBSDID("$FreeBSD$"); /* * 2000/3/24 added NetBSD/OpenBSD support (from Alex Nemirovsky) * 2000/3/07 use two bulk-pipe handles for read and write (Dirk) * 2000/3/06 change major number(143), and copyright header * some fix for 4.0 (Dirk) * 2000/3/05 codes for FreeBSD 4.x - CURRENT (Thanks to Dirk-Willem van Gulik) * 2000/3/01 remove retry code from urioioctl() * change method of bulk transfer (no interrupt) * 2000/2/28 small fixes for new rio_usb.h * 2000/2/24 first version. */ #include #include #include #include #include #include #define USB_DEBUG_VAR urio_debug #include #include #include -#include #include #include #include #include #include #include #include #if USB_DEBUG static int urio_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, urio, CTLFLAG_RW, 0, "USB urio"); SYSCTL_INT(_hw_usb2_urio, OID_AUTO, debug, CTLFLAG_RW, &urio_debug, 0, "urio debug level"); #endif #define URIO_T_WR 0 #define URIO_T_RD 1 #define URIO_T_WR_CS 2 #define URIO_T_RD_CS 3 #define URIO_T_MAX 4 #define URIO_BSIZE (1<<12) /* bytes */ #define URIO_IFQ_MAXLEN 2 /* units */ struct urio_softc { struct usb2_fifo_sc sc_fifo; struct mtx sc_mtx; struct usb2_device *sc_udev; struct usb2_xfer *sc_xfer[URIO_T_MAX]; uint8_t sc_flags; #define URIO_FLAG_READ_STALL 0x01 /* read transfer stalled */ #define URIO_FLAG_WRITE_STALL 0x02 /* write transfer stalled */ uint8_t sc_name[16]; }; /* prototypes */ static device_probe_t urio_probe; static device_attach_t urio_attach; static device_detach_t urio_detach; static usb2_callback_t urio_write_callback; static usb2_callback_t urio_write_clear_stall_callback; static usb2_callback_t urio_read_callback; static usb2_callback_t urio_read_clear_stall_callback; static usb2_fifo_close_t urio_close; static usb2_fifo_cmd_t urio_start_read; static usb2_fifo_cmd_t urio_start_write; static usb2_fifo_cmd_t urio_stop_read; static usb2_fifo_cmd_t urio_stop_write; static usb2_fifo_ioctl_t urio_ioctl; static usb2_fifo_open_t urio_open; static struct usb2_fifo_methods urio_fifo_methods = { .f_close = &urio_close, .f_ioctl = &urio_ioctl, .f_open = &urio_open, .f_start_read = &urio_start_read, .f_start_write = &urio_start_write, .f_stop_read = &urio_stop_read, .f_stop_write = &urio_stop_write, .basename[0] = "urio", }; static const struct usb2_config urio_config[URIO_T_MAX] = { [URIO_T_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = URIO_BSIZE, .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.proxy_buffer = 1,}, .mh.callback = &urio_write_callback, }, [URIO_T_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = URIO_BSIZE, .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1,}, .mh.callback = &urio_read_callback, }, [URIO_T_WR_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &urio_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [URIO_T_RD_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &urio_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static devclass_t urio_devclass; static device_method_t urio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, urio_probe), DEVMETHOD(device_attach, urio_attach), DEVMETHOD(device_detach, urio_detach), {0, 0} }; static driver_t urio_driver = { .name = "urio", .methods = urio_methods, .size = sizeof(struct urio_softc), }; DRIVER_MODULE(urio, ushub, urio_driver, urio_devclass, NULL, 0); MODULE_DEPEND(urio, usb2_storage, 1, 1, 1); MODULE_DEPEND(urio, usb2_core, 1, 1, 1); static int urio_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if ((((uaa->info.idVendor == USB_VENDOR_DIAMOND) && (uaa->info.idProduct == USB_PRODUCT_DIAMOND_RIO500USB)) || ((uaa->info.idVendor == USB_VENDOR_DIAMOND2) && ((uaa->info.idProduct == USB_PRODUCT_DIAMOND2_RIO600USB) || (uaa->info.idProduct == USB_PRODUCT_DIAMOND2_RIO800USB))))) return (0); else return (ENXIO); } static int urio_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct urio_softc *sc = device_get_softc(dev); int error; if (sc == NULL) { return (ENOMEM); } device_set_usb2_desc(dev); sc->sc_udev = uaa->device; mtx_init(&sc->sc_mtx, "urio lock", NULL, MTX_DEF | MTX_RECURSE); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); error = usb2_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, urio_config, URIO_T_MAX, sc, &sc->sc_mtx); if (error) { DPRINTF("error=%s\n", usb2_errstr(error)); goto detach; } /* set interface permissions */ usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644); error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, &urio_fifo_methods, &sc->sc_fifo, device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex); if (error) { goto detach; } return (0); /* success */ detach: urio_detach(dev); return (ENOMEM); /* failure */ } static void urio_write_callback(struct usb2_xfer *xfer) { struct urio_softc *sc = xfer->priv_sc; struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_TX]; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: if (sc->sc_flags & URIO_FLAG_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[URIO_T_WR_CS]); return; } if (usb2_fifo_get_data(f, xfer->frbuffers, 0, xfer->max_data_length, &actlen, 0)) { xfer->frlengths[0] = actlen; usb2_start_hardware(xfer); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= URIO_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[URIO_T_WR_CS]); } return; } } static void urio_write_clear_stall_callback(struct usb2_xfer *xfer) { struct urio_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[URIO_T_WR]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~URIO_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); } } static void urio_read_callback(struct usb2_xfer *xfer) { struct urio_softc *sc = xfer->priv_sc; struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX]; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: usb2_fifo_put_data(f, xfer->frbuffers, 0, xfer->actlen, 1); case USB_ST_SETUP: if (sc->sc_flags & URIO_FLAG_READ_STALL) { usb2_transfer_start(sc->sc_xfer[URIO_T_RD_CS]); return; } if (usb2_fifo_put_bytes_max(f) != 0) { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= URIO_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer[URIO_T_RD_CS]); } return; } } static void urio_read_clear_stall_callback(struct usb2_xfer *xfer) { struct urio_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[URIO_T_RD]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~URIO_FLAG_READ_STALL; usb2_transfer_start(xfer_other); } } static void urio_start_read(struct usb2_fifo *fifo) { struct urio_softc *sc = fifo->priv_sc0; usb2_transfer_start(sc->sc_xfer[URIO_T_RD]); } static void urio_stop_read(struct usb2_fifo *fifo) { struct urio_softc *sc = fifo->priv_sc0; usb2_transfer_stop(sc->sc_xfer[URIO_T_RD_CS]); usb2_transfer_stop(sc->sc_xfer[URIO_T_RD]); } static void urio_start_write(struct usb2_fifo *fifo) { struct urio_softc *sc = fifo->priv_sc0; usb2_transfer_start(sc->sc_xfer[URIO_T_WR]); } static void urio_stop_write(struct usb2_fifo *fifo) { struct urio_softc *sc = fifo->priv_sc0; usb2_transfer_stop(sc->sc_xfer[URIO_T_WR_CS]); usb2_transfer_stop(sc->sc_xfer[URIO_T_WR]); } static int urio_open(struct usb2_fifo *fifo, int fflags, struct thread *td) { struct urio_softc *sc = fifo->priv_sc0; if ((fflags & (FWRITE | FREAD)) != (FWRITE | FREAD)) { return (EACCES); } if (fflags & FREAD) { /* clear stall first */ mtx_lock(&sc->sc_mtx); sc->sc_flags |= URIO_FLAG_READ_STALL; mtx_unlock(&sc->sc_mtx); if (usb2_fifo_alloc_buffer(fifo, sc->sc_xfer[URIO_T_RD]->max_data_length, URIO_IFQ_MAXLEN)) { return (ENOMEM); } } if (fflags & FWRITE) { /* clear stall first */ sc->sc_flags |= URIO_FLAG_WRITE_STALL; if (usb2_fifo_alloc_buffer(fifo, sc->sc_xfer[URIO_T_WR]->max_data_length, URIO_IFQ_MAXLEN)) { return (ENOMEM); } } return (0); /* success */ } static void urio_close(struct usb2_fifo *fifo, int fflags, struct thread *td) { if (fflags & (FREAD | FWRITE)) { usb2_fifo_free_buffer(fifo); } } static int urio_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr, int fflags, struct thread *td) { struct usb2_ctl_request ur; struct RioCommand *rio_cmd; int error; switch (cmd) { case RIO_RECV_COMMAND: if (!(fflags & FWRITE)) { error = EPERM; goto done; } bzero(&ur, sizeof(ur)); rio_cmd = addr; ur.ucr_request.bmRequestType = rio_cmd->requesttype | UT_READ_VENDOR_DEVICE; break; case RIO_SEND_COMMAND: if (!(fflags & FWRITE)) { error = EPERM; goto done; } bzero(&ur, sizeof(ur)); rio_cmd = addr; ur.ucr_request.bmRequestType = rio_cmd->requesttype | UT_WRITE_VENDOR_DEVICE; break; default: error = EINVAL; goto done; } DPRINTFN(2, "Sending command\n"); /* Send rio control message */ ur.ucr_request.bRequest = rio_cmd->request; USETW(ur.ucr_request.wValue, rio_cmd->value); USETW(ur.ucr_request.wIndex, rio_cmd->index); USETW(ur.ucr_request.wLength, rio_cmd->length); ur.ucr_data = rio_cmd->buffer; /* reuse generic USB code */ error = ugen_do_request(fifo, &ur); done: return (error); } static int urio_detach(device_t dev) { struct urio_softc *sc = device_get_softc(dev); DPRINTF("\n"); usb2_fifo_detach(&sc->sc_fifo); usb2_transfer_unsetup(sc->sc_xfer, URIO_T_MAX); mtx_destroy(&sc->sc_mtx); return (0); }