Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 14 May 2014 17:04:02 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r266051 - head/sys/dev/usb/controller
Message-ID:  <201405141704.s4EH420q038253@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Wed May 14 17:04:02 2014
New Revision: 266051
URL: http://svnweb.freebsd.org/changeset/base/266051

Log:
  Implement USB device side driver code for SAF1761 and compatible
  chips, based on datasheet and existing USS820 DCI driver. This code is
  not yet tested.
  
  Sponsored by:	DARPA, AFRL

Modified:
  head/sys/dev/usb/controller/saf1761_dci.c
  head/sys/dev/usb/controller/saf1761_dci.h
  head/sys/dev/usb/controller/saf1761_dci_fdt.c
  head/sys/dev/usb/controller/saf1761_dci_reg.h

Modified: head/sys/dev/usb/controller/saf1761_dci.c
==============================================================================
--- head/sys/dev/usb/controller/saf1761_dci.c	Wed May 14 17:01:35 2014	(r266050)
+++ head/sys/dev/usb/controller/saf1761_dci.c	Wed May 14 17:04:02 2014	(r266051)
@@ -1,6 +1,6 @@
 /* $FreeBSD$ */
 /*-
- * Copyright (c) 2014 Hans Petter Selasky
+ * Copyright (c) 2014 Hans Petter Selasky <hselasky@FreeBSD.org>
  * All rights reserved.
  *
  * This software was developed by SRI International and the University of
@@ -29,5 +29,1958 @@
  * SUCH DAMAGE.
  */
 
+/*
+ * This file contains the driver for the SAF1761 series USB OTG
+ * controller.
+ *
+ * Datasheet is available from:
+ * http://www.nxp.com/products/automotive/multimedia/usb/SAF1761BE.html
+ */
+
+#ifdef USB_GLOBAL_INCLUDE_FILE
+#include USB_GLOBAL_INCLUDE_FILE
+#else
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#define	USB_DEBUG_VAR saf1761_dci_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>
+#endif					/* USB_GLOBAL_INCLUDE_FILE */
+
 #include <dev/usb/controller/saf1761_dci.h>
 #include <dev/usb/controller/saf1761_dci_reg.h>
