diff options
Diffstat (limited to 'scsi/rz.c')
-rw-r--r-- | scsi/rz.c | 462 |
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) |