summaryrefslogtreecommitdiff
path: root/sys/dev/usb/usbdi.c
diff options
context:
space:
mode:
authorMartin Pieuchot <mpi@cvs.openbsd.org>2017-03-10 09:14:07 +0000
committerMartin Pieuchot <mpi@cvs.openbsd.org>2017-03-10 09:14:07 +0000
commit8be19390d68917bbad04b8329790eb50f5281208 (patch)
tree7ed75ef852ff5695404c131ac118f55e2b269025 /sys/dev/usb/usbdi.c
parent89f5b2fc7363899bc4ee99e1fb9675e5531dec00 (diff)
Move per HC polling code to the stack.
This code contains a use-after-free which be addressed in an upcoming diff. This fix xhci(4) polling mode. ok kettenis@
Diffstat (limited to 'sys/dev/usb/usbdi.c')
-rw-r--r--sys/dev/usb/usbdi.c53
1 files changed, 36 insertions, 17 deletions
diff --git a/sys/dev/usb/usbdi.c b/sys/dev/usb/usbdi.c
index af8f0a57cc8..6f3c6dd1c72 100644
--- a/sys/dev/usb/usbdi.c
+++ b/sys/dev/usb/usbdi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: usbdi.c,v 1.87 2017/03/06 12:13:58 mpi Exp $ */
+/* $OpenBSD: usbdi.c,v 1.88 2017/03/10 09:14:06 mpi Exp $ */
/* $NetBSD: usbdi.c,v 1.103 2002/09/27 15:37:38 provos Exp $ */
/* $FreeBSD: src/sys/dev/usb/usbdi.c,v 1.28 1999/11/17 22:33:49 n_hibma Exp $ */
@@ -279,6 +279,8 @@ usbd_status
usbd_transfer(struct usbd_xfer *xfer)
{
struct usbd_pipe *pipe = xfer->pipe;
+ struct usbd_bus *bus = pipe->device->bus;
+ int polling = bus->use_polling;
usbd_status err;
int flags, s;
@@ -298,8 +300,6 @@ usbd_transfer(struct usbd_xfer *xfer)
/* If there is no buffer, allocate one. */
if ((xfer->rqflags & URQ_DEV_DMABUF) == 0) {
- struct usbd_bus *bus = pipe->device->bus;
-
#ifdef DIAGNOSTIC
if (xfer->rqflags & URQ_AUTO_DMABUF)
printf("usbd_transfer: has old buffer!\n");
@@ -325,8 +325,6 @@ usbd_transfer(struct usbd_xfer *xfer)
if (err != USBD_IN_PROGRESS && err) {
/* The transfer has not been queued, so free buffer. */
if (xfer->rqflags & URQ_AUTO_DMABUF) {
- struct usbd_bus *bus = pipe->device->bus;
-
usb_freemem(bus, &xfer->dmabuf);
xfer->rqflags &= ~URQ_AUTO_DMABUF;
}
@@ -338,19 +336,40 @@ usbd_transfer(struct usbd_xfer *xfer)
/* Sync transfer, wait for completion. */
if (err != USBD_IN_PROGRESS)
return (err);
+
s = splusb();
- while (!xfer->done) {
- if (pipe->device->bus->use_polling)
- panic("usbd_transfer: not done");
- flags = PRIBIO | (xfer->flags & USBD_CATCH ? PCATCH : 0);
-
- err = tsleep(xfer, flags, "usbsyn", 0);
- if (err && !xfer->done) {
- usbd_abort_pipe(pipe);
- if (err == EINTR)
- xfer->status = USBD_INTERRUPTED;
- else
- xfer->status = USBD_TIMEOUT;
+ if (polling) {
+ int timo;
+
+ for (timo = xfer->timeout; timo >= 0; timo--) {
+ usb_delay_ms(bus, 1);
+ if (bus->dying) {
+ xfer->status = USBD_IOERROR;
+ usb_transfer_complete(xfer);
+ break;
+ }
+
+ usbd_dopoll(pipe->device);
+ if (xfer->done)
+ break;
+ }
+
+ if (timo < 0) {
+ xfer->status = USBD_TIMEOUT;
+ usb_transfer_complete(xfer);
+ }
+ } else {
+ while (!xfer->done) {
+ flags = PRIBIO|(xfer->flags & USBD_CATCH ? PCATCH : 0);
+
+ err = tsleep(xfer, flags, "usbsyn", 0);
+ if (err && !xfer->done) {
+ usbd_abort_pipe(pipe);
+ if (err == EINTR)
+ xfer->status = USBD_INTERRUPTED;
+ else
+ xfer->status = USBD_TIMEOUT;
+ }
}
}
splx(s);