summaryrefslogtreecommitdiff
path: root/sys/dev/usb
diff options
context:
space:
mode:
authorChristopher Pascoe <pascoe@cvs.openbsd.org>2005-04-01 06:41:14 +0000
committerChristopher Pascoe <pascoe@cvs.openbsd.org>2005-04-01 06:41:14 +0000
commit83ebff49ddd3ca98388d1827346db6a9d68e7cfd (patch)
tree324caa3bdd02b372f982a1bd69eb7c755008d9ce /sys/dev/usb
parent2852c2f1b306fc98934727b737e68d18e78287f2 (diff)
Add support for polled umass/SCSI transactions. Should eliminate the panics
seen at reboot time after writing to umass devices. ok dlg@, testing markus@
Diffstat (limited to 'sys/dev/usb')
-rw-r--r--sys/dev/usb/umass.c118
-rw-r--r--sys/dev/usb/umass_scsi.c41
-rw-r--r--sys/dev/usb/umassvar.h7
3 files changed, 134 insertions, 32 deletions
diff --git a/sys/dev/usb/umass.c b/sys/dev/usb/umass.c
index aff2f9c3ece..ab2a7ea7c67 100644
--- a/sys/dev/usb/umass.c
+++ b/sys/dev/usb/umass.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: umass.c,v 1.38 2005/03/28 04:46:33 pascoe Exp $ */
+/* $OpenBSD: umass.c,v 1.39 2005/04/01 06:41:13 pascoe Exp $ */
/* $NetBSD: umass.c,v 1.116 2004/06/30 05:53:46 mycroft Exp $ */
/*
@@ -155,10 +155,12 @@
#include <sys/bus.h>
#include <machine/clock.h>
#endif
+#include <machine/bus.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
#include <dev/usb/usbdevs.h>
#include <dev/usb/umassvar.h>
@@ -198,6 +200,8 @@ USB_DECLARE_DRIVER(umass);
Static void umass_disco(struct umass_softc *sc);
/* generic transfer functions */
+Static usbd_status umass_polled_transfer(struct umass_softc *sc,
+ usbd_xfer_handle xfer);
Static usbd_status umass_setup_transfer(struct umass_softc *sc,
usbd_pipe_handle pipe,
void *buffer, int buflen, int flags,
@@ -743,6 +747,65 @@ umass_disco(struct umass_softc *sc)
*/
Static usbd_status
+umass_polled_transfer(struct umass_softc *sc, usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ if (sc->sc_dying)
+ return (USBD_IOERROR);
+
+ /*
+ * If a polled transfer is already in progress, preserve the new
+ * usbd_xfer_handle and run it after the running one completes.
+ * This converts the recursive calls into the umass_*_state callbacks
+ * into iteration, preventing us from running out of stack under
+ * error conditions.
+ */
+ if (sc->polling_depth) {
+ if (sc->next_polled_xfer)
+ panic("%s: got polled xfer %p, but %p already "
+ "pending\n", USBDEVNAME(sc->sc_dev), xfer,
+ sc->next_polled_xfer);
+
+ DPRINTF(UDMASS_XFER, ("%s: saving polled xfer %p\n",
+ USBDEVNAME(sc->sc_dev), xfer));
+ sc->next_polled_xfer = xfer;
+
+ return (USBD_IN_PROGRESS);
+ }
+
+ sc->polling_depth++;
+
+start_next_xfer:
+ DPRINTF(UDMASS_XFER, ("%s: start polled xfer %p\n",
+ USBDEVNAME(sc->sc_dev), xfer));
+ err = usbd_transfer(xfer);
+ if (err && err != USBD_IN_PROGRESS && sc->next_polled_xfer == NULL) {
+ DPRINTF(UDMASS_BBB, ("%s: failed to setup transfer, %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err)));
+ sc->polling_depth--;
+ return (err);
+ }
+
+ if (err && err != USBD_IN_PROGRESS) {
+ DPRINTF(UDMASS_XFER, ("umass_polled_xfer %p has error %s\n",
+ xfer, usbd_errstr(err)));
+ }
+
+ if (sc->next_polled_xfer != NULL) {
+ DPRINTF(UDMASS_XFER, ("umass_polled_xfer running next "
+ "transaction %p\n", sc->next_polled_xfer));
+ xfer = sc->next_polled_xfer;
+ sc->next_polled_xfer = NULL;
+ goto start_next_xfer;
+ }
+
+ sc->polling_depth--;
+
+ return (USBD_NORMAL_COMPLETION);
+}
+
+Static usbd_status
umass_setup_transfer(struct umass_softc *sc, usbd_pipe_handle pipe,
void *buffer, int buflen, int flags,
usbd_xfer_handle xfer)
@@ -757,10 +820,17 @@ umass_setup_transfer(struct umass_softc *sc, usbd_pipe_handle pipe,
usbd_setup_xfer(xfer, pipe, (void *)sc, buffer, buflen,
flags | sc->sc_xfer_flags, sc->timeout, sc->sc_methods->wire_state);
- err = usbd_transfer(xfer);
- DPRINTF(UDMASS_XFER,("%s: start xfer buffer=%p buflen=%d flags=0x%x "
- "timeout=%d\n", USBDEVNAME(sc->sc_dev),
- buffer, buflen, flags | sc->sc_xfer_flags, sc->timeout));
+ if (sc->sc_udev->bus->use_polling) {
+ DPRINTF(UDMASS_XFER,("%s: start polled xfer buffer=%p "
+ "buflen=%d flags=0x%x timeout=%d\n", USBDEVNAME(sc->sc_dev),
+ buffer, buflen, flags | sc->sc_xfer_flags, sc->timeout));
+ err = umass_polled_transfer(sc, xfer);
+ } else {
+ err = usbd_transfer(xfer);
+ DPRINTF(UDMASS_XFER,("%s: start xfer buffer=%p buflen=%d "
+ "flags=0x%x timeout=%d\n", USBDEVNAME(sc->sc_dev),
+ buffer, buflen, flags | sc->sc_xfer_flags, sc->timeout));
+ }
if (err && err != USBD_IN_PROGRESS) {
DPRINTF(UDMASS_BBB, ("%s: failed to setup transfer, %s\n",
USBDEVNAME(sc->sc_dev), usbd_errstr(err)));
@@ -786,7 +856,17 @@ umass_setup_ctrl_transfer(struct umass_softc *sc, usb_device_request_t *req,
USBD_DEFAULT_TIMEOUT, req, buffer, buflen, flags,
sc->sc_methods->wire_state);
- err = usbd_transfer(xfer);
+ if (sc->sc_udev->bus->use_polling) {
+ DPRINTF(UDMASS_XFER,("%s: start polled ctrl xfer buffer=%p "
+ "buflen=%d flags=0x%x\n", USBDEVNAME(sc->sc_dev), buffer,
+ buflen, flags));
+ err = umass_polled_transfer(sc, xfer);
+ } else {
+ DPRINTF(UDMASS_XFER,("%s: start ctrl xfer buffer=%p buflen=%d "
+ "flags=0x%x\n", USBDEVNAME(sc->sc_dev), buffer, buflen,
+ flags));
+ err = usbd_transfer(xfer);
+ }
if (err && err != USBD_IN_PROGRESS) {
DPRINTF(UDMASS_BBB, ("%s: failed to setup ctrl transfer, %s\n",
USBDEVNAME(sc->sc_dev), usbd_errstr(err)));
@@ -882,6 +962,7 @@ umass_bbb_transfer(struct umass_softc *sc, int lun, void *cmd, int cmdlen,
umass_callback cb, void *priv)
{
static int dCBWtag = 42; /* unique for CBW of transfer */
+ usbd_status err;
DPRINTF(UDMASS_BBB,("%s: umass_bbb_transfer cmd=0x%02x\n",
USBDEVNAME(sc->sc_dev), *(u_char *)cmd));
@@ -890,8 +971,10 @@ umass_bbb_transfer(struct umass_softc *sc, int lun, void *cmd, int cmdlen,
("sc->sc_wire == 0x%02x wrong for umass_bbb_transfer\n",
sc->sc_wire));
- if (sc->sc_dying)
+ if (sc->sc_dying) {
+ sc->polled_xfer_status = USBD_IOERROR;
return;
+ }
/* Be a little generous. */
sc->timeout = timeout + USBD_DEFAULT_TIMEOUT;
@@ -983,13 +1066,14 @@ umass_bbb_transfer(struct umass_softc *sc, int lun, void *cmd, int cmdlen,
sc->transfer_state = TSTATE_BBB_COMMAND;
/* Send the CBW from host to device via bulk-out endpoint. */
- if (umass_setup_transfer(sc, sc->sc_pipe[UMASS_BULKOUT],
+ if ((err = umass_setup_transfer(sc, sc->sc_pipe[UMASS_BULKOUT],
&sc->cbw, UMASS_BBB_CBW_SIZE, 0,
- sc->transfer_xfer[XFER_BBB_CBW])) {
+ sc->transfer_xfer[XFER_BBB_CBW])))
umass_bbb_reset(sc, STATUS_WIRE_FAILED);
- }
-}
+ if (sc->sc_udev->bus->use_polling)
+ sc->polled_xfer_status = err;
+}
Static void
umass_bbb_state(usbd_xfer_handle xfer, usbd_private_handle priv,
@@ -1378,6 +1462,8 @@ umass_cbi_transfer(struct umass_softc *sc, int lun,
void *cmd, int cmdlen, void *data, int datalen, int dir,
u_int timeout, umass_callback cb, void *priv)
{
+ usbd_status err;
+
DPRINTF(UDMASS_CBI,("%s: umass_cbi_transfer cmd=0x%02x, len=%d\n",
USBDEVNAME(sc->sc_dev), *(u_char *)cmd, datalen));
@@ -1385,8 +1471,10 @@ umass_cbi_transfer(struct umass_softc *sc, int lun,
("sc->sc_wire == 0x%02x wrong for umass_cbi_transfer\n",
sc->sc_wire));
- if (sc->sc_dying)
+ if (sc->sc_dying) {
+ sc->polled_xfer_status = USBD_IOERROR;
return;
+ }
/* Be a little generous. */
sc->timeout = timeout + USBD_DEFAULT_TIMEOUT;
@@ -1428,8 +1516,12 @@ umass_cbi_transfer(struct umass_softc *sc, int lun,
sc->transfer_state = TSTATE_CBI_COMMAND;
/* Send the Command Block from host to device via control endpoint. */
- if (umass_cbi_adsc(sc, cmd, cmdlen, sc->transfer_xfer[XFER_CBI_CB]))
+ if ((err = umass_cbi_adsc(sc, cmd, cmdlen,
+ sc->transfer_xfer[XFER_CBI_CB])))
umass_cbi_reset(sc, STATUS_WIRE_FAILED);
+
+ if (sc->sc_udev->bus->use_polling)
+ sc->polled_xfer_status = err;
}
Static void
diff --git a/sys/dev/usb/umass_scsi.c b/sys/dev/usb/umass_scsi.c
index 7978b422f93..b816f3c25ff 100644
--- a/sys/dev/usb/umass_scsi.c
+++ b/sys/dev/usb/umass_scsi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: umass_scsi.c,v 1.9 2004/07/22 02:40:21 dlg Exp $ */
+/* $OpenBSD: umass_scsi.c,v 1.10 2005/04/01 06:41:13 pascoe Exp $ */
/* $NetBSD: umass_scsipi.c,v 1.9 2003/02/16 23:14:08 augustss Exp $ */
/*
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -66,7 +66,6 @@ struct umass_scsi_softc {
struct scsi_link sc_link;
struct scsi_adapter sc_adapter;
- usbd_status sc_sync_status;
struct scsi_sense sc_sense_cmd;
};
@@ -173,7 +172,6 @@ umass_scsi_cmd(struct scsi_xfer *xs)
{
struct scsi_link *sc_link = xs->sc_link;
struct umass_softc *sc = sc_link->adapter_softc;
- struct umass_scsi_softc *scbus = (struct umass_scsi_softc *)sc->bus;
struct scsi_generic *cmd;
int cmdlen, dir, s;
@@ -239,28 +237,32 @@ umass_scsi_cmd(struct scsi_xfer *xs)
}
if (xs->flags & SCSI_POLL) {
- /* Use sync transfer. XXX Broken! */
DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: sync dir=%d\n", dir));
+ usbd_set_polling(sc->sc_udev, 1);
sc->sc_xfer_flags = USBD_SYNCHRONOUS;
- scbus->sc_sync_status = USBD_INVAL;
+ sc->polled_xfer_status = USBD_INVAL;
sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen,
xs->data, xs->datalen, dir,
- xs->timeout, 0, xs);
+ xs->timeout, umass_scsi_cb, xs);
sc->sc_xfer_flags = 0;
DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done err=%d\n",
- scbus->sc_sync_status));
- switch (scbus->sc_sync_status) {
- case USBD_NORMAL_COMPLETION:
- xs->error = XS_NOERROR;
- break;
- case USBD_TIMEOUT:
- xs->error = XS_TIMEOUT;
- break;
- default:
- xs->error = XS_DRIVER_STUFFUP;
- break;
+ sc->polled_xfer_status));
+ if (xs->error == XS_NOERROR) {
+ switch (sc->polled_xfer_status) {
+ case USBD_NORMAL_COMPLETION:
+ xs->error = XS_NOERROR;
+ break;
+ case USBD_TIMEOUT:
+ xs->error = XS_TIMEOUT;
+ break;
+ default:
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ }
}
- goto done;
+ usbd_set_polling(sc->sc_udev, 0);
+ DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done, error=%d\n",
+ xs->error));
} else {
DPRINTF(UDMASS_SCSI,
("umass_scsi_cmd: async dir=%d, cmdlen=%d"
@@ -379,6 +381,9 @@ umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, int status)
USBDEVNAME(sc->sc_dev), status);
}
+ if (xs->flags & SCSI_POLL)
+ return;
+
xs->flags |= ITSDONE;
DPRINTF(UDMASS_CMD,("umass_scsi_cb: at %lu.%06lu: return error=%d, "
diff --git a/sys/dev/usb/umassvar.h b/sys/dev/usb/umassvar.h
index 3f12e39003a..217c384d1d1 100644
--- a/sys/dev/usb/umassvar.h
+++ b/sys/dev/usb/umassvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: umassvar.h,v 1.7 2004/07/21 07:43:41 dlg Exp $ */
+/* $OpenBSD: umassvar.h,v 1.8 2005/04/01 06:41:13 pascoe Exp $ */
/* $NetBSD: umassvar.h,v 1.20 2003/09/08 19:31:01 mycroft Exp $ */
/*-
* Copyright (c) 1999 MAEKAWA Masahide <bishop@rr.iij4u.or.jp>,
@@ -264,6 +264,11 @@ struct umass_softc {
int sc_sense;
struct umassbus_softc *bus; /* bus dependent data */
+
+ /* For polled transfers */
+ int polling_depth;
+ usbd_status polled_xfer_status;
+ usbd_xfer_handle next_polled_xfer;
};
#define UMASS_MAX_TRANSFER_SIZE MAXBSIZE