Author: hselasky
Date: Sun Jun  1 10:22:18 2014
New Revision: 266946
URL: http://svnweb.freebsd.org/changeset/base/266946

Log:
  Add basic support for isochronous transfers in host mode to the
  ISP/SAF1761 driver.
  
  Sponsored by: DARPA, AFRL

Modified:
  head/sys/dev/usb/controller/saf1761_otg.c
  head/sys/dev/usb/controller/saf1761_otg_reg.h

Modified: head/sys/dev/usb/controller/saf1761_otg.c
==============================================================================
--- head/sys/dev/usb/controller/saf1761_otg.c   Sun Jun  1 08:45:27 2014        
(r266945)
+++ head/sys/dev/usb/controller/saf1761_otg.c   Sun Jun  1 10:22:18 2014        
(r266946)
@@ -112,6 +112,7 @@ SYSCTL_INT(_hw_usb_saf1761_otg, OID_AUTO
 static const struct usb_bus_methods saf1761_otg_bus_methods;
 static const struct usb_pipe_methods saf1761_otg_non_isoc_methods;
 static const struct usb_pipe_methods saf1761_otg_device_isoc_methods;
+static const struct usb_pipe_methods saf1761_otg_host_isoc_methods;
 
 static saf1761_otg_cmd_t saf1761_host_setup_tx;
 static saf1761_otg_cmd_t saf1761_host_bulk_data_rx;
@@ -758,7 +759,8 @@ saf1761_host_intr_data_rx(struct saf1761
        temp = SOTG_PTD_DW3_ACTIVE | (td->toggle << 25) | SOTG_PTD_DW3_CERR_3;
        SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
 
-       temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8) | 
(td->interval & 0xF8);
+       temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8) |
+           (td->interval & 0xF8);
        SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
 
        temp = td->dw1_value | (1 << 10) /* IN-PID */ | (td->ep_index >> 1);
@@ -847,7 +849,8 @@ saf1761_host_intr_data_tx(struct saf1761
        temp = SOTG_PTD_DW3_ACTIVE | (td->toggle << 25) | SOTG_PTD_DW3_CERR_3;
        SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
 
-       temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8) | 
(td->interval & 0xF8);
+       temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8) |
+           (td->interval & 0xF8);
        SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
 
        temp = td->dw1_value | (0 << 10) /* OUT-PID */ | (td->ep_index >> 1);
@@ -874,21 +877,160 @@ complete:
 static uint8_t
 saf1761_host_isoc_data_rx(struct saf1761_otg_softc *sc, struct saf1761_otg_td 
*td)
 {
+       uint32_t pdt_addr;
+       uint32_t temp;
+
+       if (td->channel < SOTG_HOST_CHANNEL_MAX) {
+               uint32_t status;
+               uint32_t count;
+
+               pdt_addr = SOTG_PTD(td->channel);
+
+               status = saf1761_peek_host_status_le_4(sc, pdt_addr + 
SOTG_PTD_DW3);
+
+               DPRINTFN(5, "STATUS=0x%08x\n", status);
+
+               if (status & SOTG_PTD_DW3_ACTIVE) {
+                       goto busy;
+               } else if (status & SOTG_PTD_DW3_HALTED) {
+                       goto complete;
+               }
+               count = (status & SOTG_PTD_DW3_XFER_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;
+                       } else {
+                               /* invalid USB packet */
+                               td->error_any = 1;
+                               goto complete;
+                       }
+               }
+
+               /* verify the packet byte count */
+               if (count > td->remainder) {
+                       /* invalid USB packet */
+                       td->error_any = 1;
+                       goto complete;
+               }
+
+               saf1761_read_host_memory(sc, td, count);
+               goto complete;
+       }
+
+       if (saf1761_host_channel_alloc(sc, td))
+               goto busy;
+
+       /* receive one more packet */
+
+       pdt_addr = SOTG_PTD(td->channel);
+
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW7, 0);
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW6, 0);
+
+       temp = (0xFC << td->uframe) & 0xFF;     /* complete split */
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW5, temp);
+
+       temp = (1U << td->uframe);              /* start split */
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, temp);
+
+       temp = SOTG_PTD_DW3_ACTIVE | SOTG_PTD_DW3_CERR_3;
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
+
+       temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8);
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
+
+       temp = td->dw1_value | (1 << 10) /* IN-PID */ | (td->ep_index >> 1);
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW1, temp);
+
+       temp = (td->ep_index << 31) | (1 << 29) /* pkt-multiplier */ |
+           (td->max_packet_size << 18) /* wMaxPacketSize */ |
+           (td->max_packet_size << 3) /* transfer count */ |
+           SOTG_PTD_DW0_VALID;
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW0, temp);
+
        /* activate PTD */
        SAF1761_WRITE_LE_4(sc, SOTG_ISO_PTD_SKIP_PTD,
            (~sc->sc_host_isoc_map) | sc->sc_host_isoc_suspend_map);
