diff options
Diffstat (limited to 'sys/dev/usb/serial/umoscom.c')
-rw-r--r-- | sys/dev/usb/serial/umoscom.c | 672 |
1 files changed, 672 insertions, 0 deletions
diff --git a/sys/dev/usb/serial/umoscom.c b/sys/dev/usb/serial/umoscom.c new file mode 100644 index 0000000..344f4a1 --- /dev/null +++ b/sys/dev/usb/serial/umoscom.c @@ -0,0 +1,672 @@ +/* $FreeBSD$ */ +/* $OpenBSD: umoscom.c,v 1.2 2006/10/26 06:02:43 jsg Exp $ */ + +/* + * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "usbdevs.h" +#include <dev/usb/usb.h> +#include <dev/usb/usb_mfunc.h> +#include <dev/usb/usb_error.h> +#include <dev/usb/usb_cdc.h> + +#define USB_DEBUG_VAR umoscom_debug + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_request.h> +#include <dev/usb/usb_lookup.h> +#include <dev/usb/usb_util.h> +#include <dev/usb/usb_busdma.h> + +#include <dev/usb/serial/usb_serial.h> + +#if USB_DEBUG +static int umoscom_debug = 0; + +SYSCTL_NODE(_hw_usb2, OID_AUTO, umoscom, CTLFLAG_RW, 0, "USB umoscom"); +SYSCTL_INT(_hw_usb2_umoscom, OID_AUTO, debug, CTLFLAG_RW, + &umoscom_debug, 0, "Debug level"); +#endif + +#define UMOSCOM_BUFSIZE 1024 /* bytes */ + +#define UMOSCOM_CONFIG_INDEX 0 +#define UMOSCOM_IFACE_INDEX 0 + +/* interrupt packet */ +#define UMOSCOM_IIR_RLS 0x06 +#define UMOSCOM_IIR_RDA 0x04 +#define UMOSCOM_IIR_CTI 0x0c +#define UMOSCOM_IIR_THR 0x02 +#define UMOSCOM_IIR_MS 0x00 + +/* registers */ +#define UMOSCOM_READ 0x0d +#define UMOSCOM_WRITE 0x0e +#define UMOSCOM_UART_REG 0x0300 +#define UMOSCOM_VEND_REG 0x0000 + +#define UMOSCOM_TXBUF 0x00 /* Write */ +#define UMOSCOM_RXBUF 0x00 /* Read */ +#define UMOSCOM_INT 0x01 +#define UMOSCOM_FIFO 0x02 /* Write */ +#define UMOSCOM_ISR 0x02 /* Read */ +#define UMOSCOM_LCR 0x03 +#define UMOSCOM_MCR 0x04 +#define UMOSCOM_LSR 0x05 +#define UMOSCOM_MSR 0x06 +#define UMOSCOM_SCRATCH 0x07 +#define UMOSCOM_DIV_LO 0x08 +#define UMOSCOM_DIV_HI 0x09 +#define UMOSCOM_EFR 0x0a +#define UMOSCOM_XON1 0x0b +#define UMOSCOM_XON2 0x0c +#define UMOSCOM_XOFF1 0x0d +#define UMOSCOM_XOFF2 0x0e + +#define UMOSCOM_BAUDLO 0x00 +#define UMOSCOM_BAUDHI 0x01 + +#define UMOSCOM_INT_RXEN 0x01 +#define UMOSCOM_INT_TXEN 0x02 +#define UMOSCOM_INT_RSEN 0x04 +#define UMOSCOM_INT_MDMEM 0x08 +#define UMOSCOM_INT_SLEEP 0x10 +#define UMOSCOM_INT_XOFF 0x20 +#define UMOSCOM_INT_RTS 0x40 + +#define UMOSCOM_FIFO_EN 0x01 +#define UMOSCOM_FIFO_RXCLR 0x02 +#define UMOSCOM_FIFO_TXCLR 0x04 +#define UMOSCOM_FIFO_DMA_BLK 0x08 +#define UMOSCOM_FIFO_TXLVL_MASK 0x30 +#define UMOSCOM_FIFO_TXLVL_8 0x00 +#define UMOSCOM_FIFO_TXLVL_16 0x10 +#define UMOSCOM_FIFO_TXLVL_32 0x20 +#define UMOSCOM_FIFO_TXLVL_56 0x30 +#define UMOSCOM_FIFO_RXLVL_MASK 0xc0 +#define UMOSCOM_FIFO_RXLVL_8 0x00 +#define UMOSCOM_FIFO_RXLVL_16 0x40 +#define UMOSCOM_FIFO_RXLVL_56 0x80 +#define UMOSCOM_FIFO_RXLVL_80 0xc0 + +#define UMOSCOM_ISR_MDM 0x00 +#define UMOSCOM_ISR_NONE 0x01 +#define UMOSCOM_ISR_TX 0x02 +#define UMOSCOM_ISR_RX 0x04 +#define UMOSCOM_ISR_LINE 0x06 +#define UMOSCOM_ISR_RXTIMEOUT 0x0c +#define UMOSCOM_ISR_RX_XOFF 0x10 +#define UMOSCOM_ISR_RTSCTS 0x20 +#define UMOSCOM_ISR_FIFOEN 0xc0 + +#define UMOSCOM_LCR_DBITS(x) ((x) - 5) +#define UMOSCOM_LCR_STOP_BITS_1 0x00 +#define UMOSCOM_LCR_STOP_BITS_2 0x04 /* 2 if 6-8 bits/char or 1.5 if 5 */ +#define UMOSCOM_LCR_PARITY_NONE 0x00 +#define UMOSCOM_LCR_PARITY_ODD 0x08 +#define UMOSCOM_LCR_PARITY_EVEN 0x18 +#define UMOSCOM_LCR_BREAK 0x40 +#define UMOSCOM_LCR_DIVLATCH_EN 0x80 + +#define UMOSCOM_MCR_DTR 0x01 +#define UMOSCOM_MCR_RTS 0x02 +#define UMOSCOM_MCR_LOOP 0x04 +#define UMOSCOM_MCR_INTEN 0x08 +#define UMOSCOM_MCR_LOOPBACK 0x10 +#define UMOSCOM_MCR_XONANY 0x20 +#define UMOSCOM_MCR_IRDA_EN 0x40 +#define UMOSCOM_MCR_BAUD_DIV4 0x80 + +#define UMOSCOM_LSR_RXDATA 0x01 +#define UMOSCOM_LSR_RXOVER 0x02 +#define UMOSCOM_LSR_RXPAR_ERR 0x04 +#define UMOSCOM_LSR_RXFRM_ERR 0x08 +#define UMOSCOM_LSR_RXBREAK 0x10 +#define UMOSCOM_LSR_TXEMPTY 0x20 +#define UMOSCOM_LSR_TXALLEMPTY 0x40 +#define UMOSCOM_LSR_TXFIFO_ERR 0x80 + +#define UMOSCOM_MSR_CTS_CHG 0x01 +#define UMOSCOM_MSR_DSR_CHG 0x02 +#define UMOSCOM_MSR_RI_CHG 0x04 +#define UMOSCOM_MSR_CD_CHG 0x08 +#define UMOSCOM_MSR_CTS 0x10 +#define UMOSCOM_MSR_RTS 0x20 +#define UMOSCOM_MSR_RI 0x40 +#define UMOSCOM_MSR_CD 0x80 + +#define UMOSCOM_BAUD_REF 115200 + +enum { + UMOSCOM_BULK_DT_WR, + UMOSCOM_BULK_DT_RD, + UMOSCOM_INTR_DT_RD, + UMOSCOM_N_TRANSFER, +}; + +struct umoscom_softc { + struct usb2_com_super_softc sc_super_ucom; + struct usb2_com_softc sc_ucom; + + struct usb2_xfer *sc_xfer[UMOSCOM_N_TRANSFER]; + struct usb2_device *sc_udev; + + uint8_t sc_mcr; + uint8_t sc_lcr; +}; + +/* prototypes */ + +static device_probe_t umoscom_probe; +static device_attach_t umoscom_attach; +static device_detach_t umoscom_detach; + +static usb2_callback_t umoscom_write_callback; +static usb2_callback_t umoscom_read_callback; +static usb2_callback_t umoscom_intr_callback; + +static void umoscom_cfg_open(struct usb2_com_softc *); +static void umoscom_cfg_close(struct usb2_com_softc *); +static void umoscom_cfg_set_break(struct usb2_com_softc *, uint8_t); +static void umoscom_cfg_set_dtr(struct usb2_com_softc *, uint8_t); +static void umoscom_cfg_set_rts(struct usb2_com_softc *, uint8_t); +static int umoscom_pre_param(struct usb2_com_softc *, struct termios *); +static void umoscom_cfg_param(struct usb2_com_softc *, struct termios *); +static void umoscom_cfg_get_status(struct usb2_com_softc *, uint8_t *, + uint8_t *); +static void umoscom_cfg_write(struct umoscom_softc *, uint16_t, uint16_t); +static uint8_t umoscom_cfg_read(struct umoscom_softc *, uint16_t); +static void umoscom_start_read(struct usb2_com_softc *); +static void umoscom_stop_read(struct usb2_com_softc *); +static void umoscom_start_write(struct usb2_com_softc *); +static void umoscom_stop_write(struct usb2_com_softc *); + +static const struct usb2_config umoscom_config_data[UMOSCOM_N_TRANSFER] = { + + [UMOSCOM_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UMOSCOM_BUFSIZE, + .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .mh.callback = &umoscom_write_callback, + }, + + [UMOSCOM_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UMOSCOM_BUFSIZE, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.callback = &umoscom_read_callback, + }, + + [UMOSCOM_INTR_DT_RD] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .mh.bufsize = 0, /* use wMaxPacketSize */ + .mh.callback = &umoscom_intr_callback, + }, +}; + +static const struct usb2_com_callback umoscom_callback = { + /* configuration callbacks */ + .usb2_com_cfg_get_status = &umoscom_cfg_get_status, + .usb2_com_cfg_set_dtr = &umoscom_cfg_set_dtr, + .usb2_com_cfg_set_rts = &umoscom_cfg_set_rts, + .usb2_com_cfg_set_break = &umoscom_cfg_set_break, + .usb2_com_cfg_param = &umoscom_cfg_param, + .usb2_com_cfg_open = &umoscom_cfg_open, + .usb2_com_cfg_close = &umoscom_cfg_close, + + /* other callbacks */ + .usb2_com_pre_param = &umoscom_pre_param, + .usb2_com_start_read = &umoscom_start_read, + .usb2_com_stop_read = &umoscom_stop_read, + .usb2_com_start_write = &umoscom_start_write, + .usb2_com_stop_write = &umoscom_stop_write, +}; + +static device_method_t umoscom_methods[] = { + DEVMETHOD(device_probe, umoscom_probe), + DEVMETHOD(device_attach, umoscom_attach), + DEVMETHOD(device_detach, umoscom_detach), + {0, 0} +}; + +static devclass_t umoscom_devclass; + +static driver_t umoscom_driver = { + .name = "umoscom", + .methods = umoscom_methods, + .size = sizeof(struct umoscom_softc), +}; + +DRIVER_MODULE(umoscom, ushub, umoscom_driver, umoscom_devclass, NULL, 0); +MODULE_DEPEND(umoscom, ucom, 1, 1, 1); +MODULE_DEPEND(umoscom, usb, 1, 1, 1); + +static const struct usb2_device_id umoscom_devs[] = { + {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7703, 0)} +}; + +static int +umoscom_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.bConfigIndex != UMOSCOM_CONFIG_INDEX) { + return (ENXIO); + } + if (uaa->info.bIfaceIndex != UMOSCOM_IFACE_INDEX) { + return (ENXIO); + } + return (usb2_lookup_id_by_uaa(umoscom_devs, sizeof(umoscom_devs), uaa)); +} + +static int +umoscom_attach(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct umoscom_softc *sc = device_get_softc(dev); + int error; + uint8_t iface_index; + + sc->sc_udev = uaa->device; + sc->sc_mcr = 0x08; /* enable interrupts */ + + /* XXX the device doesn't provide any ID string, so set a static one */ + device_set_desc(dev, "MOSCHIP USB Serial Port Adapter"); + device_printf(dev, "<MOSCHIP USB Serial Port Adapter>\n"); + + iface_index = UMOSCOM_IFACE_INDEX; + error = usb2_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, umoscom_config_data, + UMOSCOM_N_TRANSFER, sc, &Giant); + + if (error) { + goto detach; + } + /* clear stall at first run */ + usb2_transfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_WR]); + usb2_transfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_RD]); + + error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, + &umoscom_callback, &Giant); + if (error) { + goto detach; + } + return (0); + +detach: + device_printf(dev, "attach error: %s\n", usb2_errstr(error)); + umoscom_detach(dev); + return (ENXIO); +} + +static int +umoscom_detach(device_t dev) +{ + struct umoscom_softc *sc = device_get_softc(dev); + + mtx_lock(&Giant); + + mtx_unlock(&Giant); + + usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); + + usb2_transfer_unsetup(sc->sc_xfer, UMOSCOM_N_TRANSFER); + + return (0); +} + +static void +umoscom_cfg_open(struct usb2_com_softc *ucom) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + DPRINTF("\n"); + + /* Purge FIFOs or odd things happen */ + umoscom_cfg_write(sc, UMOSCOM_FIFO, 0x00 | UMOSCOM_UART_REG); + + /* Enable FIFO */ + umoscom_cfg_write(sc, UMOSCOM_FIFO, UMOSCOM_FIFO_EN | + UMOSCOM_FIFO_RXCLR | UMOSCOM_FIFO_TXCLR | + UMOSCOM_FIFO_DMA_BLK | UMOSCOM_FIFO_RXLVL_MASK | + UMOSCOM_UART_REG); + + /* Enable Interrupt Registers */ + umoscom_cfg_write(sc, UMOSCOM_INT, 0x0C | UMOSCOM_UART_REG); + + /* Magic */ + umoscom_cfg_write(sc, 0x01, 0x08); + + /* Magic */ + umoscom_cfg_write(sc, 0x00, 0x02); +} + +static void +umoscom_cfg_close(struct usb2_com_softc *ucom) +{ + return; +} + +static void +umoscom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct umoscom_softc *sc = ucom->sc_parent; + uint16_t val; + + val = sc->sc_lcr; + if (onoff) + val |= UMOSCOM_LCR_BREAK; + + umoscom_cfg_write(sc, UMOSCOM_LCR, val | UMOSCOM_UART_REG); +} + +static void +umoscom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + if (onoff) + sc->sc_mcr |= UMOSCOM_MCR_DTR; + else + sc->sc_mcr &= ~UMOSCOM_MCR_DTR; + + umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG); +} + +static void +umoscom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + if (onoff) + sc->sc_mcr |= UMOSCOM_MCR_RTS; + else + sc->sc_mcr &= ~UMOSCOM_MCR_RTS; + + umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG); +} + +static int +umoscom_pre_param(struct usb2_com_softc *ucom, struct termios *t) +{ + if ((t->c_ospeed <= 1) || (t->c_ospeed > 115200)) + return (EINVAL); + + return (0); +} + +static void +umoscom_cfg_param(struct usb2_com_softc *ucom, struct termios *t) +{ + struct umoscom_softc *sc = ucom->sc_parent; + uint16_t data; + + DPRINTF("speed=%d\n", t->c_ospeed); + + data = ((uint32_t)UMOSCOM_BAUD_REF) / ((uint32_t)t->c_ospeed); + + if (data == 0) { + DPRINTF("invalid baud rate!\n"); + return; + } + umoscom_cfg_write(sc, UMOSCOM_LCR, + UMOSCOM_LCR_DIVLATCH_EN | UMOSCOM_UART_REG); + + umoscom_cfg_write(sc, UMOSCOM_BAUDLO, + (data & 0xFF) | UMOSCOM_UART_REG); + + umoscom_cfg_write(sc, UMOSCOM_BAUDHI, + ((data >> 8) & 0xFF) | UMOSCOM_UART_REG); + + if (t->c_cflag & CSTOPB) + data = UMOSCOM_LCR_STOP_BITS_2; + else + data = UMOSCOM_LCR_STOP_BITS_1; + + if (t->c_cflag & PARENB) { + if (t->c_cflag & PARODD) + data |= UMOSCOM_LCR_PARITY_ODD; + else + data |= UMOSCOM_LCR_PARITY_EVEN; + } else + data |= UMOSCOM_LCR_PARITY_NONE; + + switch (t->c_cflag & CSIZE) { + case CS5: + data |= UMOSCOM_LCR_DBITS(5); + break; + case CS6: + data |= UMOSCOM_LCR_DBITS(6); + break; + case CS7: + data |= UMOSCOM_LCR_DBITS(7); + break; + case CS8: + data |= UMOSCOM_LCR_DBITS(8); + break; + } + + sc->sc_lcr = data; + umoscom_cfg_write(sc, UMOSCOM_LCR, data | UMOSCOM_UART_REG); +} + +static void +umoscom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *p_lsr, uint8_t *p_msr) +{ + struct umoscom_softc *sc = ucom->sc_parent; + uint8_t lsr; + uint8_t msr; + + DPRINTFN(5, "\n"); + + /* read status registers */ + + lsr = umoscom_cfg_read(sc, UMOSCOM_LSR); + msr = umoscom_cfg_read(sc, UMOSCOM_MSR); + + /* translate bits */ + + if (msr & UMOSCOM_MSR_CTS) + *p_msr |= SER_CTS; + + if (msr & UMOSCOM_MSR_CD) + *p_msr |= SER_DCD; + + if (msr & UMOSCOM_MSR_RI) + *p_msr |= SER_RI; + + if (msr & UMOSCOM_MSR_RTS) + *p_msr |= SER_DSR; +} + +static void +umoscom_cfg_write(struct umoscom_softc *sc, uint16_t reg, uint16_t val) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UMOSCOM_WRITE; + USETW(req.wValue, val); + USETW(req.wIndex, reg); + USETW(req.wLength, 0); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000); +} + +static uint8_t +umoscom_cfg_read(struct umoscom_softc *sc, uint16_t reg) +{ + struct usb2_device_request req; + uint8_t val; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = UMOSCOM_READ; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 1); + + usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, &val, 0, 1000); + + DPRINTF("reg=0x%04x, val=0x%02x\n", reg, val); + + return (val); +} + +static void +umoscom_start_read(struct usb2_com_softc *ucom) +{ + struct umoscom_softc *sc = ucom->sc_parent; + +#if 0 + /* start interrupt endpoint */ + usb2_transfer_start(sc->sc_xfer[UMOSCOM_INTR_DT_RD]); +#endif + /* start read endpoint */ + usb2_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_RD]); +} + +static void +umoscom_stop_read(struct usb2_com_softc *ucom) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + /* stop interrupt transfer */ + usb2_transfer_stop(sc->sc_xfer[UMOSCOM_INTR_DT_RD]); + + /* stop read endpoint */ + usb2_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_RD]); +} + +static void +umoscom_start_write(struct usb2_com_softc *ucom) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + usb2_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_WR]); +} + +static void +umoscom_stop_write(struct usb2_com_softc *ucom) +{ + struct umoscom_softc *sc = ucom->sc_parent; + + usb2_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_WR]); +} + +static void +umoscom_write_callback(struct usb2_xfer *xfer) +{ + struct umoscom_softc *sc = xfer->priv_sc; + uint32_t actlen; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + case USB_ST_TRANSFERRED: +tr_setup: + DPRINTF("\n"); + + if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, + UMOSCOM_BUFSIZE, &actlen)) { + + xfer->frlengths[0] = actlen; + usb2_start_hardware(xfer); + } + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + DPRINTFN(0, "transfer failed\n"); + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static void +umoscom_read_callback(struct usb2_xfer *xfer) +{ + struct umoscom_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTF("got %d bytes\n", xfer->actlen); + usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen); + + case USB_ST_SETUP: +tr_setup: + DPRINTF("\n"); + + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + DPRINTFN(0, "transfer failed\n"); + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} + +static void +umoscom_intr_callback(struct usb2_xfer *xfer) +{ + struct umoscom_softc *sc = xfer->priv_sc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (xfer->actlen < 2) { + DPRINTF("too short message\n"); + goto tr_setup; + } + usb2_com_status_change(&sc->sc_ucom); + + case USB_ST_SETUP: +tr_setup: + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + return; + + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + DPRINTFN(0, "transfer failed\n"); + /* try to clear stall first */ + xfer->flags.stall_pipe = 1; + goto tr_setup; + } + return; + } +} |