PERFORCE change 159479 for review
Hans Petter Selasky
hselasky at FreeBSD.org
Thu Mar 19 14:02:09 PDT 2009
http://perforce.freebsd.org/chv.cgi?CH=159479
Change 159479 by hselasky at 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 {
More information about the p4-projects
mailing list