Author: hselasky
Date: Sat Aug 15 12:06:15 2015
New Revision: 286802
URL: https://svnweb.freebsd.org/changeset/base/286802

Log:
  Fixes for HIGH speed ISOCHRONOUS traffic. HS ISOCHRONOUS traffic at
  intervals less than 250us was not handled properly. Add support for
  high-bandwidth ISOCHRONOUS packets. USB webcams, USB audio and USB DVB
  devices are expected to work better. High-bandwidth INTERRUPT
  endpoints is not yet supported.
  
  MFC after:    2 weeks

Modified:
  head/sys/dev/usb/controller/dwc_otg.c
  head/sys/dev/usb/controller/dwc_otg.h

Modified: head/sys/dev/usb/controller/dwc_otg.c
==============================================================================
--- head/sys/dev/usb/controller/dwc_otg.c       Sat Aug 15 11:08:30 2015        
(r286801)
+++ head/sys/dev/usb/controller/dwc_otg.c       Sat Aug 15 12:06:15 2015        
(r286802)
@@ -1,7 +1,7 @@
 /* $FreeBSD$ */
 /*-
  * Copyright (c) 2015 Daisuke Aoyama. All rights reserved.
- * Copyright (c) 2012 Hans Petter Selasky. All rights reserved.
+ * Copyright (c) 2012-2015 Hans Petter Selasky. All rights reserved.
  * Copyright (c) 2010-2011 Aleksandr Rybalko. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -597,14 +597,18 @@ dwc_otg_clear_hcint(struct dwc_otg_softc
 }
 
 static uint8_t
-dwc_otg_host_check_fifo_empty(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+dwc_otg_host_check_tx_fifo_empty(struct dwc_otg_softc *sc, struct dwc_otg_td 
*td)
 {
        uint32_t temp;
 
        temp = DWC_OTG_READ_4(sc, DOTG_GINTSTS);
 
-       if (td->ep_type == UE_INTERRUPT ||
-           td->ep_type == UE_ISOCHRONOUS) {
+       if (td->ep_type == UE_ISOCHRONOUS) {
+               /*
+                * NOTE: USB INTERRUPT transactions are executed like
+                * USB CONTROL transactions! See the setup standard
+                * chain function for more information.
+                */
                if (!(temp & GINTSTS_PTXFEMP)) {
                        DPRINTF("Periodic TX FIFO is not empty\n");
                        if (!(sc->sc_irq_mask & GINTMSK_PTXFEMPMSK)) {
@@ -631,8 +635,10 @@ dwc_otg_host_channel_alloc(struct dwc_ot
     struct dwc_otg_td *td, uint8_t is_out)
 {
        uint8_t x;
+       uint8_t y;
+       uint8_t z;
 
-       if (td->channel < DWC_OTG_MAX_CHANNELS)
+       if (td->channel[0] < DWC_OTG_MAX_CHANNELS)
                return (0);             /* already allocated */
 
        /* check if device is suspended */
@@ -641,20 +647,42 @@ dwc_otg_host_channel_alloc(struct dwc_ot
 
        /* compute needed TX FIFO size */
        if (is_out != 0) {
-               if (dwc_otg_host_check_fifo_empty(sc, td) != 0)
+               if (dwc_otg_host_check_tx_fifo_empty(sc, td) != 0)
                        return (1);     /* busy - cannot transfer data */
        }
-
-       for (x = 0; x != sc->sc_host_ch_max; x++) {
+       z = td->max_packet_count;
+       for (x = y = 0; x != sc->sc_host_ch_max; x++) {
                /* check if channel is allocated */
                if (sc->sc_chan_state[x].allocated != 0)
                        continue;
                /* check if channel is still enabled */
                if (sc->sc_chan_state[x].wait_halted != 0)
                        continue;
+               /* store channel number */
+               td->channel[y++] = x;
+               /* check if we got all channels */
+               if (y == z)
+                       break;
+       }
+       if (y != z) {
+               /* reset channel variable */
+               td->channel[0] = DWC_OTG_MAX_CHANNELS;
+               td->channel[1] = DWC_OTG_MAX_CHANNELS;
+               td->channel[2] = DWC_OTG_MAX_CHANNELS;
+               /* wait a bit */
+               dwc_otg_enable_sof_irq(sc);
+               return (1);     /* busy - not enough channels */
+       }
+
+       for (y = 0; y != z; y++) {
+               x = td->channel[y];
 
+               /* set allocated */
                sc->sc_chan_state[x].allocated = 1;
 
+               /* set wait halted */
+               sc->sc_chan_state[x].wait_halted = 1;
+
                /* clear interrupts */
                dwc_otg_clear_hcint(sc, x);
 
@@ -663,29 +691,22 @@ dwc_otg_host_channel_alloc(struct dwc_ot
 
                /* set active channel */
                sc->sc_active_rx_ep |= (1 << x);
-
-               /* set channel */
-               td->channel = x;
-
-               return (0);     /* allocated */
        }
-       /* wait a bit */
-       dwc_otg_enable_sof_irq(sc);
-       return (1);     /* busy */
+       return (0);     /* allocated */
 }
 
 static void
-dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+dwc_otg_host_channel_free_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td, 
uint8_t index)
 {
        uint32_t hcchar;
        uint8_t x;
 
-       if (td->channel >= DWC_OTG_MAX_CHANNELS)
+       if (td->channel[index] >= DWC_OTG_MAX_CHANNELS)
                return;         /* already freed */
 
        /* free channel */
-       x = td->channel;
-       td->channel = DWC_OTG_MAX_CHANNELS;
+       x = td->channel[index];
+       td->channel[index] = DWC_OTG_MAX_CHANNELS;
 
        DPRINTF("CH=%d\n", x);
 
@@ -704,26 +725,42 @@ dwc_otg_host_channel_free(struct dwc_otg
        /* clear active channel */
        sc->sc_active_rx_ep &= ~(1 << x);
 
+       /* check if already halted */
+       if (sc->sc_chan_state[x].wait_halted == 0)
+               return;
+
        /* disable host channel */
        hcchar = DWC_OTG_READ_4(sc, DOTG_HCCHAR(x));
        if (hcchar & HCCHAR_CHENA) {
                DPRINTF("Halting channel %d\n", x);
                DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(x),
                    hcchar | HCCHAR_CHDIS);
-               sc->sc_chan_state[x].wait_halted = 1;
                /* don't write HCCHAR until the channel is halted */
+       } else {
+               sc->sc_chan_state[x].wait_halted = 0;
        }
 }
 
 static void
+dwc_otg_host_channel_free(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+{
+       uint8_t x;
+       for (x = 0; x != td->max_packet_count; x++)
+               dwc_otg_host_channel_free_sub(sc, td, x);
+}
+
+static void
 dwc_otg_host_dump_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
 {
+       uint8_t x;
        /* dump any pending messages */
-       if (sc->sc_last_rx_status != 0) {
-               if (td->channel < DWC_OTG_MAX_CHANNELS &&
-                   td->channel == GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status)) {
-                       dwc_otg_common_rx_ack(sc);
-               }
+       if (sc->sc_last_rx_status == 0)
+               return;
+       for (x = 0; x != td->max_packet_count; x++) {
+               if (td->channel[x] >= DWC_OTG_MAX_CHANNELS ||
+                   td->channel[x] != GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status))
+                       continue;
+               dwc_otg_common_rx_ack(sc);
        }
 }
 
@@ -737,13 +774,13 @@ dwc_otg_host_setup_tx(struct dwc_otg_sof
 
        dwc_otg_host_dump_rx(sc, td);
 
-       if (td->channel < DWC_OTG_MAX_CHANNELS) {
-               hcint = sc->sc_chan_state[td->channel].hcint;
+       if (td->channel[0] < DWC_OTG_MAX_CHANNELS) {
+               hcint = sc->sc_chan_state[td->channel[0]].hcint;
 
                DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x 
HCTSIZ=0x%08x\n",
-                   td->channel, td->state, hcint,
-                   DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel)),
-                   DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel)));
+                   td->channel[0], td->state, hcint,
+                   DWC_OTG_READ_4(sc, DOTG_HCCHAR(td->channel[0])),
+                   DWC_OTG_READ_4(sc, DOTG_HCTSIZ(td->channel[0])));
        } else {
                hcint = 0;
                goto check_state;
@@ -753,12 +790,12 @@ dwc_otg_host_setup_tx(struct dwc_otg_sof
            HCINT_ACK | HCINT_NYET)) {
                /* give success bits priority over failure bits */
        } else if (hcint & HCINT_STALL) {
-               DPRINTF("CH=%d STALL\n", td->channel);
+               DPRINTF("CH=%d STALL\n", td->channel[0]);
                td->error_stall = 1;
                td->error_any = 1;
                goto complete;
        } else if (hcint & HCINT_ERRORS) {
-               DPRINTF("CH=%d ERROR\n", td->channel);
+               DPRINTF("CH=%d ERROR\n", td->channel[0]);
                td->errcnt++;
                if (td->hcsplt != 0 || td->errcnt >= 3) {
                        td->error_any = 1;
@@ -863,23 +900,23 @@ send_pkt:
 
        usbd_copy_out(td->pc, 0, &req, sizeof(req));
 
-       DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+       DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]),
            (sizeof(req) << HCTSIZ_XFERSIZE_SHIFT) |
            (1 << HCTSIZ_PKTCNT_SHIFT) |
            (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
 
-       DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
+       DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt);
 
        hcchar = td->hcchar;
        hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK);
        hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT;
 
        /* must enable channel before writing data to FIFO */
