From 8c99b398138993b55eb5ecb7fd935e8315be1a12 Mon Sep 17 00:00:00 2001 From: Christopher Pascoe Date: Sun, 13 Mar 2005 02:54:05 +0000 Subject: Merge support for USB1 devices located beyond USB2 hubs, mostly from netbsd. Supports bulk, control and interrupt pipes, letting USB1 keyboards and most network interfaces work when connected to a USB2 hub. ok dlg@ --- sys/dev/usb/ehci.c | 24 ++++++++++++++++++++++-- sys/dev/usb/uhub.c | 45 ++++++++++++++++++++++++++++++++++++++------- sys/dev/usb/usb_subr.c | 33 +++++++++++++++++++++++++-------- sys/dev/usb/usbdivar.h | 9 +++++++-- 4 files changed, 92 insertions(+), 19 deletions(-) (limited to 'sys') diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c index 349ca7a8fee..0e7e86a552b 100644 --- a/sys/dev/usb/ehci.c +++ b/sys/dev/usb/ehci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ehci.c,v 1.44 2005/03/13 02:32:57 pascoe Exp $ */ +/* $OpenBSD: ehci.c,v 1.45 2005/03/13 02:54:04 pascoe Exp $ */ /* $NetBSD: ehci.c,v 1.66 2004/06/30 03:11:56 mycroft Exp $ */ /* @@ -1336,6 +1336,7 @@ ehci_open(usbd_pipe_handle pipe) usbd_status err; int s; int ival, speed, naks; + int hshubaddr, hshubport; DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", pipe, addr, ed->bEndpointAddress, sc->sc_addr)); @@ -1343,6 +1344,14 @@ ehci_open(usbd_pipe_handle pipe) if (sc->sc_dying) return (USBD_IOERROR); + if (dev->myhsport) { + hshubaddr = dev->myhsport->parent->address; + hshubport = dev->myhsport->portno; + } else { + hshubaddr = 0; + hshubport = 0; + } + epipe->nexttoggle = 0; if (addr == sc->sc_addr) { @@ -1366,6 +1375,15 @@ ehci_open(usbd_pipe_handle pipe) case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break; default: panic("ehci_open: bad device speed %d", dev->speed); } + if (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_ISOCHRONOUS) { + printf("%s: *** WARNING: opening low/full speed isochronous " + "device, this does not work yet.\n", + USBDEVNAME(sc->sc_bus.bdev)); + DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n", + hshubaddr, hshubport)); + return (USBD_INVAL); + } + naks = 8; /* XXX */ sqh = ehci_alloc_sqh(sc); if (sqh == NULL) @@ -1383,7 +1401,9 @@ ehci_open(usbd_pipe_handle pipe) ); sqh->qh.qh_endphub = htole32( EHCI_QH_SET_MULT(1) | - /* XXX TT stuff */ + EHCI_QH_SET_HUBA(hshubaddr) | + EHCI_QH_SET_PORT(hshubport) | + EHCI_QH_SET_CMASK(0x1c) | /* XXX */ EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x01 : 0) ); sqh->qh.qh_curqtd = EHCI_NULL; diff --git a/sys/dev/usb/uhub.c b/sys/dev/usb/uhub.c index 259eee8372d..09be18efd31 100644 --- a/sys/dev/usb/uhub.c +++ b/sys/dev/usb/uhub.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uhub.c,v 1.29 2004/12/12 05:21:14 dlg Exp $ */ +/* $OpenBSD: uhub.c,v 1.30 2005/03/13 02:54:04 pascoe Exp $ */ /* $NetBSD: uhub.c,v 1.64 2003/02/08 03:32:51 ichiro Exp $ */ /* $FreeBSD: src/sys/dev/usb/uhub.c,v 1.18 1999/11/17 22:33:43 n_hibma Exp $ */ @@ -81,6 +81,9 @@ struct uhub_softc { u_int8_t sc_status[1]; /* XXX more ports */ u_char sc_running; }; +#define UHUB_PROTO(sc) ((sc)->sc_hub->ddesc.bDeviceProtocol) +#define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB) +#define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT) Static usbd_status uhub_explore(usbd_device_handle hub); Static void uhub_intr(usbd_xfer_handle, usbd_private_handle,usbd_status); @@ -152,12 +155,13 @@ USB_ATTACH(uhub) usbd_device_handle dev = uaa->device; char devinfo[1024]; usbd_status err; - struct usbd_hub *hub; + struct usbd_hub *hub = NULL; usb_device_request_t req; usb_hub_descriptor_t hubdesc; int p, port, nports, nremov, pwrdly; usbd_interface_handle iface; usb_endpoint_descriptor_t *ed; + struct usbd_tt *tts = NULL; DPRINTFN(1,("uhub_attach\n")); sc->sc_hub = dev; @@ -165,6 +169,13 @@ USB_ATTACH(uhub) USB_ATTACH_SETUP; printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); + if (UHUB_IS_HIGH_SPEED(sc)) { + printf("%s: %s transaction translator%s\n", + USBDEVNAME(sc->sc_dev), + UHUB_IS_SINGLE_TT(sc) ? "single" : "multiple", + UHUB_IS_SINGLE_TT(sc) ? "" : "s"); + } + err = usbd_set_config_index(dev, 0, 1); if (err) { DPRINTF(("%s: configuration failed, error=%s\n", @@ -204,6 +215,11 @@ USB_ATTACH(uhub) USBDEVNAME(sc->sc_dev), nports, nports != 1 ? "s" : "", nremov, dev->self_powered ? "self" : "bus"); + if (nports == 0) { + printf("%s: no ports, hub ignored\n", USBDEVNAME(sc->sc_dev)); + goto bad; + } + hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port), M_USBDEV, M_NOWAIT); if (hub == NULL) @@ -281,10 +297,16 @@ USB_ATTACH(uhub) * proceed with device attachment */ + if (UHUB_IS_HIGH_SPEED(sc)) { + tts = malloc((UHUB_IS_SINGLE_TT(sc) ? 1 : nports) * + sizeof (struct usbd_tt), M_USBDEV, M_NOWAIT); + if (!tts) + goto bad; + } /* Set up data structures */ for (p = 0; p < nports; p++) { struct usbd_port *up = &hub->ports[p]; - up->device = 0; + up->device = NULL; up->parent = dev; up->portno = p+1; if (dev->self_powered) @@ -294,6 +316,12 @@ USB_ATTACH(uhub) up->power = USB_MIN_POWER; up->restartcnt = 0; up->reattach = 0; + if (UHUB_IS_HIGH_SPEED(sc)) { + up->tt = &tts[UHUB_IS_SINGLE_TT(sc) ? 0 : p]; + up->tt->hub = hub; + } else { + up->tt = NULL; + } } /* XXX should check for none, individual, or ganged power? */ @@ -319,8 +347,9 @@ USB_ATTACH(uhub) USB_ATTACH_SUCCESS_RETURN; bad: - free(hub, M_USBDEV); - dev->hub = 0; + if (hub) + free(hub, M_USBDEV); + dev->hub = NULL; USB_ATTACH_ERROR_RETURN; } @@ -346,8 +375,6 @@ uhub_explore(usbd_device_handle dev) for(port = 1; port <= hd->bNbrPorts; port++) { up = &dev->hub->ports[port-1]; - reconnect = up->reattach; - up->reattach = 0; err = usbd_get_port_status(dev, port, &up->status); if (err) { DPRINTF(("uhub_explore: get port status failed, " @@ -356,6 +383,8 @@ uhub_explore(usbd_device_handle dev) } status = UGETW(up->status.wPortStatus); change = UGETW(up->status.wPortChange); + 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)); if (change & UPS_C_PORT_ENABLED) { @@ -558,6 +587,8 @@ USB_DETACH(uhub) usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_hub, USBDEV(sc->sc_dev)); + if (hub->ports[0].tt) + free(hub->ports[0].tt, M_USBDEV); free(hub, M_USBDEV); sc->sc_hub->hub = NULL; diff --git a/sys/dev/usb/usb_subr.c b/sys/dev/usb/usb_subr.c index dd66a79118e..9023bf131c5 100644 --- a/sys/dev/usb/usb_subr.c +++ b/sys/dev/usb/usb_subr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: usb_subr.c,v 1.32 2004/12/12 06:13:15 dlg Exp $ */ +/* $OpenBSD: usb_subr.c,v 1.33 2005/03/13 02:54:04 pascoe Exp $ */ /* $NetBSD: usb_subr.c,v 1.103 2003/01/10 11:19:13 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usb_subr.c,v 1.18 1999/11/17 22:33:47 n_hibma Exp $ */ @@ -991,12 +991,13 @@ usbd_status usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, int speed, int port, struct usbd_port *up) { - usbd_device_handle dev; + usbd_device_handle dev, adev; struct usbd_device *hub; usb_device_descriptor_t *dd; usbd_status err; int addr; int i; + int p; DPRINTF(("usbd_new_device bus=%p port=%d depth=%d speed=%d\n", bus, port, depth, speed)); @@ -1031,11 +1032,27 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, dev->depth = depth; dev->powersrc = up; dev->myhub = up->parent; - for (hub = up->parent; + + up->device = dev; + + /* Locate port on upstream high speed hub */ + for (adev = dev, hub = up->parent; hub != NULL && hub->speed != USB_SPEED_HIGH; - hub = hub->myhub) + adev = hub, hub = hub->myhub) ; - dev->myhighhub = hub; + if (hub) { + for (p = 0; p < hub->hub->hubdesc.bNbrPorts; p++) { + if (hub->hub->ports[p].device == adev) { + dev->myhsport = &hub->hub->ports[p]; + goto found; + } + } + panic("usbd_new_device: cannot find HS port\n"); + found: + DPRINTFN(1,("usbd_new_device: high speed port %d\n", p)); + } else { + dev->myhsport = NULL; + } dev->speed = speed; dev->langid = USBD_NOLANG; dev->cookie.cookie = ++usb_cookie_no; @@ -1048,7 +1065,6 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth, return (err); } - up->device = dev; dd = &dev->ddesc; /* Try a few times in case the device is slow (i.e. outside specs.) */ for (i = 0; i < 5; i++) { @@ -1163,8 +1179,8 @@ usbd_remove_device(usbd_device_handle dev, struct usbd_port *up) if (dev->default_pipe != NULL) usbd_kill_pipe(dev->default_pipe); - up->device = 0; - dev->bus->devices[dev->address] = 0; + up->device = NULL; + dev->bus->devices[dev->address] = NULL; free(dev, M_USB); } @@ -1389,6 +1405,7 @@ usb_disconnect_port(struct usbd_port *up, device_ptr_t parent) printf(" port %d", up->portno); printf(" (addr %d) disconnected\n", dev->address); config_detach(dev->subdevs[i], DETACH_FORCE); + dev->subdevs[i] = 0; } } diff --git a/sys/dev/usb/usbdivar.h b/sys/dev/usb/usbdivar.h index ed0ca70b983..78136b3462d 100644 --- a/sys/dev/usb/usbdivar.h +++ b/sys/dev/usb/usbdivar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: usbdivar.h,v 1.22 2004/12/12 05:21:14 dlg Exp $ */ +/* $OpenBSD: usbdivar.h,v 1.23 2005/03/13 02:54:04 pascoe Exp $ */ /* $NetBSD: usbdivar.h,v 1.70 2002/07/11 21:14:36 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbdivar.h,v 1.11 1999/11/17 22:33:51 n_hibma Exp $ */ @@ -74,6 +74,10 @@ struct usbd_pipe_methods { void (*done)(usbd_xfer_handle xfer); }; +struct usbd_tt { + struct usbd_hub *hub; +}; + struct usbd_port { usb_port_status_t status; u_int16_t power; /* mA of current on port */ @@ -83,6 +87,7 @@ struct usbd_port { u_int8_t reattach; struct usbd_device *device; /* Connected device */ struct usbd_device *parent; /* The ports hub */ + struct usbd_tt *tt; /* Transaction translator (if any) */ }; struct usbd_hub { @@ -145,7 +150,7 @@ struct usbd_device { usb_event_cookie_t cookie; /* unique connection id */ struct usbd_port *powersrc; /* upstream hub port, or 0 */ struct usbd_device *myhub; /* upstream hub */ - struct usbd_device *myhighhub; /* closest high speed hub */ + struct usbd_port *myhsport; /* closest high speed port */ struct usbd_endpoint def_ep; /* for pipe 0 */ usb_endpoint_descriptor_t def_ep_desc; /* for pipe 0 */ struct usbd_interface *ifaces; /* array of all interfaces */ -- cgit v1.2.3