diff options
author | hselasky <hselasky@FreeBSD.org> | 2015-08-15 12:06:15 +0000 |
---|---|---|
committer | hselasky <hselasky@FreeBSD.org> | 2015-08-15 12:06:15 +0000 |
commit | 34d45c4b9965721297453cd785853d586412ddcd (patch) | |
tree | c218b91fa5a9d70d4b47c37b47eb082964722701 | |
parent | e9dde83ce271dd947011110c5f557c4b6cb74c48 (diff) | |
download | FreeBSD-src-34d45c4b9965721297453cd785853d586412ddcd.zip FreeBSD-src-34d45c4b9965721297453cd785853d586412ddcd.tar.gz |
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
-rw-r--r-- | sys/dev/usb/controller/dwc_otg.c | 447 | ||||
-rw-r--r-- | sys/dev/usb/controller/dwc_otg.h | 5 |
2 files changed, 236 insertions, 216 deletions
diff --git a/sys/dev/usb/controller/dwc_otg.c b/sys/dev/usb/controller/dwc_otg.c index 998eb51..e018ab5 100644 --- a/sys/dev/usb/controller/dwc_otg.c +++ b/sys/dev/usb/controller/dwc_otg.c @@ -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 *sc, uint8_t x) } 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_otg_softc *sc, 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_otg_softc *sc, /* 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_otg_softc *sc, /* 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_softc *sc, struct dwc_otg_td *td) /* 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_softc *sc, struct dwc_otg_td *td) 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_softc *sc, struct dwc_otg_td *td) 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_softc *sc, struct dwc_otg_td *td) 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_softc *sc, struct dwc_otg_td *td) /* 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_softc *sc, struct dwc_otg_td *td) } /* 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_softc *sc, struct dwc_otg_td *td) 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_softc *sc, struct dwc_otg_td *td) 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_softc *sc, struct dwc_otg_td *td) 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_softc *sc, struct dwc_otg_td *td) 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; + count = rem_bytes; } - } - - /* 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 */ - } - 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); + /* store number of bytes transmitted */ + td->tx_bytes += 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 last packet index */ + td->npkt = x; + + /* check for last packet */ + if (count == rem_bytes) + break; } - - /* store number of bytes transmitted */ - td->tx_bytes = count; 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_locked(struct dwc_otg_softc *sc) 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_locked(struct dwc_otg_softc *sc) 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_locked(struct dwc_otg_softc *sc) 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 dwc_otg_std_temp *temp) 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_xfer *xfer) 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_xfer *xfer) 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_xfer *xfer) } 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_xfer *xfer) 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_params *parm) /* 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, struct usb_endpoint_descriptor *edesc, 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) diff --git a/sys/dev/usb/controller/dwc_otg.h b/sys/dev/usb/controller/dwc_otg.h index 39c9529..f5e9887 100644 --- a/sys/dev/usb/controller/dwc_otg.h +++ b/sys/dev/usb/controller/dwc_otg.h @@ -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; |