-       DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
+       DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar);
 
        /* transfer data into FIFO */
        bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
-           DOTG_DFIFO(td->channel), (uint32_t *)&req, sizeof(req) / 4);
+           DOTG_DFIFO(td->channel[0]), (uint32_t *)&req, sizeof(req) / 4);
 
        /* wait until next slot before trying complete split */
        td->tt_complete_slot = sc->sc_last_frame_num + 1;
@@ -916,17 +953,17 @@ send_cpkt:
        td->hcsplt |= HCSPLT_COMPSPLT;
        td->state = DWC_CHAN_ST_WAIT_C_ANE;
 
-       DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel),
+       DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(td->channel[0]),
            (HCTSIZ_PID_SETUP << HCTSIZ_PID_SHIFT));
 
-       DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel), td->hcsplt);
+       DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(td->channel[0]), td->hcsplt);
 
        hcchar = td->hcchar;
        hcchar &= ~(HCCHAR_EPDIR_IN | HCCHAR_EPTYPE_MASK);
        hcchar |= UE_CONTROL << HCCHAR_EPTYPE_SHIFT;
 
        /* must enable channel before writing data to FIFO */
-       DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel), hcchar);
+       DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(td->channel[0]), hcchar);
 
 busy:
        return (1);     /* busy */
