summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/uhci.c
diff options
context:
space:
mode:
authorwpaul <wpaul@FreeBSD.org>1999-12-23 05:18:58 +0000
committerwpaul <wpaul@FreeBSD.org>1999-12-23 05:18:58 +0000
commit781c6f40860cf724d53d0234b157efb59c7f1b6c (patch)
tree8259a012eea996837f23f9ddcd17c6374f05203e /sys/dev/usb/uhci.c
parentf206b46b8d53659c83d60834877077922d1f5602 (diff)
downloadFreeBSD-src-781c6f40860cf724d53d0234b157efb59c7f1b6c.zip
FreeBSD-src-781c6f40860cf724d53d0234b157efb59c7f1b6c.tar.gz
Fix one bug and make one minor enhancement:
- In uhci_intr() check to see if sc->sc_bus.bdev is NULL, and if it is, ack any pending interrupts and disable them, then return. It is possible for interrupts to be delivered the moment a handler is set up at attach time in uhci_pci.c, particularly when attempting to kldload the usb.ko module after the system is already up. However the driver isn't ready to field interrupts at that time and certain pointers in the softc struct aren't initialized yet, and we invariably end up falling off the end of one of them. The effect is that kldloading the usb module will panic the system in uhci_intr(). This added sanity check stops this from happening: I can now kldload the usb.ko module without any problems and load/attach other USB drivers after it. Of course the uhci driver has no detach method, but that's another problem. - In uhci_run(), set the UHCI_CMD_MAXP bit in the command register to allow 64-byte packets to be used for full speed bandwidth reclamation. Certain high speed devices (in this case the ADMtek USB ethernet adapter) require this bit to be set, otherwise babble errors occur at the end of large (between 1100 and 1500 byte) transfers. This should not affect other devices, although supposedly it is less efficient than the 32-byte setting. Unfortunately, this is a per-bus setting, not a per-device setting, so we can't just enable it for certain devices on the USB bus.
Diffstat (limited to 'sys/dev/usb/uhci.c')
-rw-r--r--sys/dev/usb/uhci.c24
1 files changed, 23 insertions, 1 deletions
diff --git a/sys/dev/usb/uhci.c b/sys/dev/usb/uhci.c
index 3e5be72..9601da2 100644
--- a/sys/dev/usb/uhci.c
+++ b/sys/dev/usb/uhci.c
@@ -886,6 +886,22 @@ uhci_intr(arg)
int ack;
uhci_intr_info_t *ii;
+ /*
+ * It can happen that an interrupt will be delivered to
+ * us before the device has been fully attached and the
+ * softc struct has been configured. Usually this happens
+ * when kldloading the USB support as a module after the
+ * system has been booted. If we detect this condition,
+ * we need to squelch the unwanted interrupts until we're
+ * ready for them.
+ */
+ if (sc->sc_bus.bdev == NULL) {
+ UWRITE2(sc, UHCI_STS, 0xFFFF); /* ack pending interrupts */
+ uhci_run(sc, 0); /* stop the controller */
+ UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */
+ return(0);
+ }
+
#ifdef UHCI_DEBUG
if (uhcidebug > 15) {
DPRINTF(("%s: uhci_intr\n", USBDEVNAME(sc->sc_bus.bdev)));
@@ -1210,7 +1226,13 @@ uhci_run(sc, run)
run = run != 0;
s = splusb();
DPRINTF(("uhci_run: setting run=%d\n", run));
- UHCICMD(sc, run ? UHCI_CMD_RS : 0);
+ /*
+ * When activating the controller, set the MAXP bit.
+ * Certain high speed devices such as network adapters
+ * require this in order to avoid babble errors that
+ * can cause an endpoint stall.
+ */
+ UHCICMD(sc, run ? UHCI_CMD_RS|UHCI_CMD_MAXP : 0);
for(n = 0; n < 10; n++) {
running = !(UREAD2(sc, UHCI_STS) & UHCI_STS_HCH);
/* return when we've entered the state we want */
OpenPOWER on IntegriCloud