summaryrefslogtreecommitdiff
path: root/sys/dev/usb
diff options
context:
space:
mode:
authorChristopher Pascoe <pascoe@cvs.openbsd.org>2005-03-13 02:54:05 +0000
committerChristopher Pascoe <pascoe@cvs.openbsd.org>2005-03-13 02:54:05 +0000
commit8c99b398138993b55eb5ecb7fd935e8315be1a12 (patch)
treeb89bccbb6b5267d4fed6dc98657bda69c14377a2 /sys/dev/usb
parent7d158c4e4058c60a5af4a4a03f0054b0381d62b4 (diff)
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@
Diffstat (limited to 'sys/dev/usb')
-rw-r--r--sys/dev/usb/ehci.c24
-rw-r--r--sys/dev/usb/uhub.c45
-rw-r--r--sys/dev/usb/usb_subr.c33
-rw-r--r--sys/dev/usb/usbdivar.h9
4 files changed, 92 insertions, 19 deletions
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 */