diff options
Diffstat (limited to 'sys/scsi')
-rw-r--r-- | sys/scsi/README | 196 | ||||
-rw-r--r-- | sys/scsi/cd.c | 1317 | ||||
-rw-r--r-- | sys/scsi/ch.c | 487 | ||||
-rw-r--r-- | sys/scsi/scsi_all.h | 340 | ||||
-rw-r--r-- | sys/scsi/scsi_base.c | 896 | ||||
-rw-r--r-- | sys/scsi/scsi_cd.h | 229 | ||||
-rw-r--r-- | sys/scsi/scsi_changer.h | 98 | ||||
-rw-r--r-- | sys/scsi/scsi_debug.h | 53 | ||||
-rw-r--r-- | sys/scsi/scsi_disk.h | 216 | ||||
-rw-r--r-- | sys/scsi/scsi_generic.h | 63 | ||||
-rw-r--r-- | sys/scsi/scsi_ioctl.c | 332 | ||||
-rw-r--r-- | sys/scsi/scsi_tape.h | 204 | ||||
-rw-r--r-- | sys/scsi/scsiconf.c | 699 | ||||
-rw-r--r-- | sys/scsi/scsiconf.h | 249 | ||||
-rw-r--r-- | sys/scsi/sd.c | 1072 | ||||
-rw-r--r-- | sys/scsi/st.c | 1936 | ||||
-rw-r--r-- | sys/scsi/su.c | 4 | ||||
-rw-r--r-- | sys/scsi/uk.c | 158 |
18 files changed, 8549 insertions, 0 deletions
diff --git a/sys/scsi/README b/sys/scsi/README new file mode 100644 index 0000000..b110930 --- /dev/null +++ b/sys/scsi/README @@ -0,0 +1,196 @@ +This release consists of the following files +(relative to the base of the source tree ) + +share/man/man4/scsi.4 <-useful general info +share/man/man4/uk.4 +share/man/man4/su.4 +share/man/man4/ch.4 +share/man/man4/cd.4 +share/man/man4/sd.4 +share/man/man4/st.4 <--READ THIS IF YOU USE TAPES! +sbin/scsi/procargs.c +sbin/scsi/scsi.c +sbin/scsi/scsi.1 +sbin/scsi/Makefile +sbin/st/Makefile +sbin/st/st.1 +sbin/st/st.c +sys/sys/chio.h +sys/sys/cdio.h +sys/sys/mtio.h +sys/sys/scsiio.h +sys/i386/conf/EXAMPLE +sys/i386/isa/ultra14f.c <-runs 14f and 34f +sys/i386/isa/ultra_all.c.beta <-beta version, runs 14f,24f and 34f +sys/i386/isa/bt742a.c +sys/i386/isa/aha1742.c +sys/i386/isa/aha1542.c +sys/scsi/syspatches +sys/scsi/syspatches/conf.c +sys/scsi/syspatches/user_scsi.diffs +sys/scsi/syspatches/MAKEDEV.diff +sys/scsi/syspatches/isa.c.patch +sys/scsi/syspatches/README +sys/scsi/uk.c +sys/scsi/su.c +sys/scsi/st.c +sys/scsi/sd.c +sys/scsi/ch.c +sys/scsi/cd.c +sys/scsi/scsi_ioctl.c +sys/scsi/scsi_base.c +sys/scsi/scsiconf.c +sys/scsi/scsi_tape.h +sys/scsi/scsi_disk.h +sys/scsi/scsi_changer.h +sys/scsi/scsi_cd.h +sys/scsi/scsi_all.h +sys/scsi/scsi_debug.h +sys/scsi/scsiconf.h +sys/scsi/README <--this file + +notice sys/scsi/sg.c and sys/sys/sgio.h have been removed + + +---------------------------------------------------------------- +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 * +Generic scsi-II scanners * +Exabyte tape changer device. +GENERIC SCSI DEVICES (user generated scsi commands) +---------------------------------------------------------------- + + +There are also working bottom end drivers for: +---------------------------------------------------------------- +adaptec 1542 (and 1742 in 1542 mode) +bustec 742a (apparently works for VESA version (445S?))(and 747?) +adaptec 174x (note NOT 27xx) +Ultrastore 14f (works for 34f (VESA version)) +Ultrastore 24f RSN (Beta version included here) +---------------------------------------------------------------- + + +################## 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. + +THE EXCEPTION TO THIS IS IN THE GENERIC SCSI DRIVER. in which case +the following mapping applies: + +BB TTT LLL B= scsi bus number, T = target number, L = LUN. + +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) +In addition, the scsi(1) command allows the operator ask for a +reprobe at any time. Newly found devices will be configured in. Any +device that does not map to a known device type is attached to the +'unknown' (uk) driver. + + +--------------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 + +see st(1) and st(4) for info on tape devices. + +--------------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, sys/sgio and sys/chio (based after sys/mtio for +the ioctls for mag tapes (including st). +General scsi ioctls are found in sys/scsiio.h. + +-----------cd-rom----------------- +The cd rom driver ha been tested by a number of people and +grefen@convex.com has completed the audio play +functions. +(xcdplayer was available from the 'from_ref' directory on agate) + +At this time it is possible audio play is broken on cdroms and I will +be unable to fix it until I get one to test. +***IMPORTANT*** +Cdrom audio is only suported at all for cdroms that use SCSI2 audio +definitions. + +-------------media changer--------------- +Once again courtesy of grefen@convex.com (in germany) +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. + +WARNING: This has not been tested for a LONG TIME! + + +---------recent changes----------- +Removed all bitfields from machine independent sections to make +it possible for them to be used on big-endian architectures. + +Removed scsi specific timeouts in favour of system timeout handling. + +Many structures (getting more all the time) now dynamically allocated. + +Addition of code in the tape driver to recognise models of drive that +have particular problems so they can be handled specially. + +many bug-fixes and cleanups. + +---------even more recent changes:-------- + +rewrote almost the entire thing.. + + + +------Mon Oct 11 22:20:25 WST 1993------ + +Code is now all KNF (or close to it). + +A new structure has been introduced.. +Called scsi_link, one of these exists for every bus/target/lun +that has a driver attached to it. +It has links to the adapter and to the driver, as well as status +information of global interest. (e.g. if the device is in use). +The use of this new structure has allowed the compaction of a +lot of duplicated code into a single copy (now in scsi_base.c) +and makes more simple the USER level scsi implimentation. + + diff --git a/sys/scsi/cd.c b/sys/scsi/cd.c new file mode 100644 index 0000000..899db93 --- /dev/null +++ b/sys/scsi/cd.c @@ -0,0 +1,1317 @@ +/* + * 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. + * + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id: cd.c,v 1.18 1994/04/20 07:06:51 davidg Exp $ + */ + +#define SPLCD splbio +#define ESUCCESS 0 +#include <cd.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/dkbad.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/buf.h> +#include <sys/uio.h> +#include <sys/malloc.h> +#include <sys/cdio.h> + +#include <sys/errno.h> +#include <sys/disklabel.h> +#include <scsi/scsi_all.h> +#include <scsi/scsi_cd.h> +#include <scsi/scsi_disk.h> /* rw_big and start_stop come from there */ +#include <scsi/scsiconf.h> + +/* static function prototypes */ +static errval cd_get_parms(int, int); +static errval cd_get_mode(u_int32, struct cd_mode_data *, u_int32); +static errval cd_set_mode(u_int32 unit, struct cd_mode_data *); +static errval cd_read_toc(u_int32, u_int32, u_int32, struct cd_toc_entry *, + u_int32); + + +int32 cdstrats, cdqueues; + +#include <ddb.h> +#if NDDB > 0 +#else /* NDDB > 0 */ +#define Debugger() +#endif /* NDDB > 0 */ + +#define PAGESIZ 4096 +#define SECSIZE 2048 /* XXX */ /* default only */ +#define CDOUTSTANDING 2 +#define CDRETRIES 1 + +#define UNITSHIFT 3 +#define PARTITION(z) (minor(z) & 0x07) +#define RAW_PART 3 +#define UNIT(z) ( (minor(z) >> UNITSHIFT) ) + +errval cdstrategy(); + +void cdstart(); +struct scsi_device cd_switch = +{ + NULL, /* use default error handler */ + cdstart, /* we have a queue, which is started by this */ + NULL, /* we do not have an async handler */ + NULL, /* use default 'done' routine */ + "cd", /* we are to be refered to by this name */ + 0, /* no device specific flags */ + 0, 0 /* spares not used */ +}; + +struct cd_data { + u_int32 flags; +#define CDINIT 0x04 /* device has been init'd */ + struct scsi_link *sc_link; /* address of scsi low level switch */ + u_int32 cmdscount; /* cmds allowed outstanding by board */ + struct cd_parms { + u_int32 blksize; + u_long disksize; /* total number sectors */ + } params; + struct disklabel disklabel; + u_int32 partflags[MAXPARTITIONS]; /* per partition flags */ +#define CDOPEN 0x01 + u_int32 openparts; /* one bit for each open partition */ + u_int32 xfer_block_wait; + struct buf buf_queue; +}; + +#define CD_STOP 0 +#define CD_START 1 +#define CD_EJECT -2 + +struct cd_driver { + u_int32 size; + struct cd_data **cd_data; +} cd_driver; + +static u_int32 next_cd_unit = 0; + +/* + * The routine called by the low level scsi routine when it discovers + * A device suitable for this driver + */ +int +cdattach(sc_link) + struct scsi_link *sc_link; +{ + u_int32 unit, i; + unsigned char *tbl; + struct cd_data *cd, **cdrealloc; + struct cd_parms *dp; + + SC_DEBUG(sc_link, SDEV_DB2, ("cdattach ")); + + /* + * Fill out any more info in the + * Link structure that we can + */ + unit = next_cd_unit++; + sc_link->device = &cd_switch; + sc_link->dev_unit = unit; + /* + * allocate the resources for another drive + * if we have already allocate a cd_data pointer we must + * copy the old pointers into a new region that is + * larger and release the old region, aka realloc + */ + /* XXX + * This if will always be true for now, but future code may + * preallocate more units to reduce overhead. This would be + * done by changing the malloc to be (next_cd_unit * x) and + * the cd_driver.size++ to be +x + */ + if (unit >= cd_driver.size) { + cdrealloc = + malloc(sizeof(cd_driver.cd_data) * next_cd_unit, + M_DEVBUF, M_NOWAIT); + if (!cdrealloc) { + printf("cd%d: malloc failed for cdrealloc\n", unit); + return (0); + } + /* Make sure we have something to copy before we copy it */ + bzero(cdrealloc, sizeof(cd_driver.cd_data) * next_cd_unit); + if (cd_driver.size) { + bcopy(cd_driver.cd_data, cdrealloc, + sizeof(cd_driver.cd_data) * cd_driver.size); + free(cd_driver.cd_data, M_DEVBUF); + } + cd_driver.cd_data = cdrealloc; + cd_driver.cd_data[unit] = NULL; + cd_driver.size++; + } + if (cd_driver.cd_data[unit]) { + printf("cd%d: Already has storage!\n", unit); + return (0); + } + /* + * allocate the per drive data area + */ + cd = cd_driver.cd_data[unit] = + malloc(sizeof(struct cd_data), M_DEVBUF, M_NOWAIT); + if (!cd) { + printf("cd%d: malloc failed for cd_data\n", unit); + return (0); + } + bzero(cd, sizeof(struct cd_data)); + dp = &(cd->params); + /* + * Store information needed to contact our base driver + */ + cd->sc_link = sc_link; + /* only allow 1 outstanding command on tapes */ + sc_link->opennings = cd->cmdscount = CDOUTSTANDING; + + /* + * 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%d: cd present.[%d x %d byte records]\n", + unit, + cd->params.disksize, + cd->params.blksize); + } else { + printf("cd%d: drive empty\n", unit); + } + cd->flags |= CDINIT; + return (1); +} + +/* + * open the device. Make sure the partition info is a up-to-date as can be. + */ +errval +cdopen(dev) + dev_t dev; +{ + errval errcode = 0; + u_int32 unit, part; + struct cd_parms cd_parms; + struct cd_data *cd; + struct scsi_link *sc_link; + u_int32 heldflags; + + unit = UNIT(dev); + part = PARTITION(dev); + + /* + * Check the unit is legal + */ + if (unit >= cd_driver.size) { + return (ENXIO); + } + cd = cd_driver.cd_data[unit]; + /* + * Make sure the device has been initialised + */ + if ((cd == NULL) || (!(cd->flags & CDINIT))) + return (ENXIO); + + sc_link = cd->sc_link; + SC_DEBUG(sc_link, SDEV_DB1, + ("cdopen: dev=0x%x (unit %d (of %d),partition %d)\n", + dev, unit, cd_driver.size, part)); + /* + * If it's been invalidated, and not everybody has closed it then + * forbid re-entry. (may have changed media) + */ + if ((!(sc_link->flags & SDEV_MEDIA_LOADED)) + && (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 SDEV_MEDIA_LOADED flag is not yet set + */ + scsi_test_unit_ready(sc_link, SCSI_SILENT); + + /* + * Next time actually take notice of error returns + */ + sc_link->flags |= SDEV_OPEN; /* unit attn errors are now errors */ + if (scsi_test_unit_ready(sc_link, SCSI_SILENT) != 0) { + SC_DEBUG(sc_link, SDEV_DB3, ("not ready\n")); + errcode = ENXIO; + goto bad; + } + SC_DEBUG(sc_link, SDEV_DB3, ("Device present\n")); + /* + * In case it is a funny one, tell it to start + * not needed for some drives + */ + scsi_start_unit(sc_link, CD_START); + scsi_prevent(sc_link, PR_PREVENT, SCSI_SILENT); + SC_DEBUG(sc_link, SDEV_DB3, ("started ")); + /* + * Load the physical device parameters + */ + if (cd_get_parms(unit, 0)) { + errcode = ENXIO; + goto bad; + } + SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded ")); + /* + * Make up some partition information + */ + cdgetdisklabel(unit); + SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel fabricated ")); + /* + * Check the partition is legal + */ + if ((part >= cd->disklabel.d_npartitions) + && (part != RAW_PART)) { + SC_DEBUG(sc_link, SDEV_DB3, ("partition %d > %d\n", part + ,cd->disklabel.d_npartitions)); + errcode = ENXIO; + goto bad; + } + /* + * Check that the partition exists + */ + if ((cd->disklabel.d_partitions[part].p_fstype == FS_UNUSED) + && (part != RAW_PART)) { + SC_DEBUG(sc_link, SDEV_DB3, ("part %d type UNUSED\n", part)); + errcode = ENXIO; + goto bad; + } + cd->partflags[part] |= CDOPEN; + cd->openparts |= (1 << part); + SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n")); + sc_link->flags |= SDEV_MEDIA_LOADED; + return (0); + bad: + + /* + * if we would have been the only open + * then leave things back as they were + */ + if (!(cd->openparts)) { + sc_link->flags &= ~SDEV_OPEN; + scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT); + } + return (errcode); +} + +/* + * close the device.. only called if we are the LAST + * occurence of an open device + */ +errval +cdclose(dev) + dev_t dev; +{ + u_int8 unit, part; + u_int32 old_priority; + struct cd_data *cd; + struct scsi_link *sc_link; + + unit = UNIT(dev); + part = PARTITION(dev); + cd = cd_driver.cd_data[unit]; + sc_link = cd->sc_link; + SC_DEBUG(sc_link, SDEV_DB2, ("cd%d: closing part %d\n", unit, part)); + cd->partflags[part] &= ~CDOPEN; + cd->openparts &= ~(1 << part); + + /* + * If we were the last open of the entire device, release it. + */ + if (!(cd->openparts)) { + scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT); + cd->sc_link->flags &= ~SDEV_OPEN; + } + return (0); +} + +/* + * 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_driver.cd_data[UNIT(bp->b_dev)]->sc_link->adapter->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. + */ +errval +cdstrategy(bp) + struct buf *bp; +{ + struct buf *dp; + u_int32 opri; + u_int32 unit = UNIT((bp->b_dev)); + struct cd_data *cd = cd_driver.cd_data[unit]; + + cdstrats++; + SC_DEBUG(cd->sc_link, SDEV_DB2, ("\ncdstrategy ")); + SC_DEBUG(cd->sc_link, SDEV_DB1, ("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->sc_link->flags & SDEV_MEDIA_LOADED)) { + 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) { + /* + * 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 */ + } else { + bp->b_pblkno = bp->b_blkno; + bp->b_resid = 0; + } + opri = SPLCD(); + dp = &cd->buf_queue; + + /* + * Use a bounce buffer if necessary + */ +#ifndef NOBOUNCE + if (cd->sc_link->flags & SDEV_BOUNCE) + vm_bounce_alloc(bp); +#endif + + /* + * 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 0; /* XXX ??? is this the right 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 (0); +} + +/* + * 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 scsi_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 scsi_done + */ +void +cdstart(unit) + u_int32 unit; +{ + register struct buf *bp = 0; + register struct buf *dp; + struct scsi_rw_big cmd; + u_int32 blkno, nblk; + struct partition *p; + struct cd_data *cd = cd_driver.cd_data[unit]; + struct scsi_link *sc_link = cd->sc_link; + + SC_DEBUG(sc_link, SDEV_DB2, ("cdstart%d ", unit)); + /* + * See if there is a buf to do and we are not already + * doing one + */ + if (!sc_link->opennings) { + return; /* no room for us, unit already underway */ + } + if (sc_link->flags & SDEV_WAITING) { /* is room, but a special waits */ + return; /* give the special that's waiting a chance to run */ + } + dp = &cd->buf_queue; + if ((bp = dp->b_actf) != NULL) { /* yes, an assign */ + dp->b_actf = bp->b_actf; + } else { + return; + } + /* + * Should reject all queued entries if SDEV_MEDIA_LOADED is not true. + */ + if (!(sc_link->flags & SDEV_MEDIA_LOADED)) { + goto bad; /* no I/O.. media changed or something */ + } + /* + * We have a buf, now we should make a command + * + * First, translate the block to absolute and put it in terms of the + * logical blocksize of the device. Really a bit silly until we have + * real partitions, but. + */ + blkno = bp->b_blkno / (cd->params.blksize / 512); + if (PARTITION(bp->b_dev) != RAW_PART) { + p = cd->disklabel.d_partitions + PARTITION(bp->b_dev); + blkno += p->p_offset; + } + nblk = (bp->b_bcount + (cd->params.blksize - 1)) / (cd->params.blksize); + /* what if something asks for 512 bytes not on a 2k boundary? *//*XXX */ + + /* + * Fill out the scsi command + */ + bzero(&cmd, sizeof(cmd)); + cmd.op_code = READ_BIG; + cmd.addr_3 = (blkno & 0xff000000UL) >> 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); + + /* + * Call the routine that chats with the adapter. + * Note: we cannot sleep as we may be an interrupt + */ + if (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &cmd, + sizeof(cmd), + (u_char *) bp->b_un.b_addr, + bp->b_bcount, + CDRETRIES, + 30000, + bp, + SCSI_NOSLEEP | ((bp->b_flags & B_READ) ? + SCSI_DATA_IN : SCSI_DATA_OUT)) + != SUCCESSFULLY_QUEUED) { + bad: + printf("cd%d: oops not queued", unit); + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + biodone(bp); + return; + } + cdqueues++; +} + +/* + * Perform special action on behalf of the user. + * Knows about the internals of this device + */ +errval +cdioctl(dev_t dev, int cmd, caddr_t addr, int flag) +{ + errval error = 0; + u_int32 opri; + u_int8 unit, part; + register struct cd_data *cd; + + /* + * Find the device that the user is talking about + */ + unit = UNIT(dev); + part = PARTITION(dev); + cd = cd_driver.cd_data[unit]; + SC_DEBUG(cd->sc_link, SDEV_DB2, ("cdioctl 0x%x ", cmd)); + + /* + * If the device is not valid.. abandon ship + */ + if (!(cd->sc_link->flags & SDEV_MEDIA_LOADED)) + 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; + + /* + * a bit silly, but someone might want to test something on a + * section of cdrom. + */ + case DIOCWDINFO: + case DIOCSDINFO: + if ((flag & FWRITE) == 0) + error = EBADF; + else + error = setdisklabel(&cd->disklabel, + (struct disklabel *) addr, + 0, + 0); + if (error == 0) + 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.flags &= ~CD_PA_SOTC; + data.page.audio.flags |= CD_PA_IMMED; + 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 CDIOCPLAYMSF: + { + struct ioc_play_msf *args + = (struct ioc_play_msf *) addr; + struct cd_mode_data data; + if (error = cd_get_mode(unit, &data, AUDIO_PAGE)) + break; + data.page.audio.flags &= ~CD_PA_SOTC; + data.page.audio.flags |= CD_PA_IMMED; + if (error = cd_set_mode(unit, &data)) + break; + return (cd_play_msf(unit + ,args->start_m + ,args->start_s + ,args->start_f + ,args->end_m + ,args->end_s + ,args->end_f + )); + } + 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.flags &= ~CD_PA_SOTC; + data.page.audio.flags |= CD_PA_IMMED; + 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; + u_int32 len = args->data_len; + if (len > sizeof(data) || + len < sizeof(struct cd_sub_channel_header)) { + error = EINVAL; + break; + } + if (error = cd_read_subchannel(unit, args->address_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: + { /* ??? useless bcopy? XXX */ + struct ioc_toc_header th; + if (error = cd_read_toc(unit, 0, 0, + (struct cd_toc_entry *)&th, + sizeof th)) + break; + th.len = (th.len & 0xff) << 8 + ((th.len >> 8) & 0xff); + bcopy(&th, addr, sizeof th); + } + break; + case CDIOREADTOCENTRYS: + { + struct cd_toc { + struct ioc_toc_header header; + struct cd_toc_entry entries[65]; + } data; + struct ioc_read_toc_entry *te = + (struct ioc_read_toc_entry *) addr; + struct ioc_toc_header *th; + u_int32 len = te->data_len; + th = &data.header; + + if (len > sizeof(data.entries) || len < sizeof(struct cd_toc_entry)) { + error = EINVAL; + break; + } + if (error = cd_read_toc(unit, te->address_format, + te->starting_track, + (struct cd_toc_entry *)&data, + len + sizeof(struct ioc_toc_header))) + break; + len = min(len, ((((th->len & 0xff) << 8) + ((th->len >> 8))) - (sizeof(th->starting_track) + sizeof(th->ending_track)))); + if (copyout(data.entries, 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; /* eh? */ + } + 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].channels = CHANNEL_0; + data.page.audio.port[LEFT_PORT].volume = arg->vol[LEFT_PORT]; + data.page.audio.port[RIGHT_PORT].channels = CHANNEL_1; + 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 = LEFT_CHANNEL; + data.page.audio.port[RIGHT_PORT].channels = LEFT_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 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 = scsi_start_unit(cd->sc_link, 0); + break; + case CDIOCSTOP: + error = scsi_stop_unit(cd->sc_link, 0, 0); + break; + case CDIOCEJECT: + error = scsi_stop_unit(cd->sc_link, 1, 0); + break; + case CDIOCALLOW: + error = scsi_prevent(cd->sc_link, PR_ALLOW, 0); + break; + case CDIOCPREVENT: + error = scsi_prevent(cd->sc_link, PR_PREVENT, 0); + break; + case CDIOCSETDEBUG: + cd->sc_link->flags |= (SDEV_DB1 | SDEV_DB2); + break; + case CDIOCCLRDEBUG: + cd->sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2); + break; + case CDIOCRESET: + return (cd_reset(unit)); + break; + default: + if(part == RAW_PART) + error = scsi_do_ioctl(cd->sc_link,cmd,addr,flag); + else + error = ENOTTY; + break; + } + return (error); +} + +/* + * Load the label information on the named device + * Actually fabricate a disklabel + * + * EVENTUALLY take information about different + * data tracks from the TOC and put it in the disklabel + */ +errval +cdgetdisklabel(unit) + u_int8 unit; +{ + /*unsigned int n, m; */ + char *errstring; + struct cd_data *cd; + + cd = cd_driver.cd_data[unit]; + + bzero(&cd->disklabel, sizeof(struct disklabel)); + /* + * make partition 0 the whole disk + */ + 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; + + /* + * remember that comparisons with the partition are done + * assuming the blocks are 512 bytes so fudge it. + */ + cd->disklabel.d_npartitions = 1; + cd->disklabel.d_partitions[0].p_offset = 0; + cd->disklabel.d_partitions[0].p_size + = cd->params.disksize * (cd->params.blksize / 512); + 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) + */ + return (ESUCCESS); +} + +/* + * Find out from the device what it's capacity is + */ +u_int32 +cd_size(unit, flags) + int unit; + int flags; +{ + struct scsi_read_cd_cap_data rdcap; + struct scsi_read_cd_capacity scsi_cmd; + u_int32 size; + u_int32 blksize; + struct cd_data *cd = cd_driver.cd_data[unit]; + + /* + * 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 and a blocksize + */ + if (scsi_scsi_cmd(cd->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) & rdcap, + sizeof(rdcap), + CDRETRIES, + 20000, /* might be a disk-changer */ + NULL, + SCSI_DATA_IN | flags) != 0) { + printf("cd%d: could not get size\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 (blksize < 512) + blksize = 2048; /* some drives lie ! */ + if (size < 100) + size = 400000; /* ditto */ + SC_DEBUG(cd->sc_link, SDEV_DB3, ("cd%d: %d %d byte blocks\n" + ,unit, size, blksize)); + cd->params.disksize = size; + cd->params.blksize = blksize; + return (size); +} + +/* + * Get the requested page into the buffer given + */ +static errval +cd_get_mode(unit, data, page) + u_int32 unit; + struct cd_mode_data *data; + u_int32 page; +{ + struct scsi_mode_sense scsi_cmd; + errval retval; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + bzero(data, sizeof(*data)); + scsi_cmd.op_code = MODE_SENSE; + scsi_cmd.page = page; + scsi_cmd.length = sizeof(*data) & 0xff; + retval = scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) data, + sizeof(*data), + CDRETRIES, + 20000, /* should be immed */ + NULL, + SCSI_DATA_IN); + return (retval); +} + +/* + * Get the requested page into the buffer given + */ +errval +cd_set_mode(unit, data) + u_int32 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.byte2 |= SMS_PF; + scsi_cmd.length = sizeof(*data) & 0xff; + data->header.data_length = 0; + return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) data, + sizeof(*data), + CDRETRIES, + 20000, /* should be immed */ + NULL, + SCSI_DATA_OUT)); +} + +/* + * Get scsi driver to send a "start playing" command + */ +errval +cd_play(unit, blk, len) + u_int32 unit, blk, len; +{ + struct scsi_play scsi_cmd; + errval 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; + return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + CDRETRIES, + 200000, /* should be immed */ + NULL, + 0)); +} + +/* + * Get scsi driver to send a "start playing" command + */ +errval +cd_play_big(unit, blk, len) + u_int32 unit, blk, len; +{ + struct scsi_play_big scsi_cmd; + errval 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; + return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + CDRETRIES, + 20000, /* should be immed */ + NULL, + 0)); +} + +/* + * Get scsi driver to send a "start playing" command + */ +errval +cd_play_tracks(unit, strack, sindex, etrack, eindex) + u_int32 unit, strack, sindex, etrack, eindex; +{ + struct scsi_play_track scsi_cmd; + errval 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; + return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + CDRETRIES, + 20000, /* should be immed */ + NULL, + 0)); +} + +/* + * Get scsi driver to send a "play msf" command + */ +errval +cd_play_msf(unit, startm, starts, startf, endm, ends, endf) + u_int32 unit, startm, starts, startf, endm, ends, endf; +{ + struct scsi_play_msf scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = PLAY_MSF; + scsi_cmd.start_m = startm; + scsi_cmd.start_s = starts; + scsi_cmd.start_f = startf; + scsi_cmd.end_m = endm; + scsi_cmd.end_s = ends; + scsi_cmd.end_f = endf; + + return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + CDRETRIES, + 2000, + NULL, + 0)); +} + +/* + * Get scsi driver to send a "start up" command + */ +errval +cd_pause(unit, go) + u_int32 unit, go; +{ + struct scsi_pause scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = PAUSE; + scsi_cmd.resume = go; + + return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + CDRETRIES, + 2000, + NULL, + 0)); +} + +/* + * Get scsi driver to send a "RESET" command + */ +errval +cd_reset(unit) + u_int32 unit; +{ + return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link, + 0, + 0, + 0, + 0, + CDRETRIES, + 2000, + NULL, + SCSI_RESET)); +} + +/* + * Read subchannel + */ +errval +cd_read_subchannel(unit, mode, format, track, data, len) + u_int32 unit, mode, format; + int track; + struct cd_sub_channel_info *data; + u_int32 len; +{ + struct scsi_read_subchannel scsi_cmd; + errval error; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + + scsi_cmd.op_code = READ_SUBCHANNEL; + if (mode == CD_MSF_FORMAT) + scsi_cmd.byte2 |= CD_MSF; + scsi_cmd.byte3 = SRS_SUBQ; + scsi_cmd.subchan_format = format; + scsi_cmd.track = track; + scsi_cmd.data_len[0] = (len) >> 8; + scsi_cmd.data_len[1] = (len) & 0xff; + return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(struct scsi_read_subchannel), + (u_char *) data, + len, + CDRETRIES, + 5000, + NULL, + SCSI_DATA_IN)); +} + +/* + * Read table of contents + */ +static errval +cd_read_toc(unit, mode, start, data, len) + u_int32 unit, mode, start; + struct cd_toc_entry *data; + u_int32 len; +{ + struct scsi_read_toc scsi_cmd; + errval error; + u_int32 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.byte2 |= CD_MSF; + scsi_cmd.from_track = start; + scsi_cmd.data_len[0] = (ntoc) >> 8; + scsi_cmd.data_len[1] = (ntoc) & 0xff; + return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(struct scsi_read_toc), + (u_char *) data, + len, + CDRETRIES, + 5000, + NULL, + SCSI_DATA_IN)); +} + +#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. + */ +static errval +cd_get_parms(unit, flags) + int unit; + int flags; +{ + struct cd_data *cd = cd_driver.cd_data[unit]; + + /* + * First check if we have it all loaded + */ + if (cd->sc_link->flags & SDEV_MEDIA_LOADED) + return (0); + /* + * give a number of sectors so that sec * trks * cyls + * is <= disk_size + */ + if (cd_size(unit, flags)) { + cd->sc_link->flags |= SDEV_MEDIA_LOADED; + return (0); + } else { + return (ENXIO); + } +} + +int +cdsize(dev_t dev) +{ + return (-1); +} diff --git a/sys/scsi/ch.c b/sys/scsi/ch.c new file mode 100644 index 0000000..315dab9 --- /dev/null +++ b/sys/scsi/ch.c @@ -0,0 +1,487 @@ +/* + * Written by grefen@????? + * Based on scsi drivers by Julian Elischer (julian@tfs.com) + * + * $Id: ch.c,v 1.7 1993/12/19 00:54:49 wollman Exp $ + */ + +#include <sys/types.h> +#include <ch.h> + +#include <sys/param.h> +#include <sys/systm.h> + +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/chio.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_changer.h> +#include <scsi/scsiconf.h> + +static errval ch_mode_sense(u_int32, u_int32); + +struct scsi_xfer ch_scsi_xfer[NCH]; +u_int32 ch_xfer_block_wait[NCH]; + +#define PAGESIZ 4096 +#define STQSIZE 4 +#define CHRETRIES 2 + +#define MODE(z) ( (minor(z) & 0x0F) ) +#define UNIT(z) ( (minor(z) >> 4) ) + +#define ESUCCESS 0 + +errval chattach(); + +/* + * This driver is so simple it uses all the default services + */ +struct scsi_device ch_switch = +{ + NULL, + NULL, + NULL, + NULL, + "ch", + 0, + 0, 0 +}; + +struct ch_data { + u_int32 flags; + struct scsi_link *sc_link; /* all the inter level info */ + u_int16 chmo; /* Offset of first CHM */ + u_int16 chms; /* No. of CHM */ + u_int16 slots; /* No. of Storage Elements */ + u_int16 sloto; /* Offset of first SE */ + u_int16 imexs; /* No. of Import/Export Slots */ + u_int16 imexo; /* Offset of first IM/EX */ + u_int16 drives; /* No. of CTS */ + u_int16 driveo; /* Offset of first CTS */ + u_int16 rot; /* CHM can rotate */ + u_long op_matrix; /* possible opertaions */ + u_int16 lsterr; /* details of lasterror */ + u_char stor; /* posible Storage locations */ + u_int32 initialized; +} ch_data[NCH]; + +#define CH_OPEN 0x01 +#define CH_KNOWN 0x02 + +static u_int32 next_ch_unit = 0; + +/* + * The routine called by the low level scsi routine when it discovers + * a device suitable for this driver. + */ +errval +chattach(sc_link) + struct scsi_link *sc_link; +{ + u_int32 unit, i, stat; + unsigned char *tbl; + + SC_DEBUG(sc_link, SDEV_DB2, ("chattach: ")); + /* + * Check we have the resources for another drive + */ + unit = next_ch_unit++; + if (unit >= NCH) { + printf("Too many scsi changers..(%d > %d) reconfigure kernel\n", (unit + 1), NCH); + return (0); + } + /* + * Store information needed to contact our base driver + */ + ch_data[unit].sc_link = sc_link; + sc_link->device = &ch_switch; + sc_link->dev_unit = unit; + + /* + * Use the subdriver to request information regarding + * the drive. We cannot use interrupts yet, so the + * request must specify this. + */ + if ((ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK /*| SCSI_SILENT */ ))) { + printf("ch%d: scsi changer :- offline\n", unit); + stat = CH_OPEN; + } else { + printf("ch%d: scsi changer, %d slot(s) %d drive(s) %d arm(s) %d i/e-slot(s)\n", + unit, ch_data[unit].slots, ch_data[unit].drives, ch_data[unit].chms, ch_data[unit].imexs); + stat = CH_KNOWN; + } + ch_data[unit].initialized = 1; + + return 1; + /* XXX ??? is this the right return val? */ +} + +/* + * open the device. + */ +errval +chopen(dev) + dev_t dev; +{ + errval errcode = 0; + u_int32 unit, mode; + struct scsi_link *sc_link; + + unit = UNIT(dev); + mode = MODE(dev); + + /* + * Check the unit is legal + */ + if (unit >= NCH) { + printf("ch%d: ch %d > %d\n", unit, unit, NCH); + errcode = ENXIO; + return (errcode); + } + /* + * Only allow one at a time + */ + if (ch_data[unit].flags & CH_OPEN) { + printf("ch%d: already open\n", unit); + return EBUSY; + } + /* + * Make sure the device has been initialised + */ + if (!ch_data[unit].initialized) + return (ENXIO); + + sc_link = ch_data[unit].sc_link; + + SC_DEBUG(sc_link, SDEV_DB1, ("chopen: dev=0x%x (unit %d (of %d))\n" + ,dev, unit, NCH)); + /* + * Catch any unit attention errors. + */ + scsi_test_unit_ready(sc_link, SCSI_SILENT); + + sc_link->flags |= SDEV_OPEN; + /* + * Check that it is still responding and ok. + */ + if (errcode = (scsi_test_unit_ready(sc_link, 0))) { + printf("ch%d: not ready\n", unit); + sc_link->flags &= ~SDEV_OPEN; + return errcode; + } + /* + * Make sure data is loaded + */ + if (errcode = (ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK))) { + printf("ch%d: scsi changer :- offline\n", unit); + sc_link->flags &= ~SDEV_OPEN; + return (errcode); + } + ch_data[unit].flags = CH_OPEN; + return 0; +} + +/* + * close the device.. only called if we are the LAST + * occurence of an open device + */ +errval +chclose(dev) + dev_t dev; +{ + unsigned char unit, mode; + struct scsi_link *sc_link; + + unit = UNIT(dev); + mode = MODE(dev); + sc_link = ch_data[unit].sc_link; + + SC_DEBUG(sc_link, SDEV_DB1, ("Closing device")); + ch_data[unit].flags = 0; + sc_link->flags &= ~SDEV_OPEN; + return (0); +} + +/* + * Perform special action on behalf of the user + * Knows about the internals of this device + */ +errval +chioctl(dev, cmd, arg, mode) + dev_t dev; + u_int32 cmd; + caddr_t arg; + int mode; +{ + /* struct ch_cmd_buf *args; */ + union scsi_cmd *scsi_cmd; + register i, j; + u_int32 opri; + errval errcode = 0; + unsigned char unit; + u_int32 number, flags; + errval ret; + struct scsi_link *sc_link; + + /* + * Find the device that the user is talking about + */ + flags = 0; /* give error messages, act on errors etc. */ + unit = UNIT(dev); + sc_link = ch_data[unit].sc_link; + + switch ((int)cmd) { + case CHIOOP:{ + struct chop *ch = (struct chop *) arg; + SC_DEBUG(sc_link, SDEV_DB2, + ("[chtape_chop: %x]\n", ch->ch_op)); + + switch ((short) (ch->ch_op)) { + case CHGETPARAM: + ch->u.getparam.chmo = ch_data[unit].chmo; + ch->u.getparam.chms = ch_data[unit].chms; + ch->u.getparam.sloto = ch_data[unit].sloto; + ch->u.getparam.slots = ch_data[unit].slots; + ch->u.getparam.imexo = ch_data[unit].imexo; + ch->u.getparam.imexs = ch_data[unit].imexs; + ch->u.getparam.driveo = ch_data[unit].driveo; + ch->u.getparam.drives = ch_data[unit].drives; + ch->u.getparam.rot = ch_data[unit].rot; + ch->result = 0; + return 0; + break; + case CHPOSITION: + return ch_position(unit, &ch->result, ch->u.position.chm, + ch->u.position.to, + flags); + case CHMOVE: + return ch_move(unit, &ch->result, ch->u.position.chm, + ch->u.move.from, ch->u.move.to, + flags); + case CHGETELEM: + return ch_getelem(unit, &ch->result, ch->u.get_elem_stat.type, + ch->u.get_elem_stat.from, &ch->u.get_elem_stat.elem_data, + flags); + default: + return EINVAL; + } + } + default: + return scsi_do_ioctl(sc_link, cmd, arg, mode); + } + return (ret ? ESUCCESS : EIO); +} + +errval +ch_getelem(unit, stat, type, from, data, flags) + u_int32 unit, from, flags; + int type; + short *stat; + char *data; +{ + struct scsi_read_element_status scsi_cmd; + char elbuf[32]; + errval ret; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = READ_ELEMENT_STATUS; + scsi_cmd.byte2 = type; + scsi_cmd.starting_element_addr[0] = (from >> 8) & 0xff; + scsi_cmd.starting_element_addr[1] = from & 0xff; + scsi_cmd.number_of_elements[1] = 1; + scsi_cmd.allocation_length[2] = 32; + + if ((ret = scsi_scsi_cmd(ch_data[unit].sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) elbuf, + 32, + CHRETRIES, + 100000, + NULL, + SCSI_DATA_IN | flags) != ESUCCESS)) { + *stat = ch_data[unit].lsterr; + bcopy(elbuf + 16, data, 16); + return ret; + } + bcopy(elbuf + 16, data, 16); /*Just a hack sh */ + return ret; +} + +errval +ch_move(unit, stat, chm, from, to, flags) + u_int32 unit, chm, from, to, flags; + short *stat; +{ + struct scsi_move_medium scsi_cmd; + errval ret; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = MOVE_MEDIUM; + scsi_cmd.transport_element_address[0] = (chm >> 8) & 0xff; + scsi_cmd.transport_element_address[1] = chm & 0xff; + scsi_cmd.source_address[0] = (from >> 8) & 0xff; + scsi_cmd.source_address[1] = from & 0xff; + scsi_cmd.destination_address[0] = (to >> 8) & 0xff; + scsi_cmd.destination_address[1] = to & 0xff; + scsi_cmd.invert = (chm & CH_INVERT) ? 1 : 0; + if ((ret = scsi_scsi_cmd(ch_data[unit].sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + NULL, + 0, + CHRETRIES, + 100000, + NULL, + flags) != ESUCCESS)) { + *stat = ch_data[unit].lsterr; + return ret; + } + return ret; +} + +errval +ch_position(unit, stat, chm, to, flags) + u_int32 unit, chm, to, flags; + short *stat; +{ + struct scsi_position_to_element scsi_cmd; + errval ret; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = POSITION_TO_ELEMENT; + scsi_cmd.transport_element_address[0] = (chm >> 8) & 0xff; + scsi_cmd.transport_element_address[1] = chm & 0xff; + scsi_cmd.source_address[0] = (to >> 8) & 0xff; + scsi_cmd.source_address[1] = to & 0xff; + scsi_cmd.invert = (chm & CH_INVERT) ? 1 : 0; + if ((ret = scsi_scsi_cmd(ch_data[unit].sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + NULL, + 0, + CHRETRIES, + 100000, + NULL, + flags) != ESUCCESS)) { + *stat = ch_data[unit].lsterr; + return ret; + } + return ret; +} + +#ifdef __STDC__ +#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) +#else +#define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 ) +#endif + +/* + * Get the scsi driver to send a full inquiry to the + * device and use the results to fill out the global + * parameter structure. + */ +static errval +ch_mode_sense(unit, flags) + u_int32 unit, flags; +{ + struct scsi_mode_sense scsi_cmd; + u_char scsi_sense[128]; /* Can't use scsi_mode_sense_data because of + * missing block descriptor + */ + u_char *b; + int32 i, l; + errval errcode; + struct scsi_link *sc_link = ch_data[unit].sc_link; + + /* + * First check if we have it all loaded + */ + if (sc_link->flags & SDEV_MEDIA_LOADED) + return 0; + + /* + * First do a mode sense + */ + /* sc_link->flags &= ~SDEV_MEDIA_LOADED; *//*XXX */ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = MODE_SENSE; + scsi_cmd.byte2 = SMS_DBD; + scsi_cmd.page = 0x3f; /* All Pages */ + scsi_cmd.length = sizeof(scsi_sense); + + /* + * Read in the pages + */ + if (errcode = scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(struct scsi_mode_sense), + (u_char *) & scsi_sense, + sizeof (scsi_sense), + CHRETRIES, + 5000, + NULL, + flags | SCSI_DATA_IN) != 0) { + if (!(flags & SCSI_SILENT)) + printf("ch%d: could not mode sense\n", unit); + return (errcode); + } + sc_link->flags |= SDEV_MEDIA_LOADED; + l = scsi_sense[0] - 3; + b = &scsi_sense[4]; + + /* + * To avoid alignment problems + */ +/* XXX - FIX THIS FOR MSB */ +#define p2copy(valp) (valp[1]+ (valp[0]<<8));valp+=2 +#define p4copy(valp) (valp[3]+ (valp[2]<<8) + (valp[1]<<16) + (valp[0]<<24));valp+=4 +#if 0 + printf("\nmode_sense %d\n", l); + for (i = 0; i < l + 4; i++) { + printf("%x%c", scsi_sense[i], i % 8 == 7 ? '\n' : ':'); + } printf("\n"); +#endif + for (i = 0; i < l;) { + u_int32 pc = (*b++) & 0x3f; + u_int32 pl = *b++; + u_char *bb = b; + switch ((int)pc) { + case 0x1d: + ch_data[unit].chmo = p2copy(bb); + ch_data[unit].chms = p2copy(bb); + ch_data[unit].sloto = p2copy(bb); + ch_data[unit].slots = p2copy(bb); + ch_data[unit].imexo = p2copy(bb); + ch_data[unit].imexs = p2copy(bb); + ch_data[unit].driveo = p2copy(bb); + ch_data[unit].drives = p2copy(bb); + break; + case 0x1e: + ch_data[unit].rot = (*b) & 1; + break; + case 0x1f: + ch_data[unit].stor = *b & 0xf; + bb += 2; + ch_data[unit].stor = p4copy(bb); + break; + default: + break; + } + b += pl; + i += pl + 2; + } + SC_DEBUG(sc_link, SDEV_DB2, + (" cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n", + ch_data[unit].chmo, ch_data[unit].chms, + ch_data[unit].sloto, ch_data[unit].slots, + ch_data[unit].imexo, ch_data[unit].imexs, + ch_data[unit].driveo, ch_data[unit].drives, + ch_data[unit].rot ? "can" : "can't")); + return (0); +} diff --git a/sys/scsi/scsi_all.h b/sys/scsi/scsi_all.h new file mode 100644 index 0000000..2e7bdcf --- /dev/null +++ b/sys/scsi/scsi_all.h @@ -0,0 +1,340 @@ +/* + * SCSI general interface description + */ + +/* + * Largely written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems. + * + * 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. + * + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id: scsi_all.h,v 2.0 93/10/06 21:10:28 julian Exp Locker: julian $ + */ + +#ifndef _SCSI_SCSI_ALL_H +#define _SCSI_SCSI_ALL_H 1 +/* + * SCSI command format + */ + +/* + * Define dome bits that are in ALL (or a lot of) scsi commands + */ +#define SCSI_CTL_LINK 0x01 +#define SCSI_CTL_FLAG 0x02 +#define SCSI_CTL_VENDOR 0xC0 +#define SCSI_CMD_LUN 0xA0 /* these two should not be needed */ +#define SCSI_CMD_LUN_SHIFT 5 /* LUN in the cmd is no longer SCSI */ + + +struct scsi_generic +{ + u_char opcode; + u_char bytes[11]; +}; + +struct scsi_test_unit_ready +{ + u_char op_code; + u_char byte2; + u_char unused[3]; + u_char control; +}; + +struct scsi_send_diag +{ + u_char op_code; + u_char byte2; +#define SSD_UOL 0x01 +#define SSD_DOL 0x02 +#define SSD_SELFTEST 0x04 +#define SSD_PF 0x10 + u_char unused[1]; + u_char paramlen[2]; + u_char control; +}; + +struct scsi_sense +{ + u_char op_code; + u_char byte2; + u_char unused[2]; + u_char length; + u_char control; +}; + +struct scsi_inquiry +{ + u_char op_code; + u_char byte2; + u_char unused[2]; + u_char length; + u_char control; +}; + +struct scsi_mode_sense +{ + u_char op_code; + u_char byte2; +#define SMS_DBD 0x08 + u_char page; +#define SMS_PAGE_CODE 0x3F +#define SMS_PAGE_CTRL 0xC0 +#define SMS_PAGE_CTRL_CURRENT 0x00 +#define SMS_PAGE_CTRL_CHANGEABLE 0x40 +#define SMS_PAGE_CTRL_DEFAULT 0x80 +#define SMS_PAGE_CTRL_SAVED 0xC0 + u_char unused; + u_char length; + u_char control; +}; + +struct scsi_mode_sense_big +{ + u_char op_code; + u_char byte2; /* same bits as small version */ + u_char page; /* same bits as small version */ + u_char unused[4]; + u_char length[2]; + u_char control; +}; + +struct scsi_mode_select +{ + u_char op_code; + u_char byte2; +#define SMS_SP 0x01 +#define SMS_PF 0x10 + u_char unused[2]; + u_char length; + u_char control; +}; + +struct scsi_mode_select_big +{ + u_char op_code; + u_char byte2; /* same bits as small version */ + u_char unused[5]; + u_char length[2]; + u_char control; +}; + +struct scsi_reserve +{ + u_char op_code; + u_char byte2; + u_char unused[2]; + u_char length; + u_char control; +}; + +struct scsi_release +{ + u_char op_code; + u_char byte2; + u_char unused[2]; + u_char length; + u_char control; +}; + +struct scsi_prevent +{ + u_char op_code; + u_char byte2; + u_char unused[2]; + u_char how; + u_char control; +}; +#define PR_PREVENT 0x01 +#define PR_ALLOW 0x00 + +struct scsi_changedef +{ + u_char op_code; + u_char byte2; + u_char unused1; + u_char how; + u_char unused[4]; + u_char datalen; + u_char control; +}; +#define SC_SCSI_1 0x01 +#define SC_SCSI_2 0x03 + +/* + * Opcodes + */ + +#define TEST_UNIT_READY 0x00 +#define REQUEST_SENSE 0x03 +#define INQUIRY 0x12 +#define MODE_SELECT 0x15 +#define MODE_SENSE 0x1a +#define START_STOP 0x1b +#define RESERVE 0x16 +#define RELEASE 0x17 +#define PREVENT_ALLOW 0x1e +#define POSITION_TO_ELEMENT 0x2b +#define CHANGE_DEFINITION 0x40 +#define MODE_SENSE_BIG 0x54 +#define MODE_SELECT_BIG 0x55 +#define MOVE_MEDIUM 0xa5 +#define READ_ELEMENT_STATUS 0xb8 + + +/* + * sense data format + */ +#define T_DIRECT 0 +#define T_SEQUENTIAL 1 +#define T_PRINTER 2 +#define T_PROCESSOR 3 +#define T_WORM 4 +#define T_READONLY 5 +#define T_SCANNER 6 +#define T_OPTICAL 7 +#define T_NODEVICE 0x1F + +#define T_CHANGER 8 +#define T_COMM 9 + +#define T_REMOV 1 +#define T_FIXED 0 + +struct scsi_inquiry_data +{ + u_char device; +#define SID_TYPE 0x1F +#define SID_QUAL 0xE0 +#define SID_QUAL_LU_OK 0x00 +#define SID_QUAL_LU_OFFLINE 0x20 +#define SID_QUAL_RSVD 0x40 +#define SID_QUAL_BAD_LU 0x60 + u_char dev_qual2; +#define SID_QUAL2 0x7F +#define SID_REMOVABLE 0x80 + u_char version; +#define SID_ANSII 0x07 +#define SID_ECMA 0x38 +#define SID_ISO 0xC0 + u_char response_format; + u_char additional_length; + u_char unused[2]; + u_char flags; +#define SID_SftRe 0x01 +#define SID_CmdQue 0x02 +#define SID_Linked 0x08 +#define SID_Sync 0x10 +#define SID_WBus16 0x20 +#define SID_WBus32 0x40 +#define SID_RelAdr 0x80 + char vendor[8]; + char product[16]; + char revision[4]; + u_char extra[8]; +}; + + +struct scsi_sense_data +{ +/* 1*/ u_char error_code; /* same bits as new version */ + union + { + struct + { +/* 2*/ u_char blockhi; +/* 3*/ u_char blockmed; +/* 4*/ u_char blocklow; + } unextended; + struct + { +/* 2*/ u_char segment; +/* 3*/ u_char flags; /* same bits as new version */ +/* 7*/ u_char info[4]; +/* 8*/ u_char extra_len; + /* allocate enough room to hold new stuff + ( by increasing 16 to 24 below) */ +/*32*/ u_char extra_bytes[24]; + } extended; + }ext; +}; /* total of 32 bytes */ +struct scsi_sense_data_new +{ +/* 1*/ u_char error_code; +#define SSD_ERRCODE 0x7F +#define SSD_ERRCODE_VALID 0x80 + union + { + struct /* this is deprecated, the standard says "DON'T"*/ + { +/* 2*/ u_char blockhi; +/* 3*/ u_char blockmed; +/* 4*/ u_char blocklow; + } unextended; + struct + { +/* 2*/ u_char segment; +/* 3*/ u_char flags; +#define SSD_KEY 0x0F +#define SSD_ILI 0x20 +#define SSD_EOM 0x40 +#define SSD_FILEMARK 0x80 +/* 7*/ u_char info[4]; +/* 8*/ u_char extra_len; +/*12*/ u_char cmd_spec_info[4]; +/*13*/ u_char add_sense_code; +/*14*/ u_char add_sense_code_qual; +/*15*/ u_char fru; +/*16*/ u_char sense_key_spec_1; +#define SSD_SCS_VALID 0x80 +/*17*/ u_char sense_key_spec_2; +/*18*/ u_char sense_key_spec_3; +/*32*/ u_char extra_bytes[14]; + } extended; + }ext; +}; /* total of 32 bytes */ + +struct blk_desc +{ + u_char density; + u_char nblocks[3]; + u_char reserved; + u_char blklen[3]; +}; + +struct scsi_mode_header +{ + u_char data_length; /* Sense data length */ + u_char medium_type; + u_char dev_spec; + u_char blk_desc_len; +}; + +struct scsi_mode_header_big +{ + u_char data_length[2]; /* Sense data length */ + u_char medium_type; + u_char dev_spec; + u_char unused[2]; + u_char blk_desc_len[2]; +}; + + +/* + * Status Byte + */ +#define SCSI_OK 0x00 +#define SCSI_CHECK 0x02 +#define SCSI_BUSY 0x08 +#define SCSI_INTERM 0x10 +#endif /*_SCSI_SCSI_ALL_H*/ diff --git a/sys/scsi/scsi_base.c b/sys/scsi/scsi_base.c new file mode 100644 index 0000000..cb7f009 --- /dev/null +++ b/sys/scsi/scsi_base.c @@ -0,0 +1,896 @@ +/* + * Written By Julian ELischer + * Copyright julian Elischer 1993. + * Permission is granted to use or redistribute this file in any way as long + * as this notice remains. Julian Elischer does not guarantee that this file + * is totally correct for any given task and users of this file must + * accept responsibility for any damage that occurs from the application of this + * file. + * + * Written by Julian Elischer (julian@dialix.oz.au) + * $Id: scsi_base.c,v 1.8 1994/05/19 22:21:05 jkh Exp $ + */ + +#define SPLSD splbio +#define ESUCCESS 0 +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/uio.h> +#include <sys/malloc.h> +#include <sys/errno.h> +#include <vm/vm.h> +#include <scsi/scsi_all.h> +#include <scsi/scsi_disk.h> +#include <scsi/scsiconf.h> + +static errval sc_err1(struct scsi_xfer *); +static errval scsi_interpret_sense(struct scsi_xfer *); + +#ifdef NetBSD +#ifdef DDB +int Debugger(); +#else /* DDB */ +#define Debugger() +#endif /* DDB */ +#else /* NetBSD */ +#include "ddb.h" +#if NDDB > 0 +#else /* NDDB > 0 */ +#define Debugger() +#endif /* NDDB > 0 */ +#endif + +void sc_print_addr __P((struct scsi_link *sc_link)); + +struct scsi_xfer *next_free_xs; + +/* + * Get a scsi transfer structure for the caller. Charge the structure + * to the device that is referenced by the sc_link structure. If the + * sc_link structure has no 'credits' then the device already has the + * maximum number or outstanding operations under way. In this stage, + * wait on the structure so that when one is freed, we are awoken again + * If the SCSI_NOSLEEP flag is set, then do not wait, but rather, return + * a NULL pointer, signifying that no slots were available + * Note in the link structure, that we are waiting on it. + */ + +struct scsi_xfer * +get_xs(sc_link, flags) + struct scsi_link *sc_link; /* who to charge the xs to */ + u_int32 flags; /* if this call can sleep */ +{ + struct scsi_xfer *xs; + u_int32 s; + + SC_DEBUG(sc_link, SDEV_DB3, ("get_xs\n")); + s = splbio(); + while (!sc_link->opennings) { + SC_DEBUG(sc_link, SDEV_DB3, ("sleeping\n")); + if (flags & SCSI_NOSLEEP) { + splx(s); + return 0; + } + sc_link->flags |= SDEV_WAITING; + tsleep((caddr_t)sc_link, PRIBIO, "scsiget", 0); + } + sc_link->opennings--; + if (xs = next_free_xs) { + next_free_xs = xs->next; + splx(s); + } else { + splx(s); + SC_DEBUG(sc_link, SDEV_DB3, ("making\n")); + xs = malloc(sizeof(*xs), M_TEMP, + ((flags & SCSI_NOSLEEP) ? M_NOWAIT : M_WAITOK)); + if (xs == NULL) { + sc_print_addr(sc_link); + printf("cannot allocate scsi xs\n"); + return (NULL); + } + } + SC_DEBUG(sc_link, SDEV_DB3, ("returning\n")); + xs->sc_link = sc_link; + return (xs); +} + +/* + * Given a scsi_xfer struct, and a device (referenced through sc_link) + * return the struct to the free pool and credit the device with it + * If another process is waiting for an xs, do a wakeup, let it proceed + */ +void +free_xs(xs, sc_link, flags) + struct scsi_xfer *xs; + struct scsi_link *sc_link; /* who to credit for returning it */ + u_int32 flags; +{ + xs->next = next_free_xs; + next_free_xs = xs; + + SC_DEBUG(sc_link, SDEV_DB3, ("free_xs\n")); + /* if was 0 and someone waits, wake them up */ + if ((!sc_link->opennings++) && (sc_link->flags & SDEV_WAITING)) { + sc_link->flags &= ~SDEV_WAITING; + wakeup((caddr_t)sc_link); /* remember, it wakes them ALL up */ + } else { + if (sc_link->device->start) { + SC_DEBUG(sc_link, SDEV_DB2, ("calling private start()\n")); + (*(sc_link->device->start)) (sc_link->dev_unit); + } + } +} + +/* + * Find out from the device what its capacity is. + */ +u_int32 +scsi_size(sc_link, flags) + struct scsi_link *sc_link; + u_int32 flags; +{ + struct scsi_read_cap_data rdcap; + struct scsi_read_capacity scsi_cmd; + u_int32 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 (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) & rdcap, + sizeof(rdcap), + 2, + 20000, + NULL, + flags | SCSI_DATA_IN) != 0) { + + sc_print_addr(sc_link); + printf("could not get size\n"); + 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 + */ +errval +scsi_test_unit_ready(sc_link, flags) + struct scsi_link *sc_link; + u_int32 flags; +{ + struct scsi_test_unit_ready scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = TEST_UNIT_READY; + + return (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 2, + 100000, + NULL, + flags)); +} + +/* + * Do a scsi operation, asking a device to run as SCSI-II if it can. + */ +errval +scsi_change_def(sc_link, flags) + struct scsi_link *sc_link; + u_int32 flags; +{ + struct scsi_changedef scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = CHANGE_DEFINITION; + scsi_cmd.how = SC_SCSI_2; + + return (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 2, + 100000, + NULL, + flags)); +} + +/* + * Do a scsi operation asking a device what it is + * Use the scsi_cmd routine in the switch table. + */ +errval +scsi_inquire(sc_link, inqbuf, flags) + struct scsi_link *sc_link; + struct scsi_inquiry_data *inqbuf; + u_int32 flags; +{ + struct scsi_inquiry scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = INQUIRY; + scsi_cmd.length = sizeof(struct scsi_inquiry_data); + + return (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) inqbuf, + sizeof(struct scsi_inquiry_data), + 2, + 100000, + NULL, + SCSI_DATA_IN | flags)); +} + +/* + * Prevent or allow the user to remove the media + */ +errval +scsi_prevent(sc_link, type, flags) + struct scsi_link *sc_link; + u_int32 type, flags; +{ + struct scsi_prevent scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = PREVENT_ALLOW; + scsi_cmd.how = type; + return (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 2, + 5000, + NULL, + flags)); +} + +/* + * Get scsi driver to send a "start up" command + */ +errval +scsi_start_unit(sc_link, flags) + struct scsi_link *sc_link; + u_int32 flags; +{ + struct scsi_start_stop scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = START_STOP; + scsi_cmd.how = SSS_START; + + return (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 2, + 10000, + NULL, + flags)); +} + +/* + * Get scsi driver to send a "stop" command + */ +errval +scsi_stop_unit(sc_link, eject, flags) + struct scsi_link *sc_link; + u_int32 eject; + u_int32 flags; +{ + struct scsi_start_stop scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = START_STOP; + if (eject) { + scsi_cmd.how = SSS_LOEJ; + } + + return (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 2, + 10000, + NULL, + flags)); +} + +/* + * This routine is called by the scsi interrupt when the transfer is complete. + */ +void +scsi_done(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *sc_link = xs->sc_link; + struct buf *bp = xs->bp; + errval retval; + + SC_DEBUG(sc_link, SDEV_DB2, ("scsi_done\n")); +#ifdef SCSIDEBUG + if (sc_link->flags & SDEV_DB1) + { + show_scsi_cmd(xs); + } +#endif /*SCSIDEBUG */ + /* + * If it's a user level request, bypass all usual completion processing, + * let the user work it out.. We take reponsibility for freeing the + * xs when the user returns. (and restarting the device's queue). + */ + if (xs->flags & SCSI_USER) { + biodone(xs->bp); +#ifdef NOTNOW + SC_DEBUG(sc_link, SDEV_DB3, ("calling user done()\n")); + scsi_user_done(xs); /* to take a copy of the sense etc. */ + SC_DEBUG(sc_link, SDEV_DB3, ("returned from user done()\n ")); +#endif + free_xs(xs, sc_link, SCSI_NOSLEEP); /* restarts queue too */ + SC_DEBUG(sc_link, SDEV_DB3, ("returning to adapter\n")); + return; + } + /* + * If the device has it's own done routine, call it first. + * If it returns a legit error value, return that, otherwise + * it wants us to continue with normal processing. + */ + + if (sc_link->device->done) { + SC_DEBUG(sc_link, SDEV_DB2, ("calling private done()\n")); + retval = (*sc_link->device->done) (xs); + if (retval == -1) { + free_xs(xs, sc_link, SCSI_NOSLEEP); /*XXX */ + return; /* it did it all, finish up */ + } + if (retval == -2) { + return; /* it did it all, finish up */ + } + SC_DEBUG(sc_link, SDEV_DB3, ("continuing with generic done()\n")); + } + if ((bp = xs->bp) == NULL) { + /* + * if it's a normal upper level request, then ask + * the upper level code to handle error checking + * rather than doing it here at interrupt time + */ + wakeup((caddr_t)xs); + return; + } + /* + * Go and handle errors now. + * If it returns -1 then we should RETRY + */ + if ((retval = sc_err1(xs)) == -1) { + if ((*(sc_link->adapter->scsi_cmd)) (xs) + == SUCCESSFULLY_QUEUED) { /* don't wake the job, ok? */ + return; + } + xs->flags |= ITSDONE; + } + free_xs(xs, sc_link, SCSI_NOSLEEP); /* does a start if needed */ + biodone(bp); +} + +/* + * ask the scsi driver to perform a command for us. + * tell it where to read/write the data, and how + * long the data is supposed to be. If we have a buf + * to associate with the transfer, we need that too. + */ +errval +scsi_scsi_cmd(sc_link, scsi_cmd, cmdlen, data_addr, datalen, + retries, timeout, bp, flags) + struct scsi_link *sc_link; + struct scsi_generic *scsi_cmd; + u_int32 cmdlen; + u_char *data_addr; + u_int32 datalen; + u_int32 retries; + u_int32 timeout; + struct buf *bp; + u_int32 flags; +{ + struct scsi_xfer *xs; + errval retval; + u_int32 s; + + if (bp) flags |= SCSI_NOSLEEP; + SC_DEBUG(sc_link, SDEV_DB2, ("scsi_cmd\n")); + + xs = get_xs(sc_link, flags); /* should wait unless booting */ + if (!xs) return (ENOMEM); + /* + * Fill out the scsi_xfer structure. We don't know whose context + * the cmd is in, so copy it. + */ + bcopy(scsi_cmd, &(xs->cmdstore), cmdlen); + xs->flags = INUSE | flags; + xs->sc_link = sc_link; + xs->retries = retries; + xs->timeout = timeout; + xs->cmd = &xs->cmdstore; + xs->cmdlen = cmdlen; + xs->data = data_addr; + xs->datalen = datalen; + xs->resid = datalen; + xs->bp = bp; +/*XXX*/ /*use constant not magic number */ + if (datalen && ((caddr_t) data_addr < (caddr_t) KERNBASE)) { + if (bp) { + printf("Data buffered space not in kernel context\n"); +#ifdef SCSIDEBUG + show_scsi_cmd(xs); +#endif /* SCSIDEBUG */ + retval = EFAULT; + goto bad; + } +#ifdef NOBOUNCE + xs->data = malloc(datalen, M_TEMP, M_WAITOK); +#else + xs->data = (caddr_t) vm_bounce_kva_alloc( (datalen + PAGE_SIZE - 1)/PAGE_SIZE); +#endif + /* I think waiting is ok *//*XXX */ + switch ((int)(flags & (SCSI_DATA_IN | SCSI_DATA_OUT))) { + case 0: + printf("No direction flags, assuming both\n"); +#ifdef SCSIDEBUG + show_scsi_cmd(xs); +#endif /* SCSIDEBUG */ + case SCSI_DATA_IN | SCSI_DATA_OUT: /* weird */ + case SCSI_DATA_OUT: + bcopy(data_addr, xs->data, datalen); + break; + case SCSI_DATA_IN: + bzero(xs->data, datalen); + } + } +retry: + xs->error = XS_NOERROR; +#ifdef PARANOID + if (datalen && ((caddr_t) xs->data < (caddr_t) KERNBASE)) { + printf("It's still wrong!\n"); + } +#endif /*PARANOID*/ +#ifdef SCSIDEBUG + if (sc_link->flags & SDEV_DB3) show_scsi_xs(xs); +#endif /* SCSIDEBUG */ + /* + * Do the transfer. If we are polling we will return: + * COMPLETE, Was poll, and scsi_done has been called + * TRY_AGAIN_LATER, Adapter short resources, try again + * + * if under full steam (interrupts) it will return: + * SUCCESSFULLY_QUEUED, will do a wakeup when complete + * TRY_AGAIN_LATER, (as for polling) + * After the wakeup, we must still check if it succeeded + * + * If we have a bp however, all the error proccessing + * and the buffer code both expect us to return straight + * to them, so as soon as the command is queued, return + */ + + retval = (*(sc_link->adapter->scsi_cmd)) (xs); + + switch (retval) { + case SUCCESSFULLY_QUEUED: + if (bp) + return retval; /* will sleep (or not) elsewhere */ + s = splbio(); + while (!(xs->flags & ITSDONE)) { + tsleep((caddr_t)xs, PRIBIO + 1, "scsicmd", 0); + } + splx(s); + /* fall through to check success of completed command */ + case COMPLETE: /* Polling command completed ok */ +/*XXX*/ case HAD_ERROR: /* Polling command completed with error */ + SC_DEBUG(sc_link, SDEV_DB3, ("back in cmd()\n")); + if ((retval = sc_err1(xs)) == -1) + goto retry; + break; + + case TRY_AGAIN_LATER: /* adapter resource shortage */ + SC_DEBUG(sc_link, SDEV_DB3, ("will try again \n")); + /* should sleep 1 sec here */ + if (xs->retries--) { + xs->flags &= ~ITSDONE; + goto retry; + } + default: + retval = EIO; + } + /* + * If we had to copy the data out of the user's context, + * then do the other half (copy it back or whatever) + * and free the memory buffer + */ + if (datalen && (xs->data != data_addr)) { + switch ((int)(flags & (SCSI_DATA_IN | SCSI_DATA_OUT))) { + case 0: + case SCSI_DATA_IN | SCSI_DATA_OUT: /* weird */ + case SCSI_DATA_IN: + bcopy(xs->data, data_addr, datalen); + break; + } +#ifdef NOBOUNCE + free(xs->data, M_TEMP); +#else + vm_bounce_kva_alloc_free(xs->data, (datalen + PAGE_SIZE - 1)/PAGE_SIZE, 0); +#endif + } + /* + * we have finished with the xfer stuct, free it and + * check if anyone else needs to be started up. + */ +bad: + free_xs(xs, sc_link, flags); /* includes the 'start' op */ + if (bp && retval) { + bp->b_error = retval; + bp->b_flags |= B_ERROR; + biodone(bp); + } + return (retval); +} + +static errval +sc_err1(xs) + struct scsi_xfer *xs; +{ + struct buf *bp = xs->bp; + errval retval; + + SC_DEBUG(xs->sc_link, SDEV_DB3, ("sc_err1,err = 0x%x \n", xs->error)); + /* + * If it has a buf, we might be working with + * a request from the buffer cache or some other + * piece of code that requires us to process + * errors at inetrrupt time. We have probably + * been called by scsi_done() + */ + switch ((int)xs->error) { + case XS_NOERROR: /* nearly always hit this one */ + retval = ESUCCESS; + if (bp) { + bp->b_error = 0; + bp->b_resid = 0; + } + break; + + case XS_SENSE: + if (bp) { + bp->b_error = 0; + bp->b_resid = 0; + if (retval = (scsi_interpret_sense(xs))) { + bp->b_flags |= B_ERROR; + bp->b_error = retval; + bp->b_resid = bp->b_bcount; + } + SC_DEBUG(xs->sc_link, SDEV_DB3, + ("scsi_interpret_sense (bp) returned %d\n", retval)); + } else { + retval = (scsi_interpret_sense(xs)); + SC_DEBUG(xs->sc_link, SDEV_DB3, + ("scsi_interpret_sense (no bp) returned %d\n", retval)); + } + break; + + case XS_BUSY: + /*should somehow arange for a 1 sec delay here (how?) */ + /* XXX tsleep(&localvar, priority, "foo", hz); + that's how! */ + case XS_TIMEOUT: + /* + * If we can, resubmit it to the adapter. + */ + if (xs->retries--) { + xs->error = XS_NOERROR; + xs->flags &= ~ITSDONE; + goto retry; + } + /* fall through */ + case XS_DRIVER_STUFFUP: + if (bp) { + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + } + retval = EIO; + break; + default: + retval = EIO; + sc_print_addr(xs->sc_link); + printf("unknown error category from scsi driver\n"); + } + return retval; +retry: + return (-1); +} + +/* + * Look at the returned sense and act on the error, determining + * the unix error number to pass back. (0 = report no error) + * + * THIS IS THE DEFAULT ERROR HANDLER + */ +static errval +scsi_interpret_sense(xs) + struct scsi_xfer *xs; +{ + struct scsi_sense_data *sense; + struct scsi_link *sc_link = xs->sc_link; + u_int32 key; + u_int32 silent; + u_int32 info; + errval errcode; + + static char *error_mes[] = + {"soft error (corrected)", + "not ready", "medium error", + "non-media hardware failure", "illegal request", + "unit attention", "readonly device", + "no data found", "vendor unique", + "copy aborted", "command aborted", + "search returned equal", "volume overflow", + "verify miscompare", "unknown error key" + }; + + /* + * If the flags say errs are ok, then always return ok. + */ + if (xs->flags & SCSI_ERR_OK) + return (ESUCCESS); + + sense = &(xs->sense); +#ifdef SCSIDEBUG + if (sc_link->flags & SDEV_DB1) { + u_int32 count = 0; + printf("code%x valid%x ", + sense->error_code & SSD_ERRCODE, + sense->error_code & SSD_ERRCODE_VALID ? 1 : 0); + printf("seg%x key%x ili%x eom%x fmark%x\n", + sense->ext.extended.segment, + sense->ext.extended.flags & SSD_KEY, + sense->ext.extended.flags & SSD_ILI ? 1 : 0, + sense->ext.extended.flags & SSD_EOM ? 1 : 0, + sense->ext.extended.flags & SSD_FILEMARK ? 1 : 0); + 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"); + } +#endif /*SCSIDEBUG */ + /* + * If the device has it's own error handler, call it first. + * If it returns a legit error value, return that, otherwise + * it wants us to continue with normal error processing. + */ + if (sc_link->device->err_handler) { + SC_DEBUG(sc_link, SDEV_DB2, ("calling private err_handler()\n")); + errcode = (*sc_link->device->err_handler) (xs); + if (errcode != -1) + return errcode; /* errcode >= 0 better ? */ + } + /* otherwise use the default */ + silent = (xs->flags & SCSI_SILENT); + switch (sense->error_code & SSD_ERRCODE) { + /* + * If it's code 70, use the extended stuff and interpret the key + */ + case 0x71: /* delayed error */ + sc_print_addr(sc_link); + key = sense->ext.extended.flags & SSD_KEY; + printf(" DELAYED ERROR, key = 0x%x\n", key); + case 0x70: + if (sense->error_code & SSD_ERRCODE_VALID) { + info = ntohl(*((long *) sense->ext.extended.info)); + } else { + info = 0; + } + key = sense->ext.extended.flags & SSD_KEY; + + if (key && !silent) { + sc_print_addr(sc_link); + printf("%s", error_mes[key - 1]); + if (sense->error_code & SSD_ERRCODE_VALID) { + switch ((int)key) { + case 0x2: /* NOT READY */ + case 0x5: /* ILLEGAL REQUEST */ + case 0x6: /* UNIT ATTENTION */ + case 0x7: /* DATA PROTECT */ + break; + case 0x8: /* BLANK CHECK */ + printf(", requested size: %d (decimal)", + info); + break; + default: + printf(", info = %d (decimal)", info); + } + } + printf("\n"); + } + switch ((int)key) { + case 0x0: /* NO SENSE */ + case 0x1: /* RECOVERED ERROR */ + if (xs->resid == xs->datalen) + xs->resid = 0; /* not short read */ + case 0xc: /* EQUAL */ + return (ESUCCESS); + case 0x2: /* NOT READY */ + sc_link->flags &= ~SDEV_MEDIA_LOADED; + return (EBUSY); + case 0x5: /* ILLEGAL REQUEST */ + return (EINVAL); + case 0x6: /* UNIT ATTENTION */ + sc_link->flags &= ~SDEV_MEDIA_LOADED; + if (sc_link->flags & SDEV_OPEN) { + return (EIO); + } else { + return 0; + } + case 0x7: /* DATA PROTECT */ + return (EACCES); + case 0xd: /* VOLUME OVERFLOW */ + return (ENOSPC); + case 0x8: /* BLANK CHECK */ + return (ESUCCESS); + default: + return (EIO); + } + /* + * Not code 70, just report it + */ + default: + if (!silent) { + sc_print_addr(sc_link); + printf("error code %d", + sense->error_code & SSD_ERRCODE); + if (sense->error_code & SSD_ERRCODE_VALID) { + printf(" at block no. %d (decimal)", + (sense->ext.unextended.blockhi << 16) + + (sense->ext.unextended.blockmed << 8) + + (sense->ext.unextended.blocklow)); + } + printf("\n"); + } + return (EIO); + } +} + +/* + * Utility routines often used in SCSI stuff + */ + +/* + * convert a physical address to 3 bytes, + * MSB at the lowest address, + * LSB at the highest. + */ +void +lto3b(val, bytes) + int val; + u_char *bytes; +{ + *bytes++ = (val & 0xff0000) >> 16; + *bytes++ = (val & 0xff00) >> 8; + *bytes = val & 0xff; +} + +/* + * The reverse of lto3b + */ +int +_3btol(bytes) + u_char *bytes; +{ + u_int32 rc; + rc = (*bytes++ << 16); + rc += (*bytes++ << 8); + rc += *bytes; + return ((int) rc); +} + +/* + * Print out the scsi_link structure's address info. + */ + +void +sc_print_addr(sc_link) + struct scsi_link *sc_link; +{ + + printf("%s%d(%s%d:%d:%d): ", sc_link->device->name, sc_link->dev_unit, + sc_link->adapter->name, sc_link->adapter_unit, + sc_link->target, sc_link->lun); +} +#ifdef SCSIDEBUG +/* + * Given a scsi_xfer, dump the request, in all it's glory + */ +void +show_scsi_xs(xs) + struct scsi_xfer *xs; +{ + printf("xs(0x%x): ", xs); + printf("flg(0x%x)", xs->flags); + printf("sc_link(0x%x)", xs->sc_link); + printf("retr(0x%x)", xs->retries); + printf("timo(0x%x)", xs->timeout); + printf("cmd(0x%x)", xs->cmd); + printf("len(0x%x)", xs->cmdlen); + printf("data(0x%x)", xs->data); + printf("len(0x%x)", xs->datalen); + printf("res(0x%x)", xs->resid); + printf("err(0x%x)", xs->error); + printf("bp(0x%x)", xs->bp); + show_scsi_cmd(xs); +} + +void +show_scsi_cmd(struct scsi_xfer *xs) +{ + u_char *b = (u_char *) xs->cmd; + int i = 0; + + sc_print_addr(xs->sc_link); + printf("command: "); + + if (!(xs->flags & SCSI_RESET)) { + while (i < xs->cmdlen) { + if (i) + printf(","); + printf("%x", b[i++]); + } + printf("-[%d bytes]\n", xs->datalen); + if (xs->datalen) + show_mem(xs->data, min(64, xs->datalen)); + } else { + printf("-RESET-\n"); + } +} + +void +show_mem(address, num) + unsigned char *address; + u_int32 num; +{ + u_int32 x, y; + printf("------------------------------"); + for (y = 0; y < num; y += 1) { + if (!(y % 16)) + printf("\n%03d: ", y); + printf("%02x ", *address++); + } + printf("\n------------------------------\n"); +} +#endif /*SCSIDEBUG */ diff --git a/sys/scsi/scsi_cd.h b/sys/scsi/scsi_cd.h new file mode 100644 index 0000000..0a4759b --- /dev/null +++ b/sys/scsi/scsi_cd.h @@ -0,0 +1,229 @@ +/* + * Written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems. + * + * 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. + * + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id: scsi_cd.h,v 1.6 93/08/26 21:09:19 julian Exp Locker: julian $ + */ +#ifndef _SCSI_SCSI_CD_H +#define _SCSI_SCSI_CD_H 1 + +/* + * Define two bits always in the same place in byte 2 (flag byte) + */ +#define CD_RELADDR 0x01 +#define CD_MSF 0x02 + +/* + * SCSI command format + */ + +struct scsi_read_capacity_cd +{ + u_char op_code; + u_char byte2; + u_char addr_3; /* Most Significant */ + u_char addr_2; + u_char addr_1; + u_char addr_0; /* Least Significant */ + u_char unused[3]; + u_char control; +}; + +struct scsi_pause +{ + u_char op_code; + u_char byte2; + u_char unused[6]; + u_char resume; + u_char control; +}; +#define PA_PAUSE 1 +#define PA_RESUME 0 + +struct scsi_play_msf +{ + u_char op_code; + u_char byte2; + u_char unused; + u_char start_m; + u_char start_s; + u_char start_f; + u_char end_m; + u_char end_s; + u_char end_f; + u_char control; +}; + +struct scsi_play_track +{ + u_char op_code; + u_char byte2; + u_char unused[2]; + u_char start_track; + u_char start_index; + u_char unused1; + u_char end_track; + u_char end_index; + u_char control; +}; + +struct scsi_play +{ + u_char op_code; + u_char byte2; + u_char blk_addr[4]; + u_char unused; + u_char xfer_len[2]; + u_char control; +}; + +struct scsi_play_big +{ + u_char op_code; + u_char byte2; /* same as above */ + u_char blk_addr[4]; + u_char xfer_len[4]; + u_char unused; + u_char control; +}; + +struct scsi_play_rel_big +{ + u_char op_code; + u_char byte2; /* same as above */ + u_char blk_addr[4]; + u_char xfer_len[4]; + u_char track; + u_char control; +}; + +struct scsi_read_header +{ + u_char op_code; + u_char byte2; + u_char blk_addr[4]; + u_char unused; + u_char data_len[2]; + u_char control; +}; + +struct scsi_read_subchannel +{ + u_char op_code; + u_char byte2; + u_char byte3; +#define SRS_SUBQ 0x40 + u_char subchan_format; + u_char unused[2]; + u_char track; + u_char data_len[2]; + u_char control; +}; + +struct scsi_read_toc +{ + u_char op_code; + u_char byte2; + u_char unused[4]; + u_char from_track; + u_char data_len[2]; + u_char control; +}; +; + +struct scsi_read_cd_capacity +{ + u_char op_code; + u_char byte2; + u_char addr_3; /* Most Significant */ + u_char addr_2; + u_char addr_1; + u_char addr_0; /* Least Significant */ + u_char unused[3]; + u_char control; +}; + +/* + * Opcodes + */ + +#define READ_CD_CAPACITY 0x25 /* slightly different from disk */ +#define READ_SUBCHANNEL 0x42 /* cdrom read Subchannel */ +#define READ_TOC 0x43 /* cdrom read TOC */ +#define READ_HEADER 0x44 /* cdrom read header */ +#define PLAY 0x45 /* cdrom play 'play audio' mode */ +#define PLAY_MSF 0x47 /* cdrom play Min,Sec,Frames mode */ +#define PLAY_TRACK 0x48 /* cdrom play track/index mode */ +#define PLAY_TRACK_REL 0x49 /* cdrom play track/index mode */ +#define PAUSE 0x4b /* cdrom pause in 'play audio' mode */ +#define PLAY_BIG 0xa5 /* cdrom pause in 'play audio' mode */ +#define PLAY_TRACK_REL_BIG 0xa9 /* cdrom play track/index mode */ + + + +struct scsi_read_cd_cap_data +{ + u_char addr_3; /* Most significant */ + u_char addr_2; + u_char addr_1; + u_char addr_0; /* Least significant */ + u_char length_3; /* Most significant */ + u_char length_2; + u_char length_1; + u_char length_0; /* Least significant */ +}; + +union cd_pages +{ + struct audio_page + { + u_char page_code; +#define CD_PAGE_CODE 0x3F +#define AUDIO_PAGE 0x0e +#define CD_PAGE_PS 0x80 + u_char param_len; + u_char flags; +#define CD_PA_SOTC 0x02 +#define CD_PA_IMMED 0x04 + u_char unused[2]; + u_char format_lba; +#define CD_PA_FORMAT_LBA 0x0F +#define CD_PA_APR_VALID 0x80 + u_char lb_per_sec[2]; + struct port_control + { + u_char channels; +#define CHANNEL 0x0F +#define CHANNEL_0 1 +#define CHANNEL_1 2 +#define CHANNEL_2 4 +#define CHANNEL_3 8 +#define LEFT_CHANNEL CHANNEL_0 +#define RIGHT_CHANNEL CHANNEL_1 + u_char volume; + } port[4]; +#define LEFT_PORT 0 +#define RIGHT_PORT 1 + }audio; +}; + +struct cd_mode_data +{ + struct scsi_mode_header header; + struct blk_desc blk_desc; + union cd_pages page; +}; +#endif /*_SCSI_SCSI_CD_H*/ + diff --git a/sys/scsi/scsi_changer.h b/sys/scsi/scsi_changer.h new file mode 100644 index 0000000..85819c8 --- /dev/null +++ b/sys/scsi/scsi_changer.h @@ -0,0 +1,98 @@ +/* + * SCSI changer interface description + */ + +/* + * Written by Stefan Grefen (grefen@goofy.zdv.uni-mainz.de soon grefen@convex.com) + * based on the SCSI System by written Julian Elischer (julian@tfs.com) + * for TRW Financial Systems. + * + * 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. + * + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id: scsi_changer.h,v 1.5 93/08/26 21:09:22 julian Exp Locker: julian $ + */ +#ifndef _SCSI_SCSI_CHANGER_H +#define _SCSI_SCSI_CHANGER_H 1 + +/* + * SCSI command format + */ +struct scsi_read_element_status +{ + u_char op_code; + u_char byte2; +#define SRES_ELEM_TYPE_CODE 0x0F +#define SRES_ELEM_VOLTAG 0x10 + u_char starting_element_addr[2]; + u_char number_of_elements[2]; + u_char resv1; + u_char allocation_length[3]; + u_char resv2; + u_char control; +}; +#define RE_ALL_ELEMENTS 0 +#define RE_MEDIUM_TRANSPORT_ELEMENT 1 +#define RE_STORAGE_ELEMENT 2 +#define RE_IMPORT_EXPORT 3 +#define RE_DATA_TRANSFER_ELEMENT 4 + +struct scsi_move_medium +{ + u_char op_code; + u_char byte2; + u_char transport_element_address[2]; + u_char source_address[2]; + u_char destination_address[2]; + u_char rsvd[2]; + u_char invert; + u_char control; +}; + +struct scsi_position_to_element +{ + u_char op_code; + u_char byte2; + u_char transport_element_address[2]; + u_char source_address[2]; + u_char rsvd[2]; + u_char invert; + u_char control; +}; + +/* + * Opcodes + */ +#define POSITION_TO_ELEMENT 0x2b +#define MOVE_MEDIUM 0xa5 +#define READ_ELEMENT_STATUS 0xb8 + +struct scsi_element_status_data +{ + u_char first_element_reported[2]; + u_char number_of_elements_reported[2]; + u_char rsvd; + u_char byte_count_of_report[3]; +}; + +struct element_status_page +{ + u_char element_type_code; + u_char flags; +#define ESP_AVOLTAG 0x40 +#define ESP_PVOLTAG 0x80 + u_char element_descriptor_length[2]; + u_char rsvd; + u_char byte_count_of_descriptor_data[3]; +}; +#endif /*_SCSI_SCSI_CHANGER_H*/ + diff --git a/sys/scsi/scsi_debug.h b/sys/scsi/scsi_debug.h new file mode 100644 index 0000000..480ff14 --- /dev/null +++ b/sys/scsi/scsi_debug.h @@ -0,0 +1,53 @@ +/*#define SCSIDEBUG 1*/ +/* + * Written by Julian Elischer (julian@tfs.com) + * + * $Id: scsi_debug.h,v 1.3 93/10/10 09:26:05 julian Exp Locker: julian $ + */ +#ifndef _SCSI_SCSI_DEBUG_H +#define _SCSI_SCSI_DEBUG_H 1 + +/* + * These are the new debug bits. (Sat Oct 2 12:46:46 WST 1993) + * the following DEBUG bits are defined to exist in the flags word of + * the scsi_link structure. + */ +#define SDEV_DB1 0x10 /* scsi commands, errors, data */ +#define SDEV_DB2 0x20 /* routine flow tracking */ +#define SDEV_DB3 0x40 /* internal to routine flows */ +#define SDEV_DB4 0x80 /* level 4 debugging for this dev */ + +/* target and LUN we want to debug */ +#define DEBUGTARG 9 /*9 = dissable*/ +#define DEBUGLUN 0 +#define DEBUGLEVEL (SDEV_DB1|SDEV_DB2) + +/* + * This is the usual debug macro for use with the above bits + */ +#ifdef SCSIDEBUG +#define SC_DEBUG(sc_link,Level,Printstuff) \ + if((sc_link)->flags & (Level)) \ + { \ + printf("%s%d(%s%d:%d:%d): ", \ + sc_link->device->name, \ + sc_link->dev_unit, \ + sc_link->adapter->name, \ + sc_link->adapter_unit, \ + sc_link->target, \ + sc_link->lun); \ + printf Printstuff; \ + } +#define SC_DEBUGN(sc_link,Level,Printstuff) \ + if((sc_link)->flags & (Level)) \ + { \ + printf Printstuff; \ + } +#else +#define SC_DEBUG(A,B,C) /* not included */ +#define SC_DEBUGN(A,B,C) /* not included */ +#endif + +#endif /*_SCSI_SCSI_DEBUG_H*/ +/* END OF FILE */ + diff --git a/sys/scsi/scsi_disk.h b/sys/scsi/scsi_disk.h new file mode 100644 index 0000000..60d0bcc --- /dev/null +++ b/sys/scsi/scsi_disk.h @@ -0,0 +1,216 @@ +/* + * SCSI interface description + */ + +/* + * Some lines of this file come from a file of the name "scsi.h" + * distributed by OSF as part of mach2.5, + * so the following disclaimer has been kept. + * + * Copyright 1990 by Open Software Foundation, + * Grenoble, FRANCE + * + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appears in all copies and + * that both the copyright notice and this permission notice appear in + * supporting documentation, and that the name of OSF or Open Software + * Foundation not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. + * + * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, + * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Largely written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems. + * + * 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. + * + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id: scsi_disk.h,v 1.4 93/08/26 21:09:23 julian Exp Locker: julian $ + */ + +/* + * SCSI command format + */ + +#ifndef _SCSI_SCSI_DISK_H +#define _SCSI_SCSI_DISK_H 1 + +struct scsi_reassign_blocks +{ + u_char op_code; + u_char byte2; + u_char unused[3]; + u_char control; +}; + +struct scsi_rw +{ + u_char op_code; + u_char addr_2; /* Most significant */ +#define SRW_TOPADDR 0x1F /* only 5 bits here */ + u_char addr_1; + u_char addr_0; /* least significant */ + u_char length; + u_char control; +}; + +struct scsi_rw_big +{ + u_char op_code; + u_char byte2; +#define SRWB_RELADDR 0x01 + u_char addr_3; /* Most significant */ + u_char addr_2; + u_char addr_1; + u_char addr_0; /* least significant */ + u_char reserved;; + u_char length2; + u_char length1; + u_char control; +}; + +struct scsi_read_capacity +{ + u_char op_code; + u_char byte2; + u_char addr_3; /* Most Significant */ + u_char addr_2; + u_char addr_1; + u_char addr_0; /* Least Significant */ + u_char unused[3]; + u_char control; +}; + +struct scsi_start_stop +{ + u_char op_code; + u_char byte2; + u_char unused[2]; + u_char how; +#define SSS_START 0x01 +#define SSS_LOEJ 0x02 + u_char control; +}; + + + +/* + * Opcodes + */ + +#define REASSIGN_BLOCKS 0x07 +#define READ_COMMAND 0x08 +#define WRITE_COMMAND 0x0a +#define MODE_SELECT 0x15 +#define MODE_SENSE 0x1a +#define START_STOP 0x1b +#define PREVENT_ALLOW 0x1e +#define READ_CAPACITY 0x25 +#define READ_BIG 0x28 +#define WRITE_BIG 0x2a + + + +struct scsi_read_cap_data +{ + u_char addr_3; /* Most significant */ + u_char addr_2; + u_char addr_1; + u_char addr_0; /* Least significant */ + u_char length_3; /* Most significant */ + u_char length_2; + u_char length_1; + u_char length_0; /* Least significant */ +}; + +struct scsi_reassign_blocks_data +{ + u_char reserved[2]; + u_char length_msb; + u_char length_lsb; + struct + { + u_char dlbaddr_3; /* defect logical block address (MSB) */ + u_char dlbaddr_2; + u_char dlbaddr_1; + u_char dlbaddr_0; /* defect logical block address (LSB) */ + } defect_descriptor[1]; +}; + +union disk_pages /* this is the structure copied from osf */ +{ + struct page_disk_format { + u_char pg_code; /* page code (should be 3) */ +#define DISK_PGCODE 0x3F /* only 6 bits valid */ + u_char pg_length; /* page length (should be 0x16) */ + u_char trk_z_1; /* tracks per zone (MSB) */ + u_char trk_z_0; /* tracks per zone (LSB) */ + u_char alt_sec_1; /* alternate sectors per zone (MSB) */ + u_char alt_sec_0; /* alternate sectors per zone (LSB) */ + u_char alt_trk_z_1; /* alternate tracks per zone (MSB) */ + u_char alt_trk_z_0; /* alternate tracks per zone (LSB) */ + u_char alt_trk_v_1; /* alternate tracks per volume (MSB) */ + u_char alt_trk_v_0; /* alternate tracks per volume (LSB) */ + u_char ph_sec_t_1; /* physical sectors per track (MSB) */ + u_char ph_sec_t_0; /* physical sectors per track (LSB) */ + u_char bytes_s_1; /* bytes per sector (MSB) */ + u_char bytes_s_0; /* bytes per sector (LSB) */ + u_char interleave_1;/* interleave (MSB) */ + u_char interleave_0;/* interleave (LSB) */ + u_char trk_skew_1; /* track skew factor (MSB) */ + u_char trk_skew_0; /* track skew factor (LSB) */ + u_char cyl_skew_1; /* cylinder skew (MSB) */ + u_char cyl_skew_0; /* cylinder skew (LSB) */ + u_char flags; /* various */ +#define DISK_FMT_SURF 0x10 +#define DISK_FMT_RMB 0x20 +#define DISK_FMT_HSEC 0x40 +#define DISK_FMT_SSEC 0x80 + u_char reserved2; + u_char reserved3; + } disk_format; + struct page_rigid_geometry { + u_char pg_code; /* page code (should be 4) */ + u_char pg_length; /* page length (should be 0x16) */ + u_char ncyl_2; /* number of cylinders (MSB) */ + u_char ncyl_1; /* number of cylinders */ + u_char ncyl_0; /* number of cylinders (LSB) */ + u_char nheads; /* number of heads */ + u_char st_cyl_wp_2; /* starting cyl., write precomp (MSB) */ + u_char st_cyl_wp_1; /* starting cyl., write precomp */ + u_char st_cyl_wp_0; /* starting cyl., write precomp (LSB) */ + u_char st_cyl_rwc_2;/* starting cyl., red. write cur (MSB)*/ + u_char st_cyl_rwc_1;/* starting cyl., red. write cur */ + u_char st_cyl_rwc_0;/* starting cyl., red. write cur (LSB)*/ + u_char driv_step_1; /* drive step rate (MSB) */ + u_char driv_step_0; /* drive step rate (LSB) */ + u_char land_zone_2; /* landing zone cylinder (MSB) */ + u_char land_zone_1; /* landing zone cylinder */ + u_char land_zone_0; /* landing zone cylinder (LSB) */ + u_char reserved1; + u_char reserved2; + u_char reserved3; + } rigid_geometry; +} ; +#endif /* _SCSI_SCSI_DISK_H*/ diff --git a/sys/scsi/scsi_generic.h b/sys/scsi/scsi_generic.h new file mode 100644 index 0000000..44f2bd1 --- /dev/null +++ b/sys/scsi/scsi_generic.h @@ -0,0 +1,63 @@ +/* + * Contributed by HD Associates (hd@world.std.com). + * Copyright (c) 1992, 1993 HD Associates + * + * Berkeley style copyright. I've just snarfed it out of stdio.h: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)stdio.h 5.17 (Berkeley) 6/3/91 + * $Id$ + */ + +/* generic SCSI header file. We use the same minor number format + * as on SGI except that the flag bits aren't available because they + * are used as the board index. + * + * The minor number format is: + * FF UUU III (FFUU UIII) + * + * Where: + * FF is the board index + * UUU are the LUN + * III is the SCSI ID (controller) + */ + +#ifndef _SCSI_GENERIC_H_ +#define _SCSI_GENERIC_H_ + +#define G_SCSI_FLAG(DEV) (((DEV) & 0xC0) >> 6) +#define G_SCSI_UNIT(DEV) G_SCSI_FLAG(DEV) +#define G_SCSI_LUN(DEV) (((DEV) & 0x38) >> 3) +#define G_SCSI_ID(DEV) ((DEV) & 0x7) + +#define G_SCSI_MINOR(FLAG, LUN, ID) \ + (((FLAG) << 6) | ((LUN) << 3) | (ID)) + +#endif /* _SCSI_GENERIC_H_ */ diff --git a/sys/scsi/scsi_ioctl.c b/sys/scsi/scsi_ioctl.c new file mode 100644 index 0000000..d35ad2d --- /dev/null +++ b/sys/scsi/scsi_ioctl.c @@ -0,0 +1,332 @@ +/* + * Contributed by HD Associates (hd@world.std.com). + * Copyright (c) 1992, 1993 HD Associates + * + * Berkeley style copyright. + * + * + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#define b_screq b_driver1 /* a patch in buf.h */ +#define b_sc_link b_driver2 /* a patch in buf.h */ +#include <sys/proc.h> +#include <vm/vm.h> + +#include "scbus.h" +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <sys/scsiio.h> + +void scsierr(struct buf *, int); /* XXX ??? */ + +/* + * We let the user interpret his own sense in the generic scsi world. + * This routine is called at interrupt time if the SCSI_USER bit was set + * in the flags passed to scsi_scsi_cmd(). No other completion processing + * takes place, even if we are running over another device driver. + * The lower level routines that call us here, will free the xs and restart + * the device's queue if such exists. + */ +#ifndef min +#define min(A,B) ((A<B) ? A : B ) +#endif + +void scsi_user_done(xs) +struct scsi_xfer *xs; +{ + + struct buf *bp; + scsireq_t *screq; + + bp = xs->bp; + if(!bp) { /* ALL user requests must have a buf */ + sc_print_addr(xs->sc_link); + printf("User command with no buf\n"); + return ; + } + screq = bp->b_screq; + if (!screq) { /* Is it one of ours? (the SCSI_USER bit says it is) */ + sc_print_addr(xs->sc_link); + printf("User command with no request\n"); + return ; + } + + SC_DEBUG(xs->sc_link,SDEV_DB2,("user-done\n")); + screq->retsts = 0; + screq->status = xs->status; + switch((int)xs->error) { + case XS_NOERROR: + SC_DEBUG(xs->sc_link,SDEV_DB3,("no error\n")); + screq->datalen_used = xs->datalen - xs->resid; /* probably rubbish */ + screq->retsts = SCCMD_OK; + break; + + case XS_SENSE: + SC_DEBUG(xs->sc_link,SDEV_DB3,("have sense\n")); + screq->senselen_used = min(sizeof(xs->sense),SENSEBUFLEN); + bcopy(&xs->sense,screq->sense,screq->senselen); + screq->retsts = SCCMD_SENSE; + break; + + case XS_DRIVER_STUFFUP: + sc_print_addr(xs->sc_link); + printf("host adapter code inconsistency\n"); + screq->retsts = SCCMD_UNKNOWN; + break; + + case XS_TIMEOUT: + SC_DEBUG(xs->sc_link,SDEV_DB3,("timeout\n")); + screq->retsts = SCCMD_TIMEOUT; + break; + + case XS_BUSY: + SC_DEBUG(xs->sc_link,SDEV_DB3,("busy\n")); + screq->retsts = SCCMD_BUSY; + break; + + default: + sc_print_addr(xs->sc_link); + printf("unknown error category from host adapter code\n"); + screq->retsts = SCCMD_UNKNOWN; + break; + } + biodone(bp); /* we're waiting on it in scsi_strategy() */ + return; /* it'll free the xs and restart any queue */ +} + + +/* Pseudo strategy function + * Called by scsi_do_ioctl() via physio/physstrat if there is to + * be data transfered, and directly if there is no data transfer. + * + * Should I reorganize this so it returns to physio instead + * of sleeping in scsiio_scsi_cmd? Is there any advantage, other + * than avoiding the probable duplicate wakeup in iodone? [PD] + * + * No, seems ok to me... [JRE] + * (I don't see any duplicate wakeups) + * + * Can't be used with block devices or raw_read/raw_write directly + * from the cdevsw/bdevsw tables because they couldn't have added + * the screq structure. [JRE] + */ +void scsistrategy(struct buf *bp) +{ + errval err; + struct scsi_link *sc_link = bp->b_sc_link; + scsireq_t *screq; + u_int32 flags = 0; + int s; + + + if(!sc_link) { + printf("user_strat: No link pointer\n"); + scsierr(bp,EINVAL); + return; + } + SC_DEBUG(sc_link,SDEV_DB2,("user_strategy\n")); + screq = bp->b_screq; + if(!screq) { + sc_print_addr(sc_link); + printf("No request block\n"); + scsierr(bp,EINVAL); + return; + } + + /* We're in trouble if physio tried to break up the + * transfer: + */ + if (bp->b_bcount != screq->datalen) { + sc_print_addr(sc_link); + printf("physio split the request.. cannot proceed\n"); + scsierr(bp, EIO); + return; + } + + if (screq->timeout == 0) { + scsierr(bp, EINVAL); + return; + } + + if (screq->cmdlen > sizeof(struct scsi_generic)) { + sc_print_addr(sc_link); + printf("cmdlen too big "); + scsierr(bp, EFAULT); + return; + } + + + if (screq->flags & SCCMD_READ) + flags |= SCSI_DATA_IN; + + if (screq->flags & SCCMD_WRITE) + flags |= SCSI_DATA_OUT; + + if (screq->flags & SCCMD_TARGET) + flags |= SCSI_TARGET; + + if (screq->flags & SCCMD_ESCAPE) + flags |= SCSI_ESCAPE; + err = scsi_scsi_cmd(sc_link, + (struct scsi_generic *)screq->cmd, + screq->cmdlen, + (u_char *)bp->b_un.b_addr, + screq->datalen, + 0, /* user must do the retries *//* ignored */ + screq->timeout, + bp, + flags | SCSI_USER); + + + + /*because there is a bp, scsi_scsi_cmd will return immediatly*/ + if (err) + { + scsierr(bp, err); + return; + } + SC_DEBUG(sc_link,SDEV_DB3,("about to sleep\n")); + s = splbio(); + while(!(bp->b_flags & B_DONE)) + { + tsleep((caddr_t)bp, PRIBIO, "scsistrat", 0); + } + splx(s); + SC_DEBUG(sc_link,SDEV_DB3,("back from sleep\n")); + return; +} + +void scsiminphys(struct buf *bp) +{ + /*XXX*//* call the adapter's minphys */ +} + + +/* + * Something (e.g. another driver) has called us + * with an sc_link for a target/lun/adapter, and a scsi + * specific ioctl to perform, better try. + * If user-level type command, we must still be running + * in the context of the calling process + */ +errval scsi_do_ioctl(struct scsi_link *sc_link, int cmd, caddr_t addr, int f) +{ + errval ret = 0; + int phys; + + SC_DEBUG(sc_link,SDEV_DB2,("scsi_do_ioctl(0x%x)\n",cmd)); + switch(cmd) + { +#if 0 + case SCIOCCOMMAND: + { + /* + * You won't believe this, but the arg copied in + * from the user space, is on the kernel stack + * for this process, so we can't write + * to it at interrupt time.. + * we need to copy it in and out! + * Make a static copy using malloc! + */ + scsireq_t *screq2 = (scsireq_t *)addr; + scsireq_t *screq = (scsireq_t *)addr; + int rwflag = (screq->flags & SCCMD_READ) ? B_READ : B_WRITE; + struct buf *bp; + caddr_t d_addr; + int len; + + if((unsigned int)screq < (unsigned int)KERNBASE) + { + screq = malloc(sizeof(scsireq_t),M_TEMP,M_WAITOK); + bcopy(screq2,screq,sizeof(scsireq_t)); + } + bp = malloc(sizeof (struct buf),M_TEMP,M_WAITOK); + bzero(bp,sizeof(struct buf)); + d_addr = screq->databuf; + bp->b_bcount = len = screq->datalen; + bp->b_screq = screq; + bp->b_sc_link = sc_link; + if (len) { + /* have data, translate it. (physio)*/ +#ifdef __NetBSD__ +#error "dev, mincntfn & uio need defining" + ret = physio(scsistrategy, bp, dev, rwflag, + mincntfn, uio); +#else + ret = physio(scsistrategy,0,bp,0,rwflag, + d_addr,&len,curproc); +#endif + } else { + /* if no data, no need to translate it.. */ + bp->b_un.b_addr = 0; + bp->b_dev = -1; /* irrelevant info */ + bp->b_flags = 0; + + scsistrategy(bp); + ret = bp->b_error; + } + free(bp,M_TEMP); + if((unsigned int)screq2 < (unsigned int)KERNBASE) + { + bcopy(screq,screq2,sizeof(scsireq_t)); + free(screq,M_TEMP); + } + break; + } +#endif /* !NetBSD */ + case SCIOCDEBUG: + { + int level = *((int *)addr); + SC_DEBUG(sc_link,SDEV_DB3,("debug set to %d\n",level)); + sc_link->flags &= ~SDEV_DBX; /*clear debug bits */ + if(level & 1) sc_link->flags |= SDEV_DB1; + if(level & 2) sc_link->flags |= SDEV_DB2; + if(level & 4) sc_link->flags |= SDEV_DB3; + if(level & 8) sc_link->flags |= SDEV_DB4; + ret = 0; + break; + } + case SCIOCREPROBE: + { + extern int scsibus; + struct scsi_addr *sca = (struct scsi_addr *) addr; + + ret = scsi_probe_busses(sca->scbus,sca->target,sca->lun); + break; + } + case SCIOCRECONFIG: + case SCIOCDECONFIG: + ret = EINVAL; + break; + case SCIOCIDENTIFY: + { + struct scsi_addr *sca = (struct scsi_addr *) addr; + sca->scbus = sc_link->scsibus; + sca->target = sc_link->target; + sca->lun = sc_link->lun; + break; + } + + default: + ret = ENOTTY; + break; + } + + return ret; +} + +void +scsierr(bp,err) + struct buf *bp; + int err; +{ + bp->b_flags |= B_ERROR; + bp->b_error = err; + biodone(bp); + return; +} + diff --git a/sys/scsi/scsi_tape.h b/sys/scsi/scsi_tape.h new file mode 100644 index 0000000..ac417c2 --- /dev/null +++ b/sys/scsi/scsi_tape.h @@ -0,0 +1,204 @@ +/* + * SCSI tape interface description + */ + +/* + * Written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems. + * + * 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. + * + */ + +/* + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id: scsi_tape.h,v 1.8 93/08/31 21:40:16 julian Exp Locker: julian $ + */ +#ifndef SCSI_SCSI_TAPE_H +#define SCSI_SCSI_TAPE_H 1 + + + +/* + * SCSI command formats + */ + + +struct scsi_rw_tape +{ + u_char op_code; + u_char byte2; +#define SRWT_FIXED 0x01 + u_char len[3]; + u_char control; +} rw_tape; + +struct scsi_space +{ + u_char op_code; + u_char byte2; +#define SS_CODE 0x03 + u_char number[3]; + u_char control; +} space; +#define SP_BLKS 0 +#define SP_FILEMARKS 1 +#define SP_SEQ_FILEMARKS 2 +#define SP_EOM 3 + +struct scsi_write_filemarks +{ + u_char op_code; + u_char byte2; + u_char number[3]; + u_char control; +} write_filemarks; + +struct scsi_rewind +{ + u_char op_code; + u_char byte2; +#define SR_IMMED 0x01 + u_char unused[3]; + u_char control; +} rewind; + +struct scsi_load +{ + u_char op_code; + u_char byte2; +#define SL_IMMED 0x01 + u_char unused[2]; + u_char how; + u_char control; +} load; +#define LD_UNLOAD 0 +#define LD_LOAD 1 +#define LD_RETEN 2 + + +struct scsi_blk_limits +{ + u_char op_code; + u_char byte2; + u_char unused[3]; + u_char control; +} blk_limits; + +/* + * Opcodes + */ + +#define REWIND 0x01 +#define READ_BLK_LIMITS 0x05 +#define READ_COMMAND_TAPE 0x08 +#define WRITE_COMMAND_TAPE 0x0a +#define WRITE_FILEMARKS 0x10 +#define SPACE 0x11 +#define LOAD_UNLOAD 0x1b /* same as above */ + + + +struct scsi_blk_limits_data +{ + u_char reserved; + u_char max_length_2; /* Most significant */ + u_char max_length_1; + u_char max_length_0; /* Least significant */ + u_char min_length_1; /* Most significant */ + u_char min_length_0; /* Least significant */ +}; + +/* defines for the device specific byte in the mode select/sense header */ +#define SMH_DSP_SPEED 0x0F +#define SMH_DSP_BUFF_MODE 0x70 +#define SMH_DSP_BUFF_MODE_OFF 0x00 +#define SMH_DSP_BUFF_MODE_ON 0x10 +#define SMH_DSP_BUFF_MODE_MLTI 0x20 +#define SMH_DSP_WRITE_PROT 0x80 + +/* A special for the CIPHER ST150S(old drive) */ +struct blk_desc_cipher +{ + u_char density; + u_char nblocks[3]; + u_char reserved; + u_char blklen[3]; + u_char other; +#define ST150_SEC 0x01 /* soft error count */ +#define SR150_AUI 0x02 /* autoload inhibit */ +}; + + + +/********************************************************************** + from the scsi2 spec + Value Tracks Density(bpi) Code Type Reference Note + 0x1 9 800 NRZI R X3.22-1983 2 + 0x2 9 1600 PE R X3.39-1986 2 + 0x3 9 6250 GCR R X3.54-1986 2 + 0x5 4/9 8000 GCR C X3.136-1986 1 + 0x6 9 3200 PE R X3.157-1987 2 + 0x7 4 6400 IMFM C X3.116-1986 1 + 0x8 4 8000 GCR CS X3.158-1986 1 + 0x9 18 37871 GCR C X3B5/87-099 2 + 0xA 22 6667 MFM C X3B5/86-199 1 + 0xB 4 1600 PE C X3.56-1986 1 + 0xC 24 12690 GCR C HI-TC1 1,5 + 0xD 24 25380 GCR C HI-TC2 1,5 + 0xF 15 10000 GCR C QIC-120 1,5 + 0x10 18 10000 GCR C QIC-150 1,5 + 0x11 26 16000 GCR C QIC-320(525?) 1,5 + 0x12 30 51667 RLL C QIC-1350 1,5 + 0x13 1 61000 DDS CS X3B5/88-185A 4 + 0x14 1 43245 RLL CS X3.202-1991 4 + 0x15 1 45434 RLL CS ECMA TC17 4 + 0x16 48 10000 MFM C X3.193-1990 1 + 0x17 48 42500 MFM C X3B5/91-174 1 + + where Code means: + NRZI Non Return to Zero, change on ones + GCR Group Code Recording + PE Phase Encoded + IMFM Inverted Modified Frequency Modulation + MFM Modified Frequency Modulation + DDS Dat Data Storage + RLL Run Length Encoding + + where Type means: + R Real-to-Real + C Cartridge + CS cassette + + where Notes means: + 1 Serial Recorded + 2 Parallel Recorded + 3 Old format know as QIC-11 + 4 Helical Scan + 5 Not ANSI standard, rather industry standard. + +********************************************************************/ + +#define HALFINCH_800 0x01 +#define HALFINCH_1600 0x02 +#define HALFINCH_6250 0x03 +#define QIC_11 0x04 /* from Archive 150S Theory of Op. XXX */ +#define QIC_24 0x05 /* may be bad, works for CIPHER ST150S XXX */ +#define QIC_120 0x0f +#define QIC_150 0x10 +#define QIC_320 0x11 +#define QIC_525 0x11 +#define QIC_1320 0x12 +#define DDS 0x13 +#define DAT_1 0x13 + +#endif /*SCSI_SCSI_TAPE_H*/ diff --git a/sys/scsi/scsiconf.c b/sys/scsi/scsiconf.c new file mode 100644 index 0000000..9e2dfef --- /dev/null +++ b/sys/scsi/scsiconf.c @@ -0,0 +1,699 @@ +/* + * 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. + * + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id: scsiconf.c,v 1.7 1993/11/18 05:02:58 rgrimes Exp $ + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> + +#include <sys/malloc.h> +#include "st.h" +#include "sd.h" +#include "ch.h" +#include "cd.h" +#include "uk.h" +#include "su.h" +#ifndef NSCBUS +#define NSCBUS 8 +#endif /* NSCBUS */ + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#ifdef TFS +#include "bll.h" +#include "cals.h" +#include "kil.h" +#include "scan.h" +#else /* TFS */ +#define NBLL 0 +#define NCALS 0 +#define NKIL 0 +#define NSCAN 0 +#endif /* TFS */ + +#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 */ +#if NUK > 0 +extern ukattach(); +#endif /* NUK */ + +/* + * One of these is allocated and filled in for each scsi bus. + * it holds pointers to allow the scsi bus to get to the driver + * That is running each LUN on the bus + * it also has a template entry which is the prototype struct + * supplied by the adapter driver, this is used to initialise + * the others, before they have the rest of the fields filled in + */ +struct scsibus_data *scbus_data[NSCBUS]; + +/* + * 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; + errval(*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 + */ +struct scsidevs { + u_int32 type; + boolean removable; + char *manufacturer; + char *model; + char *version; + errval(*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 +#if NUK > 0 + +static struct scsidevs unknowndev = { + -1, 0, "standard", "any" + ,"any", ukattach, "uk", SC_MORE_LUS +}; +#endif /*NUK*/ +static struct scsidevs 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 +#ifndef UKTEST /* make cdroms unrecognised to test the uk driver */ + { + 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 +#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(); +errval scsi_probe_bus __P((int bus, int targ, int lun)); + +struct scsi_device probe_switch = +{ + NULL, + NULL, + NULL, + NULL, + "probe", + 0, + { 0, 0 } +}; + +/* + * controls debug level within the scsi subsystem - + * see scsiconf.h for values + */ +int32 scsibus = 0x0; /* This is the Nth scsibus we've seen */ + +/* + * The routine called by the adapter boards to get all their + * devices configured in. + */ +void +scsi_attachdevs(sc_link_proto) + struct scsi_link *sc_link_proto; +{ + + if(scsibus >= NSCBUS) { + printf("too many scsi busses, reconfigure the kernel\n"); + return; + } + sc_link_proto->scsibus = scsibus; + scbus_data[scsibus] = malloc(sizeof(struct scsibus_data), M_TEMP, M_NOWAIT); + if(!scbus_data[scsibus]) { + panic("scsi_attachdevs: malloc\n"); + } + bzero(scbus_data[scsibus], sizeof(struct scsibus_data)); + scbus_data[scsibus]->adapter_link = sc_link_proto; +#if defined(SCSI_DELAY) && SCSI_DELAY > 2 + printf("%s%d waiting for scsi devices to settle\n", + sc_link_proto->adapter->name, sc_link_proto->adapter_unit); +#else /* SCSI_DELAY > 2 */ +#undef SCSI_DELAY +#define SCSI_DELAY 2 +#endif /* SCSI_DELAY */ + DELAY(1000000 * SCSI_DELAY); + scsibus++; + scsi_probe_bus(scsibus - 1,-1,-1); +} + +/* + * Probe the requested scsi bus. It must be already set up. + * -1 requests all set up scsi busses. + * targ and lun optionally narrow the search if not -1 + */ +errval +scsi_probe_busses(int bus, int targ, int lun) +{ + if (bus == -1) { + for(bus = 0; bus < scsibus; bus++) { + scsi_probe_bus(bus, targ, lun); + } + return 0; + } else { + return scsi_probe_bus(bus, targ, lun); + } +} + +/* + * Probe the requested scsi bus. It must be already set up. + * targ and lun optionally narrow the search if not -1 + */ +errval +scsi_probe_bus(int bus, int targ, int lun) +{ + struct scsibus_data *scsi ; + int maxtarg,mintarg,maxlun,minlun; + struct scsi_link *sc_link_proto; + u_int8 scsi_addr ; + struct scsidevs *bestmatch = NULL; + struct predefined *predef = NULL; + struct scsi_link *sc_link = NULL; + boolean maybe_more; + + if ((bus < 0 ) || ( bus >= scsibus)) { + return ENXIO; + } + scsi = scbus_data[bus]; + if(!scsi) return ENXIO; + sc_link_proto = scsi->adapter_link; + scsi_addr = sc_link_proto->adapter_targ; + if(targ == -1){ + maxtarg = 7; + mintarg = 0; + } else { + if((targ < 0 ) || (targ > 7)) return EINVAL; + maxtarg = mintarg = targ; + } + + if(lun == -1){ + maxlun = 7; + minlun = 0; + } else { + if((lun < 0 ) || (lun > 7)) return EINVAL; + maxlun = minlun = lun; + } + + + for ( targ = mintarg;targ <= maxtarg; targ++) { + maybe_more = 0; /* by default only check 1 lun */ + if (targ == scsi_addr) { + continue; + } + for ( lun = minlun; lun <= maxlun ;lun++) { + /* + * The spot appears to already have something + * linked in, skip past it. Must be doing a 'reprobe' + */ + if(scsi->sc_link[targ][lun]) + {/* don't do this one, but check other luns */ + maybe_more = 1; + continue; + } + /* + * If we presently don't have a link block + * then allocate one to use while probing + */ + if (!sc_link) { + sc_link = malloc(sizeof(*sc_link), M_TEMP, M_NOWAIT); + *sc_link = *sc_link_proto; /* struct copy */ + sc_link->opennings = 1; + sc_link->device = &probe_switch; + } + sc_link->target = targ; + sc_link->lun = lun; + predef = scsi_get_predef(sc_link, &maybe_more); + bestmatch = scsi_probedev(sc_link, &maybe_more); + if ((bestmatch) && (predef)) { /* both exist */ + if (bestmatch->attach_rtn + != predef->attach_rtn) { + printf("Clash in found/expected devices\n"); +#if NUK > 0 + if(bestmatch == &unknowndev) { + printf("will link in PREDEFINED\n"); + (*(predef->attach_rtn)) (sc_link); + } else +#endif /*NUK*/ + { + printf("will link in FOUND\n"); + (*(bestmatch->attach_rtn)) (sc_link); + } + } else { + (*(bestmatch->attach_rtn)) (sc_link); + } + } + if ((bestmatch) && (!predef)) { /* just FOUND */ + (*(bestmatch->attach_rtn)) (sc_link); + } + if ((!bestmatch) && (predef)) { /* just predef */ + (*(predef->attach_rtn)) (sc_link); + } + if ((bestmatch) || (predef)) { /* one exists */ + scsi->sc_link[targ][lun] = sc_link; + sc_link = NULL; /* it's been used */ + } + if (!(maybe_more)) { /* nothing suggests we'll find more */ + break; /* nothing here, skip to next targ */ + } + /* otherwise something says we should look further */ + } + } + if (sc_link) { + free(sc_link, M_TEMP); + } + return 0; +} + +/* + * given a target and lu, check if there is a predefined device for + * that address + */ +struct predefined * +scsi_get_predef(sc_link, maybe_more) + struct scsi_link *sc_link; + boolean *maybe_more; +{ + u_int8 unit = sc_link->scsibus; + u_int8 target = sc_link->target; + u_int8 lu = sc_link->lun; + struct scsi_adapter *scsi_adapter = sc_link->adapter; + u_int32 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("%s%d targ %d lun %d: <%s> - PRECONFIGURED -\n" + ,scsi_adapter->name + ,unit + ,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(sc_link, maybe_more) + boolean *maybe_more; + struct scsi_link *sc_link; +{ + u_int8 unit = sc_link->adapter_unit; + u_int8 target = sc_link->target; + u_int8 lu = sc_link->lun; + struct scsi_adapter *scsi_adapter = sc_link->adapter; + struct scsidevs *bestmatch = (struct scsidevs *) 0; + char *dtype = (char *) 0, *desc; + char *qtype; + static struct scsi_inquiry_data inqbuf; + u_int32 len, qualifier, type; + boolean remov; + char manu[32]; + char model[32]; + char version[32]; + + bzero(&inqbuf, sizeof(inqbuf)); + /* + * Ask the device what it is + */ +#ifdef SCSIDEBUG + if ((target == DEBUGTARG) && (lu == DEBUGLUN)) + sc_link->flags |= (DEBUGLEVEL); + else + sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2 | SDEV_DB3 | SDEV_DB4); +#endif /* SCSIDEBUG */ + /* catch unit attn */ + scsi_test_unit_ready(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT); +#ifdef DOUBTFULL + switch (scsi_test_unit_ready(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) { + case 0: /* said it WAS ready */ + case EBUSY: /* replied 'NOT READY' but WAS present, continue */ + case ENXIO: + break; + case EIO: /* device timed out */ + case EINVAL: /* Lun not supported */ + default: + return (struct scsidevs *) 0; + + } +#endif /*DOUBTFULL*/ +#ifdef SCSI_2_DEF + /* some devices need to be told to go to SCSI2 */ + /* However some just explode if you tell them this.. leave it out */ + scsi_change_def(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT); +#endif /*SCSI_2_DEF */ + + /* Now go ask the device all about itself */ + if (scsi_inquire(sc_link, &inqbuf, SCSI_NOSLEEP | SCSI_NOMASK) != 0) { + return (struct scsidevs *) 0; + } + + /* + * note what BASIC type of device it is + */ + type = inqbuf.device & SID_TYPE; + qualifier = inqbuf.device & SID_QUAL; + remov = inqbuf.dev_qual2 & SID_REMOVABLE; + + /* + * Any device qualifier that has the top bit set (qualifier&4 != 0) + * is vendor specific and won't match in this switch. + */ + + switch ((int)qualifier) { + case SID_QUAL_LU_OK: + qtype = ""; + break; + + case SID_QUAL_LU_OFFLINE: + qtype = ", Unit not Connected!"; + break; + + case SID_QUAL_RSVD: + qtype = ", Reserved Peripheral Qualifier!"; + *maybe_more = 1; + return (struct scsidevs *) 0; + break; + + case SID_QUAL_BAD_LU: + /* + * Check for a non-existent unit. If the device is returning + * this much, then we must set the flag that has + * the searchers keep looking on other luns. + */ + qtype = ", The Target can't support this Unit!"; + *maybe_more = 1; + return (struct scsidevs *) 0; + + default: + dtype = "vendor specific"; + qtype = ""; + *maybe_more = 1; + break; + } + if (dtype == 0) { + switch ((int)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; + case T_NODEVICE: + *maybe_more = 1; + return (struct scsidevs *) 0; + default: + dtype = "unknown"; + break; + } + } + /* + * Then if it's advanced enough, more detailed + * information + */ + if ((inqbuf.version & SID_ANSII) > 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("%s%d targ %d lun %d: type %d(%s) %s SCSI%d\n" + ,scsi_adapter->name + ,unit + ,target + ,lu + ,type + ,dtype + ,remov ? "removable" : "fixed" + ,inqbuf.version & SID_ANSII + ); + printf("%s%d targ %d lun %d: <%s%s%s>\n" + ,scsi_adapter->name + ,unit + ,target + ,lu + ,manu + ,model + ,version + ); + if (qtype[0]) { + printf("%s%d targ %d lun %d: qualifier %d(%s)\n" + ,scsi_adapter->name + ,unit + ,target + ,lu + ,qualifier + ,qtype + ); + } + /* + * Try make as good a match as possible with + * available sub drivers + */ + bestmatch = (selectdev( + qualifier, type, remov ? T_REMOV : T_FIXED, 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(qualifier, type, remov, manu, model, rev) + u_int32 qualifier, type; + boolean remov; + char *manu, *model, *rev; +{ + u_int32 numents = (sizeof(knowndevs) / sizeof(struct scsidevs)) - 1; + u_int32 count = 0; + u_int32 bestmatches = 0; + struct scsidevs *bestmatch = (struct scsidevs *) 0; + struct scsidevs *thisentry = knowndevs; + + type |= qualifier; /* why? */ + + 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) { +#if NUK > 0 + bestmatch = &unknowndev; +#else + printf("No explicit device driver match.\n"); +#endif + } + return (bestmatch); +} diff --git a/sys/scsi/scsiconf.h b/sys/scsi/scsiconf.h new file mode 100644 index 0000000..f3f9586 --- /dev/null +++ b/sys/scsi/scsiconf.h @@ -0,0 +1,249 @@ +/* + * 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. + * + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * + * $Id: scsiconf.h,v 1.8 1993/12/19 00:54:55 wollman Exp $ + */ +#ifndef SCSI_SCSICONF_H +#define SCSI_SCSICONF_H 1 +typedef int boolean; +typedef int errval; +typedef long int int32; +typedef short int int16; +typedef char int8; +typedef unsigned long int u_int32; +typedef unsigned short int u_int16; +typedef unsigned char u_int8; + +#include <scsi/scsi_debug.h> + +/* + * The following documentation tries to describe the relationship between the + * various structures defined in this file: + * + * each adapter type has a scsi_adapter struct. This describes the adapter and + * identifies routines that can be called to use the adapter. + * each device type has a scsi_device struct. This describes the device and + * identifies routines that can be called to use the device. + * each existing device position (scsibus + target + lun) + * can be described by a scsi_link struct. + * Only scsi positions that actually have devices, have a scsi_link + * structure assigned. so in effect each device has scsi_link struct. + * The scsi_link structure contains information identifying both the + * device driver and the adapter driver for that position on that scsi bus, + * and can be said to 'link' the two. + * each individual scsi bus has an array that points to all the scsi_link + * structs associated with that scsi bus. Slots with no device have + * a NULL pointer. + * each individual device also knows the address of it's own scsi_link + * structure. + * + * ------------- + * + * The key to all this is the scsi_link structure which associates all the + * other structures with each other in the correct configuration. The + * scsi_link is the connecting information that allows each part of the + * scsi system to find the associated other parts. + */ + + +/* + * These entrypoints are called by the high-end drivers to get services from + * whatever low-end drivers they are attached to each adapter type has one of + * these statically allocated. + */ +struct scsi_adapter +{ +/* 04*/ int32 (*scsi_cmd)(); +/* 08*/ void (*scsi_minphys)(); +/* 12*/ int32 (*open_target_lu)(); +/* 16*/ int32 (*close_target_lu)(); +/* 20*/ u_int32 (*adapter_info)(); /* see definitions below */ +/* 24*/ char *name; /* name of scsi bus controller */ +/* 32*/ u_long spare[2]; +}; + +/* + * return values for scsi_cmd() + */ +#define SUCCESSFULLY_QUEUED 0 +#define TRY_AGAIN_LATER 1 +#define COMPLETE 2 +#define HAD_ERROR 3 /* do not use this, use COMPLETE */ +#define ESCAPE_NOT_SUPPORTED 4 + +/* + * Format of adapter_info() response data + * e.g. maximum number of entries queuable to a device by the adapter + */ +#define AD_INF_MAX_CMDS 0x000000FF +/* 24 bits of other adapter characteristics go here */ + +/* + * These entry points are called by the low-end drivers to get services from + * whatever high-end drivers they are attached to. Each device type has one + * of these statically allocated. + */ +struct scsi_device +{ +/* 4*/ errval (*err_handler)(); /* returns -1 to say err processing complete */ +/* 8*/ void (*start)(); +/* 12*/ int32 (*async)(); +/* 16*/ int32 (*done)(); /* returns -1 to say done processing complete */ +/* 20*/ char *name; /* name of device type */ +/* 24*/ u_int32 flags; /* device type dependent flags */ +/* 32*/ int32 spare[2]; +}; + +/* + * This structure describes the connection between an adapter driver and + * a device driver, and is used by each to call services provided by + * the other, and to allow generic scsi glue code to call these services + * as well. + */ +struct scsi_link +{ +/* 1*/ u_int8 target; /* targ of this dev */ +/* 2*/ u_int8 lun; /* lun of this dev */ +/* 3*/ u_int8 adapter_targ; /* what are we on the scsi bus */ +/* 4*/ u_int8 adapter_unit; /* e.g. the 0 in aha0 */ +/* 5*/ u_int8 scsibus; /* the Nth scsibus */ +/* 6*/ u_int8 dev_unit; /* e.g. the 0 in sd0 */ +/* 7*/ u_int8 opennings; /* available operations */ +/* 8*/ u_int8 active; /* operations in progress */ +/* 10*/ u_int16 flags; /* flags that all devices have */ +/* 12*/ u_int8 spareb[2]; /* unused */ +/* 16*/ struct scsi_adapter *adapter; /* adapter entry points etc. */ +/* 20*/ struct scsi_device *device; /* device entry points etc. */ +/* 24*/ struct scsi_xfer *active_xs; /* operations under way */ +/* 28*/ void * fordriver; /* for private use by the driver */ +/* 32*/ u_int32 spare; +}; +#define SDEV_MEDIA_LOADED 0x01 /* device figures are still valid */ +#define SDEV_WAITING 0x02 /* a process is waiting for this */ +#define SDEV_OPEN 0x04 /* at least 1 open session */ +#define SDEV_BOUNCE 0x08 /* unit requires DMA bounce buffer */ +#define SDEV_DBX 0xF0 /* debuging flags (scsi_debug.h) */ + +/* + * One of these is allocated and filled in for each scsi bus. + * it holds pointers to allow the scsi bus to get to the driver + * That is running each LUN on the bus + * it also has a template entry which is the prototype struct + * supplied by the adapter driver, this is used to initialise + * the others, before they have the rest of the fields filled in + */ +struct scsibus_data { + struct scsi_link *adapter_link; /* prototype supplied by adapter */ + struct scsi_link *sc_link[8][8]; +}; + +/* + * Each scsi transaction is fully described by one of these structures + * It includes information about the source of the command and also the + * device and adapter for which the command is destined. + * (via the scsi_link structure) * + */ +struct scsi_xfer +{ +/*04*/ struct scsi_xfer *next; /* when free */ +/*08*/ u_int32 flags; +/*12*/ struct scsi_link *sc_link; /* all about our device and adapter */ +/*13*/ u_int8 retries; /* the number of times to retry */ +/*16*/ u_int8 spare[3]; +/*20*/ int32 timeout; /* in milliseconds */ +/*24*/ struct scsi_generic *cmd; /* The scsi command to execute */ +/*28*/ int32 cmdlen; /* how long it is */ +/*32*/ u_char *data; /* dma address OR a uio address */ +/*36*/ int32 datalen; /* data len (blank if uio) */ +/*40*/ int32 resid; /* how much buffer was not touched */ +/*44*/ int32 error; /* an error value */ +/*48*/ struct buf *bp; /* If we need to associate with a buf */ +/*80*/ struct scsi_sense_data sense; /* 32 bytes*/ + /* + * Believe it or not, Some targets fall on the ground with + * anything but a certain sense length. + */ +/*84*/ int32 req_sense_length; /* Explicit request sense length */ +/*88*/ int32 status; /* SCSI status */ +/*100*/ struct scsi_generic cmdstore; /* stash the command in here */ +}; + +/* + * Per-request 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 SCSI_USER 0x08 /* Is a user cmd, call scsi_user_done */ +#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. */ +#define SCSI_ESCAPE 0x2000 /* Escape operation */ + +/* + * Escape op codes. This provides an extensible setup for operations + * that are not scsi commands. They are intended for modal operations. + */ + +#define SCSI_OP_TARGET 0x0001 +#define SCSI_OP_RESET 0x0002 +#define SCSI_OP_BDINFO 0x0003 + +/* + * Error values an adapter driver may return + */ +#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? */ + +void scsi_attachdevs __P((struct scsi_link *sc_link_proto)); +struct scsi_xfer *get_xs( struct scsi_link *sc_link, u_int32 flags); +void free_xs(struct scsi_xfer *xs, struct scsi_link *sc_link,u_int32 flags); +u_int32 scsi_size( struct scsi_link *sc_link,u_int32 flags); +errval scsi_test_unit_ready( struct scsi_link *sc_link, u_int32 flags); +errval scsi_change_def( struct scsi_link *sc_link, u_int32 flags); +errval scsi_inquire( struct scsi_link *sc_link, + struct scsi_inquiry_data *inqbuf, u_int32 flags); +errval scsi_prevent( struct scsi_link *sc_link, u_int32 type,u_int32 flags); +errval scsi_start_unit( struct scsi_link *sc_link, u_int32 flags); +void scsi_done(struct scsi_xfer *xs); +errval scsi_scsi_cmd( struct scsi_link *sc_link, struct scsi_generic *scsi_cmd, + u_int32 cmdlen, u_char *data_addr, + u_int32 datalen, u_int32 retries, + u_int32 timeout, struct buf *bp, + u_int32 flags); +errval scsi_do_ioctl __P((struct scsi_link *sc_link, int cmd, caddr_t addr, int f)); + +void show_scsi_xs(struct scsi_xfer *xs); +void show_scsi_cmd(struct scsi_xfer *xs); +void show_mem(unsigned char * , u_int32); + +void lto3b __P((int val, u_char *bytes)); +int _3btol __P((u_char *bytes)); + +extern void sc_print_addr(struct scsi_link *); + +#endif /*SCSI_SCSICONF_H*/ +/* END OF FILE */ diff --git a/sys/scsi/sd.c b/sys/scsi/sd.c new file mode 100644 index 0000000..691430e --- /dev/null +++ b/sys/scsi/sd.c @@ -0,0 +1,1072 @@ +/* + * Written by Julian Elischer (julian@dialix.oz.au) + * 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. + * + * Ported to run under 386BSD by Julian Elischer (julian@dialix.oz.au) Sept 1992 + * + * $Id: sd.c,v 1.23 1994/04/20 07:06:57 davidg Exp $ + */ + +#define SPLSD splbio +#define ESUCCESS 0 +#include <sd.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/dkbad.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/buf.h> +#include <sys/uio.h> +#include <sys/malloc.h> +#include <sys/errno.h> +#include <sys/dkstat.h> +#include <sys/disklabel.h> +#include <scsi/scsi_all.h> +#include <scsi/scsi_disk.h> +#include <scsi/scsiconf.h> +#include <vm/vm.h> + +u_int32 sdstrats, sdqueues; + +#ifdef NetBSD +#ifdef DDB +int Debugger(); +#else /* DDB */ +#define Debugger() +#endif /* DDB */ +#else /* NetBSD */ +#include <ddb.h> +#if NDDB > 0 +#else /* NDDB > 0 */ +#define Debugger(s) +#endif /* NDDB > 0 */ +#endif + +#define PAGESIZ 4096 +#define SECSIZE 512 +#define PDLOCATION 29 +#define BOOTRECORDSIGNATURE (0x55aa & 0x00ff) +#define SDOUTSTANDING 2 +#define SDQSIZE 4 +#define SD_RETRIES 4 +#define MAXTRANSFER 8 /* 1 page at a time */ + +#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 ) + +extern char *readdisklabel(); +errval sdgetdisklabel __P((unsigned char unit)); +errval sd_get_parms __P((int unit, int flags)); +void sdstrategy __P((struct buf *)); +void sdstart __P((u_int32)); + +struct scsi_device sd_switch = +{ + NULL, /* Use default error handler */ + sdstart, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ + "sd", + 0, + { 0, 0 } +}; + +struct sd_data { + u_int32 flags; +#define SDINIT 0x04 /* device has been init'd */ +#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_link *sc_link; /* contains our targ, lun etc. */ + u_int32 ad_info; /* info about the adapter */ + u_int32 cmdscount; /* cmds allowed outstanding by board */ + boolean wlabel; /* label is writable */ + struct disk_parms { + u_char heads; /* Number of heads */ + u_int16 cyls; /* Number of cylinders */ + u_char sectors; /*dubious *//* Number of sectors/track */ + u_int16 secsiz; /* Number of bytes/sector */ + u_int32 disksize; /* total number sectors */ + } params; + struct disklabel disklabel; +#ifdef NetBSD + struct cpu_disklabel cpudisklabel; +#else + struct dos_partition dosparts[NDOSPART]; /* DOS view of disk */ +#endif /* NetBSD */ + u_int32 partflags[MAXPARTITIONS]; /* per partition flags */ +#define SDOPEN 0x01 + u_int32 openparts; /* one bit for each open partition */ + u_int32 sd_start_of_unix; /* unix vs dos partitions */ + struct buf buf_queue; + u_int32 xfer_block_wait; +} *sd_data[NSD]; + +static u_int32 next_sd_unit = 0; + +static struct scsi_xfer sx; + +/* + * The routine called by the low level scsi routine when it discovers + * a device suitable for this driver. + */ +errval +sdattach(sc_link) + struct scsi_link *sc_link; +{ + u_int32 unit; + struct sd_data *sd; + struct disk_parms *dp; + + unit = next_sd_unit++; + SC_DEBUG(sc_link, SDEV_DB2, ("sdattach: ")); + /* + * Check we have the resources for another drive + */ + if (unit >= NSD) { + printf("Too many scsi disks..(%d > %d) reconfigure kernel\n", + (unit + 1), NSD); + return 0; + } + if (sd_data[unit]) { + printf("sd%d: unit already has storage allocated!\n", unit); + return 0; + } + sd = sd_data[unit] = malloc(sizeof(struct sd_data), M_DEVBUF, M_NOWAIT); + if (!sd) { + printf("malloc failed in sd.c\n"); + return (0); + } + bzero(sd, sizeof(struct sd_data)); + + dp = &(sd->params); + /* + * Store information needed to contact our base driver + */ + sd->sc_link = sc_link; + sc_link->device = &sd_switch; + sc_link->dev_unit = unit; + + if (sd->sc_link->adapter->adapter_info) { + sd->ad_info = ((*(sd->sc_link->adapter->adapter_info)) (sc_link->adapter_unit)); + sd->cmdscount = sd->ad_info & AD_INF_MAX_CMDS; + if (sd->cmdscount > SDOUTSTANDING) { + sd->cmdscount = SDOUTSTANDING; + } + } else { + sd->ad_info = 1; + sd->cmdscount = 1; + } + sc_link->opennings = sd->cmdscount; + /* + * 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 (%d total sec), %d cyl, %d head, %d sec, bytes/sec %d\n", + unit, + dp->disksize / ((1024L * 1024L) / dp->secsiz), + dp->disksize, + dp->cyls, + dp->heads, + dp->sectors, + dp->secsiz); + sd->flags |= SDINIT; + return 0; +} + +/* + * open the device. Make sure the partition info is a up-to-date as can be. + */ +errval +sdopen(dev) + int dev; /* XXX should be dev_t, but avoid promotion problems for now */ +{ + errval errcode = 0; + u_int32 unit, part; + struct sd_data *sd; + struct scsi_link *sc_link; + + unit = UNIT(dev); + part = PARTITION(dev); + sd = sd_data[unit]; + /* + * 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) || (!(sd->flags & SDINIT))) { + return (ENXIO); + } + sc_link = sd->sc_link; + + SC_DEBUG(sc_link, SDEV_DB1, + ("sdopen: dev=0x%x (unit %d (of %d),partition %d)\n" + ,dev, unit, NSD, part)); + + /* + * "unit attention" errors should occur here if the + * drive has been restarted or the pack changed. + * just ingnore the result, it's a decoy instruction + * The error code will act on the error though + * and invalidate any media information we had. + */ + scsi_test_unit_ready(sc_link, 0); + + /* + * If it's been invalidated, then forget the label + */ + sc_link->flags |= SDEV_OPEN; /* unit attn becomes an err now */ + if (!(sc_link->flags & SDEV_MEDIA_LOADED)) { + sd->flags &= ~SDHAVELABEL; + + /* + * If somebody still has it open, then forbid re-entry. + */ + if (sd->openparts) { + errcode = ENXIO; + goto bad; + } + } + /* + * In case it is a funny one, tell it to start + * not needed for most hard drives (ignore failure) + */ + scsi_start_unit(sc_link, SCSI_ERR_OK | SCSI_SILENT); + + /* + * Check that it is still responding and ok. + */ + if (scsi_test_unit_ready(sc_link, 0)) { + SC_DEBUG(sc_link, SDEV_DB3, ("device not reponding\n")); + errcode = ENXIO; + goto bad; + } + SC_DEBUG(sc_link, SDEV_DB3, ("device ok\n")); + + /* + * Load the physical device parameters + */ + sd_get_parms(unit, 0); /* sets SDEV_MEDIA_LOADED */ + if (sd->params.secsiz != SECSIZE) { /* XXX One day... */ + printf("sd%d: Can't deal with %d bytes logical blocks\n", + unit, sd->params.secsiz); + Debugger("sd"); + errcode = ENXIO; + goto bad; + } + SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded ")); + + /* Lock the pack in. */ + scsi_prevent(sc_link, PR_PREVENT, SCSI_ERR_OK | SCSI_SILENT); + + /* + * Load the partition info if not already loaded. + */ + if ((errcode = sdgetdisklabel(unit)) && (part != RAW_PART)) { + goto bad; + } + SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel loaded ")); + /* + * Check the partition is legal + */ + if (part >= MAXPARTITIONS) { + errcode = ENXIO; + goto bad; + } + SC_DEBUG(sc_link, SDEV_DB3, ("partition ok")); + + /* + * Check that the partition exists + */ + if ((sd->disklabel.d_partitions[part].p_size == 0) + && (part != RAW_PART)) { + errcode = ENXIO; + goto bad; + } + sd->partflags[part] |= SDOPEN; + sd->openparts |= (1 << part); + SC_DEBUG(sc_link, SDEV_DB3, ("open %d %d\n", sdstrats, sdqueues)); + return 0; + +bad: + if (!(sd->openparts)) { + scsi_prevent(sc_link, PR_ALLOW, SCSI_ERR_OK | SCSI_SILENT); + sc_link->flags &= ~SDEV_OPEN; + } + return errcode; +} + +/* + * close the device.. only called if we are the LAST occurence of an open + * device. Convenient now but usually a pain. + */ +errval +sdclose(dev) + dev_t dev; +{ + unsigned char unit, part; + struct sd_data *sd; + + unit = UNIT(dev); + part = PARTITION(dev); + sd = sd_data[unit]; + sd->partflags[part] &= ~SDOPEN; + sd->openparts &= ~(1 << part); + scsi_prevent(sd->sc_link, PR_ALLOW, SCSI_SILENT | SCSI_ERR_OK); + if (!(sd->openparts)) + sd->sc_link->flags &= ~SDEV_OPEN; + return 0; +} + +/* + * 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_link->adapter->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. + */ +void +sdstrategy(bp) + struct buf *bp; +{ + struct buf *dp; + u_int32 opri; + struct sd_data *sd; + u_int32 unit; + + sdstrats++; + unit = UNIT((bp->b_dev)); + sd = sd_data[unit]; + SC_DEBUG(sd->sc_link, SDEV_DB2, ("sdstrategy ")); + SC_DEBUG(sd->sc_link, SDEV_DB1, + (" %d bytes @ blk%d\n", bp->b_bcount, bp->b_blkno)); + sdminphys(bp); + /* + * If the device has been made invalid, error out + */ + if (!(sd->sc_link->flags & SDEV_MEDIA_LOADED)) { + sd->flags &= ~SDHAVELABEL; + 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 */ + } else { + bp->b_pblkno = bp->b_blkno; + bp->b_resid = 0; + } + opri = SPLSD(); + dp = &sd->buf_queue; + + /* + * Use a bounce buffer if necessary + */ +/* +#ifndef NOBOUNCE + if (sd->sc_link->flags & SDEV_BOUNCE) + vm_bounce_alloc(bp); +#endif +*/ + + /* + * Place it in the queue of disk activities for this disk + */ +/* + cldisksort(dp, bp, 64*1024); +*/ +if ((bp->b_blkno < 0) || (bp->b_bcount > 3000000) /* || (bp->b_flags & B_WRITE) */) { + printf("blkno=%d bcount=%d flags=0x%x\n", bp->b_blkno, bp->b_bcount, bp->b_flags); + Debugger(""); +} + 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 /*0*/; +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 /*0*/; +} + +/* + * 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 dequeues the buf and creates a scsi command to perform the + * transfer in the buf. The transfer request will call scsi_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 scsi_done + */ +void +sdstart(unit) + u_int32 unit; +{ + register struct sd_data *sd = sd_data[unit]; + register struct scsi_link *sc_link = sd->sc_link; + struct buf *bp = 0; + struct buf *dp; + struct scsi_rw_big cmd; + u_int32 blkno, nblk; + struct partition *p; + + SC_DEBUG(sc_link, SDEV_DB2, ("sdstart ")); + /* + * Check if the device has room for another command + */ + while (sc_link->opennings) { + + /* + * there is excess capacity, but a special waits + * It'll need the adapter as soon as we clear out of the + * way and let it run (user level wait). + */ + if (sc_link->flags & SDEV_WAITING) { + return; + } + /* + * See if there is a buf with work for us to do.. + */ + dp = &sd->buf_queue; + if ((bp = dp->b_actf) == NULL) { /* yes, an assign */ + return; + } + dp->b_actf = bp->b_actf; + + /* + * If the device has become invalid, abort all the + * reads and writes until all files have been closed and + * re-openned + */ + if (!(sc_link->flags & SDEV_MEDIA_LOADED)) { + sd->flags &= ~SDHAVELABEL; + goto bad; + } + /* + * We have a buf, now we know we are going to go through + * With this thing.. + * + * 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 & 0xff000000UL) >> 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); + /* + * Call the routine that chats with the adapter. + * Note: we cannot sleep as we may be an interrupt + */ + if (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &cmd, + sizeof(cmd), + (u_char *) bp->b_un.b_addr, + bp->b_bcount, + SD_RETRIES, + 10000, + bp, + SCSI_NOSLEEP | ((bp->b_flags & B_READ) ? + SCSI_DATA_IN : SCSI_DATA_OUT)) + == SUCCESSFULLY_QUEUED) { + sdqueues++; + } else { +bad: + printf("sd%d: oops not queued", unit); + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + biodone(bp); + } + } +} + +/* + * Perform special action on behalf of the user + * Knows about the internals of this device + */ +errval +sdioctl(dev_t dev, int cmd, caddr_t addr, int flag) +{ + /* struct sd_cmd_buf *args; */ + errval error = 0; + 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]; + SC_DEBUG(sd->sc_link, SDEV_DB1, ("sdioctl (0x%x)", cmd)); + + /* + * If the device is not valid.. abandon ship + */ + if (!(sd->sc_link->flags & SDEV_MEDIA_LOADED)) + 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 +#ifdef NetBSD + ,&sd->cpudisklabel +#else +#if 0 + ,sd->dosparts +#endif +#endif + ); + if (error == 0) { + sd->flags |= SDHAVELABEL; + } + break; + + case DIOCWLABEL: + sd->flags &= ~SDWRITEPROT; + if ((flag & FWRITE) == 0) + error = EBADF; + else + sd->wlabel = *(boolean *) addr; + break; + + case DIOCWDINFO: + sd->flags &= ~SDWRITEPROT; + if ((flag & FWRITE) == 0) + error = EBADF; + else { + error = setdisklabel(&sd->disklabel, + (struct disklabel *)addr, + /*(sd->flags & SDHAVELABEL) ? sd->openparts : */ 0 +#ifdef NetBSD + ,&sd->cpudisklabel +#else +#if 0 + ,sd->dosparts +#endif +#endif + ); + if (!error) { + boolean wlab; + + /* ok - write will succeed */ + sd->flags |= SDHAVELABEL; + + /* simulate opening partition 0 so write succeeds */ + sd->openparts |= (1 << 0); /* XXX */ + wlab = sd->wlabel; + sd->wlabel = 1; + error = writedisklabel(dev, sdstrategy, + &sd->disklabel +#ifdef NetBSD + ,&sd->cpudisklabel +#else +#if 0 + ,sd->dosparts +#endif +#endif + ); + sd->wlabel = wlab; + } + } + break; + + default: + if (part == RAW_PART) + error = scsi_do_ioctl(sd->sc_link, cmd, addr, flag); + else + error = ENOTTY; + break; + } + return error; +} + +/* + * Load the label information on the named device + */ +errval +sdgetdisklabel(unsigned char unit) +{ + char *errstring; + struct sd_data *sd = sd_data[unit]; + dev_t dev; + + dev = makedev(0, (unit << UNITSHIFT) + 3); + /* + * 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 + * for historical reasons, make part a same as raw part + */ + 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 = SECSIZE; /* 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 (?) */ + } + /* + * Call the generic disklabel extraction routine + */ + if (errstring = readdisklabel(makedev(0, (unit << UNITSHIFT) + 3), + sdstrategy, + &sd->disklabel +#ifdef NetBSD + ,&sd->cpu_disklabel, +#else + ,sd->dosparts, 0 +#endif + )) { + printf("sd%d: %s\n", unit, errstring); + return ENXIO; + } + sd->flags |= SDHAVELABEL; /* WE HAVE IT ALL NOW */ + return ESUCCESS; +} + +/* + * Find out from the device what it's capacity is + */ +u_int32 +sd_size(unit, flags) + int unit, flags; +{ + struct scsi_read_cap_data rdcap; + struct scsi_read_capacity scsi_cmd; + u_int32 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 (scsi_scsi_cmd(sd_data[unit]->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) & rdcap, + sizeof(rdcap), + SD_RETRIES, + 2000, + NULL, + flags | SCSI_DATA_IN) != 0) { + printf("sd%d: could not get size\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); +} + +/* + * Tell the device to map out a defective block + */ +errval +sd_reassign_blocks(unit, block) + int 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 (scsi_scsi_cmd(sd_data[unit]->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) & rbdata, + sizeof(rbdata), + SD_RETRIES, + 5000, + NULL, + SCSI_DATA_OUT)); +} +#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. + */ +errval +sd_get_parms(unit, flags) + int 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; + u_int32 sectors; + + /* + * First check if we have it all loaded + */ + if (sd->sc_link->flags & SDEV_MEDIA_LOADED) + return 0; + + /* + * do a "mode sense page 4" + */ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = MODE_SENSE; + scsi_cmd.page = 4; + scsi_cmd.length = 0x20; + /* + * If the command worked, use the results to fill out + * the parameter structure + */ + if (scsi_scsi_cmd(sd->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) & scsi_sense, + sizeof(scsi_sense), + SD_RETRIES, + 2000, + NULL, + flags | SCSI_DATA_IN) != 0) { + + printf("sd%d could not mode sense (4).", unit); + printf(" Using ficticious geometry\n"); + /* + * use adaptec standard ficticious geometry + * this depends on which controller (e.g. 1542C is + * different. but we have to put SOMETHING here..) + */ + sectors = sd_size(unit, flags); + disk_parms->heads = 64; + disk_parms->sectors = 32; + disk_parms->cyls = sectors / (64 * 32); + disk_parms->secsiz = SECSIZE; + disk_parms->disksize = sectors; + } else { + + SC_DEBUG(sd->sc_link, SDEV_DB3, + ("%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 + * can lead to wasted space! THINK ABOUT THIS ! + */ + 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); + disk_parms->disksize = sectors; + /* Check if none of these values are zero */ + if(disk_parms->heads && disk_parms->cyls) { + sectors /= (disk_parms->heads * disk_parms->cyls); + } + else { + /* set it to something reasonable */ + sectors = 32; + disk_parms->heads = 64; + disk_parms->cyls = sectors / (64 * 32); + } + disk_parms->sectors = sectors; /* dubious on SCSI *//*XXX */ + } + sd->sc_link->flags |= SDEV_MEDIA_LOADED; + return 0; +} + +int +sdsize(dev_t dev) +{ + u_int32 unit = UNIT(dev), part = PARTITION(dev), val; + struct sd_data *sd; + + if (unit >= NSD) + return -1; + + sd = sd_data[unit]; + if (!sd) + return -1; + 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) + return -1; + } + if (sd->flags & SDWRITEPROT) + return -1; + + return (int)sd->disklabel.d_partitions[part].p_size; +} + +/* + * dump all of physical memory into the partition specified, starting + * at offset 'dumplo' into the partition. + */ +errval +sddump(dev_t dev) +{ /* dump core after a system crash */ + register struct sd_data *sd; /* disk unit to do the IO */ + int32 num; /* number of sectors to write */ + u_int32 unit, part; + int32 blkoff, blknum, blkcnt = MAXTRANSFER; + int32 nblocks; + char *addr; + struct scsi_rw_big cmd; + extern int Maxmem; + static int sddoingadump = 0; + extern caddr_t CADDR1; /* map the page we are about to write, here */ + extern struct pte *CMAP1; + struct scsi_xfer *xs = &sx; + errval retval; + int c; + + addr = (char *) 0; /* starting address */ + + /* toss any characters present prior to dump */ + while ((c = sgetc(1)) && (c != 0x100)); /*syscons and pccons differ */ + + /* size of memory to dump */ + num = Maxmem; + unit = UNIT(dev); /* eventually support floppies? */ + part = PARTITION(dev); /* file system */ + /* check for acceptable drive number */ + if (unit >= NSD) + return (ENXIO); + + sd = sd_data[unit]; + if (!sd) + return (ENXIO); + /* was it ever initialized etc. ? */ + if (!(sd->flags & SDINIT)) + return (ENXIO); + if (sd->sc_link->flags & SDEV_MEDIA_LOADED != SDEV_MEDIA_LOADED) + return (ENXIO); + if (sd->flags & SDWRITEPROT) + return (ENXIO); + + /* Convert to disk sectors */ + num = (u_int32) num * NBPG / sd->disklabel.d_secsize; + + /* check if controller active */ + if (sddoingadump) + return (EFAULT); + + nblocks = sd->disklabel.d_partitions[part].p_size; + blkoff = sd->disklabel.d_partitions[part].p_offset; + + /* check transfer bounds against partition size */ + if ((dumplo < 0) || ((dumplo + num) > nblocks)) + return (EINVAL); + + sddoingadump = 1; + + blknum = dumplo + blkoff; + while (num > 0) { + *(int *)CMAP1 = + PG_V | PG_KW | trunc_page(addr); + tlbflush(); + /* + * Fill out the scsi command + */ + bzero(&cmd, sizeof(cmd)); + cmd.op_code = WRITE_BIG; + cmd.addr_3 = (blknum & 0xff000000) >> 24; + cmd.addr_2 = (blknum & 0xff0000) >> 16; + cmd.addr_1 = (blknum & 0xff00) >> 8; + cmd.addr_0 = blknum & 0xff; + cmd.length2 = (blkcnt & 0xff00) >> 8; + cmd.length1 = (blkcnt & 0xff); + /* + * Fill out the scsi_xfer structure + * Note: we cannot sleep as we may be an interrupt + * don't use scsi_scsi_cmd() as it may want + * to wait for an xs. + */ + bzero(xs, sizeof(sx)); + xs->flags |= SCSI_NOMASK | SCSI_NOSLEEP | INUSE; + xs->sc_link = sd->sc_link; + xs->retries = SD_RETRIES; + xs->timeout = 10000; /* 10000 millisecs for a disk ! */ + xs->cmd = (struct scsi_generic *) &cmd; + xs->cmdlen = sizeof(cmd); + xs->resid = blkcnt * 512; + xs->error = XS_NOERROR; + xs->bp = 0; + xs->data = (u_char *) CADDR1; + xs->datalen = blkcnt * 512; + + /* + * Pass all this info to the scsi driver. + */ + retval = (*(sd->sc_link->adapter->scsi_cmd)) (xs); + switch (retval) { + case SUCCESSFULLY_QUEUED: + case HAD_ERROR: + return (ENXIO); /* we said not to sleep! */ + case COMPLETE: + break; + default: + return (ENXIO); /* we said not to sleep! */ + } + + if ((unsigned) addr % (1024 * 1024) == 0) + printf("%d ", num / 2048); + /* update block count */ + num -= blkcnt; + blknum += blkcnt; + (int) addr += 512 * blkcnt; + + /* operator aborting dump? */ + if ((c = sgetc(1)) && (c != 0x100)) + return (EINTR); + } + return (0); +} diff --git a/sys/scsi/st.c b/sys/scsi/st.c new file mode 100644 index 0000000..b72cb76 --- /dev/null +++ b/sys/scsi/st.c @@ -0,0 +1,1936 @@ +/* + * Written by Julian Elischer (julian@tfs.com)(now julian@DIALix.oz.au) + * 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 + * 1.15 is the last version to support MACH and OSF/1 + */ +/* $Revision: 1.15 $ */ + +/* + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * major changes by Julian Elischer (julian@jules.dialix.oz.au) May 1993 + * + * $Id: st.c,v 1.15 1994/01/29 10:30:41 rgrimes Exp $ + */ + +/* + * 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 <sys/types.h> +#include <st.h> + +#include <sys/param.h> +#include <sys/systm.h> + +#include <fcntl.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/mtio.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_tape.h> +#include <scsi/scsiconf.h> + +u_int32 ststrats, stqueues; + +/* Defines for device specific stuff */ +#define PAGE_0_SENSE_DATA_SIZE 12 +#define PAGESIZ 4096 +#define DEF_FIXED_BSIZE 512 +#define ST_RETRIES 4 /* only on non IO commands */ + +#define MODE(z) ( (minor(z) & 0x03) ) +#define DSTY(z) ( ((minor(z) >> 2) & 0x03) ) +#define UNIT(z) ( (minor(z) >> 4) ) +#define CTLMODE 3 + +#define SCSI_2_MAX_DENSITY_CODE 0x17 /* maximum density code specified + * in SCSI II spec. */ +/* + * Define various devices that we know mis-behave in some way, + * and note how they are bad, so we can correct for them + */ +struct modes { + u_int32 blksiz; + u_int32 quirks; /* same definitions as in rogues */ + char density; + char spare[3]; +}; + +struct rogues { + char *name; + char *manu; + char *model; + char *version; + u_int32 quirks; /* valid for all modes */ + struct modes modes[4]; +}; + +/* define behaviour codes (quirks) */ +#define ST_Q_NEEDS_PAGE_0 0x00001 +#define ST_Q_FORCE_FIXED_MODE 0x00002 +#define ST_Q_FORCE_VAR_MODE 0x00004 +#define ST_Q_SNS_HLP 0x00008 /* must do READ for good MODE SENSE */ +#define ST_Q_IGNORE_LOADS 0x00010 +#define ST_Q_BLKSIZ 0x00020 /* variable-block media_blksiz > 0 */ + +static struct rogues gallery[] = /* ends with an all-null entry */ +{ + {"Such an old device ", "pre-scsi", " unknown model ", "????", + 0, + { + {512, ST_Q_FORCE_FIXED_MODE, 0}, /* minor 0,1,2,3 */ + {512, ST_Q_FORCE_FIXED_MODE, QIC_24}, /* minor 4,5,6,7 */ + {0, ST_Q_FORCE_VAR_MODE, HALFINCH_1600}, /* minor 8,9,10,11 */ + {0, ST_Q_FORCE_VAR_MODE, HALFINCH_6250} /* minor 12,13,14,15 */ + } + }, + {"Tandberg tdc3600", "TANDBERG", " TDC 3600", "????", + ST_Q_NEEDS_PAGE_0, + { + {0, 0, 0}, /* minor 0,1,2,3 */ + {0, ST_Q_FORCE_VAR_MODE, QIC_525}, /* minor 4,5,6,7 */ + {0, 0, QIC_150}, /* minor 8,9,10,11 */ + {0, 0, QIC_120} /* minor 12,13,14,15 */ + } + }, + {"Rev 5 of the Archive 2525", "ARCHIVE ", "VIPER 2525 25462", "-005", + 0, + { + {0, ST_Q_SNS_HLP, 0}, /* minor 0,1,2,3 */ + {0, ST_Q_SNS_HLP, QIC_525}, /* minor 4,5,6,7 */ + {0, 0, QIC_150}, /* minor 8,9,10,11 */ + {0, 0, QIC_120} /* minor 12,13,14,15 */ + } + }, + {"Archive Viper 150", "ARCHIVE ", "VIPER 150", "????", + ST_Q_NEEDS_PAGE_0, + { + {0, 0, 0}, /* minor 0,1,2,3 */ + {0, 0, QIC_150}, /* minor 4,5,6,7 */ + {0, 0, QIC_120}, /* minor 8,9,10,11 */ + {0, 0, QIC_24} /* minor 12,13,14,15 */ + } + }, + {"Wangtek 5525ES", "WANGTEK ", "5525ES SCSI REV7", "????", + 0, + { + {0, 0, 0}, /* minor 0,1,2,3 */ + {0, ST_Q_BLKSIZ, QIC_525}, /* minor 4,5,6,7 */ + {0, 0, QIC_150}, /* minor 8,9,10,11 */ + {0, 0, QIC_120} /* minor 12,13,14,15 */ + } + }, + {"WangDAT model 1300", "WangDAT ", "Model 1300", "????", + 0, + { + {0, 0, 0}, /* minor 0,1,2,3 */ + {512, ST_Q_FORCE_FIXED_MODE, 0x13}, /* minor 4,5,6,7 */ + {1024, ST_Q_FORCE_FIXED_MODE, 0x13}, /* minor 8,9,10,11 */ + {0, ST_Q_FORCE_VAR_MODE, 0x13} /* minor 12,13,14,15 */ + } + }, + {(char *) 0} +}; + +errval st_space __P((u_int32 unit, int32 number, u_int32 what, u_int32 flags)); +errval st_rewind __P((u_int32 unit, boolean immed, u_int32 flags)); +errval st_mode_sense __P((u_int32 unit, u_int32 flags)); +errval st_decide_mode __P((u_int32 unit, boolean first_read)); +errval st_rd_blk_lim __P((u_int32 unit, u_int32 flags)); +errval st_touch_tape __P((u_int32 unit)); +errval st_write_filemarks __P((u_int32 unit, int32 number, u_int32 flags)); +errval st_load __P((u_int32 unit, u_int32 type, u_int32 flags)); +errval st_mode_select __P((u_int32 unit, u_int32 flags)); +void ststrategy(); +void stminphys(); +int32 st_chkeod(); +errval stattach(); +void ststart(); +void st_unmount(); +errval st_mount_tape(); +void st_loadquirks(); +void st_identify_drive(); +errval st_interpret_sense(); + +#define ESUCCESS 0 +#define NOEJECT 0 +#define EJECT 1 + +struct scsi_device st_switch = +{ + st_interpret_sense, /* check errors with us first */ + ststart, /* we have a queue, and this is how we service it */ + NULL, + NULL, /* use the default 'done' routine */ + "st", + 0, + { 0, 0 } +}; + +struct st_data { +/*--------------------present operating parameters, flags etc.----------------*/ + u_int32 flags; /* see below */ + u_int32 blksiz; /* blksiz we are using */ + u_int32 density; /* present density */ + u_int32 quirks; /* quirks for the open mode */ + u_int32 last_dsty; /* last density openned */ +/*--------------------device/scsi parameters----------------------------------*/ + struct scsi_link *sc_link; /* our link to the adpter etc. */ +/*--------------------parameters reported by the device ----------------------*/ + u_int32 blkmin; /* min blk size */ + u_int32 blkmax; /* max blk size */ + struct rogues *rogues; /* if we have a rogue entry */ +/*--------------------parameters reported by the device for this media--------*/ + u_int32 numblks; /* nominal blocks capacity */ + u_int32 media_blksiz; /* 0 if not ST_FIXEDBLOCKS */ + u_int32 media_density; /* this is what it said when asked */ +/*--------------------quirks for the whole drive------------------------------*/ + u_int32 drive_quirks; /* quirks of this drive */ +/*--------------------How we should set up when openning each minor device----*/ + struct modes modes[4]; /* plus more for each mode */ + u_int8 modeflags[4]; /* flags for the modes */ +#define DENSITY_SET_BY_USER 0x01 +#define DENSITY_SET_BY_QUIRK 0x02 +#define BLKSIZE_SET_BY_USER 0x04 +#define BLKSIZE_SET_BY_QUIRK 0x08 +/*--------------------storage for sense data returned by the drive------------*/ + unsigned char sense_data[12]; /* + * additional sense data needed + * for mode sense/select. + */ + struct buf *buf_queue; /* the queue of pending IO operations */ + struct scsi_xfer scsi_xfer; /* scsi xfer struct for this drive */ + u_int32 xfer_block_wait; /* is a process waiting? */ +} *st_data[NST]; + +#define ST_INITIALIZED 0x01 +#define ST_INFO_VALID 0x02 +#define ST_OPEN 0x04 +#define ST_BLOCK_SET 0x08 /* block size, mode set by ioctl */ +#define ST_WRITTEN 0x10 /* data have been written, EOD needed */ +#define ST_FIXEDBLOCKS 0x20 +#define ST_AT_FILEMARK 0x40 +#define ST_EIO_PENDING 0x80 /* we couldn't report it then (had data) */ +#define ST_NEW_MOUNT 0x100 /* still need to decide mode */ +#define ST_READONLY 0x200 /* st_mode_sense says write protected */ +#define ST_FM_WRITTEN 0x400 /* + * EOF file mark written -- used with + * ~ST_WRITTEN to indicate that multiple file + * marks have been written + */ +#define ST_BLANK_READ 0x800 /* BLANK CHECK encountered already */ +#define ST_2FM_AT_EOD 0x1000 /* write 2 file marks at EOD */ +#define ST_MOUNTED 0x2000 /* Device is presently mounted */ + +#define ST_PER_ACTION (ST_AT_FILEMARK | ST_EIO_PENDING | ST_BLANK_READ) +#define ST_PER_MOUNT (ST_INFO_VALID | ST_BLOCK_SET | ST_WRITTEN | \ + ST_FIXEDBLOCKS | ST_READONLY | \ + ST_FM_WRITTEN | ST_2FM_AT_EOD | ST_PER_ACTION) + +static u_int32 next_st_unit = 0; + +/* + * The routine called by the low level scsi routine when it discovers + * A device suitable for this driver + */ + +errval +stattach(sc_link) + struct scsi_link *sc_link; +{ + u_int32 unit; + struct st_data *st; + + SC_DEBUG(sc_link, SDEV_DB2, ("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\n", + (unit + 1), NST); + return 0; + } + if (st_data[unit]) { + printf("st%d: Already has storage!\n", unit); + return 0; + } + sc_link->device = &st_switch; + sc_link->dev_unit = unit; + st = st_data[unit] = malloc(sizeof(struct st_data), M_DEVBUF, M_NOWAIT); + if (!st) { + printf("st%d: malloc failed in st.c\n", unit); + return 0; + } + bzero(st, sizeof(struct st_data)); + + /* + * Store information needed to contact our base driver + */ + st->sc_link = sc_link; + + /* + * Check if the drive is a known criminal and take + * Any steps needed to bring it into line + */ + st_identify_drive(unit); + + /* + * 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: drive offline\n", unit); + } else { + printf("st%d: density code 0x%x, ", unit, st->media_density); + if (!scsi_test_unit_ready(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) { + if (st->media_blksiz) { + printf("%d-byte", st->media_blksiz); + } else { + printf("variable"); + } + printf(" blocks, write-%s\n", + (st->flags & ST_READONLY) ? "protected" : "enabled"); + } else { + printf(" drive empty\n"); + } + } + /* + * Set up the buf queue for this device + */ + st->buf_queue = 0; + st->flags |= ST_INITIALIZED; + return 0; +} + +/* + * Use the inquiry routine in 'scsi_base' to get drive info so we can + * Further tailor our behaviour. + */ +void +st_identify_drive(unit) + u_int32 unit; +{ + struct st_data *st = st_data[unit]; + struct scsi_inquiry_data inqbuf; + struct rogues *finger; + char manu[32]; + char model[32]; + char model2[32]; + char version[32]; + u_int32 model_len; + + /* + * Get the device type information + */ + if (scsi_inquire(st->sc_link, &inqbuf, + SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT) != 0) { + printf("st%d: couldn't get device type, using default\n", unit); + return; + } + if ((inqbuf.version & SID_ANSII) == 0) { + /* + * If not advanced enough, use default values + */ + strncpy(manu, "pre-scsi", 8); + manu[8] = 0; + strncpy(model, " unknown model ", 16); + model[16] = 0; + strncpy(version, "????", 4); + version[4] = 0; + } else { + strncpy(manu, inqbuf.vendor, 8); + manu[8] = 0; + strncpy(model, inqbuf.product, 16); + model[16] = 0; + strncpy(version, inqbuf.revision, 4); + version[4] = 0; + } + + /* + * Load the parameters for this kind of device, so we + * treat it as appropriate for each operating mode. + * Only check the number of characters in the array's + * model entry, not the entire model string returned. + */ + finger = gallery; + while (finger->name) { + model_len = 0; + while (finger->model[model_len] && (model_len < 32)) { + model2[model_len] = model[model_len]; + model_len++; + } + model2[model_len] = 0; + if ((strcmp(manu, finger->manu) == 0) + && (strcmp(model2, finger->model) == 0 || + strcmp("????????????????", finger->model) == 0) + && (strcmp(version, finger->version) == 0 || + strcmp("????", finger->version) == 0)) { + printf("st%d: %s is a known rogue\n", unit, finger->name); + st->rogues = finger; + st->drive_quirks = finger->quirks; + st->quirks = finger->quirks; /*start value */ + st_loadquirks(st); + break; + } else { + finger++; /* go to next suspect */ + } + } +} + +/* + * initialise the subdevices to the default (QUIRK) state. + * this will remove any setting made by the system operator or previous + * operations. + */ +void +st_loadquirks(st) + struct st_data *st; +{ + int i; + struct modes *mode; + struct modes *mode2; + + if (!st->rogues) + return; + mode = st->rogues->modes; + mode2 = st->modes; + for (i = 0; i < 4; i++) { + bzero(mode2, sizeof(struct modes)); + st->modeflags[i] &= ~(BLKSIZE_SET_BY_QUIRK + | DENSITY_SET_BY_QUIRK + | BLKSIZE_SET_BY_USER + | DENSITY_SET_BY_USER); + if (mode->blksiz && ((mode->quirks | st->drive_quirks) + & (ST_Q_FORCE_FIXED_MODE))) { + mode2->blksiz = mode->blksiz; + st->modeflags[i] |= BLKSIZE_SET_BY_QUIRK; + } else { + if ((mode->quirks | st->drive_quirks) + & ST_Q_FORCE_VAR_MODE) { + mode2->blksiz = 0; + st->modeflags[i] |= BLKSIZE_SET_BY_QUIRK; + } + } + if (mode->density) { + mode2->density = mode->density; + st->modeflags[i] |= DENSITY_SET_BY_QUIRK; + } + mode++; + mode2++; + } +} + +/* + * open the device. + */ +errval +stopen(dev, flags) + dev_t dev; + u_int32 flags; +{ + u_int32 unit, mode, dsty; + errval errno = 0; + struct st_data *st; + struct scsi_link *sc_link; + unit = UNIT(dev); + mode = MODE(dev); + dsty = DSTY(dev); + + /* + * Check the unit is legal + */ + if (unit >= NST) { + return (ENXIO); + } + st = st_data[unit]; + /* + * Make sure the device has been initialised + */ + if ((st == NULL) || (!(st->flags & ST_INITIALIZED))) + return (ENXIO); + + sc_link = st->sc_link; + SC_DEBUG(sc_link, SDEV_DB1, ("open: dev=0x%x (unit %d (of %d))\n" + ,dev, unit, NST)); + /* + * Only allow one at a time + */ + if (st->flags & ST_OPEN) { + return (EBUSY); + } + /* + * Throw out a dummy instruction to catch 'Unit attention + * errors (the error handling will invalidate all our + * device info if we get one, but otherwise, ignore it) + */ + scsi_test_unit_ready(sc_link, SCSI_SILENT); + + sc_link->flags |= SDEV_OPEN; /* unit attn are now errors */ + /* + * If the mode is 3 (e.g. minor = 3,7,11,15) + * then the device has been openned to set defaults + * This mode does NOT ALLOW I/O, only ioctls + */ + if (mode == CTLMODE) + return 0; + + /* + * Check that the device is ready to use (media loaded?) + * This time take notice of the return result + */ + if (errno = (scsi_test_unit_ready(sc_link, 0))) { + printf("st%d: not ready\n", unit); + st_unmount(unit, NOEJECT); + return (errno); + } + /* + * if it's a different mode, or if the media has been + * invalidated, unmount the tape from the previous + * session but continue with open processing + */ + if ((st->last_dsty != dsty) + || (!(sc_link->flags & SDEV_MEDIA_LOADED))) { + st_unmount(unit, NOEJECT); + } + /* + * If we are not mounted, then we should start a new + * mount session. + */ + if (!(st->flags & ST_MOUNTED)) { + st_mount_tape(dev, flags); + st->last_dsty = dsty; + } + /* + * Make sure that a tape opened in write-only mode will have + * file marks written on it when closed, even if not written to. + * This is for SUN compatibility + */ + if ((flags & O_ACCMODE) == FWRITE) + st->flags |= ST_WRITTEN; + + SC_DEBUG(sc_link, SDEV_DB2, ("Open complete\n")); + + st->flags |= ST_OPEN; + return (0); +} + +/* + * close the device.. only called if we are the LAST + * occurence of an open device + */ +errval +stclose(dev) + dev_t dev; +{ + unsigned char unit, mode; + struct st_data *st; + struct scsi_link *sc_link; + + unit = UNIT(dev); + mode = MODE(dev); + st = st_data[unit]; + sc_link = st->sc_link; + + SC_DEBUG(sc_link, SDEV_DB1, ("closing\n")); + if ((st->flags & (ST_WRITTEN | ST_FM_WRITTEN)) == ST_WRITTEN) + st_write_filemarks(unit, 1, 0); + switch (mode & 0x3) { + case 0: + case 3: /* for now */ + st_unmount(unit, NOEJECT); + break; + case 1: /*leave mounted unless media seems to have been removed */ + if (!(sc_link->flags & SDEV_MEDIA_LOADED)) { + st_unmount(unit, NOEJECT); + } + break; + case 2: + st_unmount(unit, EJECT); + break; + } + sc_link->flags &= ~SDEV_OPEN; + st->flags &= ~ST_OPEN; + return (0); +} + +/* + * Start a new mount session. + * Copy in all the default parameters from the selected device mode. + * and try guess any that seem to be defaulted. + */ +errval +st_mount_tape(dev, flags) + dev_t dev; + u_int32 flags; +{ + u_int32 unit, mode, dsty; + struct st_data *st; + struct scsi_link *sc_link; + errval errno = 0; + + unit = UNIT(dev); + mode = MODE(dev); + dsty = DSTY(dev); + st = st_data[unit]; + sc_link = st->sc_link; + + if (st->flags & ST_MOUNTED) + return 0; + + SC_DEBUG(sc_link, SDEV_DB1, ("mounting\n ")); + st->flags |= ST_NEW_MOUNT; + st->quirks = st->drive_quirks | st->modes[dsty].quirks; + /* + * If the media is new, then make sure we give it a chance to + * to do a 'load' instruction. ( We assume it is new) + */ + if (errno = st_load(unit, LD_LOAD, 0)) { + return (errno); + } + /* + * Throw another dummy instruction to catch + * 'Unit attention' errors. Some drives appear to give + * these after doing a Load instruction. + * (noteably some DAT drives) + */ + scsi_test_unit_ready(sc_link, SCSI_SILENT); + + /* + * Some devices can't tell you much until they have been + * asked to look at the media. This quirk does this. + */ + if (st->quirks & ST_Q_SNS_HLP) { + if (errno = st_touch_tape(unit)) + return errno; + } + /* + * Load the physical device parameters + * loads: blkmin, blkmax + */ + if (errno = st_rd_blk_lim(unit, 0)) { + return errno; + } + /* + * Load the media dependent parameters + * includes: media_blksiz,media_density,numblks + * As we have a tape in, it should be reflected here. + * If not you may need the "quirk" above. + */ + if (errno = st_mode_sense(unit, 0)) { + return errno; + } + /* + * If we have gained a permanent density from somewhere, + * then use it in preference to the one supplied by + * default by the driver. + */ + if (st->modeflags[dsty] & (DENSITY_SET_BY_QUIRK | DENSITY_SET_BY_USER)) { + st->density = st->modes[dsty].density; + } else { + st->density = st->media_density; + } + /* + * If we have gained a permanent blocksize + * then use it in preference to the one supplied by + * default by the driver. + */ + st->flags &= ~ST_FIXEDBLOCKS; + if (st->modeflags[dsty] & (BLKSIZE_SET_BY_QUIRK | BLKSIZE_SET_BY_USER)) { + st->blksiz = st->modes[dsty].blksiz; + if (st->blksiz) { + st->flags |= ST_FIXEDBLOCKS; + } + } else { + if (errno = st_decide_mode(unit, FALSE)) { + return errno; + } + } + if (errno = st_mode_select(unit, 0)) { + printf("st%d: Cannot set selected mode", unit); + return errno; + } + scsi_prevent(sc_link, PR_PREVENT, 0); /* who cares if it fails? */ + st->flags &= ~ST_NEW_MOUNT; + st->flags |= ST_MOUNTED; + sc_link->flags |= SDEV_MEDIA_LOADED; /* move earlier? */ + + return 0; +} + +/* + * End the present mount session. + * Rewind, and optionally eject the tape. + * Reset various flags to indicate that all new + * operations require another mount operation + */ +void +st_unmount(int unit, boolean eject) +{ + struct st_data *st = st_data[unit]; + struct scsi_link *sc_link = st->sc_link; + int32 nmarks; + + if (!(st->flags & ST_MOUNTED)) + return; + SC_DEBUG(sc_link, SDEV_DB1, ("unmounting\n")); + st_chkeod(unit, FALSE, &nmarks, SCSI_SILENT); + st_rewind(unit, FALSE, SCSI_SILENT); + scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT); + if (eject) { + st_load(unit, LD_UNLOAD, SCSI_SILENT); + } + st->flags &= ~(ST_MOUNTED | ST_NEW_MOUNT); + sc_link->flags &= ~SDEV_MEDIA_LOADED; +} + +/* + * Given all we know about the device, media, mode, 'quirks' and + * initial operation, make a decision as to how we should be set + * to run (regarding blocking and EOD marks) + */ +errval +st_decide_mode(unit, first_read) + u_int32 unit; + boolean first_read; +{ + struct st_data *st = st_data[unit]; +#ifdef SCSIDEBUG + struct scsi_link *sc_link = st->sc_link; +#endif + + SC_DEBUG(sc_link, SDEV_DB2, ("starting block mode decision\n")); + + /* + * If the user hasn't already specified fixed or variable-length + * blocks and the block size (zero if variable-length), we'll + * have to try to figure them out ourselves. + * + * Our first shot at a method is, "The quirks made me do it!" + */ + switch ((int)(st->quirks & (ST_Q_FORCE_FIXED_MODE | ST_Q_FORCE_VAR_MODE))) { + case (ST_Q_FORCE_FIXED_MODE | ST_Q_FORCE_VAR_MODE): + printf("st%d: bad quirks\n", unit); + return (EINVAL); + case ST_Q_FORCE_FIXED_MODE: /*specified fixed, but not what size */ + st->flags |= ST_FIXEDBLOCKS; + if (st->blkmin && (st->blkmin == st->blkmax)) + st->blksiz = st->blkmin; + else if (st->media_blksiz > 0) + st->blksiz = st->media_blksiz; + else + st->blksiz = DEF_FIXED_BSIZE; + SC_DEBUG(sc_link, SDEV_DB3, ("Quirks force fixed mode(%d)\n", + st->blksiz)); + goto done; + case ST_Q_FORCE_VAR_MODE: + st->flags &= ~ST_FIXEDBLOCKS; + st->blksiz = 0; + SC_DEBUG(sc_link, SDEV_DB3, ("Quirks force variable mode\n")); + goto done; + } + /* + * If the drive can only handle fixed-length blocks and only at + * one size, perhaps we should just do that. + */ + if (st->blkmin && (st->blkmin == st->blkmax)) { + st->flags |= ST_FIXEDBLOCKS; + st->blksiz = st->blkmin; + SC_DEBUG(sc_link, SDEV_DB3, + ("blkmin == blkmax of %d\n", st->blkmin)); + goto done; + } + /* + * If the tape density mandates (or even suggests) use of fixed + * or variable-length blocks, comply. + */ + switch ((int)st->density) { + case HALFINCH_800: + case HALFINCH_1600: + case HALFINCH_6250: + case DDS: + st->flags &= ~ST_FIXEDBLOCKS; + st->blksiz = 0; + SC_DEBUG(sc_link, SDEV_DB3, ("density specified variable\n")); + goto done; + case QIC_11: + case QIC_24: + case QIC_120: + case QIC_150: + case QIC_525: + case QIC_1320: + st->flags |= ST_FIXEDBLOCKS; + if (st->media_blksiz > 0) { + st->blksiz = st->media_blksiz; + } else { + st->blksiz = DEF_FIXED_BSIZE; + } + SC_DEBUG(sc_link, SDEV_DB3, ("density specified fixed\n")); + goto done; + } + /* + * If we're about to read the tape, perhaps we should choose + * fixed or variable-length blocks and block size according to + * what the drive found on the tape. + */ + if (first_read + && (!(st->quirks & ST_Q_BLKSIZ) + || (st->media_blksiz == 0) + || (st->media_blksiz == DEF_FIXED_BSIZE) + || (st->media_blksiz == 1024))) { + if (st->media_blksiz == 0) { + st->flags &= ~ST_FIXEDBLOCKS; + } else { + st->flags |= ST_FIXEDBLOCKS; + } + st->blksiz = st->media_blksiz; + SC_DEBUG(sc_link, SDEV_DB3, + ("Used media_blksiz of %d\n", st->media_blksiz)); + goto done; + } + /* + * We're getting no hints from any direction. Choose variable- + * length blocks arbitrarily. + */ + st->flags &= ~ST_FIXEDBLOCKS; + st->blksiz = 0; + SC_DEBUG(sc_link, SDEV_DB3, ("Give up and default to variable mode\n")); +done: + + /* + * Decide whether or not to write two file marks to signify end- + * of-data. Make the decision as a function of density. If + * the decision is not to use a second file mark, the SCSI BLANK + * CHECK condition code will be recognized as end-of-data when + * first read. + * (I think this should be a by-product of fixed/variable..julian) + */ + switch ((int)st->density) { +/* case 8 mm: What is the SCSI density code for 8 mm, anyway? */ + case QIC_11: + case QIC_24: + case QIC_120: + case QIC_150: + case QIC_525: + case QIC_1320: + st->flags &= ~ST_2FM_AT_EOD; + break; + default: + st->flags |= ST_2FM_AT_EOD; + } + return 0; +} + +/* + * 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_link->adapter->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. + */ +void +ststrategy(bp) + struct buf *bp; +{ + struct buf **dp; + unsigned char unit; + u_int32 opri; + struct st_data *st; + + ststrats++; + unit = UNIT((bp->b_dev)); + st = st_data[unit]; + SC_DEBUG(st->sc_link, SDEV_DB1, + (" strategy: %d bytes @ blk%d\n", 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->flags & ST_FIXEDBLOCKS) { + if (bp->b_bcount % st->blksiz) { + printf("st%d: bad request, must be multiple of %d\n", + unit, st->blksiz); + bp->b_error = EIO; + goto bad; + } + } + /* + * as are out-of-range requests on variable drives. + */ + else if (bp->b_bcount < st->blkmin || bp->b_bcount > st->blkmax) { + printf("st%d: bad request, must be between %d and %d\n", + unit, st->blkmin, st->blkmax); + bp->b_error = EIO; + goto bad; + } + stminphys(bp); + opri = splbio(); + + /* + * Use a bounce buffer if necessary + */ +#ifndef NOBOUNCE + if (st->sc_link->flags & SDEV_BOUNCE) + vm_bounce_alloc(bp); +#endif + + /* + * Place it in the queue of activities for this tape + * at the end (a bit silly because we only have on user.. + * (but it could fork() )) + */ + dp = &(st->buf_queue); + while (*dp) { + dp = &((*dp)->b_actf); + } + *dp = 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 + * (All a bit silly if we're only allowing 1 open but..) + */ + 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 dequeues the buf and creates a scsi command to perform the + * transfer required. The transfer request will call scsi_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 + */ +void +ststart(unit) + u_int32 unit; +{ + struct st_data *st = st_data[unit]; + struct scsi_link *sc_link = st->sc_link; + register struct buf *bp = 0; + struct scsi_rw_tape cmd; + u_int32 flags; + + SC_DEBUG(sc_link, SDEV_DB2, ("ststart ")); + /* + * See if there is a buf to do and we are not already + * doing one + */ + while (sc_link->opennings != 0) { + + /* if a special awaits, let it proceed first */ + if (sc_link->flags & SDEV_WAITING) { + sc_link->flags &= ~SDEV_WAITING; + wakeup((caddr_t)sc_link); + return; + } + if ((bp = st->buf_queue) == NULL) { + return; /* no work to bother with */ + } + st->buf_queue = bp->b_actf; + + /* + * if the device has been unmounted byt the user + * then throw away all requests until done + */ + if ((!(st->flags & ST_MOUNTED)) + || (!(sc_link->flags & SDEV_MEDIA_LOADED))) { + /* make sure that one implies the other.. */ + sc_link->flags &= ~SDEV_MEDIA_LOADED; + goto badnews; + } + /* + * only FIXEDBLOCK devices have pending operations + */ + if (st->flags & ST_FIXEDBLOCKS) { + /* + * If we are at a filemark but have not reported it yet + * then we should report it now + */ + if (st->flags & ST_AT_FILEMARK) { + if ((bp->b_flags & B_READ) == B_WRITE) { + /* + * Handling of ST_AT_FILEMARK in + * st_space will fill in the right file + * mark count. + * Back up over filemark + */ + if (st_space(unit, 0, SP_FILEMARKS, 0) != + ESUCCESS) + goto badnews; + } else { + bp->b_resid = bp->b_bcount; + bp->b_error = 0; + bp->b_flags &= ~B_ERROR; + st->flags &= ~ST_AT_FILEMARK; + biodone(bp); + continue; /* seek more work */ + } + } + /* + * If we are at EIO (e.g. EOM) but have not reported it + * yet then we should report it now + */ + if (st->flags & ST_EIO_PENDING) { + bp->b_resid = bp->b_bcount; + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + st->flags &= ~ST_EIO_PENDING; + biodone(bp); + continue; /* seek more work */ + } + } + /* + * Fill out the scsi command + */ + bzero(&cmd, sizeof(cmd)); + if ((bp->b_flags & B_READ) == B_WRITE) { + cmd.op_code = WRITE_COMMAND_TAPE; + st->flags &= ~ST_FM_WRITTEN; + st->flags |= ST_WRITTEN; + flags = SCSI_DATA_OUT; + } else { + cmd.op_code = READ_COMMAND_TAPE; + flags = SCSI_DATA_IN; + } + /* + * Handle "fixed-block-mode" tape drives by using the + * block count instead of the length. + */ + if (st->flags & ST_FIXEDBLOCKS) { + cmd.byte2 |= SRWT_FIXED; + lto3b(bp->b_bcount / st->blksiz, cmd.len); + } else { + lto3b(bp->b_bcount, cmd.len); + } + /* + * go ask the adapter to do all this for us + */ + if (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &cmd, + sizeof(cmd), + (u_char *) bp->b_un.b_addr, + bp->b_bcount, + 0, /* can't retry a read on a tape really */ + 100000, + bp, + flags | SCSI_NOSLEEP) == SUCCESSFULLY_QUEUED) { + stqueues++; + } else { +badnews: + printf("st%d: oops not queued\n", unit); + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + biodone(bp); + } + } /* go back and see if we can cram more work in.. */ +} + +/* + * Perform special action on behalf of the user; + * knows about the internals of this device + */ +errval +stioctl(dev, cmd, arg, flag) + dev_t dev; + int cmd; + caddr_t arg; + int flag; +{ + errval errcode = 0; + unsigned char unit; + u_int32 number, flags, dsty; + struct st_data *st; + u_int32 hold_blksiz; + u_int32 hold_density; + int32 nmarks; + struct mtop *mt = (struct mtop *) arg; + + /* + * Find the device that the user is talking about + */ + flags = 0; /* give error messages, act on errors etc. */ + unit = UNIT(dev); + dsty = DSTY(dev); + st = st_data[unit]; + hold_blksiz = st->blksiz; + hold_density = st->density; + + switch (cmd) { + + case MTIOCGET: + { + struct mtget *g = (struct mtget *) arg; + + SC_DEBUG(st->sc_link, SDEV_DB1, ("[ioctl: get status]\n")); + bzero(g, sizeof(struct mtget)); + g->mt_type = 0x7; /* Ultrix compat *//*? */ + g->mt_density = st->density; + g->mt_blksiz = st->blksiz; + g->mt_density0 = st->modes[0].density; + g->mt_density1 = st->modes[1].density; + g->mt_density2 = st->modes[2].density; + g->mt_density3 = st->modes[3].density; + g->mt_blksiz0 = st->modes[0].blksiz; + g->mt_blksiz1 = st->modes[1].blksiz; + g->mt_blksiz2 = st->modes[2].blksiz; + g->mt_blksiz3 = st->modes[3].blksiz; + break; + } + case MTIOCTOP: + { + + SC_DEBUG(st->sc_link, SDEV_DB1, ("[ioctl: op=0x%x count=0x%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 */ + errcode = st_write_filemarks(unit, number, flags); + break; + case MTBSF: /* backward space file */ + number = -number; + case MTFSF: /* forward space file */ + errcode = st_chkeod(unit, FALSE, &nmarks, flags); + if (errcode == ESUCCESS) + errcode = st_space(unit, number - nmarks, + SP_FILEMARKS, flags); + break; + case MTBSR: /* backward space record */ + number = -number; + case MTFSR: /* forward space record */ + errcode = st_chkeod(unit, TRUE, &nmarks, flags); + if (errcode == ESUCCESS) + errcode = st_space(unit, number, SP_BLKS, flags); + break; + case MTREW: /* rewind */ + errcode = st_rewind(unit, FALSE, flags); + break; + case MTOFFL: /* rewind and put the drive offline */ + st_unmount(unit, EJECT); + break; + case MTNOP: /* no operation, sets status only */ + case MTCACHE: /* enable controller cache */ + case MTNOCACHE: /* disable controller cache */ + break; + case MTSETBSIZ: /* Set block size for device */ +#ifdef NOTYET + if (!(st->flags & ST_NEW_MOUNT)) { + uprintf("re-mount tape before changing blocksize"); + errcode = EINVAL; + break; + } +#endif + if (number == 0) { + st->flags &= ~ST_FIXEDBLOCKS; + } else { + if ((st->blkmin || st->blkmax) /* they exist */ + &&((number < st->blkmin + || number > st->blkmax))) { + errcode = EINVAL; + break; + } + st->flags |= ST_FIXEDBLOCKS; + } + st->blksiz = number; + st->flags |= ST_BLOCK_SET; /*XXX */ + goto try_new_value; + + case MTSETDNSTY: /* Set density for device and mode */ + if (number > SCSI_2_MAX_DENSITY_CODE) { + errcode = EINVAL; + } else { + st->density = number; + } + goto try_new_value; + + default: + errcode = EINVAL; + } + break; + } + case MTIOCIEOT: + case MTIOCEEOT: + break; + default: + if(MODE(dev) == CTLMODE) + errcode = scsi_do_ioctl(st->sc_link,cmd,arg,flag); + else + errcode = ENOTTY; + break; + } + return errcode; +/*-----------------------------*/ +try_new_value: + /* + * Check that the mode being asked for is aggreeable to the + * drive. If not, put it back the way it was. + */ + if (errcode = st_mode_select(unit, 0)) { /* put it back as it was */ + printf("st%d: Cannot set selected mode", unit); + st->density = hold_density; + st->blksiz = hold_blksiz; + if (st->blksiz) { + st->flags |= ST_FIXEDBLOCKS; + } else { + st->flags &= ~ST_FIXEDBLOCKS; + } + return (errcode); + } + /* + * As the drive liked it, if we are setting a new default, + * set it into the structures as such. + * + * The means for deciding this are not finalised yet + */ + if (MODE(dev) == 0x03) { + /* special mode */ + /* XXX */ + switch ((short) (mt->mt_op)) { + case MTSETBSIZ: + st->modes[dsty].blksiz = st->blksiz; + st->modeflags[dsty] |= BLKSIZE_SET_BY_USER; + break; + case MTSETDNSTY: + st->modes[dsty].density = st->density; + st->modeflags[dsty] |= DENSITY_SET_BY_USER; + break; + } + } + return 0; +} + +/* + * Do a synchronous read. + */ +errval +st_read(unit, buf, size, flags) + u_int32 unit, size, flags; + char *buf; +{ + struct scsi_rw_tape scsi_cmd; + struct st_data *st = st_data[unit]; + + /* + * If it's a null transfer, return immediatly + */ + if (size == 0) { + return (ESUCCESS); + } + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = READ_COMMAND_TAPE; + if (st->flags & ST_FIXEDBLOCKS) { + scsi_cmd.byte2 |= SRWT_FIXED; + lto3b(size / (st->blksiz ? st->blksiz : DEF_FIXED_BSIZE), + scsi_cmd.len); + } else { + lto3b(size, scsi_cmd.len); + } + return (scsi_scsi_cmd(st->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) buf, + size, + 0, /* not on io commands */ + 100000, + NULL, + flags | SCSI_DATA_IN)); +} +#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. + */ +errval +st_rd_blk_lim(unit, flags) + u_int32 unit, flags; +{ + struct scsi_blk_limits scsi_cmd; + struct scsi_blk_limits_data scsi_blkl; + struct st_data *st = st_data[unit]; + errval errno; + struct scsi_link *sc_link = st->sc_link; + + /* + * First check if we have it all loaded + */ + if ((sc_link->flags & SDEV_MEDIA_LOADED)) + return 0; + + /* + * 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 (errno = scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) & scsi_blkl, + sizeof(scsi_blkl), + ST_RETRIES, + 5000, + NULL, + flags | SCSI_DATA_IN)) { + return errno; + } + st->blkmin = b2tol(scsi_blkl.min_length); + st->blkmax = _3btol(&scsi_blkl.max_length_2); + + SC_DEBUG(sc_link, SDEV_DB3, + ("(%d <= blksiz <= %d)\n", st->blkmin, st->blkmax)); + return 0; +} + +/* + * Get the scsi driver to send a full inquiry to the + * device and use the results to fill out the global + * parameter structure. + * + * called from: + * attach + * open + * ioctl (to reset original blksize) + */ +errval +st_mode_sense(unit, flags) + u_int32 unit, flags; +{ + u_int32 scsi_sense_len; + errval errno; + char *scsi_sense_ptr; + struct scsi_mode_sense scsi_cmd; + struct scsi_sense { + struct scsi_mode_header header; + struct blk_desc blk_desc; + } scsi_sense; + + struct scsi_sense_page_0 { + struct scsi_mode_header header; + struct blk_desc blk_desc; + unsigned char sense_data[PAGE_0_SENSE_DATA_SIZE]; + /* Tandberg tape drives returns page 00 + * with the sense data, whether or not + * you want it( ie the don't like you + * saying you want anything less!!!!! + * They also expect page 00 + * back when you issue a mode select + */ + } scsi_sense_page_0; + struct st_data *st = st_data[unit]; + struct scsi_link *sc_link = st->sc_link; + + /* + * Define what sort of structure we're working with + */ + if (st->quirks & ST_Q_NEEDS_PAGE_0) { + scsi_sense_len = sizeof(scsi_sense_page_0); + scsi_sense_ptr = (char *) &scsi_sense_page_0; + } else { + scsi_sense_len = sizeof(scsi_sense); + scsi_sense_ptr = (char *) &scsi_sense; + } + /* + * Set up a mode sense + */ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = MODE_SENSE; + scsi_cmd.length = scsi_sense_len; + + /* + * do the command, but we don't need the results + * just print them for our interest's sake, if asked, + * or if we need it as a template for the mode select + * store it away. + */ + if (errno = scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) scsi_sense_ptr, + scsi_sense_len, + ST_RETRIES, + 5000, + NULL, + flags | SCSI_DATA_IN)) { + return errno; + } + st->numblks = _3btol(((struct scsi_sense *)scsi_sense_ptr)->blk_desc.nblocks); + st->media_blksiz = _3btol(((struct scsi_sense *)scsi_sense_ptr)->blk_desc.blklen); + st->media_density = ((struct scsi_sense *) scsi_sense_ptr)->blk_desc.density; + if (((struct scsi_sense *) scsi_sense_ptr)->header.dev_spec & + SMH_DSP_WRITE_PROT) { + st->flags |= ST_READONLY; + } + SC_DEBUG(sc_link, SDEV_DB3, + ("density code 0x%x, %d-byte blocks, write-%s, ", + st->media_density, st->media_blksiz, + st->flags & ST_READONLY ? "protected" : "enabled")); + SC_DEBUG(sc_link, SDEV_DB3, + ("%sbuffered\n", + ((struct scsi_sense *) scsi_sense_ptr)->header.dev_spec + & SMH_DSP_BUFF_MODE ? "" : "un")); + if (st->quirks & ST_Q_NEEDS_PAGE_0) { + bcopy(((struct scsi_sense_page_0 *) scsi_sense_ptr)->sense_data, + st->sense_data, + sizeof(((struct scsi_sense_page_0 *) scsi_sense_ptr)->sense_data)); + } + sc_link->flags |= SDEV_MEDIA_LOADED; + return 0; +} + +/* + * Send a filled out parameter structure to the drive to + * set it into the desire modes etc. + */ +errval +st_mode_select(unit, flags) + u_int32 unit, flags; +{ + u_int32 dat_len; + char *dat_ptr; + struct scsi_mode_select scsi_cmd; + struct dat { + struct scsi_mode_header header; + struct blk_desc blk_desc; + } dat; + struct dat_page_0 { + struct scsi_mode_header header; + struct blk_desc blk_desc; + unsigned char sense_data[PAGE_0_SENSE_DATA_SIZE]; + } dat_page_0; + struct st_data *st = st_data[unit]; + + /* + * Define what sort of structure we're working with + */ + if (st->quirks & ST_Q_NEEDS_PAGE_0) { + dat_len = sizeof(dat_page_0); + dat_ptr = (char *) &dat_page_0; + } else { + dat_len = sizeof(dat); + dat_ptr = (char *) &dat; + } + /* + * Set up for a mode select + */ + bzero(dat_ptr, dat_len); + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = MODE_SELECT; + scsi_cmd.length = dat_len; + ((struct dat *) dat_ptr)->header.blk_desc_len = sizeof(struct blk_desc); + ((struct dat *) dat_ptr)->header.dev_spec |= SMH_DSP_BUFF_MODE_ON; + ((struct dat *) dat_ptr)->blk_desc.density = st->density; + if (st->flags & ST_FIXEDBLOCKS) { + lto3b(st->blksiz, ((struct dat *) dat_ptr)->blk_desc.blklen); + } + if (st->quirks & ST_Q_NEEDS_PAGE_0) { + bcopy(st->sense_data, ((struct dat_page_0 *) dat_ptr)->sense_data, + sizeof(((struct dat_page_0 *) dat_ptr)->sense_data)); + /* the Tandberg tapes need the block size to */ + /* be set on each mode sense/select. */ + } + /* + * do the command + */ + return (scsi_scsi_cmd(st->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) dat_ptr, + dat_len, + ST_RETRIES, + 5000, + NULL, + flags | SCSI_DATA_OUT)); +} + +/* + * skip N blocks/filemarks/seq filemarks/eom + */ +errval +st_space(unit, number, what, flags) + u_int32 unit, what, flags; + int32 number; +{ + errval error; + struct scsi_space scsi_cmd; + struct st_data *st = st_data[unit]; + + switch ((int)what) { + case SP_BLKS: + if (st->flags & ST_PER_ACTION) { + if (number > 0) { + st->flags &= ~ST_PER_ACTION; + return (EIO); + } else if (number < 0) { + if (st->flags & ST_AT_FILEMARK) { + /* + * Handling of ST_AT_FILEMARK + * in st_space will fill in the + * right file mark count. + */ + error = st_space(unit, 0, SP_FILEMARKS, + flags); + if (error) + return (error); + } + if (st->flags & ST_BLANK_READ) { + st->flags &= ~ST_BLANK_READ; + return (EIO); + } + st->flags &= ~ST_EIO_PENDING; + } + } + break; + case SP_FILEMARKS: + if (st->flags & ST_EIO_PENDING) { + if (number > 0) { /* pretend we just discover the error */ + st->flags &= ~ST_EIO_PENDING; + return (EIO); + } else if (number < 0) { /* back away from the error */ + st->flags &= ~ST_EIO_PENDING; + } + } + if (st->flags & ST_AT_FILEMARK) { + st->flags &= ~ST_AT_FILEMARK; + number--; + } + if ((st->flags & ST_BLANK_READ) && (number < 0)) { /* back away from unwritten tape */ + st->flags &= ~ST_BLANK_READ; + number++; /* dubious */ + } + } + if (number == 0) { + return (ESUCCESS); + } + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = SPACE; + scsi_cmd.byte2 = what & SS_CODE; + lto3b(number, scsi_cmd.number); + return (scsi_scsi_cmd(st->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 0, /* no retries please , just fail */ + 600000, /* 10 mins enough? */ + NULL, + flags)); +} + +/* + * write N filemarks + */ +errval +st_write_filemarks(unit, number, flags) + u_int32 unit, flags; + int32 number; +{ + struct scsi_write_filemarks scsi_cmd; + struct st_data *st = st_data[unit]; + + /* + * It's hard to write a negative number of file marks. + * Don't try. + */ + if (number < 0) { + return EINVAL; + } + switch ((int)number) { + case 0: /* really a command to sync the drive's buffers */ + break; + case 1: + if (st->flags & ST_FM_WRITTEN) { /* already have one down */ + st->flags &= ~ST_WRITTEN; + } else { + st->flags |= ST_FM_WRITTEN; + } + st->flags &= ~ST_PER_ACTION; + break; + default: + st->flags &= ~(ST_PER_ACTION | ST_WRITTEN); + } + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = WRITE_FILEMARKS; + lto3b(number, scsi_cmd.number); + return scsi_scsi_cmd(st->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 0, /* no retries, just fail */ + 100000, /* 10 secs.. (may need to repos head ) */ + NULL, + flags); +} + +/* + * Make sure the right number of file marks is on tape if the + * tape has been written. If the position argument is true, + * leave the tape positioned where it was originally. + * + * nmarks returns the number of marks to skip (or, if position + * true, which were skipped) to get back original position. + */ +int32 +st_chkeod(unit, position, nmarks, flags) + u_int32 unit; + boolean position; + int32 *nmarks; + u_int32 flags; +{ + errval error; + struct st_data *st = st_data[unit]; + + switch ((int)(st->flags & (ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD))) { + default: + *nmarks = 0; + return (ESUCCESS); + case ST_WRITTEN: + case ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD: + *nmarks = 1; + break; + case ST_WRITTEN | ST_2FM_AT_EOD: + *nmarks = 2; + } + error = st_write_filemarks(unit, *nmarks, flags); + if (position && (error == ESUCCESS)) + error = st_space(unit, -*nmarks, SP_FILEMARKS, flags); + return (error); +} + +/* + * load/unload (with retension if true) + */ +errval +st_load(unit, type, flags) + u_int32 unit, type, flags; +{ + struct scsi_load scsi_cmd; + struct st_data *st = st_data[unit]; + struct scsi_link *sc_link = st->sc_link; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + if (type != LD_LOAD) { + errval error; + int32 nmarks; + + error = st_chkeod(unit, FALSE, &nmarks, flags); + if (error != ESUCCESS) + return (error); + sc_link->flags &= ~SDEV_MEDIA_LOADED; + } + if (st->quirks & ST_Q_IGNORE_LOADS) + return (0); + scsi_cmd.op_code = LOAD_UNLOAD; + scsi_cmd.how |= type; + return (scsi_scsi_cmd(st->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + ST_RETRIES, + 300000, /* 5 min */ + NULL, + flags)); +} + +/* + * Rewind the device + */ +errval +st_rewind(unit, immed, flags) + u_int32 unit, flags; + boolean immed; +{ + struct scsi_rewind scsi_cmd; + struct st_data *st = st_data[unit]; + errval error; + int32 nmarks; + + error = st_chkeod(unit, FALSE, &nmarks, flags); + if (error != ESUCCESS) + return (error); + st->flags &= ~ST_PER_ACTION; + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = REWIND; + scsi_cmd.byte2 = immed ? SR_IMMED : 0; + return (scsi_scsi_cmd(st->sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + ST_RETRIES, + immed ? 5000 : 300000, /* 5 sec or 5 min */ + NULL, + flags)); +} + +#ifdef NETBSD +#define SIGNAL_SHORT_READ +#else +#define SIGNAL_SHORT_READ bp->b_flags |= B_ERROR; +#endif + +/* + * Look at the returned sense and act on the error and detirmine + * The unix error number to pass back... (0 = report no error) + * (-1 = continue processing) + */ +errval +st_interpret_sense(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *sc_link = xs->sc_link; + struct scsi_sense_data *sense = &(xs->sense); + boolean silent = xs->flags & SCSI_SILENT; + struct buf *bp = xs->bp; + u_int32 unit = sc_link->dev_unit; + struct st_data *st = st_data[unit]; + u_int32 key; + int32 info; + + /* + * Get the sense fields and work out what code + */ + if (sense->error_code & SSD_ERRCODE_VALID) { + info = ntohl(*((int32 *) sense->ext.extended.info)); + } else { + info = xs->datalen; /* bad choice if fixed blocks */ + } + if ((sense->error_code & SSD_ERRCODE) != 0x70) { + return (-1); /* let the generic code handle it */ + } + if (st->flags & ST_FIXEDBLOCKS) { + xs->resid = info * st->blksiz; + if (sense->ext.extended.flags & SSD_EOM) { + st->flags |= ST_EIO_PENDING; + if (bp) { + bp->b_resid = xs->resid; + SIGNAL_SHORT_READ + } + } + if (sense->ext.extended.flags & SSD_FILEMARK) { + st->flags |= ST_AT_FILEMARK; + if (bp) { + bp->b_resid = xs->resid; + SIGNAL_SHORT_READ + } + } + if (sense->ext.extended.flags & SSD_ILI) { + st->flags |= ST_EIO_PENDING; + if (bp) { + bp->b_resid = xs->resid; + SIGNAL_SHORT_READ + } + if (sense->error_code & SSD_ERRCODE_VALID && + !silent) + printf("st%d: block wrong size" + ", %d blocks residual\n", unit + ,info); + + /* + * This quirk code helps the drive read + * the first tape block, regardless of + * format. That is required for these + * drives to return proper MODE SENSE + * information. + */ + if ((st->quirks & ST_Q_SNS_HLP) && + !(sc_link->flags & SDEV_MEDIA_LOADED)) { + st->blksiz -= 512; + } + } + /* + * If no data was tranfered, do it immediatly + */ + if (xs->resid >= xs->datalen) { + if (st->flags & ST_EIO_PENDING) { + return EIO; + } + if (st->flags & ST_AT_FILEMARK) { + if (bp) { + bp->b_resid = xs->resid; + SIGNAL_SHORT_READ + } + return 0; + } + } + } else { /* must be variable mode */ + xs->resid = xs->datalen; /* to be sure */ + if (sense->ext.extended.flags & SSD_EOM) { + return (EIO); + } + if (sense->ext.extended.flags & SSD_FILEMARK) { + if (bp) + bp->b_resid = bp->b_bcount; + return 0; + } + if (sense->ext.extended.flags & SSD_ILI) { + if (info < 0) { + /* + * the record was bigger than the read + */ + if (!silent) + printf("st%d: %d-byte record " + "too big\n", unit, + xs->datalen - info); + return (EIO); + } + xs->resid = info; + if (bp) { + bp->b_resid = info; + SIGNAL_SHORT_READ + } + } + } + key = sense->ext.extended.flags & SSD_KEY; + + if (key == 0x8) { + /* + * This quirk code helps the drive read the + * first tape block, regardless of format. That + * is required for these drives to return proper + * MODE SENSE information. + */ + if ((st->quirks & ST_Q_SNS_HLP) && + !(sc_link->flags & SDEV_MEDIA_LOADED)) { /* still starting */ + st->blksiz -= 512; + } else if (!(st->flags & (ST_2FM_AT_EOD | ST_BLANK_READ))) { + st->flags |= ST_BLANK_READ; + xs->resid = xs->datalen; + if (bp) { + bp->b_resid = xs->resid; + /*return an EOF */ + } + return (ESUCCESS); + } + } + return (-1); /* let the default/generic handler handle it */ +} + +/* + * The quirk here is that the drive returns some value to st_mode_sense + * incorrectly until the tape has actually passed by the head. + * + * The method is to set the drive to large fixed-block state (user-specified + * density and 1024-byte blocks), then read and rewind to get it to sense the + * tape. If that doesn't work, try 512-byte fixed blocks. If that doesn't + * work, as a last resort, try variable- length blocks. The result will be + * the ability to do an accurate st_mode_sense. + * + * We know we can do a rewind because we just did a load, which implies rewind. + * Rewind seems preferable to space backward if we have a virgin tape. + * + * The rest of the code for this quirk is in ILI processing and BLANK CHECK + * error processing, both part of st_interpret_sense. + */ +errval +st_touch_tape(unit) + u_int32 unit; +{ + struct st_data *st = st_data[unit]; + char *buf; + u_int32 readsiz; + errval errno; + + buf = malloc(1024, M_TEMP, M_NOWAIT); + if (!buf) + return (ENOMEM); + + if (errno = st_mode_sense(unit, 0)) { + goto bad; + } + st->blksiz = 1024; + do { + switch ((int)st->blksiz) { + case 512: + case 1024: + readsiz = st->blksiz; + st->flags |= ST_FIXEDBLOCKS; + break; + default: + readsiz = 1; + st->flags &= ~ST_FIXEDBLOCKS; + } if (errno = st_mode_select(unit, 0)) { + goto bad; + } + st_read(unit, buf, readsiz, SCSI_SILENT); + if (errno = st_rewind(unit, FALSE, 0)) { +bad: free(buf, M_TEMP); + return (errno); + } + } while (readsiz != 1 && readsiz > st->blksiz); + free(buf, M_TEMP); + return 0; +} diff --git a/sys/scsi/su.c b/sys/scsi/su.c new file mode 100644 index 0000000..de4f017 --- /dev/null +++ b/sys/scsi/su.c @@ -0,0 +1,4 @@ + +/* this will be a special user scsi device */ +/* not written yet */ + diff --git a/sys/scsi/uk.c b/sys/scsi/uk.c new file mode 100644 index 0000000..149e2df --- /dev/null +++ b/sys/scsi/uk.c @@ -0,0 +1,158 @@ +/* + * Dummy driver for a device we can't identify. + * by Julian Elischer (julian@tfs.com) + * + * $Id: uk.c,v 1.2 1993/11/25 01:37:35 wollman Exp $ + */ + +#include <sys/param.h> +#include "systm.h" +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#define NUK 16 + +/* + * This driver is so simple it uses all the default services + */ +struct scsi_device uk_switch = +{ + NULL, + NULL, + NULL, + NULL, + "uk", + 0, + 0, 0 +}; + +struct uk_data { + u_int32 flags; + struct scsi_link *sc_link; /* all the inter level info */ +} uk_data[NUK]; + +#define UK_KNOWN 0x02 + +static u_int32 next_uk_unit = 0; + +/* + * The routine called by the low level scsi routine when it discovers + * a device suitable for this driver. + */ +errval +ukattach(sc_link) + struct scsi_link *sc_link; +{ + u_int32 unit, i, stat; + unsigned char *tbl; + + SC_DEBUG(sc_link, SDEV_DB2, ("ukattach: ")); + /* + * Check we have the resources for another drive + */ + unit = next_uk_unit++; + if (unit >= NUK) { + printf("Too many unknown devices..(%d > %d) reconfigure kernel\n", + (unit + 1), NUK); + return (0); + } + /* + * Store information needed to contact our base driver + */ + uk_data[unit].sc_link = sc_link; + sc_link->device = &uk_switch; + sc_link->dev_unit = unit; + + printf("uk%d: unknown device\n", unit); + uk_data[unit].flags = UK_KNOWN; + + return 1; /* XXX ??? */ + +} + +/* + * open the device. + */ +errval +ukopen(dev) + dev_t dev; +{ + errval errcode = 0; + u_int32 unit, mode; + struct scsi_link *sc_link; + unit = minor(dev); + + /* + * Check the unit is legal + */ + if (unit >= NUK) { + printf("uk%d: uk %d > %d\n", unit, unit, NUK); + return ENXIO; + } + + /* + * Make sure the device has been initialised + */ + if((uk_data[unit].flags & UK_KNOWN) == 0) { + printf("uk%d: not set up\n", unit); + return ENXIO; + } + + /* + * Only allow one at a time + */ + sc_link = uk_data[unit].sc_link; + if (sc_link->flags & SDEV_OPEN) { + printf("uk%d: already open\n", unit); + return ENXIO; + } + sc_link->flags |= SDEV_OPEN; + SC_DEBUG(sc_link, SDEV_DB1, ("ukopen: dev=0x%x (unit %d (of %d))\n" + ,dev, unit, NUK)); + /* + * Catch any unit attention errors. + */ + return 0; +} + +/* + * close the device.. only called if we are the LAST + * occurence of an open device + */ +errval +ukclose(dev) + dev_t dev; +{ + unsigned char unit = 0, mode; /* XXX !!! XXX FIXME!!! 0??? */ + struct scsi_link *sc_link; + + sc_link = uk_data[unit].sc_link; + + SC_DEBUG(sc_link, SDEV_DB1, ("Closing device")); + sc_link->flags &= ~SDEV_OPEN; + return (0); +} + +/* + * Perform special action on behalf of the user + * Only does generic scsi ioctls. + */ +errval +ukioctl(dev, cmd, arg, mode) + dev_t dev; + u_int32 cmd; + caddr_t arg; + int mode; +{ + unsigned char unit; + struct scsi_link *sc_link; + + /* + * Find the device that the user is talking about + */ + unit = minor(dev); + sc_link = uk_data[unit].sc_link; + return(scsi_do_ioctl(sc_link,cmd,arg,mode)); +} + |