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