diff options
-rw-r--r-- | sys/dev/usb/controller/ehci.c | 28 |
1 files changed, 25 insertions, 3 deletions
diff --git a/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c index e631e55..812e422 100644 --- a/sys/dev/usb/controller/ehci.c +++ b/sys/dev/usb/controller/ehci.c @@ -1180,6 +1180,26 @@ _ehci_remove_qh(ehci_qh_t *sqh, ehci_qh_t *last) return (last); } +static void +ehci_data_toggle_update(struct usb_xfer *xfer, uint16_t actlen, uint16_t xlen) +{ + uint8_t full = (actlen == xlen); + uint8_t dt; + + /* count number of full packets */ + dt = (actlen / xfer->max_packet_size) & 1; + + /* cumpute remainder */ + actlen = actlen % xfer->max_packet_size; + + if (actlen > 0) + dt ^= 1; /* short packet at the end */ + else if (!full) + dt ^= 1; /* zero length packet at the end */ + + xfer->endpoint->toggle_next ^= dt; +} + static usb_error_t ehci_non_isoc_done_sub(struct usb_xfer *xfer) { @@ -1213,7 +1233,10 @@ ehci_non_isoc_done_sub(struct usb_xfer *xfer) status |= EHCI_QTD_HALTED; } else if (xfer->aframes != xfer->nframes) { xfer->frlengths[xfer->aframes] += td->len - len; + /* manually update data toggle */ + ehci_data_toggle_update(xfer, td->len - len, td->len); } + /* Check for last transfer */ if (((void *)td) == xfer->td_transfer_last) { td = NULL; @@ -1295,9 +1318,6 @@ ehci_non_isoc_done(struct usb_xfer *xfer) status = hc32toh(sc, qh->qh_qtd.qtd_status); - xfer->endpoint->toggle_next = - (status & EHCI_QTD_TOGGLE_MASK) ? 1 : 0; - /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; @@ -1876,6 +1896,8 @@ ehci_setup_standard_chain(struct usb_xfer *xfer, ehci_qh_t **qh_last) if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { + xfer->endpoint->toggle_next = 0; + temp.qtd_status &= htohc32(temp.sc, EHCI_QTD_SET_CERR(3)); temp.qtd_status |= htohc32(temp.sc, |