@@ -1064,11 +1101,11 @@ dwc_otg_host_rate_check(struct dwc_otg_s
 
        if (td->ep_type == UE_ISOCHRONOUS) {
                /* non TT isochronous traffic */
-               if ((td->tmr_val != 0) ||
-                   (frame_num & (td->tmr_res - 1))) {
+               if (frame_num & (td->tmr_res - 1))
                        goto busy;
-               }
-               td->tmr_val = 1;        /* executed */
+               if ((frame_num ^ td->tmr_val) & td->tmr_res)
+                       goto busy;
+               td->tmr_val = td->tmr_res + sc->sc_last_frame_num;
                td->toggle = 0;
                return (0);
        } else if (td->ep_type == UE_INTERRUPT) {
@@ -1096,16 +1133,15 @@ busy:
 }
 
 static uint8_t
-dwc_otg_host_data_rx_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
+dwc_otg_host_data_rx_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td,
+    uint8_t channel)
 {
        uint32_t count;
-       uint8_t channel;
 
        /* check endpoint status */
        if (sc->sc_last_rx_status == 0)
                goto busy;
 
-       channel = td->channel;
        if (channel >= DWC_OTG_MAX_CHANNELS)
                goto busy;
 
@@ -1130,21 +1166,22 @@ dwc_otg_host_data_rx_sub(struct dwc_otg_
                /* get the packet byte count */
                count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status);
 
-               /* check for isochronous transfer or high-speed bandwidth 
endpoint */
-               if (td->ep_type == UE_ISOCHRONOUS || td->max_packet_count > 1) {
-                       if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) != 
GRXSTSRD_DPID_DATA0) {
+               /* check for ISOCHRONOUS endpoint */
+               if (td->ep_type == UE_ISOCHRONOUS) {
+                       if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) !=
+                           GRXSTSRD_DPID_DATA0) {
+                               /* more data to be received */
                                td->tt_xactpos = HCSPLT_XACTPOS_MIDDLE;
                        } else {
+                               /* all data received */
                                td->tt_xactpos = HCSPLT_XACTPOS_BEGIN;
-
                                /* verify the packet byte count */
-                               if (count < td->max_packet_size) {
+                               if (count != td->remainder) {
                                        /* we have a short packet */
                                        td->short_pkt = 1;
                                        td->got_short = 1;
                                }
                        }
-                       td->toggle = 0;
                } else {
                        /* verify the packet byte count */
                        if (count != td->max_packet_size) {
@@ -1196,15 +1233,17 @@ complete:
 static uint8_t
 dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
 {
-       uint32_t hcint;
+       uint32_t hcint = 0;
        uint32_t hcchar;
        uint8_t delta;
        uint8_t channel;
+       uint8_t x;
 
-       channel = td->channel;
-
-       if (channel < DWC_OTG_MAX_CHANNELS) {
-               hcint = sc->sc_chan_state[channel].hcint;
+       for (x = 0; x != td->max_packet_count; x++) {
+               channel = td->channel[x];
+               if (channel >= DWC_OTG_MAX_CHANNELS)
+                       continue;
+               hcint |= sc->sc_chan_state[channel].hcint;
 
                DPRINTF("CH=%d ST=%d HCINT=0x%08x HCCHAR=0x%08x 
HCTSIZ=0x%08x\n",
                    channel, td->state, hcint,
@@ -1232,19 +1271,17 @@ dwc_otg_host_data_rx(struct dwc_otg_soft
                }
 
                /* check channels for data, if any */
-               if (dwc_otg_host_data_rx_sub(sc, td))
+               if (dwc_otg_host_data_rx_sub(sc, td, channel))
                        goto complete;
 
                /* refresh interrupt status */
-               hcint = sc->sc_chan_state[channel].hcint;
+               hcint |= sc->sc_chan_state[channel].hcint;
 
                if (hcint & (HCINT_ERRORS | HCINT_RETRY |
                    HCINT_ACK | HCINT_NYET)) {
                        if (!(hcint & HCINT_ERRORS))
                                td->errcnt = 0;
                }
-       } else {
-               hcint = 0;
        }
 
        switch (td->state) {
@@ -1271,6 +1308,8 @@ dwc_otg_host_data_rx(struct dwc_otg_soft
                                        td->toggle ^= 1;
                                        goto receive_pkt;
                                }
+                       } else if (td->ep_type == UE_ISOCHRONOUS) {
+                               goto complete;
                        }
                        td->did_nak = 1;
                        td->tt_scheduled = 0;
@@ -1294,12 +1333,12 @@ dwc_otg_host_data_rx(struct dwc_otg_soft
 
                        if (td->ep_type == UE_ISOCHRONOUS) {
                                /* check if we are complete */
-                               if ((td->remainder == 0) ||
-                                   (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN)) {
+                               if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN) {
                                        goto complete;
+                               } else {
+                                       /* get more packets */
+                                       goto busy;
                                }
-                               /* get another packet */
-                               goto receive_pkt;
                        } else {
                                /* check if we are complete */
                                if ((td->remainder == 0) || (td->got_short != 
0)) {
@@ -1367,8 +1406,7 @@ receive_pkt:
                }
                /* complete split */
                td->hcsplt |= HCSPLT_COMPSPLT;
-       } else if (td->tt_xactpos == HCSPLT_XACTPOS_BEGIN &&
-           dwc_otg_host_rate_check(sc, td)) {
+       } else if (dwc_otg_host_rate_check(sc, td)) {
                td->state = DWC_CHAN_ST_WAIT_C_PKT;
                goto busy;
        }
@@ -1379,8 +1417,6 @@ receive_pkt:
                goto busy;
        }
 
-       channel = td->channel;
-
        /* set toggle, if any */
        if (td->set_toggle) {
                td->set_toggle = 0;
@@ -1389,28 +1425,31 @@ receive_pkt:
 
        td->state = DWC_CHAN_ST_WAIT_ANE;
 
-       /* receive one packet */
-       DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
-           (td->max_packet_size << HCTSIZ_XFERSIZE_SHIFT) |
-           (1 << HCTSIZ_PKTCNT_SHIFT) |
-           (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
-           (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+       for (x = 0; x != td->max_packet_count; x++) {
+               channel = td->channel[x];
 
-       DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
+               /* receive one packet */
+               DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+                   (td->max_packet_size << HCTSIZ_XFERSIZE_SHIFT) |
+                   (1 << HCTSIZ_PKTCNT_SHIFT) |
+                   (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
+                   (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
 
-       hcchar = td->hcchar;
-       hcchar |= HCCHAR_EPDIR_IN;
+               DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
 
-       /* receive complete split ASAP */
-       if ((sc->sc_last_frame_num & 1) != 0 &&
-           (td->ep_type == UE_INTERRUPT || td->ep_type == UE_ISOCHRONOUS))
-               hcchar |= HCCHAR_ODDFRM;
-       else
-               hcchar &= ~HCCHAR_ODDFRM;
+               hcchar = td->hcchar;
+               hcchar |= HCCHAR_EPDIR_IN;
 
-       /* must enable channel before data can be received */
-       DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
+               /* receive complete split ASAP */
+               if ((sc->sc_last_frame_num & 1) != 0 &&
+                   td->ep_type == UE_ISOCHRONOUS)
+                       hcchar |= HCCHAR_ODDFRM;
+               else
+                       hcchar &= ~HCCHAR_ODDFRM;
 
+               /* must enable channel before data can be received */
+               DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
+       }
        /* wait until next slot before trying complete split */
        td->tt_complete_slot = sc->sc_last_frame_num + 1;
 
@@ -1439,7 +1478,7 @@ receive_spkt:
                goto busy;
        }
 
-       channel = td->channel;
+       channel = td->channel[0];
 
        td->hcsplt &= ~HCSPLT_COMPSPLT;
        td->state = DWC_CHAN_ST_WAIT_S_ANE;
@@ -1452,7 +1491,7 @@ receive_spkt:
 
        /* send after next SOF event */
        if ((sc->sc_last_frame_num & 1) == 0 &&
-           (td->ep_type == UE_INTERRUPT || td->ep_type == UE_ISOCHRONOUS))
+           td->ep_type == UE_ISOCHRONOUS)
                td->hcchar |= HCCHAR_ODDFRM;
        else
                td->hcchar &= ~HCCHAR_ODDFRM;
@@ -1607,10 +1646,12 @@ dwc_otg_host_data_tx(struct dwc_otg_soft
        uint32_t hcchar;
        uint8_t delta;
        uint8_t channel;
+       uint8_t x;
 
        dwc_otg_host_dump_rx(sc, td);
 
-       channel = td->channel;
+       /* check that last channel is complete */
+       channel = td->channel[td->npkt];
 
        if (channel < DWC_OTG_MAX_CHANNELS) {
                hcint = sc->sc_chan_state[channel].hcint;
@@ -1721,33 +1762,13 @@ dwc_otg_host_data_tx(struct dwc_otg_soft
                goto send_cpkt;
 
        case DWC_CHAN_ST_TX_WAIT_ISOC:
-
-               /* Check if isochronous OUT traffic is complete */
+               /* Check if ISOCHRONOUS OUT traffic is complete */
                if ((hcint & HCINT_HCH_DONE_MASK) == 0)
                        break;
 
                td->offset += td->tx_bytes;
                td->remainder -= td->tx_bytes;
-
-               if (td->hcsplt != 0 || td->remainder == 0)
-                       goto complete;
-
-               /* check for next packet */
-               if (td->max_packet_count > 1)
-                       td->tt_xactpos++;
-
-               /* free existing channel, if any */
-               dwc_otg_host_channel_free(sc, td);
-
-               td->state = DWC_CHAN_ST_TX_PKT_ISOC;
-
-               /* FALLTHROUGH */
-
-       case DWC_CHAN_ST_TX_PKT_ISOC:
-               if (dwc_otg_host_channel_alloc(sc, td, 1))
-                       break;
-               channel = td->channel;
-               goto send_isoc_pkt;
+               goto complete;
        default:
                break;
        }
@@ -1781,8 +1802,6 @@ send_pkt:
                goto busy;
        }
 
-       channel = td->channel;
-
        /* set toggle, if any */
        if (td->set_toggle) {
                td->set_toggle = 0;
@@ -1790,8 +1809,7 @@ send_pkt:
        }
 
        if (td->ep_type == UE_ISOCHRONOUS) {
-send_isoc_pkt:
-               /* Isochronous OUT transfers don't have any ACKs */
+               /* ISOCHRONOUS OUT transfers don't have any ACKs */
                td->state = DWC_CHAN_ST_TX_WAIT_ISOC;
                td->hcsplt &= ~HCSPLT_COMPSPLT;
                if (td->hcsplt != 0) {
@@ -1805,123 +1823,110 @@ send_isoc_pkt:
                        /* Update transaction position */
                        td->hcsplt &= ~HCSPLT_XACTPOS_MASK;
                        td->hcsplt |= (HCSPLT_XACTPOS_ALL << 
HCSPLT_XACTPOS_SHIFT);
-               } else {
-                       /* send one packet at a time */
-                       count = td->max_packet_size;
-                       if (td->remainder < count) {
-                               /* we have a short packet */
-                               td->short_pkt = 1;
-                               count = td->remainder;
-                       }
                }
        } else if (td->hcsplt != 0) {
-
                td->hcsplt &= ~HCSPLT_COMPSPLT;
-
                /* Wait for ACK/NAK/ERR from TT */
                td->state = DWC_CHAN_ST_WAIT_S_ANE;
-
-               /* send one packet at a time */
-               count = td->max_packet_size;
-               if (td->remainder < count) {
-                       /* we have a short packet */
-                       td->short_pkt = 1;
-                       count = td->remainder;
-               }
        } else {
                /* Wait for ACK/NAK/STALL from device */
                td->state = DWC_CHAN_ST_WAIT_ANE;
+       }
+
+       td->tx_bytes = 0;
+       
+       for (x = 0; x != td->max_packet_count; x++) {
+               uint32_t rem_bytes;
+
+               channel = td->channel[x];
 
                /* send one packet at a time */
                count = td->max_packet_size;
-               if (td->remainder < count) {
+               rem_bytes = td->remainder - td->tx_bytes;
+               if (rem_bytes < count) {
                        /* we have a short packet */
                        td->short_pkt = 1;
-                       count = td->remainder;
-               }
-       }
-
-       /* check for High-Speed multi-packets */
-       if ((td->hcsplt == 0) && (td->max_packet_count > 1)) {
-               if (td->npkt == 0) {
-                       if (td->remainder >= (3 * td->max_packet_size))
-                               td->npkt = 3;
-                       else if (td->remainder >= (2 * td->max_packet_size))
-                               td->npkt = 2;
-                       else
-                               td->npkt = 1;
-
-                       if (td->npkt > td->max_packet_count)
-                               td->npkt = td->max_packet_count;
-
-                       td->tt_xactpos = 1;     /* overload */
+                       count = rem_bytes;
                }
-               if (td->tt_xactpos == td->npkt) {
-                       if (td->npkt == 1) {
+               if (count == rem_bytes) {
+                       /* last packet */
+                       switch (x) {
+                       case 0:
                                DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
                                    (count << HCTSIZ_XFERSIZE_SHIFT) |
                                    (1 << HCTSIZ_PKTCNT_SHIFT) |
-                                   (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT));
-                       } else if (td->npkt == 2) {
+                                   (td->toggle ? (HCTSIZ_PID_DATA1 << 
HCTSIZ_PID_SHIFT) :
+                                   (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
+                               break;
+                       case 1:
                                DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
                                    (count << HCTSIZ_XFERSIZE_SHIFT) |
                                    (1 << HCTSIZ_PKTCNT_SHIFT) |
                                    (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT));
-                       } else {
+                               break;
+                       default:
                                DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
                                    (count << HCTSIZ_XFERSIZE_SHIFT) |
                                    (1 << HCTSIZ_PKTCNT_SHIFT) |
                                    (HCTSIZ_PID_DATA2 << HCTSIZ_PID_SHIFT));
+                               break;
                        }
-                       td->npkt = 0;
-               } else {
+               } else if (td->ep_type == UE_ISOCHRONOUS &&
+                          td->max_packet_count > 1) {
+                       /* ISOCHRONOUS multi packet */
                        DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
                            (count << HCTSIZ_XFERSIZE_SHIFT) |
                            (1 << HCTSIZ_PKTCNT_SHIFT) |
                            (HCTSIZ_PID_MDATA << HCTSIZ_PID_SHIFT));
+               } else {
+                       /* TODO: HCTSIZ_DOPNG */
+                       /* standard BULK/INTERRUPT/CONTROL packet */
+                       DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
+                           (count << HCTSIZ_XFERSIZE_SHIFT) |
+                           (1 << HCTSIZ_PKTCNT_SHIFT) |
+                           (td->toggle ? (HCTSIZ_PID_DATA1 << 
HCTSIZ_PID_SHIFT) :
+                           (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
                }
-       } else {
-               /* TODO: HCTSIZ_DOPNG */
 
-               DWC_OTG_WRITE_4(sc, DOTG_HCTSIZ(channel),
-                   (count << HCTSIZ_XFERSIZE_SHIFT) |
-                   (1 << HCTSIZ_PKTCNT_SHIFT) |
-                   (td->toggle ? (HCTSIZ_PID_DATA1 << HCTSIZ_PID_SHIFT) :
-                   (HCTSIZ_PID_DATA0 << HCTSIZ_PID_SHIFT)));
-       }
+               DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
 
-       DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
+               hcchar = td->hcchar;
+               hcchar &= ~HCCHAR_EPDIR_IN;
 
-       hcchar = td->hcchar;
-       hcchar &= ~HCCHAR_EPDIR_IN;
+               /* send after next SOF event */
+               if ((sc->sc_last_frame_num & 1) == 0 &&
+                   td->ep_type == UE_ISOCHRONOUS)
+                       hcchar |= HCCHAR_ODDFRM;
+               else
+                       hcchar &= ~HCCHAR_ODDFRM;
 
-       /* send after next SOF event */
-       if ((sc->sc_last_frame_num & 1) == 0 &&
-           (td->ep_type == UE_INTERRUPT || td->ep_type == UE_ISOCHRONOUS))
-               hcchar |= HCCHAR_ODDFRM;
-       else
-               hcchar &= ~HCCHAR_ODDFRM;
+               /* must enable before writing data to FIFO */
+               DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
 
-       /* must enable before writing data to FIFO */
-       DWC_OTG_WRITE_4(sc, DOTG_HCCHAR(channel), hcchar);
+               if (count != 0) {
+                       /* clear topmost word before copy */
+                       sc->sc_tx_bounce_buffer[(count - 1) / 4] = 0;
 
-       if (count != 0) {
+                       /* copy out data */
+                       usbd_copy_out(td->pc, td->offset + td->tx_bytes,
+                           sc->sc_tx_bounce_buffer, count);
 
-               /* clear topmost word before copy */
-               sc->sc_tx_bounce_buffer[(count - 1) / 4] = 0;
+                       /* transfer data into FIFO */
+                       bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
+                           DOTG_DFIFO(channel),
+                           sc->sc_tx_bounce_buffer, (count + 3) / 4);
+               }
 
-               /* copy out data */
-               usbd_copy_out(td->pc, td->offset,
-                   sc->sc_tx_bounce_buffer, count);
-
-               /* transfer data into FIFO */
-               bus_space_write_region_4(sc->sc_io_tag, sc->sc_io_hdl,
-                   DOTG_DFIFO(channel),
-                   sc->sc_tx_bounce_buffer, (count + 3) / 4);
-       }
+               /* store number of bytes transmitted */
+               td->tx_bytes += count;
 
-       /* store number of bytes transmitted */
-       td->tx_bytes = count;
+               /* store last packet index */
+               td->npkt = x;
+               
+               /* check for last packet */
+               if (count == rem_bytes)
+                       break;
+       }
        goto busy;
 
 send_cpkt:
@@ -1947,7 +1952,7 @@ send_cpkt:
                goto busy;
        }
 
-       channel = td->channel;
+       channel = td->channel[0];
 
        td->hcsplt |= HCSPLT_COMPSPLT;
        td->state = DWC_CHAN_ST_WAIT_C_ANE;
@@ -1962,7 +1967,7 @@ send_cpkt:
 
        /* receive complete split ASAP */
        if ((sc->sc_last_frame_num & 1) != 0 &&
-           (td->ep_type == UE_INTERRUPT || td->ep_type == UE_ISOCHRONOUS))
+           td->ep_type == UE_ISOCHRONOUS)
                hcchar |= HCCHAR_ODDFRM;
        else
                hcchar &= ~HCCHAR_ODDFRM;
@@ -2389,9 +2394,6 @@ dwc_otg_update_host_transfer_schedule_lo
                        if ((td->hcchar & HCCHAR_EPDIR_IN) != 0)
                                continue;
 
-                       /* execute more frames */
-                       td->tmr_val = 0;
-
                        sc->sc_needsof = 1;
 
                        if (td->hcsplt == 0 || td->tt_scheduled != 0)
@@ -2423,9 +2425,6 @@ dwc_otg_update_host_transfer_schedule_lo
                        if ((td->hcchar & HCCHAR_EPDIR_IN) == 0)
                                continue;
 
-                       /* execute more frames */
-                       td->tmr_val = 0;
-
                        sc->sc_needsof = 1;
 
                        if (td->hcsplt == 0 || td->tt_scheduled != 0)
@@ -2519,10 +2518,10 @@ dwc_otg_update_host_transfer_schedule_lo
        TAILQ_CONCAT(&head, &sc->sc_bus.intr_q.head, wait_entry);
        TAILQ_CONCAT(&sc->sc_bus.intr_q.head, &head, wait_entry);
 
-       /* put non-TT BULK transfers last */
+       /* put non-TT non-ISOCHRONOUS transfers last */
        TAILQ_FOREACH_SAFE(xfer, &sc->sc_bus.intr_q.head, wait_entry, 
xfer_next) {
                td = xfer->td_transfer_cache;
-               if (td == NULL || td->hcsplt != 0 || td->ep_type != UE_BULK)
+               if (td == NULL || td->hcsplt != 0 || td->ep_type == 
UE_ISOCHRONOUS)
                        continue;
                TAILQ_REMOVE(&sc->sc_bus.intr_q.head, xfer, wait_entry);
                TAILQ_INSERT_TAIL(&head, xfer, wait_entry);
@@ -2984,7 +2983,9 @@ dwc_otg_setup_standard_chain_sub(struct 
        td->set_toggle = 0;
        td->got_short = 0;
        td->did_nak = 0;
-       td->channel = DWC_OTG_MAX_CHANNELS;
+       td->channel[0] = DWC_OTG_MAX_CHANNELS;
+       td->channel[1] = DWC_OTG_MAX_CHANNELS;
+       td->channel[2] = DWC_OTG_MAX_CHANNELS;
        td->state = 0;
        td->errcnt = 0;
        td->tt_scheduled = 0;
@@ -3249,8 +3250,10 @@ dwc_otg_setup_standard_chain(struct usb_
                                td->tmr_val = sc->sc_tmr_val + ival;
                                td->tmr_res = ival;
                        } else if (td->ep_type == UE_ISOCHRONOUS) {
-                               td->tmr_val = 0;
                                td->tmr_res = 1;
+                               td->tmr_val = sc->sc_last_frame_num;
+                               if (td->hcchar & HCCHAR_EPDIR_IN)
+                                       td->tmr_val++;
                        } else {
                                td->tmr_val = 0;
                                td->tmr_res = (uint8_t)sc->sc_last_frame_num;
@@ -3260,10 +3263,8 @@ dwc_otg_setup_standard_chain(struct usb_
                        hcsplt = 0;
                        if (td->ep_type == UE_INTERRUPT) {
                                uint32_t ival;
-#if 0
                                hcchar |= ((xfer->max_packet_count & 3)
                                    << HCCHAR_MC_SHIFT);
-#endif
                                ival = xfer->interval / DWC_OTG_HOST_TIMER_RATE;
                                if (ival == 0)
                                        ival = 1;
@@ -3274,8 +3275,11 @@ dwc_otg_setup_standard_chain(struct usb_
                        } else if (td->ep_type == UE_ISOCHRONOUS) {
                                hcchar |= ((xfer->max_packet_count & 3)
                                    << HCCHAR_MC_SHIFT);
-                               td->tmr_val = 0;
                                td->tmr_res = 1 << 
usbd_xfer_get_fps_shift(xfer);
+                               td->tmr_val = sc->sc_last_frame_num;
+                               if (td->hcchar & HCCHAR_EPDIR_IN)
+                                       td->tmr_val += td->tmr_res;
+
                        } else {
                                td->tmr_val = 0;
                                td->tmr_res = (uint8_t)sc->sc_last_frame_num;
@@ -3332,6 +3336,19 @@ dwc_otg_start_standard_chain(struct usb_
                dwc_otg_xfer_do_fifo(sc, xfer);
                if (dwc_otg_xfer_do_complete_locked(sc, xfer))
                        goto done;
+       } else {
+               struct dwc_otg_td *td = xfer->td_transfer_cache;
+               if (td->ep_type == UE_ISOCHRONOUS &&
+                   (td->hcchar & HCCHAR_EPDIR_IN) == 0) {
+                       /*
+                        * Need to start ISOCHRONOUS OUT transfer ASAP
+                        * because execution is delayed by one 125us
+                        * microframe:
+                        */
+                       dwc_otg_xfer_do_fifo(sc, xfer);
+                       if (dwc_otg_xfer_do_complete_locked(sc, xfer))
+                               goto done;
+               }
        }
 
        /* put transfer on interrupt queue */
@@ -4725,6 +4742,9 @@ dwc_otg_xfer_setup(struct usb_setup_para
                        /* init TD */
                        td->max_packet_size = xfer->max_packet_size;
                        td->max_packet_count = xfer->max_packet_count;
+                       /* range check */
+                       if (td->max_packet_count == 0 || td->max_packet_count > 
3)
+                               td->max_packet_count = 1;
                        td->ep_no = ep_no;
                        td->ep_type = ep_type;
                        td->obj_next = last_obj;
@@ -4763,12 +4783,13 @@ dwc_otg_ep_init(struct usb_device *udev,
                                return;
                        }
                } else {
-                       if (udev->speed == USB_SPEED_HIGH) {
-                               if ((UGETW(edesc->wMaxPacketSize) >> 11) & 3) {
-                                       /* high bandwidth endpoint - not tested 
*/
-                                       DPRINTF("High Bandwidth Endpoint - not 
tested\n");
-                                       return;
-                               }
+                       if (udev->speed == USB_SPEED_HIGH &&
+                           (edesc->wMaxPacketSize[1] & 0x18) != 0 &&
+                           (edesc->bmAttributes & UE_XFERTYPE) != 
UE_ISOCHRONOUS) {
+                               /* not supported */
+                               DPRINTFN(-1, "Non-isochronous high bandwidth "
+                                   "endpoint not supported\n");
+                               return;
                        }
                }
                if ((edesc->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS)

Modified: head/sys/dev/usb/controller/dwc_otg.h
==============================================================================
--- head/sys/dev/usb/controller/dwc_otg.h       Sat Aug 15 11:08:30 2015        
(r286801)
+++ head/sys/dev/usb/controller/dwc_otg.h       Sat Aug 15 12:06:15 2015        
(r286802)
@@ -69,7 +69,7 @@ struct dwc_otg_td {
        uint8_t tmr_val;
        uint8_t ep_no;
        uint8_t ep_type;
-       uint8_t channel;
+       uint8_t channel[3];
        uint8_t tt_index;               /* TT data */
        uint8_t tt_start_slot;          /* TT data */
        uint8_t tt_complete_slot;       /* TT data */
@@ -80,8 +80,7 @@ struct dwc_otg_td {
 #define        DWC_CHAN_ST_WAIT_S_ANE 2
 #define        DWC_CHAN_ST_WAIT_C_ANE 3
 #define        DWC_CHAN_ST_WAIT_C_PKT 4
-#define        DWC_CHAN_ST_TX_PKT_ISOC 5
-#define        DWC_CHAN_ST_TX_WAIT_ISOC 6
+#define        DWC_CHAN_ST_TX_WAIT_ISOC 5
        uint8_t error_any:1;
        uint8_t error_stall:1;
        uint8_t alt_next:1;
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to