svn commit: r192554 - head/sys/dev/usb/controller
Andrew Thompson
thompsa at FreeBSD.org
Thu May 21 17:42:32 UTC 2009
Author: thompsa
Date: Thu May 21 17:42:32 2009
New Revision: 192554
URL: http://svn.freebsd.org/changeset/base/192554
Log:
Add a driver for the AVR32 series USB Device Controller. Not hooked up as
FreeBSD does not yet support this platform but it makes it easier to stay in
sync.
Submitted by: Hans Petter Selasky
Added:
head/sys/dev/usb/controller/avr32dci.c (contents, props changed)
head/sys/dev/usb/controller/avr32dci.h (contents, props changed)
Added: head/sys/dev/usb/controller/avr32dci.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ head/sys/dev/usb/controller/avr32dci.c Thu May 21 17:42:32 2009 (r192554)
@@ -0,0 +1,2065 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2009 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 the AVR32 series USB Device
+ * Controller
+ */
+
+/*
+ * NOTE: When the chip detects BUS-reset it will also reset the
+ * endpoints, Function-address and more.
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR avr32dci_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/avr32dci.h>
+
+#define AVR32_BUS2SC(bus) \
+ ((struct avr32dci_softc *)(((uint8_t *)(bus)) - \
+ ((uint8_t *)&(((struct avr32dci_softc *)0)->sc_bus))))
+
+#define AVR32_PC2SC(pc) \
+ AVR32_BUS2SC(USB_DMATAG_TO_XROOT((pc)->tag_parent)->bus)
+
+#if USB_DEBUG
+static int avr32dci_debug = 0;
+
+SYSCTL_NODE(_hw_usb, OID_AUTO, avr32dci, CTLFLAG_RW, 0, "USB AVR32 DCI");
+SYSCTL_INT(_hw_usb_avr32dci, OID_AUTO, debug, CTLFLAG_RW,
+ &avr32dci_debug, 0, "AVR32 DCI debug level");
+#endif
+
+#define AVR32_INTR_ENDPT 1
+
+/* prototypes */
+
+struct usb2_bus_methods avr32dci_bus_methods;
+struct usb2_pipe_methods avr32dci_device_non_isoc_methods;
+struct usb2_pipe_methods avr32dci_device_isoc_fs_methods;
+
+static avr32dci_cmd_t avr32dci_setup_rx;
+static avr32dci_cmd_t avr32dci_data_rx;
+static avr32dci_cmd_t avr32dci_data_tx;
+static avr32dci_cmd_t avr32dci_data_tx_sync;
+static void avr32dci_device_done(struct usb2_xfer *, usb2_error_t);
+static void avr32dci_do_poll(struct usb2_bus *);
+static void avr32dci_standard_done(struct usb2_xfer *);
+static void avr32dci_root_intr(struct avr32dci_softc *sc);
+
+/*
+ * Here is a list of what the chip supports:
+ */
+static const struct usb2_hw_ep_profile
+ avr32dci_ep_profile[4] = {
+
+ [0] = {
+ .max_in_frame_size = 64,
+ .max_out_frame_size = 64,
+ .is_simplex = 1,
+ .support_control = 1,
+ },
+
+ [1] = {
+ .max_in_frame_size = 512,
+ .max_out_frame_size = 512,
+ .is_simplex = 1,
+ .support_bulk = 1,
+ .support_interrupt = 1,
+ .support_isochronous = 1,
+ .support_in = 1,
+ .support_out = 1,
+ },
+
+ [2] = {
+ .max_in_frame_size = 64,
+ .max_out_frame_size = 64,
+ .is_simplex = 1,
+ .support_bulk = 1,
+ .support_interrupt = 1,
+ .support_in = 1,
+ .support_out = 1,
+ },
+
+ [3] = {
+ .max_in_frame_size = 1024,
+ .max_out_frame_size = 1024,
+ .is_simplex = 1,
+ .support_bulk = 1,
+ .support_interrupt = 1,
+ .support_isochronous = 1,
+ .support_in = 1,
+ .support_out = 1,
+ },
+};
+
+static void
+avr32dci_get_hw_ep_profile(struct usb2_device *udev,
+ const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr)
+{
+ if (ep_addr == 0)
+ *ppf = avr32dci_ep_profile;
+ else if (ep_addr < 3)
+ *ppf = avr32dci_ep_profile + 1;
+ else if (ep_addr < 5)
+ *ppf = avr32dci_ep_profile + 2;
+ else if (ep_addr < 7)
+ *ppf = avr32dci_ep_profile + 3;
+ else
+ *ppf = NULL;
+}
+
+static void
+avr32dci_mod_ctrl(struct avr32dci_softc *sc, uint32_t set, uint32_t clear)
+{
+ uint32_t temp;
+
+ temp = AVR32_READ_4(sc, AVR32_CTRL);
+ temp |= set;
+ temp &= ~clear;
+ AVR32_WRITE_4(sc, AVR32_CTRL, temp);
+}
+
+static void
+avr32dci_mod_ien(struct avr32dci_softc *sc, uint32_t set, uint32_t clear)
+{
+ uint32_t temp;
+
+ temp = AVR32_READ_4(sc, AVR32_IEN);
+ temp |= set;
+ temp &= ~clear;
+ AVR32_WRITE_4(sc, AVR32_IEN, temp);
+}
+
+static void
+avr32dci_clocks_on(struct avr32dci_softc *sc)
+{
+ if (sc->sc_flags.clocks_off &&
+ sc->sc_flags.port_powered) {
+
+ DPRINTFN(5, "\n");
+
+ /* turn on clocks */
+ (sc->sc_clocks_on) (&sc->sc_bus);
+
+ avr32dci_mod_ctrl(sc, AVR32_CTRL_DEV_EN_USBA, 0);
+
+ sc->sc_flags.clocks_off = 0;
+ }
+}
+
+static void
+avr32dci_clocks_off(struct avr32dci_softc *sc)
+{
+ if (!sc->sc_flags.clocks_off) {
+
+ DPRINTFN(5, "\n");
+
+ avr32dci_mod_ctrl(sc, 0, AVR32_CTRL_DEV_EN_USBA);
+
+ /* turn clocks off */
+ (sc->sc_clocks_off) (&sc->sc_bus);
+
+ sc->sc_flags.clocks_off = 1;
+ }
+}
+
+static void
+avr32dci_pull_up(struct avr32dci_softc *sc)
+{
+ /* pullup D+, if possible */
+
+ if (!sc->sc_flags.d_pulled_up &&
+ sc->sc_flags.port_powered) {
+ sc->sc_flags.d_pulled_up = 1;
+ avr32dci_mod_ctrl(sc, 0, AVR32_CTRL_DEV_DETACH);
+ }
+}
+
+static void
+avr32dci_pull_down(struct avr32dci_softc *sc)
+{
+ /* pulldown D+, if possible */
+
+ if (sc->sc_flags.d_pulled_up) {
+ sc->sc_flags.d_pulled_up = 0;
+ avr32dci_mod_ctrl(sc, AVR32_CTRL_DEV_DETACH, 0);
+ }
+}
+
+static void
+avr32dci_wakeup_peer(struct avr32dci_softc *sc)
+{
+ if (!sc->sc_flags.status_suspend) {
+ return;
+ }
+ avr32dci_mod_ctrl(sc, AVR32_CTRL_DEV_REWAKEUP, 0);
+
+ /* wait 8 milliseconds */
+ /* Wait for reset to complete. */
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125);
+
+ /* hardware should have cleared RMWKUP bit */
+}
+
+static void
+avr32dci_set_address(struct avr32dci_softc *sc, uint8_t addr)
+{
+ DPRINTFN(5, "addr=%d\n", addr);
+
+ avr32dci_mod_ctrl(sc, AVR32_UDADDR_ADDEN | addr, 0);
+}
+
+static uint8_t
+avr32dci_setup_rx(struct avr32dci_td *td)
+{
+ struct avr32dci_softc *sc;
+ struct usb2_device_request req;
+ uint16_t count;
+ uint32_t temp;
+
+ /* get pointer to softc */
+ sc = AVR32_PC2SC(td->pc);
+
+ /* check endpoint status */
+ temp = AVR32_READ_4(sc, AVR32_EPTSTA(td->ep_no));
+
+ DPRINTFN(5, "EPTSTA(%u)=0x%08x\n", td->ep_no, temp);
+
+ if (!(temp & AVR32_EPTSTA_RX_SETUP)) {
+ goto not_complete;
+ }
+ /* clear did stall */
+ td->did_stall = 0;
+ /* get the packet byte count */
+ count = AVR32_EPTSTA_BYTE_COUNT(temp);
+
+ /* verify data length */
+ if (count != td->remainder) {
+ DPRINTFN(0, "Invalid SETUP packet "
+ "length, %d bytes\n", count);
+ goto not_complete;
+ }
+ if (count != sizeof(req)) {
+ DPRINTFN(0, "Unsupported SETUP packet "
+ "length, %d bytes\n", count);
+ goto not_complete;
+ }
+ /* receive data */
+ memcpy(&req, sc->physdata, sizeof(req));
+
+ /* copy data into real buffer */
+ usb2_copy_in(td->pc, 0, &req, sizeof(req));
+
+ td->offset = sizeof(req);
+ td->remainder = 0;
+
+ /* sneak peek the set address */
+ if ((req.bmRequestType == UT_WRITE_DEVICE) &&
+ (req.bRequest == UR_SET_ADDRESS)) {
+ sc->sc_dv_addr = req.wValue[0] & 0x7F;
+ /* must write address before ZLP */
+ avr32dci_mod_ctrl(sc, 0, AVR32_CTRL_DEV_FADDR_EN |
+ AVR32_CTRL_DEV_ADDR);
+ avr32dci_mod_ctrl(sc, sc->sc_dv_addr, 0);
+ } else {
+ sc->sc_dv_addr = 0xFF;
+ }
+
+ /* clear SETUP packet interrupt */
+ AVR32_WRITE_4(sc, AVR32_EPTCLRSTA(td->ep_no), AVR32_EPTSTA_RX_SETUP);
+ return (0); /* complete */
+
+not_complete:
+ if (temp & AVR32_EPTSTA_RX_SETUP) {
+ /* clear SETUP packet interrupt */
+ AVR32_WRITE_4(sc, AVR32_EPTCLRSTA(td->ep_no), AVR32_EPTSTA_RX_SETUP);
+ }
+ /* abort any ongoing transfer */
+ if (!td->did_stall) {
+ DPRINTFN(5, "stalling\n");
+ AVR32_WRITE_4(sc, AVR32_EPTSETSTA(td->ep_no),
+ AVR32_EPTSTA_FRCESTALL);
+ td->did_stall = 1;
+ }
+ return (1); /* not complete */
+}
+
+static uint8_t
+avr32dci_data_rx(struct avr32dci_td *td)
+{
+ struct avr32dci_softc *sc;
+ struct usb2_page_search buf_res;
+ uint16_t count;
+ uint32_t temp;
+ uint8_t to;
+ uint8_t got_short;
+
+ to = 4; /* don't loop forever! */
+ got_short = 0;
+
+ /* get pointer to softc */
+ sc = AVR32_PC2SC(td->pc);
+
+repeat:
+ /* check if any of the FIFO banks have data */
+ /* check endpoint status */
+ temp = AVR32_READ_4(sc, AVR32_EPTSTA(td->ep_no));
+
+ DPRINTFN(5, "EPTSTA(%u)=0x%08x\n", td->ep_no, temp);
+
+ if (temp & AVR32_EPTSTA_RX_SETUP) {
+ if (td->remainder == 0) {
+ /*
+ * We are actually complete and have
+ * received the next SETUP
+ */
+ DPRINTFN(5, "faking complete\n");
+ return (0); /* complete */
+ }
+ /*
+ * USB Host Aborted the transfer.
+ */
+ td->error = 1;
+ return (0); /* complete */
+ }
+ /* check status */
+ if (!(temp & AVR32_EPTSTA_RX_BK_RDY)) {
+ /* no data */
+ goto not_complete;
+ }
+ /* get the packet byte count */
+ count = AVR32_EPTSTA_BYTE_COUNT(temp);
+
+ /* verify the packet byte count */
+ if (count != td->max_packet_size) {
+ if (count < td->max_packet_size) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ got_short = 1;
+ } else {
+ /* invalid USB packet */
+ td->error = 1;
+ return (0); /* we are complete */
+ }
+ }
+ /* verify the packet byte count */
+ if (count > td->remainder) {
+ /* invalid USB packet */
+ td->error = 1;
+ return (0); /* we are complete */
+ }
+ while (count > 0) {
+ usb2_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* receive data */
+ bcopy(sc->physdata +
+ (AVR32_EPTSTA_CURRENT_BANK(temp) << td->bank_shift) +
+ (td->ep_no << 16) + (td->offset % td->max_packet_size),
+ buf_res.buffer, buf_res.length)
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* clear OUT packet interrupt */
+ AVR32_WRITE_4(sc, AVR32_EPTCLRSTA(td->ep_no), AVR32_EPTSTA_RX_BK_RDY);
+
+ /* 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 */
+ }
+ if (--to) {
+ goto repeat;
+ }
+not_complete:
+ return (1); /* not complete */
+}
+
+static uint8_t
+avr32dci_data_tx(struct avr32dci_td *td)
+{
+ struct avr32dci_softc *sc;
+ struct usb2_page_search buf_res;
+ uint16_t count;
+ uint8_t to;
+ uint32_t temp;
+
+ to = 4; /* don't loop forever! */
+
+ /* get pointer to softc */
+ sc = AVR32_PC2SC(td->pc);
+
+repeat:
+
+ /* check endpoint status */
+ temp = AVR32_READ_4(sc, AVR32_EPTSTA(td->ep_no));
+
+ DPRINTFN(5, "EPTSTA(%u)=0x%08x\n", td->ep_no, temp);
+
+ if (temp & AVR32_EPTSTA_RX_SETUP) {
+ /*
+ * The current transfer was aborted
+ * by the USB Host
+ */
+ td->error = 1;
+ return (0); /* complete */
+ }
+ if (temp & AVR32_EPTSTA_TX_PK_RDY) {
+ /* cannot write any data - all banks are busy */
+ goto not_complete;
+ }
+ count = td->max_packet_size;
+ if (td->remainder < count) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ count = td->remainder;
+ }
+ while (count > 0) {
+
+ usb2_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* transmit data */
+ bcopy(buf_res.buffer, sc->physdata +
+ (AVR32_EPTSTA_CURRENT_BANK(temp) << td->bank_shift) +
+ (td->ep_no << 16) + (td->offset % td->max_packet_size),
+ buf_res.length)
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* allocate FIFO bank */
+ AVR32_WRITE_4(sc, AVR32_EPTCLRSTA(td->ep_no), AVR32_EPTSTA_TX_BK_RDY);
+
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt) {
+ return (0); /* complete */
+ }
+ /* else we need to transmit a short packet */
+ }
+ if (--to) {
+ goto repeat;
+ }
+not_complete:
+ return (1); /* not complete */
+}
+
+static uint8_t
+avr32dci_data_tx_sync(struct avr32dci_td *td)
+{
+ struct avr32dci_softc *sc;
+ uint32_t temp;
+
+ /* get pointer to softc */
+ sc = AVR32_PC2SC(td->pc);
+
+ /* check endpoint status */
+ temp = AVR32_READ_4(sc, AVR32_EPTSTA(td->ep_no));
+
+ DPRINTFN(5, "EPTSTA(%u)=0x%08x\n", td->ep_no, temp);
+
+ if (temp & AVR32_EPTSTA_RX_SETUP) {
+ DPRINTFN(5, "faking complete\n");
+ /* Race condition */
+ return (0); /* complete */
+ }
+ /*
+ * The control endpoint has only got one bank, so if that bank
+ * is free the packet has been transferred!
+ */
+ if (AVR32_EPTSTA_BUSY_BANK_STA(temp) != 0) {
+ /* cannot write any data - a bank is busy */
+ goto not_complete;
+ }
+ if (sc->sc_dv_addr != 0xFF) {
+ /* set new address */
+ avr32dci_set_address(sc, sc->sc_dv_addr);
+ }
+ return (0); /* complete */
+
+not_complete:
+ return (1); /* not complete */
+}
+
+static uint8_t
+avr32dci_xfer_do_fifo(struct usb2_xfer *xfer)
+{
+ struct avr32dci_td *td;
+
+ DPRINTFN(9, "\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) {
+ goto done;
+ } else if (td->remainder > 0) {
+ /*
+ * We had a short transfer. If there is no alternate
+ * next, stop processing !
+ */
+ if (!td->alt_next) {
+ 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 */
+
+ avr32dci_standard_done(xfer);
+ return (0); /* complete */
+}
+
+static void
+avr32dci_interrupt_poll(struct avr32dci_softc *sc)
+{
+ struct usb2_xfer *xfer;
+
+repeat:
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+ if (!avr32dci_xfer_do_fifo(xfer)) {
+ /* queue has been modified */
+ goto repeat;
+ }
+ }
+}
+
+void
+avr32dci_vbus_interrupt(struct avr32dci_softc *sc, uint8_t is_on)
+{
+ DPRINTFN(5, "vbus = %u\n", is_on);
+
+ if (is_on) {
+ if (!sc->sc_flags.status_vbus) {
+ sc->sc_flags.status_vbus = 1;
+
+ /* complete root HUB interrupt endpoint */
+
+ avr32dci_root_intr(sc);
+ }
+ } else {
+ if (sc->sc_flags.status_vbus) {
+ sc->sc_flags.status_vbus = 0;
+ sc->sc_flags.status_bus_reset = 0;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ /* complete root HUB interrupt endpoint */
+
+ avr32dci_root_intr(sc);
+ }
+ }
+}
+
+void
+avr32dci_interrupt(struct avr32dci_softc *sc)
+{
+ uint32_t status;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* read interrupt status */
+ status = AVR32_READ_4(sc, AVR32_INTSTA);
+
+ /* clear all set interrupts */
+ AVR32_WRITE_4(sc, AVR32_CLRINT, status);
+
+ DPRINTFN(14, "INTSTA=0x%08x\n", status);
+
+ /* check for any bus state change interrupts */
+ if (status & AVR32_INT_ENDRESET) {
+
+ DPRINTFN(5, "end of reset\n");
+
+ /* set correct state */
+ sc->sc_flags.status_bus_reset = 1;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ /* disable resume interrupt */
+ avr32dci_mod_ien(sc, AVR32_INT_DET_SUSPD |
+ AVR32_INT_ENDRESET, AVR32_INT_WAKE_UP);
+
+ /* complete root HUB interrupt endpoint */
+ avr32dci_root_intr(sc);
+ }
+ /*
+ * If resume and suspend is set at the same time we interpret
+ * that like RESUME. Resume is set when there is at least 3
+ * milliseconds of inactivity on the USB BUS.
+ */
+ if (status & AVR32_INT_WAKE_UP) {
+
+ DPRINTFN(5, "resume interrupt\n");
+
+ if (sc->sc_flags.status_suspend) {
+ /* update status bits */
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 1;
+
+ /* disable resume interrupt */
+ avr32dci_mod_ien(sc, AVR32_INT_DET_SUSPD |
+ AVR32_INT_ENDRESET, AVR32_INT_WAKE_UP);
+
+ /* complete root HUB interrupt endpoint */
+ avr32dci_root_intr(sc);
+ }
+ } else if (status & AVR32_INT_DET_SUSPD) {
+
+ DPRINTFN(5, "suspend interrupt\n");
+
+ if (!sc->sc_flags.status_suspend) {
+ /* update status bits */
+ sc->sc_flags.status_suspend = 1;
+ sc->sc_flags.change_suspend = 1;
+
+ /* disable suspend interrupt */
+ avr32dci_mod_ien(sc, AVR32_INT_WAKE_UP |
+ AVR32_INT_ENDRESET, AVR32_INT_DET_SUSPD);
+
+ /* complete root HUB interrupt endpoint */
+ avr32dci_root_intr(sc);
+ }
+ }
+ /* check for any endpoint interrupts */
+ if (status & -AVR32_INT_EPT_INT(0)) {
+
+ DPRINTFN(5, "real endpoint interrupt\n");
+
+ avr32dci_interrupt_poll(sc);
+ }
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+avr32dci_setup_standard_chain_sub(struct avr32dci_std_temp *temp)
+{
+ struct avr32dci_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;
+ td->offset = temp->offset;
+ td->remainder = temp->len;
+ td->error = 0;
+ td->did_stall = temp->did_stall;
+ td->short_pkt = temp->short_pkt;
+ td->alt_next = temp->setup_alt_next;
+}
+
+static void
+avr32dci_setup_standard_chain(struct usb2_xfer *xfer)
+{
+ struct avr32dci_std_temp temp;
+ struct avr32dci_softc *sc;
+ struct avr32dci_td *td;
+ uint32_t x;
+ uint8_t ep_no;
+ uint8_t need_sync;
+
+ DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n",
+ xfer->address, UE_GET_ADDR(xfer->endpoint),
+ xfer->sumlen, usb2_get_speed(xfer->xroot->udev));
+
+ temp.max_frame_size = xfer->max_frame_size;
+
+ td = xfer->td_start[0];
+ xfer->td_transfer_first = td;
+ xfer->td_transfer_cache = td;
+
+ /* setup temp */
+
+ temp.td = NULL;
+ temp.td_next = xfer->td_start[0];
+ temp.offset = 0;
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.did_stall = !xfer->flags_int.control_stall;
+
+ sc = AVR32_BUS2SC(xfer->xroot->bus);
+ ep_no = (xfer->endpoint & UE_ADDR);
+
+ /* check if we should prepend a setup message */
+
+ if (xfer->flags_int.control_xfr) {
+ if (xfer->flags_int.control_hdr) {
+
+ temp.func = &avr32dci_setup_rx;
+ temp.len = xfer->frlengths[0];
+ temp.pc = xfer->frbuffers + 0;
+ temp.short_pkt = temp.len ? 1 : 0;
+ /* check for last frame */
+ if (xfer->nframes == 1) {
+ /* no STATUS stage yet, SETUP is last */
+ if (xfer->flags_int.control_act)
+ temp.setup_alt_next = 0;
+ }
+ avr32dci_setup_standard_chain_sub(&temp);
+ }
+ x = 1;
+ } else {
+ x = 0;
+ }
+
+ if (x != xfer->nframes) {
+ if (xfer->endpoint & UE_DIR_IN) {
+ temp.func = &avr32dci_data_tx;
+ need_sync = 1;
+ } else {
+ temp.func = &avr32dci_data_rx;
+ need_sync = 0;
+ }
+
+ /* setup "pc" pointer */
+ temp.pc = xfer->frbuffers + x;
+ } else {
+ need_sync = 0;
+ }
+ while (x != xfer->nframes) {
+
+ /* DATA0 / DATA1 message */
+
+ temp.len = xfer->frlengths[x];
+
+ x++;
+
+ if (x == xfer->nframes) {
+ if (xfer->flags_int.control_xfr) {
+ if (xfer->flags_int.control_act) {
+ temp.setup_alt_next = 0;
+ }
+ } else {
+ temp.setup_alt_next = 0;
+ }
+ }
+ if (temp.len == 0) {
+
+ /* make sure that we send an USB packet */
+
+ temp.short_pkt = 0;
+
+ } else {
+
+ /* regular data transfer */
+
+ temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1;
+ }
+
+ avr32dci_setup_standard_chain_sub(&temp);
+
+ if (xfer->flags_int.isochronous_xfr) {
+ temp.offset += temp.len;
+ } else {
+ /* get next Page Cache pointer */
+ temp.pc = xfer->frbuffers + x;
+ }
+ }
+
+ if (xfer->flags_int.control_xfr) {
+
+ /* always setup a valid "pc" pointer for status and sync */
+ temp.pc = xfer->frbuffers + 0;
+ temp.len = 0;
+ temp.short_pkt = 0;
+ temp.setup_alt_next = 0;
+
+ /* check if we need to sync */
+ if (need_sync) {
+ /* we need a SYNC point after TX */
+ temp.func = &avr32dci_data_tx_sync;
+ avr32dci_setup_standard_chain_sub(&temp);
+ }
+ /* check if we should append a status stage */
+ if (!xfer->flags_int.control_act) {
+
+ /*
+ * Send a DATA1 message and invert the current
+ * endpoint direction.
+ */
+ if (xfer->endpoint & UE_DIR_IN) {
+ temp.func = &avr32dci_data_rx;
+ need_sync = 0;
+ } else {
+ temp.func = &avr32dci_data_tx;
+ need_sync = 1;
+ }
+
+ avr32dci_setup_standard_chain_sub(&temp);
+ if (need_sync) {
+ /* we need a SYNC point after TX */
+ temp.func = &avr32dci_data_tx_sync;
+ avr32dci_setup_standard_chain_sub(&temp);
+ }
+ }
+ }
+ /* must have at least one frame! */
+ td = temp.td;
+ xfer->td_transfer_last = td;
+}
+
+static void
+avr32dci_timeout(void *arg)
+{
+ struct usb2_xfer *xfer = arg;
+
+ DPRINTF("xfer=%p\n", xfer);
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ /* transfer is transferred */
+ avr32dci_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+avr32dci_start_standard_chain(struct usb2_xfer *xfer)
+{
+ DPRINTFN(9, "\n");
+
+ /* poll one time - will turn on interrupts */
+ if (avr32dci_xfer_do_fifo(xfer)) {
+ uint8_t ep_no = xfer->endpoint & UE_ADDR_MASK;
+
+ avr32dci_mod_ien(sc, AVR32_INT_EPT_INT(ep_no), 0);
+
+ /* put transfer on interrupt queue */
+ usb2_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+
+ /* start timeout, if any */
+ if (xfer->timeout != 0) {
+ usb2_transfer_timeout_ms(xfer,
+ &avr32dci_timeout, xfer->timeout);
+ }
+ }
+}
+
+static void
+avr32dci_root_intr(struct avr32dci_softc *sc)
+{
+ DPRINTFN(9, "\n");
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ /* set port bit */
+ sc->sc_hub_idata[0] = 0x02; /* we only have one port */
+
+ uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata,
+ sizeof(sc->sc_hub_idata));
+}
+
+static usb2_error_t
+avr32dci_standard_done_sub(struct usb2_xfer *xfer)
+{
+ struct avr32dci_td *td;
+ uint32_t len;
+ uint8_t error;
+
+ DPRINTFN(9, "\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 = 1;
+ } else {
+ xfer->frlengths[xfer->aframes] -= len;
+ }
+ }
+ /* Check for transfer error */
+ if (td->error) {
+ /* the transfer is finished */
+ error = 1;
+ 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 ?
+ USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION);
+}
+
+static void
+avr32dci_standard_done(struct usb2_xfer *xfer)
+{
+ usb2_error_t err = 0;
+
+ DPRINTFN(13, "xfer=%p pipe=%p transfer done\n",
+ xfer, xfer->pipe);
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-head
mailing list