summaryrefslogtreecommitdiff
path: root/scsi/rz.c
diff options
context:
space:
mode:
Diffstat (limited to 'scsi/rz.c')
-rw-r--r--scsi/rz.c462
1 files changed, 462 insertions, 0 deletions
diff --git a/scsi/rz.c b/scsi/rz.c
new file mode 100644
index 0000000..febf629
--- /dev/null
+++ b/scsi/rz.c
@@ -0,0 +1,462 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: rz.c
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 10/90
+ *
+ * Top layer of the SCSI driver: interface with the MI side.
+ */
+
+/*
+ * This file contains the code that is common to all scsi devices,
+ * operations and/or behaviours specific to certain devices live
+ * in the corresponding rz_mumble files.
+ */
+
+#include <scsi.h>
+
+#if (NSCSI>0)
+
+#include <mach/std_types.h>
+#include <machine/machspl.h> /* spl definitions */
+#include <scsi/compat_30.h>
+
+#ifdef MACH_KERNEL
+#include <kern/time_out.h>
+#else /*MACH_KERNEL*/
+#include <sys/kernel.h> /* for hz */
+
+static io_req_t getbp();
+#endif /*MACH_KERNEL*/
+
+#include <scsi/scsi_defs.h>
+#include <scsi/rz.h>
+
+
+boolean_t
+rz_check(dev, p_sc, p_tgt)
+ int dev;
+ scsi_softc_t **p_sc;
+ target_info_t **p_tgt;
+{
+ if (rzcontroller(dev) >= NSCSI ||
+ (*p_sc = scsi_softc[rzcontroller(dev)]) == 0)
+ return FALSE;
+
+ *p_tgt = (*p_sc)->target[rzslave(dev)];
+
+ if (!*p_tgt ||
+ !((*p_tgt)->flags&TGT_ALIVE))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * Open routine
+ *
+ * On tapes and other devices might have to wait a bit for
+ * the unit to come alive. The following patchable variable
+ * takes this into account
+ */
+int rz_open_timeout = 60;/* seconds */
+
+int rz_open(dev, mode, ior)
+ int dev;
+ dev_mode_t mode;
+ io_req_t ior;
+{
+ scsi_softc_t *sc = 0;
+ target_info_t *tgt;
+ scsi_ret_t ret;
+ register int i;
+
+ if (!rz_check(dev, &sc, &tgt)) {
+ /*
+ * Probe it again: might have installed a new device
+ */
+ if (!sc || !scsi_probe(sc, &tgt, rzslave(dev), ior))
+ return D_NO_SUCH_DEVICE;
+ }
+
+ /* tapes do not wait for rewind to complete on close */
+ if (tgt->ior && !(tgt->flags & TGT_ONLINE))
+ return D_WOULD_BLOCK;
+
+ if (scsi_debug)
+ printf("opening %s%d..", (*tgt->dev_ops->driver_name)(TRUE), dev&0xff);
+
+ if (sc->watchdog) {
+ (*sc->watchdog)(tgt->hw_state);
+ sc->watchdog = 0;
+ }
+
+ /*
+ * Bring the unit online, retrying if necessary.
+ * If the target is spinning up we wait for it.
+ */
+ if ( ! (tgt->flags & TGT_ONLINE)) {
+ io_req_t tmp_ior;
+
+ io_req_alloc(tmp_ior,0);
+ tmp_ior->io_next = 0;
+ tmp_ior->io_count = 0;
+
+ for (i = 0; i < rz_open_timeout; i++) {
+
+ tmp_ior->io_op = IO_INTERNAL;
+ tmp_ior->io_error = 0;
+ ret = scsi_test_unit_ready(tgt, tmp_ior);
+
+ if (ret == SCSI_RET_SUCCESS)
+ break;
+
+ if (ret == SCSI_RET_DEVICE_DOWN) {
+ i = rz_open_timeout;
+ break;
+ }
+
+ if (ret == SCSI_RET_NEED_SENSE) {
+
+ tmp_ior->io_op = IO_INTERNAL;
+ tmp_ior->io_count = 0;
+ tmp_ior->io_residual = 0;
+ tgt->ior = tmp_ior;
+ scsi_request_sense(tgt, tmp_ior, 0);
+ iowait(tmp_ior);
+
+ }
+
+ if (i == 5) printf("%s%d: %s\n",
+ (*tgt->dev_ops->driver_name)(TRUE),
+ tgt->target_id,
+ "Waiting to come online..");
+ timeout(wakeup, tgt, hz);
+ await(tgt);
+ }
+
+ /* lock on removable media */
+ if ((i != rz_open_timeout) && (tgt->flags & TGT_REMOVABLE_MEDIA)) {
+ tmp_ior->io_op = IO_INTERNAL;
+ /* too many dont support it. Sigh */
+ tgt->flags |= TGT_OPTIONAL_CMD;
+ (void) scsi_medium_removal( tgt, FALSE, tmp_ior);
+ tgt->flags &= ~TGT_OPTIONAL_CMD;
+ }
+
+ io_req_free(tmp_ior);
+ if (i == rz_open_timeout)
+ return D_DEVICE_DOWN;
+ }
+ /*
+ * Perform anything open-time special on the device
+ */
+ if (tgt->dev_ops->open != SCSI_OPEN_NULL) {
+ ret = (*tgt->dev_ops->open)(tgt, ior);
+ if (ret != SCSI_RET_SUCCESS) {
+ if (scsi_debug) printf("%s%d: open failed x%x\n",
+ (*tgt->dev_ops->driver_name)(TRUE), dev&0xff, ret);
+ return ret;
+ }
+ }
+ tgt->flags |= TGT_ONLINE;
+ ior->io_device->bsize = tgt->block_size;
+ return D_SUCCESS;
+}
+
+int rz_close(dev)
+ int dev;
+{
+ scsi_softc_t *sc;
+ target_info_t *tgt;
+ scsi_ret_t ret;
+
+ if (!rz_check(dev, &sc, &tgt))
+ return D_NO_SUCH_DEVICE;
+
+ if (scsi_debug)
+ printf("closing %s%d..", (*tgt->dev_ops->driver_name)(TRUE), dev&0xff);
+
+ if (tgt->flags & TGT_REMOVABLE_MEDIA) {
+ io_req_t ior;
+
+ io_req_alloc(ior,0);
+ ior->io_next = 0;
+ ior->io_count = 0;
+ ior->io_op = IO_INTERNAL;
+ ior->io_error = 0;
+ /* too many dont support it. Sigh */
+ tgt->flags |= TGT_OPTIONAL_CMD;
+ (void) scsi_medium_removal( tgt, TRUE, ior);
+ tgt->flags &= ~TGT_OPTIONAL_CMD;
+ io_req_free(ior);
+ }
+
+ /*
+ * Perform anything close-time special on the device
+ */
+ if (tgt->dev_ops->close != SCSI_CLOSE_NULL) {
+ ret = (*tgt->dev_ops->close)(tgt);
+ if (ret != SCSI_RET_SUCCESS) {
+ printf("%s%d: close failed x%x\n",
+ (*tgt->dev_ops->driver_name)(TRUE), dev&0xff, ret);
+ }
+ }
+ if (tgt->flags & TGT_REMOVABLE_MEDIA)
+ tgt->flags &= ~TGT_ONLINE;
+
+ return D_SUCCESS;
+}
+
+/* our own minphys */
+void rz_minphys(ior)
+ io_req_t ior;
+{
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+ if (ior->io_count > scsi_per_target_virtual)
+ ior->io_count = scsi_per_target_virtual;
+#endif /*MACH_KERNEL*/
+}
+
+int rz_read(dev, ior)
+ int dev;
+ io_req_t ior;
+{
+ target_info_t *tgt;
+
+ tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)];
+
+#ifdef MACH_KERNEL
+ return block_io(tgt->dev_ops->strategy, rz_minphys, ior);
+#else /*MACH_KERNEL*/
+ return physio(tgt->dev_ops->strategy, getbp(dev), dev, IO_READ, rz_minphys, ior);
+#endif /*MACH_KERNEL*/
+}
+
+int rz_write(dev, ior)
+ int dev;
+ io_req_t ior;
+{
+ target_info_t *tgt;
+
+ tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)];
+
+ if (tgt->flags & TGT_READONLY)
+ return D_INVALID_OPERATION;
+
+#ifdef MACH_KERNEL
+ return block_io(tgt->dev_ops->strategy, rz_minphys, ior);
+#else /*MACH_KERNEL*/
+ return physio(tgt->dev_ops->strategy, getbp(dev), dev, IO_WRITE, rz_minphys, ior);
+#endif /*MACH_KERNEL*/
+}
+
+int rz_get_status(dev, flavor, status, status_count)
+ int dev;
+ dev_flavor_t flavor;
+ dev_status_t status;
+ natural_t *status_count;
+{
+ target_info_t *tgt;
+
+ tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)];
+
+ if (scsi_debug)
+ printf("rz_get_status: x%x x%x x%x x%x\n",
+ dev, flavor, status, *status_count);
+ return (*tgt->dev_ops->get_status)(dev, tgt, flavor, status, status_count);
+}
+
+int rz_set_status(dev, flavor, status, status_count)
+ int dev;
+ dev_flavor_t flavor;
+ dev_status_t status;
+ natural_t status_count;
+{
+ target_info_t *tgt;
+
+ tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)];
+
+ if (scsi_debug)
+ printf("rz_set_status: x%x x%x x%x x%x\n",
+ dev, flavor, status, status_count);
+ return (*tgt->dev_ops->set_status)(dev, tgt, flavor, status, status_count);
+}
+
+/*
+ * Routine to return information to kernel.
+ */
+int
+rz_devinfo(dev, flavor, info)
+ int dev;
+ int flavor;
+ char *info;
+{
+ register int result;
+
+ result = D_SUCCESS;
+
+ switch (flavor) {
+ /* Caller stupidity, should use device->bsize instead */
+ case D_INFO_BLOCK_SIZE:
+ *((int *) info) = scsi_softc[rzcontroller(dev)]->
+ target[rzslave(dev)]->block_size;
+ break;
+ default:
+ result = D_INVALID_OPERATION;
+ }
+
+ return(result);
+}
+
+void
+rz_simpleq_strategy(ior, start)
+ io_req_t ior;
+ void (*start)();
+{
+ target_info_t *tgt;
+ register scsi_softc_t *sc;
+ scsi_ret_t ret;
+ register int i = ior->io_unit;
+ io_req_t head, tail;
+ spl_t s;
+
+ sc = scsi_softc[rzcontroller(i)];
+ tgt = sc->target[rzslave(i)];
+
+ ior->io_next = 0;
+ ior->io_prev = 0;
+
+ s = splbio();
+ simple_lock(&tgt->target_lock);
+ if (head = tgt->ior) {
+ /* Queue it up at the end of the list */
+ if (tail = head->io_prev)
+ tail->io_next = ior;
+ else
+ head->io_next = ior;
+ head->io_prev = ior; /* tail pointer */
+ simple_unlock(&tgt->target_lock);
+ } else {
+ /* Was empty, start operation */
+ tgt->ior = ior;
+ simple_unlock(&tgt->target_lock);
+ (*start)( tgt, FALSE);
+ }
+ splx(s);
+}
+#ifdef MACH_KERNEL
+#else /*MACH_KERNEL*/
+
+rz_strategy(ior)
+ io_req_t ior;
+{
+ target_info_t *tgt;
+ register int dev = ior->io_unit;
+
+ tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)];
+
+ return (*tgt->dev_ops->strategy)(ior);
+}
+
+
+#define IOCPARM_SIZE(c) (((c)>>16)&IOCPARM_MASK)
+#define IOC_WDSIZE(s) ((IOCPARM_SIZE(s))>>2)
+
+rz_ioctl(dev, cmd, data, flag)
+{
+ io_return_t error;
+ unsigned int count;
+
+ count = IOC_WDSIZE(cmd);
+ if (cmd & (IOC_VOID|IOC_IN)) {
+ error = rz_set_status(dev, cmd, (dev_status_t)data, count);
+ if (error)
+ return (error);
+ }
+ if (cmd & IOC_OUT) {
+ error = rz_get_status(dev, cmd, (dev_status_t *)data, &count);
+ if (error)
+ return (error);
+ }
+ return (0);
+}
+
+/* This is a very simple-minded config,
+ * assumes we have << 8 disks per bus */
+#define NBUF (NSCSI*8)
+struct io_req rz_buffers[NBUF];
+
+static io_req_t
+getbp(dev)
+{
+ io_req_t ior;
+ int hash = minor(dev) >> 3;
+
+ ior = &rz_buffers[hash];
+ if (ior->io_op & IO_BUSY) {
+ register io_req_t ior;
+ for (ior = rz_buffers; ior < &rz_buffers[NBUF]; ior++)
+ if ((ior->io_op & IO_BUSY) == 0)
+ return ior;
+
+ }
+ return ior;
+}
+
+/*
+ * This ugliness is only needed because of the
+ * way the minor is encoded for tapes.
+ */
+tz_open(dev, mode, ior)
+ int dev;
+ dev_mode_t mode;
+ io_req_t ior;
+{
+ io_return_t error;
+
+ error = rz_open(TAPE_UNIT(dev), mode, ior);
+ if(error)
+ return error;
+ if (TAPE_REWINDS(dev)) {
+ scsi_softc_t *sc;
+ target_info_t *tgt;
+
+ rz_check(TAPE_UNIT(dev), &sc, &tgt);
+ tgt->flags |= TGT_REWIND_ON_CLOSE;
+ }
+ return 0;
+}
+
+tz_close(dev) { return rz_close(TAPE_UNIT(dev));}
+tz_read(dev, ior) { return rz_read(TAPE_UNIT(dev), ior);}
+tz_write(dev, ior) { return rz_write(TAPE_UNIT(dev), ior);}
+tz_ioctl(dev, cmd, data, flag) { return rz_ioctl(TAPE_UNIT(dev), cmd, data, flag);}
+
+#endif /*MACH_KERNEL*/
+
+#endif (NSCSI>0)