102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /* $NetBSD: ugensa.c,v 1.9.2.1 2007/03/24 14:55:50 yamt Exp $ */ 302ac6454SAndrew Thompson 4718cf2ccSPedro F. Giffuni /*- 5718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-NetBSD 6718cf2ccSPedro F. Giffuni * 702ac6454SAndrew Thompson * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc. 802ac6454SAndrew Thompson * All rights reserved. 902ac6454SAndrew Thompson * 1002ac6454SAndrew Thompson * This code is derived from software contributed to The NetBSD Foundation 1102ac6454SAndrew Thompson * by Roland C. Dowdeswell <elric@netbsd.org>. 1202ac6454SAndrew Thompson * 1302ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 1402ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 1502ac6454SAndrew Thompson * are met: 1602ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 1702ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1802ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1902ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 2002ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 2102ac6454SAndrew Thompson * 2202ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2302ac6454SAndrew Thompson * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2402ac6454SAndrew Thompson * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2502ac6454SAndrew Thompson * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2602ac6454SAndrew Thompson * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2702ac6454SAndrew Thompson * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2802ac6454SAndrew Thompson * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2902ac6454SAndrew Thompson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3002ac6454SAndrew Thompson * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3102ac6454SAndrew Thompson * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3202ac6454SAndrew Thompson * POSSIBILITY OF SUCH DAMAGE. 3302ac6454SAndrew Thompson */ 3402ac6454SAndrew Thompson 3502ac6454SAndrew Thompson /* 3602ac6454SAndrew Thompson * NOTE: all function names beginning like "ugensa_cfg_" can only 3702ac6454SAndrew Thompson * be called from within the config thread function ! 3802ac6454SAndrew Thompson */ 3902ac6454SAndrew Thompson 40ed6d949aSAndrew Thompson #include <sys/stdint.h> 41ed6d949aSAndrew Thompson #include <sys/stddef.h> 42ed6d949aSAndrew Thompson #include <sys/param.h> 43ed6d949aSAndrew Thompson #include <sys/queue.h> 44ed6d949aSAndrew Thompson #include <sys/types.h> 45ed6d949aSAndrew Thompson #include <sys/systm.h> 46ed6d949aSAndrew Thompson #include <sys/kernel.h> 47ed6d949aSAndrew Thompson #include <sys/bus.h> 48ed6d949aSAndrew Thompson #include <sys/module.h> 49ed6d949aSAndrew Thompson #include <sys/lock.h> 50ed6d949aSAndrew Thompson #include <sys/mutex.h> 51ed6d949aSAndrew Thompson #include <sys/condvar.h> 52ed6d949aSAndrew Thompson #include <sys/sysctl.h> 53ed6d949aSAndrew Thompson #include <sys/sx.h> 54ed6d949aSAndrew Thompson #include <sys/unistd.h> 55ed6d949aSAndrew Thompson #include <sys/callout.h> 56ed6d949aSAndrew Thompson #include <sys/malloc.h> 57ed6d949aSAndrew Thompson #include <sys/priv.h> 58ed6d949aSAndrew Thompson 5902ac6454SAndrew Thompson #include <dev/usb/usb.h> 60ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 61ed6d949aSAndrew Thompson #include "usbdevs.h" 6202ac6454SAndrew Thompson 63a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug 6402ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6502ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 6602ac6454SAndrew Thompson 6702ac6454SAndrew Thompson #include <dev/usb/serial/usb_serial.h> 6802ac6454SAndrew Thompson 6902ac6454SAndrew Thompson #define UGENSA_BUF_SIZE 2048 /* bytes */ 7002ac6454SAndrew Thompson #define UGENSA_CONFIG_INDEX 0 7102ac6454SAndrew Thompson #define UGENSA_IFACE_INDEX 0 7202ac6454SAndrew Thompson #define UGENSA_IFACE_MAX 8 /* exclusivly */ 73ccbb3559SPoul-Henning Kamp #define UGENSA_PORT_MAX 8 /* exclusivly */ 7402ac6454SAndrew Thompson 7502ac6454SAndrew Thompson enum { 7602ac6454SAndrew Thompson UGENSA_BULK_DT_WR, 7702ac6454SAndrew Thompson UGENSA_BULK_DT_RD, 7802ac6454SAndrew Thompson UGENSA_N_TRANSFER, 7902ac6454SAndrew Thompson }; 8002ac6454SAndrew Thompson 8102ac6454SAndrew Thompson struct ugensa_sub_softc { 82a593f6b8SAndrew Thompson struct ucom_softc *sc_ucom_ptr; 83760bc48eSAndrew Thompson struct usb_xfer *sc_xfer[UGENSA_N_TRANSFER]; 8402ac6454SAndrew Thompson }; 8502ac6454SAndrew Thompson 8602ac6454SAndrew Thompson struct ugensa_softc { 87760bc48eSAndrew Thompson struct ucom_super_softc sc_super_ucom; 88ccbb3559SPoul-Henning Kamp struct ucom_softc sc_ucom[UGENSA_PORT_MAX]; 89ccbb3559SPoul-Henning Kamp struct ugensa_sub_softc sc_sub[UGENSA_PORT_MAX]; 9002ac6454SAndrew Thompson 9102ac6454SAndrew Thompson struct mtx sc_mtx; 92ccbb3559SPoul-Henning Kamp uint8_t sc_nports; 9302ac6454SAndrew Thompson }; 9402ac6454SAndrew Thompson 9502ac6454SAndrew Thompson /* prototypes */ 9602ac6454SAndrew Thompson 9702ac6454SAndrew Thompson static device_probe_t ugensa_probe; 9802ac6454SAndrew Thompson static device_attach_t ugensa_attach; 9902ac6454SAndrew Thompson static device_detach_t ugensa_detach; 100c01fc06eSHans Petter Selasky static void ugensa_free_softc(struct ugensa_softc *); 10102ac6454SAndrew Thompson 102e0a69b51SAndrew Thompson static usb_callback_t ugensa_bulk_write_callback; 103e0a69b51SAndrew Thompson static usb_callback_t ugensa_bulk_read_callback; 10402ac6454SAndrew Thompson 1055805d178SHans Petter Selasky static void ugensa_free(struct ucom_softc *); 106760bc48eSAndrew Thompson static void ugensa_start_read(struct ucom_softc *); 107760bc48eSAndrew Thompson static void ugensa_stop_read(struct ucom_softc *); 108760bc48eSAndrew Thompson static void ugensa_start_write(struct ucom_softc *); 109760bc48eSAndrew Thompson static void ugensa_stop_write(struct ucom_softc *); 110655dc9d0SAndrew Thompson static void ugensa_poll(struct ucom_softc *ucom); 11102ac6454SAndrew Thompson 112655dc9d0SAndrew Thompson static const struct usb_config ugensa_xfer_config[UGENSA_N_TRANSFER] = { 11302ac6454SAndrew Thompson [UGENSA_BULK_DT_WR] = { 11402ac6454SAndrew Thompson .type = UE_BULK, 11502ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY, 11602ac6454SAndrew Thompson .direction = UE_DIR_OUT, 1174eae601eSAndrew Thompson .bufsize = UGENSA_BUF_SIZE, 1184eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 1194eae601eSAndrew Thompson .callback = &ugensa_bulk_write_callback, 12002ac6454SAndrew Thompson }, 12102ac6454SAndrew Thompson 12202ac6454SAndrew Thompson [UGENSA_BULK_DT_RD] = { 12302ac6454SAndrew Thompson .type = UE_BULK, 12402ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY, 12502ac6454SAndrew Thompson .direction = UE_DIR_IN, 1264eae601eSAndrew Thompson .bufsize = UGENSA_BUF_SIZE, 1274eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 1284eae601eSAndrew Thompson .callback = &ugensa_bulk_read_callback, 12902ac6454SAndrew Thompson }, 13002ac6454SAndrew Thompson }; 13102ac6454SAndrew Thompson 132760bc48eSAndrew Thompson static const struct ucom_callback ugensa_callback = { 133a593f6b8SAndrew Thompson .ucom_start_read = &ugensa_start_read, 134a593f6b8SAndrew Thompson .ucom_stop_read = &ugensa_stop_read, 135a593f6b8SAndrew Thompson .ucom_start_write = &ugensa_start_write, 136a593f6b8SAndrew Thompson .ucom_stop_write = &ugensa_stop_write, 137655dc9d0SAndrew Thompson .ucom_poll = &ugensa_poll, 1385805d178SHans Petter Selasky .ucom_free = &ugensa_free, 13902ac6454SAndrew Thompson }; 14002ac6454SAndrew Thompson 14102ac6454SAndrew Thompson static device_method_t ugensa_methods[] = { 14202ac6454SAndrew Thompson /* Device methods */ 14302ac6454SAndrew Thompson DEVMETHOD(device_probe, ugensa_probe), 14402ac6454SAndrew Thompson DEVMETHOD(device_attach, ugensa_attach), 14502ac6454SAndrew Thompson DEVMETHOD(device_detach, ugensa_detach), 1465805d178SHans Petter Selasky DEVMETHOD_END 14702ac6454SAndrew Thompson }; 14802ac6454SAndrew Thompson 14902ac6454SAndrew Thompson static devclass_t ugensa_devclass; 15002ac6454SAndrew Thompson 15102ac6454SAndrew Thompson static driver_t ugensa_driver = { 15202ac6454SAndrew Thompson .name = "ugensa", 15302ac6454SAndrew Thompson .methods = ugensa_methods, 15402ac6454SAndrew Thompson .size = sizeof(struct ugensa_softc), 15502ac6454SAndrew Thompson }; 15602ac6454SAndrew Thompson 157ccbb3559SPoul-Henning Kamp /* Driver-info is max number of serial ports per interface */ 158f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID ugensa_devs[] = { 159ccbb3559SPoul-Henning Kamp {USB_VPI(USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220, 1)}, 160ccbb3559SPoul-Henning Kamp {USB_VPI(USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CDMA_MODEM1, 1)}, 161ccbb3559SPoul-Henning Kamp {USB_VPI(USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_CDMA_MSM_K, 1)}, 162ccbb3559SPoul-Henning Kamp {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_49GPLUS, 1)}, 163ccbb3559SPoul-Henning Kamp {USB_VPI(USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 3)}, 164*88535229SHans Petter Selasky {USB_VENDOR(USB_VENDOR_GOOGLE), USB_IFACE_CLASS(UICLASS_VENDOR), 165*88535229SHans Petter Selasky USB_IFACE_SUBCLASS(0x50), USB_IFACE_PROTOCOL(0x01), USB_DRIVER_INFO(10)}, 16602ac6454SAndrew Thompson }; 16702ac6454SAndrew Thompson 168f809f280SWarner Losh DRIVER_MODULE(ugensa, uhub, ugensa_driver, ugensa_devclass, NULL, 0); 169f809f280SWarner Losh MODULE_DEPEND(ugensa, ucom, 1, 1, 1); 170f809f280SWarner Losh MODULE_DEPEND(ugensa, usb, 1, 1, 1); 171f809f280SWarner Losh MODULE_VERSION(ugensa, 1); 172f809f280SWarner Losh USB_PNP_HOST_INFO(ugensa_devs); 173f809f280SWarner Losh 17402ac6454SAndrew Thompson static int 17502ac6454SAndrew Thompson ugensa_probe(device_t dev) 17602ac6454SAndrew Thompson { 177760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 17802ac6454SAndrew Thompson 179f29a0724SAndrew Thompson if (uaa->usb_mode != USB_MODE_HOST) { 18002ac6454SAndrew Thompson return (ENXIO); 18102ac6454SAndrew Thompson } 18202ac6454SAndrew Thompson if (uaa->info.bConfigIndex != UGENSA_CONFIG_INDEX) { 18302ac6454SAndrew Thompson return (ENXIO); 18402ac6454SAndrew Thompson } 18502ac6454SAndrew Thompson if (uaa->info.bIfaceIndex != 0) { 18602ac6454SAndrew Thompson return (ENXIO); 18702ac6454SAndrew Thompson } 188a593f6b8SAndrew Thompson return (usbd_lookup_id_by_uaa(ugensa_devs, sizeof(ugensa_devs), uaa)); 18902ac6454SAndrew Thompson } 19002ac6454SAndrew Thompson 19102ac6454SAndrew Thompson static int 19202ac6454SAndrew Thompson ugensa_attach(device_t dev) 19302ac6454SAndrew Thompson { 194760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 19502ac6454SAndrew Thompson struct ugensa_softc *sc = device_get_softc(dev); 19602ac6454SAndrew Thompson struct ugensa_sub_softc *ssc; 197760bc48eSAndrew Thompson struct usb_interface *iface; 198ccbb3559SPoul-Henning Kamp struct usb_config xfer_config[UGENSA_N_TRANSFER]; 19902ac6454SAndrew Thompson int32_t error; 20002ac6454SAndrew Thompson uint8_t iface_index; 201ccbb3559SPoul-Henning Kamp int x, maxports; 20202ac6454SAndrew Thompson 203ccbb3559SPoul-Henning Kamp maxports = USB_GET_DRIVER_INFO(uaa); 204a593f6b8SAndrew Thompson device_set_usb_desc(dev); 20502ac6454SAndrew Thompson mtx_init(&sc->sc_mtx, "ugensa", NULL, MTX_DEF); 2065805d178SHans Petter Selasky ucom_ref(&sc->sc_super_ucom); 20702ac6454SAndrew Thompson 208ccbb3559SPoul-Henning Kamp for (iface_index = UGENSA_IFACE_INDEX; iface_index < UGENSA_IFACE_MAX; iface_index++) { 209ccbb3559SPoul-Henning Kamp iface = usbd_get_iface(uaa->device, iface_index); 210ccbb3559SPoul-Henning Kamp if (iface == NULL || iface->idesc->bInterfaceClass != UICLASS_VENDOR) 21102ac6454SAndrew Thompson /* Not a serial port, most likely a SD reader */ 21202ac6454SAndrew Thompson continue; 21302ac6454SAndrew Thompson 214ccbb3559SPoul-Henning Kamp /* Loop over all endpoints pairwise */ 215ccbb3559SPoul-Henning Kamp for (x = 0; x < maxports && sc->sc_nports < UGENSA_PORT_MAX; x++) { 216ccbb3559SPoul-Henning Kamp ssc = sc->sc_sub + sc->sc_nports; 217ccbb3559SPoul-Henning Kamp ssc->sc_ucom_ptr = sc->sc_ucom + sc->sc_nports; 218ccbb3559SPoul-Henning Kamp 219ccbb3559SPoul-Henning Kamp memcpy(xfer_config, ugensa_xfer_config, sizeof ugensa_xfer_config); 220ccbb3559SPoul-Henning Kamp xfer_config[UGENSA_BULK_DT_RD].ep_index = x; 221ccbb3559SPoul-Henning Kamp xfer_config[UGENSA_BULK_DT_WR].ep_index = x; 222ccbb3559SPoul-Henning Kamp 223a593f6b8SAndrew Thompson error = usbd_transfer_setup(uaa->device, 224ccbb3559SPoul-Henning Kamp &iface_index, ssc->sc_xfer, xfer_config, 22502ac6454SAndrew Thompson UGENSA_N_TRANSFER, ssc, &sc->sc_mtx); 22602ac6454SAndrew Thompson 22702ac6454SAndrew Thompson if (error) { 228ccbb3559SPoul-Henning Kamp if (x == 0) { 22902ac6454SAndrew Thompson device_printf(dev, "allocating USB " 230ccbb3559SPoul-Henning Kamp "transfers failed (%d)\n", error); 23102ac6454SAndrew Thompson goto detach; 23202ac6454SAndrew Thompson } 233ccbb3559SPoul-Henning Kamp break; 234ccbb3559SPoul-Henning Kamp } 235ccbb3559SPoul-Henning Kamp 23602ac6454SAndrew Thompson /* clear stall at first run */ 237deefe583SAndrew Thompson mtx_lock(&sc->sc_mtx); 238ed6d949aSAndrew Thompson usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_WR]); 239ed6d949aSAndrew Thompson usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_RD]); 240deefe583SAndrew Thompson mtx_unlock(&sc->sc_mtx); 24102ac6454SAndrew Thompson 24202ac6454SAndrew Thompson /* initialize port number */ 243ccbb3559SPoul-Henning Kamp ssc->sc_ucom_ptr->sc_portno = sc->sc_nports; 244ccbb3559SPoul-Henning Kamp if (iface_index != uaa->info.bIfaceIndex) { 245ccbb3559SPoul-Henning Kamp usbd_set_parent_iface(uaa->device, iface_index, 24602ac6454SAndrew Thompson uaa->info.bIfaceIndex); 24702ac6454SAndrew Thompson } 248ccbb3559SPoul-Henning Kamp sc->sc_nports++; 249ccbb3559SPoul-Henning Kamp } 250ccbb3559SPoul-Henning Kamp } 251ccbb3559SPoul-Henning Kamp device_printf(dev, "Found %d serial ports.\n", sc->sc_nports); 25202ac6454SAndrew Thompson 253ccbb3559SPoul-Henning Kamp error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_nports, sc, 25402ac6454SAndrew Thompson &ugensa_callback, &sc->sc_mtx); 255ccbb3559SPoul-Henning Kamp 25602ac6454SAndrew Thompson if (error) { 257ccbb3559SPoul-Henning Kamp DPRINTF("ucom attach failed\n"); 25802ac6454SAndrew Thompson goto detach; 25902ac6454SAndrew Thompson } 2606416c259SNick Hibma ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); 2616416c259SNick Hibma 26202ac6454SAndrew Thompson return (0); /* success */ 26302ac6454SAndrew Thompson 26402ac6454SAndrew Thompson detach: 26502ac6454SAndrew Thompson ugensa_detach(dev); 26602ac6454SAndrew Thompson return (ENXIO); /* failure */ 26702ac6454SAndrew Thompson } 26802ac6454SAndrew Thompson 26902ac6454SAndrew Thompson static int 27002ac6454SAndrew Thompson ugensa_detach(device_t dev) 27102ac6454SAndrew Thompson { 27202ac6454SAndrew Thompson struct ugensa_softc *sc = device_get_softc(dev); 27302ac6454SAndrew Thompson uint8_t x; 27402ac6454SAndrew Thompson 275015bb88fSNick Hibma ucom_detach(&sc->sc_super_ucom, sc->sc_ucom); 27602ac6454SAndrew Thompson 277ccbb3559SPoul-Henning Kamp for (x = 0; x < sc->sc_nports; x++) { 278a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_sub[x].sc_xfer, UGENSA_N_TRANSFER); 27902ac6454SAndrew Thompson } 28002ac6454SAndrew Thompson 281c01fc06eSHans Petter Selasky device_claim_softc(dev); 282c01fc06eSHans Petter Selasky 283c01fc06eSHans Petter Selasky ugensa_free_softc(sc); 284c01fc06eSHans Petter Selasky 28502ac6454SAndrew Thompson return (0); 28602ac6454SAndrew Thompson } 28702ac6454SAndrew Thompson 2885805d178SHans Petter Selasky UCOM_UNLOAD_DRAIN(ugensa); 2895805d178SHans Petter Selasky 2905805d178SHans Petter Selasky static void 291c01fc06eSHans Petter Selasky ugensa_free_softc(struct ugensa_softc *sc) 2925805d178SHans Petter Selasky { 2935805d178SHans Petter Selasky if (ucom_unref(&sc->sc_super_ucom)) { 2945805d178SHans Petter Selasky mtx_destroy(&sc->sc_mtx); 295c01fc06eSHans Petter Selasky device_free_softc(sc); 2965805d178SHans Petter Selasky } 2975805d178SHans Petter Selasky } 2985805d178SHans Petter Selasky 2995805d178SHans Petter Selasky static void 3005805d178SHans Petter Selasky ugensa_free(struct ucom_softc *ucom) 3015805d178SHans Petter Selasky { 302c01fc06eSHans Petter Selasky ugensa_free_softc(ucom->sc_parent); 3035805d178SHans Petter Selasky } 3045805d178SHans Petter Selasky 30502ac6454SAndrew Thompson static void 306ed6d949aSAndrew Thompson ugensa_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 30702ac6454SAndrew Thompson { 308ed6d949aSAndrew Thompson struct ugensa_sub_softc *ssc = usbd_xfer_softc(xfer); 309ed6d949aSAndrew Thompson struct usb_page_cache *pc; 31002ac6454SAndrew Thompson uint32_t actlen; 31102ac6454SAndrew Thompson 31202ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 31302ac6454SAndrew Thompson case USB_ST_SETUP: 31402ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 31502ac6454SAndrew Thompson tr_setup: 316ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 317ed6d949aSAndrew Thompson if (ucom_get_data(ssc->sc_ucom_ptr, pc, 0, 31802ac6454SAndrew Thompson UGENSA_BUF_SIZE, &actlen)) { 319ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, actlen); 320a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 32102ac6454SAndrew Thompson } 32202ac6454SAndrew Thompson return; 32302ac6454SAndrew Thompson 32402ac6454SAndrew Thompson default: /* Error */ 325ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 32602ac6454SAndrew Thompson /* try to clear stall first */ 327ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); 32802ac6454SAndrew Thompson goto tr_setup; 32902ac6454SAndrew Thompson } 33002ac6454SAndrew Thompson return; 33102ac6454SAndrew Thompson } 33202ac6454SAndrew Thompson } 33302ac6454SAndrew Thompson 33402ac6454SAndrew Thompson static void 335ed6d949aSAndrew Thompson ugensa_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 33602ac6454SAndrew Thompson { 337ed6d949aSAndrew Thompson struct ugensa_sub_softc *ssc = usbd_xfer_softc(xfer); 338ed6d949aSAndrew Thompson struct usb_page_cache *pc; 339ed6d949aSAndrew Thompson int actlen; 340ed6d949aSAndrew Thompson 341ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 34202ac6454SAndrew Thompson 34302ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 34402ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 345ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 346ed6d949aSAndrew Thompson ucom_put_data(ssc->sc_ucom_ptr, pc, 0, actlen); 34702ac6454SAndrew Thompson 34802ac6454SAndrew Thompson case USB_ST_SETUP: 34902ac6454SAndrew Thompson tr_setup: 350ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 351a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 35202ac6454SAndrew Thompson return; 35302ac6454SAndrew Thompson 35402ac6454SAndrew Thompson default: /* Error */ 355ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 35602ac6454SAndrew Thompson /* try to clear stall first */ 357ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); 35802ac6454SAndrew Thompson goto tr_setup; 35902ac6454SAndrew Thompson } 36002ac6454SAndrew Thompson return; 36102ac6454SAndrew Thompson } 36202ac6454SAndrew Thompson } 36302ac6454SAndrew Thompson 36402ac6454SAndrew Thompson static void 365760bc48eSAndrew Thompson ugensa_start_read(struct ucom_softc *ucom) 36602ac6454SAndrew Thompson { 36702ac6454SAndrew Thompson struct ugensa_softc *sc = ucom->sc_parent; 36802ac6454SAndrew Thompson struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 36902ac6454SAndrew Thompson 370a593f6b8SAndrew Thompson usbd_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_RD]); 37102ac6454SAndrew Thompson } 37202ac6454SAndrew Thompson 37302ac6454SAndrew Thompson static void 374760bc48eSAndrew Thompson ugensa_stop_read(struct ucom_softc *ucom) 37502ac6454SAndrew Thompson { 37602ac6454SAndrew Thompson struct ugensa_softc *sc = ucom->sc_parent; 37702ac6454SAndrew Thompson struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 37802ac6454SAndrew Thompson 379a593f6b8SAndrew Thompson usbd_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_RD]); 38002ac6454SAndrew Thompson } 38102ac6454SAndrew Thompson 38202ac6454SAndrew Thompson static void 383760bc48eSAndrew Thompson ugensa_start_write(struct ucom_softc *ucom) 38402ac6454SAndrew Thompson { 38502ac6454SAndrew Thompson struct ugensa_softc *sc = ucom->sc_parent; 38602ac6454SAndrew Thompson struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 38702ac6454SAndrew Thompson 388a593f6b8SAndrew Thompson usbd_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_WR]); 38902ac6454SAndrew Thompson } 39002ac6454SAndrew Thompson 39102ac6454SAndrew Thompson static void 392760bc48eSAndrew Thompson ugensa_stop_write(struct ucom_softc *ucom) 39302ac6454SAndrew Thompson { 39402ac6454SAndrew Thompson struct ugensa_softc *sc = ucom->sc_parent; 39502ac6454SAndrew Thompson struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 39602ac6454SAndrew Thompson 397a593f6b8SAndrew Thompson usbd_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_WR]); 39802ac6454SAndrew Thompson } 399655dc9d0SAndrew Thompson 400655dc9d0SAndrew Thompson static void 401655dc9d0SAndrew Thompson ugensa_poll(struct ucom_softc *ucom) 402655dc9d0SAndrew Thompson { 403655dc9d0SAndrew Thompson struct ugensa_softc *sc = ucom->sc_parent; 404655dc9d0SAndrew Thompson struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; 405655dc9d0SAndrew Thompson 406655dc9d0SAndrew Thompson usbd_transfer_poll(ssc->sc_xfer, UGENSA_N_TRANSFER); 407655dc9d0SAndrew Thompson } 408