diff options
author | Christopher Pascoe <pascoe@cvs.openbsd.org> | 2005-04-01 06:41:14 +0000 |
---|---|---|
committer | Christopher Pascoe <pascoe@cvs.openbsd.org> | 2005-04-01 06:41:14 +0000 |
commit | 83ebff49ddd3ca98388d1827346db6a9d68e7cfd (patch) | |
tree | 324caa3bdd02b372f982a1bd69eb7c755008d9ce /sys/dev/usb | |
parent | 2852c2f1b306fc98934727b737e68d18e78287f2 (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.c | 118 | ||||
-rw-r--r-- | sys/dev/usb/umass_scsi.c | 41 | ||||
-rw-r--r-- | sys/dev/usb/umassvar.h | 7 |
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 |