PERFORCE change 132516 for review

Hans Petter Selasky hselasky at FreeBSD.org
Fri Jan 4 13:14:00 PST 2008


http://perforce.freebsd.org/chv.cgi?CH=132516

Change 132516 by hselasky at hselasky_laptop001 on 2008/01/04 21:13:48

	
	Final version of the generic USS820 DCI driver.
	
	The driver has been tested and works fine.

Affected files ...

.. //depot/projects/usb/src/sys/dev/usb/uss820_dci.c#4 edit

Differences ...

==== //depot/projects/usb/src/sys/dev/usb/uss820_dci.c#4 (text+ko) ====

@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2007 Hans Petter Selasky <hselasky at freebsd.org>
+ * Copyright (c) 2008 Hans Petter Selasky <hselasky at freebsd.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -23,3 +23,2666 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
+/*
+ * This file contains the driver for the USS820 series USB Device
+ * Controller
+ *
+ * NOTE: The datasheet does not document everything!
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+
+#define	usbd_config_td_cc uss820_dci_config_copy
+#define	usbd_config_td_softc uss820_dci_softc
+
+#include <dev/usb/usb_port.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_subr.h>
+#include <dev/usb/uss820_dci.h>
+
+#define	USS820_DCI_BUS2SC(bus) \
+   ((struct uss820_dci_softc *)(((uint8_t *)(bus)) - \
+   POINTER_TO_UNSIGNED(&(((struct uss820_dci_softc *)0)->sc_bus))))
+
+#ifdef USB_DEBUG
+#define	DPRINTFN(n,fmt,...) do {			\
+  if (uss820_dcidebug > (n)) {				\
+    printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__);	\
+  }							\
+} while (0)
+
+static int uss820_dcidebug = 0;
+
+SYSCTL_NODE(_hw_usb, OID_AUTO, uss820_dci, CTLFLAG_RW, 0, "USB uss820_dci");
+SYSCTL_INT(_hw_usb_uss820_dci, OID_AUTO, debug, CTLFLAG_RW,
+    &uss820_dcidebug, 0, "uss820_dci debug level");
+#else
+#define	DPRINTFN(...) do { } while (0)
+#endif
+
+#define	USS820_DCI_INTR_ENDPT 1
+
+/* prototypes */
+
+struct usbd_bus_methods uss820_dci_bus_methods;
+struct usbd_pipe_methods uss820_dci_device_bulk_methods;
+struct usbd_pipe_methods uss820_dci_device_ctrl_methods;
+struct usbd_pipe_methods uss820_dci_device_intr_methods;
+struct usbd_pipe_methods uss820_dci_device_isoc_fs_methods;
+struct usbd_pipe_methods uss820_dci_root_ctrl_methods;
+struct usbd_pipe_methods uss820_dci_root_intr_methods;
+
+static uss820_dci_cmd_t uss820_dci_setup_rx;
+static uss820_dci_cmd_t uss820_dci_data_rx;
+static uss820_dci_cmd_t uss820_dci_data_tx;
+static uss820_dci_cmd_t uss820_dci_data_tx_sync;
+static void uss820_dci_device_done(struct usbd_xfer *xfer, usbd_status_t error);
+static void uss820_dci_do_poll(struct usbd_bus *bus);
+static void uss820_dci_root_ctrl_poll(struct uss820_dci_softc *sc);
+static void uss820_dci_standard_done(struct usbd_xfer *xfer);
+static void uss820_dci_intr_set(struct usbd_xfer *xfer, uint8_t set);
+
+static usbd_std_root_transfer_func_t uss820_dci_root_intr_done;
+static usbd_std_root_transfer_func_t uss820_dci_root_ctrl_done;
+static usbd_config_td_command_t uss820_dci_root_ctrl_task;
+
+/*
+ * Here is a list of what the USS820D chip can support. The main
+ * limitation is that the sum of the buffer sizes must be less than
+ * 1120 bytes.
+ */
+static const struct usbd_hw_ep_profile
+	uss820_dci_ep_profile[] = {
+
+	[0] = {
+		.max_frame_size = 32,
+		.is_simplex = 0,
+		.support_control = 1,
+	},
+	[1] = {
+		.max_frame_size = 64,
+		.is_simplex = 0,
+		.support_multi_buffer = 1,
+		.support_bulk = 1,
+		.support_interrupt = 1,
+		.support_in = 1,
+		.support_out = 1,
+	},
+	[2] = {
+		.max_frame_size = 8,
+		.is_simplex = 0,
+		.support_multi_buffer = 1,
+		.support_bulk = 1,
+		.support_interrupt = 1,
+		.support_in = 1,
+		.support_out = 1,
+	},
+	[3] = {
+		.max_frame_size = 256,
+		.is_simplex = 0,
+		.support_multi_buffer = 1,
+		.support_isochronous = 1,
+		.support_in = 1,
+		.support_out = 1,
+	},
+};
+
+static void
+uss820_dci_get_hw_ep_profile(struct usbd_device *udev,
+    const struct usbd_hw_ep_profile **ppf, uint8_t ep_addr)
+{
+	if (ep_addr == 0) {
+		*ppf = uss820_dci_ep_profile + 0;
+	} else if (ep_addr < 5) {
+		*ppf = uss820_dci_ep_profile + 1;
+	} else if (ep_addr < 7) {
+		*ppf = uss820_dci_ep_profile + 2;
+	} else if (ep_addr == 7) {
+		*ppf = uss820_dci_ep_profile + 3;
+	} else {
+		*ppf = NULL;
+	}
+	return;
+}
+
+static void
+uss820_dci_pull_up(struct uss820_dci_softc *sc)
+{
+	uint8_t temp;
+
+	/* pullup D+, if possible */
+
+	if (!sc->sc_flags.d_pulled_up &&
+	    sc->sc_flags.port_powered) {
+		sc->sc_flags.d_pulled_up = 1;
+
+		DPRINTFN(0, "\n");
+
+		temp = USS820_READ_1(sc, USS820_MCSR);
+		temp |= USS820_MCSR_DPEN;
+		USS820_WRITE_1(sc, USS820_MCSR, temp);
+	}
+	return;
+}
+
+static void
+uss820_dci_pull_down(struct uss820_dci_softc *sc)
+{
+	uint8_t temp;
+
+	/* pulldown D+, if possible */
+
+	if (sc->sc_flags.d_pulled_up) {
+		sc->sc_flags.d_pulled_up = 0;
+
+		DPRINTFN(0, "\n");
+
+		temp = USS820_READ_1(sc, USS820_MCSR);
+		temp &= ~USS820_MCSR_DPEN;
+		USS820_WRITE_1(sc, USS820_MCSR, temp);
+	}
+	return;
+}
+
+static void
+uss820_dci_wakeup_peer(struct uss820_dci_softc *sc)
+{
+	if (!(sc->sc_flags.status_suspend)) {
+		return;
+	}
+	DPRINTFN(-1, "not supported\n");
+
+	return;
+}
+
+static void
+uss820_dci_rem_wakeup_set(struct usbd_device *udev, uint8_t is_on)
+{
+	struct uss820_dci_softc *sc;
+	uint8_t temp;
+
+	DPRINTFN(4, "is_on=%u\n", is_on);
+
+	mtx_assert(&(udev->bus->mtx), MA_OWNED);
+
+	sc = USS820_DCI_BUS2SC(udev->bus);
+
+	temp = USS820_READ_1(sc, USS820_SCR);
+
+	if (is_on) {
+		temp |= USS820_SCR_RWUPE;
+	} else {
+		temp &= ~USS820_SCR_RWUPE;
+	}
+
+	USS820_WRITE_1(sc, USS820_SCR, temp);
+
+	return;
+}
+
+static void
+uss820_dci_set_address(struct uss820_dci_softc *sc, uint8_t addr)
+{
+	DPRINTFN(4, "addr=%d\n", addr);
+
+	USS820_WRITE_1(sc, USS820_FADDR, addr);
+
+	return;
+}
+
+static uint8_t
+uss820_dci_setup_rx(struct uss820_dci_td *td)
+{
+	struct uss820_dci_softc *sc;
+	usb_device_request_t req;
+	uint16_t count;
+	uint8_t rx_stat;
+	uint8_t temp;
+
+	/* select the correct endpoint */
+	bus_space_write_1(td->io_tag, td->io_hdl,
+	    td->ep_reg, td->ep_index);
+
+	/* read out FIFO status */
+	rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->rx_stat_reg);
+
+	/* get pointer to softc */
+	sc = td->pc->xfer->usb_sc;
+
+	DPRINTFN(4, "rx_stat=0x%02x rem=%u\n", rx_stat, td->remainder);
+
+	if (!(rx_stat & USS820_RXSTAT_RXSETUP)) {
+		/* abort any ongoing transfer */
+		if (!td->did_stall) {
+			DPRINTFN(4, "stalling\n");
+
+			/* set stall */
+
+			temp = USS820_READ_1(sc, USS820_EPCON);
+
+			temp |= (USS820_EPCON_TXSTL |
+			    USS820_EPCON_RXSTL);
+
+			USS820_WRITE_SHARED_1(sc, USS820_EPCON, temp);
+
+			td->did_stall = 1;
+		}
+		goto not_complete;
+	}
+	/* clear stall and all I/O */
+	temp = USS820_READ_1(sc, USS820_EPCON);
+
+	temp &= ~(USS820_EPCON_TXSTL |
+	    USS820_EPCON_RXSTL |
+	    USS820_EPCON_RXIE |
+	    USS820_EPCON_TXOE);
+
+	USS820_WRITE_SHARED_1(sc, USS820_EPCON, temp);
+
+	/* clear end overwrite flag */
+	rx_stat &= ~USS820_RXSTAT_EDOVW;
+	USS820_WRITE_SHARED_1(sc, USS820_RXSTAT, rx_stat);
+
+	/* get the packet byte count */
+	count = bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->rx_count_low_reg);
+	count |= (bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->rx_count_high_reg) << 8);
+	count &= 0x3FF;
+
+	/* verify data length */
+	if (count != td->remainder) {
+		DPRINTFN(-1, "Invalid SETUP packet "
+		    "length, %d bytes\n", count);
+		goto not_complete;
+	}
+	if (count != sizeof(req)) {
+		DPRINTFN(-1, "Unsupported SETUP packet "
+		    "length, %d bytes\n", count);
+		goto not_complete;
+	}
+	/* receive data */
+	bus_space_read_multi_1(td->io_tag, td->io_hdl,
+	    td->rx_fifo_reg, (void *)&req, sizeof(req));
+
+	/* read out FIFO status */
+	rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->rx_stat_reg);
+
+	if (rx_stat & (USS820_RXSTAT_EDOVW |
+	    USS820_RXSTAT_STOVW)) {
+		DPRINTFN(0, "new SETUP packet received\n");
+		return (1);		/* not complete */
+	}
+	/* clear receive setup bit */
+	rx_stat &= ~(USS820_RXSTAT_RXSETUP |
+	    USS820_RXSTAT_EDOVW |
+	    USS820_RXSTAT_STOVW);
+	USS820_WRITE_SHARED_1(sc, USS820_RXSTAT, rx_stat);
+
+	/* set RXFFRC bit */
+	temp = bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->rx_cntl_reg);
+	temp |= USS820_RXCON_RXFFRC;
+	bus_space_write_1(td->io_tag, td->io_hdl,
+	    td->rx_cntl_reg, temp);
+
+	/* 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 */
+
+not_complete:
+	/* clear end overwrite flag, if any */
+	if (rx_stat & USS820_RXSTAT_RXSETUP) {
+		rx_stat &= ~(USS820_RXSTAT_EDOVW |
+		    USS820_RXSTAT_STOVW |
+		    USS820_RXSTAT_RXSETUP);
+		USS820_WRITE_SHARED_1(sc, USS820_RXSTAT, rx_stat);
+	}
+	return (1);			/* not complete */
+
+}
+
+static void
+uss820_dci_shared_write(struct uss820_dci_td *td, uint8_t reg,
+    uint8_t val)
+{
+	bus_space_write_1(td->io_tag, td->io_hdl, td->pend_reg, 1);
+	bus_space_write_1(td->io_tag, td->io_hdl, reg, val);
+	bus_space_write_1(td->io_tag, td->io_hdl, td->pend_reg, 0);
+	return;
+}
+
+static uint8_t
+uss820_dci_data_rx(struct uss820_dci_td *td)
+{
+	struct usbd_page_search buf_res;
+	uint16_t count;
+	uint8_t rx_flag;
+	uint8_t rx_stat;
+	uint8_t rx_cntl;
+	uint8_t ep_con;
+	uint8_t to;
+	uint8_t got_short;
+
+	to = 2;				/* don't loop forever! */
+	got_short = 0;
+
+	/* select the correct endpoint */
+	bus_space_write_1(td->io_tag, td->io_hdl, td->ep_reg, td->ep_index);
+
+	/* check if any of the FIFO banks have data */
+repeat:
+	/* read out FIFO flag */
+	rx_flag = bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->rx_flag_reg);
+	/* read out FIFO status */
+	rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->rx_stat_reg);
+
+	DPRINTFN(4, "rx_stat=0x%02x rem=%u\n", rx_stat, td->remainder);
+
+	if (rx_stat & (USS820_RXSTAT_RXSETUP |
+	    USS820_RXSTAT_RXSOVW |
+	    USS820_RXSTAT_EDOVW)) {
+		if (td->remainder == 0) {
+			/*
+			 * We are actually complete and have
+			 * received the next SETUP
+			 */
+			DPRINTFN(4, "faking complete\n");
+			return (0);	/* complete */
+		}
+		/*
+	         * USB Host Aborted the transfer.
+	         */
+		td->error = 1;
+		return (0);		/* complete */
+	}
+	/* check for errors */
+	if (rx_flag & (USS820_RXFLG_RXOVF |
+	    USS820_RXFLG_RXURF)) {
+		DPRINTFN(4, "overflow or underflow\n");
+		/* should not happen */
+		td->error = 1;
+		return (0);		/* complete */
+	}
+	/* check status */
+	if (!(rx_flag & (USS820_RXFLG_RXFIF0 |
+	    USS820_RXFLG_RXFIF1))) {
+
+		/* read out EPCON register */
+		ep_con = bus_space_read_1(td->io_tag,
+		    td->io_hdl, td->ep_con_reg);
+		/* enable RX input */
+		if (!(ep_con & USS820_EPCON_RXIE)) {
+			ep_con |= USS820_EPCON_RXIE;
+			uss820_dci_shared_write(td, td->ep_con_reg, ep_con);
+		}
+		return (1);		/* not complete */
+	}
+	/* get the packet byte count */
+	count = bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->rx_count_low_reg);
+
+	count |= (bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->rx_count_high_reg) << 8);
+	count &= 0x3FF;
+
+	/* 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 */
+		bus_space_read_multi_1(td->io_tag, td->io_hdl,
+		    td->rx_fifo_reg, buf_res.buffer, buf_res.length);
+
+		/* update counters */
+		count -= buf_res.length;
+		td->offset += buf_res.length;
+		td->remainder -= buf_res.length;
+	}
+
+	/* set RXFFRC bit */
+	rx_cntl = bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->rx_cntl_reg);
+	rx_cntl |= USS820_RXCON_RXFFRC;
+	bus_space_write_1(td->io_tag, td->io_hdl,
+	    td->rx_cntl_reg, rx_cntl);
+
+	/* 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;
+	}
+	return (1);			/* not complete */
+}
+
+static uint8_t
+uss820_dci_data_tx(struct uss820_dci_td *td)
+{
+	struct usbd_page_search buf_res;
+	uint16_t count;
+	uint16_t count_copy;
+	uint8_t rx_stat;
+	uint8_t tx_flag;
+	uint8_t ep_con;
+	uint8_t to;
+
+	/* select the correct endpoint */
+	bus_space_write_1(td->io_tag, td->io_hdl,
+	    td->ep_reg, td->ep_index);
+
+	to = 2;				/* don't loop forever! */
+
+repeat:
+	/* read out TX FIFO flags */
+	tx_flag = bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->tx_flag_reg);
+
+	/* read out RX FIFO status last */
+	rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->rx_stat_reg);
+
+	DPRINTFN(4, "rx_stat=0x%02x tx_flag=0x%02x rem=%u\n",
+	    rx_stat, tx_flag, td->remainder);
+
+	if (rx_stat & (USS820_RXSTAT_RXSETUP |
+	    USS820_RXSTAT_RXSOVW |
+	    USS820_RXSTAT_EDOVW)) {
+		/*
+	         * The current transfer was aborted
+	         * by the USB Host
+	         */
+		td->error = 1;
+		return (0);		/* complete */
+	}
+	if (tx_flag & (USS820_TXFLG_TXOVF |
+	    USS820_TXFLG_TXURF)) {
+		td->error = 1;
+		return (0);		/* complete */
+	}
+	if (tx_flag & USS820_TXFLG_TXFIF0) {
+		if (tx_flag & USS820_TXFLG_TXFIF1) {
+			return (1);	/* not complete */
+		}
+	}
+	if ((!td->support_multi_buffer) &&
+	    (tx_flag & (USS820_TXFLG_TXFIF0 |
+	    USS820_TXFLG_TXFIF1))) {
+		return (1);		/* not complete */
+	}
+	count = td->max_packet_size;
+	if (td->remainder < count) {
+		/* we have a short packet */
+		td->short_pkt = 1;
+		count = td->remainder;
+	}
+	count_copy = 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 */
+		bus_space_write_multi_1(td->io_tag, td->io_hdl,
+		    td->tx_fifo_reg, buf_res.buffer, buf_res.length);
+
+		/* update counters */
+		count -= buf_res.length;
+		td->offset += buf_res.length;
+		td->remainder -= buf_res.length;
+	}
+
+	/* post-write high packet byte count first */
+	bus_space_write_1(td->io_tag, td->io_hdl,
+	    td->tx_count_high_reg, count_copy >> 8);
+
+	/* post-write low packet byte count last */
+	bus_space_write_1(td->io_tag, td->io_hdl,
+	    td->tx_count_low_reg, count_copy);
+
+	/* read out EPCON register */
+	ep_con = bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->ep_con_reg);
+	/*
+	 * Enable TX output, which must happen after that we have written
+	 * data into the FIFO. This is undocumented.
+	 */
+	if (!(ep_con & USS820_EPCON_TXOE)) {
+		ep_con |= USS820_EPCON_TXOE;
+		uss820_dci_shared_write(td, td->ep_con_reg, ep_con);
+	}
+	/* 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;
+	}
+	return (1);			/* not complete */
+}
+
+static uint8_t
+uss820_dci_data_tx_sync(struct uss820_dci_td *td)
+{
+	struct uss820_dci_softc *sc;
+	uint8_t rx_stat;
+	uint8_t tx_flag;
+
+	/* select the correct endpoint */
+	bus_space_write_1(td->io_tag, td->io_hdl,
+	    td->ep_reg, td->ep_index);
+
+	/* read out TX FIFO flag */
+	tx_flag = bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->tx_flag_reg);
+
+	/* read out RX FIFO status last */
+	rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
+	    td->rx_stat_reg);
+
+	DPRINTFN(4, "rx_stat=0x%02x rem=%u\n", rx_stat, td->remainder);
+
+	if (rx_stat & (USS820_RXSTAT_RXSETUP |
+	    USS820_RXSTAT_RXSOVW |
+	    USS820_RXSTAT_EDOVW)) {
+		DPRINTFN(4, "faking complete\n");
+		/* Race condition */
+		return (0);		/* complete */
+	}
+	DPRINTFN(4, "tx_flag=0x%02x rem=%u\n",
+	    tx_flag, td->remainder);
+
+	if (tx_flag & (USS820_TXFLG_TXOVF |
+	    USS820_TXFLG_TXURF)) {
+		td->error = 1;
+		return (0);		/* complete */
+	}
+	if (tx_flag & (USS820_TXFLG_TXFIF0 |
+	    USS820_TXFLG_TXFIF1)) {
+		return (1);		/* not complete */
+	}
+	sc = td->pc->xfer->usb_sc;
+	if (sc->sc_dv_addr != 0xFF) {
+		/* write function address */
+		uss820_dci_set_address(sc, sc->sc_dv_addr);
+	}
+	return (0);			/* complete */
+}
+
+static uint8_t
+uss820_dci_xfer_do_fifo(struct usbd_xfer *xfer)
+{
+	struct uss820_dci_td *td;
+
+	DPRINTFN(8, "\n");
+
+	td = xfer->td_transfer_cache;
+	while (1) {
+		if ((td->func) (td)) {
+			/* operation in progress */
+			break;
+		}
+		if (((void *)td) == xfer->td_transfer_last) {
+			goto done;
+		}
+		if (td->error) {
+			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 */
+
+	uss820_dci_standard_done(xfer);
+
+	return (0);			/* complete */
+}
+
+static void
+uss820_dci_interrupt_poll(struct uss820_dci_softc *sc)
+{
+	struct usbd_xfer *xfer;
+
+	LIST_FOREACH(xfer, &sc->sc_bus.intr_list_head, interrupt_list) {
+		if (!uss820_dci_xfer_do_fifo(xfer)) {
+			/* queue callback for execution */
+			usbd_callback_wrapper(xfer, NULL,
+			    USBD_CONTEXT_CALLBACK);
+		}
+	}
+	return;
+}
+
+static void
+uss820_dci_wait_suspend(struct uss820_dci_softc *sc, uint8_t on)
+{
+	uint8_t scr;
+	uint8_t scratch;
+
+	scr = USS820_READ_1(sc, USS820_SCR);
+	scratch = USS820_READ_1(sc, USS820_SCRATCH);
+
+	if (on) {
+		scr |= USS820_SCR_IE_SUSP;
+		scratch &= ~USS820_SCRATCH_IE_RESUME;
+	} else {
+		scr &= ~USS820_SCR_IE_SUSP;
+		scratch |= USS820_SCRATCH_IE_RESUME;
+	}
+
+	USS820_WRITE_1(sc, USS820_SCR, scr);
+	USS820_WRITE_1(sc, USS820_SCRATCH, scratch);
+	return;
+}
+
+void
+uss820_dci_interrupt(struct uss820_dci_softc *sc)
+{
+	uint8_t ssr;
+	uint8_t event;
+
+	mtx_lock(&(sc->sc_bus.mtx));
+
+	ssr = USS820_READ_1(sc, USS820_SSR);
+
+	ssr &= (USS820_SSR_SUSPEND |
+	    USS820_SSR_RESUME |
+	    USS820_SSR_RESET);
+
+	/* acknowledge all interrupts */
+
+	USS820_WRITE_SHARED_1(sc, USS820_SSR, 0);
+
+	/* check for any bus state change interrupts */
+
+	if (ssr) {
+
+		event = 0;
+
+		if (ssr & USS820_SSR_RESET) {
+			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 */
+			uss820_dci_wait_suspend(sc, 1);
+
+			event = 1;
+		}
+		/*
+	         * 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 (ssr & USS820_SSR_RESUME) {
+			if (sc->sc_flags.status_suspend) {
+				sc->sc_flags.status_suspend = 0;
+				sc->sc_flags.change_suspend = 1;
+				/* disable resume interrupt */
+				uss820_dci_wait_suspend(sc, 1);
+				event = 1;
+			}
+		} else if (ssr & USS820_SSR_SUSPEND) {
+			if (!sc->sc_flags.status_suspend) {
+				sc->sc_flags.status_suspend = 1;
+				sc->sc_flags.change_suspend = 1;
+				/* enable resume interrupt */
+				uss820_dci_wait_suspend(sc, 0);
+				event = 1;
+			}
+		}
+		if (event) {
+
+			DPRINTFN(0, "real bus interrupt 0x%02x\n", ssr);
+
+			/* complete root HUB interrupt endpoint */
+
+			usbd_std_root_transfer(&(sc->sc_root_intr),
+			    &uss820_dci_root_intr_done);
+		}
+	}
+	/* acknowledge all SBI interrupts */
+	USS820_WRITE_SHARED_1(sc, USS820_SBI, 0);
+
+	/* acknowledge all SBI1 interrupts */
+	USS820_WRITE_SHARED_1(sc, USS820_SBI1, 0);
+
+	/* poll all active transfers */
+	uss820_dci_interrupt_poll(sc);
+
+	mtx_unlock(&(sc->sc_bus.mtx));
+
+	return;
+}
+
+static void
+uss820_dci_setup_standard_chain_sub(struct uss820_std_temp *temp)
+{
+	struct uss820_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 = 0;
+	td->short_pkt = temp->short_pkt;
+	td->alt_next = temp->setup_alt_next;
+	return;
+}
+
+static void
+uss820_dci_setup_standard_chain(struct usbd_xfer *xfer)
+{
+	struct uss820_std_temp temp;
+	struct uss820_dci_softc *sc;
+	struct uss820_dci_td *td;
+	uint32_t x;
+	uint8_t need_sync;
+	uint8_t ep_no;
+
+	DPRINTFN(8, "addr=%d endpt=%d sumlen=%d speed=%d\n",
+	    xfer->address, UE_GET_ADDR(xfer->endpoint),
+	    xfer->sumlen, usbd_get_speed(xfer->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 = xfer->usb_sc;
+	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 = &uss820_dci_setup_rx;
+			temp.len = xfer->frlengths[0];
+			temp.pc = xfer->frbuffers + 0;
+			temp.short_pkt = temp.len ? 1 : 0;
+
+			uss820_dci_setup_standard_chain_sub(&temp);
+		}
+		x = 1;
+	} else {
+		x = 0;
+	}
+
+	if (x != xfer->nframes) {
+		if (xfer->endpoint & UE_DIR_IN) {
+			temp.func = &uss820_dci_data_tx;
+			need_sync = 1;
+		} else {
+			temp.func = &uss820_dci_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;
+		}
+
+		uss820_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;
+		}
+	}
+
+	/* 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.isochronous_xfr) {
+
+		/* we need a SYNC point after TX */
+		temp.func = &uss820_dci_data_tx_sync;
+		temp.len = 0;
+		temp.short_pkt = 0;
+
+		uss820_dci_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 = &uss820_dci_data_rx;
+			need_sync = 0;
+		} else {
+			temp.func = &uss820_dci_data_tx;
+			need_sync = 1;
+		}
+		temp.len = 0;
+		temp.short_pkt = 0;
+
+		uss820_dci_setup_standard_chain_sub(&temp);
+		if (need_sync) {
+			/* we need a SYNC point after TX */
+			temp.func = &uss820_dci_data_tx_sync;
+			temp.len = 0;
+			temp.short_pkt = 0;
+
+			uss820_dci_setup_standard_chain_sub(&temp);
+		}
+	}
+	/* must have at least one frame! */
+	td = temp.td;
+	xfer->td_transfer_last = td;
+
+	return;
+}
+
+static void
+uss820_dci_timeout(struct usbd_xfer *xfer)
+{

>>> TRUNCATED FOR MAIL (1000 lines) <<<


More information about the p4-projects mailing list