Keep track of the per-port status change notifications coming in

through the interrupt pipe; during exploration check only the ports
where we got such a notification. This speeds up things.
(I believe we should go a step further and use a thread per hub
instead of per bus. If power management gets implemented, we should
be able to react quickly on a resume event.)
Try to simplify the logics in the explore function a bit. (The reattach
thing was hacked in badly, not sure whether I broke it. Only used
by if_atu.)
Clean up some dead code.
This commit is contained in:
drochner 2007-03-15 15:29:09 +00:00
parent dc9d1127cc
commit 82d96e1fac

View File

@ -1,4 +1,4 @@
/* $NetBSD: uhub.c,v 1.86 2007/03/13 13:51:55 drochner Exp $ */
/* $NetBSD: uhub.c,v 1.87 2007/03/15 15:29:09 drochner Exp $ */
/* $FreeBSD: src/sys/dev/usb/uhub.c,v 1.18 1999/11/17 22:33:43 n_hibma Exp $ */
/*
@ -43,7 +43,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uhub.c,v 1.86 2007/03/13 13:51:55 drochner Exp $");
__KERNEL_RCSID(0, "$NetBSD: uhub.c,v 1.87 2007/03/15 15:29:09 drochner Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -79,19 +79,25 @@ struct uhub_softc {
usbd_device_handle sc_hub; /* USB device */
int sc_proto; /* device protocol */
usbd_pipe_handle sc_ipipe; /* interrupt pipe */
/* XXX second buffer needed because we can't suspend pipes yet */
u_int8_t *sc_statusbuf;
u_int8_t *sc_status;
size_t sc_statuslen;
int sc_explorepending;
u_char sc_running;
};
#define UHUB_IS_HIGH_SPEED(sc) ((sc)->sc_proto != UDPROTO_FSHUB)
#define UHUB_IS_SINGLE_TT(sc) ((sc)->sc_proto == UDPROTO_HSHUBSTT)
#define PORTSTAT_ISSET(sc, port) \
((sc)->sc_status[(port) / 8] & (1 << ((port) % 8)))
Static usbd_status uhub_explore(usbd_device_handle hub);
Static void uhub_intr(usbd_xfer_handle, usbd_private_handle,usbd_status);
#if defined(__FreeBSD__)
Static bus_child_detached_t uhub_child_detached;
#endif
/*
* We need two attachment points:
@ -152,7 +158,6 @@ USB_ATTACH(uhub)
#if 0 /* notyet */
struct usbd_tt *tts = NULL;
#endif
size_t statuslen;
DPRINTFN(1,("uhub_attach\n"));
sc->sc_hub = dev;
@ -247,14 +252,21 @@ USB_ATTACH(uhub)
goto bad;
}
statuslen = (nports + 1 + 7) / 8;
sc->sc_status = malloc(statuslen, M_USBDEV, M_NOWAIT);
sc->sc_statuslen = (nports + 1 + 7) / 8;
sc->sc_statusbuf = malloc(sc->sc_statuslen, M_USBDEV, M_NOWAIT);
if (!sc->sc_statusbuf)
goto bad;
sc->sc_status = malloc(sc->sc_statuslen, M_USBDEV, M_NOWAIT);
if (!sc->sc_status)
goto bad;
/* force initial scan */
memset(sc->sc_status, 0xff, sc->sc_statuslen);
sc->sc_explorepending = 1;
err = usbd_open_pipe_intr(iface, ed->bEndpointAddress,
USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_status,
statuslen, uhub_intr, USBD_DEFAULT_INTERVAL);
USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_statusbuf,
sc->sc_statuslen, uhub_intr, USBD_DEFAULT_INTERVAL);
if (err) {
printf("%s: cannot open interrupt pipe\n",
USBDEVNAME(sc->sc_dev));
@ -375,18 +387,31 @@ uhub_explore(usbd_device_handle dev)
for (port = 1; port <= hd->bNbrPorts; port++) {
up = &dev->hub->ports[port-1];
err = usbd_get_port_status(dev, port, &up->status);
if (err) {
DPRINTF(("uhub_explore: get port status failed, "
"error=%s\n", usbd_errstr(err)));
continue;
}
status = UGETW(up->status.wPortStatus);
change = UGETW(up->status.wPortChange);
/* reattach is needed after firmware upload */
reconnect = up->reattach;
up->reattach = 0;
DPRINTFN(3,("uhub_explore: %s port %d status 0x%04x 0x%04x\n",
USBDEVNAME(sc->sc_dev), port, status, change));
status = change = 0;
/* don't check if no change summary notification */
if (PORTSTAT_ISSET(sc, port) || reconnect) {
err = usbd_get_port_status(dev, port, &up->status);
if (err) {
DPRINTF(("uhub_explore: get port stat failed, "
"error=%s\n", usbd_errstr(err)));
continue;
}
status = UGETW(up->status.wPortStatus);
change = UGETW(up->status.wPortChange);
}
if (!change && !reconnect) {
/* No status change, just do recursive explore. */
if (up->device != NULL && up->device->hub != NULL)
up->device->hub->explore(up->device);
continue;
}
if (change & UPS_C_PORT_ENABLED) {
DPRINTF(("uhub_explore: C_PORT_ENABLED\n"));
usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
@ -411,27 +436,17 @@ uhub_explore(usbd_device_handle dev)
USBDEVNAME(sc->sc_dev), port);
}
}
if (!reconnect && !(change & UPS_C_CONNECT_STATUS)) {
DPRINTFN(3,("uhub_explore: port=%d !C_CONNECT_"
"STATUS\n", port));
/* No status change, just do recursive explore. */
if (up->device != NULL && up->device->hub != NULL)
up->device->hub->explore(up->device);
#if 0 && defined(DIAGNOSTIC)
if (up->device == NULL &&
(status & UPS_CURRENT_CONNECT_STATUS))
printf("%s: connected, no device\n",
USBDEVNAME(sc->sc_dev));
#endif
/* XXX handle overcurrent and resume events! */
if (!(change & UPS_C_CONNECT_STATUS))
continue;
}
/* We have a connect status change, handle it. */
DPRINTF(("uhub_explore: status change hub=%d port=%d\n",
dev->address, port));
usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION);
/*usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);*/
/*
* If there is already a device on the port the change status
* must mean that is has disconnected. Looking at the
@ -488,15 +503,6 @@ uhub_explore(usbd_device_handle dev)
continue;
}
#if 0
if (UHUB_IS_HIGH_SPEED(sc) && !(status & UPS_HIGH_SPEED)) {
printf("%s: port %d, transaction translation not "
"implemented, low/full speed device ignored\n",
USBDEVNAME(sc->sc_dev), port);
continue;
}
#endif
/* Figure out device speed */
if (status & UPS_HIGH_SPEED)
speed = USB_SPEED_HIGH;
@ -530,6 +536,8 @@ uhub_explore(usbd_device_handle dev)
up->device->hub->explore(up->device);
}
}
/* enable status change notifications again */
sc->sc_explorepending = 0;
return (USBD_NORMAL_COMPLETION);
}
@ -652,15 +660,20 @@ uhub_intr(usbd_xfer_handle xfer, usbd_private_handle addr,
{
struct uhub_softc *sc = addr;
#if 0
void *buf; int cnt;
usbd_get_xfer_status(xfer, NULL, &buf, &cnt, NULL);
#endif
DPRINTFN(5,("uhub_intr: sc=%p\n", sc));
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sc->sc_ipipe);
else if (status == USBD_NORMAL_COMPLETION)
else if (status == USBD_NORMAL_COMPLETION &&
!sc->sc_explorepending) {
/*
* Make sure the status is not overwritten in between.
* XXX we should suspend the pipe instead
*/
memcpy(sc->sc_status, sc->sc_statusbuf, sc->sc_statuslen);
sc->sc_explorepending = 1;
usb_needs_explore(sc->sc_hub);
}
}
#if defined(__FreeBSD__)