-
+busy:
        return (1);     /* busy */
+complete:
+       saf1761_host_channel_free(sc, td);
+       return (0);     /* complete */
 }
 
 static uint8_t
 saf1761_host_isoc_data_tx(struct saf1761_otg_softc *sc, struct saf1761_otg_td 
*td)
 {
+       uint32_t pdt_addr;
+       uint32_t temp;
+       uint32_t count;
+
+       if (td->channel < SOTG_HOST_CHANNEL_MAX) {
+               uint32_t status;
+
+               pdt_addr = SOTG_PTD(td->channel);
+
+               status = saf1761_peek_host_status_le_4(sc, pdt_addr + 
SOTG_PTD_DW3);
+
+               DPRINTFN(5, "STATUS=0x%08x\n", status);
+
+               if (status & SOTG_PTD_DW3_ACTIVE) {
+                       goto busy;
+               } else if (status & SOTG_PTD_DW3_HALTED) {
+                       goto complete;
+               }
+
+               goto complete;
+       }
+       if (saf1761_host_channel_alloc(sc, td))
+               goto busy;
+
+       count = td->max_packet_size;
+       if (td->remainder < count) {
+               /* we have a short packet */
+               td->short_pkt = 1;
+               count = td->remainder;
+       }
+
+       saf1761_write_host_memory(sc, td, count);
+
+       /* send one more packet */
+
+       pdt_addr = SOTG_PTD(td->channel);
+
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW7, 0);
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW6, 0);
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW5, 0);
+
+       temp = (1U << td->uframe);              /* start split */
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, temp);
+
+       temp = SOTG_PTD_DW3_ACTIVE | SOTG_PTD_DW3_CERR_3;
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
+
+       temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8);
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
+
+       temp = td->dw1_value | (0 << 10) /* OUT-PID */ | (td->ep_index >> 1);
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW1, temp);
+
+       temp = (td->ep_index << 31) | (1 << 29) /* pkt-multiplier */ |
+           (count << 18) /* wMaxPacketSize */ |
+           (count << 3) /* transfer count */ |
+           SOTG_PTD_DW0_VALID;
+       SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW0, temp);
+
        /* activate PTD */
        SAF1761_WRITE_LE_4(sc, SOTG_ISO_PTD_SKIP_PTD,
            (~sc->sc_host_isoc_map) | sc->sc_host_isoc_suspend_map);
-
+busy:
        return (1);     /* busy */
+complete:
+       saf1761_host_channel_free(sc, td);
+       return (0);     /* complete */
 }
 
 static void
@@ -2371,7 +2513,7 @@ static const struct usb_pipe_methods saf
 };
 
 /*------------------------------------------------------------------------*
- * saf1761_otg isochronous support
+ * saf1761_otg device side isochronous support
  *------------------------------------------------------------------------*/
 static void
 saf1761_otg_device_isoc_open(struct usb_xfer *xfer)