+
+#define	SAF1761_DCI_BUS2SC(bus) \
+   ((struct saf1761_dci_softc *)(((uint8_t *)(bus)) - \
+    ((uint8_t *)&(((struct saf1761_dci_softc *)0)->sc_bus))))
+
+#ifdef USB_DEBUG
+static int saf1761_dci_debug = 0;
+static int saf1761_dci_forcefs = 0;
+
+static 
+SYSCTL_NODE(_hw_usb, OID_AUTO, saf1761_dci, CTLFLAG_RW, 0,
+    "USB SAF1761 DCI");
+
+SYSCTL_INT(_hw_usb_saf1761_dci, OID_AUTO, debug, CTLFLAG_RW,
+    &saf1761_dci_debug, 0, "SAF1761 DCI debug level");
+SYSCTL_INT(_hw_usb_saf1761_dci, OID_AUTO, forcefs, CTLFLAG_RW,
+    &saf1761_dci_forcefs, 0, "SAF1761 DCI force FULL speed");
+#endif
+
+#define	SAF1761_DCI_INTR_ENDPT 1
+
+/* prototypes */
+
+static const struct usb_bus_methods saf1761_dci_bus_methods;
+static const struct usb_pipe_methods saf1761_dci_device_non_isoc_methods;
+static const struct usb_pipe_methods saf1761_dci_device_isoc_methods;
+
+static saf1761_dci_cmd_t saf1761_dci_setup_rx;
+static saf1761_dci_cmd_t saf1761_dci_data_rx;
+static saf1761_dci_cmd_t saf1761_dci_data_tx;
+static saf1761_dci_cmd_t saf1761_dci_data_tx_sync;
+static void saf1761_dci_device_done(struct usb_xfer *, usb_error_t);
+static void saf1761_dci_do_poll(struct usb_bus *);
+static void saf1761_dci_standard_done(struct usb_xfer *);
+static void saf1761_dci_intr_set(struct usb_xfer *, uint8_t);
+static void saf1761_dci_root_intr(struct saf1761_dci_softc *);
+
+/*
+ * Here is a list of what the SAF1761 chip can support. The main
+ * limitation is that the sum of the buffer sizes must be less than
+ * 8192 bytes.
+ */
+static const struct usb_hw_ep_profile saf1761_dci_ep_profile[] = {
+
+	[0] = {
+		.max_in_frame_size = 64,
+		.max_out_frame_size = 64,
+		.is_simplex = 0,
+		.support_control = 1,
+	},
+	[1] = {
+		.max_in_frame_size = SOTG_HS_MAX_PACKET_SIZE,
+		.max_out_frame_size = SOTG_HS_MAX_PACKET_SIZE,
+		.is_simplex = 0,
+		.support_interrupt = 1,
+		.support_bulk = 1,
+		.support_isochronous = 1,
+		.support_in = 1,
+		.support_out = 1,
+	},
+};
+
+static void
+saf1761_dci_get_hw_ep_profile(struct usb_device *udev,
+    const struct usb_hw_ep_profile **ppf, uint8_t ep_addr)
+{
+	if (ep_addr == 0) {
+		*ppf = saf1761_dci_ep_profile + 0;
+	} else if (ep_addr < 8) {
+		*ppf = saf1761_dci_ep_profile + 1;
+	} else {
+		*ppf = NULL;
+	}
+}
+
+static void
+saf1761_dci_pull_up(struct saf1761_dci_softc *sc)
+{
+	/* activate pullup on D+, if possible */
+
+	if (!sc->sc_flags.d_pulled_up && sc->sc_flags.port_powered) {
+		DPRINTF("\n");
+
+		sc->sc_flags.d_pulled_up = 1;
+
+		SAF1761_WRITE_2(sc, SOTG_CTRL_SET, SOTG_CTRL_DP_PULL_UP);
+	}
+}
+
+static void
+saf1761_dci_pull_down(struct saf1761_dci_softc *sc)
+{
+	/* release pullup on D+, if possible */
+
+	if (sc->sc_flags.d_pulled_up) {
+		DPRINTF("\n");
+
+		sc->sc_flags.d_pulled_up = 0;
+
+		SAF1761_WRITE_2(sc, SOTG_CTRL_CLR, SOTG_CTRL_DP_PULL_UP);
+	}
+}
+
+static void
+saf1761_dci_wakeup_peer(struct saf1761_dci_softc *sc)
+{
+	uint16_t temp;
+
+	if (!(sc->sc_flags.status_suspend))
+		return;
+
+	DPRINTFN(5, "\n");
+
+	temp = SAF1761_READ_2(sc, SOTG_MODE);
+	SAF1761_WRITE_2(sc, SOTG_MODE, temp | SOTG_MODE_SNDRSU);
+	SAF1761_WRITE_2(sc, SOTG_MODE, temp & ~SOTG_MODE_SNDRSU);
+
+	/* Wait 8ms for remote wakeup to complete. */
+	usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125);
+
+}
+
+static void
+saf1761_dci_set_address(struct saf1761_dci_softc *sc, uint8_t addr)
+{
+	DPRINTFN(5, "addr=%d\n", addr);
+
+	SAF1761_WRITE_1(sc, SOTG_ADDRESS, addr | SOTG_ADDRESS_ENABLE);
+}
+
+static void
+saf1761_read_fifo(struct saf1761_dci_softc *sc, void *buf, uint32_t len)
+{
+	bus_space_read_multi_1((sc)->sc_io_tag, (sc)->sc_io_hdl, SOTG_DATA_PORT, buf, len);
+}
+
+static void
+saf1761_write_fifo(struct saf1761_dci_softc *sc, void *buf, uint32_t len)
+{
+	bus_space_write_multi_1((sc)->sc_io_tag, (sc)->sc_io_hdl, SOTG_DATA_PORT, buf, len);
+}
+
+static uint8_t
+saf1761_dci_setup_rx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td)
+{
+	struct usb_device_request req;
+	uint16_t count;
+
+	/* select the correct endpoint */
+	SAF1761_WRITE_1(sc, SOTG_EP_INDEX, SOTG_EP_INDEX_EP0SETUP);
+
+	/* check buffer status */
+	if ((SAF1761_READ_1(sc, SOTG_DCBUFFERSTATUS) &
+	    SOTG_DCBUFFERSTATUS_FILLED_MASK) == 0)
+		goto busy;
+
+	/* read buffer length */
+	count = SAF1761_READ_2(sc, SOTG_BUF_LENGTH);
+
+	DPRINTFN(5, "count=%u rem=%u\n", count, td->remainder);
+
+	/* clear did stall */
+	td->did_stall = 0;
+
+	/* clear stall */
+	SAF1761_WRITE_1(sc, SOTG_CTRL_FUNC, 0);
+
+	/* verify data length */
+	if (count != td->remainder) {
+		DPRINTFN(0, "Invalid SETUP packet "
+		    "length, %d bytes\n", count);
+		goto busy;
+	}
+	if (count != sizeof(req)) {
+		DPRINTFN(0, "Unsupported SETUP packet "
+		    "length, %d bytes\n", count);
+		goto busy;
+	}
+	/* receive data */
+	saf1761_read_fifo(sc, &req, sizeof(req));
+
+	/* copy data into real buffer */
+	usbd_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;
+	}
+	return (0);			/* complete */
+
+busy:
+	/* abort any ongoing transfer */
+	if (!td->did_stall) {
+		DPRINTFN(5, "stalling\n");
+
+		/* set stall */
+		SAF1761_WRITE_1(sc, SOTG_CTRL_FUNC, SOTG_CTRL_FUNC_STALL);
+
+		td->did_stall = 1;
+	}
+	return (1);			/* not complete */
+}
+
+static uint8_t
+saf1761_dci_data_rx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td)
+{
+	struct usb_page_search buf_res;
+	uint16_t count;
+	uint8_t got_short = 0;
+
+	if (td->ep_index == 0) {
+		/* select the correct endpoint */
+		SAF1761_WRITE_1(sc, SOTG_EP_INDEX, SOTG_EP_INDEX_EP0SETUP);
+
+		/* check buffer status */
+		if ((SAF1761_READ_1(sc, SOTG_DCBUFFERSTATUS) &
+		    SOTG_DCBUFFERSTATUS_FILLED_MASK) != 0) {
+
+			if (td->remainder == 0) {
+				/*
+				 * We are actually complete and have
+				 * received the next SETUP:
+				 */
+				DPRINTFN(5, "faking complete\n");
+				return (0);	/* complete */
+			}
+			DPRINTFN(5, "SETUP packet while receiving data\n");
+			/*
+			 * USB Host Aborted the transfer.
+			 */
+			td->error = 1;
+			return (0);	/* complete */
+		}
+	}
+	/* select the correct endpoint */
+	SAF1761_WRITE_1(sc, SOTG_EP_INDEX,
+	    (td->ep_index << SOTG_EP_INDEX_ENDP_INDEX_SHIFT) |
+	    SOTG_EP_INDEX_DIR_OUT);
+
+	/* check buffer status */
+	if ((SAF1761_READ_1(sc, SOTG_DCBUFFERSTATUS) &
+	    SOTG_DCBUFFERSTATUS_FILLED_MASK) == 0) {
+		return (1);		/* not complete */
+	}
+	/* read buffer length */
+	count = SAF1761_READ_2(sc, SOTG_BUF_LENGTH);
+
+	DPRINTFN(5, "rem=%u count=0x%04x\n", td->remainder, count);
+
+	/* 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) {
+		usbd_get_page(td->pc, td->offset, &buf_res);
+
+		/* get correct length */
+		if (buf_res.length > count)
+			buf_res.length = count;
+
+		/* receive data */
+		saf1761_read_fifo(sc, buf_res.buffer, buf_res.length);
+
+		/* update counters */
+		count -= buf_res.length;
+		td->offset += buf_res.length;
+		td->remainder -= buf_res.length;
+	}
+	/* 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 */
+	}
+	return (1);			/* not complete */
+}
+
+static uint8_t
+saf1761_dci_data_tx(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td)
+{
+	struct usb_page_search buf_res;
+	uint16_t count;
+	uint16_t count_old;
+
+	if (td->ep_index == 0) {
+		/* select the correct endpoint */
+		SAF1761_WRITE_1(sc, SOTG_EP_INDEX, SOTG_EP_INDEX_EP0SETUP);
+
+		/* check buffer status */
+		if ((SAF1761_READ_1(sc, SOTG_DCBUFFERSTATUS) &
+		    SOTG_DCBUFFERSTATUS_FILLED_MASK) != 0) {
+			DPRINTFN(5, "SETUP abort\n");
+			/*
+			 * USB Host Aborted the transfer.
+			 */
+			td->error = 1;
+			return (0);	/* complete */
+		}
+	}
+	/* select the correct endpoint */
+	SAF1761_WRITE_1(sc, SOTG_EP_INDEX,
+	    (td->ep_index << SOTG_EP_INDEX_ENDP_INDEX_SHIFT) |
+	    SOTG_EP_INDEX_DIR_IN);
+
+	/* check buffer status */
+	if ((SAF1761_READ_1(sc, SOTG_DCBUFFERSTATUS) &
+	    SOTG_DCBUFFERSTATUS_FILLED_MASK) != 0) {
+		return (1);		/* not complete */
+	}
+	DPRINTFN(5, "rem=%u\n", td->remainder);
+
+	count = td->max_packet_size;
+	if (td->remainder < count) {
+		/* we have a short packet */
+		td->short_pkt = 1;
+		count = td->remainder;
+	}
+	count_old = count;
+
+	while (count > 0) {
+
+		usbd_get_page(td->pc, td->offset, &buf_res);
+
+		/* get correct length */
+		if (buf_res.length > count)
+			buf_res.length = count;
+
+		/* transmit data */
+		saf1761_write_fifo(sc, buf_res.buffer, buf_res.length);
+
+		/* update counters */
+		count -= buf_res.length;
+		td->offset += buf_res.length;
+		td->remainder -= buf_res.length;
+	}
+
+	if (td->ep_index == 0) {
+		if (count_old < SOTG_FS_MAX_PACKET_SIZE) {
+			/* set end of packet */
+			SAF1761_WRITE_1(sc, SOTG_CTRL_FUNC, SOTG_CTRL_FUNC_VENDP);
+		}
+	} else {
+		if (count_old < SOTG_HS_MAX_PACKET_SIZE) {
+			/* set end of packet */
+			SAF1761_WRITE_1(sc, SOTG_CTRL_FUNC, SOTG_CTRL_FUNC_VENDP);
+		}
+	}
+
+	/* check remainder */
+	if (td->remainder == 0) {
+		if (td->short_pkt) {
+			return (0);	/* complete */
+		}
+		/* else we need to transmit a short packet */
+	}
+	return (1);			/* not complete */
+}
+
+static uint8_t
+saf1761_dci_data_tx_sync(struct saf1761_dci_softc *sc, struct saf1761_dci_td *td)
+{
+	if (td->ep_index == 0) {
+		/* select the correct endpoint */
+		SAF1761_WRITE_1(sc, SOTG_EP_INDEX, SOTG_EP_INDEX_EP0SETUP);
+
+		/* check buffer status */
+		if ((SAF1761_READ_1(sc, SOTG_DCBUFFERSTATUS) &
+		    SOTG_DCBUFFERSTATUS_FILLED_MASK) != 0) {
+			DPRINTFN(5, "Faking complete\n");
+			return (0);	/* complete */
+		}
+	}
+	/* select the correct endpoint */
+	SAF1761_WRITE_1(sc, SOTG_EP_INDEX,
+	    (td->ep_index << SOTG_EP_INDEX_ENDP_INDEX_SHIFT) |
+	    SOTG_EP_INDEX_DIR_IN);
+
+	/* check buffer status */
+	if ((SAF1761_READ_1(sc, SOTG_DCBUFFERSTATUS) &
+	    SOTG_DCBUFFERSTATUS_FILLED_MASK) != 0)
+		return (1);		/* busy */
+
+	if (sc->sc_dv_addr != 0xFF) {
+		/* write function address */
+		saf1761_dci_set_address(sc, sc->sc_dv_addr);
+	}
+	return (0);			/* complete */
+}
+
+static uint8_t
+saf1761_dci_xfer_do_fifo(struct saf1761_dci_softc *sc, struct usb_xfer *xfer)
+{
+	struct saf1761_dci_td *td;
+
+	DPRINTFN(9, "\n");
+
+	td = xfer->td_transfer_cache;
+	while (1) {
+		if ((td->func) (sc, 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.
+		 */
+		td = td->obj_next;
+		xfer->td_transfer_cache = td;
+	}
+	return (1);			/* not complete */
+
+done:
+	/* compute all actual lengths */
+
+	saf1761_dci_standard_done(xfer);
+
+	return (0);			/* complete */
+}
+
+static void
+saf1761_dci_interrupt_poll(struct saf1761_dci_softc *sc)
+{
+	struct usb_xfer *xfer;
+
+repeat:
+	TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+		if (!saf1761_dci_xfer_do_fifo(sc, xfer)) {
+			/* queue has been modified */
+			goto repeat;
+		}
+	}
+}
+
+static void
+saf1761_dci_wait_suspend(struct saf1761_dci_softc *sc, uint8_t on)
+{
+	if (on) {
+		sc->sc_intr_enable |= SOTG_DCINTERRUPT_IESUSP;
+		sc->sc_intr_enable &= ~SOTG_DCINTERRUPT_IERESM;
+	} else {
+		sc->sc_intr_enable &= ~SOTG_DCINTERRUPT_IESUSP;
+		sc->sc_intr_enable |= SOTG_DCINTERRUPT_IERESM;
+	}
+	SAF1761_WRITE_4(sc, SOTG_DCINTERRUPT_EN, sc->sc_intr_enable);
+}
+
+static void
+saf1761_dci_update_vbus(struct saf1761_dci_softc *sc)
+{
+	if (SAF1761_READ_4(sc, SOTG_MODE) & SOTG_MODE_VBUSSTAT) {
+		DPRINTFN(4, "VBUS ON\n");
+
+		/* VBUS present */
+		if (!sc->sc_flags.status_vbus) {
+			sc->sc_flags.status_vbus = 1;
+
+			/* complete root HUB interrupt endpoint */
+			saf1761_dci_root_intr(sc);
+		}
+	} else {
+		DPRINTFN(4, "VBUS OFF\n");
+
+		/* VBUS not-present */
+		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 */
+			saf1761_dci_root_intr(sc);
+		}
+	}
+}
+
+void
+saf1761_dci_interrupt(struct saf1761_dci_softc *sc)
+{
+	uint32_t status;
+
+	USB_BUS_LOCK(&sc->sc_bus);
+
+	status = SAF1761_READ_4(sc, SOTG_DCINTERRUPT);
+
+	/* acknowledge all interrupts */
+	SAF1761_WRITE_4(sc, SOTG_DCINTERRUPT, status);
+
+	if (status & SOTG_DCINTERRUPT_IEVBUS) {
+		/* update VBUS bit */
+		saf1761_dci_update_vbus(sc);
+	}
+	if (status & SOTG_DCINTERRUPT_IEBRST) {
+		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 */
+		saf1761_dci_wait_suspend(sc, 1);
+		/* complete root HUB interrupt endpoint */
+		saf1761_dci_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 & SOTG_DCINTERRUPT_IERESM) {
+		if (sc->sc_flags.status_suspend) {
+			sc->sc_flags.status_suspend = 0;
+			sc->sc_flags.change_suspend = 1;
+			/* disable resume interrupt */
+			saf1761_dci_wait_suspend(sc, 1);
+			/* complete root HUB interrupt endpoint */
+			saf1761_dci_root_intr(sc);
+		}
+	} else if (status & SOTG_DCINTERRUPT_IESUSP) {
+		if (!sc->sc_flags.status_suspend) {
+			sc->sc_flags.status_suspend = 1;
+			sc->sc_flags.change_suspend = 1;
+			/* enable resume interrupt */
+			saf1761_dci_wait_suspend(sc, 0);
+			/* complete root HUB interrupt endpoint */
+			saf1761_dci_root_intr(sc);
+		}
+	}
+	/* poll all active transfers */
+	saf1761_dci_interrupt_poll(sc);
+
+	USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+saf1761_dci_setup_standard_chain_sub(struct saf1761_dci_std_temp *temp)
+{
+	struct saf1761_dci_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
+saf1761_dci_setup_standard_chain(struct usb_xfer *xfer)
+{
+	struct saf1761_dci_std_temp temp;
+	struct saf1761_dci_softc *sc;
+	struct saf1761_dci_td *td;
+	uint32_t x;
+	uint8_t ep_no;
+
+	DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n",
+	    xfer->address, UE_GET_ADDR(xfer->endpointno),
+	    xfer->sumlen, usbd_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.pc = NULL;
+	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 = SAF1761_DCI_BUS2SC(xfer->xroot->bus);
+	ep_no = (xfer->endpointno & UE_ADDR);
+
+	/* check if we should prepend a setup message */
+
+	if (xfer->flags_int.control_xfr) {
+		if (xfer->flags_int.control_hdr) {
+
+			temp.func = &saf1761_dci_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;
+			}
+			saf1761_dci_setup_standard_chain_sub(&temp);
+		}
+		x = 1;
+	} else {
+		x = 0;
+	}
+
+	if (x != xfer->nframes) {
+		if (xfer->endpointno & UE_DIR_IN) {
+			temp.func = &saf1761_dci_data_tx;
+		} else {
+			temp.func = &saf1761_dci_data_rx;
+		}
+
+		/* setup "pc" pointer */
+		temp.pc = xfer->frbuffers + x;
+	}
+	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;
+		}
+
+		saf1761_dci_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;
+		}
+	}
+
+	/* check for control transfer */
+	if (xfer->flags_int.control_xfr) {
+		uint8_t need_sync;
+
+		/* 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 should append a status stage */
+		if (!xfer->flags_int.control_act) {
+
+			/*
+			 * Send a DATA1 message and invert the current
+			 * endpoint direction.
+			 */
+			if (xfer->endpointno & UE_DIR_IN) {
+				temp.func = &saf1761_dci_data_rx;
+				need_sync = 0;
+			} else {
+				temp.func = &saf1761_dci_data_tx;
+				need_sync = 1;
+			}
+			temp.len = 0;
+			temp.short_pkt = 0;
+
+			saf1761_dci_setup_standard_chain_sub(&temp);
+			if (need_sync) {
+				/* we need a SYNC point after TX */
+				temp.func = &saf1761_dci_data_tx_sync;
+				saf1761_dci_setup_standard_chain_sub(&temp);
+			}
+		}
+	}
+	/* must have at least one frame! */
+	td = temp.td;
+	xfer->td_transfer_last = td;
+}
+
+static void
+saf1761_dci_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 */
+	saf1761_dci_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+saf1761_dci_intr_set(struct usb_xfer *xfer, uint8_t set)
+{
+	struct saf1761_dci_softc *sc = SAF1761_DCI_BUS2SC(xfer->xroot->bus);
+	uint8_t ep_no = (xfer->endpointno & UE_ADDR);
+	uint32_t mask;
+
+	DPRINTFN(15, "endpoint 0x%02x\n", xfer->endpointno);
+
+	if (ep_no == 0) {
+		mask = SOTG_DCINTERRUPT_IEPRX(0) |
+		    SOTG_DCINTERRUPT_IEPTX(0) |
+		    SOTG_DCINTERRUPT_IEP0SETUP;
+	} else if (xfer->endpointno & UE_DIR_IN) {
+		mask = SOTG_DCINTERRUPT_IEPTX(ep_no);
+	} else {
+		mask = SOTG_DCINTERRUPT_IEPRX(ep_no);
+	}
+
+	if (set)
+		sc->sc_intr_enable |= mask;
+	else
+		sc->sc_intr_enable &= ~mask;
+
+	SAF1761_WRITE_4(sc, SOTG_DCINTERRUPT_EN, sc->sc_intr_enable);
+}
+
+static void
+saf1761_dci_start_standard_chain(struct usb_xfer *xfer)
+{
+	struct saf1761_dci_softc *sc = SAF1761_DCI_BUS2SC(xfer->xroot->bus);
+
+	DPRINTFN(9, "\n");
+
+	/* poll one time */
+	if (saf1761_dci_xfer_do_fifo(sc, xfer)) {
+
+		/*
+		 * Only enable the endpoint interrupt when we are
+		 * actually waiting for data, hence we are dealing
+		 * with level triggered interrupts !
+		 */
+		saf1761_dci_intr_set(xfer, 1);
+
+		/* 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,
+			    &saf1761_dci_timeout, xfer->timeout);
+		}
+	}
+}
+
+static void
+saf1761_dci_root_intr(struct saf1761_dci_softc *sc)
+{
+	DPRINTFN(9, "\n");
+
+	USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+	/* set port bit - we only have one port */
+	sc->sc_hub_idata[0] = 0x02;
+
+	uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata,
+	    sizeof(sc->sc_hub_idata));
+}
+
+static usb_error_t
+saf1761_dci_standard_done_sub(struct usb_xfer *xfer)
+{
+	struct saf1761_dci_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
+saf1761_dci_standard_done(struct usb_xfer *xfer)
+{
+	usb_error_t err = 0;
+
+	DPRINTFN(13, "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) {
+
+			err = saf1761_dci_standard_done_sub(xfer);
+		}
+		xfer->aframes = 1;
+
+		if (xfer->td_transfer_cache == NULL) {
+			goto done;
+		}
+	}
+	while (xfer->aframes != xfer->nframes) {
+
+		err = saf1761_dci_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) {
+
+		err = saf1761_dci_standard_done_sub(xfer);
+	}
+done:
+	saf1761_dci_device_done(xfer, err);
+}
+
+/*------------------------------------------------------------------------*
+ *	saf1761_dci_device_done
+ *
+ * NOTE: this function can be called more than one time on the
+ * same USB transfer!
+ *------------------------------------------------------------------------*/
+static void
+saf1761_dci_device_done(struct usb_xfer *xfer, usb_error_t error)
+{
+	USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+	DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n",
+	    xfer, xfer->endpoint, error);

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



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