From owner-svn-src-user@FreeBSD.ORG Sat Jun 19 21:11:06 2010 Return-Path: Delivered-To: svn-src-user@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id D5289106566C; Sat, 19 Jun 2010 21:11:06 +0000 (UTC) (envelope-from jmallett@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id C354A8FC0A; Sat, 19 Jun 2010 21:11:06 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o5JLB6NP096205; Sat, 19 Jun 2010 21:11:06 GMT (envelope-from jmallett@svn.freebsd.org) Received: (from jmallett@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o5JLB6I1096201; Sat, 19 Jun 2010 21:11:06 GMT (envelope-from jmallett@svn.freebsd.org) Message-Id: <201006192111.o5JLB6I1096201@svn.freebsd.org> From: Juli Mallett Date: Sat, 19 Jun 2010 21:11:06 +0000 (UTC) To: src-committers@freebsd.org, svn-src-user@freebsd.org X-SVN-Group: user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r209356 - in user/jmallett/octeon/sys/mips: cavium cavium/usb conf X-BeenThere: svn-src-user@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the experimental " user" src tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 19 Jun 2010 21:11:06 -0000 Author: jmallett Date: Sat Jun 19 21:11:06 2010 New Revision: 209356 URL: http://svn.freebsd.org/changeset/base/209356 Log: Add octusb driver from hps@ which uses the Simple Executive code to provide a simple host-mode controller. Only compile-tested. Submitted by: hps Added: user/jmallett/octeon/sys/mips/cavium/usb/ user/jmallett/octeon/sys/mips/cavium/usb/octusb.c user/jmallett/octeon/sys/mips/cavium/usb/octusb.h Modified: user/jmallett/octeon/sys/mips/cavium/files.octeon1 user/jmallett/octeon/sys/mips/conf/OCTEON1 user/jmallett/octeon/sys/mips/conf/OCTEON1-32 Modified: user/jmallett/octeon/sys/mips/cavium/files.octeon1 ============================================================================== --- user/jmallett/octeon/sys/mips/cavium/files.octeon1 Sat Jun 19 21:10:13 2010 (r209355) +++ user/jmallett/octeon/sys/mips/cavium/files.octeon1 Sat Jun 19 21:11:06 2010 (r209356) @@ -36,6 +36,8 @@ mips/cavium/octe/octebus.c optional oc mips/cavium/octopci.c optional pci mips/cavium/octopci_bus_space.c optional pci +mips/cavium/usb/octusb.c optional usb octusb + contrib/octeon-sdk/cvmx-cmd-queue.c optional octe contrib/octeon-sdk/cvmx-fpa.c optional octe contrib/octeon-sdk/cvmx-helper.c optional octe @@ -54,6 +56,8 @@ contrib/octeon-sdk/cvmx-spi.c optional contrib/octeon-sdk/cvmx-spi4000.c optional octe contrib/octeon-sdk/cvmx-twsi.c optional octe +contrib/octeon-sdk/cvmx-usb.c optional octusb + # XXX Some files could be excluded in some configurations. Making them # optional but on in the default config would seem reasonable. contrib/octeon-sdk/cvmx-bootmem.c standard Added: user/jmallett/octeon/sys/mips/cavium/usb/octusb.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/jmallett/octeon/sys/mips/cavium/usb/octusb.c Sat Jun 19 21:11:06 2010 (r209356) @@ -0,0 +1,1906 @@ +#include +__FBSDID("$FreeBSD: $"); + +/*- + * Copyright (c) 2010 Hans Petter Selasky. 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 file contains the driver for Octeon Executive Library USB + * Controller driver API. + */ + +/* TODO: The root HUB port callback is not yet implemented. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define USB_DEBUG_VAR octusbdebug + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#define OCTUSB_BUS2SC(bus) \ + ((struct octusb_softc *)(((uint8_t *)(bus)) - \ + ((uint8_t *)&(((struct octusb_softc *)0)->sc_bus)))) + +#ifdef USB_DEBUG +static int octusbdebug = 0; + +SYSCTL_NODE(_hw_usb, OID_AUTO, octusb, CTLFLAG_RW, 0, "OCTUSB"); +SYSCTL_INT(_hw_usb_octusb, OID_AUTO, debug, CTLFLAG_RW, + &octusbdebug, 0, "OCTUSB debug level"); + +TUNABLE_INT("hw.usb.octusb.debug", &octusbdebug); + +#endif + +struct octusb_std_temp { + octusb_cmd_t *func; + struct octusb_td *td; + struct octusb_td *td_next; + struct usb_page_cache *pc; + uint32_t offset; + uint32_t len; + uint8_t short_pkt; + uint8_t setup_alt_next; +}; + +extern struct usb_bus_methods octusb_bus_methods; +extern struct usb_pipe_methods octusb_device_bulk_methods; +extern struct usb_pipe_methods octusb_device_ctrl_methods; +extern struct usb_pipe_methods octusb_device_intr_methods; +extern struct usb_pipe_methods octusb_device_isoc_methods; + +static void octusb_standard_done(struct usb_xfer *); +static void octusb_device_done(struct usb_xfer *, usb_error_t); +static void octusb_timeout(void *); +static void octusb_do_poll(struct usb_bus *); + +static cvmx_usb_speed_t +octusb_convert_speed(enum usb_dev_speed speed) +{ + ; /* indent fix */ + switch (speed) { + case USB_SPEED_HIGH: + return (CVMX_USB_SPEED_HIGH); + case USB_SPEED_FULL: + return (CVMX_USB_SPEED_FULL); + default: + return (CVMX_USB_SPEED_LOW); + } +} + +static cvmx_usb_transfer_t +octusb_convert_ep_type(uint8_t ep_type) +{ + ; /* indent fix */ + switch (ep_type & UE_XFERTYPE) { + case UE_CONTROL: + return (CVMX_USB_TRANSFER_CONTROL); + case UE_INTERRUPT: + return (CVMX_USB_TRANSFER_INTERRUPT); + case UE_ISOCHRONOUS: + return (CVMX_USB_TRANSFER_ISOCHRONOUS); + case UE_BULK: + return (CVMX_USB_TRANSFER_BULK); + default: + return (0); /* should not happen */ + } +} + +static uint8_t +octusb_host_alloc_endpoint(struct octusb_td *td) +{ + struct octusb_softc *sc; + int ep_handle; + + if (td->qh->fixup_pending) + return (1); /* busy */ + + if (td->qh->ep_allocated) + return (0); /* success */ + + /* get softc */ + sc = td->qh->sc; + + ep_handle = cvmx_usb_open_pipe( + &sc->sc_port[td->qh->port_index].state, + 0, + td->qh->dev_addr, + td->qh->ep_num, + octusb_convert_speed(td->qh->dev_speed), + td->qh->max_packet_size, + octusb_convert_ep_type(td->qh->ep_type), + (td->qh->ep_num & UE_DIR_IN) ? CVMX_USB_DIRECTION_IN : + CVMX_USB_DIRECTION_OUT, + td->qh->ep_interval, + td->qh->ep_mult, + td->qh->hs_hub_addr, + td->qh->hs_hub_port); + + if (ep_handle < 0) + return (1); /* busy */ + + cvmx_usb_set_toggle( + &sc->sc_port[td->qh->port_index].state, + ep_handle, td->qh->ep_toggle_next); + + td->qh->fixup_handle = -1; + td->qh->fixup_complete = 0; + td->qh->fixup_len = 0; + td->qh->fixup_off = 0; + td->qh->fixup_pending = 0; + td->qh->fixup_actlen = 0; + + td->qh->ep_handle = ep_handle; + td->qh->ep_allocated = 1; + + return (0); /* success */ +} + +static void +octusb_host_free_endpoint(struct octusb_td *td) +{ + struct octusb_softc *sc; + + if (td->qh->ep_allocated == 0) + return; + + /* get softc */ + sc = td->qh->sc; + + if (td->qh->fixup_handle >= 0) { + /* cancel, if any */ + cvmx_usb_cancel(&sc->sc_port[td->qh->port_index].state, + td->qh->ep_handle, td->qh->fixup_handle); + } + cvmx_usb_close_pipe(&sc->sc_port[td->qh->port_index].state, td->qh->ep_handle); + + td->qh->ep_allocated = 0; +} + +static void +octusb_complete_cb(cvmx_usb_state_t *state, + cvmx_usb_callback_t reason, + cvmx_usb_complete_t status, + int pipe_handle, int submit_handle, + int bytes_transferred, void *user_data) +{ + struct octusb_td *td; + + if (reason != CVMX_USB_CALLBACK_TRANSFER_COMPLETE) + return; + + td = user_data; + + td->qh->fixup_complete = 1; + td->qh->fixup_pending = 0; + td->qh->fixup_actlen = bytes_transferred; + td->qh->fixup_handle = -1; + + switch (status) { + case CVMX_USB_COMPLETE_SUCCESS: + case CVMX_USB_COMPLETE_SHORT: + td->error_any = 0; + td->error_stall = 0; + break; + case CVMX_USB_COMPLETE_STALL: + td->error_stall = 1; + td->error_any = 1; + break; + default: + td->error_any = 1; + break; + } +} + +static uint8_t +octusb_host_control_header_tx(struct octusb_td *td) +{ + int status; + + /* allocate endpoint and check pending */ + if (octusb_host_alloc_endpoint(td)) + return (1); /* busy */ + + /* check error */ + if (td->error_any) + return (0); /* done */ + + if (td->qh->fixup_complete != 0) { + /* clear complete flag */ + td->qh->fixup_complete = 0; + + /* flush data */ + usb_pc_cpu_invalidate(td->qh->fixup_pc); + return (0); /* done */ + } + /* verify length */ + if (td->remainder != 8) { + td->error_any = 1; + return (0); /* done */ + } + usbd_copy_out(td->pc, td->offset, td->qh->fixup_buf, 8); + + /* update offset and remainder */ + td->offset += 8; + td->remainder -= 8; + + td->qh->fixup_len = UGETW(td->qh->fixup_buf + 6); + if (td->qh->fixup_len > (OCTUSB_MAX_FIXUP - 8)) { + td->error_any = 1; + return (0); /* done */ + } + td->qh->fixup_len += 8; + td->qh->fixup_off = 8; + + /* do control IN request */ + if (td->qh->fixup_buf[0] & UE_DIR_IN) { + + struct octusb_softc *sc; + + /* get softc */ + sc = td->qh->sc; + + /* flush data */ + usb_pc_cpu_flush(td->qh->fixup_pc); + + status = cvmx_usb_submit_control( + &sc->sc_port[td->qh->port_index].state, + td->qh->ep_handle, td->qh->fixup_phys, + td->qh->fixup_phys + 8ULL, td->qh->fixup_len - 8, + &octusb_complete_cb, td); + /* check status */ + if (status < 0) { + td->error_any = 1; + return (0); /* done */ + } + td->qh->fixup_handle = status; + td->qh->fixup_pending = 1; + td->qh->fixup_complete = 0; + + return (1); /* busy */ + } + return (0); /* done */ +} + +static uint8_t +octusb_host_control_data_tx(struct octusb_td *td) +{ + uint32_t rem; + + /* allocate endpoint and check pending */ + if (octusb_host_alloc_endpoint(td)) + return (1); /* busy */ + + /* check error */ + if (td->error_any) + return (0); /* done */ + + rem = td->qh->fixup_len - td->qh->fixup_off; + + if (td->remainder > rem) { + td->error_any = 1; + DPRINTF(1, "Excess setup transmit data\n"); + return (0); /* done */ + } + usbd_copy_out(td->pc, td->offset, td->qh->fixup_buf + td->qh->fixup_off, td->remainder); + + td->offset += td->remainder; + td->qh->fixup_off += td->remainder; + td->remainder = 0; + + return (0); /* done */ +} + +static uint8_t +octusb_host_control_data_rx(struct octusb_td *td) +{ + uint32_t rem; + + /* allocate endpoint and check pending */ + if (octusb_host_alloc_endpoint(td)) + return (1); /* busy */ + + /* check error */ + if (td->error_any) + return (0); /* done */ + + /* copy data from buffer */ + rem = 8 + td->qh->fixup_actlen - td->qh->fixup_off; + + if (rem > td->remainder) + rem = td->remainder; + + usbd_copy_in(td->pc, td->offset, td->qh->fixup_buf + td->qh->fixup_off, rem); + + td->offset += rem; + td->remainder -= rem; + td->qh->fixup_off += rem; + + return (1); /* done */ +} + +static uint8_t +octusb_host_control_status_tx(struct octusb_td *td) +{ + int status; + + /* allocate endpoint and check pending */ + if (octusb_host_alloc_endpoint(td)) + return (1); /* busy */ + + /* check error */ + if (td->error_any) + return (0); /* done */ + + if (td->qh->fixup_complete != 0) { + /* clear complete flag */ + td->qh->fixup_complete = 0; + /* done */ + return (0); + } + /* do control IN request */ + if (!(td->qh->fixup_buf[0] & UE_DIR_IN)) { + + struct octusb_softc *sc; + + /* get softc */ + sc = td->qh->sc; + + /* flush data */ + usb_pc_cpu_flush(td->qh->fixup_pc); + + /* start USB transfer */ + status = cvmx_usb_submit_control( + &sc->sc_port[td->qh->port_index].state, + td->qh->ep_handle, td->qh->fixup_phys, + td->qh->fixup_phys + 8ULL, td->qh->fixup_len - 8, + &octusb_complete_cb, td); + + /* check status */ + if (status < 0) { + td->error_any = 1; + return (0); /* done */ + } + td->qh->fixup_handle = status; + td->qh->fixup_pending = 1; + td->qh->fixup_complete = 0; + + return (1); /* busy */ + } + return (0); /* done */ +} + +static uint8_t +octusb_non_control_data_tx(struct octusb_td *td) +{ + struct octusb_softc *sc; + uint32_t rem; + int status; + + /* allocate endpoint and check pending */ + if (octusb_host_alloc_endpoint(td)) + return (1); /* busy */ + + /* check error */ + if (td->error_any) + return (0); /* done */ + + if ((td->qh->fixup_complete != 0) && + ((td->qh->ep_type & UE_XFERTYPE) == UE_ISOCHRONOUS)) { + td->qh->fixup_complete = 0; + return (0); /* done */ + } + /* check complete */ + if (td->remainder == 0) { + if (td->short_pkt) + return (0); /* complete */ + /* else need to send a zero length packet */ + rem = 0; + td->short_pkt = 1; + } else { + /* get maximum length */ + rem = OCTUSB_MAX_FIXUP % td->qh->max_frame_size; + rem = OCTUSB_MAX_FIXUP - rem; + + if (rem == 0) { + /* should not happen */ + DPRINTF(1, "Fixup buffer is too small\n"); + td->error_any = 1; + return (0); /* done */ + } + /* get minimum length */ + if (rem > td->remainder) { + rem = td->remainder; + if ((rem == 0) || (rem % td->qh->max_frame_size)) + td->short_pkt = 1; + } + /* copy data into fixup buffer */ + usbd_copy_out(td->pc, td->offset, td->qh->fixup_buf, rem); + + /* flush data */ + usb_pc_cpu_flush(td->qh->fixup_pc); + + /* pre-increment TX buffer offset */ + td->offset += rem; + td->remainder -= rem; + } + + /* get softc */ + sc = td->qh->sc; + + switch (td->qh->ep_type & UE_XFERTYPE) { + case UE_ISOCHRONOUS: + td->qh->iso_pkt.offset = 0; + td->qh->iso_pkt.length = rem; + td->qh->iso_pkt.status = 0; + /* start USB transfer */ + status = cvmx_usb_submit_isochronous(&sc->sc_port[td->qh->port_index].state, + td->qh->ep_handle, 1, CVMX_USB_ISOCHRONOUS_FLAGS_ALLOW_SHORT | + CVMX_USB_ISOCHRONOUS_FLAGS_ASAP, 1, &td->qh->iso_pkt, + td->qh->fixup_phys, rem, &octusb_complete_cb, td); + break; + case UE_BULK: + /* start USB transfer */ + status = cvmx_usb_submit_bulk(&sc->sc_port[td->qh->port_index].state, + td->qh->ep_handle, td->qh->fixup_phys, rem, &octusb_complete_cb, td); + break; + case UE_INTERRUPT: + /* start USB transfer (interrupt or interrupt) */ + status = cvmx_usb_submit_interrupt(&sc->sc_port[td->qh->port_index].state, + td->qh->ep_handle, td->qh->fixup_phys, rem, &octusb_complete_cb, td); + break; + default: + status = -1; + break; + } + + /* check status */ + if (status < 0) { + td->error_any = 1; + return (0); /* done */ + } + td->qh->fixup_handle = status; + td->qh->fixup_len = rem; + td->qh->fixup_pending = 1; + td->qh->fixup_complete = 0; + + return (1); /* busy */ +} + +static uint8_t +octusb_non_control_data_rx(struct octusb_td *td) +{ + struct octusb_softc *sc; + uint32_t rem; + int status; + uint8_t got_short; + + /* allocate endpoint and check pending */ + if (octusb_host_alloc_endpoint(td)) + return (1); /* busy */ + + /* check error */ + if (td->error_any) + return (0); /* done */ + + got_short = 0; + + if (td->qh->fixup_complete != 0) { + + /* invalidate data */ + usb_pc_cpu_invalidate(td->qh->fixup_pc); + + rem = td->qh->fixup_actlen; + + /* verify transfer length */ + if (rem != td->qh->fixup_len) { + if (rem < td->qh->fixup_len) { + /* we have a short packet */ + td->short_pkt = 1; + got_short = 1; + } else { + /* invalid USB packet */ + td->error_any = 1; + return (0); /* we are complete */ + } + } + /* copy data into fixup buffer */ + usbd_copy_in(td->pc, td->offset, td->qh->fixup_buf, rem); + + /* post-increment RX buffer offset */ + td->offset += rem; + td->remainder -= rem; + + td->qh->fixup_complete = 0; + + if ((td->qh->ep_type & UE_XFERTYPE) == UE_ISOCHRONOUS) + return (0); /* done */ + } + /* check if we are complete */ + if ((td->remainder == 0) || got_short) { + if (td->short_pkt) { + /* we are complete */ + return (0); + } + /* else need to receive a zero length packet */ + rem = 0; + td->short_pkt = 1; + } else { + /* get maximum length */ + rem = OCTUSB_MAX_FIXUP % td->qh->max_frame_size; + rem = OCTUSB_MAX_FIXUP - rem; + + if (rem == 0) { + /* should not happen */ + DPRINTF(1, "Fixup buffer is too small\n"); + td->error_any = 1; + return (0); /* done */ + } + /* get minimum length */ + if (rem > td->remainder) + rem = td->remainder; + } + + /* invalidate data */ + usb_pc_cpu_invalidate(td->qh->fixup_pc); + + /* get softc */ + sc = td->qh->sc; + + switch (td->qh->ep_type & UE_XFERTYPE) { + case UE_ISOCHRONOUS: + td->qh->iso_pkt.offset = 0; + td->qh->iso_pkt.length = rem; + td->qh->iso_pkt.status = 0; + /* start USB transfer */ + status = cvmx_usb_submit_isochronous(&sc->sc_port[td->qh->port_index].state, + td->qh->ep_handle, 1, CVMX_USB_ISOCHRONOUS_FLAGS_ALLOW_SHORT | + CVMX_USB_ISOCHRONOUS_FLAGS_ASAP, 1, &td->qh->iso_pkt, + td->qh->fixup_phys, rem, &octusb_complete_cb, td); + break; + case UE_BULK: + /* start USB transfer */ + status = cvmx_usb_submit_bulk(&sc->sc_port[td->qh->port_index].state, + td->qh->ep_handle, td->qh->fixup_phys, rem, &octusb_complete_cb, td); + break; + case UE_INTERRUPT: + /* start USB transfer */ + status = cvmx_usb_submit_interrupt(&sc->sc_port[td->qh->port_index].state, + td->qh->ep_handle, td->qh->fixup_phys, rem, &octusb_complete_cb, td); + break; + default: + status = -1; + break; + } + + /* check status */ + if (status < 0) { + td->error_any = 1; + return (0); /* done */ + } + td->qh->fixup_handle = status; + td->qh->fixup_len = rem; + td->qh->fixup_pending = 1; + td->qh->fixup_complete = 0; + + return (1); /* busy */ +} + +static uint8_t +octusb_xfer_do_fifo(struct usb_xfer *xfer) +{ + struct octusb_td *td; + + DPRINTFN(8, "\n"); + + td = xfer->td_transfer_cache; + + while (1) { + if ((td->func) (td)) { + /* operation in progress */ + break; + } + if (((void *)td) == xfer->td_transfer_last) { + goto done; + } + if (td->error_any) { + goto done; + } else if (td->remainder > 0) { + /* + * We had a short transfer. If there is no + * alternate next, stop processing ! + */ + if (td->alt_next == 0) + goto done; + } + /* + * Fetch the next transfer descriptor and transfer + * some flags to the next transfer descriptor + */ + td = td->obj_next; + xfer->td_transfer_cache = td; + } + return (1); /* not complete */ + +done: + /* compute all actual lengths */ + + octusb_standard_done(xfer); + + return (0); /* complete */ +} + +static usb_error_t +octusb_standard_done_sub(struct usb_xfer *xfer) +{ + struct octusb_td *td; + uint32_t len; + usb_error_t error; + + DPRINTFN(8, "\n"); + + td = xfer->td_transfer_cache; + + do { + len = td->remainder; + + if (xfer->aframes != xfer->nframes) { + /* + * Verify the length and subtract + * the remainder from "frlengths[]": + */ + if (len > xfer->frlengths[xfer->aframes]) { + td->error_any = 1; + } else { + xfer->frlengths[xfer->aframes] -= len; + } + } + /* Check for transfer error */ + if (td->error_any) { + /* the transfer is finished */ + error = td->error_stall ? USB_ERR_STALLED : USB_ERR_IOERROR; + td = NULL; + break; + } + /* Check for short transfer */ + if (len > 0) { + if (xfer->flags_int.short_frames_ok) { + /* follow alt next */ + if (td->alt_next) { + td = td->obj_next; + } else { + td = NULL; + } + } else { + /* the transfer is finished */ + td = NULL; + } + error = 0; + break; + } + td = td->obj_next; + + /* this USB frame is complete */ + error = 0; + break; + + } while (0); + + /* update transfer cache */ + + xfer->td_transfer_cache = td; + + return (error); +} + +static void +octusb_standard_done(struct usb_xfer *xfer) +{ + struct octusb_softc *sc; + struct octusb_qh *qh; + usb_error_t error = 0; + + DPRINTFN(12, "xfer=%p endpoint=%p transfer done\n", + xfer, xfer->endpoint); + + /* reset scanner */ + + xfer->td_transfer_cache = xfer->td_transfer_first; + + if (xfer->flags_int.control_xfr) { + + if (xfer->flags_int.control_hdr) + error = octusb_standard_done_sub(xfer); + + xfer->aframes = 1; + + if (xfer->td_transfer_cache == NULL) + goto done; + } + while (xfer->aframes != xfer->nframes) { + + error = octusb_standard_done_sub(xfer); + + xfer->aframes++; + + if (xfer->td_transfer_cache == NULL) + goto done; + } + + if (xfer->flags_int.control_xfr && + !xfer->flags_int.control_act) + error = octusb_standard_done_sub(xfer); + +done: + /* update data toggle */ + + qh = xfer->qh_start[0]; + sc = qh->sc; + + xfer->endpoint->toggle_next = + cvmx_usb_get_toggle( + &sc->sc_port[qh->port_index].state, + qh->ep_handle) ? 1 : 0; + + octusb_device_done(xfer, error); +} + +static void +octusb_interrupt_poll(struct octusb_softc *sc) +{ + struct usb_xfer *xfer; + uint8_t x; + + /* poll all ports */ + for (x = 0; x != sc->sc_noport; x++) + cvmx_usb_poll(&sc->sc_port[x].state); + +repeat: + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + if (!octusb_xfer_do_fifo(xfer)) { + /* queue has been modified */ + goto repeat; + } + } +} + +static void +octusb_start_standard_chain(struct usb_xfer *xfer) +{ + DPRINTFN(8, "\n"); + + /* poll one time */ + if (octusb_xfer_do_fifo(xfer)) { + + /* put transfer on interrupt queue */ + usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); + + /* start timeout, if any */ + if (xfer->timeout != 0) { + usbd_transfer_timeout_ms(xfer, + &octusb_timeout, xfer->timeout); + } + } +} + +void +octusb_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb) +{ + +} + +usb_error_t +octusb_init(struct octusb_softc *sc) +{ + int status; + uint8_t x; + + /* flush all cache into memory */ + + usb_bus_mem_flush_all(&sc->sc_bus, &octusb_iterate_hw_softc); + + /* set up the bus struct */ + sc->sc_bus.methods = &octusb_bus_methods; + + /* get number of ports */ + sc->sc_noport = cvmx_usb_get_num_ports(); + + /* check number of ports */ + if (sc->sc_noport > OCTUSB_MAX_PORTS) + sc->sc_noport = OCTUSB_MAX_PORTS; + + USB_BUS_LOCK(&sc->sc_bus); + + /* setup all ports */ + for (x = 0; x != sc->sc_noport; x++) { + status = cvmx_usb_initialize( + &sc->sc_port[x].state, x, + CVMX_USB_INITIALIZE_FLAGS_CLOCK_AUTO); + if (status < 0) + sc->sc_port[x].disabled = 1; + } + + USB_BUS_UNLOCK(&sc->sc_bus); + + /* catch lost interrupts */ + octusb_do_poll(&sc->sc_bus); + + return (0); +} + +usb_error_t +octusb_uninit(struct octusb_softc *sc) +{ + uint8_t x; + + USB_BUS_LOCK(&sc->sc_bus); + + for (x = 0; x != sc->sc_noport; x++) { + if (sc->sc_port[x].disabled == 0) + cvmx_usb_shutdown(&sc->sc_port[x].state); + } + USB_BUS_UNLOCK(&sc->sc_bus); + + return (0); + +} + +void +octusb_suspend(struct octusb_softc *sc) +{ + +} + +void +octusb_resume(struct octusb_softc *sc) +{ + +} + +/*------------------------------------------------------------------------* + * octusb_interrupt - OCTUSB interrupt handler + *------------------------------------------------------------------------*/ +void +octusb_interrupt(struct octusb_softc *sc) +{ + USB_BUS_LOCK(&sc->sc_bus); + + DPRINTFN(16, "real interrupt\n"); + + /* poll all the USB transfers */ + octusb_interrupt_poll(sc); + + USB_BUS_UNLOCK(&sc->sc_bus); +} + +/*------------------------------------------------------------------------* + * octusb_timeout - OCTUSB transfer timeout handler + *------------------------------------------------------------------------*/ +static void +octusb_timeout(void *arg) +{ + struct usb_xfer *xfer = arg; + + DPRINTF("xfer=%p\n", xfer); + + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); + + /* transfer is transferred */ + octusb_device_done(xfer, USB_ERR_TIMEOUT); +} + +/*------------------------------------------------------------------------* + * octusb_do_poll - OCTUSB poll transfers + *------------------------------------------------------------------------*/ +static void +octusb_do_poll(struct usb_bus *bus) +{ + struct octusb_softc *sc = OCTUSB_BUS2SC(bus); + + USB_BUS_LOCK(&sc->sc_bus); + octusb_interrupt_poll(sc); + USB_BUS_UNLOCK(&sc->sc_bus); +} + +static void +octusb_setup_standard_chain_sub(struct octusb_std_temp *temp) +{ + struct octusb_td *td; + + /* get current Transfer Descriptor */ + td = temp->td_next; + temp->td = td; + + /* prepare for next TD */ + temp->td_next = td->obj_next; + + /* fill out the Transfer Descriptor */ + td->func = temp->func; + td->pc = temp->pc; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***