@@ -2453,6 +2595,88 @@ static const struct usb_pipe_methods saf
 };
 
 /*------------------------------------------------------------------------*
+ * saf1761_otg host side isochronous support
+ *------------------------------------------------------------------------*/
+static void
+saf1761_otg_host_isoc_open(struct usb_xfer *xfer)
+{
+       return;
+}
+
+static void
+saf1761_otg_host_isoc_close(struct usb_xfer *xfer)
+{
+       saf1761_otg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+saf1761_otg_host_isoc_enter(struct usb_xfer *xfer)
+{
+       struct saf1761_otg_softc *sc = SAF1761_OTG_BUS2SC(xfer->xroot->bus);
+       uint32_t temp;
+       uint32_t nframes;
+
+       DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
+           xfer, xfer->endpoint->isoc_next, xfer->nframes);
+
+       /* get the current frame index - we don't need the high bits */
+
+       nframes = (SAF1761_READ_LE_4(sc, SOTG_FRINDEX) & SOTG_FRINDEX_MASK) >> 
3;
+
+       /*
+        * check if the frame index is within the window where the
+        * frames will be inserted
+        */
+       temp = (nframes - xfer->endpoint->isoc_next) & (SOTG_FRINDEX_MASK >> 3);
+
+       if ((xfer->endpoint->is_synced == 0) ||
+           (temp < xfer->nframes)) {
+               /*
+                * If there is data underflow or the pipe queue is
+                * empty we schedule the transfer a few frames ahead
+                * of the current frame position. Else two isochronous
+                * transfers might overlap.
+                */
+               xfer->endpoint->isoc_next = (nframes + 3) & (SOTG_FRINDEX_MASK 
>> 3);
+               xfer->endpoint->is_synced = 1;
+               DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
+       }
+       /*
+        * compute how many milliseconds the insertion is ahead of the
+        * current frame position:
+        */
+       temp = (xfer->endpoint->isoc_next - nframes) & (SOTG_FRINDEX_MASK >> 3);
+
+       /*
+        * pre-compute when the isochronous transfer will be finished:
+        */
+       xfer->isoc_time_complete =
+           usb_isoc_time_expand(&sc->sc_bus, nframes) + temp +
+           xfer->nframes;
+
+       /* compute frame number for next insertion */
+       xfer->endpoint->isoc_next += xfer->nframes;
+
+       /* setup TDs */
+       saf1761_otg_setup_standard_chain(xfer);
+}
+
+static void
+saf1761_otg_host_isoc_start(struct usb_xfer *xfer)
+{
+       /* start TD chain */
+       saf1761_otg_start_standard_chain(xfer);
+}
+
+static const struct usb_pipe_methods saf1761_otg_host_isoc_methods =
+{
+       .open = saf1761_otg_host_isoc_open,
+       .close = saf1761_otg_host_isoc_close,
+       .enter = saf1761_otg_host_isoc_enter,
+       .start = saf1761_otg_host_isoc_start,
+};
+
+/*------------------------------------------------------------------------*
  * saf1761_otg root control support
  *------------------------------------------------------------------------*
  * Simulate a hardware HUB by handling all the necessary requests.
@@ -3174,7 +3398,17 @@ saf1761_otg_xfer_setup(struct usb_setup_
                        td->ep_index = ep_no;
                        td->ep_type = ep_type;
                        td->dw1_value = dw1;
-                       td->uframe = 0;
+                       if (ep_type == UE_ISOCHRONOUS) {
+                               if (parm->udev->speed == USB_SPEED_HIGH) {
+                                       uint8_t uframe_index = (ntd - 1 - n);
+                                       uframe_index <<= 
usbd_xfer_get_fps_shift(xfer);
+                                       td->uframe = (uframe_index & 7);
+                               } else {
+                                       td->uframe = 0;
+                               }
+                       } else {
+                               td->uframe = 0;
+                       }
                        if (ep_type == UE_INTERRUPT) {
                                if (xfer->interval > 32)
                                        td->interval = (32 / 2) << 3;
@@ -3202,6 +3436,8 @@ static void
 saf1761_otg_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor 
*edesc,
     struct usb_endpoint *ep)
 {
+       uint16_t mps;
+
        DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d\n",
            ep, udev->address,
            edesc->bEndpointAddress, udev->flags.usb_mode);
@@ -3211,6 +3447,20 @@ saf1761_otg_ep_init(struct usb_device *u
                return;
        }
 
+       /* Verify wMaxPacketSize */
+       mps = UGETW(edesc->wMaxPacketSize);
+       if (udev->speed == USB_SPEED_HIGH) {
+               if ((mps >> 11) & 3) {
+                       DPRINTF("A packet multiplier different from "
+                           "1 is not supported\n");
+                       return;
+               }
+       }
+       if (mps > SOTG_HS_MAX_PACKET_SIZE) {
+               DPRINTF("Packet size %d bigger than %d\n",
+                   (int)mps, SOTG_HS_MAX_PACKET_SIZE);
+               return;
+       }
        if (udev->flags.usb_mode == USB_MODE_DEVICE) {
                if (udev->speed != USB_SPEED_FULL &&
                    udev->speed != USB_SPEED_HIGH) {
@@ -3227,13 +3477,11 @@ saf1761_otg_ep_init(struct usb_device *u
                }
        } else {
                switch (edesc->bmAttributes & UE_XFERTYPE) {
-               case UE_CONTROL:
-               case UE_BULK:
-               case UE_INTERRUPT:
-                       ep->methods = &saf1761_otg_non_isoc_methods;
+               case UE_ISOCHRONOUS:
+                       ep->methods = &saf1761_otg_host_isoc_methods;
                        break;
                default:
-                       /* TODO */
+                       ep->methods = &saf1761_otg_non_isoc_methods;
                        break;
                }
        }

Modified: head/sys/dev/usb/controller/saf1761_otg_reg.h
==============================================================================
--- head/sys/dev/usb/controller/saf1761_otg_reg.h       Sun Jun  1 08:45:27 
2014        (r266945)
+++ head/sys/dev/usb/controller/saf1761_otg_reg.h       Sun Jun  1 10:22:18 
2014        (r266946)
@@ -183,6 +183,7 @@
 /* Host controller specific registers */
 
 #define        SOTG_FRINDEX 0x002c
+#define        SOTG_FRINDEX_MASK 0x3fff
 #define        SOTG_CONFIGFLAG 0x0060
 #define        SOTG_CONFIGFLAG_ENABLE (1 << 0)
 #define        SOTG_PORTSC1 0x0064
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to