From 25062ba061871945759b3baa833fe64969383e40 Mon Sep 17 00:00:00 2001 From: rgrimes Date: Sat, 12 Jun 1993 14:58:17 +0000 Subject: Initial import, 0.1 + pk 0.2.4-B1 --- sys/scsi/README | 182 ++++++ sys/scsi/cd.c | 1722 ++++++++++++++++++++++++++++++++++++++++++++++++ sys/scsi/scsi_all.h | 373 +++++++++++ sys/scsi/scsi_cd.h | 295 +++++++++ sys/scsi/scsi_disk.h | 249 +++++++ sys/scsi/scsi_tape.h | 169 +++++ sys/scsi/scsiconf.c | 761 ++++++++++++++++++++++ sys/scsi/scsiconf.h | 113 ++++ sys/scsi/sd.c | 1466 +++++++++++++++++++++++++++++++++++++++++ sys/scsi/st.c | 1774 ++++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 7104 insertions(+) create mode 100644 sys/scsi/README create mode 100644 sys/scsi/cd.c create mode 100644 sys/scsi/scsi_all.h create mode 100644 sys/scsi/scsi_cd.h create mode 100644 sys/scsi/scsi_disk.h create mode 100644 sys/scsi/scsi_tape.h create mode 100644 sys/scsi/scsiconf.c create mode 100644 sys/scsi/scsiconf.h create mode 100644 sys/scsi/sd.c create mode 100644 sys/scsi/st.c (limited to 'sys/scsi') diff --git a/sys/scsi/README b/sys/scsi/README new file mode 100644 index 0000000..16e0998 --- /dev/null +++ b/sys/scsi/README @@ -0,0 +1,182 @@ + * + * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE + * -------------------- ----- ---------------------- + * CURRENT PATCH LEVEL: 1 00098 + * -------------------- ----- ---------------------- + * + * 16 Feb 93 Julian Elischer ADDED for SCSI system +This release consists of the following files +(relative to the base of the kernel tree) + + +MAKEDEV + +scsi +scsi/README +scsi/scsiconf.h +scsi/scsiconf.c +scsi/scsi_all.h + +scsi/scsi_disk.h +scsi/sd.c + +scsi/scsi_tape.h +scsi/st.c + +sys/chio.h +scsi/scsi_changer.h +scsi/ch.c + +sys/cdio.h +scsi/scsi_cd.h +scsi/cd.c + +i386/conf/SCSITEST +i386/isa/aha1542.c + +README.AHA1742 +i386/conf/AHBTEST +i386/isa/aha1742.c + +i386/conf/UHATEST +i386/isa/ultra14f.c + +i386/conf/BTTEST +i386/isa/bt742a.c + + + +---------------------------------------------------------------- +This scsi system is designed to allow the re-use of top end drivers +such as disk and tape drivers, with different scsi adapters. + +As of writing this document, There are top end drivers working for: +---------------------------------------------------------------- +generic scsi disk +generic scsi tape +cd-rom (plays music under the xcplayer (?) program) +AEG Character recognition devices * +Calera Character recognition devices * +Kodak IL900 scanner * +Exabyte tape changer device. +---------------------------------------------------------------- + + +There are also working bottom end drivers for: +---------------------------------------------------------------- +adaptec 1542 (and 1742 in 1542 mode) +bustec 742a +adaptec 174x +Ultrastore 14f +---------------------------------------------------------------- + + +Work is proceeding on the following bottom end drivers: +---------------------------------------------------------------- +Future Domain (1680)** hosler@tfs.com & me +Future Domain (8 bit)**** rpr@oce.nl +WD7000** terry@icarus.weber.edu +seagate st01/st02**** overby@aspen.cray.com ? +---------------------------------------------------------------- +* drivers not made public (proprietary.. proof that the concept works though) +** driver not yet released but working. +*** just a dream so far. +**** some amount more than just a dream so far. + + +################## Using the scsi system ################## +------------minor numbers--------------- +This scsi system does not allocate minor numbers to devices depending +on their SCSI IDs is any way. A devices minor number is dependant +on the order in which it was found. +e.g. the first tape found will become st0 (minor number 0) + the second found will become st1 (minor number 16) + the third will become st2 (minor 32) + etc. + +These devices could be on the same scsi bus or different scsi busses. +That would not change their minor numbers. + +It is possible to run two different TYPES of scsi adapters at the +same time and have st0 on one and st1 on another. (for example) + +There is a scheme supported in which scsi devices can be 'wired in' even +if they are not present or powered on at probe time. (see scsiconf.c) + +--------------getting started------------ +It should be possible to use the /dev entries for as0 as if they were +/dev entries for sd0 and the old as bootblocks should +continue to work if you are using an adaptec 1542b. + +--------------making devices------------ +A changed version of /dev/MAKEDEV is supplied that +can be used to make devices sd[01234] and st[01234] + +e.g. +cd /dev +sh MAKEDEV sd0 sd1 sd2 st0 st1 cd0 + + +The tape devices are as follows: +rst0 basic raw device, will rewind on close +nrst0 will not rewind on close +erst0 will rewind and EJECTon close +nerst0 will not rewind and WILL eject (some devices may rewind anyhow) + +------------future enhancements-------------- +Some people have indicated that they would like to have the SCSI ID +encoded into the minor number in some way, and +this may be supported at some timein the future, using +minor numbers greater than 128. (or maybe a different major number) + +I will also be writing (probably) a generic scsi-error +handling routine that will be table driven, so that the routine can +be removed from each individual driver. With enough care, +two similar devices with different error codes (quite common) could run +the same driver but use different error tables. + +--------------file layout------------------- +Originally I had all scsi definitions in one file: scsi.h +I have since moved definitions of commands so that all +definitions needed for a particular type of device are +found together in the include file of that name. +This approximatly follows the layout of their definition +in the SCSI-2 spec. +As such they are: + +scsi_all.h general commands for all devices --- CHAPTER 7 +scsi-disk.h commands relevant to disk --- CHAPTER 8 +scsi-tape.h commands for scsi tapes --- CHAPTER 9 +scsi-cd.h commands for cd-roms (and audio) --- CHAPTER 13 +scsi-changer.h commands medium changer devices --- CHAPTER 16 + +---------ioctl definitions------------- +User accessable structures (e.g. ioctl definitions) have been +placed in sys/cdio and sys/chio (based after sys/mtio for +the ioctls for mag tapes (including st). + +-----------cd-rom----------------- +The cd rom driver ha been tested by a number of people and +grefen@wilbur.zdv.uni-mainz.de has completed the audio play +functions. +He tells me he has some Public Domain package that +allows an control of the cd player from an Xwindow +but I don't have it. + +-------------media changer--------------- +Once again courtesy of grefen@wilbur.zdv.uni-mainz.de. +I have not tested this but he assures me it's ready for testing. +If anyone has an exabyte tape changer or similar, +contact the author for information regarding the control interface +and program. + +-----------booting from an AHA-174x--------- +For some reason I have not yet worked out, +the BIOS-based bootblocks I have posted will not boot +from the aha1742 in extended mode. (it can't be serious +because the MACH version works) This is in fact not a +problem because the aha1742 driver will force the board into extended +mode during probe, so it can be left in standard mode during the boot. +During the next reboot, the bios will place it back in standard mode +ready for the NEXT boot. + diff --git a/sys/scsi/cd.c b/sys/scsi/cd.c new file mode 100644 index 0000000..2a2261e --- /dev/null +++ b/sys/scsi/cd.c @@ -0,0 +1,1722 @@ +/* + * Written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems for use under the MACH(2.5) operating system. + * + * TRW Financial Systems, in accordance with their agreement with Carnegie + * Mellon University, makes this software available to CMU to distribute + * or use in any manner that they see fit as long as this message is kept with + * the software. For this reason TFS also grants any other persons or + * organisations permission to use or modify this software. + * + * TFS supplies this software to be publicly redistributed + * on the understanding that TFS is not responsible for the correct + * functioning of this software in any circumstances. + * + */ +static char rev[] = "$Revision: 1.3 $"; + +/* + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE + * -------------------- ----- ---------------------- + * CURRENT PATCH LEVEL: 2 00149 + * -------------------- ----- ---------------------- + * + * 16 Feb 93 Julian Elischer ADDED for SCSI system + * 20 Apr 93 Julian Elishcer Fixed error reporting + */ + +#define SPLCD splbio +#define ESUCCESS 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include /* rw_big and start_stop come from there */ +#include + +long int cdstrats,cdqueues; + + +#include +#if NDDB > 0 +int Debugger(); +#else NDDB > 0 +#define Debugger() +#endif NDDB > 0 + + +#define PAGESIZ 4096 +#define SECSIZE 2048 /* XXX */ /* default only */ +#define CDOUTSTANDING 2 +#define CDQSIZE 4 +#define CD_RETRIES 4 + +#define UNITSHIFT 3 +#define PARTITION(z) (minor(z) & 0x07) +#define RAW_PART 3 +#define UNIT(z) ( (minor(z) >> UNITSHIFT) ) + + +extern int hz; +int cd_done(); +int cdstrategy(); +int cd_debug = 0; + +struct buf cd_buf_queue[NCD]; +struct scsi_xfer cd_scsi_xfer[NCD][CDOUTSTANDING]; /* XXX */ +struct scsi_xfer *cd_free_xfer[NCD]; +int cd_xfer_block_wait[NCD]; + +struct cd_data +{ + int flags; +#define CDVALID 0x02 /* PARAMS LOADED */ +#define CDINIT 0x04 /* device has been init'd */ +#define CDWAIT 0x08 /* device has someone waiting */ +#define CDHAVELABEL 0x10 /* have read the label */ + struct scsi_switch *sc_sw; /* address of scsi low level switch */ + int ctlr; /* so they know which one we want */ + int targ; /* our scsi target ID */ + int lu; /* out scsi lu */ + int cmdscount; /* cmds allowed outstanding by board*/ + struct cd_parms + { + int blksize; + u_long disksize; /* total number sectors */ + }params; + struct disklabel disklabel; + int partflags[MAXPARTITIONS]; /* per partition flags */ +#define CDOPEN 0x01 + int openparts; /* one bit for each open partition */ +}cd_data[NCD]; + +#define CD_STOP 0 +#define CD_START 1 +#define CD_EJECT -2 + + +static int next_cd_unit = 0; +/***********************************************************************\ +* The routine called by the low level scsi routine when it discovers * +* A device suitable for this driver * +\***********************************************************************/ +int cdattach(ctlr,targ,lu,scsi_switch) +struct scsi_switch *scsi_switch; +{ + int unit,i; + unsigned char *tbl; + struct cd_data *cd; + struct cd_parms *dp; + + unit = next_cd_unit++; + cd = cd_data + unit; + dp = &(cd->params); + if(scsi_debug & PRINTROUTINES) printf("cdattach: "); + /*******************************************************\ + * Check we have the resources for another drive * + \*******************************************************/ + if( unit >= NCD) + { + printf("Too many scsi CDs..(%d > %d) reconfigure kernel",(unit + 1),NCD); + return(0); + } + /*******************************************************\ + * Store information needed to contact our base driver * + \*******************************************************/ + cd->sc_sw = scsi_switch; + cd->ctlr = ctlr; + cd->targ = targ; + cd->lu = lu; + cd->cmdscount = CDOUTSTANDING; /* XXX (ask the board) */ + + + i = cd->cmdscount; + while(i-- ) + { + cd_scsi_xfer[unit][i].next = cd_free_xfer[unit]; + cd_free_xfer[unit] = &cd_scsi_xfer[unit][i]; + } + /*******************************************************\ + * Use the subdriver to request information regarding * + * the drive. We cannot use interrupts yet, so the * + * request must specify this. * + \*******************************************************/ + cd_get_parms(unit, SCSI_NOSLEEP | SCSI_NOMASK); + if(dp->disksize) + { + printf("cd present\n"); + } + else + { + printf("drive empty\n"); + } + cd->flags |= CDINIT; + return; + +} + + + +/*******************************************************\ +* open the device. Make sure the partition info * +* is a up-to-date as can be. * +\*******************************************************/ +cdopen(dev) +{ + int errcode = 0; + int unit, part; + struct cd_parms cd_parms; + struct cd_data *cd ; + + unit = UNIT(dev); + part = PARTITION(dev); + cd = cd_data + unit; + if(scsi_debug & (PRINTROUTINES | TRACEOPENS)) + printf("cdopen: dev=0x%x (unit %d (of %d),partition %d)\n" + , dev, unit, NCD, part); + /*******************************************************\ + * Check the unit is legal * + \*******************************************************/ + if ( unit >= NCD ) + { + return(ENXIO); + } + /*******************************************************\ + * Make sure the disk has been initialised * + * At some point in the future, get the scsi driver * + * to look for a new device if we are not initted * + \*******************************************************/ + if (! (cd->flags & CDINIT)) + return(ENXIO); + + /*******************************************************\ + * If it's been invalidated, and not everybody has * + * closed it then forbid re-entry. * + * (may have changed media) * + \*******************************************************/ + if ((! (cd->flags & CDVALID)) + && ( cd->openparts)) + return(ENXIO); + /*******************************************************\ + * Check that it is still responding and ok. * + * if the media has been changed this will result in a * + * "unit attention" error which the error code will * + * disregard because the CDVALID flag is not yet set * + \*******************************************************/ + if (cd_req_sense(unit, SCSI_SILENT) != 0) { + if(scsi_debug & TRACEOPENS) + printf("not reponding\n"); + return(ENXIO); + } + if(scsi_debug & TRACEOPENS) + printf("Device present\n"); + /*******************************************************\ + * In case it is a funny one, tell it to start * + * not needed for hard drives * + \*******************************************************/ + cd_start_unit(unit,part,CD_START); + cd_prevent_unit(unit,PR_PREVENT,SCSI_SILENT); + if(scsi_debug & TRACEOPENS) + printf("started "); + /*******************************************************\ + * Load the physical device parameters * + \*******************************************************/ + cd_get_parms(unit, 0); + if(scsi_debug & TRACEOPENS) + printf("Params loaded "); + /*******************************************************\ + * Load the partition info if not already loaded * + \*******************************************************/ + cdgetdisklabel(unit); + if(scsi_debug & TRACEOPENS) + printf("Disklabel fabricated "); + /*******************************************************\ + * Check the partition is legal * + \*******************************************************/ + if (( part >= cd->disklabel.d_npartitions ) + && (part != RAW_PART)) + { + if(scsi_debug & TRACEOPENS) + printf("partition %d > %d\n",part + ,cd->disklabel.d_npartitions); + cd_prevent_unit(unit,PR_ALLOW,SCSI_SILENT); + return(ENXIO); + } + /*******************************************************\ + * Check that the partition exists * + \*******************************************************/ + if (( cd->disklabel.d_partitions[part].p_fstype != FS_UNUSED ) + || (part == RAW_PART)) + { + cd->partflags[part] |= CDOPEN; + cd->openparts |= (1 << part); + if(scsi_debug & TRACEOPENS) + printf("open complete\n"); + cd->flags |= CDVALID; + } + else + { + if(scsi_debug & TRACEOPENS) + printf("part %d type UNUSED\n",part); + cd_prevent_unit(unit,PR_ALLOW,SCSI_SILENT); + return(ENXIO); + } + return(0); +} + +/*******************************************************\ +* Get ownership of a scsi_xfer structure * +* If need be, sleep on it, until it comes free * +\*******************************************************/ +struct scsi_xfer *cd_get_xs(unit,flags) +int flags; +int unit; +{ + struct scsi_xfer *xs; + int s; + + if(flags & (SCSI_NOSLEEP | SCSI_NOMASK)) + { + if (xs = cd_free_xfer[unit]) + { + cd_free_xfer[unit] = xs->next; + xs->flags = 0; + } + } + else + { + s = SPLCD(); + while (!(xs = cd_free_xfer[unit])) + { + cd_xfer_block_wait[unit]++; /* someone waiting! */ + sleep((caddr_t)&cd_free_xfer[unit], PRIBIO+1); + cd_xfer_block_wait[unit]--; + } + cd_free_xfer[unit] = xs->next; + splx(s); + xs->flags = 0; + } + return(xs); +} + +/*******************************************************\ +* Free a scsi_xfer, wake processes waiting for it * +\*******************************************************/ +cd_free_xs(unit,xs,flags) +struct scsi_xfer *xs; +int unit; +int flags; +{ + int s; + + if(flags & SCSI_NOMASK) + { + if (cd_xfer_block_wait[unit]) + { + printf("doing a wakeup from NOMASK mode\n"); + wakeup((caddr_t)&cd_free_xfer[unit]); + } + xs->next = cd_free_xfer[unit]; + cd_free_xfer[unit] = xs; + } + else + { + s = SPLCD(); + if (cd_xfer_block_wait[unit]) + wakeup((caddr_t)&cd_free_xfer[unit]); + xs->next = cd_free_xfer[unit]; + cd_free_xfer[unit] = xs; + splx(s); + } +} + +/*******************************************************\ +* trim the size of the transfer if needed, * +* called by physio * +* basically the smaller of our max and the scsi driver's* +* minphys (note we have no max ourselves) * +\*******************************************************/ +/* Trim buffer length if buffer-size is bigger than page size */ +void cdminphys(bp) +struct buf *bp; +{ + (*(cd_data[UNIT(bp->b_dev)].sc_sw->scsi_minphys))(bp); +} + +/*******************************************************\ +* Actually translate the requested transfer into * +* one the physical driver can understand * +* The transfer is described by a buf and will include * +* only one physical transfer. * +\*******************************************************/ + +int cdstrategy(bp) +struct buf *bp; +{ + struct buf *dp; + unsigned int opri; + struct cd_data *cd ; + int unit; + + cdstrats++; + unit = UNIT((bp->b_dev)); + cd = cd_data + unit; + if(scsi_debug & PRINTROUTINES) printf("\ncdstrategy "); + if(scsi_debug & SHOWREQUESTS) printf("cd%d: %d bytes @ blk%d\n", + unit,bp->b_bcount,bp->b_blkno); + cdminphys(bp); + /*******************************************************\ + * If the device has been made invalid, error out * + * maybe the media changed * + \*******************************************************/ + if(!(cd->flags & CDVALID)) + { + bp->b_error = EIO; + goto bad; + } + /*******************************************************\ + * can't ever write to a CD * + \*******************************************************/ + if ((bp->b_flags & B_READ) == 0) { + bp->b_error = EROFS; + goto bad; + } + /*******************************************************\ + * If it's a null transfer, return immediatly * + \*******************************************************/ + if (bp->b_bcount == 0) { + goto done; + } + + /*******************************************************\ + * Decide which unit and partition we are talking about * + \*******************************************************/ + if(PARTITION(bp->b_dev) != RAW_PART) + { + if (!(cd->flags & CDHAVELABEL)) + { + bp->b_error = EIO; + goto bad; + } + /* + * do bounds checking, adjust transfer. if error, process. + * if end of partition, just return + */ + if (bounds_check_with_label(bp,&cd->disklabel,1) <= 0) + goto done; + /* otherwise, process transfer request */ + } + + opri = SPLCD(); + dp = &cd_buf_queue[unit]; + + /*******************************************************\ + * Place it in the queue of disk activities for this disk* + \*******************************************************/ + disksort(dp, bp); + + /*******************************************************\ + * Tell the device to get going on the transfer if it's * + * not doing anything, otherwise just wait for completion* + \*******************************************************/ + cdstart(unit); + + splx(opri); + return; +bad: + bp->b_flags |= B_ERROR; +done: + + /*******************************************************\ + * Correctly set the buf to indicate a completed xfer * + \*******************************************************/ + bp->b_resid = bp->b_bcount; + biodone(bp); + return; +} + +/***************************************************************\ +* cdstart looks to see if there is a buf waiting for the device * +* and that the device is not already busy. If both are true, * +* It deques the buf and creates a scsi command to perform the * +* transfer in the buf. The transfer request will call cd_done * +* on completion, which will in turn call this routine again * +* so that the next queued transfer is performed. * +* The bufs are queued by the strategy routine (cdstrategy) * +* * +* This routine is also called after other non-queued requests * +* have been made of the scsi driver, to ensure that the queue * +* continues to be drained. * +* * +* must be called at the correct (highish) spl level * +\***************************************************************/ +/* cdstart() is called at SPLCD from cdstrategy and cd_done*/ +cdstart(unit) +int unit; +{ + register struct buf *bp = 0; + register struct buf *dp; + struct scsi_xfer *xs; + struct scsi_rw_big cmd; + int blkno, nblk; + struct cd_data *cd = cd_data + unit; + struct partition *p ; + + if(scsi_debug & PRINTROUTINES) printf("cdstart%d ",unit); + /*******************************************************\ + * See if there is a buf to do and we are not already * + * doing one * + \*******************************************************/ + if(!cd_free_xfer[unit]) + { + return; /* none for us, unit already underway */ + } + + if(cd_xfer_block_wait[unit]) /* there is one, but a special waits */ + { + return; /* give the special that's waiting a chance to run */ + } + + + dp = &cd_buf_queue[unit]; + if ((bp = dp->b_actf) != NULL) /* yes, an assign */ + { + dp->b_actf = bp->av_forw; + } + else + { + return; + } + + xs=cd_get_xs(unit,0); /* ok we can grab it */ + xs->flags = INUSE; /* Now ours */ + /***************************************************************\ + * Should reject all queued entries if CDVALID is not true * + \***************************************************************/ + if(!(cd->flags & CDVALID)) + { + goto bad; /* no I/O.. media changed or something */ + } + + /*******************************************************\ + * We have a buf, now we should move the data into * + * a scsi_xfer definition and try start it * + \*******************************************************/ + /*******************************************************\ + * First, translate the block to absolute * + * and put it in terms of the logical blocksize of the * + * device.. * + \*******************************************************/ + p = cd->disklabel.d_partitions + PARTITION(bp->b_dev); + blkno = ((bp->b_blkno / (cd->params.blksize/512)) + p->p_offset); + nblk = (bp->b_bcount + (cd->params.blksize - 1)) / (cd->params.blksize); + + /*******************************************************\ + * Fill out the scsi command * + \*******************************************************/ + bzero(&cmd, sizeof(cmd)); + cmd.op_code = READ_BIG; + cmd.addr_3 = (blkno & 0xff000000) >> 24; + cmd.addr_2 = (blkno & 0xff0000) >> 16; + cmd.addr_1 = (blkno & 0xff00) >> 8; + cmd.addr_0 = blkno & 0xff; + cmd.length2 = (nblk & 0xff00) >> 8; + cmd.length1 = (nblk & 0xff); + /*******************************************************\ + * Fill out the scsi_xfer structure * + * Note: we cannot sleep as we may be an interrupt * + \*******************************************************/ + xs->flags |= SCSI_NOSLEEP; + xs->adapter = cd->ctlr; + xs->targ = cd->targ; + xs->lu = cd->lu; + xs->retries = CD_RETRIES; + xs->timeout = 10000;/* 10000 millisecs for a disk !*/ + xs->cmd = (struct scsi_generic *)&cmd; + xs->cmdlen = sizeof(cmd); + xs->resid = bp->b_bcount; + xs->when_done = cd_done; + xs->done_arg = unit; + xs->done_arg2 = (int)xs; + xs->error = XS_NOERROR; + xs->bp = bp; + xs->data = (u_char *)bp->b_un.b_addr; + xs->datalen = bp->b_bcount; + + /*******************************************************\ + * Pass all this info to the scsi driver. * + \*******************************************************/ + if ( (*(cd->sc_sw->scsi_cmd))(xs) != SUCCESSFULLY_QUEUED) + { + printf("cd%d: oops not queued",unit); + goto bad; + } + cdqueues++; + return; +bad: xs->error = XS_DRIVER_STUFFUP; + cd_done(unit,xs); +} + +/*******************************************************\ +* This routine is called by the scsi interrupt when * +* the transfer is complete. (or failed) * +\*******************************************************/ +int cd_done(unit,xs) +int unit; +struct scsi_xfer *xs; +{ + struct buf *bp; + int retval; + + if(scsi_debug & PRINTROUTINES) printf("cd_done%d ",unit); + if (! (xs->flags & INUSE)) /* paranoia always pays off */ + panic("scsi_xfer not in use!"); + if(bp = xs->bp) + { + switch(xs->error) + { + case XS_NOERROR: + bp->b_error = 0; + bp->b_resid = 0; + break; + + case XS_SENSE: + retval = (cd_interpret_sense(unit,xs)); + if(retval) + { + bp->b_flags |= B_ERROR; + bp->b_error = retval; + } + break; + + case XS_TIMEOUT: + printf("cd%d timeout\n",unit); + + case XS_BUSY: + /***********************************\ + * Just resubmit it straight back to * + * the SCSI driver to try it again * + \***********************************/ + if(xs->retries--) + { + xs->error = XS_NOERROR; + xs->flags &= ~ITSDONE; + if ( (*(cd_data[unit].sc_sw->scsi_cmd))(xs) + == SUCCESSFULLY_QUEUED) + { /* shhh! don't wake the job, ok? */ + /* don't tell cdstart either, */ + return; + } + /* xs->error is set by the scsi driver */ + } /* Fall through */ + + case XS_DRIVER_STUFFUP: + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + break; + default: + printf("cd%d: unknown error category from scsi driver\n" + ,unit); + } + biodone(bp); + cd_free_xs(unit,xs,0); + cdstart(unit); /* If there's anything waiting.. do it */ + } + else /* special has finished */ + { + wakeup(xs); + } +} +/*******************************************************\ +* Perform special action on behalf of the user * +* Knows about the internals of this device * +\*******************************************************/ +cdioctl(dev_t dev, int cmd, caddr_t addr, int flag) +{ + int error = 0; + unsigned int opri; + unsigned char unit, part; + register struct cd_data *cd; + + + /*******************************************************\ + * Find the device that the user is talking about * + \*******************************************************/ + unit = UNIT(dev); + part = PARTITION(dev); + cd = &cd_data[unit]; + if(scsi_debug & PRINTROUTINES) printf("cdioctl%d ",unit); + + /*******************************************************\ + * If the device is not valid.. abandon ship * + \*******************************************************/ + if (!(cd_data[unit].flags & CDVALID)) + return(EIO); + switch(cmd) + { + + case DIOCSBAD: + error = EINVAL; + break; + + case DIOCGDINFO: + *(struct disklabel *)addr = cd->disklabel; + break; + + case DIOCGPART: + ((struct partinfo *)addr)->disklab = &cd->disklabel; + ((struct partinfo *)addr)->part = + &cd->disklabel.d_partitions[PARTITION(dev)]; + break; + + case DIOCWDINFO: + case DIOCSDINFO: + if ((flag & FWRITE) == 0) + error = EBADF; + else + error = setdisklabel(&cd->disklabel, + (struct disklabel *)addr, + /*(cd->flags & DKFL_BSDLABEL) ? cd->openparts : */0, + 0); + if (error == 0) { + cd->flags |= CDHAVELABEL; + } + break; + + case DIOCWLABEL: + error = EBADF; + break; + + case CDIOCPLAYTRACKS: + { + struct ioc_play_track *args + = (struct ioc_play_track *)addr; + struct cd_mode_data data; + if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) + break; + data.page.audio.sotc = 0; + data.page.audio.immed = 1; + if(error = cd_set_mode(unit,&data)) + break; + return(cd_play_tracks(unit + ,args->start_track + ,args->start_index + ,args->end_track + ,args->end_index + )); + } + break; + case CDIOCPLAYBLOCKS: + { + struct ioc_play_blocks *args + = (struct ioc_play_blocks *)addr; + struct cd_mode_data data; + if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) + break; + data.page.audio.sotc = 0; + data.page.audio.immed = 1; + if(error = cd_set_mode(unit,&data)) + break; + return(cd_play(unit,args->blk,args->len)); + + + } + break; + case CDIOCREADSUBCHANNEL: + { + struct ioc_read_subchannel *args + = (struct ioc_read_subchannel *)addr; + struct cd_sub_channel_info data; + int len=args->data_len; + if(len>sizeof(data)|| + lenaddress_format, + args->data_format,args->track,&data,len)) { + break; + } + len=MIN(len,((data.header.data_len[0]<<8)+data.header.data_len[1]+ + sizeof(struct cd_sub_channel_header))); + if(copyout(&data,args->data,len)!=0) { + error=EFAULT; + } + } + break; + case CDIOREADTOCHEADER: + { + struct ioc_toc_header th; + if( error = cd_read_toc(unit,0,0,&th,sizeof(th))) + break; + th.len=(th.len&0xff)<<8+((th.len>>8)&0xff); + bcopy(&th,addr,sizeof(th)); + } + break; + case CDIOREADTOCENTRYS: + { + struct ioc_read_toc_entry *te= + (struct ioc_read_toc_entry *)addr; + struct cd_toc_entry data[65]; + struct ioc_toc_header *th; + int len=te->data_len; + th=(struct ioc_toc_header *)data; + + if(len>sizeof(data) || lenaddress_format, + te->starting_track, + data, + len)) + break; + len=MIN(len,((((th->len&0xff)<<8)+((th->len>>8)))+ + sizeof(*th))); + if(copyout(th,te->data,len)!=0) { + error=EFAULT; + } + + } + break; + case CDIOCSETPATCH: + { + struct ioc_patch *arg = (struct ioc_patch *)addr; + struct cd_mode_data data; + if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) + break; + data.page.audio.port[LEFT_PORT].channels = arg->patch[0]; + data.page.audio.port[RIGHT_PORT].channels = arg->patch[1]; + data.page.audio.port[2].channels = arg->patch[2]; + data.page.audio.port[3].channels = arg->patch[3]; + if(error = cd_set_mode(unit,&data)) + break; + } + break; + case CDIOCGETVOL: + { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) + break; + arg->vol[LEFT_PORT] = data.page.audio.port[LEFT_PORT].volume; + arg->vol[RIGHT_PORT] = data.page.audio.port[RIGHT_PORT].volume; + arg->vol[2] = data.page.audio.port[2].volume; + arg->vol[3] = data.page.audio.port[3].volume; + } + break; + case CDIOCSETVOL: + { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) + break; + data.page.audio.port[LEFT_PORT].volume = arg->vol[LEFT_PORT]; + data.page.audio.port[RIGHT_PORT].volume = arg->vol[RIGHT_PORT]; + data.page.audio.port[2].volume = arg->vol[2]; + data.page.audio.port[3].volume = arg->vol[3]; + if(error = cd_set_mode(unit,&data)) + break; + } + break; + case CDIOCSETMONO: + { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) + break; + data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL|RIGHT_CHANNEL|4|8; + data.page.audio.port[RIGHT_PORT].channels = LEFT_CHANNEL|RIGHT_CHANNEL; + data.page.audio.port[2].channels = 0; + data.page.audio.port[3].channels = 0; + if(error = cd_set_mode(unit,&data)) + break; + } + break; + case CDIOCSETSTERIO: + { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) + break; + data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL; + data.page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; + data.page.audio.port[2].channels = 0; + data.page.audio.port[3].channels = 0; + if(error = cd_set_mode(unit,&data)) + break; + } + break; + case CDIOCSETMUTE: + { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) + break; + data.page.audio.port[LEFT_PORT].channels = 0; + data.page.audio.port[RIGHT_PORT].channels = 0; + data.page.audio.port[2].channels = 0; + data.page.audio.port[3].channels = 0; + if(error = cd_set_mode(unit,&data)) + break; + } + break; + case CDIOCSETLEFT: + { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) + break; + data.page.audio.port[LEFT_PORT].channels = 15; + data.page.audio.port[RIGHT_PORT].channels = 15; + data.page.audio.port[2].channels = 15; + data.page.audio.port[3].channels = 15; + if(error = cd_set_mode(unit,&data)) + break; + } + break; + case CDIOCSETRIGHT: + { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) + break; + data.page.audio.port[LEFT_PORT].channels = RIGHT_CHANNEL; + data.page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; + data.page.audio.port[2].channels = 0; + data.page.audio.port[3].channels = 0; + if(error = cd_set_mode(unit,&data)) + break; + } + break; + case CDIOCRESUME: + error = cd_pause(unit,1); + break; + case CDIOCPAUSE: + error = cd_pause(unit,0); + break; + case CDIOCSTART: + error = cd_start_unit(unit,part,CD_START); + break; + case CDIOCSTOP: + error = cd_start_unit(unit,part,CD_STOP); + break; + case CDIOCEJECT: + error = cd_start_unit(unit,part,CD_EJECT); + break; + case CDIOCSETDEBUG: + scsi_debug = 0xfff; cd_debug = 0xfff; + break; + case CDIOCCLRDEBUG: + scsi_debug = 0; cd_debug = 0; + break; + case CDIOCRESET: + return(cd_reset(unit)); + break; + default: + error = ENOTTY; + break; + } + return (error); +} + + +/*******************************************************\ +* Load the label information on the named device * +* * +* EVENTUALLY take information about different * +* data tracks from the TOC and put it in the disklabel * +\*******************************************************/ +int cdgetdisklabel(unit) +unsigned char unit; +{ + /*unsigned int n, m;*/ + char *errstring; + struct dos_partition *dos_partition_p; + struct cd_data *cd = cd_data + unit; + + /*******************************************************\ + * If the inflo is already loaded, use it * + \*******************************************************/ + if(cd->flags & CDHAVELABEL) return; + + bzero(&cd->disklabel,sizeof(struct disklabel)); + /*******************************************************\ + * make partition 3 the whole disk in case of failure * + * then get pdinfo * + \*******************************************************/ + strncpy(cd->disklabel.d_typename,"scsi cd_rom",16); + strncpy(cd->disklabel.d_packname,"ficticious",16); + cd->disklabel.d_secsize = cd->params.blksize; /* as long as it's not 0 */ + cd->disklabel.d_nsectors = 100; + cd->disklabel.d_ntracks = 1; + cd->disklabel.d_ncylinders = (cd->params.disksize / 100) + 1; + cd->disklabel.d_secpercyl = 100; + cd->disklabel.d_secperunit = cd->params.disksize; + cd->disklabel.d_rpm = 300; + cd->disklabel.d_interleave = 1; + cd->disklabel.d_flags = D_REMOVABLE; + + cd->disklabel.d_npartitions = 1; + cd->disklabel.d_partitions[0].p_offset = 0; + cd->disklabel.d_partitions[0].p_size = cd->params.disksize; + cd->disklabel.d_partitions[0].p_fstype = 9; + + cd->disklabel.d_magic = DISKMAGIC; + cd->disklabel.d_magic2 = DISKMAGIC; + cd->disklabel.d_checksum = dkcksum(&(cd->disklabel)); + + /*******************************************************\ + * Signal to other users and routines that we now have a * + * disklabel that represents the media (maybe) * + \*******************************************************/ + cd->flags |= CDHAVELABEL; + return(ESUCCESS); +} + +/*******************************************************\ +* Find out form the device what it's capacity is * +\*******************************************************/ +cd_size(unit, flags) +{ + struct scsi_read_cd_cap_data rdcap; + struct scsi_read_cd_capacity scsi_cmd; + int size; + int blksize; + + /*******************************************************\ + * make up a scsi command and ask the scsi driver to do * + * it for you. * + \*******************************************************/ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = READ_CD_CAPACITY; + + /*******************************************************\ + * If the command works, interpret the result as a 4 byte* + * number of blocks * + \*******************************************************/ + if (cd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + &rdcap, + sizeof(rdcap), + 2000, + flags) != 0) + { + printf("could not get size of unit %d\n", unit); + return(0); + } else { + size = rdcap.addr_0 + 1 ; + size += rdcap.addr_1 << 8; + size += rdcap.addr_2 << 16; + size += rdcap.addr_3 << 24; + blksize = rdcap.length_0 ; + blksize += rdcap.length_1 << 8; + blksize += rdcap.length_2 << 16; + blksize += rdcap.length_3 << 24; + } + if(cd_debug)printf("cd%d: %d %d byte blocks\n",unit,size,blksize); + cd_data[unit].params.disksize = size; + cd_data[unit].params.blksize = blksize; + return(size); +} + +/*******************************************************\ +* Check with the device that it is ok, (via scsi driver)* +\*******************************************************/ +cd_req_sense(unit, flags) +{ + struct scsi_sense_data sense_data; + struct scsi_sense scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = REQUEST_SENSE; + scsi_cmd.length = sizeof(sense_data); + + if (cd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + &sense_data, + sizeof(sense_data), + 2000, + flags) != 0) + { + return(ENXIO); + } + else + return(0); +} + +/*******************************************************\ +* Get the requested page into the buffer given * +\*******************************************************/ +cd_get_mode(unit,data,page) +int unit; +struct cd_mode_data *data; +int page; +{ + struct scsi_mode_sense scsi_cmd; + int retval; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + bzero(data,sizeof(*data)); + scsi_cmd.op_code = MODE_SENSE; + scsi_cmd.page_code = page; + scsi_cmd.length = sizeof(*data) & 0xff; + retval = cd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + data, + sizeof(*data), + 20000, /* should be immed */ + 0); + return (retval); +} +/*******************************************************\ +* Get the requested page into the buffer given * +\*******************************************************/ +cd_set_mode(unit,data) +int unit; +struct cd_mode_data *data; +{ + struct scsi_mode_select scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = MODE_SELECT; + scsi_cmd.pf = 1; + scsi_cmd.length = sizeof(*data) & 0xff; + data->header.data_length = 0; + /*show_mem(data,sizeof(*data));/**/ + return (cd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + data, + sizeof(*data), + 20000, /* should be immed */ + 0) + ); +} +/*******************************************************\ +* Get scsi driver to send a "start playing" command * +\*******************************************************/ +cd_play(unit,blk,len) +int unit,blk,len; +{ + struct scsi_play scsi_cmd; + int retval; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = PLAY; + scsi_cmd.blk_addr[0] = (blk >> 24) & 0xff; + scsi_cmd.blk_addr[1] = (blk >> 16) & 0xff; + scsi_cmd.blk_addr[2] = (blk >> 8) & 0xff; + scsi_cmd.blk_addr[3] = blk & 0xff; + scsi_cmd.xfer_len[0] = (len >> 8) & 0xff; + scsi_cmd.xfer_len[1] = len & 0xff; + retval = cd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 200000, /* should be immed */ + 0); + return(retval); +} +/*******************************************************\ +* Get scsi driver to send a "start playing" command * +\*******************************************************/ +cd_play_big(unit,blk,len) +int unit,blk,len; +{ + struct scsi_play_big scsi_cmd; + int retval; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = PLAY_BIG; + scsi_cmd.blk_addr[0] = (blk >> 24) & 0xff; + scsi_cmd.blk_addr[1] = (blk >> 16) & 0xff; + scsi_cmd.blk_addr[2] = (blk >> 8) & 0xff; + scsi_cmd.blk_addr[3] = blk & 0xff; + scsi_cmd.xfer_len[0] = (len >> 24) & 0xff; + scsi_cmd.xfer_len[1] = (len >> 16) & 0xff; + scsi_cmd.xfer_len[2] = (len >> 8) & 0xff; + scsi_cmd.xfer_len[3] = len & 0xff; + retval = cd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 20000, /* should be immed */ + 0); + return(retval); +} +/*******************************************************\ +* Get scsi driver to send a "start playing" command * +\*******************************************************/ +cd_play_tracks(unit,strack,sindex,etrack,eindex) +int unit,strack,sindex,etrack,eindex; +{ + struct scsi_play_track scsi_cmd; + int retval; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = PLAY_TRACK; + scsi_cmd.start_track = strack; + scsi_cmd.start_index = sindex; + scsi_cmd.end_track = etrack; + scsi_cmd.end_index = eindex; + retval = cd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 20000, /* should be immed */ + 0); + return(retval); +} +/*******************************************************\ +* Get scsi driver to send a "start up" command * +\*******************************************************/ +cd_pause(unit,go) +int unit,go; +{ + struct scsi_pause scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = PAUSE; + scsi_cmd.resume = go; + + return (cd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 2000, + 0)); +} +/*******************************************************\ +* Get scsi driver to send a "start up" command * +\*******************************************************/ +cd_reset(unit) +int unit; +{ + return(cd_scsi_cmd(unit,0,0,0,0,2000,SCSI_RESET)); +} +/*******************************************************\ +* Get scsi driver to send a "start up" command * +\*******************************************************/ +cd_start_unit(unit,part,type) +{ + struct scsi_start_stop scsi_cmd; + + if(type==CD_EJECT && (cd_data[unit].openparts&~(1<>8; + scsi_cmd.data_len[1]=(len)&0xff; + return cd_scsi_cmd(unit, + &scsi_cmd, + sizeof(struct scsi_read_subchannel), + data, + len, + 5000, + 0); +} + +/*******************************************************\ +* Read Table of contents * +\*******************************************************/ +cd_read_toc(unit,mode,start,data,len) +int unit,mode,start,len; +struct cd_toc_entry *data; +{ + struct scsi_read_toc scsi_cmd; + int error; + int ntoc; + + bzero(&scsi_cmd,sizeof(scsi_cmd)); + /*if(len!=sizeof(struct ioc_toc_header)) + ntoc=((len)-sizeof(struct ioc_toc_header))/sizeof(struct cd_toc_entry); + else*/ + ntoc=len; + + scsi_cmd.op_code=READ_TOC; + if(mode==CD_MSF_FORMAT) + scsi_cmd.msf=1; + scsi_cmd.from_track=start; + scsi_cmd.data_len[0]=(ntoc)>>8; + scsi_cmd.data_len[1]=(ntoc)&0xff; + return cd_scsi_cmd(unit, + &scsi_cmd, + sizeof(struct scsi_read_toc), + data, + len, + 5000, + 0); +} + + +#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) + +/*******************************************************\ +* Get the scsi driver to send a full inquiry to the * +* device and use the results to fill out the disk * +* parameter structure. * +\*******************************************************/ + +int cd_get_parms(unit, flags) +{ + struct cd_data *cd = cd_data + unit; + + /*******************************************************\ + * First check if we have it all loaded * + \*******************************************************/ + if(cd->flags & CDVALID) return(0); + /*******************************************************\ + * give a number of sectors so that sec * trks * cyls * + * is <= disk_size * + \*******************************************************/ + if(cd_size(unit, flags)) + { + cd->flags |= CDVALID; + return(0); + } + else + { + return(ENXIO); + } +} + +/*******************************************************\ +* close the device.. only called if we are the LAST * +* occurence of an open device * +\*******************************************************/ +cdclose(dev) +dev_t dev; +{ + unsigned char unit, part; + unsigned int old_priority; + + unit = UNIT(dev); + part = PARTITION(dev); + if(scsi_debug & TRACEOPENS) + printf("closing cd%d part %d\n",unit,part); + cd_data[unit].partflags[part] &= ~CDOPEN; + cd_data[unit].openparts &= ~(1 << part); + cd_prevent_unit(unit,PR_ALLOW,SCSI_SILENT); + return(0); +} + +/*******************************************************\ +* ask the scsi driver to perform a command for us. * +* Call it through the switch table, and tell it which * +* sub-unit we want, and what target and lu we wish to * +* talk to. Also tell it where to find the command * +* how long int is. * +* Also tell it where to read/write the data, and how * +* long the data is supposed to be * +\*******************************************************/ +int cd_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags) + +int unit,flags; +struct scsi_generic *scsi_cmd; +int cmdlen; +int timeout; +u_char *data_addr; +int datalen; +{ + struct scsi_xfer *xs; + int retval; + int s; + struct cd_data *cd = cd_data + unit; + + if(scsi_debug & PRINTROUTINES) printf("\ncd_scsi_cmd%d ",unit); + if(cd->sc_sw) /* If we have a scsi driver */ + { + xs = cd_get_xs(unit,flags); /* should wait unless booting */ + if(!xs) + { + printf("cd_scsi_cmd%d: controller busy" + " (this should never happen)\n",unit); + return(EBUSY); + } + xs->flags |= INUSE; + /*******************************************************\ + * Fill out the scsi_xfer structure * + \*******************************************************/ + xs->flags |= flags; + xs->adapter = cd->ctlr; + xs->targ = cd->targ; + xs->lu = cd->lu; + xs->retries = CD_RETRIES; + xs->timeout = timeout; + xs->cmd = scsi_cmd; + xs->cmdlen = cmdlen; + xs->data = data_addr; + xs->datalen = datalen; + xs->resid = datalen; + xs->when_done = (flags & SCSI_NOMASK) + ?(int (*)())0 + :cd_done; + xs->done_arg = unit; + xs->done_arg2 = (int)xs; +retry: xs->error = XS_NOERROR; + xs->bp = 0; + retval = (*(cd->sc_sw->scsi_cmd))(xs); + switch(retval) + { + case SUCCESSFULLY_QUEUED: + s = splbio(); + while(!(xs->flags & ITSDONE)) + sleep(xs,PRIBIO+1); + splx(s); + + case HAD_ERROR: + /*printf("err = %d ",xs->error);*/ + switch(xs->error) + { + case XS_NOERROR: + retval = ESUCCESS; + break; + case XS_SENSE: + retval = (cd_interpret_sense(unit,xs)); + break; + case XS_DRIVER_STUFFUP: + retval = EIO; + break; + + + case XS_BUSY: + case XS_TIMEOUT: + if(xs->retries-- ) + { + xs->flags &= ~ITSDONE; + goto retry; + } + retval = EIO; + break; + default: + retval = EIO; + printf("cd%d: unknown error category from scsi driver\n" + ,unit); + } + break; + case COMPLETE: + retval = ESUCCESS; + break; + case TRY_AGAIN_LATER: + if(xs->retries-- ) + { + if(tsleep( 0,PRIBIO + 2,"retry",hz * 2)) + { + xs->flags &= ~ITSDONE; + goto retry; + } + } + retval = EIO; + break; + default: + retval = EIO; + } + cd_free_xs(unit,xs,flags); + cdstart(unit); /* check if anything is waiting fr the xs */ + } + else + { + printf("cd%d: not set up\n",unit); + return(EINVAL); + } + return(retval); +} +/***************************************************************\ +* Look at the returned sense and act on the error and detirmine * +* The unix error number to pass back... (0 = report no error) * +\***************************************************************/ + +int cd_interpret_sense(unit,xs) +int unit; +struct scsi_xfer *xs; +{ + struct scsi_sense_data *sense; + int key; + int silent; + + /***************************************************************\ + * If the flags say errs are ok, then always return ok. * + \***************************************************************/ + if (xs->flags & SCSI_ERR_OK) return(ESUCCESS); + silent = (xs->flags & SCSI_SILENT); + + sense = &(xs->sense); + switch(sense->error_class) + { + case 7: + { + key=sense->ext.extended.sense_key; + switch(key) + { + case 0x0: + return(ESUCCESS); + case 0x1: + if(!silent) + { + printf("cd%d: soft error(corrected) ", unit); + if(sense->valid) + { + printf("block no. %d (decimal)", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + printf("\n"); + } + return(ESUCCESS); + case 0x2: + if(!silent)printf("cd%d: not ready\n ", + unit); + return(ENODEV); + case 0x3: + if(!silent) + { + printf("cd%d: medium error ", unit); + if(sense->valid) + { + printf("block no. %d (decimal)", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + printf("\n"); + } + return(EIO); + case 0x4: + if(!silent)printf("cd%d: non-media hardware failure\n ", + unit); + return(EIO); + case 0x5: + if(!silent)printf("cd%d: illegal request\n ", + unit); + return(EINVAL); + case 0x6: + if(!silent)printf("cd%d: Unit attention.\n ", unit); + if (cd_data[unit].openparts) + cd_data[unit].flags &= ~(CDVALID | CDHAVELABEL); + { + return(EIO); + } + return(ESUCCESS); + case 0x7: + if(!silent) + { + printf("cd%d: attempted protection violation ", + unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + printf("\n"); + } + return(EACCES); + case 0x8: + if(!silent) + { + printf("cd%d: block wrong state (worm)\n ", + unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + printf("\n"); + } + return(EIO); + case 0x9: + if(!silent)printf("cd%d: vendor unique\n", + unit); + return(EIO); + case 0xa: + if(!silent)printf("cd%d: copy aborted\n ", + unit); + return(EIO); + case 0xb: + if(!silent)printf("cd%d: command aborted\n ", + unit); + return(EIO); + case 0xc: + if(!silent) + { + printf("cd%d: search returned\n ", + unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + printf("\n"); + } + return(ESUCCESS); + case 0xd: + if(!silent)printf("cd%d: volume overflow\n ", + unit); + return(ENOSPC); + case 0xe: + if(!silent) + { + printf("cd%d: verify miscompare\n ", + unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + printf("\n"); + } + return(EIO); + case 0xf: + if(!silent)printf("cd%d: unknown error key\n ", + unit); + return(EIO); + } + break; + } + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + { + if(!silent)printf("cd%d: error class %d code %d\n", + unit, + sense->error_class, + sense->error_code); + if(sense->valid) + if(!silent)printf("block no. %d (decimal)\n", + (sense->ext.unextended.blockhi <<16) + + (sense->ext.unextended.blockmed <<8) + + (sense->ext.unextended.blocklow )); + } + return(EIO); + } +} + + + + +int +cdsize(dev_t dev) +{ + return (-1); +} + +show_mem(address,num) +unsigned char *address; +int num; +{ + int x,y; + printf("------------------------------"); + for (y = 0; y +#include "st.h" +#include "sd.h" +#include "ch.h" +#include "cd.h" + +#ifdef MACH +#include +#endif MACH +#include +#include + +#if !defined(OSF) && !defined(__386BSD__) +#include "bll.h" +#include "cals.h" +#include "kil.h" +#else +#define NBLL 0 +#define NCALS 0 +#define NKIL 0 +#endif /* !defined(OSF) && !defined(__386BSD__) */ + +#if NSD > 0 +extern sdattach(); +#endif NSD +#if NST > 0 +extern stattach(); +#endif NST +#if NCH > 0 +extern chattach(); +#endif NCH +#if NCD > 0 +extern cdattach(); +#endif NCD +#if NBLL > 0 +extern bllattach(); +#endif NBLL +#if NCALS > 0 +extern calsattach(); +#endif NCALS +#if NKIL > 0 +extern kil_attach(); +#endif NKIL + +/***************************************************************\ +* The structure of pre-configured devices that might be turned * +* off and therefore may not show up * +\***************************************************************/ +struct predefined +{ + u_char scsibus; + u_char dev; + u_char lu; + int (*attach_rtn)(); + char *devname; + char flags; +} +pd[] = +{ +#ifdef EXAMPLE_PREDEFINE +#if NSD > 0 + {0,0,0,sdattach,"sd",0},/* define a disk at scsibus=0 dev=0 lu=0 */ +#endif NSD +#endif EXAMPLE_PREDEFINE + {0,9,9} /*illegal dummy end entry */ +}; + + +/***************************************************************\ +* The structure of known drivers for autoconfiguration * +\***************************************************************/ +static struct scsidevs +{ + int type; + int removable; + char *manufacturer; + char *model; + char *version; + int (*attach_rtn)(); + char *devname; + char flags; /* 1 show my comparisons during boot(debug) */ +} +#define SC_SHOWME 0x01 +#define SC_ONE_LU 0x00 +#define SC_MORE_LUS 0x02 +knowndevs[] = { +#if NSD > 0 + { T_DIRECT,T_FIXED,"standard","any" + ,"any",sdattach,"sd",SC_ONE_LU }, + { T_DIRECT,T_FIXED,"MAXTOR ","XT-4170S " + ,"B5A ",sdattach,"mx1",SC_ONE_LU }, +#endif NSD +#if NST > 0 + { T_SEQUENTIAL,T_REMOV,"standard","any" + ,"any",stattach,"st",SC_ONE_LU }, +#endif NST +#if NCALS > 0 + { T_PROCESSOR,T_FIXED,"standard","any" + ,"any",calsattach,"cals",SC_MORE_LUS }, +#endif NCALS +#if NCH > 0 + { T_CHANGER,T_REMOV,"standard","any" + ,"any",chattach,"ch",SC_ONE_LU }, +#endif NCH +#if NCD > 0 + { T_READONLY,T_REMOV,"SONY ","CD-ROM CDU-8012 " + ,"3.1a",cdattach,"cd",SC_ONE_LU }, + { T_READONLY,T_REMOV,"PIONEER ","CD-ROM DRM-600 " + ,"any",cdattach,"cd",SC_MORE_LUS }, +#endif NCD +#if NBLL > 0 + { T_PROCESSOR,T_FIXED,"AEG ","READER " + ,"V1.0",bllattach,"bll",SC_MORE_LUS }, +#endif NBLL +#if NKIL > 0 + { T_SCANNER,T_FIXED,"KODAK ","IL Scanner 900 " + ,"any",kil_attach,"kil",SC_ONE_LU }, +#endif NKIL + +{0} +}; +/***************************************************************\ +* Declarations * +\***************************************************************/ +struct predefined *scsi_get_predef(); +struct scsidevs *scsi_probedev(); +struct scsidevs *selectdev(); + +/* controls debug level within the scsi subsystem */ +/* see scsiconf.h for values */ +int scsi_debug = 0x0; +int scsibus = 0x0; /* This is the Nth scsibus */ + +/***************************************************************\ +* The routine called by the adapter boards to get all their * +* devices configured in. * +\***************************************************************/ +scsi_attachdevs( unit, scsi_addr, scsi_switch) +int unit,scsi_addr; +struct scsi_switch *scsi_switch; +{ + int targ,lun; + struct scsidevs *bestmatch = (struct scsidevs *)0; + struct predefined *predef; + int maybe_more; + +#ifdef SCSI_DELAY +#if SCSI_DELAY > 2 + printf("waiting for scsi devices to settle\n"); +#else SCSI_DELAY > 2 +#define SCSI_DELAY 15 +#endif SCSI_DELAY > 2 +#else +#define SCSI_DELAY 2 +#endif SCSI_DELAY + spinwait(1000 * SCSI_DELAY); + targ = 0; + while(targ < 8) + { + maybe_more = 0; /* by default only check 1 lun */ + if (targ == scsi_addr) + { + targ++; + continue; + } + lun = 0; + while(lun < 8) + { + predef = scsi_get_predef(scsibus + ,targ + ,lun + ,&maybe_more); + bestmatch = scsi_probedev(unit + ,targ + ,lun + ,scsi_switch + ,&maybe_more); + if((bestmatch) && (predef)) /* both exist */ + { + if(bestmatch->attach_rtn + != predef->attach_rtn) + { + printf("Clash in found/expected devices\n"); + printf("will link in FOUND\n"); + } + (*(bestmatch->attach_rtn))(unit, + targ, + lun, + scsi_switch); + } + if((bestmatch) && (!predef)) /* just FOUND */ + { + (*(bestmatch->attach_rtn))(unit, + targ, + lun, + scsi_switch); + } + if((!bestmatch) && (predef)) /* just predef */ + { + (*(predef->attach_rtn))(unit, + targ, + lun, + scsi_switch); + } + if(!(maybe_more)) /* nothing suggests we'll find more */ + { + break; /* nothing here, skip to next targ */ + } + /* otherwise something says we should look further*/ + lun++; + } + targ++; + } +#if NGENSCSI > 0 + /***************************************************************\ + * If available hook up the generic scsi driver, letting it * + * know which target is US. (i.e. illegal or at least special) * + \***************************************************************/ + genscsi_attach(unit,scsi_addr,0,scsi_switch); +#endif + scsibus++; /* next time we are on the NEXT scsi bus */ +} + +/***********************************************\ +* given a target and lu, check if there is a * +* predefined device for that address * +\***********************************************/ +struct predefined *scsi_get_predef(unit,target,lu,maybe_more) +int unit,target,lu,*maybe_more; +{ + int upto,numents; + + numents = (sizeof(pd)/sizeof(struct predefined)) - 1; + + for(upto = 0;upto < numents;upto++) + { + if(pd[upto].scsibus != unit) + continue; + if(pd[upto].dev != target) + continue; + if(pd[upto].lu != lu) + continue; + + printf(" dev%d,lu%d: %s - PRECONFIGURED -\n" + ,target + ,lu + ,pd[upto].devname); + *maybe_more = pd[upto].flags & SC_MORE_LUS; + return(&(pd[upto])); + } + return((struct predefined *)0); +} + +/***********************************************\ +* given a target and lu, ask the device what * +* it is, and find the correct driver table * +* entry. * +\***********************************************/ +struct scsidevs *scsi_probedev(unit,target,lu,scsi_switch, maybe_more) + +struct scsi_switch *scsi_switch; +int unit,target,lu; +int *maybe_more; +{ + struct scsidevs *bestmatch = (struct scsidevs *)0; + char *dtype=(char *)0,*desc; + char *qtype; + static struct scsi_inquiry_data inqbuf; + int len,qualifier,type,remov; + char manu[32]; + char model[32]; + char version[32]; + + + bzero(&inqbuf,sizeof(inqbuf)); + /***********************************************\ + * Ask the device what it is * + \***********************************************/ +#ifdef DEBUG + if((target == 0) && (lu == 0)) + scsi_debug = 0xfff; + else + scsi_debug = 0; +#endif DEBUG + if(scsi_ready( unit, + target, + lu, + scsi_switch, + SCSI_NOSLEEP | SCSI_NOMASK) != COMPLETE) + { + return(struct scsidevs *)0; + } + if(scsi_inquire(unit, + target, + lu, + scsi_switch, + &inqbuf, + SCSI_NOSLEEP | SCSI_NOMASK) != COMPLETE) + { + return(struct scsidevs *)0; + } + + /***********************************************\ + * note what BASIC type of device it is * + \***********************************************/ + if(scsi_debug & SHOWINQUIRY) + { + desc=(char *)&inqbuf; + printf("inq: %x %x %x %x %x %x %x %x %x %x %x %x %x\n", + desc[0], desc[1], desc[2], desc[3], + desc[4], desc[5], desc[6], desc[7], + desc[8], desc[9], desc[10], desc[11], + desc[12]); + } + + type = inqbuf.device_type; + qualifier = inqbuf.device_qualifier; + remov = inqbuf.removable; + + /* Check for a non-existent unit. If the device is returning + * this much, then we must set the flag that has + * the searcher keep looking on other luns. + */ + if (qualifier == 3 && type == T_NODEVICE) + { + *maybe_more = 1; + return (struct scsidevs *)0; + } + + /* Any device qualifier that has + * the top bit set (qualifier&4 != 0) is vendor specific and + * won't match in this switch. + */ + + switch(qualifier) + { + case 0: + qtype=""; + break; + case 1: + qtype=", Unit not Connected!"; + break; + case 2: + qtype=", Reserved Peripheral Qualifier!"; + break; + case 3: + qtype=", The Target can't support this Unit!"; + break; + + default: + dtype="vendor specific"; + qtype=""; + *maybe_more = 1; + break; + } + + if (dtype == 0) + switch(type) + { + case T_DIRECT: + dtype="direct"; + break; + case T_SEQUENTIAL: + dtype="sequential"; + break; + case T_PRINTER: + dtype="printer"; + break; + case T_PROCESSOR: + dtype="processor"; + break; + case T_READONLY: + dtype="readonly"; + break; + case T_WORM: + dtype="worm"; + break; + case T_SCANNER: + dtype="scanner"; + break; + case T_OPTICAL: + dtype="optical"; + break; + case T_CHANGER: + dtype="changer"; + break; + case T_COMM: + dtype="communication"; + break; + default: + dtype="unknown"; + break; + } + + /***********************************************\ + * Then if it's advanced enough, more detailed * + * information * + \***********************************************/ + if(inqbuf.ansii_version > 0) + { + if ((len = inqbuf.additional_length + + ( (char *)inqbuf.unused + - (char *)&inqbuf)) + > (sizeof(struct scsi_inquiry_data) - 1)) + len = sizeof(struct scsi_inquiry_data) - 1; + desc=inqbuf.vendor; + desc[len-(desc - (char *)&inqbuf)] = 0; + strncpy(manu,inqbuf.vendor,8);manu[8]=0; + strncpy(model,inqbuf.product,16);model[16]=0; + strncpy(version,inqbuf.revision,4);version[4]=0; + } + else + /***********************************************\ + * If not advanced enough, use default values * + \***********************************************/ + { + desc="early protocol device"; + strncpy(manu,"unknown",8); + strncpy(model,"unknown",16); + strncpy(version,"????",4); + } + printf(" dev%d,lu%d: type %d:%d(%s%s),%s '%s%s%s' scsi%d\n" + ,target + ,lu + ,qualifier,type + ,dtype,qtype + ,remov?"removable":"fixed" + ,manu + ,model + ,version + ,inqbuf.ansii_version + ); + /***********************************************\ + * Try make as good a match as possible with * + * available sub drivers * + \***********************************************/ + bestmatch = (selectdev(unit,target,lu,&scsi_switch, + qualifier,type,remov,manu,model,version)); + if((bestmatch) && (bestmatch->flags & SC_MORE_LUS)) + { + *maybe_more = 1; + } + return(bestmatch); +} + +/***********************************************\ +* Try make as good a match as possible with * +* available sub drivers * +\***********************************************/ +struct scsidevs +*selectdev(unit,target,lu,dvr_switch,qualifier,type,remov,manu,model,rev) +int unit,target,lu; +struct scsi_switch *dvr_switch; +int qualifier,type,remov; +char *manu,*model,*rev; +{ + int numents = (sizeof(knowndevs)/sizeof(struct scsidevs)) - 1; + int count = 0; + int bestmatches = 0; + struct scsidevs *bestmatch = (struct scsidevs *)0; + struct scsidevs *thisentry = knowndevs; + + type |= (qualifier << 5); + + thisentry--; + while( count++ < numents) + { + thisentry++; + if(type != thisentry->type) + { + continue; + } + if(bestmatches < 1) + { + bestmatches = 1; + bestmatch = thisentry; + } + if(remov != thisentry->removable) + { + continue; + } + if(bestmatches < 2) + { + bestmatches = 2; + bestmatch = thisentry; + } + if(thisentry->flags & SC_SHOWME) + printf("\n%s-\n%s-",thisentry->manufacturer, manu); + if(strcmp(thisentry->manufacturer, manu)) + { + continue; + } + if(bestmatches < 3) + { + bestmatches = 3; + bestmatch = thisentry; + } + if(thisentry->flags & SC_SHOWME) + printf("\n%s-\n%s-",thisentry->model, model); + if(strcmp(thisentry->model, model)) + { + continue; + } + if(bestmatches < 4) + { + bestmatches = 4; + bestmatch = thisentry; + } + if(thisentry->flags & SC_SHOWME) + printf("\n%s-\n%s-",thisentry->version, rev); + if(strcmp(thisentry->version, rev)) + { + continue; + } + if(bestmatches < 5) + { + bestmatches = 5; + bestmatch = thisentry; + break; + } + } + + if (bestmatch == (struct scsidevs *)0) + printf(" No explicit device driver match for \"%s %s\".\n", + manu, model); + + return(bestmatch); +} + +static int recurse = 0; +/***********************************************\ +* Do a scsi operation asking a device if it is * +* ready. Use the scsi_cmd routine in the switch * +* table. * +\***********************************************/ +scsi_ready(unit,target,lu,scsi_switch, flags) +struct scsi_switch *scsi_switch; +{ + struct scsi_test_unit_ready scsi_cmd; + struct scsi_xfer scsi_xfer; + volatile int rval; + int key; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + bzero(&scsi_xfer, sizeof(scsi_xfer)); + scsi_cmd.op_code = TEST_UNIT_READY; + + scsi_xfer.flags=flags | INUSE; + scsi_xfer.adapter=unit; + scsi_xfer.targ=target; + scsi_xfer.lu=lu; + scsi_xfer.cmd=(struct scsi_generic *)&scsi_cmd; + scsi_xfer.retries=8; + scsi_xfer.timeout=10000; + scsi_xfer.cmdlen=sizeof(scsi_cmd); + scsi_xfer.data=0; + scsi_xfer.datalen=0; + scsi_xfer.resid=0; + scsi_xfer.when_done=0; + scsi_xfer.done_arg=0; +retry: scsi_xfer.error=0; + /*******************************************************\ + * do not use interrupts * + \*******************************************************/ + rval = (*(scsi_switch->scsi_cmd))(&scsi_xfer); + if (rval != COMPLETE) + { + if(scsi_debug) + { + printf("scsi error, rval = 0x%x\n",rval); + printf("code from driver: 0x%x\n",scsi_xfer.error); + } + switch(scsi_xfer.error) + { + case XS_SENSE: + /*******************************************************\ + * Any sense value is illegal except UNIT ATTENTION * + * In which case we need to check again to get the * + * correct response. * + *( especially exabytes) * + \*******************************************************/ + if(scsi_xfer.sense.error_class == 7 ) + { + key = scsi_xfer.sense.ext.extended.sense_key ; + switch(key) + { + case 2: /* not ready BUT PRESENT! */ + return(COMPLETE); + case 6: + spinwait(1000); + if(scsi_xfer.retries--) + { + scsi_xfer.flags &= ~ITSDONE; + goto retry; + } + return(COMPLETE); + default: + if(scsi_debug) + printf("%d:%d,key=%x.", + target,lu,key); + } + } + return(HAD_ERROR); + case XS_BUSY: + spinwait(1000); + if(scsi_xfer.retries--) + { + scsi_xfer.flags &= ~ITSDONE; + goto retry; + } + return(COMPLETE); /* it's busy so it's there */ + case XS_TIMEOUT: + default: + return(HAD_ERROR); + } + } + return(COMPLETE); +} +/***********************************************\ +* Do a scsi operation asking a device what it is* +* Use the scsi_cmd routine in the switch table. * +\***********************************************/ +scsi_inquire(unit,target,lu,scsi_switch,inqbuf, flags) +struct scsi_switch *scsi_switch; +u_char *inqbuf; +{ + struct scsi_inquiry scsi_cmd; + struct scsi_xfer scsi_xfer; + volatile int rval; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + bzero(&scsi_xfer, sizeof(scsi_xfer)); + scsi_cmd.op_code = INQUIRY; + scsi_cmd.length = sizeof(struct scsi_inquiry_data); + + scsi_xfer.flags=flags | SCSI_DATA_IN | INUSE; + scsi_xfer.adapter=unit; + scsi_xfer.targ=target; + scsi_xfer.lu=lu; + scsi_xfer.retries=8; + scsi_xfer.timeout=10000; + scsi_xfer.cmd=(struct scsi_generic *)&scsi_cmd; + scsi_xfer.cmdlen= sizeof(struct scsi_inquiry); + scsi_xfer.data=inqbuf; + scsi_xfer.datalen=sizeof(struct scsi_inquiry_data); + scsi_xfer.resid=sizeof(struct scsi_inquiry_data); + scsi_xfer.when_done=0; + scsi_xfer.done_arg=0; +retry: scsi_xfer.error=0; + /*******************************************************\ + * do not use interrupts * + \*******************************************************/ + if ((*(scsi_switch->scsi_cmd))(&scsi_xfer) != COMPLETE) + { + if(scsi_debug) printf("inquiry had error(0x%x) ",scsi_xfer.error); + switch(scsi_xfer.error) + { + case XS_NOERROR: + break; + case XS_SENSE: + /*******************************************************\ + * Any sense value is illegal except UNIT ATTENTION * + * In which case we need to check again to get the * + * correct response. * + *( especially exabytes) * + \*******************************************************/ + if((scsi_xfer.sense.error_class == 7 ) + && (scsi_xfer.sense.ext.extended.sense_key == 6)) + { /* it's changed so it's there */ + spinwait(1000); + { + if(scsi_xfer.retries--) + { + scsi_xfer.flags &= ~ITSDONE; + goto retry; + } + } + return( COMPLETE); + } + return(HAD_ERROR); + case XS_BUSY: + spinwait(1000); + if(scsi_xfer.retries--) + { + scsi_xfer.flags &= ~ITSDONE; + goto retry; + } + case XS_TIMEOUT: + default: + return(HAD_ERROR); + } + } + return(COMPLETE); +} + + + + +/***********************************************\ +* Utility routines often used in SCSI stuff * +\***********************************************/ + +/***********************************************\ +* convert a physical address to 3 bytes, * +* MSB at the lowest address, * +* LSB at the highest. * +\***********************************************/ + +lto3b(val, bytes) +u_char *bytes; +{ + *bytes++ = (val&0xff0000)>>16; + *bytes++ = (val&0xff00)>>8; + *bytes = val&0xff; +} + +/***********************************************\ +* The reverse of lto3b * +\***********************************************/ +_3btol(bytes) +u_char *bytes; +{ + int rc; + rc = (*bytes++ << 16); + rc += (*bytes++ << 8); + rc += *bytes; + return(rc); +} + diff --git a/sys/scsi/scsiconf.h b/sys/scsi/scsiconf.h new file mode 100644 index 0000000..c36efde --- /dev/null +++ b/sys/scsi/scsiconf.h @@ -0,0 +1,113 @@ +/* + * Written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems for use under the MACH(2.5) operating system. + * + * TRW Financial Systems, in accordance with their agreement with Carnegie + * Mellon University, makes this software available to CMU to distribute + * or use in any manner that they see fit as long as this message is kept with + * the software. For this reason TFS also grants any other persons or + * organisations permission to use or modify this software. + * + * TFS supplies this software to be publicly redistributed + * on the understanding that TFS is not responsible for the correct + * functioning of this software in any circumstances. + * + * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE + * -------------------- ----- ---------------------- + * CURRENT PATCH LEVEL: 1 00098 + * -------------------- ----- ---------------------- + * + * 16 Feb 93 Julian Elischer ADDED for SCSI system + * + */ + +/* + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + */ + +/***********************************************\ +* these calls are called by the high-end * +* drivers to get services from whatever low-end * +* drivers they are attached to * +\***********************************************/ +struct scsi_switch +{ + int (*scsi_cmd)(); + void (*scsi_minphys)(); + int (*open_target_lu)(); + int (*close_target_lu)(); + long int (*adapter_info)(); /* see definitions below */ + u_long spare[3]; +}; +#define AD_INF_MAX_CMDS 0x000000FF /* maximum number of entries + queuable to a device by + the adapter */ +/* 24 bits of other adapter charcteristics go here */ + +/***********************************************\ +* The scsi debug control bits * +\***********************************************/ +extern int scsi_debug; +#define PRINTROUTINES 0x01 +#define TRACEOPENS 0x02 +#define TRACEINTERRUPTS 0x04 +#define SHOWREQUESTS 0x08 +#define SHOWSCATGATH 0x10 +#define SHOWINQUIRY 0x20 +#define SHOWCOMMANDS 0x40 + + +/********************************/ +/* return values for scsi_cmd() */ +/********************************/ +#define SUCCESSFULLY_QUEUED 0 +#define TRY_AGAIN_LATER 1 +#define COMPLETE 2 +#define HAD_ERROR 3 + +struct scsi_xfer +{ + struct scsi_xfer *next; /* when free */ + int flags; + u_char adapter; + u_char targ; + u_char lu; + u_char retries; /* the number of times to retry */ + long int timeout; /* in miliseconds */ + struct scsi_generic *cmd; + int cmdlen; + u_char *data; /* either the dma address OR a uio address */ + int datalen; /* data len (blank if uio) */ + int resid; + int (*when_done)(); + int done_arg; + int done_arg2; + int error; + struct buf *bp; + struct scsi_sense_data sense; +}; +/********************************/ +/* Flag values */ +/********************************/ +#define SCSI_NOSLEEP 0x01 /* Not a user... don't sleep */ +#define SCSI_NOMASK 0x02 /* dont allow interrupts.. booting */ +#define SCSI_NOSTART 0x04 /* left over from ancient history */ +#define ITSDONE 0x10 /* the transfer is as done as it gets */ +#define INUSE 0x20 /* The scsi_xfer block is in use */ +#define SCSI_SILENT 0x40 /* Don't report errors to console */ +#define SCSI_ERR_OK 0x80 /* An error on this operation is OK. */ +#define SCSI_RESET 0x100 /* Reset the device in question */ +#define SCSI_DATA_UIO 0x200 /* The data address refers to a UIO */ +#define SCSI_DATA_IN 0x400 /* expect data to come INTO memory */ +#define SCSI_DATA_OUT 0x800 /* expect data to flow OUT of memory */ +#define SCSI_TARGET 0x1000 /* This defines a TARGET mode op. */ +/********************************/ +/* Error values */ +/********************************/ +#define XS_NOERROR 0x0 /* there is no error, (sense is invalid) */ +#define XS_SENSE 0x1 /* Check the returned sense for the error */ +#define XS_DRIVER_STUFFUP 0x2 /* Driver failed to perform operation */ +#define XS_TIMEOUT 0x03 /* The device timed out.. turned off? */ +#define XS_SWTIMEOUT 0x04 /* The Timeout reported was caught by SW */ +#define XS_BUSY 0x08 /* The device busy, try again later? */ + diff --git a/sys/scsi/sd.c b/sys/scsi/sd.c new file mode 100644 index 0000000..af8ae79 --- /dev/null +++ b/sys/scsi/sd.c @@ -0,0 +1,1466 @@ +/* + * Written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems for use under the MACH(2.5) operating system. + * + * TRW Financial Systems, in accordance with their agreement with Carnegie + * Mellon University, makes this software available to CMU to distribute + * or use in any manner that they see fit as long as this message is kept with + * the software. For this reason TFS also grants any other persons or + * organisations permission to use or modify this software. + * + * TFS supplies this software to be publicly redistributed + * on the understanding that TFS is not responsible for the correct + * functioning of this software in any circumstances. + * + * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE + * -------------------- ----- ---------------------- + * CURRENT PATCH LEVEL: 2 00149 + * -------------------- ----- ---------------------- + * + * 16 Feb 93 Julian Elischer ADDED for SCSI system + * 20 Apr 93 Julian Elischer Fixed error reporting + * + */ + +static char rev[] = "$Revision: 1.3 $"; + +/* + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + */ + +#define SPLSD splbio +#define ESUCCESS 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +long int sdstrats,sdqueues; + + +#include +#if NDDB > 0 +int Debugger(); +#else NDDB > 0 +#define Debugger() +#endif NDDB > 0 + + +#define PAGESIZ 4096 +#define SECSIZE 512 +#define PDLOCATION 29 +#define BOOTRECORDSIGNATURE (0x55aa & 0x00ff) +#define SDOUTSTANDING 2 +#define SDQSIZE 4 +#define SD_RETRIES 4 + +#define MAKESDDEV(maj, unit, part) (makedev(maj,((unit<<3)+part))) +#define UNITSHIFT 3 +#define PARTITION(z) (minor(z) & 0x07) +#define RAW_PART 3 +#define UNIT(z) ( (minor(z) >> UNITSHIFT) ) + +#define WHOLE_DISK(unit) ( (unit << UNITSHIFT) + RAW_PART ) + +struct buf sd_buf_queue[NSD]; +int sd_done(); +int sdstrategy(); + +int sd_debug = 0; + +struct scsi_xfer *sd_free_xfer[NSD]; +int sd_xfer_block_wait[NSD]; + +struct sd_data +{ + int flags; +#define SDVALID 0x02 /* PARAMS LOADED */ +#define SDINIT 0x04 /* device has been init'd */ +#define SDWAIT 0x08 /* device has someone waiting */ +#define SDHAVELABEL 0x10 /* have read the label */ +#define SDDOSPART 0x20 /* Have read the DOS partition table */ +#define SDWRITEPROT 0x40 /* Device in readonly mode (S/W)*/ + struct scsi_switch *sc_sw; /* address of scsi low level switch */ + int ctlr; /* so they know which one we want */ + int targ; /* our scsi target ID */ + int lu; /* out scsi lu */ + long int ad_info; /* info about the adapter */ + int cmdscount; /* cmds allowed outstanding by board*/ + int wlabel; /* label is writable */ + struct disk_parms + { + u_char heads; /* Number of heads */ + u_short cyls; /* Number of cylinders */ + u_char sectors;/*dubious*/ /* Number of sectors/track */ + u_short secsiz; /* Number of bytes/sector */ + u_long disksize; /* total number sectors */ + }params; + struct disklabel disklabel; + struct dos_partition dosparts[NDOSPART]; /* DOS view of disk */ + int partflags[MAXPARTITIONS]; /* per partition flags */ +#define SDOPEN 0x01 + int openparts; /* one bit for each open partition */ + unsigned int sd_start_of_unix; /* unix vs dos partitions */ +}sd_data[NSD]; + + +static int next_sd_unit = 0; +/***********************************************************************\ +* The routine called by the low level scsi routine when it discovers * +* A device suitable for this driver * +\***********************************************************************/ + +int sdattach(ctlr,targ,lu,scsi_switch) +struct scsi_switch *scsi_switch; +{ + int unit,i; + unsigned char *tbl; + struct sd_data *sd; + struct disk_parms *dp; + long int ad_info; + struct scsi_xfer *sd_scsi_xfer; + + unit = next_sd_unit++; + sd = sd_data + unit; + dp = &(sd->params); + if(scsi_debug & PRINTROUTINES) printf("sdattach: "); + /*******************************************************\ + * Check we have the resources for another drive * + \*******************************************************/ + if( unit >= NSD) + { + printf("Too many scsi disks..(%d > %d) reconfigure kernel",(unit + 1),NSD); + return(0); + } + /*******************************************************\ + * Store information needed to contact our base driver * + \*******************************************************/ + sd->sc_sw = scsi_switch; + sd->ctlr = ctlr; + sd->targ = targ; + sd->lu = lu; + if(sd->sc_sw->adapter_info) + { + sd->ad_info = ( (*(sd->sc_sw->adapter_info))(ctlr)); + sd->cmdscount = sd->ad_info & AD_INF_MAX_CMDS; + if(sd->cmdscount > SDOUTSTANDING) + { + sd->cmdscount = SDOUTSTANDING; + } + } + else + { + sd->ad_info = 1; + sd->cmdscount = 1; + } + + i = sd->cmdscount; + sd_scsi_xfer = (struct scsi_xfer *)malloc(sizeof(struct scsi_xfer) * i + ,M_TEMP, M_NOWAIT); + while(i-- ) + { + sd_scsi_xfer->next = sd_free_xfer[unit]; + sd_free_xfer[unit] = sd_scsi_xfer; + sd_scsi_xfer++; + } + /*******************************************************\ + * Use the subdriver to request information regarding * + * the drive. We cannot use interrupts yet, so the * + * request must specify this. * + \*******************************************************/ + sd_get_parms(unit, SCSI_NOSLEEP | SCSI_NOMASK); + printf(" sd%d: %dMB, cyls %d, heads %d, secs %d, bytes/sec %d\n", + unit, + ( dp->cyls + * dp->heads + * dp->sectors + * dp->secsiz + ) + / (1024 * 1024), + dp->cyls, + dp->heads, + dp->sectors, + dp->secsiz); + /*******************************************************\ + * Set up the bufs for this device * + \*******************************************************/ + sd->flags |= SDINIT; + return; + +} + + + +/*******************************************************\ +* open the device. Make sure the partition info * +* is a up-to-date as can be. * +\*******************************************************/ +sdopen(dev) +{ + int errcode = 0; + int unit, part; + struct disk_parms disk_parms; + struct sd_data *sd ; + + unit = UNIT(dev); + part = PARTITION(dev); + sd = sd_data + unit; + if(scsi_debug & (PRINTROUTINES | TRACEOPENS)) + printf("sdopen: dev=0x%x (unit %d (of %d),partition %d)\n" + , dev, unit, NSD, part); + /*******************************************************\ + * Check the unit is legal * + \*******************************************************/ + if ( unit >= NSD ) + { + return(ENXIO); + } + /*******************************************************\ + * Make sure the disk has been initialised * + * At some point in the future, get the scsi driver * + * to look for a new device if we are not initted * + \*******************************************************/ + if (! (sd->flags & SDINIT)) + { + return(ENXIO); + } + + /*******************************************************\ + * If it's been invalidated, and not everybody has * + * closed it then forbid re-entry. * + \*******************************************************/ + if ((! (sd->flags & SDVALID)) + && ( sd->openparts)) + return(ENXIO); + /*******************************************************\ + * Check that it is still responding and ok. * + * "unit attention errors should occur here if the drive * + * has been restarted or the pack changed * + \*******************************************************/ + + if(scsi_debug & TRACEOPENS) + printf("device is "); + if (sd_test_unit_ready(unit,0)) + { + if(scsi_debug & TRACEOPENS) printf("not reponding\n"); + return(ENXIO); + } + if(scsi_debug & TRACEOPENS) + printf("ok\n"); + /*******************************************************\ + * In case it is a funny one, tell it to start * + * not needed for most hard drives (ignore failure) * + \*******************************************************/ + sd_start_unit(unit,SCSI_ERR_OK|SCSI_SILENT); + if(scsi_debug & TRACEOPENS) + printf("started "); + /*******************************************************\ + * Load the physical device parameters * + \*******************************************************/ + sd_get_parms(unit, 0); /* sets SDVALID */ + if (sd->params.secsiz != SECSIZE) + { + printf("sd%d: Can't deal with %d bytes logical blocks\n" + ,unit, sd->params.secsiz); + Debugger(); + return(ENXIO); + } + if(scsi_debug & TRACEOPENS) + printf("Params loaded "); + /*******************************************************\ + * Load the partition info if not already loaded * + \*******************************************************/ + sd_prevent(unit,PR_PREVENT,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */ + if((errcode = sdgetdisklabel(unit)) && (part != RAW_PART)) + { + sd_prevent(unit,PR_ALLOW,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */ + return(errcode); + } + if(scsi_debug & TRACEOPENS) + printf("Disklabel loaded "); + /*******************************************************\ + * Check the partition is legal * + \*******************************************************/ + if ( part >= MAXPARTITIONS ) { + sd_prevent(unit,PR_ALLOW,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */ + return(ENXIO); + } + if(scsi_debug & TRACEOPENS) + printf("ok"); + /*******************************************************\ + * Check that the partition exists * + \*******************************************************/ + if (( sd->disklabel.d_partitions[part].p_size == 0 ) + && (part != RAW_PART)) + { + sd_prevent(unit,PR_ALLOW,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */ + return(ENXIO); + } + sd->partflags[part] |= SDOPEN; + sd->openparts |= (1 << part); + if(scsi_debug & TRACEOPENS) + printf("open %d %d\n",sdstrats,sdqueues); + return(0); +} + +/*******************************************************\ +* Get ownership of a scsi_xfer * +* If need be, sleep on it, until it comes free * +\*******************************************************/ +struct scsi_xfer *sd_get_xs(unit,flags) +int flags; +int unit; +{ + struct scsi_xfer *xs; + int s; + + if(flags & (SCSI_NOSLEEP | SCSI_NOMASK)) + { + if (xs = sd_free_xfer[unit]) + { + sd_free_xfer[unit] = xs->next; + xs->flags = 0; + } + } + else + { + s = SPLSD(); + while (!(xs = sd_free_xfer[unit])) + { + sd_xfer_block_wait[unit]++; /* someone waiting! */ + sleep((caddr_t)&sd_free_xfer[unit], PRIBIO+1); + sd_xfer_block_wait[unit]--; + } + sd_free_xfer[unit] = xs->next; + splx(s); + xs->flags = 0; + } + return(xs); +} + +/*******************************************************\ +* Free a scsi_xfer, wake processes waiting for it * +\*******************************************************/ +sd_free_xs(unit,xs,flags) +struct scsi_xfer *xs; +int unit; +int flags; +{ + int s; + + if(flags & SCSI_NOMASK) + { + if (sd_xfer_block_wait[unit]) + { + printf("doing a wakeup from NOMASK mode\n"); + wakeup((caddr_t)&sd_free_xfer[unit]); + } + xs->next = sd_free_xfer[unit]; + sd_free_xfer[unit] = xs; + } + else + { + s = SPLSD(); + if (sd_xfer_block_wait[unit]) + wakeup((caddr_t)&sd_free_xfer[unit]); + xs->next = sd_free_xfer[unit]; + sd_free_xfer[unit] = xs; + splx(s); + } +} + +/*******************************************************\ +* trim the size of the transfer if needed, * +* called by physio * +* basically the smaller of our max and the scsi driver's* +* minphys (note we have no max) * +\*******************************************************/ +/* Trim buffer length if buffer-size is bigger than page size */ +void sdminphys(bp) +struct buf *bp; +{ + (*(sd_data[UNIT(bp->b_dev)].sc_sw->scsi_minphys))(bp); +} + +/*******************************************************\ +* Actually translate the requested transfer into * +* one the physical driver can understand * +* The transfer is described by a buf and will include * +* only one physical transfer. * +\*******************************************************/ + +int sdstrategy(bp) +struct buf *bp; +{ + struct buf *dp; + unsigned int opri; + struct sd_data *sd ; + int unit; + + sdstrats++; + unit = UNIT((bp->b_dev)); + sd = sd_data + unit; + if(scsi_debug & PRINTROUTINES) printf("\nsdstrategy "); + if(scsi_debug & SHOWREQUESTS) printf("sd%d: %d bytes @ blk%d\n", + unit,bp->b_bcount,bp->b_blkno); + sdminphys(bp); + /*******************************************************\ + * If the device has been made invalid, error out * + \*******************************************************/ + if(!(sd->flags & SDVALID)) + { + bp->b_error = EIO; + goto bad; + } + /*******************************************************\ + * "soft" write protect check * + \*******************************************************/ + if ((sd->flags & SDWRITEPROT) && (bp->b_flags & B_READ) == 0) { + bp->b_error = EROFS; + goto bad; + } + /*******************************************************\ + * If it's a null transfer, return immediatly * + \*******************************************************/ + if (bp->b_bcount == 0) + { + goto done; + } + + /*******************************************************\ + * Decide which unit and partition we are talking about * + * only raw is ok if no label * + \*******************************************************/ + if(PARTITION(bp->b_dev) != RAW_PART) + { + if (!(sd->flags & SDHAVELABEL)) + { + bp->b_error = EIO; + goto bad; + } + + /* + * do bounds checking, adjust transfer. if error, process. + * if end of partition, just return + */ + if (bounds_check_with_label(bp,&sd->disklabel,sd->wlabel) <= 0) + goto done; + /* otherwise, process transfer request */ + } + + opri = SPLSD(); + dp = &sd_buf_queue[unit]; + + /*******************************************************\ + * Place it in the queue of disk activities for this disk* + \*******************************************************/ + disksort(dp, bp); + + /*******************************************************\ + * Tell the device to get going on the transfer if it's * + * not doing anything, otherwise just wait for completion* + \*******************************************************/ + sdstart(unit); + + splx(opri); + return; +bad: + bp->b_flags |= B_ERROR; +done: + + /*******************************************************\ + * Correctly set the buf to indicate a completed xfer * + \*******************************************************/ + bp->b_resid = bp->b_bcount; + biodone(bp); + return; +} + +/***************************************************************\ +* sdstart looks to see if there is a buf waiting for the device * +* and that the device is not already busy. If both are true, * +* It deques the buf and creates a scsi command to perform the * +* transfer in the buf. The transfer request will call sd_done * +* on completion, which will in turn call this routine again * +* so that the next queued transfer is performed. * +* The bufs are queued by the strategy routine (sdstrategy) * +* * +* This routine is also called after other non-queued requests * +* have been made of the scsi driver, to ensure that the queue * +* continues to be drained. * +* * +* must be called at the correct (highish) spl level * +\***************************************************************/ +/* sdstart() is called at SPLSD from sdstrategy and sd_done*/ +sdstart(unit) +int unit; +{ + int drivecount; + register struct buf *bp = 0; + register struct buf *dp; + struct scsi_xfer *xs; + struct scsi_rw_big cmd; + int blkno, nblk; + struct sd_data *sd = sd_data + unit; + struct partition *p ; + + if(scsi_debug & PRINTROUTINES) printf("sdstart%d ",unit); + /*******************************************************\ + * See if there is a buf to do and we are not already * + * doing one * + \*******************************************************/ + if(!sd_free_xfer[unit]) + { + return; /* none for us, unit already underway */ + } + + if(sd_xfer_block_wait[unit]) /* there is one, but a special waits */ + { + return; /* give the special that's waiting a chance to run */ + } + + + dp = &sd_buf_queue[unit]; + if ((bp = dp->b_actf) != NULL) /* yes, an assign */ + { + dp->b_actf = bp->av_forw; + } + else + { + return; + } + + xs=sd_get_xs(unit,0); /* ok we can grab it */ + xs->flags = INUSE; /* Now ours */ + /*******************************************************\ + * If the device has become invalid, abort all the * + * reads and writes until all files have been closed and * + * re-openned * + \*******************************************************/ + if(!(sd->flags & SDVALID)) + { + xs->error = XS_DRIVER_STUFFUP; + sd_done(unit,xs); /* clean up (calls sdstart) */ + return ; + } + /*******************************************************\ + * We have a buf, now we should move the data into * + * a scsi_xfer definition and try start it * + \*******************************************************/ + /*******************************************************\ + * First, translate the block to absolute * + \*******************************************************/ + p = sd->disklabel.d_partitions + PARTITION(bp->b_dev); + blkno = bp->b_blkno + p->p_offset; + nblk = (bp->b_bcount + 511) >> 9; + + /*******************************************************\ + * Fill out the scsi command * + \*******************************************************/ + bzero(&cmd, sizeof(cmd)); + cmd.op_code = (bp->b_flags & B_READ) + ? READ_BIG : WRITE_BIG; + cmd.addr_3 = (blkno & 0xff000000) >> 24; + cmd.addr_2 = (blkno & 0xff0000) >> 16; + cmd.addr_1 = (blkno & 0xff00) >> 8; + cmd.addr_0 = blkno & 0xff; + cmd.length2 = (nblk & 0xff00) >> 8; + cmd.length1 = (nblk & 0xff); + /*******************************************************\ + * Fill out the scsi_xfer structure * + * Note: we cannot sleep as we may be an interrupt * + \*******************************************************/ + xs->flags |= SCSI_NOSLEEP; + xs->adapter = sd->ctlr; + xs->targ = sd->targ; + xs->lu = sd->lu; + xs->retries = SD_RETRIES; + xs->timeout = 10000;/* 10000 millisecs for a disk !*/ + xs->cmd = (struct scsi_generic *)&cmd; + xs->cmdlen = sizeof(cmd); + xs->resid = bp->b_bcount; + xs->when_done = sd_done; + xs->done_arg = unit; + xs->done_arg2 = (int)xs; + xs->error = XS_NOERROR; + xs->bp = bp; + xs->data = (u_char *)bp->b_un.b_addr; + xs->datalen = bp->b_bcount; + /*******************************************************\ + * Pass all this info to the scsi driver. * + \*******************************************************/ + + + + if ( (*(sd->sc_sw->scsi_cmd))(xs) != SUCCESSFULLY_QUEUED) + { + printf("sd%d: oops not queued",unit); + xs->error = XS_DRIVER_STUFFUP; + sd_done(unit,xs); /* clean up (calls sdstart) */ + } + sdqueues++; +} + +/*******************************************************\ +* This routine is called by the scsi interrupt when * +* the transfer is complete. +\*******************************************************/ +int sd_done(unit,xs) +int unit; +struct scsi_xfer *xs; +{ + struct buf *bp; + int retval; + int retries = 0; + + if(scsi_debug & PRINTROUTINES) printf("sd_done%d ",unit); + if (! (xs->flags & INUSE)) + panic("scsi_xfer not in use!"); + if(bp = xs->bp) + { + switch(xs->error) + { + case XS_NOERROR: + bp->b_error = 0; + bp->b_resid = 0; + break; + + case XS_SENSE: + retval = (sd_interpret_sense(unit,xs)); + if(retval) + { + bp->b_flags |= B_ERROR; + bp->b_error = retval; + } + break; + + case XS_TIMEOUT: + printf("sd%d timeout\n",unit); + + case XS_BUSY: /* should retry */ /* how? */ + /************************************************/ + /* SHOULD put buf back at head of queue */ + /* and decrement retry count in (*xs) */ + /* HOWEVER, this should work as a kludge */ + /************************************************/ + if(xs->retries--) + { + xs->error = XS_NOERROR; + xs->flags &= ~ITSDONE; + if ( (*(sd_data[unit].sc_sw->scsi_cmd))(xs) + == SUCCESSFULLY_QUEUED) + { /* don't wake the job, ok? */ + return; + } + xs->flags |= ITSDONE; + } /* fall through */ + + case XS_DRIVER_STUFFUP: + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + break; + default: + printf("sd%d: unknown error category from scsi driver\n" + ,unit); + } + biodone(bp); + sd_free_xs(unit,xs,0); + sdstart(unit); /* If there's anything waiting.. do it */ + } + else /* special has finished */ + { + wakeup(xs); + } +} +/*******************************************************\ +* Perform special action on behalf of the user * +* Knows about the internals of this device * +\*******************************************************/ +sdioctl(dev_t dev, int cmd, caddr_t addr, int flag) +{ + /* struct sd_cmd_buf *args;*/ + int error = 0; + unsigned int opri; + unsigned char unit, part; + register struct sd_data *sd; + + + /*******************************************************\ + * Find the device that the user is talking about * + \*******************************************************/ + unit = UNIT(dev); + part = PARTITION(dev); + sd = &sd_data[unit]; + if(scsi_debug & PRINTROUTINES) printf("sdioctl%d ",unit); + + /*******************************************************\ + * If the device is not valid.. abandon ship * + \*******************************************************/ + if (!(sd_data[unit].flags & SDVALID)) + return(EIO); + switch(cmd) + { + + case DIOCSBAD: + error = EINVAL; + break; + + case DIOCGDINFO: + *(struct disklabel *)addr = sd->disklabel; + break; + + case DIOCGPART: + ((struct partinfo *)addr)->disklab = &sd->disklabel; + ((struct partinfo *)addr)->part = + &sd->disklabel.d_partitions[PARTITION(dev)]; + break; + + case DIOCSDINFO: + if ((flag & FWRITE) == 0) + error = EBADF; + else + error = setdisklabel(&sd->disklabel, + (struct disklabel *)addr, + /*(sd->flags & DKFL_BSDLABEL) ? sd->openparts : */0, + sd->dosparts); + if (error == 0) { + sd->flags |= SDHAVELABEL; + } + break; + + case DIOCWLABEL: + sd->flags &= ~SDWRITEPROT; + if ((flag & FWRITE) == 0) + error = EBADF; + else + sd->wlabel = *(int *)addr; + break; + + case DIOCWDINFO: + sd->flags &= ~SDWRITEPROT; + if ((flag & FWRITE) == 0) + error = EBADF; + else + { + if ((error = setdisklabel(&sd->disklabel + , (struct disklabel *)addr + , /*(sd->flags & SDHAVELABEL) ? sd->openparts :*/ 0 + , sd->dosparts)) == 0) + { + int wlab; + + sd->flags |= SDHAVELABEL; /* ok write will succeed */ + + /* simulate opening partition 0 so write succeeds */ + sd->openparts |= (1 << 0); /* XXX */ + wlab = sd->wlabel; + sd->wlabel = 1; + error = writedisklabel(dev, sdstrategy, + &sd->disklabel, sd->dosparts); + sd->wlabel = wlab; + } + } + break; + + + default: + error = ENOTTY; + break; + } + return (error); +} + + +/*******************************************************\ +* Load the label information on the named device * +\*******************************************************/ +int sdgetdisklabel(unit) +unsigned char unit; +{ + /*unsigned int n, m;*/ + char *errstring; + struct dos_partition *dos_partition_p; + struct sd_data *sd = sd_data + unit; + + /*******************************************************\ + * If the inflo is already loaded, use it * + \*******************************************************/ + if(sd->flags & SDHAVELABEL) return(ESUCCESS); + + bzero(&sd->disklabel,sizeof(struct disklabel)); + /*******************************************************\ + * make partition 3 the whole disk in case of failure * + * then get pdinfo * + \*******************************************************/ + sd->disklabel.d_partitions[0].p_offset = 0; + sd->disklabel.d_partitions[0].p_size = sd->params.disksize; + sd->disklabel.d_partitions[RAW_PART].p_offset = 0; + sd->disklabel.d_partitions[RAW_PART].p_size = sd->params.disksize; + sd->disklabel.d_npartitions = MAXPARTITIONS; + sd->disklabel.d_secsize = 512; /* as long as it's not 0 */ + sd->disklabel.d_ntracks = sd->params.heads; + sd->disklabel.d_nsectors = sd->params.sectors; + sd->disklabel.d_ncylinders = sd->params.cyls; + sd->disklabel.d_secpercyl = sd->params.heads * sd->params.sectors; + if (sd->disklabel.d_secpercyl == 0) + { + sd->disklabel.d_secpercyl = 100; + /* as long as it's not 0 */ + /* readdisklabel divides by it */ + } + + /*******************************************************\ + * all the generic bisklabel extraction routine * + \*******************************************************/ + if(errstring = readdisklabel(makedev(0 ,(unit<disklabel + , sd->dosparts + , 0 + , 0)) + { + printf("sd%d: %s\n",unit, errstring); + return(ENXIO); + } + /*******************************************************\ + * leave partition 2 "open" for raw I/O * + \*******************************************************/ + + sd->flags |= SDHAVELABEL; /* WE HAVE IT ALL NOW */ + return(ESUCCESS); +} + +/*******************************************************\ +* Find out from the device what it's capacity is * +\*******************************************************/ +sd_size(unit, flags) +{ + struct scsi_read_cap_data rdcap; + struct scsi_read_capacity scsi_cmd; + int size; + + /*******************************************************\ + * make up a scsi command and ask the scsi driver to do * + * it for you. * + \*******************************************************/ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = READ_CAPACITY; + + /*******************************************************\ + * If the command works, interpret the result as a 4 byte* + * number of blocks * + \*******************************************************/ + if (sd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + &rdcap, + sizeof(rdcap), + 2000, + flags) != 0) + { + printf("could not get size of unit %d\n", unit); + return(0); + } else { + size = rdcap.addr_0 + 1 ; + size += rdcap.addr_1 << 8; + size += rdcap.addr_2 << 16; + size += rdcap.addr_3 << 24; + } + return(size); +} + +/*******************************************************\ +* Get scsi driver to send a "are you ready?" command * +\*******************************************************/ +sd_test_unit_ready(unit,flags) +int unit,flags; +{ + struct scsi_test_unit_ready scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = TEST_UNIT_READY; + + return (sd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 100000, + flags)); +} + +/*******************************************************\ +* Prevent or allow the user to remove the tape * +\*******************************************************/ +sd_prevent(unit,type,flags) +int unit,type,flags; +{ + struct scsi_prevent scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = PREVENT_ALLOW; + scsi_cmd.prevent=type; + return (sd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 5000, + flags) ); +} +/*******************************************************\ +* Get scsi driver to send a "start up" command * +\*******************************************************/ +sd_start_unit(unit,flags) +int unit,flags; +{ + struct scsi_start_stop scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = START_STOP; + scsi_cmd.start = 1; + + return (sd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 2000, + flags)); +} + +/*******************************************************\ +* Tell the device to map out a defective block * +\*******************************************************/ +sd_reassign_blocks(unit,block) +{ + struct scsi_reassign_blocks scsi_cmd; + struct scsi_reassign_blocks_data rbdata; + + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + bzero(&rbdata, sizeof(rbdata)); + scsi_cmd.op_code = REASSIGN_BLOCKS; + + rbdata.length_msb = 0; + rbdata.length_lsb = sizeof(rbdata.defect_descriptor[0]); + rbdata.defect_descriptor[0].dlbaddr_3 = ((block >> 24) & 0xff); + rbdata.defect_descriptor[0].dlbaddr_2 = ((block >> 16) & 0xff); + rbdata.defect_descriptor[0].dlbaddr_1 = ((block >> 8) & 0xff); + rbdata.defect_descriptor[0].dlbaddr_0 = ((block ) & 0xff); + + return(sd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + &rbdata, + sizeof(rbdata), + 5000, + 0)); +} + +#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) + +/*******************************************************\ +* Get the scsi driver to send a full inquiry to the * +* device and use the results to fill out the disk * +* parameter structure. * +\*******************************************************/ + +int sd_get_parms(unit, flags) +{ + struct sd_data *sd = sd_data + unit; + struct disk_parms *disk_parms = &sd->params; + struct scsi_mode_sense scsi_cmd; + struct scsi_mode_sense_data + { + struct scsi_mode_header header; + struct blk_desc blk_desc; + union disk_pages pages; + }scsi_sense; + int sectors; + + /*******************************************************\ + * First check if we have it all loaded * + \*******************************************************/ + if(sd->flags & SDVALID) return(0); + /*******************************************************\ + * First do a mode sense page 3 * + \*******************************************************/ + if (sd_debug) + { + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = MODE_SENSE; + scsi_cmd.page_code = 3; + scsi_cmd.length = 0x24; + /*******************************************************\ + * do the command, but we don't need the results * + * just print them for our interest's sake * + \*******************************************************/ + if (sd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + &scsi_sense, + sizeof(scsi_sense), + 2000, + flags) != 0) + { + printf("could not mode sense (3) for unit %d\n", unit); + return(ENXIO); + } + printf("unit %d: %d trk/zone, %d alt_sec/zone, %d alt_trk/zone, %d alt_trk/lun\n", + unit, + b2tol(scsi_sense.pages.disk_format.trk_z), + b2tol(scsi_sense.pages.disk_format.alt_sec), + b2tol(scsi_sense.pages.disk_format.alt_trk_z), + b2tol(scsi_sense.pages.disk_format.alt_trk_v)); + printf(" %d sec/trk, %d bytes/sec, %d interleave, %d %d bytes/log_blk\n", + b2tol(scsi_sense.pages.disk_format.ph_sec_t), + b2tol(scsi_sense.pages.disk_format.bytes_s), + b2tol(scsi_sense.pages.disk_format.interleave), + sd_size(unit, flags), + _3btol(scsi_sense.blk_desc.blklen)); + } + + + /*******************************************************\ + * do a "mode sense page 4" * + \*******************************************************/ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = MODE_SENSE; + scsi_cmd.page_code = 4; + scsi_cmd.length = 0x20; + /*******************************************************\ + * If the command worked, use the results to fill out * + * the parameter structure * + \*******************************************************/ + if (sd_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + &scsi_sense, + sizeof(scsi_sense), + 2000, + flags) != 0) + { + printf("could not mode sense (4) for unit %d\n", unit); + printf(" using ficticious geometry\n"); + /* use adaptec standard ficticious geometry */ + sectors = sd_size(unit, flags); + disk_parms->heads = 64; + disk_parms->sectors = 32; + disk_parms->cyls = sectors/(64 * 32); + disk_parms->secsiz = SECSIZE; + } + else + { + + if (sd_debug) + { + printf(" %d cyls, %d heads, %d precomp, %d red_write, %d land_zone\n", + _3btol(&scsi_sense.pages.rigid_geometry.ncyl_2), + scsi_sense.pages.rigid_geometry.nheads, + b2tol(scsi_sense.pages.rigid_geometry.st_cyl_wp), + b2tol(scsi_sense.pages.rigid_geometry.st_cyl_rwc), + b2tol(scsi_sense.pages.rigid_geometry.land_zone)); + } + + /*******************************************************\ + * KLUDGE!!(for zone recorded disks) * + * give a number of sectors so that sec * trks * cyls * + * is <= disk_size * + \*******************************************************/ + disk_parms->heads = scsi_sense.pages.rigid_geometry.nheads; + disk_parms->cyls = _3btol(&scsi_sense.pages.rigid_geometry.ncyl_2); + disk_parms->secsiz = _3btol(&scsi_sense.blk_desc.blklen); + + sectors = sd_size(unit, flags); + sectors /= disk_parms->cyls; + sectors /= disk_parms->heads; + disk_parms->sectors = sectors; /* dubious on SCSI*/ + } + + sd->flags |= SDVALID; + return(0); +} + +/*******************************************************\ +* close the device.. only called if we are the LAST * +* occurence of an open device * +\*******************************************************/ +sdclose(dev) +dev_t dev; +{ + unsigned char unit, part; + unsigned int old_priority; + + unit = UNIT(dev); + part = PARTITION(dev); + sd_data[unit].partflags[part] &= ~SDOPEN; + sd_data[unit].openparts &= ~(1 << part); + if(sd_data[unit].openparts == 0) /* if all partitions closed */ + { + sd_prevent(unit,PR_ALLOW,SCSI_SILENT|SCSI_ERR_OK); + } + return(0); +} + +/*******************************************************\ +* ask the scsi driver to perform a command for us. * +* Call it through the switch table, and tell it which * +* sub-unit we want, and what target and lu we wish to * +* talk to. Also tell it where to find the command * +* how long int is. * +* Also tell it where to read/write the data, and how * +* long the data is supposed to be * +\*******************************************************/ +int sd_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags) + +int unit,flags; +struct scsi_generic *scsi_cmd; +int cmdlen; +int timeout; +u_char *data_addr; +int datalen; +{ + struct scsi_xfer *xs; + int retval; + int s; + struct sd_data *sd = sd_data + unit; + + if(scsi_debug & PRINTROUTINES) printf("\nsd_scsi_cmd%d ",unit); + if(sd->sc_sw) /* If we have a scsi driver */ + { + xs = sd_get_xs(unit,flags); /* should wait unless booting */ + if(!xs) + { + printf("sd_scsi_cmd%d: controller busy" + " (this should never happen)\n",unit); + return(EBUSY); + } + xs->flags |= INUSE; + /*******************************************************\ + * Fill out the scsi_xfer structure * + \*******************************************************/ + xs->flags |= flags; + xs->adapter = sd->ctlr; + xs->targ = sd->targ; + xs->lu = sd->lu; + xs->retries = SD_RETRIES; + xs->timeout = timeout; + xs->cmd = scsi_cmd; + xs->cmdlen = cmdlen; + xs->data = data_addr; + xs->datalen = datalen; + xs->resid = datalen; + xs->when_done = (flags & SCSI_NOMASK) + ?(int (*)())0 + :sd_done; + xs->done_arg = unit; + xs->done_arg2 = (int)xs; +retry: xs->error = XS_NOERROR; + xs->bp = 0; + retval = (*(sd->sc_sw->scsi_cmd))(xs); + switch(retval) + { + case SUCCESSFULLY_QUEUED: + s = splbio(); + while(!(xs->flags & ITSDONE)) + sleep(xs,PRIBIO+1); + splx(s); + + case HAD_ERROR: + /*printf("err = %d ",xs->error);*/ + switch(xs->error) + { + case XS_NOERROR: + retval = ESUCCESS; + break; + case XS_SENSE: + retval = (sd_interpret_sense(unit,xs)); + break; + case XS_DRIVER_STUFFUP: + retval = EIO; + break; + case XS_TIMEOUT: + if(xs->retries-- ) + { + xs->flags &= ~ITSDONE; + goto retry; + } + retval = EIO; + break; + case XS_BUSY: + if(xs->retries-- ) + { + xs->flags &= ~ITSDONE; + goto retry; + } + retval = EIO; + break; + default: + retval = EIO; + printf("sd%d: unknown error category from scsi driver\n" + ,unit); + } + break; + case COMPLETE: + retval = ESUCCESS; + break; + case TRY_AGAIN_LATER: + if(xs->retries-- ) + { + xs->flags &= ~ITSDONE; + goto retry; + } + retval = EIO; + break; + default: + retval = EIO; + } + sd_free_xs(unit,xs,flags); + sdstart(unit); /* check if anything is waiting fr the xs */ + } + else + { + printf("sd%d: not set up\n",unit); + return(EINVAL); + } + return(retval); +} +/***************************************************************\ +* Look at the returned sense and act on the error and detirmine * +* The unix error number to pass back... (0 = report no error) * +\***************************************************************/ + +int sd_interpret_sense(unit,xs) +int unit; +struct scsi_xfer *xs; +{ + struct scsi_sense_data *sense; + int key; + int silent; + + /***************************************************************\ + * If the flags say errs are ok, then always return ok. * + \***************************************************************/ + if (xs->flags & SCSI_ERR_OK) return(ESUCCESS); + silent = (xs->flags & SCSI_SILENT); + + sense = &(xs->sense); + switch(sense->error_class) + { + case 7: + { + key=sense->ext.extended.sense_key; + switch(key) + { + case 0x0: + return(ESUCCESS); + case 0x1: + if(!silent) + { + printf("sd%d: soft error(corrected) ", unit); + if(sense->valid) + { + printf("block no. %d (decimal)", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + printf("\n"); + } + return(ESUCCESS); + case 0x2: + if(!silent)printf("sd%d: not ready\n ", + unit); + return(ENODEV); + case 0x3: + if(!silent) + { + printf("sd%d: medium error ", unit); + if(sense->valid) + { + printf("block no. %d (decimal)", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + printf("\n"); + } + return(EIO); + case 0x4: + if(!silent)printf("sd%d: non-media hardware failure\n ", + unit); + return(EIO); + case 0x5: + if(!silent)printf("sd%d: illegal request\n ", + unit); + return(EINVAL); + case 0x6: + /***********************************************\ + * If we are not open, then this is not an error * + * as we don't have state yet. Either way, make * + * sure that we don't have any residual state * + \***********************************************/ + if(!silent)printf("sd%d: Unit attention.\n ", unit); + sd_data[unit].flags &= ~(SDVALID | SDHAVELABEL); + if (sd_data[unit].openparts) + { + return(EIO); + } + return(ESUCCESS); /* not an error if nothing's open */ + case 0x7: + if(!silent) + { + printf("sd%d: attempted protection violation ", + unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + printf("\n"); + } + return(EACCES); + case 0x8: + if(!silent) + { + printf("sd%d: block wrong state (worm)\n ", + unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + printf("\n"); + } + return(EIO); + case 0x9: + if(!silent)printf("sd%d: vendor unique\n", + unit); + return(EIO); + case 0xa: + if(!silent)printf("sd%d: copy aborted\n ", + unit); + return(EIO); + case 0xb: + if(!silent)printf("sd%d: command aborted\n ", + unit); + return(EIO); + case 0xc: + if(!silent) + { + printf("sd%d: search returned\n ", + unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + printf("\n"); + } + return(ESUCCESS); + case 0xd: + if(!silent)printf("sd%d: volume overflow\n ", + unit); + return(ENOSPC); + case 0xe: + if(!silent) + { + printf("sd%d: verify miscompare\n ", + unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + printf("\n"); + } + return(EIO); + case 0xf: + if(!silent)printf("sd%d: unknown error key\n ", + unit); + return(EIO); + } + break; + } + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + { + if(!silent)printf("sd%d: error class %d code %d\n", + unit, + sense->error_class, + sense->error_code); + if(sense->valid) + if(!silent)printf("block no. %d (decimal)\n", + (sense->ext.unextended.blockhi <<16) + + (sense->ext.unextended.blockmed <<8) + + (sense->ext.unextended.blocklow )); + } + return(EIO); + } +} + + + + +int +sdsize(dev_t dev) +{ + int unit = UNIT(dev), part = PARTITION(dev), val; + struct sd_data *sd; + + if (unit >= NSD) + return(-1); + + sd = &sd_data[unit]; + if((sd->flags & SDINIT) == 0) return(-1); + if (sd == 0 || (sd->flags & SDHAVELABEL) == 0) + val = sdopen (MAKESDDEV(major(dev), unit, RAW_PART), FREAD, S_IFBLK, 0); + if ( val != 0 || sd->flags & SDWRITEPROT) + return (-1); + + return((int)sd->disklabel.d_partitions[part].p_size); +} + +sddump() +{ + printf("sddump() -- not implemented\n"); + return(-1); +} + + + + + diff --git a/sys/scsi/st.c b/sys/scsi/st.c new file mode 100644 index 0000000..e896902 --- /dev/null +++ b/sys/scsi/st.c @@ -0,0 +1,1774 @@ +/* + * Written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems for use under the MACH(2.5) operating system. + * + * TRW Financial Systems, in accordance with their agreement with Carnegie + * Mellon University, makes this software available to CMU to distribute + * or use in any manner that they see fit as long as this message is kept with + * the software. For this reason TFS also grants any other persons or + * organisations permission to use or modify this software. + * + * TFS supplies this software to be publicly redistributed + * on the understanding that TFS is not responsible for the correct + * functioning of this software in any circumstances. + * + * + * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE + * -------------------- ----- ---------------------- + * CURRENT PATCH LEVEL: 1 00098 + * -------------------- ----- ---------------------- + * + * 16 Feb 93 Julian Elischer ADDED for SCSI system + */ + +/* + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + */ + + +/* + * To do: + * work out some better way of guessing what a good timeout is going + * to be depending on whether we expect to retension or not. + * + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(OSF) +#define SECSIZE 512 +#endif /* defined(OSF) */ + +#include +#include +#include + + +long int ststrats,stqueues; + + +#define PAGESIZ 4096 +#define STQSIZE 4 +#define ST_RETRIES 4 + + +#define MODE(z) ( (minor(z) & 0x03) ) +#define DSTY(z) ( ((minor(z) >> 2) & 0x03) ) +#define UNIT(z) ( (minor(z) >> 4) ) + +#define DSTY_QIC120 3 +#define DSTY_QIC150 2 +#define DSTY_QIC525 1 + +#define QIC120 0x0f +#define QIC150 0x10 +#define QIC525 0x11 + + + + +#ifndef __386BSD__ +struct buf stbuf[NST][STQSIZE]; /* buffer for raw io (one per device) */ +struct buf *stbuf_free[NST]; /* queue of free buffers for raw io */ +#endif __386BSD__ +struct buf st_buf_queue[NST]; +int ststrategy(); +void stminphys(); +struct scsi_xfer st_scsi_xfer[NST]; +int st_xfer_block_wait[NST]; + +#if defined(OSF) +caddr_t st_window[NST]; +#endif /* defined(OSF) */ +#ifndef MACH +#define ESUCCESS 0 +#endif MACH + +int st_info_valid[NST]; /* the info about the device is valid */ +int st_initialized[NST] ; +int st_debug = 0; + +int stattach(); +int st_done(); +struct st_data +{ + int flags; + struct scsi_switch *sc_sw; /* address of scsi low level switch */ + int ctlr; /* so they know which one we want */ + int targ; /* our scsi target ID */ + int lu; /* out scsi lu */ + int blkmin; /* min blk size */ + int blkmax; /* max blk size */ + int numblks; /* nominal blocks capacity */ + int blksiz; /* nominal block size */ +}st_data[NST]; +#define ST_OPEN 0x01 +#define ST_NOREWIND 0x02 +#define ST_WRITTEN 0x04 +#define ST_FIXEDBLOCKS 0x10 +#define ST_AT_FILEMARK 0x20 +#define ST_AT_EOM 0x40 + +#define ST_PER_ACTION (ST_AT_FILEMARK | ST_AT_EOM) +#define ST_PER_OPEN (ST_OPEN | ST_NOREWIND | ST_WRITTEN | ST_PER_ACTION) +#define ST_PER_MEDIA ST_FIXEDBLOCKS + +static int next_st_unit = 0; +/***********************************************************************\ +* The routine called by the low level scsi routine when it discovers * +* A device suitable for this driver * +\***********************************************************************/ + +int stattach(ctlr,targ,lu,scsi_switch) +struct scsi_switch *scsi_switch; +{ + int unit,i; + unsigned char *tbl; + struct st_data *st; + + if(scsi_debug & PRINTROUTINES) printf("stattach: "); + /*******************************************************\ + * Check we have the resources for another drive * + \*******************************************************/ + unit = next_st_unit++; + if( unit >= NST) + { + printf("Too many scsi tapes..(%d > %d) reconfigure kernel",(unit + 1),NST); + return(0); + } + st = st_data + unit; + /*******************************************************\ + * Store information needed to contact our base driver * + \*******************************************************/ + st->sc_sw = scsi_switch; + st->ctlr = ctlr; + st->targ = targ; + st->lu = lu; + + /*******************************************************\ + * Use the subdriver to request information regarding * + * the drive. We cannot use interrupts yet, so the * + * request must specify this. * + \*******************************************************/ + if((st_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT))) + { + printf(" st%d: scsi tape drive, %d blocks of %d bytes\n", + unit, st->numblks, st->blksiz); + } + else + { + printf(" st%d: scsi tape drive :- offline\n", unit); + } + /*******************************************************\ + * Set up the bufs for this device * + \*******************************************************/ +#ifndef __386BSD__ + stbuf_free[unit] = (struct buf *)0; + for (i = 1; i < STQSIZE; i++) + { + stbuf[unit][i].b_forw = stbuf_free[unit]; + stbuf_free[unit]=&stbuf[unit][i]; + } +#endif __386BSD__ + st_buf_queue[unit].b_active = 0; + st_buf_queue[unit].b_actf = st_buf_queue[unit].b_actl = 0; + st_initialized[unit] = 1; + +#if defined(OSF) + st_window[unit] = (caddr_t)alloc_kva(SECSIZE*256+PAGESIZ); +#endif /* defined(OSF) */ + + return; + +} + + + +/*******************************************************\ +* open the device. * +\*******************************************************/ +stopen(dev) +{ + int errcode = 0; + int unit,mode,dsty; + int dsty_code; + struct st_data *st; + unit = UNIT(dev); + mode = MODE(dev); + dsty = DSTY(dev); + st = st_data + unit; + + /*******************************************************\ + * Check the unit is legal * + \*******************************************************/ + if ( unit >= NST ) + { + errcode = ENXIO; + return(errcode); + } + /*******************************************************\ + * Only allow one at a time * + \*******************************************************/ + if(st->flags & ST_OPEN) + { + errcode = ENXIO; + goto bad; + } + /*******************************************************\ + * Set up the mode flags according to the minor number * + * ensure all open flags are in a known state * + \*******************************************************/ + st->flags &= ~ST_PER_OPEN; + switch(mode) + { + case 2: + case 0: + st->flags &= ~ST_NOREWIND; + break; + case 3: + case 1: + st->flags |= ST_NOREWIND; + break; + default: + printf("st%d: Bad mode (minor number)%d\n",unit,mode); + return(EINVAL); + } + /*******************************************************\ + * Check density code: 0 is drive default * + \*******************************************************/ + switch(dsty) + { + case 0: dsty_code = 0; break; + case DSTY_QIC120: dsty_code = QIC120; break; + case DSTY_QIC150: dsty_code = QIC150; break; + case DSTY_QIC525: dsty_code = QIC525; break; + default: + printf("st%d: Bad density (minor number)%d\n",unit,dsty); + return(EINVAL); + } + if(scsi_debug & (PRINTROUTINES | TRACEOPENS)) + printf("stopen: dev=0x%x (unit %d (of %d))\n" + , dev, unit, NST); + /*******************************************************\ + * Make sure the device has been initialised * + \*******************************************************/ + + if (!st_initialized[unit]) + return(ENXIO); + /*******************************************************\ + * Check that it is still responding and ok. * + \*******************************************************/ + + if(scsi_debug & TRACEOPENS) + printf("device is "); + if (!(st_req_sense(unit, 0))) + { + errcode = ENXIO; + if(scsi_debug & TRACEOPENS) + printf("not responding\n"); + goto bad; + } + if(scsi_debug & TRACEOPENS) + printf("ok\n"); + + if(!(st_test_ready(unit,0))) + { + printf("st%d not ready\n",unit); + return(EIO); + } + + if(!st_info_valid[unit]) /* is media new? */ + if(!st_load(unit,LD_LOAD,0)) + { + return(EIO); + } + + if(!st_rd_blk_lim(unit,0)) + { + return(EIO); + } + + if(!st_mode_sense(unit,0)) + { + return(EIO); + } + + if(!st_mode_select(unit,0,dsty_code)) + { + return(EIO); + } + + st_info_valid[unit] = TRUE; + + st_prevent(unit,PR_PREVENT,0); /* who cares if it fails? */ + + /*******************************************************\ + * Load the physical device parameters * + \*******************************************************/ + if(scsi_debug & TRACEOPENS) + printf("Params loaded "); + + + st->flags |= ST_OPEN; + +bad: + return(errcode); +} + +/*******************************************************\ +* close the device.. only called if we are the LAST * +* occurence of an open device * +\*******************************************************/ +stclose(dev) +{ + unsigned char unit,mode; + struct st_data *st; + + unit = UNIT(dev); + mode = MODE(dev); + st = st_data + unit; + + if(scsi_debug & TRACEOPENS) + printf("Closing device"); + if(st->flags & ST_WRITTEN) + { + st_write_filemarks(unit,1,0); + } + st->flags &= ~ST_WRITTEN; + switch(mode) + { + case 0: + st_rewind(unit,FALSE,SCSI_SILENT); + st_prevent(unit,PR_ALLOW,SCSI_SILENT); + break; + case 1: + st_prevent(unit,PR_ALLOW,SCSI_SILENT); + break; + case 2: + st_rewind(unit,FALSE,SCSI_SILENT); + st_prevent(unit,PR_ALLOW,SCSI_SILENT); + st_load(unit,LD_UNLOAD,SCSI_SILENT); + break; + case 3: + st_prevent(unit,PR_ALLOW,SCSI_SILENT); + st_load(unit,LD_UNLOAD,SCSI_SILENT); + break; + default: + printf("st%d:close: Bad mode (minor number)%d how's it open?\n" + ,unit,mode); + return(EINVAL); + } + st->flags &= ~ST_PER_OPEN; + return(0); +} + +#ifndef __386BSD__ +/*******************************************************\ +* Get ownership of this unit's buf * +* If need be, sleep on it, until it comes free * +\*******************************************************/ +struct buf * +st_get_buf(unit) { + struct buf *rc; + + while (!(rc = stbuf_free[unit])) + sleep((caddr_t)&stbuf_free[unit], PRIBIO+1); + stbuf_free[unit] = stbuf_free[unit]->b_forw; + rc->b_error = 0; + rc->b_resid = 0; + rc->b_flags = 0; + return(rc); +} + +/*******************************************************\ +* Free this unit's buf, wake processes waiting for it * +\*******************************************************/ +st_free_buf(unit,bp) +struct buf *bp; +{ + if (!stbuf_free[unit]) + wakeup((caddr_t)&stbuf_free[unit]); + bp->b_forw = stbuf_free[unit]; + stbuf_free[unit] = bp; +} + + +/*******************************************************\ +* Get the buf for this unit and use physio to do it * +\*******************************************************/ +stread(dev,uio) +register short dev; +struct uio *uio; +{ + int unit = UNIT(dev); + struct buf *bp = st_get_buf(unit); + int rc; + rc = physio(ststrategy, bp, dev, B_READ, stminphys, uio); + st_free_buf(unit,bp); + return(rc); +} + +/*******************************************************\ +* Get the buf for this unit and use physio to do it * +\*******************************************************/ +stwrite(dev,uio) +dev_t dev; +struct uio *uio; +{ + int unit = UNIT(dev); + struct buf *bp = st_get_buf(unit); + int rc; + + rc = physio(ststrategy, bp, dev, B_WRITE, stminphys, uio); + st_free_buf(unit,bp); + return(rc); +} + + +#endif __386BSD__ +/*******************************************************\ +* trim the size of the transfer if needed, * +* called by physio * +* basically the smaller of our min and the scsi driver's* +* minphys * +\*******************************************************/ +void stminphys(bp) +struct buf *bp; +{ + (*(st_data[UNIT(bp->b_dev)].sc_sw->scsi_minphys))(bp); +} + +/*******************************************************\ +* Actually translate the requested transfer into * +* one the physical driver can understand * +* The transfer is described by a buf and will include * +* only one physical transfer. * +\*******************************************************/ + +int ststrategy(bp) +struct buf *bp; +{ + struct buf *dp; + unsigned char unit; + unsigned int opri; + + ststrats++; + unit = UNIT((bp->b_dev)); + if(scsi_debug & PRINTROUTINES) printf("\nststrategy "); + if(scsi_debug & SHOWREQUESTS) printf("st%d: %d bytes @ blk%d\n", + unit,bp->b_bcount,bp->b_blkno); + /*******************************************************\ + * If it's a null transfer, return immediatly * + \*******************************************************/ + if (bp->b_bcount == 0) { + goto done; + } + + /*******************************************************\ + * Odd sized request on fixed drives are verboten * + \*******************************************************/ + if((st_data[unit].flags & ST_FIXEDBLOCKS) + && bp->b_bcount % st_data[unit].blkmin) + { + printf("st%d: bad request, must be multiple of %d\n", + unit, st_data[unit].blkmin); + bp->b_error = EIO; + goto bad; + } + +#ifdef __386BSD__ + stminphys(bp); +#endif __386BSD__ + opri = splbio(); + dp = &st_buf_queue[unit]; + + /*******************************************************\ + * Place it in the queue of disk activities for this tape* + * at the end * + \*******************************************************/ + while ( dp->b_actf) + { + dp = dp->b_actf; + } + dp->b_actf = bp; + bp->b_actf = NULL; + + /*******************************************************\ + * Tell the device to get going on the transfer if it's * + * not doing anything, otherwise just wait for completion* + \*******************************************************/ + ststart(unit); + + splx(opri); + return; +bad: + bp->b_flags |= B_ERROR; +done: + /*******************************************************\ + * Correctly set the buf to indicate a completed xfer * + \*******************************************************/ + iodone(bp); + return; +} + + +/***************************************************************\ +* ststart looks to see if there is a buf waiting for the device * +* and that the device is not already busy. If both are true, * +* It deques the buf and creates a scsi command to perform the * +* transfer in the buf. The transfer request will call st_done * +* on completion, which will in turn call this routine again * +* so that the next queued transfer is performed. * +* The bufs are queued by the strategy routine (ststrategy) * +* * +* This routine is also called after other non-queued requests * +* have been made of the scsi driver, to ensure that the queue * +* continues to be drained. * +\***************************************************************/ +/* ststart() is called at splbio */ +ststart(unit) +{ + int drivecount; + register struct buf *bp = 0; + register struct buf *dp; + struct scsi_xfer *xs; + struct scsi_rw_tape cmd; + int blkno, nblk; + struct st_data *st; + + + st = st_data + unit; + + if(scsi_debug & PRINTROUTINES) printf("ststart%d ",unit); + /*******************************************************\ + * See if there is a buf to do and we are not already * + * doing one * + \*******************************************************/ + xs=&st_scsi_xfer[unit]; + if(xs->flags & INUSE) + { + return; /* unit already underway */ + } +trynext: + if(st_xfer_block_wait[unit]) /* a special awaits, let it proceed first */ + { + wakeup(&st_xfer_block_wait[unit]); + return; + } + + dp = &st_buf_queue[unit]; + if ((bp = dp->b_actf) != NULL) + { + dp->b_actf = bp->b_actf; + } + else /* no work to do */ + { + return; + } + xs->flags = INUSE; /* Now ours */ + + + /*******************************************************\ + * We have a buf, now we should move the data into * + * a scsi_xfer definition and try start it * + \*******************************************************/ + + /*******************************************************\ + * If we are at a filemark but have not reported it yet * + * then we should report it now * + \*******************************************************/ + if(st->flags & ST_AT_FILEMARK) + { + bp->b_error = 0; + bp->b_flags |= B_ERROR; /* EOF*/ + st->flags &= ~ST_AT_FILEMARK; + biodone(bp); + xs->flags = 0; /* won't need it now */ + goto trynext; + } + /*******************************************************\ + * If we are at EOM but have not reported it yet * + * then we should report it now * + \*******************************************************/ + if(st->flags & ST_AT_EOM) + { + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + st->flags &= ~ST_AT_EOM; + biodone(bp); + xs->flags = 0; /* won't need it now */ + goto trynext; + } + /*******************************************************\ + * Fill out the scsi command * + \*******************************************************/ + bzero(&cmd, sizeof(cmd)); + if((bp->b_flags & B_READ) == B_WRITE) + { + st->flags |= ST_WRITTEN; + xs->flags |= SCSI_DATA_OUT; + } + else + { + xs->flags |= SCSI_DATA_IN; + } + cmd.op_code = (bp->b_flags & B_READ) + ? READ_COMMAND_TAPE + : WRITE_COMMAND_TAPE; + + /*******************************************************\ + * Handle "fixed-block-mode" tape drives by using the * + * block count instead of the length. * + \*******************************************************/ + if(st->flags & ST_FIXEDBLOCKS) + { + cmd.fixed = 1; + lto3b(bp->b_bcount/st->blkmin,cmd.len); + } + else + { + lto3b(bp->b_bcount,cmd.len); + } + + /*******************************************************\ + * Fill out the scsi_xfer structure * + * Note: we cannot sleep as we may be an interrupt * + \*******************************************************/ + xs->flags |= SCSI_NOSLEEP; + xs->adapter = st->ctlr; + xs->targ = st->targ; + xs->lu = st->lu; + xs->retries = 1; /* can't retry on tape*/ + xs->timeout = 100000; /* allow 100 secs for retension */ + xs->cmd = (struct scsi_generic *)&cmd; + xs->cmdlen = sizeof(cmd); + xs->data = (u_char *)bp->b_un.b_addr; + xs->datalen = bp->b_bcount; + xs->resid = bp->b_bcount; + xs->when_done = st_done; + xs->done_arg = unit; + xs->done_arg2 = (int)xs; + xs->error = XS_NOERROR; + xs->bp = bp; + /*******************************************************\ + * Pass all this info to the scsi driver. * + \*******************************************************/ + + +#if defined(OSF)||defined(FIX_ME) + if (bp->b_flags & B_PHYS) { + xs->data = (u_char*)map_pva_kva(bp->b_proc, bp->b_un.b_addr, + bp->b_bcount, st_window[unit], + (bp->b_flags&B_READ)?B_WRITE:B_READ); + } else { + xs->data = (u_char*)bp->b_un.b_addr; + } +#endif /* defined(OSF) */ + + if ( (*(st->sc_sw->scsi_cmd))(xs) != SUCCESSFULLY_QUEUED) + { + printf("st%d: oops not queued",unit); + xs->error = XS_DRIVER_STUFFUP; + st_done(unit,xs); + } + stqueues++; +} + +/*******************************************************\ +* This routine is called by the scsi interrupt when * +* the transfer is complete. +\*******************************************************/ +int st_done(unit,xs) +int unit; +struct scsi_xfer *xs; +{ + struct buf *bp; + int retval; + + if(scsi_debug & PRINTROUTINES) printf("st_done%d ",unit); + if (! (xs->flags & INUSE)) + panic("scsi_xfer not in use!"); + if(bp = xs->bp) + { + switch(xs->error) + { + case XS_NOERROR: + bp->b_flags &= ~B_ERROR; + bp->b_error = 0; + bp->b_resid = 0; + break; + case XS_SENSE: + retval = (st_interpret_sense(unit,xs)); + if(retval) + { + /***************************************\ + * We have a real error, the bit should * + * be set to indicate this. The return * + * value will contain the unix error code* + * that the error interpretation routine * + * thought was suitable, so pass this * + * value back in the buf structure. * + * Furthermore we return information * + * saying that no data was transferred * + \***************************************/ + bp->b_flags |= B_ERROR; + bp->b_error = retval; + bp->b_resid = bp->b_bcount; + st_data[unit].flags + &= ~(ST_AT_FILEMARK|ST_AT_EOM); + } + else + { + /***********************************************\ + * The error interpretation code has declared * + * that it wasn't a real error, or at least that * + * we should be ignoring it if it was. * + \***********************************************/ + if(xs->resid && ( xs->resid != xs->datalen )) + { + /***************************************\ + * Here we have the tricky part.. * + * We successfully read less data than * + * we requested. (but not 0) * + *------for variable blocksize tapes:----* + * UNDER 386BSD: * + * We should legitimatly have the error * + * bit set, with the error value set to * + * zero.. This is to indicate to the * + * physio code that while we didn't get * + * as much information as was requested, * + * we did reach the end of the record * + * and so physio should not call us * + * again for more data... we have it all * + * SO SET THE ERROR BIT! * + * * + * UNDER MACH:(CMU) * + * To indicate the same as above, we * + * need only have a non 0 resid that is * + * less than the b_bcount, but the * + * ERROR BIT MUST BE CLEAR! (sigh) * + * * + * UNDER OSF1: * + * To indicate the same as above, we * + * need to have a non 0 resid that is * + * less than the b_bcount, but the * + * ERROR BIT MUST BE SET! (gasp)(sigh) * + * * + *-------for fixed blocksize device------* + * We could have read some successful * + * records before hitting * + * the EOF or EOT. These must be passed * + * to the user, before we report the * + * EOx. Only if there is no data for the * + * user do we report it now. (via an EIO * + * for EOM and resid == count for EOF). * + * We will report the EOx NEXT time.. * + \***************************************/ +#ifdef MACH /*osf and cmu varieties */ +#ifdef OSF + bp->b_flags |= B_ERROR; +#else OSF + bp->b_flags &= ~B_ERROR; +#endif OSF +#endif MACH +#ifdef __386BSD__ + bp->b_flags |= B_ERROR; +#endif __386BSD__ + bp->b_error = 0; + bp->b_resid = xs->resid; + if((st_data[unit].flags & ST_FIXEDBLOCKS)) + { + bp->b_resid *= st_data[unit].blkmin; + if( (st_data[unit].flags & ST_AT_EOM) + && (bp->b_resid == bp->b_bcount)) + { + bp->b_error = EIO; + st_data[unit].flags + &= ~ST_AT_EOM; + } + } + xs->error = XS_NOERROR; + break; + } + else + { + /***************************************\ + * We have come out of the error handler * + * with no error code.. we have also * + * not had an ili (would have gone to * + * the previous clause). Now we need to * + * distiguish between succesful read of * + * no data (EOF or EOM) and successfull * + * read of all requested data. * + * At least all o/s agree that: * + * 0 bytes read with no error is EOF * + * 0 bytes read with an EIO is EOM * + \***************************************/ + + bp->b_resid = bp->b_bcount; + if(st_data[unit].flags & ST_AT_FILEMARK) + { + st_data[unit].flags &= ~ST_AT_FILEMARK; + bp->b_flags &= ~B_ERROR; + bp->b_error = 0; + break; + } + if(st_data[unit].flags & ST_AT_EOM) + { + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + st_data[unit].flags &= ~ST_AT_EOM; + break; + } + printf("st%d:error ignored\n" ,unit); + } + } + break; + + case XS_TIMEOUT: + printf("st%d timeout\n",unit); + break; + + case XS_BUSY: /* should retry */ /* how? */ + /************************************************/ + /* SHOULD put buf back at head of queue */ + /* and decrement retry count in (*xs) */ + /* HOWEVER, this should work as a kludge */ + /************************************************/ + if(xs->retries--) + { + xs->flags &= ~ITSDONE; + xs->error = XS_NOERROR; + if ( (*(st_data[unit].sc_sw->scsi_cmd))(xs) + == SUCCESSFULLY_QUEUED) + { /* don't wake the job, ok? */ + return; + } + printf("device busy"); + xs->flags |= ITSDONE; + } + + case XS_DRIVER_STUFFUP: + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + break; + default: + printf("st%d: unknown error category from scsi driver\n" + ,unit); + } + biodone(bp); + xs->flags = 0; /* no longer in use */ + ststart(unit); /* If there's another waiting.. do it */ + } + else + { + wakeup(xs); + } +} +/*******************************************************\ +* Perform special action on behalf of the user * +* Knows about the internals of this device * +\*******************************************************/ +stioctl(dev, cmd, arg, mode) +dev_t dev; +int cmd; +caddr_t arg; +{ + register i,j; + unsigned int opri; + int errcode = 0; + unsigned char unit; + int number,flags,ret; + + /*******************************************************\ + * Find the device that the user is talking about * + \*******************************************************/ + flags = 0; /* give error messages, act on errors etc. */ + unit = UNIT(dev); + + switch(cmd) + { + + case MTIOCGET: + { + struct mtget *g = (struct mtget *) arg; + + bzero(g, sizeof(struct mtget)); + g->mt_type = 0x7; /* Ultrix compat */ /*?*/ + ret=TRUE; + break; + } + + + case MTIOCTOP: + { + struct mtop *mt = (struct mtop *) arg; + + if (st_debug) + printf("[sctape_sstatus: %x %x]\n", + mt->mt_op, mt->mt_count); + + + + /* compat: in U*x it is a short */ + number = mt->mt_count; + switch ((short)(mt->mt_op)) + { + case MTWEOF: /* write an end-of-file record */ + ret = st_write_filemarks(unit,number,flags); + st_data[unit].flags &= ~ST_WRITTEN; + break; + case MTFSF: /* forward space file */ + ret = st_space(unit,number,SP_FILEMARKS,flags); + break; + case MTBSF: /* backward space file */ + ret = st_space(unit,-number,SP_FILEMARKS,flags); + break; + case MTFSR: /* forward space record */ + ret = st_space(unit,number,SP_BLKS,flags); + break; + case MTBSR: /* backward space record */ + ret = st_space(unit,-number,SP_BLKS,flags); + break; + case MTREW: /* rewind */ + ret = st_rewind(unit,FALSE,flags); + break; + case MTOFFL: /* rewind and put the drive offline */ + if((ret = st_rewind(unit,FALSE,flags))) + { + st_prevent(unit,PR_ALLOW,0); + ret = st_load(unit,LD_UNLOAD,flags); + } + else + { + printf("rewind failed, unit still loaded\n"); + } + break; + case MTNOP: /* no operation, sets status only */ + case MTCACHE: /* enable controller cache */ + case MTNOCACHE: /* disable controller cache */ + ret = TRUE;; + break; + default: + return EINVAL; + } + break; + } + case MTIOCIEOT: + case MTIOCEEOT: + ret=TRUE; + break; + } + + return(ret?ESUCCESS:EIO); +} + + +/*******************************************************\ +* Check with the device that it is ok, (via scsi driver)* +\*******************************************************/ +st_req_sense(unit, flags) +int flags; +{ + struct scsi_sense_data sense; + struct scsi_sense scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = REQUEST_SENSE; + scsi_cmd.length = sizeof(sense); + + if (st_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + &sense, + sizeof(sense), + 100000, + flags | SCSI_DATA_IN) != 0) + { + return(FALSE); + } + else + return(TRUE); +} + +/*******************************************************\ +* Get scsi driver to send a "are you ready" command * +\*******************************************************/ +st_test_ready(unit,flags) +int unit,flags; +{ + struct scsi_test_unit_ready scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = TEST_UNIT_READY; + + if (st_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 100000, + flags) != 0) { + return(FALSE); + } else + return(TRUE); +} + + +#ifdef __STDC__ +#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) +#else +#define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 ) +#endif + +/*******************************************************\ +* Ask the drive what it's min and max blk sizes are. * +\*******************************************************/ +st_rd_blk_lim(unit, flags) +int unit,flags; +{ + struct scsi_blk_limits scsi_cmd; + struct scsi_blk_limits_data scsi_blkl; + struct st_data *st = st_data + unit; + /*******************************************************\ + * First check if we have it all loaded * + \*******************************************************/ + if (st_info_valid[unit]) goto done; + + /*******************************************************\ + * do a 'Read Block Limits' * + \*******************************************************/ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = READ_BLK_LIMITS; + + /*******************************************************\ + * do the command, update the global values * + \*******************************************************/ + if (st_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + &scsi_blkl, + sizeof(scsi_blkl), + 5000, + flags | SCSI_DATA_IN) != 0) + { + if(!(flags & SCSI_SILENT)) + printf("could not get blk limits for unit %d\n", unit); + st_info_valid[unit] = FALSE; + return(FALSE); + } + if (st_debug) + { + printf(" (%d <= blksiz <= %d\n) ", + b2tol(scsi_blkl.min_length), + _3btol(&scsi_blkl.max_length_2)); + } + st->blkmin = b2tol(scsi_blkl.min_length); + st->blkmax = _3btol(&scsi_blkl.max_length_2); + +done: + if(st->blkmin && (st->blkmin == st->blkmax)) + { + st->flags |= ST_FIXEDBLOCKS; + } + return(TRUE); +} +/*******************************************************\ +* Get the scsi driver to send a full inquiry to the * +* device and use the results to fill out the global * +* parameter structure. * +\*******************************************************/ +st_mode_sense(unit, flags) +int unit,flags; +{ + struct scsi_mode_sense scsi_cmd; + struct + { + struct scsi_mode_header_tape header; + struct blk_desc blk_desc; + }scsi_sense; + struct st_data *st = st_data + unit; + + /*******************************************************\ + * First check if we have it all loaded * + \*******************************************************/ + if (st_info_valid[unit]) return(TRUE); + /*******************************************************\ + * First do a mode sense * + \*******************************************************/ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = MODE_SENSE; + scsi_cmd.length = sizeof(scsi_sense); + /*******************************************************\ + * do the command, but we don't need the results * + * just print them for our interest's sake * + \*******************************************************/ + if (st_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + &scsi_sense, + sizeof(scsi_sense), + 5000, + flags | SCSI_DATA_IN) != 0) + { + if(!(flags & SCSI_SILENT)) + printf("could not mode sense for unit %d\n", unit); + st_info_valid[unit] = FALSE; + return(FALSE); + } + if (st_debug) + { + printf("unit %d: %d blocks of %d bytes, write %s, %sbuffered", + unit, + _3btol(&scsi_sense.blk_desc.nblocks), + _3btol(&scsi_sense.blk_desc.blklen), + (scsi_sense.header.write_protected ? + "protected" : "enabled"), + (scsi_sense.header.buf_mode ? + "" : "un") + ); + } + st->numblks = _3btol(&scsi_sense.blk_desc.nblocks); + st->blksiz = _3btol(&scsi_sense.blk_desc.blklen); + return(TRUE); +} + +/*******************************************************\ +* Get the scsi driver to send a full inquiry to the * +* device and use the results to fill out the global * +* parameter structure. * +\*******************************************************/ +st_mode_select(unit, flags, dsty_code) +int unit,flags,dsty_code; +{ + struct scsi_mode_select scsi_cmd; + struct + { + struct scsi_mode_header_tape header; + struct blk_desc blk_desc; + }dat; + struct st_data *st = st_data + unit; + + /*******************************************************\ + * Set up for a mode select * + \*******************************************************/ + bzero(&dat, sizeof(dat)); + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = MODE_SELECT; + scsi_cmd.length = sizeof(dat); + dat.header.blk_desc_len = sizeof(struct blk_desc); + dat.header.buf_mode = 1; + dat.blk_desc.density = dsty_code; + if(st->flags & ST_FIXEDBLOCKS) + { + lto3b( st->blkmin , dat.blk_desc.blklen); + } +/* lto3b( st->numblks , dat.blk_desc.nblocks); use defaults!!!! + lto3b( st->blksiz , dat.blk_desc.blklen); +*/ + /*******************************************************\ + * do the command * + \*******************************************************/ + if (st_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + &dat, + sizeof(dat), + 5000, + flags | SCSI_DATA_OUT) != 0) + { + if(!(flags & SCSI_SILENT)) + printf("could not mode select for unit %d\n", unit); + st_info_valid[unit] = FALSE; + return(FALSE); + } + return(TRUE); +} + +/*******************************************************\ +* skip N blocks/filemarks/seq filemarks/eom * +\*******************************************************/ +st_space(unit,number,what,flags) +int unit,number,what,flags; +{ + struct scsi_space scsi_cmd; + + /* if we are at a filemark now, we soon won't be*/ + st_data[unit].flags &= ~(ST_AT_FILEMARK | ST_AT_EOM); + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = SPACE; + scsi_cmd.code = what; + lto3b(number,scsi_cmd.number); + if (st_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 600000, /* 10 mins enough? */ + flags) != 0) + { + if(!(flags & SCSI_SILENT)) + printf("could not space st%d\n", unit); + st_info_valid[unit] = FALSE; + return(FALSE); + } + return(TRUE); +} +/*******************************************************\ +* write N filemarks * +\*******************************************************/ +st_write_filemarks(unit,number,flags) +int unit,number,flags; +{ + struct scsi_write_filemarks scsi_cmd; + + st_data[unit].flags &= ~(ST_AT_FILEMARK); + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = WRITE_FILEMARKS; + lto3b(number,scsi_cmd.number); + if (st_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 100000, /* 10 secs.. (may need to repos head )*/ + flags) != 0) + { + if(!(flags & SCSI_SILENT)) + printf("could not write_filemarks st%d\n", unit); + st_info_valid[unit] = FALSE; + return(FALSE); + } + return(TRUE); +} +/*******************************************************\ +* load /unload (with retension if true) * +\*******************************************************/ +st_load(unit,type,flags) +int unit,type,flags; +{ + struct scsi_load scsi_cmd; + + st_data[unit].flags &= ~(ST_AT_FILEMARK | ST_AT_EOM); + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = LOAD_UNLOAD; + scsi_cmd.load=type; + if (type == LD_LOAD) + { + /*scsi_cmd.reten=TRUE;*/ + scsi_cmd.reten=FALSE; + } + else + { + scsi_cmd.reten=FALSE; + } + if (st_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 30000, /* 30 secs */ + flags) != 0) + { + if(!(flags & SCSI_SILENT)) + printf("cannot load/unload st%d\n", unit); + st_info_valid[unit] = FALSE; + return(FALSE); + } + return(TRUE); +} +/*******************************************************\ +* Prevent or allow the user to remove the tape * +\*******************************************************/ +st_prevent(unit,type,flags) +int unit,type,flags; +{ + struct scsi_prevent scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = PREVENT_ALLOW; + scsi_cmd.prevent=type; + if (st_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 5000, + flags) != 0) + { + if(!(flags & SCSI_SILENT)) + printf("cannot prevent/allow on st%d\n", unit); + st_info_valid[unit] = FALSE; + return(FALSE); + } + return(TRUE); +} +/*******************************************************\ +* Rewind the device * +\*******************************************************/ +st_rewind(unit,immed,flags) +int unit,immed,flags; +{ + struct scsi_rewind scsi_cmd; + + st_data[unit].flags &= ~(ST_AT_FILEMARK | ST_AT_EOM); + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = REWIND; + scsi_cmd.immed=immed; + if (st_scsi_cmd(unit, + &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + immed?5000:300000, /* 5 sec or 5 min */ + flags) != 0) + { + if(!(flags & SCSI_SILENT)) + printf("could not rewind st%d\n", unit); + st_info_valid[unit] = FALSE; + return(FALSE); + } + return(TRUE); +} +/*******************************************************\ +* ask the scsi driver to perform a command for us. * +* Call it through the switch table, and tell it which * +* sub-unit we want, and what target and lu we wish to * +* talk to. Also tell it where to find the command * +* how long int is. * +* Also tell it where to read/write the data, and how * +* long the data is supposed to be * +\*******************************************************/ +int st_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags) + +int unit,flags; +struct scsi_generic *scsi_cmd; +int cmdlen; +int timeout; +u_char *data_addr; +int datalen; +{ + struct scsi_xfer *xs; + int retval; + int s; + struct st_data *st = st_data + unit; + + if(scsi_debug & PRINTROUTINES) printf("\nst_scsi_cmd%d ",unit); + if(st->sc_sw) /* If we have a scsi driver */ + { + + xs = &(st_scsi_xfer[unit]); + if(!(flags & SCSI_NOMASK)) + s = splbio(); + st_xfer_block_wait[unit]++; /* there is someone waiting */ + while (xs->flags & INUSE) + { + sleep(&st_xfer_block_wait[unit],PRIBIO+1); + } + st_xfer_block_wait[unit]--; + xs->flags = INUSE; + if(!(flags & SCSI_NOMASK)) + splx(s); + + /*******************************************************\ + * Fill out the scsi_xfer structure * + \*******************************************************/ + xs->flags |= flags; + xs->adapter = st->ctlr; + xs->targ = st->targ; + xs->lu = st->lu; + xs->retries = ST_RETRIES; + xs->timeout = timeout; + xs->cmd = scsi_cmd; + xs->cmdlen = cmdlen; + xs->data = data_addr; + xs->datalen = datalen; + xs->resid = datalen; + xs->when_done = (flags & SCSI_NOMASK) + ?(int (*)())0 + :st_done; + xs->done_arg = unit; + xs->done_arg2 = (int)xs; +retry: xs->error = XS_NOERROR; + xs->bp = 0; + retval = (*(st->sc_sw->scsi_cmd))(xs); + switch(retval) + { + case SUCCESSFULLY_QUEUED: + s = splbio(); + while(!(xs->flags & ITSDONE)) + sleep(xs,PRIBIO+1); + splx(s); + + case HAD_ERROR: + case COMPLETE: + switch(xs->error) + { + case XS_NOERROR: + retval = ESUCCESS; + break; + case XS_SENSE: + retval = (st_interpret_sense(unit,xs)); + /* only useful for reads */ + if (retval) + { /* error... don't care about filemarks */ + st->flags &= ~(ST_AT_FILEMARK + | ST_AT_EOM); + } + else + { + xs->error = XS_NOERROR; + retval = ESUCCESS; + } + break; + case XS_DRIVER_STUFFUP: + retval = EIO; + break; + case XS_TIMEOUT: + if(xs->retries-- ) + { + xs->flags &= ~ITSDONE; + goto retry; + } + retval = EIO; + break; + case XS_BUSY: + if(xs->retries-- ) + { + xs->flags &= ~ITSDONE; + goto retry; + } + retval = EIO; + break; + default: + retval = EIO; + printf("st%d: unknown error category from scsi driver\n" + ,unit); + break; + } + break; + case TRY_AGAIN_LATER: + if(xs->retries-- ) + { + xs->flags &= ~ITSDONE; + goto retry; + } + retval = EIO; + break; + default: + retval = EIO; + } + xs->flags = 0; /* it's free! */ + ststart(unit); + } + else + { + printf("st%d: not set up\n",unit); + return(EINVAL); + } + return(retval); +} +/***************************************************************\ +* Look at the returned sense and act on the error and detirmine * +* The unix error number to pass back... (0 = report no error) * +\***************************************************************/ + +int st_interpret_sense(unit,xs) +int unit; +struct scsi_xfer *xs; +{ + struct scsi_sense_data *sense; + int key; + int silent = xs->flags & SCSI_SILENT; + + /***************************************************************\ + * If errors are ok, report a success * + \***************************************************************/ + if(xs->flags & SCSI_ERR_OK) return(ESUCCESS); + + /***************************************************************\ + * Get the sense fields and work out what CLASS * + \***************************************************************/ + sense = &(xs->sense); + if(st_debug) + { + int count = 0; + printf("code%x class%x valid%x\n" + ,sense->error_code + ,sense->error_class + ,sense->valid); + printf("seg%x key%x ili%x eom%x fmark%x\n" + ,sense->ext.extended.segment + ,sense->ext.extended.sense_key + ,sense->ext.extended.ili + ,sense->ext.extended.eom + ,sense->ext.extended.filemark); + printf("info: %x %x %x %x followed by %d extra bytes\n" + ,sense->ext.extended.info[0] + ,sense->ext.extended.info[1] + ,sense->ext.extended.info[2] + ,sense->ext.extended.info[3] + ,sense->ext.extended.extra_len); + printf("extra: "); + while(count < sense->ext.extended.extra_len) + { + printf ("%x ",sense->ext.extended.extra_bytes[count++]); + } + printf("\n"); + } + switch(sense->error_class) + { + /***************************************************************\ + * If it's class 7, use the extended stuff and interpret the key * + \***************************************************************/ + case 7: + { + if(sense->ext.extended.eom) + { + st_data[unit].flags |= ST_AT_EOM; + } + + if(sense->ext.extended.filemark) + { + st_data[unit].flags |= ST_AT_FILEMARK; + } + + if(sense->ext.extended.ili) + { + if(sense->valid) + { + /*******************************\ + * In all ili cases, note that * + * the resid is non-0 AND not * + * unchanged. * + \*******************************/ + xs->resid + = ntohl(*((long *)sense->ext.extended.info)); + if(xs->bp) + { + if(xs->resid < 0) + { /* never on block devices */ + /***********************\ + * it's only really bad * + * if we have lost data * + * (the record was * + * bigger than the read) * + \***********************/ + return(EIO); + } + } + } + else + { /* makes no sense.. complain */ + printf("BAD length error?"); + } + }/* there may be some other error. check the rest */ + + key=sense->ext.extended.sense_key; + switch(key) + { + case 0x0: + return(ESUCCESS); + case 0x1: + if(!silent) + { + printf("st%d: soft error(corrected) ", unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + else + { + printf("\n"); + } + } + return(ESUCCESS); + case 0x2: + if(!silent) printf("st%d: not ready\n ", unit); + return(ENODEV); + case 0x3: + if(!silent) + { + printf("st%d: medium error ", unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + else + { + printf("\n"); + } + } + return(EIO); + case 0x4: + if(!silent) printf("st%d: non-media hardware failure\n ", + unit); + return(EIO); + case 0x5: + if(!silent) printf("st%d: illegal request\n ", unit); + return(EINVAL); + case 0x6: + if(!silent) printf("st%d: Unit attention.\n ", unit); + st_data[unit].flags &= ~(ST_AT_FILEMARK|ST_AT_EOM); + st_info_valid[unit] = FALSE; + if (st_data[unit].flags & ST_OPEN) /* TEMP!!!! */ + return(EIO); + else + return(ESUCCESS); + case 0x7: + if(!silent) + { + printf("st%d: attempted protection violation " + , unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + else + { + printf("\n"); + } + } + return(EACCES); + case 0x8: + if(!silent) + { + printf("st%d: block wrong state (worm)\n " + , unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + else + { + printf("\n"); + } + } + return(EIO); + case 0x9: + if(!silent) printf("st%d: vendor unique\n", + unit); + return(EIO); + case 0xa: + if(!silent) printf("st%d: copy aborted\n ", + unit); + return(EIO); + case 0xb: + if(!silent) printf("st%d: command aborted\n ", + unit); + return(EIO); + case 0xc: + if(!silent) + { + printf("st%d: search returned\n ", unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + else + { + printf("\n"); + } + } + return(ESUCCESS); + case 0xd: + if(!silent) printf("st%d: volume overflow\n ", + unit); + return(ENOSPC); + case 0xe: + if(!silent) + { + printf("st%d: verify miscompare\n ", unit); + if(sense->valid) + { + printf("block no. %d (decimal)\n", + (sense->ext.extended.info[0] <<24)| + (sense->ext.extended.info[1] <<16)| + (sense->ext.extended.info[2] <<8)| + (sense->ext.extended.info[3] )); + } + else + { + printf("\n"); + } + } + return(EIO); + case 0xf: + if(!silent) printf("st%d: unknown error key\n ", + unit); + return(EIO); + } + break; + } + /***************************************************************\ + * If it's NOT class 7, just report it. * + \***************************************************************/ + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + { + if(!silent) printf("st%d: error class %d code %d\n", + unit, + sense->error_class, + sense->error_code); + if(sense->valid) + if(!silent) printf("block no. %d (decimal)\n", + (sense->ext.unextended.blockhi <<16), + + (sense->ext.unextended.blockmed <<8), + + (sense->ext.unextended.blocklow )); + } + return(EIO); + } +} + +#if defined(OSF) + +stsize(dev_t dev) +{ + printf("stsize() -- not implemented\n"); + return(0); +} + +stdump() +{ + printf("stdump() -- not implemented\n"); + return(-1); +} + +#endif /* defined(OSF) */ + -- cgit v1.1