Date: Thu, 19 Mar 2009 21:02:05 GMT From: Hans Petter Selasky <hselasky@FreeBSD.org> To: Perforce Change Reviews <perforce@FreeBSD.org> Subject: PERFORCE change 159479 for review Message-ID: <200903192102.n2JL25iE035643@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=159479 Change 159479 by hselasky@hselasky_laptop001 on 2009/03/19 21:01:59 USB controller + USB core - Workaround for buggy USB hardware not handling new SETUP packet before STATUS stage is complete! Reported by: Andrew Thompson Affected files ... .. //depot/projects/usb/src/sys/dev/usb/controller/ehci.c#8 edit .. //depot/projects/usb/src/sys/dev/usb/controller/ohci.c#5 edit .. //depot/projects/usb/src/sys/dev/usb/controller/uhci.c#4 edit .. //depot/projects/usb/src/sys/dev/usb/usb_transfer.c#132 edit Differences ... ==== //depot/projects/usb/src/sys/dev/usb/controller/ehci.c#8 (text+ko) ==== @@ -117,7 +117,7 @@ uint8_t shortpkt; uint8_t auto_data_toggle; uint8_t setup_alt_next; - uint8_t short_frames_ok; + uint8_t last_frame; }; void @@ -1546,10 +1546,12 @@ uint32_t buf_offset; uint32_t average; uint32_t len_old; + uint32_t terminate; uint8_t shortpkt_old; uint8_t precompute; - qtd_altnext = htohc32(temp->sc, EHCI_LINK_TERMINATE); + terminate = htohc32(temp->sc, EHCI_LINK_TERMINATE); + qtd_altnext = terminate; td_alt_next = NULL; buf_offset = 0; shortpkt_old = temp->shortpkt; @@ -1696,14 +1698,17 @@ precompute = 0; /* setup alt next pointer, if any */ - if (temp->short_frames_ok) { + if (temp->last_frame) { + td_alt_next = NULL; + qtd_altnext = terminate; + } else { + /* we use this field internally */ + td_alt_next = td_next; if (temp->setup_alt_next) { - td_alt_next = td_next; qtd_altnext = td_next->qtd_self; + } else { + qtd_altnext = terminate; } - } else { - /* we use this field internally */ - td_alt_next = td_next; } /* restore */ @@ -1746,8 +1751,7 @@ temp.td = NULL; temp.td_next = td; temp.qtd_status = 0; - temp.setup_alt_next = xfer->flags_int.short_frames_ok; - temp.short_frames_ok = xfer->flags_int.short_frames_ok; + temp.last_frame = 0; if (xfer->flags_int.control_xfr) { if (xfer->pipe->toggle_next) { @@ -1780,7 +1784,14 @@ temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.shortpkt = temp.len ? 1 : 0; - + /* no "alt_next" for SETUP stage */ + temp.setup_alt_next = 0; + /* check for last frame */ + if (xfer->nframes == 1) { + /* no STATUS stage yet, SETUP is last */ + if (xfer->flags_int.control_act) + temp.last_frame = 1; + } ehci_setup_standard_chain_sub(&temp); } x = 1; @@ -1788,6 +1799,8 @@ x = 0; } + temp.setup_alt_next = xfer->flags_int.short_frames_ok; + while (x != xfer->nframes) { /* DATA0 / DATA1 message */ @@ -1798,7 +1811,16 @@ x++; if (x == xfer->nframes) { - temp.setup_alt_next = 0; + if (xfer->flags_int.control_xfr) { + /* no STATUS stage yet, DATA is last */ + if (xfer->flags_int.control_act) { + temp.last_frame = 1; + temp.setup_alt_next = 0; + } + } else { + temp.last_frame = 1; + temp.setup_alt_next = 0; + } } /* keep previous data toggle and error count */ @@ -1855,6 +1877,8 @@ temp.len = 0; temp.pc = NULL; temp.shortpkt = 0; + temp.last_frame = 1; + temp.setup_alt_next = 0; ehci_setup_standard_chain_sub(&temp); } ==== //depot/projects/usb/src/sys/dev/usb/controller/ohci.c#5 (text+ko) ==== @@ -115,7 +115,7 @@ uint16_t max_frame_size; uint8_t shortpkt; uint8_t setup_alt_next; - uint8_t short_frames_ok; + uint8_t last_frame; }; static struct ohci_hcca * @@ -1379,10 +1379,9 @@ precompute = 0; /* setup alt next pointer, if any */ - if (temp->short_frames_ok) { - if (temp->setup_alt_next) { - td_alt_next = td_next; - } + if (temp->last_frame) { + /* no alternate next */ + td_alt_next = NULL; } else { /* we use this field internally */ td_alt_next = td_next; @@ -1425,8 +1424,7 @@ temp.td = NULL; temp.td_next = td; - temp.setup_alt_next = xfer->flags_int.short_frames_ok; - temp.short_frames_ok = xfer->flags_int.short_frames_ok; + temp.last_frame = 0; methods = xfer->pipe->methods; @@ -1441,7 +1439,14 @@ temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.shortpkt = temp.len ? 1 : 0; - + /* no "alt_next" for SETUP stage */ + temp.setup_alt_next = 0; + /* check for last frame */ + if (xfer->nframes == 1) { + /* no STATUS stage yet, SETUP is last */ + if (xfer->flags_int.control_act) + temp.last_frame = 1; + } ohci_setup_standard_chain_sub(&temp); /* @@ -1455,6 +1460,7 @@ x = 0; } temp.td_flags = htole32(OHCI_TD_NOCC | OHCI_TD_NOINTR); + temp.setup_alt_next = xfer->flags_int.short_frames_ok; /* set data toggle */ @@ -1482,7 +1488,16 @@ x++; if (x == xfer->nframes) { - temp.setup_alt_next = 0; + if (xfer->flags_int.control_xfr) { + /* no STATUS stage yet, DATA is last */ + if (xfer->flags_int.control_act) { + temp.last_frame = 1; + temp.setup_alt_next = 0; + } + } else { + temp.last_frame = 1; + temp.setup_alt_next = 0; + } } if (temp.len == 0) { @@ -1523,11 +1538,14 @@ temp.len = 0; temp.pc = NULL; temp.shortpkt = 0; + temp.last_frame = 1; + temp.setup_alt_next = 0; ohci_setup_standard_chain_sub(&temp); } td = temp.td; + /* Ensure that last TD is terminating: */ td->td_next = htole32(OHCI_TD_NEXT_END); td->td_flags &= ~htole32(OHCI_TD_INTR_MASK); td->td_flags |= htole32(OHCI_TD_SET_DI(1)); ==== //depot/projects/usb/src/sys/dev/usb/controller/uhci.c#4 (text+ko) ==== @@ -124,7 +124,7 @@ uint16_t max_frame_size; uint8_t shortpkt; uint8_t setup_alt_next; - uint8_t short_frames_ok; + uint8_t last_frame; }; extern struct usb2_bus_methods uhci_bus_methods; @@ -1253,8 +1253,12 @@ td_self = td->td_self; td_alt_next = td->alt_next; + if ((xfer->flags_int.control_xfr) && + (!xfer->flags_int.control_act) && + (((void *)td) == xfer->td_transfer_last)) + goto skip; /* don't touch DT value on STATUS stage */ + if ((td->td_token ^ td_token) & htole32(UHCI_TD_SET_DT(1))) { - /* * The data toggle is wrong and * we need to switch it ! @@ -1277,6 +1281,8 @@ } } } +skip: + /* update the QH */ qh->qh_e_next = td_self; usb2_pc_cpu_flush(qh->page_cache); @@ -1631,10 +1637,8 @@ precompute = 0; /* setup alt next pointer, if any */ - if (temp->short_frames_ok) { - if (temp->setup_alt_next) { - td_alt_next = td_next; - } + if (temp->last_frame) { + td_alt_next = NULL; } else { /* we use this field internally */ td_alt_next = td_next; @@ -1673,8 +1677,7 @@ temp.td = NULL; temp.td_next = td; - temp.setup_alt_next = xfer->flags_int.short_frames_ok; - temp.short_frames_ok = xfer->flags_int.short_frames_ok; + temp.last_frame = 0; uhci_mem_layout_init(&temp.ml, xfer); @@ -1707,7 +1710,14 @@ temp.len = xfer->frlengths[0]; temp.ml.buf_pc = xfer->frbuffers + 0; temp.shortpkt = temp.len ? 1 : 0; - + /* no "alt_next" for SETUP stage */ + temp.setup_alt_next = 0; + /* check for last frame */ + if (xfer->nframes == 1) { + /* no STATUS stage yet, SETUP is last */ + if (xfer->flags_int.control_act) + temp.last_frame = 1; + } uhci_setup_standard_chain_sub(&temp); } x = 1; @@ -1715,6 +1725,8 @@ x = 0; } + temp.setup_alt_next = xfer->flags_int.short_frames_ok; + while (x != xfer->nframes) { /* DATA0 / DATA1 message */ @@ -1725,7 +1737,16 @@ x++; if (x == xfer->nframes) { - temp.setup_alt_next = 0; + if (xfer->flags_int.control_xfr) { + /* no STATUS stage yet, DATA is last */ + if (xfer->flags_int.control_act) { + temp.last_frame = 1; + temp.setup_alt_next = 0; + } + } else { + temp.last_frame = 1; + temp.setup_alt_next = 0; + } } /* * Keep previous data toggle, @@ -1780,11 +1801,14 @@ temp.len = 0; temp.ml.buf_pc = NULL; temp.shortpkt = 0; + temp.last_frame = 1; + temp.setup_alt_next = 0; uhci_setup_standard_chain_sub(&temp); } td = temp.td; + /* Ensure that last TD is terminating: */ td->td_next = htole32(UHCI_PTR_T); /* set interrupt bit */ ==== //depot/projects/usb/src/sys/dev/usb/usb_transfer.c#132 (text+ko) ==== @@ -1504,6 +1504,15 @@ if (xfer->flags.short_xfer_ok) { xfer->flags_int.short_xfer_ok = 1; + /* + * Due to sometimes buggy device side + * firmware we need to do a STATUS + * stage in case of short control + * transfers in USB host mode, via + * the "alt_next" feature! + */ + if (udev->flags.usb2_mode == USB_MODE_HOST) + xfer->flags_int.short_frames_ok = 1; } } else {
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200903192102.n2JL25iE035643>