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
This commit is contained in:
parent
e9dde83ce2
commit
34d45c4b99
@ -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 @@ dwc_otg_host_setup_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
|
||||
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 @@ dwc_otg_host_setup_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
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 @@ dwc_otg_host_rate_check(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
}
|
||||
|
||||
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 @@ dwc_otg_host_data_rx_sub(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
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 @@ dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
}
|
||||
/* 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 @@ dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
goto busy;
|
||||
}
|
||||
|
||||
channel = td->channel;
|
||||
|
||||
/* set toggle, if any */
|
||||
if (td->set_toggle) {
|
||||
td->set_toggle = 0;
|
||||
@ -1389,28 +1425,31 @@ dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
|
||||
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 @@ dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
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 @@ dwc_otg_host_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
|
||||
/* 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 @@ dwc_otg_host_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
goto busy;
|
||||
}
|
||||
|
||||
channel = td->channel;
|
||||
|
||||
/* set toggle, if any */
|
||||
if (td->set_toggle) {
|
||||
td->set_toggle = 0;
|
||||
@ -1790,8 +1809,7 @@ dwc_otg_host_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
}
|
||||
|
||||
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 @@ dwc_otg_host_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
/* 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);
|
||||
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* copy out data */
|
||||
usbd_copy_out(td->pc, td->offset + td->tx_bytes,
|
||||
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 last packet index */
|
||||
td->npkt = x;
|
||||
|
||||
/* check for last packet */
|
||||
if (count == rem_bytes)
|
||||
break;
|
||||
}
|
||||
|
||||
DWC_OTG_WRITE_4(sc, DOTG_HCSPLT(channel), td->hcsplt);
|
||||
|
||||
hcchar = td->hcchar;
|
||||
hcchar &= ~HCCHAR_EPDIR_IN;
|
||||
|
||||
/* 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);
|
||||
|
||||
if (count != 0) {
|
||||
|
||||
/* clear topmost word before copy */
|
||||
sc->sc_tx_bounce_buffer[(count - 1) / 4] = 0;
|
||||
|
||||
/* 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;
|
||||
goto busy;
|
||||
|
||||
send_cpkt:
|
||||
@ -1947,7 +1952,7 @@ dwc_otg_host_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
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 @@ dwc_otg_host_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
|
||||
/* 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)
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user