summaryrefslogtreecommitdiff
path: root/sys/dev/usb/ehci.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb/ehci.c')
-rw-r--r--sys/dev/usb/ehci.c39
1 files changed, 37 insertions, 2 deletions
diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c
index 35b9d6c6365..50f889326ea 100644
--- a/sys/dev/usb/ehci.c
+++ b/sys/dev/usb/ehci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ehci.c,v 1.168 2014/09/01 08:13:02 mpi Exp $ */
+/* $OpenBSD: ehci.c,v 1.169 2014/10/05 08:40:29 mpi Exp $ */
/* $NetBSD: ehci.c,v 1.66 2004/06/30 03:11:56 mycroft Exp $ */
/*
@@ -99,6 +99,7 @@ struct ehci_pipe {
u_int8_t ehci_reverse_bits(u_int8_t, int);
usbd_status ehci_open(struct usbd_pipe *);
+int ehci_setaddr(struct usbd_device *, int);
void ehci_poll(struct usbd_bus *);
void ehci_softintr(void *);
int ehci_intr1(struct ehci_softc *);
@@ -215,7 +216,7 @@ void ehci_dump_exfer(struct ehci_xfer *);
struct usbd_bus_methods ehci_bus_methods = {
.open_pipe = ehci_open,
- .dev_setaddr = usbd_set_address,
+ .dev_setaddr = ehci_setaddr,
.soft_intr = ehci_softintr,
.do_poll = ehci_poll,
.allocx = ehci_allocx,
@@ -605,6 +606,40 @@ ehci_pcd(struct ehci_softc *sc, struct usbd_xfer *xfer)
usb_transfer_complete(xfer);
}
+/*
+ * Work around the half configured control (default) pipe when setting
+ * the address of a device.
+ *
+ * Because a single QH is setup per endpoint in ehci_open(), and the
+ * control pipe is configured before we could have set the address
+ * of the device or read the wMaxPacketSize of the endpoint, we have
+ * to re-open the pipe twice here.
+ */
+int
+ehci_setaddr(struct usbd_device *dev, int addr)
+{
+ /* Root Hub */
+ if (dev->depth == 0)
+ return (0);
+
+ /* Re-establish the default pipe with the new max packet size. */
+ ehci_close_pipe(dev->default_pipe);
+ if (ehci_open(dev->default_pipe))
+ return (EINVAL);
+
+ if (usbd_set_address(dev, addr))
+ return (1);
+
+ dev->address = addr;
+
+ /* Re-establish the default pipe with the new address. */
+ ehci_close_pipe(dev->default_pipe);
+ if (ehci_open(dev->default_pipe))
+ return (EINVAL);
+
+ return (0);
+}
+
void
ehci_softintr(void *v)
{