summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/umass_scsi.c
diff options
context:
space:
mode:
authornate <nate@openbsd.org>2003-05-17 06:07:57 +0000
committernate <nate@openbsd.org>2003-05-17 06:07:57 +0000
commitff61a120eb82fa583ca10d146ed6dcd0851998bf (patch)
tree00b99126e2d4fb77c15b11b65844e4a1e7821333 /sys/dev/usb/umass_scsi.c
parentdocument tags (diff)
downloadwireguard-openbsd-ff61a120eb82fa583ca10d146ed6dcd0851998bf.tar.xz
wireguard-openbsd-ff61a120eb82fa583ca10d146ed6dcd0851998bf.zip
sync with NetBSD and add various local hacks to make things work correctly
with our scsi layer
Diffstat (limited to 'sys/dev/usb/umass_scsi.c')
-rw-r--r--sys/dev/usb/umass_scsi.c464
1 files changed, 464 insertions, 0 deletions
diff --git a/sys/dev/usb/umass_scsi.c b/sys/dev/usb/umass_scsi.c
new file mode 100644
index 00000000000..0c7f4b91f5c
--- /dev/null
+++ b/sys/dev/usb/umass_scsi.c
@@ -0,0 +1,464 @@
+/* $OpenBSD: umass_scsi.c,v 1.3 2003/05/17 06:07:57 nate Exp $ */
+/* $NetBSD: umass_scsipi.c,v 1.9 2003/02/16 23:14:08 augustss Exp $ */
+/*
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "atapiscsi.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/buf.h>
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+
+#include <dev/usb/umassvar.h>
+#include <dev/usb/umass_scsi.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#include <scsi/scsi_disk.h>
+#include <machine/bus.h>
+
+struct umass_scsi_softc {
+ struct umassbus_softc base;
+ struct scsi_link sc_link;
+ struct scsi_adapter sc_adapter;
+
+ usbd_status sc_sync_status;
+ struct scsi_sense sc_sense_cmd;
+};
+
+
+#define SHORT_INQUIRY_LENGTH 36 /* XXX */
+
+#define UMASS_SCSIID_HOST 0x00
+#define UMASS_SCSIID_DEVICE 0x01
+
+#define UMASS_ATAPI_DRIVE 0
+
+int umass_scsi_cmd(struct scsi_xfer *);
+void umass_scsi_minphys(struct buf *);
+
+void umass_scsi_cb(struct umass_softc *sc, void *priv, int residue,
+ int status);
+void umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue,
+ int status);
+struct umass_scsi_softc *umass_scsi_setup(struct umass_softc *);
+
+struct scsi_device umass_scsi_dev = { NULL, NULL, NULL, NULL, };
+
+#if NATAPISCSI > 0
+struct scsi_device umass_atapiscsi_dev = { NULL, NULL, NULL, NULL, };
+#endif
+
+int
+umass_scsi_attach(struct umass_softc *sc)
+{
+ struct umass_scsi_softc *scbus;
+
+ scbus = umass_scsi_setup(sc);
+ scbus->sc_link.adapter_target = UMASS_SCSIID_HOST;
+ scbus->sc_link.luns = sc->maxlun + 1;
+ scbus->sc_link.flags &= ~SDEV_ATAPI;
+ scbus->sc_link.device = &umass_scsi_dev;
+
+ DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: SCSI\n"
+ "sc = 0x%x, scbus = 0x%x\n",
+ USBDEVNAME(sc->sc_dev), sc, scbus));
+
+ sc->sc_refcnt++;
+ scbus->base.sc_child =
+ config_found((struct device *)sc, &scbus->sc_link, scsiprint);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+
+ return (0);
+}
+
+#if NATAPISCSI > 0
+int
+umass_atapi_attach(struct umass_softc *sc)
+{
+ struct umass_scsi_softc *scbus;
+
+ scbus = umass_scsi_setup(sc);
+ scbus->sc_link.adapter_target = UMASS_SCSIID_HOST;
+ scbus->sc_link.luns = 1;
+ scbus->sc_link.flags |= SDEV_ATAPI;
+ scbus->sc_link.quirks |= SDEV_NOLUNS;
+ scbus->sc_link.device = &umass_atapiscsi_dev;
+
+ DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: ATAPI\n"
+ "sc = 0x%x, scbus = 0x%x\n",
+ USBDEVNAME(sc->sc_dev), sc, scbus));
+
+ sc->sc_refcnt++;
+ scbus->base.sc_child =
+ config_found((struct device *)sc, &scbus->sc_link, scsiprint);
+ if (--sc->sc_refcnt < 0)
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+
+ return (0);
+}
+#endif
+
+struct umass_scsi_softc *
+umass_scsi_setup(struct umass_softc *sc)
+{
+ struct umass_scsi_softc *scbus;
+
+ scbus = malloc(sizeof(struct umass_scsi_softc), M_DEVBUF, M_WAITOK);
+ memset(&scbus->sc_link, 0, sizeof(struct scsi_link));
+ memset(&scbus->sc_adapter, 0, sizeof(struct scsi_adapter));
+
+ sc->bus = (struct umassbus_softc *)scbus;
+
+ /* Fill in the adapter. */
+ scbus->sc_adapter.scsi_cmd = umass_scsi_cmd;
+ scbus->sc_adapter.scsi_minphys = umass_scsi_minphys;
+
+ /* Fill in the link. */
+ scbus->sc_link.adapter_buswidth = 2;
+ scbus->sc_link.openings = 1;
+ scbus->sc_link.adapter = &scbus->sc_adapter;
+ scbus->sc_link.adapter_softc = sc;
+ scbus->sc_link.openings = 1;
+ scbus->sc_link.quirks |= PQUIRK_ONLYBIG | PQUIRK_NOMODESENSE |
+ sc->sc_busquirks;
+
+ return (scbus);
+}
+
+int
+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, trcmd;
+ int cmdlen, dir, s;
+
+#ifdef UMASS_DEBUG
+ microtime(&sc->tv);
+#endif
+
+ memset(&trcmd, 0, sizeof(trcmd));
+
+ DIF(UDMASS_UPPER, sc_link->flags |= SCSIDEBUG_LEVEL);
+
+ DPRINTF(UDMASS_CMD, ("%s: umass_scsi_cmd: at %lu.%06lu: %d:%d "
+ "xs=%p cmd=0x%02x datalen=%d (quirks=0x%x, poll=%d)\n",
+ USBDEVNAME(sc->sc_dev), sc->tv.tv_sec, sc->tv.tv_usec,
+ sc_link->target, sc_link->lun, xs, xs->cmd->opcode,
+ xs->datalen, sc_link->quirks, xs->flags & SCSI_POLL));
+
+#if defined(USB_DEBUG) && defined(SCSIDEBUG)
+ if (umassdebug & UDMASS_SCSI)
+ show_scsi_xs(xs);
+ else if (umassdebug & ~UDMASS_CMD)
+ show_scsi_cmd(xs);
+#endif
+
+ if (sc->sc_dying) {
+ xs->error = XS_DRIVER_STUFFUP;
+ goto done;
+ }
+
+#if defined(UMASS_DEBUG)
+ if (sc_link->target != UMASS_SCSIID_DEVICE) {
+ DPRINTF(UDMASS_SCSI, ("%s: wrong SCSI ID %d\n",
+ USBDEVNAME(sc->sc_dev), sc_link->target));
+ xs->error = XS_DRIVER_STUFFUP;
+ goto done;
+ }
+#endif
+
+ cmd = xs->cmd;
+ cmdlen = xs->cmdlen;
+
+ if (cmd->opcode == MODE_SENSE &&
+ (sc_link->quirks & SDEV_NOMODESENSE)) {
+ xs->error = XS_TIMEOUT;
+ goto done;
+ }
+
+ if (cmd->opcode == START_STOP &&
+ (sc->sc_quirks & UMASS_QUIRK_NO_START_STOP)) {
+ xs->error = XS_NOERROR;
+ goto done;
+ }
+
+ if (cmd->opcode == INQUIRY &&
+ (sc->sc_quirks & UMASS_QUIRK_FORCE_SHORT_INQUIRY)) {
+ /*
+ * Some drives wedge when asked for full inquiry
+ * information.
+ */
+ memcpy(&trcmd, cmd, sizeof(trcmd));
+ trcmd.bytes[4] = SHORT_INQUIRY_LENGTH;
+ cmd = &trcmd;
+ xs->datalen = SHORT_INQUIRY_LENGTH;
+ }
+
+ dir = DIR_NONE;
+ if (xs->datalen) {
+ switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
+ case SCSI_DATA_IN:
+ dir = DIR_IN;
+ break;
+ case SCSI_DATA_OUT:
+ dir = DIR_OUT;
+ break;
+ }
+ }
+
+ if (xs->datalen > UMASS_MAX_TRANSFER_SIZE) {
+ printf("umass_cmd: large datalen, %d\n", xs->datalen);
+ xs->error = XS_DRIVER_STUFFUP;
+ goto done;
+ }
+
+ if (xs->flags & SCSI_POLL) {
+ /* Use sync transfer. XXX Broken! */
+ DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: sync dir=%d\n", dir));
+ sc->sc_xfer_flags = USBD_SYNCHRONOUS;
+ scbus->sc_sync_status = USBD_INVAL;
+ sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen,
+ xs->data, xs->datalen, dir,
+ xs->timeout, 0, 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;
+ }
+ goto done;
+ } else {
+ DPRINTF(UDMASS_SCSI,
+ ("umass_scsi_cmd: async dir=%d, cmdlen=%d"
+ " datalen=%d\n",
+ dir, cmdlen, xs->datalen));
+ sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen,
+ xs->data, xs->datalen, dir,
+ xs->timeout, umass_scsi_cb, xs);
+ return (SUCCESSFULLY_QUEUED);
+ }
+
+ /* Return if command finishes early. */
+ done:
+ xs->flags |= ITSDONE;
+
+ s = splbio();
+ scsi_done(xs);
+ splx(s);
+ if (xs->flags & SCSI_POLL)
+ return (COMPLETE);
+ else
+ return (SUCCESSFULLY_QUEUED);
+}
+
+void
+umass_scsi_minphys(struct buf *bp)
+{
+ if (bp->b_bcount > UMASS_MAX_TRANSFER_SIZE)
+ bp->b_bcount = UMASS_MAX_TRANSFER_SIZE;
+
+ minphys(bp);
+}
+
+void
+umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, int status)
+{
+ struct umass_scsi_softc *scbus = (struct umass_scsi_softc *)sc->bus;
+ struct scsi_xfer *xs = priv;
+ struct scsi_link *link = xs->sc_link;
+ int cmdlen;
+ int s;
+#ifdef UMASS_DEBUG
+ struct timeval tv;
+ u_int delta;
+ microtime(&tv);
+ delta = (tv.tv_sec - sc->tv.tv_sec) * 1000000 +
+ tv.tv_usec - sc->tv.tv_usec;
+#endif
+
+ DPRINTF(UDMASS_CMD,
+ ("umass_scsi_cb: at %lu.%06lu, delta=%u: xs=%p residue=%d"
+ " status=%d\n", tv.tv_sec, tv.tv_usec, delta, xs, residue,
+ status));
+
+ xs->resid = residue;
+
+ switch (status) {
+ case STATUS_CMD_OK:
+ xs->error = XS_NOERROR;
+ break;
+
+ case STATUS_CMD_UNKNOWN:
+ DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd unknown\n"));
+ /* we can't issue REQUEST SENSE */
+ if (xs->sc_link->quirks & PQUIRK_NOSENSE) {
+ /*
+ * If no residue and no other USB error,
+ * command succeeded.
+ */
+ if (residue == 0) {
+ xs->error = XS_NOERROR;
+ break;
+ }
+
+ /*
+ * Some devices return a short INQUIRY
+ * response, omitting response data from the
+ * "vendor specific data" on...
+ */
+ if (xs->cmd->opcode == INQUIRY &&
+ residue < xs->datalen) {
+ xs->error = XS_NOERROR;
+ break;
+ }
+
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ }
+ /* FALLTHROUGH */
+ case STATUS_CMD_FAILED:
+ printf("umass_scsi_cb: status cmd failed\n");
+ /* fetch sense data */
+ memset(&scbus->sc_sense_cmd, 0, sizeof(scbus->sc_sense_cmd));
+ scbus->sc_sense_cmd.opcode = REQUEST_SENSE;
+ scbus->sc_sense_cmd.byte2 = link->lun << SCSI_CMD_LUN_SHIFT;
+ scbus->sc_sense_cmd.length = sizeof(xs->sense);
+
+ cmdlen = sizeof(scbus->sc_sense_cmd);
+ if (sc->sc_cmd == UMASS_CPROTO_UFI) /* XXX */
+ cmdlen = UFI_COMMAND_LENGTH;
+ sc->sc_methods->wire_xfer(sc, link->lun,
+ &scbus->sc_sense_cmd, cmdlen,
+ &xs->sense, sizeof(xs->sense),
+ DIR_IN, xs->timeout,
+ umass_scsi_sense_cb, xs);
+ return;
+
+ case STATUS_WIRE_FAILED:
+ xs->error = XS_RESET;
+ break;
+
+ default:
+ panic("%s: Unknown status %d in umass_scsi_cb",
+ USBDEVNAME(sc->sc_dev), status);
+ }
+
+ xs->flags |= ITSDONE;
+
+ DPRINTF(UDMASS_CMD,("umass_scsi_cb: at %lu.%06lu: return error=%d, "
+ "status=0x%x resid=%d\n",
+ tv.tv_sec, tv.tv_usec,
+ xs->error, xs->status, xs->resid));
+
+ s = splbio();
+ scsi_done(xs);
+ splx(s);
+}
+
+/*
+ * Finalise a completed autosense operation
+ */
+void
+umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue,
+ int status)
+{
+ struct scsi_xfer *xs = priv;
+ int s;
+
+ DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: xs=%p residue=%d "
+ "status=%d\n", xs, residue, status));
+
+ switch (status) {
+ case STATUS_CMD_OK:
+ case STATUS_CMD_UNKNOWN:
+ /* getting sense data succeeded */
+ if (xs->cmd->opcode == INQUIRY && (xs->resid < xs->datalen ||
+ (sc->sc_quirks & UMASS_QUIRK_RS_NO_CLEAR_UA /* XXX */))) {
+ /*
+ * Some drivers return SENSE errors even after INQUIRY.
+ * The upper layer doesn't like that.
+ */
+ xs->error = XS_NOERROR;
+ break;
+ }
+ /* XXX look at residue */
+ if (residue == 0 || residue == 14)/* XXX */
+ xs->error = XS_SENSE;
+ else
+ xs->error = XS_SHORTSENSE;
+ break;
+ default:
+ DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n",
+ USBDEVNAME(sc->sc_dev), status));
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ }
+
+ xs->flags |= ITSDONE;
+
+ DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: return xs->error=%d, "
+ "xs->flags=0x%x xs->resid=%d\n", xs->error, xs->status,
+ xs->resid));
+
+ s = splbio();
+ scsi_done(xs);
+ splx(s);
+}
+