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 <hsela...@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 ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to