diff options
Diffstat (limited to 'scsi/scsi.c')
-rw-r--r-- | scsi/scsi.c | 642 |
1 files changed, 0 insertions, 642 deletions
diff --git a/scsi/scsi.c b/scsi/scsi.c deleted file mode 100644 index d4aecf6..0000000 --- a/scsi/scsi.c +++ /dev/null @@ -1,642 +0,0 @@ -/* - * Mach Operating System - * Copyright (c) 1993-1989 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: scsi.c - * Author: Alessandro Forin, Carnegie Mellon University - * Date: 9/90 - * - * Middle layer of the SCSI driver: chip independent functions - * This file contains Controller and Device-independent functions - */ - -#include <scsi.h> - -#if NSCSI > 0 -#include <platforms.h> - -#include <machine/machspl.h> /* spl definitions */ - -#include <mach/std_types.h> -#include <sys/types.h> -#include <scsi/compat_30.h> - -#include <chips/busses.h> -#include <scsi/scsi.h> -#include <scsi/scsi2.h> -#include <scsi/scsi_defs.h> -#include <machine/machspl.h> - - - -#ifdef VAXSTATION -/* We run some of this code on the interrupt stack */ -#undef spl0 -#define spl0() spl1() -#endif /*VAXSTATION*/ - -/* - * Overall driver state - */ - -target_info_t scsi_target_data[NSCSI*8]; /* per target state */ -scsi_softc_t scsi_softc_data[NSCSI]; /* per HBA state */ -scsi_softc_t *scsi_softc[NSCSI]; /* quick access&checking */ - -/* - * If a specific target should NOT be asked to go synchronous - * then its bit in this bitmap should be set. Each SCSI controller - * (Host Bus Adapter) can hold at most 8 targets --> use one - * byte per controller. A bit set to one means NO synchronous. - * Patch with adb if necessary. - */ -unsigned char scsi_no_synchronous_xfer[NSCSI]; - -/* - * For certain targets it is wise to use the long form of the - * read/write commands even if their capacity would not necessitate - * it. Same as above for usage. - */ -unsigned char scsi_use_long_form[NSCSI]; - - -/* - * Control over disconnect-reconnect mode. - */ -unsigned char scsi_might_disconnect[NSCSI] = /* do it if deemed appropriate */ - { 0xff, 0xff, 0xff, 0xff, 0xff};/* Fix by hand viz NSCSI */ -unsigned char scsi_should_disconnect[NSCSI] = /* just do it */ - { 0,}; -unsigned char scsi_initiator_id[NSCSI] = /* our id on the bus(ses) */ - { 7, 7, 7, 7, 7}; - -/* - * Miscellaneus config - */ -boolean_t scsi_exabyte_filemarks = FALSE; /* use short filemarks */ -int scsi_watchdog_period = 10; /* but exabyte needs >=30 for bspace */ -int scsi_delay_after_reset = 1000000;/* microseconds */ -boolean_t scsi_no_automatic_bbr = FALSE; /* revector bad blocks automatically */ - -#ifdef MACH_KERNEL -#else -/* This covers Exabyte's max record size */ -unsigned int scsi_per_target_virtual = 256*1024; -#endif MACH_KERNEL - - -/* - * Device-specific operations are switched off this table - */ - -extern char - *scdisk_name(), *sctape_name(), *scprt_name(), - *sccpu_name(), *scworm_name(), *sccdrom_name(), - *scscn_name(), *scmem_name(), *scjb_name(), *sccomm_name(); -extern void - sctape_optimize(); -extern scsi_ret_t - scdisk_open(), sctape_open(), sctape_close(), - sccomm_open(), sccomm_close(); -extern int - scdisk_strategy(), sctape_strategy(), sccpu_strategy(), - sccomm_strategy(); -extern void - scdisk_start(), sctape_start(), sccpu_start(), sccomm_start(); - -extern io_return_t - scdisk_set_status(), scdisk_get_status(), - sctape_set_status(), sctape_get_status(), - sccomm_set_status(), sccomm_get_status(); - -scsi_devsw_t scsi_devsw[] = { - -/* SCSI_DISK */ { scdisk_name, SCSI_OPTIMIZE_NULL, - scdisk_open, SCSI_CLOSE_NULL, - scdisk_strategy, scdisk_start, - scdisk_get_status, scdisk_set_status }, - -/* SCSI_TAPE */ { sctape_name, sctape_optimize, - sctape_open, sctape_close, - sctape_strategy, sctape_start, - sctape_get_status, sctape_set_status }, - -/* SCSI_PRINTER */ { scprt_name, SCSI_OPTIMIZE_NULL, /*XXX*/}, - -/* SCSI_CPU */ { sccpu_name, SCSI_OPTIMIZE_NULL, - SCSI_OPEN_NULL, SCSI_CLOSE_NULL, - sccpu_strategy, sccpu_start,}, - -/* SCSI_WORM */ { scworm_name, SCSI_OPTIMIZE_NULL, - scdisk_open, SCSI_CLOSE_NULL, - scdisk_strategy, scdisk_start, - scdisk_get_status, scdisk_set_status }, - -/* SCSI_CDROM */ { sccdrom_name, SCSI_OPTIMIZE_NULL, - scdisk_open, SCSI_CLOSE_NULL, - scdisk_strategy, scdisk_start, - scdisk_get_status, scdisk_set_status }, -/* scsi2 */ -/* SCSI_SCANNER */ { scscn_name, SCSI_OPTIMIZE_NULL, /*XXX*/ }, - -/* SCSI_MEMORY */ { scmem_name, SCSI_OPTIMIZE_NULL, - scdisk_open, SCSI_CLOSE_NULL, - scdisk_strategy, scdisk_start, - scdisk_get_status, scdisk_set_status }, - -/* SCSI_J_BOX */ { scjb_name, SCSI_OPTIMIZE_NULL, /*XXX*/ }, - -/* SCSI_COMM */ { sccomm_name, SCSI_OPTIMIZE_NULL, -#if (NCENDATA>0) - sccomm_open, sccomm_close, - sccomm_strategy, sccomm_start, - sccomm_get_status, sccomm_set_status -#endif - }, - 0 -}; - -/* - * Allocation routines for state structures - */ -scsi_softc_t * -scsi_master_alloc(unit, hw) - unsigned unit; - char *hw; -{ - scsi_softc_t *sc; - - if (unit < NSCSI) { - sc = &scsi_softc_data[unit]; - scsi_softc[unit] = sc; - sc->masterno = unit; - sc->hw_state = hw; - return sc; - } - return 0; -} - -target_info_t * -scsi_slave_alloc(unit, slave, hw) - unsigned unit, slave; - char *hw; -{ - target_info_t *tgt; - - tgt = &scsi_target_data[(unit<<3) + slave]; - tgt->hw_state = hw; - tgt->dev_ops = 0; /* later */ - tgt->target_id = slave; - tgt->masterno = unit; - tgt->block_size = 1; /* default */ - tgt->flags = TGT_ALIVE; - tgt->sync_period = 0; - tgt->sync_offset = 0; - simple_lock_init(&tgt->target_lock); - - scsi_softc[unit]->target[slave] = tgt; - return tgt; -} - -void -zero_ior( - io_req_t ior ) -{ - ior->io_next = ior->io_prev = 0; - ior->io_count = 0; - ior->io_op = IO_INTERNAL; - ior->io_error = 0; -} - -/* - * Slave routine: - * See if the slave description (controller, unit, ..) - * matches one of the slaves found during probe - * - * Implementation: - * Send out an INQUIRY command to see what sort of device - * the slave is. - * Notes: - * At this time the driver is fully functional and works - * off interrupts. - * TODO: - * The SCSI2 spec says what exactly must happen: see F.2.3 - */ -int scsi_slave( ui, reg) - struct bus_device *ui; - unsigned reg; -{ - scsi_softc_t *sc = scsi_softc[(unsigned char)ui->ctlr]; - target_info_t *tgt = sc->target[(unsigned char)ui->slave]; - scsi2_inquiry_data_t *inq; - int scsi_std; - int ptype, s; - - if (!tgt || !(tgt->flags & TGT_ALIVE)) - return 0; - - /* Might have scanned already */ - if (tgt->dev_ops) - goto out; - -#ifdef SCSI2 - This is what should happen: - - for all LUNs - INQUIRY - scsi_verify_state (see) - scsi_initialize (see) -#endif SCSI2 - - tgt->unit_no = ui->slave; /* incorrect, but needed early */ - - s = spl0(); /* we need interrupts */ - - if (BGET(scsi_no_synchronous_xfer,(unsigned char)sc->masterno,tgt->target_id)) - tgt->flags |= TGT_DID_SYNCH; - - /* - * Ok, it is time to see what type of device this is, - * send an INQUIRY cmd and wait till done. - * Possibly do the synch negotiation here. - */ - scsi_inquiry(tgt, SCSI_INQ_STD_DATA); - - inq = (scsi2_inquiry_data_t*)tgt->cmd_ptr; - ptype = inq->periph_type; - - switch (ptype) { - case SCSI_CDROM : - tgt->flags |= TGT_READONLY; - /* fall through */ - case SCSI_DISK : - case SCSI_TAPE : - case SCSI_PRINTER : - case SCSI_CPU : - case SCSI_WORM : - case SCSI_SCANNER : - case SCSI_MEMORY : - case SCSI_J_BOX : - case SCSI_COMM : -/* case SCSI_PREPRESS1 : reserved, really - case SCSI_PREPRESS2 : */ - tgt->dev_ops = &scsi_devsw[ptype]; - break; - default: - printf("scsi%d: %s %d (x%x). ", ui->ctlr, - "Unsupported device type at SCSI id", ui->slave, - inq->periph_type); - scsi_print_inquiry((scsi2_inquiry_data_t*)inq, - SCSI_INQ_STD_DATA, 0); - tgt->flags = 0; - splx(s); - return 0; - } - - if (inq->rmb) - tgt->flags |= TGT_REMOVABLE_MEDIA; - - /* - * Tell the user we know this target, then see if we - * can be a bit smart about it. - */ - scsi_print_inquiry((scsi2_inquiry_data_t*)inq, - SCSI_INQ_STD_DATA, tgt->tgt_name); - if (scsi_debug) - scsi_print_inquiry((scsi2_inquiry_data_t*)inq, - SCSI_INQ_STD_DATA, 0); - - /* - * The above says if it currently behaves as a scsi2, - * however scsi1 might just be the default setting. - * The spec say that even if in scsi1 mode the target - * should answer to the full scsi2 inquiry spec. - */ - scsi_std = (inq->ansi == 2 || inq->response_fmt == 2) ? 2 : 1; -#if nosey - if (scsi_std == 2) { - unsigned char supp_pages[256], i; - scsi2_impl_opdef_page_t *impl; - - scsi_inquiry(tgt, SCSI_INQ_SUPP_PAGES); - impl = (scsi2_impl_opdef_page_t *)inq; - npages = impl->page_len - 2; - bcopy(impl->supp_opdef, supp_pages, npages); - - for (i = 0; i < npages; i++) { - scsi_inquiry(tgt, supp_pages[i]); - scsi_print_inquiry(inq, supp_pages[i], 0); - } - } - - if (scsi_std == 2) { - scsi2_impl_opdef_page_t *impl; - int i; - - scsi_inquiry(tgt, SCSI_INQ_IMPL_OPDEF); - impl = (scsi2_impl_opdef_page_t *)inq; - for (i = 0; i < impl->page_len - 2; i++) - if (impl->supp_opdef[i] == SCSI2_OPDEF) { - scsi_change_definition(tgt, SCSI2_OPDEF); - /* if success .. */ - tgt->flags |= TGT_SCSI_2_MODE; - break; - } - } -#endif nosey - - splx(s); -out: - return (strcmp(ui->name, (*tgt->dev_ops->driver_name)(TRUE)) == 0); -} - -#ifdef SCSI2 -scsi_verify_state(...) -{ -verify_state: send test_unit_ready up to 3 times, each time it fails -(with check condition) send a requeste_sense. It is ok to get UNIT ATTENTION -the first time only, NOT READY the second, only GOOD the last time. -If you get BUSY or RESERVATION CONFLICT retry. -} - -scsi_initialize(...) -{ - -initialize: send start_unit with immed=0 (->disconnect), if fails -with check condition send requeste_sense and if "illegal request" -proceed anyways. Retry on BUSY. -Do a verify_state, then -disks: - - mode_sense (current) if ANSI2 or needed by vendor (!!!!) - and if check-condition&illegal-request goto capacity - - mode_sense (changeable) - - if needed do a mode_select (yes, 512) - - read_capacity -tapes: - -} -#endif SCSI2 - -/* - * Attach routine: - * Fill in all the relevant per-slave data and make - * the slave operational. - * - * Implementation: - * Get target's status, start the unit and then - * switch off to device-specific functions to gather - * as much info as possible about the slave. - */ -void scsi_attach(ui) - register struct bus_device *ui; -{ - scsi_softc_t *sc = scsi_softc[ui->mi->unit]; - target_info_t *tgt = sc->target[(unsigned char)ui->slave]; - int i; - spl_t s; - - printf(" (%s %s) ", (*tgt->dev_ops->driver_name)(FALSE),tgt->tgt_name); - - if (tgt->flags & TGT_US) { - printf(" [this cpu]"); - return; - } - - s = spl0(); - - /* sense return from inquiry */ - scsi_request_sense(tgt, 0, 0); - - /* - * Do this twice, certain targets need it - */ - if (tgt->dev_ops != &scsi_devsw[SCSI_CPU]) { - (void) scsi_start_unit(tgt, SCSI_CMD_SS_START, 0); - i = 0; - while (scsi_start_unit(tgt, SCSI_CMD_SS_START, 0) == SCSI_RET_RETRY) { - if (i++ == 5) - printf(".. not yet online .."); - delay(1000000); - if (i == 60) { - printf(" seems hopeless."); - break; - } - } - } - - /* - * See if it is up and about - */ - scsi_test_unit_ready(tgt, 0); - - if (tgt->dev_ops->optimize != SCSI_OPTIMIZE_NULL) - (*tgt->dev_ops->optimize)(tgt); - - tgt->flags |= TGT_FULLY_PROBED; - - splx(s); -} - -/* - * Probe routine: - * See if a device answers. Used AFTER autoconf. - * - * Implementation: - * First ask the HBA to see if anyone is there at all, then - * call the scsi_slave and scsi_attach routines with a fake ui. - */ -boolean_t -scsi_probe( sc, tgt_ptr, target_id, ior) - scsi_softc_t *sc; - target_info_t **tgt_ptr; - int target_id; - io_req_t ior; -{ - struct bus_device ui; - target_info_t *tgt; - - if (!sc->probe || target_id > 7 || target_id == sc->initiator_id) - return FALSE; /* sanity */ - - if (sc->target[target_id] == 0) - scsi_slave_alloc( sc->masterno, target_id, sc->hw_state); - tgt = sc->target[target_id]; - tgt->flags = 0;/* we donno yet */ - tgt->dev_ops = 0; - - /* mildly enquire */ - if (!(sc->probe)(tgt, ior)) - goto fail; - - /* There is something there, see what it is */ - bzero(&ui, sizeof(ui)); - ui.ctlr = sc->masterno; - ui.unit = - ui.slave = target_id; - ui.name = ""; - - /* this fails on the name for sure */ - (void) scsi_slave( &ui, 0 /* brrrr */); - if ((tgt->flags & TGT_ALIVE) == 0) - goto fail; - - { - struct bus_ctlr mi; - - mi.unit = sc->masterno; - ui.mi = &mi; - printf("%s at slave %d ", - (*tgt->dev_ops->driver_name)(TRUE), target_id); - scsi_attach(&ui); - } - - *tgt_ptr = tgt; - return TRUE; -fail: - tgt->flags = 0; - return FALSE; -} - - -/* - * Watchdog routine: - * Issue a SCSI bus reset if a target holds up the - * bus for too long. - * - * Implementation: - * Each HBA that wants to use this should have a - * watchdog_t structure at the head of its hardware - * descriptor. This variable is set by this periodic - * routine and reset on bus activity. If it is not reset on - * time (say some ten seconds or so) we reset the - * SCSI bus. - * NOTE: - * An HBA must be ready to accept bus reset interrupts - * properly in order to use this. - */ -void scsi_watchdog(hw) - watchdog_t *hw; -{ - spl_t s = splbio(); - - switch (hw->watchdog_state) { - case SCSI_WD_EXPIRED: - - /* double check first */ - if (hw->nactive == 0) { - hw->watchdog_state = SCSI_WD_INACTIVE; - break; - } - if (scsi_debug) - printf("SCSI Watchdog expired\n"); - hw->watchdog_state = SCSI_WD_INACTIVE; - (*hw->reset)(hw); - break; - - case SCSI_WD_ACTIVE: - - hw->watchdog_state = SCSI_WD_EXPIRED; - break; - - case SCSI_WD_INACTIVE: - - break; - } - - /* do this here, fends against powered down devices */ - if (scsi_watchdog_period != 0) - timeout((int(*)())scsi_watchdog, (char*)hw, scsi_watchdog_period * hz); - - splx(s); -} - - -/* - * BusReset Notification: - * Called when the HBA sees a BusReset interrupt - * - * Implementation: - * Go through the list of targets, redo the synch - * negotiation, and restart whatever operation was - * in progress for that target. - */ -void scsi_bus_was_reset(sc) - scsi_softc_t *sc; -{ - register target_info_t *tgt; - int i; - /* - * Redo the synch negotiation - */ - for (i = 0; i < 8; i++) { - io_req_t ior; - spl_t s; - - if (i == sc->initiator_id) - continue; - tgt = sc->target[i]; - if (!tgt || !(tgt->flags & TGT_ALIVE)) - continue; - - tgt->flags &= ~(TGT_DID_SYNCH|TGT_DISCONNECTED); -#if 0 - /* the standard does *not* imply this gets reset too */ - tgt->sync_period = 0; - tgt->sync_offset = 0; -#endif - - /* - * retry the synch negotiation - */ - ior = tgt->ior; - tgt->ior = 0; - printf(".. tgt %d ", tgt->target_id); - if (BGET(scsi_no_synchronous_xfer,(unsigned char)sc->masterno,tgt->target_id)) - tgt->flags |= TGT_DID_SYNCH; - else { - s = spl0(); - scsi_test_unit_ready(tgt, 0); - splx(s); - } - tgt->ior = ior; - } - - /* - * Notify each target of the accident - */ - for (i = 0; i < 8; i++) { - if (i == sc->initiator_id) - continue; - tgt = sc->target[i]; - if (!tgt) - continue; - tgt->done = SCSI_RET_ABORTED|SCSI_RET_RETRY; - if (tgt->ior) - (*tgt->dev_ops->restart)( tgt, TRUE); - } - - printf("%s", " reset complete\n"); -} - -#endif NSCSI > 0 |