Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 13 Jan 2009 18:49:35 +0000 (UTC)
From:      Andrew Thompson <thompsa@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r187160 - in head/sys: dev/usb2/controller modules/usb2/controller_atmegadci
Message-ID:  <200901131849.n0DInZVY011676@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: thompsa
Date: Tue Jan 13 18:49:35 2009
New Revision: 187160
URL: http://svn.freebsd.org/changeset/base/187160

Log:
  MFp4: //depot/projects/usb@155869
  
  	Initial version of ATMEGA USB device controller
  	driver. Has not been tested on real hardware yet.
  	The driver is based upon the AT91DCI driver.
  
  Submitted by: Hans Petter Selasky

Added:
  head/sys/dev/usb2/controller/atmegadci.c   (contents, props changed)
  head/sys/dev/usb2/controller/atmegadci.h   (contents, props changed)
  head/sys/dev/usb2/controller/atmegadci_atmelarm.c   (contents, props changed)
  head/sys/modules/usb2/controller_atmegadci/
  head/sys/modules/usb2/controller_atmegadci/Makefile   (contents, props changed)

Added: head/sys/dev/usb2/controller/atmegadci.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/usb2/controller/atmegadci.c	Tue Jan 13 18:49:35 2009	(r187160)
@@ -0,0 +1,2327 @@
+#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 ATMEGA series USB Device
+ * Controller
+ */
+
+/*
+ * NOTE: When the chip detects BUS-reset it will also reset the
+ * endpoints, Function-address and more.
+ */
+
+#include <dev/usb2/include/usb2_standard.h>
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_defs.h>
+
+#define	USB_DEBUG_VAR atmegadci_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_debug.h>
+#include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_sw_transfer.h>
+#include <dev/usb2/core/usb2_transfer.h>
+#include <dev/usb2/core/usb2_device.h>
+#include <dev/usb2/core/usb2_hub.h>
+#include <dev/usb2/core/usb2_util.h>
+
+#include <dev/usb2/controller/usb2_controller.h>
+#include <dev/usb2/controller/usb2_bus.h>
+#include <dev/usb2/controller/atmegadci.h>
+
+#define	ATMEGA_BUS2SC(bus) \
+   ((struct atmegadci_softc *)(((uint8_t *)(bus)) - \
+   USB_P2U(&(((struct atmegadci_softc *)0)->sc_bus))))
+
+#define	ATMEGA_PC2SC(pc) \
+   ATMEGA_BUS2SC((pc)->tag_parent->info->bus)
+
+#if USB_DEBUG
+static int atmegadci_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, atmegadci, CTLFLAG_RW, 0, "USB ATMEGA DCI");
+SYSCTL_INT(_hw_usb2_atmegadci, OID_AUTO, debug, CTLFLAG_RW,
+    &atmegadci_debug, 0, "ATMEGA DCI debug level");
+#endif
+
+#define	ATMEGA_INTR_ENDPT 1
+
+/* prototypes */
+
+struct usb2_bus_methods atmegadci_bus_methods;
+struct usb2_pipe_methods atmegadci_device_bulk_methods;
+struct usb2_pipe_methods atmegadci_device_ctrl_methods;
+struct usb2_pipe_methods atmegadci_device_intr_methods;
+struct usb2_pipe_methods atmegadci_device_isoc_fs_methods;
+struct usb2_pipe_methods atmegadci_root_ctrl_methods;
+struct usb2_pipe_methods atmegadci_root_intr_methods;
+
+static atmegadci_cmd_t atmegadci_setup_rx;
+static atmegadci_cmd_t atmegadci_data_rx;
+static atmegadci_cmd_t atmegadci_data_tx;
+static atmegadci_cmd_t atmegadci_data_tx_sync;
+static void atmegadci_device_done(struct usb2_xfer *, usb2_error_t);
+static void atmegadci_do_poll(struct usb2_bus *);
+static void atmegadci_root_ctrl_poll(struct atmegadci_softc *);
+static void atmegadci_standard_done(struct usb2_xfer *);
+
+static usb2_sw_transfer_func_t atmegadci_root_intr_done;
+static usb2_sw_transfer_func_t atmegadci_root_ctrl_done;
+
+/*
+ * Here is a list of what the chip supports:
+ */
+static const struct usb2_hw_ep_profile
+	atmegadci_ep_profile[2] = {
+
+	[0] = {
+		.max_in_frame_size = 64,
+		.max_out_frame_size = 64,
+		.is_simplex = 1,
+		.support_control = 1,
+	},
+	[1] = {
+		.max_in_frame_size = 64,
+		.max_out_frame_size = 64,
+		.is_simplex = 1,
+		.support_multi_buffer = 1,
+		.support_bulk = 1,
+		.support_interrupt = 1,
+		.support_isochronous = 1,
+		.support_in = 1,
+		.support_out = 1,
+	},
+};
+
+static void
+atmegadci_get_hw_ep_profile(struct usb2_device *udev,
+    const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr)
+{
+	if (ep_addr == 0)
+		*ppf = atmegadci_ep_profile;
+	else if (ep_addr < ATMEGA_EP_MAX)
+		*ppf = atmegadci_ep_profile + 1;
+	else
+		*ppf = NULL;
+}
+
+static void
+atmegadci_clocks_on(struct atmegadci_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);
+
+		ATMEGA_WRITE_1(sc, ATMEGA_USBCON,
+		    ATMEGA_USBCON_USBE |
+		    ATMEGA_USBCON_OTGPADE |
+		    ATMEGA_USBCON_VBUSTE);
+
+		sc->sc_flags.clocks_off = 0;
+
+		/* enable transceiver ? */
+	}
+}
+
+static void
+atmegadci_clocks_off(struct atmegadci_softc *sc)
+{
+	if (!sc->sc_flags.clocks_off) {
+
+		DPRINTFN(5, "\n");
+
+		/* disable Transceiver ? */
+
+		ATMEGA_WRITE_1(sc, ATMEGA_USBCON,
+		    ATMEGA_USBCON_USBE |
+		    ATMEGA_USBCON_OTGPADE |
+		    ATMEGA_USBCON_FRZCLK |
+		    ATMEGA_USBCON_VBUSTE);
+
+		/* turn clocks off */
+		(sc->sc_clocks_off) (&sc->sc_bus);
+
+		sc->sc_flags.clocks_off = 1;
+	}
+}
+
+static void
+atmegadci_pull_up(struct atmegadci_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;
+		ATMEGA_WRITE_1(sc, ATMEGA_UDCON, 0);
+	}
+}
+
+static void
+atmegadci_pull_down(struct atmegadci_softc *sc)
+{
+	/* pulldown D+, if possible */
+
+	if (sc->sc_flags.d_pulled_up) {
+		sc->sc_flags.d_pulled_up = 0;
+		ATMEGA_WRITE_1(sc, ATMEGA_UDCON, ATMEGA_UDCON_DETACH);
+	}
+}
+
+static void
+atmegadci_wakeup_peer(struct usb2_xfer *xfer)
+{
+	struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus);
+	uint8_t use_polling;
+	uint8_t temp;
+
+	if (!sc->sc_flags.status_suspend) {
+		return;
+	}
+	use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0;
+
+	temp = ATMEGA_READ_1(sc, ATMEGA_UDCON);
+	ATMEGA_WRITE_1(sc, ATMEGA_UDCON, temp | ATMEGA_UDCON_RMWKUP);
+
+	/* wait 8 milliseconds */
+	if (use_polling) {
+		/* polling */
+		DELAY(8000);
+	} else {
+		/* Wait for reset to complete. */
+		usb2_pause_mtx(&sc->sc_bus.bus_mtx, 8);
+	}
+
+	/* hardware should have cleared RMWKUP bit */
+}
+
+static void
+atmegadci_set_address(struct atmegadci_softc *sc, uint8_t addr)
+{
+	DPRINTFN(5, "addr=%d\n", addr);
+
+	ATMEGA_WRITE_1(sc, ATMEGA_UDADDR, addr);
+
+	addr |= ATMEGA_UDADDR_ADDEN;
+
+	ATMEGA_WRITE_1(sc, ATMEGA_UDADDR, addr);
+}
+
+static uint8_t
+atmegadci_setup_rx(struct atmegadci_td *td)
+{
+	struct atmegadci_softc *sc;
+	struct usb2_device_request req;
+	uint16_t count;
+	uint8_t temp;
+
+	/* get pointer to softc */
+	sc = ATMEGA_PC2SC(td->pc);
+
+	/* select endpoint number */
+	ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no);
+
+	/* check endpoint status */
+	temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX);
+
+	DPRINTFN(5, "UEINTX=0x%02x\n", temp);
+
+	if (!(temp & ATMEGA_UEINTX_RXSTPI)) {
+		/* abort any ongoing transfer */
+		if (!td->did_stall) {
+			DPRINTFN(5, "stalling\n");
+			ATMEGA_WRITE_1(sc, ATMEGA_UECONX,
+			    ATMEGA_UECONX_EPEN |
+			    ATMEGA_UECONX_STALLRQ);
+			td->did_stall = 1;
+		}
+		goto not_complete;
+	}
+	/* get the packet byte count */
+	count =
+	    (ATMEGA_READ_1(sc, ATMEGA_UEBCHX) << 8) |
+	    (ATMEGA_READ_1(sc, ATMEGA_UEBCLX));
+
+	/* mask away undefined bits */
+	count &= 0x7FF;
+
+	/* 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 */
+	ATMEGA_READ_MULTI_1(sc, ATMEGA_UEDATX,
+	    (void *)&req, 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;
+	} else {
+		sc->sc_dv_addr = 0xFF;
+	}
+
+	/* clear SETUP packet interrupt */
+	ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, ~ATMEGA_UEINTX_RXSTPI);
+	return (0);			/* complete */
+
+not_complete:
+	/* we only want to know if there is a SETUP packet */
+	ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, ATMEGA_UEIENX_RXSTPE);
+	return (1);			/* not complete */
+}
+
+static uint8_t
+atmegadci_data_rx(struct atmegadci_td *td)
+{
+	struct atmegadci_softc *sc;
+	struct usb2_page_search buf_res;
+	uint16_t count;
+	uint8_t temp;
+	uint8_t to;
+	uint8_t got_short;
+
+	to = 3;				/* don't loop forever! */
+	got_short = 0;
+
+	/* get pointer to softc */
+	sc = ATMEGA_PC2SC(td->pc);
+
+	/* select endpoint number */
+	ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no);
+
+repeat:
+	/* check if any of the FIFO banks have data */
+	/* check endpoint status */
+	temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX);
+
+	DPRINTFN(5, "temp=0x%02x rem=%u\n", temp, td->remainder);
+
+	if (temp & ATMEGA_UEINTX_RXSTPI) {
+		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 & (ATMEGA_UEINTX_FIFOCON |
+	    ATMEGA_UEINTX_RXOUTI))) {
+		/* no data */
+		goto not_complete;
+	}
+	/* get the packet byte count */
+	count =
+	    (ATMEGA_READ_1(sc, ATMEGA_UEBCHX) << 8) |
+	    (ATMEGA_READ_1(sc, ATMEGA_UEBCLX));
+
+	/* mask away undefined bits */
+	count &= 0x7FF;
+
+	/* 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 */
+		ATMEGA_READ_MULTI_1(sc, ATMEGA_UEDATX,
+		    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 */
+	ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, ATMEGA_UEINTX_RXOUTI ^ 0xFF);
+
+	/* release FIFO bank */
+	ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, ATMEGA_UEINTX_FIFOCON ^ 0xFF);
+
+	/* 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:
+	/* we only want to know if there is a SETUP packet or OUT packet */
+	ATMEGA_WRITE_1(sc, ATMEGA_UEIENX,
+	    ATMEGA_UEIENX_RXSTPE | ATMEGA_UEIENX_RXOUTE);
+	return (1);			/* not complete */
+}
+
+static uint8_t
+atmegadci_data_tx(struct atmegadci_td *td)
+{
+	struct atmegadci_softc *sc;
+	struct usb2_page_search buf_res;
+	uint16_t count;
+	uint8_t to;
+	uint8_t temp;
+
+	to = 3;				/* don't loop forever! */
+
+	/* get pointer to softc */
+	sc = ATMEGA_PC2SC(td->pc);
+
+	/* select endpoint number */
+	ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no);
+
+repeat:
+
+	/* check endpoint status */
+	temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX);
+
+	DPRINTFN(5, "temp=0x%02x rem=%u\n", temp, td->remainder);
+
+	if (temp & ATMEGA_UEINTX_RXSTPI) {
+		/*
+	         * The current transfer was aborted
+	         * by the USB Host
+	         */
+		td->error = 1;
+		return (0);		/* complete */
+	}
+	if (!(temp & (ATMEGA_UEINTX_FIFOCON |
+	    ATMEGA_UEINTX_TXINI))) {
+		/* cannot write any data */
+		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 */
+		ATMEGA_WRITE_MULTI_1(sc, ATMEGA_UEDATX,
+		    buf_res.buffer, buf_res.length);
+
+		/* update counters */
+		count -= buf_res.length;
+		td->offset += buf_res.length;
+		td->remainder -= buf_res.length;
+	}
+
+	/* clear IN packet interrupt */
+	ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, 0xFF ^ ATMEGA_UEINTX_TXINI);
+
+	/* allocate FIFO bank */
+	ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, 0xFF ^ ATMEGA_UEINTX_FIFOCON);
+
+	/* 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:
+	/* we only want to know if there is a SETUP packet or free IN packet */
+	ATMEGA_WRITE_1(sc, ATMEGA_UEIENX,
+	    ATMEGA_UEIENX_RXSTPE | ATMEGA_UEIENX_TXINE);
+	return (1);			/* not complete */
+}
+
+static uint8_t
+atmegadci_data_tx_sync(struct atmegadci_td *td)
+{
+	struct atmegadci_softc *sc;
+	uint8_t temp;
+
+	/* get pointer to softc */
+	sc = ATMEGA_PC2SC(td->pc);
+
+	/* select endpoint number */
+	ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no);
+
+	/* check endpoint status */
+	temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX);
+
+	DPRINTFN(5, "temp=0x%02x\n", temp);
+
+	if (temp & ATMEGA_UEINTX_RXSTPI) {
+		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 (!(temp & (ATMEGA_UEINTX_FIFOCON |
+	    ATMEGA_UEINTX_TXINI))) {
+		/* cannot write any data */
+		goto not_complete;
+	}
+	if (sc->sc_dv_addr != 0xFF) {
+		/* set new address */
+		atmegadci_set_address(sc, sc->sc_dv_addr);
+	}
+	return (0);			/* complete */
+
+not_complete:
+	/* we only want to know if there is a SETUP packet or free IN packet */
+	ATMEGA_WRITE_1(sc, ATMEGA_UEIENX,
+	    ATMEGA_UEIENX_RXSTPE | ATMEGA_UEIENX_TXINE);
+	return (1);			/* not complete */
+}
+
+static uint8_t
+atmegadci_xfer_do_fifo(struct usb2_xfer *xfer)
+{
+	struct atmegadci_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 */
+
+	atmegadci_standard_done(xfer);
+	return (0);			/* complete */
+}
+
+static void
+atmegadci_interrupt_poll(struct atmegadci_softc *sc)
+{
+	struct usb2_xfer *xfer;
+
+repeat:
+	TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+		if (!atmegadci_xfer_do_fifo(xfer)) {
+			/* queue has been modified */
+			goto repeat;
+		}
+	}
+}
+
+static void
+atmegadci_vbus_interrupt(struct atmegadci_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 */
+
+			usb2_sw_transfer(&sc->sc_root_intr,
+			    &atmegadci_root_intr_done);
+		}
+	} 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 */
+
+			usb2_sw_transfer(&sc->sc_root_intr,
+			    &atmegadci_root_intr_done);
+		}
+	}
+}
+
+void
+atmegadci_interrupt(struct atmegadci_softc *sc)
+{
+	uint8_t status;
+
+	USB_BUS_LOCK(&sc->sc_bus);
+
+	/* read interrupt status */
+	status = ATMEGA_READ_1(sc, ATMEGA_UDINT);
+
+	/* clear all set interrupts */
+	ATMEGA_WRITE_1(sc, ATMEGA_UDINT, ~status);
+
+	/* check for any bus state change interrupts */
+	if (status & ATMEGA_UDINT_EORSTI) {
+
+		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 */
+		ATMEGA_WRITE_1(sc, ATMEGA_UDIEN,
+		    ATMEGA_UDINT_SUSPE |
+		    ATMEGA_UDINT_EORSTE);
+
+		/* complete root HUB interrupt endpoint */
+		usb2_sw_transfer(&sc->sc_root_intr,
+		    &atmegadci_root_intr_done);
+	}
+	/*
+	 * 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 & ATMEGA_UDINT_EORSMI) {
+
+		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 */
+			ATMEGA_WRITE_1(sc, ATMEGA_UDIEN,
+			    ATMEGA_UDINT_SUSPE |
+			    ATMEGA_UDINT_EORSTE);
+
+			/* complete root HUB interrupt endpoint */
+			usb2_sw_transfer(&sc->sc_root_intr,
+			    &atmegadci_root_intr_done);
+		}
+	} else if (status & ATMEGA_UDINT_SUSPI) {
+
+		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 */
+			ATMEGA_WRITE_1(sc, ATMEGA_UDIEN,
+			    ATMEGA_UDINT_EORSMI |
+			    ATMEGA_UDINT_EORSTE);
+
+			/* complete root HUB interrupt endpoint */
+			usb2_sw_transfer(&sc->sc_root_intr,
+			    &atmegadci_root_intr_done);
+		}
+	}
+	/* check VBUS */
+	status = ATMEGA_READ_1(sc, ATMEGA_USBINT);
+
+	/* clear all set interrupts */
+	ATMEGA_WRITE_1(sc, ATMEGA_USBINT, ~status);
+
+	if (status & ATMEGA_USBINT_VBUSTI) {
+		uint8_t temp;
+
+		temp = ATMEGA_READ_1(sc, ATMEGA_USBSTA);
+		atmegadci_vbus_interrupt(sc, temp & ATMEGA_USBSTA_VBUS);
+	}
+	/* check for any endpoint interrupts */
+	status = ATMEGA_READ_1(sc, ATMEGA_UEINT);
+
+	/* clear all set interrupts */
+	ATMEGA_WRITE_1(sc, ATMEGA_UEINT, ~status);
+
+	if (status) {
+
+		DPRINTFN(5, "real endpoint interrupt 0x%02x\n", status);
+
+		atmegadci_interrupt_poll(sc);
+	}
+	USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+atmegadci_setup_standard_chain_sub(struct atmegadci_std_temp *temp)
+{
+	struct atmegadci_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 = 0;
+	td->short_pkt = temp->short_pkt;
+	td->alt_next = temp->setup_alt_next;
+}
+
+static void
+atmegadci_setup_standard_chain(struct usb2_xfer *xfer)
+{
+	struct atmegadci_std_temp temp;
+	struct atmegadci_softc *sc;
+	struct atmegadci_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.setup_alt_next = xfer->flags_int.short_frames_ok;
+	temp.offset = 0;
+
+	sc = ATMEGA_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 = &atmegadci_setup_rx;
+			temp.len = xfer->frlengths[0];
+			temp.pc = xfer->frbuffers + 0;
+			temp.short_pkt = temp.len ? 1 : 0;
+
+			atmegadci_setup_standard_chain_sub(&temp);
+		}
+		x = 1;
+	} else {
+		x = 0;
+	}
+
+	if (x != xfer->nframes) {
+		if (xfer->endpoint & UE_DIR_IN) {
+			temp.func = &atmegadci_data_tx;
+			need_sync = 1;
+		} else {
+			temp.func = &atmegadci_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) {
+			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;
+		}
+
+		atmegadci_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;
+		}
+	}
+
+	/* always setup a valid "pc" pointer for status and sync */
+	temp.pc = xfer->frbuffers + 0;
+
+	/* check if we need to sync */
+	if (need_sync && xfer->flags_int.control_xfr) {
+
+		/* we need a SYNC point after TX */
+		temp.func = &atmegadci_data_tx_sync;
+		temp.len = 0;
+		temp.short_pkt = 0;
+
+		atmegadci_setup_standard_chain_sub(&temp);
+	}
+	/* check if we should append a status stage */
+	if (xfer->flags_int.control_xfr &&
+	    !xfer->flags_int.control_act) {
+
+		/*
+		 * Send a DATA1 message and invert the current
+		 * endpoint direction.
+		 */
+		if (xfer->endpoint & UE_DIR_IN) {
+			temp.func = &atmegadci_data_rx;
+			need_sync = 0;
+		} else {
+			temp.func = &atmegadci_data_tx;
+			need_sync = 1;
+		}
+		temp.len = 0;
+		temp.short_pkt = 0;
+
+		atmegadci_setup_standard_chain_sub(&temp);
+		if (need_sync) {
+			/* we need a SYNC point after TX */
+			temp.func = &atmegadci_data_tx_sync;
+			temp.len = 0;
+			temp.short_pkt = 0;
+
+			atmegadci_setup_standard_chain_sub(&temp);
+		}
+	}
+	/* must have at least one frame! */
+	td = temp.td;
+	xfer->td_transfer_last = td;
+}
+
+static void
+atmegadci_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 */
+	atmegadci_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+atmegadci_start_standard_chain(struct usb2_xfer *xfer)
+{
+	DPRINTFN(9, "\n");
+
+	/* poll one time - will turn on interrupts */
+	if (atmegadci_xfer_do_fifo(xfer)) {
+
+		/* 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,
+			    &atmegadci_timeout, xfer->timeout);
+		}
+	}
+}
+
+static void
+atmegadci_root_intr_done(struct usb2_xfer *xfer,
+    struct usb2_sw_transfer *std)
+{
+	struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus);
+
+	DPRINTFN(9, "\n");
+
+	USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+	if (std->state != USB_SW_TR_PRE_DATA) {
+		if (std->state == USB_SW_TR_PRE_CALLBACK) {
+			/* transfer transferred */
+			atmegadci_device_done(xfer, std->err);
+		}
+		goto done;
+	}
+	/* setup buffer */
+	std->ptr = sc->sc_hub_idata;
+	std->len = sizeof(sc->sc_hub_idata);
+
+	/* set port bit */
+	sc->sc_hub_idata[0] = 0x02;	/* we only have one port */
+
+done:
+	return;
+}
+
+static usb2_error_t
+atmegadci_standard_done_sub(struct usb2_xfer *xfer)
+{
+	struct atmegadci_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;
+			}

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200901131849.n0DInZVY011676>