diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /sys/scsi |
initial import of NetBSD tree
Diffstat (limited to 'sys/scsi')
-rw-r--r-- | sys/scsi/README | 43 | ||||
-rw-r--r-- | sys/scsi/cd.c | 1169 | ||||
-rw-r--r-- | sys/scsi/ch.c | 462 | ||||
-rw-r--r-- | sys/scsi/files.scsi | 26 | ||||
-rw-r--r-- | sys/scsi/scsi_all.h | 316 | ||||
-rw-r--r-- | sys/scsi/scsi_base.c | 848 | ||||
-rw-r--r-- | sys/scsi/scsi_cd.h | 212 | ||||
-rw-r--r-- | sys/scsi/scsi_changer.h | 94 | ||||
-rw-r--r-- | sys/scsi/scsi_debug.h | 42 | ||||
-rw-r--r-- | sys/scsi/scsi_disk.h | 208 | ||||
-rw-r--r-- | sys/scsi/scsi_ioctl.c | 361 | ||||
-rw-r--r-- | sys/scsi/scsi_message.h | 33 | ||||
-rw-r--r-- | sys/scsi/scsi_tape.h | 209 | ||||
-rw-r--r-- | sys/scsi/scsiconf.c | 576 | ||||
-rw-r--r-- | sys/scsi/scsiconf.h | 281 | ||||
-rw-r--r-- | sys/scsi/sd.c | 996 | ||||
-rw-r--r-- | sys/scsi/st.c | 1786 | ||||
-rw-r--r-- | sys/scsi/su.c | 5 | ||||
-rw-r--r-- | sys/scsi/uk.c | 174 |
19 files changed, 7841 insertions, 0 deletions
diff --git a/sys/scsi/README b/sys/scsi/README new file mode 100644 index 00000000000..b0c51b69992 --- /dev/null +++ b/sys/scsi/README @@ -0,0 +1,43 @@ +/* $NetBSD: README,v 1.6 1995/06/02 14:04:36 mycroft Exp $ */ + +This release consists of the following files + +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 + + +################## Using the scsi system ################## +--------------making devices------------ +see st(1) and st(4) for info on tape devices. + +---------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----------------- +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! + diff --git a/sys/scsi/cd.c b/sys/scsi/cd.c new file mode 100644 index 00000000000..3dbe5c741f7 --- /dev/null +++ b/sys/scsi/cd.c @@ -0,0 +1,1169 @@ +/* $NetBSD: cd.c,v 1.76 1995/10/10 02:52:56 mycroft Exp $ */ + +/* + * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved. + * + * 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 Charles M. Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Originally 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 + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.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/device.h> +#include <sys/disklabel.h> +#include <sys/disk.h> +#include <sys/cdio.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> + +#define CDOUTSTANDING 2 +#define CDRETRIES 1 + +#define CDUNIT(z) DISKUNIT(z) +#define CDPART(z) DISKPART(z) +#define MAKECDDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part) + +struct cd_softc { + struct device sc_dev; + struct dkdevice sc_dk; + + int flags; +#define CDF_LOCKED 0x01 +#define CDF_WANTED 0x02 +#define CDF_WLABEL 0x04 /* label is writable */ +#define CDF_LABELLING 0x08 /* writing label */ + struct scsi_link *sc_link; /* contains our targ, lun, etc. */ + struct cd_parms { + int blksize; + u_long disksize; /* total number sectors */ + } params; + struct buf buf_queue; +}; + +int cdmatch __P((struct device *, void *, void *)); +void cdattach __P((struct device *, struct device *, void *)); + +struct cfdriver cdcd = { + NULL, "cd", cdmatch, cdattach, DV_DISK, sizeof(struct cd_softc) +}; + +void cdgetdisklabel __P((struct cd_softc *)); +int cd_get_parms __P((struct cd_softc *, int)); +void cdstrategy __P((struct buf *)); +void cdstart __P((struct cd_softc *)); + +struct dkdriver cddkdriver = { cdstrategy }; + +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 */ +}; + +struct scsi_inquiry_pattern cd_patterns[] = { + {T_CDROM, T_REMOV, + "", "", ""}, +#if 0 + {T_CDROM, T_REMOV, /* more luns */ + "PIONEER ", "CD-ROM DRM-600 ", ""}, +#endif +}; + +int +cdmatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct cfdata *cf = match; + struct scsibus_attach_args *sa = aux; + int priority; + + (void)scsi_inqmatch(sa->sa_inqbuf, + (caddr_t)cd_patterns, sizeof(cd_patterns)/sizeof(cd_patterns[0]), + sizeof(cd_patterns[0]), &priority); + return (priority); +} + +/* + * The routine called by the low level scsi routine when it discovers + * A device suitable for this driver + */ +void +cdattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct cd_softc *cd = (void *)self; + struct cd_parms *dp = &cd->params; + struct scsibus_attach_args *sa = aux; + struct scsi_link *sc_link = sa->sa_sc_link; + + SC_DEBUG(sc_link, SDEV_DB2, ("cdattach: ")); + + /* + * Store information needed to contact our base driver + */ + cd->sc_link = sc_link; + sc_link->device = &cd_switch; + sc_link->device_softc = cd; + if (sc_link->openings > CDOUTSTANDING) + sc_link->openings = CDOUTSTANDING; + + cd->sc_dk.dk_driver = &cddkdriver; +#if !defined(i386) || defined(NEWCONFIG) + dk_establish(&cd->sc_dk, &cd->sc_dev); +#endif + + /* + * Use the subdriver to request information regarding + * the drive. We cannot use interrupts yet, so the + * request must specify this. + */ + if (scsi_start(cd->sc_link, SSS_START, + SCSI_AUTOCONF | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT) || + cd_get_parms(cd, SCSI_AUTOCONF) != 0) + printf(": drive empty\n"); + else + printf(": cd present, %d x %d byte records\n", + cd->params.disksize, cd->params.blksize); +} + +/* + * Wait interruptibly for an exclusive lock. + * + * XXX + * Several drivers do this; it should be abstracted and made MP-safe. + */ +int +cdlock(cd) + struct cd_softc *cd; +{ + int error; + + while ((cd->flags & CDF_LOCKED) != 0) { + cd->flags |= CDF_WANTED; + if ((error = tsleep(cd, PRIBIO | PCATCH, "cdlck", 0)) != 0) + return error; + } + cd->flags |= CDF_LOCKED; + return 0; +} + +/* + * Unlock and wake up any waiters. + */ +void +cdunlock(cd) + struct cd_softc *cd; +{ + + cd->flags &= ~CDF_LOCKED; + if ((cd->flags & CDF_WANTED) != 0) { + cd->flags &= ~CDF_WANTED; + wakeup(cd); + } +} + +/* + * open the device. Make sure the partition info is a up-to-date as can be. + */ +int +cdopen(dev, flag, fmt) + dev_t dev; + int flag, fmt; +{ + struct cd_softc *cd; + struct scsi_link *sc_link; + int unit, part; + int error; + + unit = CDUNIT(dev); + if (unit >= cdcd.cd_ndevs) + return ENXIO; + cd = cdcd.cd_devs[unit]; + if (!cd) + 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, + cdcd.cd_ndevs, part)); + + if (error = cdlock(cd)) + return error; + + if (cd->sc_dk.dk_openmask != 0) { + /* + * If any partition is open, but the disk has been invalidated, + * disallow further opens. + */ + if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) { + error = EIO; + goto bad3; + } + } else { + /* Check that it is still responding and ok. */ + if (error = scsi_test_unit_ready(sc_link, + SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_IGNORE_NOT_READY)) + goto bad3; + + /* Start the pack spinning if necessary. */ + if (error = scsi_start(sc_link, SSS_START, + SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT)) + goto bad3; + + sc_link->flags |= SDEV_OPEN; + + /* Lock the pack in. */ + if (error = scsi_prevent(sc_link, PR_PREVENT, + SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE)) + goto bad; + + if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) { + sc_link->flags |= SDEV_MEDIA_LOADED; + + /* Load the physical device parameters. */ + if (cd_get_parms(cd, 0) != 0) { + error = ENXIO; + goto bad2; + } + SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded ")); + + /* Fabricate a disk label. */ + cdgetdisklabel(cd); + SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel fabricated ")); + } + } + + part = CDPART(dev); + + /* Check that the partition exists. */ + if (part != RAW_PART && + (part >= cd->sc_dk.dk_label.d_npartitions || + cd->sc_dk.dk_label.d_partitions[part].p_fstype == FS_UNUSED)) { + error = ENXIO; + goto bad; + } + + /* Insure only one open at a time. */ + switch (fmt) { + case S_IFCHR: + cd->sc_dk.dk_copenmask |= (1 << part); + break; + case S_IFBLK: + cd->sc_dk.dk_bopenmask |= (1 << part); + break; + } + cd->sc_dk.dk_openmask = cd->sc_dk.dk_copenmask | cd->sc_dk.dk_bopenmask; + + SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n")); + cdunlock(cd); + return 0; + +bad2: + sc_link->flags &= ~SDEV_MEDIA_LOADED; + +bad: + if (cd->sc_dk.dk_openmask == 0) { + scsi_prevent(sc_link, PR_ALLOW, + SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE); + sc_link->flags &= ~SDEV_OPEN; + } + +bad3: + cdunlock(cd); + return error; +} + +/* + * close the device.. only called if we are the LAST + * occurence of an open device + */ +int +cdclose(dev, flag, fmt) + dev_t dev; + int flag, fmt; +{ + struct cd_softc *cd = cdcd.cd_devs[CDUNIT(dev)]; + int part = CDPART(dev); + int error; + + if (error = cdlock(cd)) + return error; + + switch (fmt) { + case S_IFCHR: + cd->sc_dk.dk_copenmask &= ~(1 << part); + break; + case S_IFBLK: + cd->sc_dk.dk_bopenmask &= ~(1 << part); + break; + } + cd->sc_dk.dk_openmask = cd->sc_dk.dk_copenmask | cd->sc_dk.dk_bopenmask; + + if (cd->sc_dk.dk_openmask == 0) { + /* XXXX Must wait for I/O to complete! */ + + scsi_prevent(cd->sc_link, PR_ALLOW, + SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY); + cd->sc_link->flags &= ~SDEV_OPEN; + } + + cdunlock(cd); + return 0; +} + +/* + * 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 +cdstrategy(bp) + struct buf *bp; +{ + struct cd_softc *cd = cdcd.cd_devs[CDUNIT(bp->b_dev)]; + int opri; + + SC_DEBUG(cd->sc_link, SDEV_DB2, ("cdstrategy ")); + SC_DEBUG(cd->sc_link, SDEV_DB1, + ("%d bytes @ blk %d\n", bp->b_bcount, bp->b_blkno)); + /* + * The transfer must be a whole number of blocks. + */ + if ((bp->b_bcount % cd->sc_dk.dk_label.d_secsize) != 0) { + bp->b_error = EINVAL; + goto bad; + } + /* + * If the device has been made invalid, error out + * maybe the media changed + */ + if ((cd->sc_link->flags & SDEV_MEDIA_LOADED) == 0) { + bp->b_error = EIO; + goto bad; + } + /* + * If it's a null transfer, return immediately + */ + if (bp->b_bcount == 0) + goto done; + + /* + * Do bounds checking, adjust transfer. if error, process. + * If end of partition, just return. + */ + if (CDPART(bp->b_dev) != RAW_PART && + bounds_check_with_label(bp, &cd->sc_dk.dk_label, + (cd->flags & (CDF_WLABEL|CDF_LABELLING)) != 0) <= 0) + goto done; + + opri = splbio(); + + /* + * Place it in the queue of disk activities for this disk + */ + disksort(&cd->buf_queue, bp); + + /* + * Tell the device to get going on the transfer if it's + * not doing anything, otherwise just wait for completion + */ + cdstart(cd); + + splx(opri); + return; + +bad: + bp->b_flags |= B_ERROR; +done: + /* + * Correctly set the buf to indicate a completed xfer + */ + bp->b_resid = bp->b_bcount; + biodone(bp); +} + +/* + * 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 splbio from cdstrategy and scsi_done + */ +void +cdstart(cd) + register struct cd_softc *cd; +{ + register struct scsi_link *sc_link = cd->sc_link; + struct buf *bp = 0; + struct buf *dp; + struct scsi_rw_big cmd; + int blkno, nblks; + struct partition *p; + + SC_DEBUG(sc_link, SDEV_DB2, ("cdstart ")); + /* + * Check if the device has room for another command + */ + while (sc_link->openings > 0) { + /* + * 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) { + sc_link->flags &= ~SDEV_WAITING; + wakeup((caddr_t)sc_link); + return; + } + + /* + * See if there is a buf with work for us to do.. + */ + dp = &cd->buf_queue; + if ((bp = dp->b_actf) == NULL) /* yes, an assign */ + return; + dp->b_actf = bp->b_actf; + + /* + * If the deivce has become invalid, abort all the + * reads and writes until all files have been closed and + * re-opened + */ + if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) { + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + biodone(bp); + continue; + } + + /* + * 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. + */ + blkno = + bp->b_blkno / (cd->sc_dk.dk_label.d_secsize / DEV_BSIZE); + if (CDPART(bp->b_dev) != RAW_PART) { + p = &cd->sc_dk.dk_label.d_partitions[CDPART(bp->b_dev)]; + blkno += p->p_offset; + } + nblks = howmany(bp->b_bcount, cd->sc_dk.dk_label.d_secsize); + + /* + * Fill out the scsi command + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = (bp->b_flags & B_READ) ? READ_BIG : WRITE_BIG; + cmd.addr_3 = (blkno >> 24) & 0xff; + cmd.addr_2 = (blkno >> 16) & 0xff; + cmd.addr_1 = (blkno >> 8) & 0xff; + cmd.addr_0 = blkno & 0xff; + cmd.length2 = (nblks >> 8) & 0xff; + cmd.length1 = nblks & 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_data, bp->b_bcount, + CDRETRIES, 30000, bp, SCSI_NOSLEEP | + ((bp->b_flags & B_READ) ? SCSI_DATA_IN : SCSI_DATA_OUT))) + printf("%s: not queued", cd->sc_dev.dv_xname); + } +} + +int +cdread(dev, uio) + dev_t dev; + struct uio *uio; +{ + struct cd_softc *cd = cdcd.cd_devs[CDUNIT(dev)]; + + return (physio(cdstrategy, NULL, dev, B_READ, + cd->sc_link->adapter->scsi_minphys, uio)); +} + +int +cdwrite(dev, uio) + dev_t dev; + struct uio *uio; +{ + struct cd_softc *cd = cdcd.cd_devs[CDUNIT(dev)]; + + return (physio(cdstrategy, NULL, dev, B_WRITE, + cd->sc_link->adapter->scsi_minphys, uio)); +} + +/* + * Perform special action on behalf of the user. + * Knows about the internals of this device + */ +int +cdioctl(dev, cmd, addr, flag, p) + dev_t dev; + u_long cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + struct cd_softc *cd = cdcd.cd_devs[CDUNIT(dev)]; + int error; + + SC_DEBUG(cd->sc_link, SDEV_DB2, ("cdioctl 0x%lx ", cmd)); + + /* + * If the device is not valid.. abandon ship + */ + if ((cd->sc_link->flags & SDEV_MEDIA_LOADED) == 0) + return EIO; + + switch (cmd) { + case DIOCGDINFO: + *(struct disklabel *)addr = cd->sc_dk.dk_label; + return 0; + + case DIOCGPART: + ((struct partinfo *)addr)->disklab = &cd->sc_dk.dk_label; + ((struct partinfo *)addr)->part = + &cd->sc_dk.dk_label.d_partitions[CDPART(dev)]; + return 0; + + case DIOCWDINFO: + case DIOCSDINFO: + if ((flag & FWRITE) == 0) + return EBADF; + + if (error = cdlock(cd)) + return error; + cd->flags |= CDF_LABELLING; + + error = setdisklabel(&cd->sc_dk.dk_label, + (struct disklabel *)addr, /*cd->sc_dk.dk_openmask : */0, + &cd->sc_dk.dk_cpulabel); + if (error == 0) { + } + + cd->flags &= ~CDF_LABELLING; + cdunlock(cd); + return error; + + case DIOCWLABEL: + return EBADF; + + case CDIOCPLAYTRACKS: { + struct ioc_play_track *args = (struct ioc_play_track *)addr; + struct cd_mode_data data; + if (error = cd_get_mode(cd, &data, AUDIO_PAGE)) + return error; + data.page.audio.flags &= ~CD_PA_SOTC; + data.page.audio.flags |= CD_PA_IMMED; + if (error = cd_set_mode(cd, &data)) + return error; + return cd_play_tracks(cd, args->start_track, args->start_index, + args->end_track, args->end_index); + } + case CDIOCPLAYMSF: { + struct ioc_play_msf *args + = (struct ioc_play_msf *)addr; + struct cd_mode_data data; + if (error = cd_get_mode(cd, &data, AUDIO_PAGE)) + return error; + data.page.audio.flags &= ~CD_PA_SOTC; + data.page.audio.flags |= CD_PA_IMMED; + if (error = cd_set_mode(cd, &data)) + return error; + return cd_play_msf(cd, args->start_m, args->start_s, + args->start_f, args->end_m, args->end_s, args->end_f); + } + case CDIOCPLAYBLOCKS: { + struct ioc_play_blocks *args + = (struct ioc_play_blocks *)addr; + struct cd_mode_data data; + if (error = cd_get_mode(cd, &data, AUDIO_PAGE)) + return error; + data.page.audio.flags &= ~CD_PA_SOTC; + data.page.audio.flags |= CD_PA_IMMED; + if (error = cd_set_mode(cd, &data)) + return error; + return cd_play(cd, args->blk, args->len); + } + case CDIOCREADSUBCHANNEL: { + struct ioc_read_subchannel *args + = (struct ioc_read_subchannel *)addr; + struct cd_sub_channel_info data; + int len = args->data_len; + if (len > sizeof(data) || + len < sizeof(struct cd_sub_channel_header)) + return EINVAL; + if (error = cd_read_subchannel(cd, args->address_format, + args->data_format, args->track, &data, len)) + return error; + len = min(len, ((data.header.data_len[0] << 8) + + data.header.data_len[1] + + sizeof(struct cd_sub_channel_header))); + return copyout(&data, args->data, len); + } + case CDIOREADTOCHEADER: { + struct ioc_toc_header th; + if (error = cd_read_toc(cd, 0, 0, &th, sizeof(th))) + return error; + th.len = ntohs(th.len); + bcopy(&th, addr, sizeof(th)); + return 0; + } + 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; + int len = te->data_len; + th = &data.header; + + if (len > sizeof(data.entries) || + len < sizeof(struct cd_toc_entry)) + return EINVAL; + if (error = cd_read_toc(cd, te->address_format, + te->starting_track, (struct cd_toc_entry *)&data, + len + sizeof(struct ioc_toc_header))) + return error; + len = min(len, ntohs(th->len) - (sizeof(th->starting_track) + + sizeof(th->ending_track))); + return copyout(data.entries, te->data, len); + } + case CDIOCSETPATCH: { + struct ioc_patch *arg = (struct ioc_patch *)addr; + struct cd_mode_data data; + if (error = cd_get_mode(cd, &data, AUDIO_PAGE)) + return error; + 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]; + return cd_set_mode(cd, &data); + } + case CDIOCGETVOL: { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if (error = cd_get_mode(cd, &data, AUDIO_PAGE)) + return error; + 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; + return 0; + } + case CDIOCSETVOL: { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if (error = cd_get_mode(cd, &data, AUDIO_PAGE)) + return error; + 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]; + return cd_set_mode(cd, &data); + } + case CDIOCSETMONO: { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if (error = cd_get_mode(cd, &data, AUDIO_PAGE)) + return error; + 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; + return cd_set_mode(cd, &data); + } + case CDIOCSETSTEREO: { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if (error = cd_get_mode(cd, &data, AUDIO_PAGE)) + return error; + 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; + return cd_set_mode(cd, &data); + } + case CDIOCSETMUTE: { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if (error = cd_get_mode(cd, &data, AUDIO_PAGE)) + return error; + 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; + return cd_set_mode(cd, &data); + } + case CDIOCSETLEFT: { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if (error = cd_get_mode(cd, &data, AUDIO_PAGE)) + return error; + 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; + return cd_set_mode(cd, &data); + } + case CDIOCSETRIGHT: { + struct ioc_vol *arg = (struct ioc_vol *)addr; + struct cd_mode_data data; + if (error = cd_get_mode(cd, &data, AUDIO_PAGE)) + return error; + 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; + return cd_set_mode(cd, &data); + } + case CDIOCRESUME: + return cd_pause(cd, 1); + case CDIOCPAUSE: + return cd_pause(cd, 0); + case CDIOCSTART: + return scsi_start(cd->sc_link, SSS_START, 0); + case CDIOCSTOP: + return scsi_start(cd->sc_link, SSS_STOP, 0); + case CDIOCEJECT: + return scsi_start(cd->sc_link, SSS_STOP|SSS_LOEJ, 0); + case CDIOCALLOW: + return scsi_prevent(cd->sc_link, PR_ALLOW, 0); + case CDIOCPREVENT: + return scsi_prevent(cd->sc_link, PR_PREVENT, 0); + case CDIOCSETDEBUG: + cd->sc_link->flags |= (SDEV_DB1 | SDEV_DB2); + return 0; + case CDIOCCLRDEBUG: + cd->sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2); + return 0; + case CDIOCRESET: + return cd_reset(cd); + + default: + if (CDPART(dev) != RAW_PART) + return ENOTTY; + return scsi_do_ioctl(cd->sc_link, dev, cmd, addr, flag, p); + } + +#ifdef DIAGNOSTIC + panic("cdioctl: impossible"); +#endif +} + +/* + * 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 + */ +void +cdgetdisklabel(cd) + struct cd_softc *cd; +{ + struct disklabel *lp = &cd->sc_dk.dk_label; + + bzero(lp, sizeof(struct disklabel)); + bzero(&cd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel)); + + lp->d_secsize = cd->params.blksize; + lp->d_ntracks = 1; + lp->d_nsectors = 100; + lp->d_ncylinders = (cd->params.disksize / 100) + 1; + lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; + + strncpy(lp->d_typename, "SCSI CD-ROM", 16); + lp->d_type = DTYPE_SCSI; + strncpy(lp->d_packname, "fictitious", 16); + lp->d_secperunit = cd->params.disksize; + lp->d_rpm = 300; + lp->d_interleave = 1; + lp->d_flags = D_REMOVABLE; + + lp->d_partitions[0].p_offset = 0; + lp->d_partitions[0].p_size = + lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); + lp->d_partitions[0].p_fstype = FS_ISO9660; + lp->d_partitions[RAW_PART].p_offset = 0; + lp->d_partitions[RAW_PART].p_size = + lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); + lp->d_partitions[RAW_PART].p_fstype = FS_ISO9660; + lp->d_npartitions = RAW_PART + 1; + + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_checksum = dkcksum(lp); +} + +/* + * Find out from the device what it's capacity is + */ +u_long +cd_size(cd, flags) + struct cd_softc *cd; + int flags; +{ + struct scsi_read_cd_cap_data rdcap; + struct scsi_read_cd_capacity scsi_cmd; + int blksize; + u_long size; + + /* + * make up a scsi command and ask the scsi driver to do + * it for you. + */ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = 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, + 2000, NULL, flags | SCSI_DATA_IN) != 0) + return 0; + + blksize = (rdcap.length_3 << 24) + (rdcap.length_2 << 16) + + (rdcap.length_1 << 8) + rdcap.length_0; + if (blksize < 512) + blksize = 2048; /* some drives lie ! */ + cd->params.blksize = blksize; + + size = (rdcap.addr_3 << 24) + (rdcap.addr_2 << 16) + + (rdcap.addr_1 << 8) + rdcap.addr_0 + 1; + if (size < 100) + size = 400000; /* ditto */ + cd->params.disksize = size; + + return size; +} + +/* + * Get the requested page into the buffer given + */ +int +cd_get_mode(cd, data, page) + struct cd_softc *cd; + struct cd_mode_data *data; + int page; +{ + struct scsi_mode_sense scsi_cmd; + int error; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + bzero(data, sizeof(*data)); + scsi_cmd.opcode = MODE_SENSE; + scsi_cmd.page = page; + scsi_cmd.length = sizeof(*data) & 0xff; + return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd, + sizeof(scsi_cmd), (u_char *)data, sizeof(*data), CDRETRIES, 20000, + NULL, SCSI_DATA_IN); +} + +/* + * Get the requested page into the buffer given + */ +int +cd_set_mode(cd, data) + struct cd_softc *cd; + struct cd_mode_data *data; +{ + struct scsi_mode_select scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = MODE_SELECT; + scsi_cmd.byte2 |= SMS_PF; + scsi_cmd.length = sizeof(*data) & 0xff; + data->header.data_length = 0; + return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd, + sizeof(scsi_cmd), (u_char *)data, sizeof(*data), CDRETRIES, 20000, + NULL, SCSI_DATA_OUT); +} + +/* + * Get scsi driver to send a "start playing" command + */ +int +cd_play(cd, blkno, nblks) + struct cd_softc *cd; + int blkno, nblks; +{ + struct scsi_play scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = PLAY; + scsi_cmd.blk_addr[0] = (blkno >> 24) & 0xff; + scsi_cmd.blk_addr[1] = (blkno >> 16) & 0xff; + scsi_cmd.blk_addr[2] = (blkno >> 8) & 0xff; + scsi_cmd.blk_addr[3] = blkno & 0xff; + scsi_cmd.xfer_len[0] = (nblks >> 8) & 0xff; + scsi_cmd.xfer_len[1] = nblks & 0xff; + return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd, + sizeof(scsi_cmd), 0, 0, CDRETRIES, 200000, NULL, 0); +} + +/* + * Get scsi driver to send a "start playing" command + */ +int +cd_play_big(cd, blkno, nblks) + struct cd_softc *cd; + int blkno, nblks; +{ + struct scsi_play_big scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = PLAY_BIG; + scsi_cmd.blk_addr[0] = (blkno >> 24) & 0xff; + scsi_cmd.blk_addr[1] = (blkno >> 16) & 0xff; + scsi_cmd.blk_addr[2] = (blkno >> 8) & 0xff; + scsi_cmd.blk_addr[3] = blkno & 0xff; + scsi_cmd.xfer_len[0] = (nblks >> 24) & 0xff; + scsi_cmd.xfer_len[1] = (nblks >> 16) & 0xff; + scsi_cmd.xfer_len[2] = (nblks >> 8) & 0xff; + scsi_cmd.xfer_len[3] = nblks & 0xff; + return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd, + sizeof(scsi_cmd), 0, 0, CDRETRIES, 20000, NULL, 0); +} + +/* + * Get scsi driver to send a "start playing" command + */ +int +cd_play_tracks(cd, strack, sindex, etrack, eindex) + struct cd_softc *cd; + int strack, sindex, etrack, eindex; +{ + struct scsi_play_track scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = 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->sc_link, (struct scsi_generic *)&scsi_cmd, + sizeof(scsi_cmd), 0, 0, CDRETRIES, 20000, NULL, 0); +} + +/* + * Get scsi driver to send a "play msf" command + */ +int +cd_play_msf(cd, startm, starts, startf, endm, ends, endf) + struct cd_softc *cd; + int startm, starts, startf, endm, ends, endf; +{ + struct scsi_play_msf scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = 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->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 + */ +int +cd_pause(cd, go) + struct cd_softc *cd; + int go; +{ + struct scsi_pause scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = PAUSE; + scsi_cmd.resume = go; + return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd, + sizeof(scsi_cmd), 0, 0, CDRETRIES, 2000, NULL, 0); +} + +/* + * Get scsi driver to send a "RESET" command + */ +int +cd_reset(cd) + struct cd_softc *cd; +{ + + return scsi_scsi_cmd(cd->sc_link, 0, 0, 0, 0, CDRETRIES, 2000, NULL, + SCSI_RESET); +} + +/* + * Read subchannel + */ +int +cd_read_subchannel(cd, mode, format, track, data, len) + struct cd_softc *cd; + int mode, format, len; + struct cd_sub_channel_info *data; +{ + struct scsi_read_subchannel scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = 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) & 0xff; + scsi_cmd.data_len[1] = len & 0xff; + return scsi_scsi_cmd(cd->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 + */ +int +cd_read_toc(cd, mode, start, data, len) + struct cd_softc *cd; + int mode, start, len; + struct cd_toc_entry *data; +{ + struct scsi_read_toc scsi_cmd; + int ntoc; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + /*if (len!=sizeof(struct ioc_toc_header)) + * ntoc=((len)-sizeof(struct ioc_toc_header))/sizeof(struct cd_toc_entry); + * else */ + ntoc = len; + scsi_cmd.opcode = READ_TOC; + if (mode == CD_MSF_FORMAT) + scsi_cmd.byte2 |= CD_MSF; + scsi_cmd.from_track = start; + scsi_cmd.data_len[0] = (ntoc >> 8) & 0xff; + scsi_cmd.data_len[1] = ntoc & 0xff; + return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd, + sizeof(struct scsi_read_toc), (u_char *)data, len, CDRETRIES, + 5000, NULL, SCSI_DATA_IN); +} + +/* + * Get the scsi driver to send a full inquiry to the device and use the + * results to fill out the disk parameter structure. + */ +int +cd_get_parms(cd, flags) + struct cd_softc *cd; + int flags; +{ + + /* + * give a number of sectors so that sec * trks * cyls + * is <= disk_size + */ + if (cd_size(cd, flags) == 0) + return ENXIO; + + return 0; +} + +int +cdsize(dev) + dev_t dev; +{ + + /* CD-ROMs are read-only. */ + return -1; +} + +int +cddump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ + + /* Not implemented. */ + return ENXIO; +} diff --git a/sys/scsi/ch.c b/sys/scsi/ch.c new file mode 100644 index 00000000000..8f9cb9e17a9 --- /dev/null +++ b/sys/scsi/ch.c @@ -0,0 +1,462 @@ +/* $NetBSD: ch.c,v 1.14 1995/01/16 21:31:38 mycroft Exp $ */ + +/* + * Copyright (c) 1994 Charles Hannum. All rights reserved. + * + * 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 Charles Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Originally written by grefen@????? + * Based on scsi drivers by Julian Elischer (julian@tfs.com) + */ + +#include <sys/types.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 <sys/device.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_changer.h> +#include <scsi/scsiconf.h> + +#define CHRETRIES 2 + +#define CHMODE(z) (minor(z) & 0x0f) +#define CHUNIT(z) (minor(z) >> 4) + +struct ch_softc { + struct device sc_dev; + + struct scsi_link *sc_link; /* all the inter level info */ + u_int16_t chmo; /* Offset of first CHM */ + u_int16_t chms; /* No. of CHM */ + u_int16_t slots; /* No. of Storage Elements */ + u_int16_t sloto; /* Offset of first SE */ + u_int16_t imexs; /* No. of Import/Export Slots */ + u_int16_t imexo; /* Offset of first IM/EX */ + u_int16_t drives; /* No. of CTS */ + u_int16_t driveo; /* Offset of first CTS */ + u_int16_t rot; /* CHM can rotate */ + u_long op_matrix; /* possible operations */ + u_int16_t lsterr; /* details of lasterror */ + u_char stor; /* posible Storage locations */ +}; + +int chmatch __P((struct device *, void *, void *)); +void chattach __P((struct device *, struct device *, void *)); + +struct cfdriver chcd = { + NULL, "ch", chmatch, chattach, DV_DULL, sizeof(struct ch_softc) +}; + +/* + * This driver is so simple it uses all the default services + */ +struct scsi_device ch_switch = { + NULL, + NULL, + NULL, + NULL, +}; + +struct scsi_inquiry_pattern ch_patterns[] = { + {T_CHANGER, T_REMOV, + "", "", ""}, +}; + +int +chmatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct cfdata *cf = match; + struct scsibus_attach_args *sa = aux; + int priority; + + (void)scsi_inqmatch(sa->sa_inqbuf, + (caddr_t)ch_patterns, sizeof(ch_patterns)/sizeof(ch_patterns[0]), + sizeof(ch_patterns[0]), &priority); + return (priority); +} + +/* + * The routine called by the low level scsi routine when it discovers + * a device suitable for this driver. + */ +void +chattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct ch_softc *ch = (void *)self; + struct scsibus_attach_args *sa = aux; + struct scsi_link *sc_link = sa->sa_sc_link; + + SC_DEBUG(sc_link, SDEV_DB2, ("chattach: ")); + + /* + * Store information needed to contact our base driver + */ + ch->sc_link = sc_link; + sc_link->device = &ch_switch; + sc_link->device_softc = ch; + sc_link->openings = 1; + + /* + * Use the subdriver to request information regarding + * the drive. We cannot use interrupts yet, so the + * request must specify this. + */ + if (ch_mode_sense(ch, SCSI_AUTOCONF) != 0) + printf(": offline\n"); + else + printf(": %d slot(s), %d drive(s), %d arm(s), %d i/e-slot(s)\n", + ch->slots, ch->drives, ch->chms, ch->imexs); +} + +/* + * open the device. + */ +int +chopen(dev) + dev_t dev; +{ + int error = 0; + int unit, mode; + struct ch_softc *ch; + struct scsi_link *sc_link; + + unit = CHUNIT(dev); + if (unit >= chcd.cd_ndevs) + return ENXIO; + ch = chcd.cd_devs[unit]; + if (!ch) + return ENXIO; + + mode = CHMODE(dev); + sc_link = ch->sc_link; + + SC_DEBUG(sc_link, SDEV_DB1, + ("chopen: dev=0x%x (unit %d (of %d))\n", dev, unit, chcd.cd_ndevs)); + + /* + * Only allow one at a time + */ + if (sc_link->flags & SDEV_OPEN) { + printf("%s: already open\n", ch->sc_dev.dv_xname); + return EBUSY; + } + + /* + * Catch any unit attention errors. + */ + if (error = scsi_test_unit_ready(sc_link, SCSI_IGNORE_MEDIA_CHANGE)) + goto bad; + + sc_link->flags |= SDEV_OPEN; /* unit attn are now errors */ + + /* + * Make sure data is loaded + */ + if (error = ch_mode_sense(ch, 0)) { + printf("%s: offline\n", ch->sc_dev.dv_xname); + goto bad; + } + + SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n")); + return 0; + +bad: + sc_link->flags &= ~SDEV_OPEN; + return error; +} + +/* + * close the device.. only called if we are the LAST + * occurence of an open device + */ +int +chclose(dev) + dev_t dev; +{ + struct ch_softc *ch = chcd.cd_devs[CHUNIT(dev)]; + + SC_DEBUG(ch->sc_link, SDEV_DB1, ("closing\n")); + ch->sc_link->flags &= ~SDEV_OPEN; + + return 0; +} + +/* + * Perform special action on behalf of the user + * Knows about the internals of this device + */ +int +chioctl(dev, cmd, arg, mode, p) + dev_t dev; + u_long cmd; + caddr_t arg; + int mode; + struct proc *p; +{ + struct ch_softc *ch = chcd.cd_devs[CHUNIT(dev)]; + struct scsi_link *sc_link = ch->sc_link; + int number; + int flags; + + /* + * Find the device that the user is talking about + */ + flags = 0; /* give error messages, act on errors etc. */ + + switch (cmd) { + case CHIOOP: { + struct chop *chop = (struct chop *) arg; + SC_DEBUG(sc_link, SDEV_DB2, ("[chtape_chop: %x]\n", + chop->ch_op)); + + switch (chop->ch_op) { + case CHGETPARAM: + chop->u.getparam.chmo = ch->chmo; + chop->u.getparam.chms = ch->chms; + chop->u.getparam.sloto = ch->sloto; + chop->u.getparam.slots = ch->slots; + chop->u.getparam.imexo = ch->imexo; + chop->u.getparam.imexs = ch->imexs; + chop->u.getparam.driveo = ch->driveo; + chop->u.getparam.drives = ch->drives; + chop->u.getparam.rot = ch->rot; + chop->result = 0; + return 0; + break; + case CHPOSITION: + return ch_position(ch, &chop->result, + chop->u.position.chm, chop->u.position.to, flags); + case CHMOVE: + return ch_move(ch, &chop->result, chop->u.position.chm, + chop->u.move.from, chop->u.move.to, flags); + case CHGETELEM: + return ch_getelem(ch, &chop->result, + chop->u.get_elem_stat.type, + chop->u.get_elem_stat.from, + &chop->u.get_elem_stat.elem_data, flags); + default: + return EINVAL; + } + } + default: + return scsi_do_ioctl(sc_link, dev, cmd, arg, mode, p); + } +#ifdef DIAGNOSTIC + panic("chioctl: impossible"); +#endif +} + +int +ch_getelem(ch, stat, type, from, data, flags) + struct ch_softc *ch; + short *stat; + int type, from; + char *data; + int flags; +{ + struct scsi_read_element_status scsi_cmd; + char elbuf[32]; + int error; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = 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; + + error = scsi_scsi_cmd(ch->sc_link, (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), (u_char *) elbuf, 32, CHRETRIES, 100000, NULL, + SCSI_DATA_IN | flags); + if (error) + *stat = ch->lsterr; + else + *stat = 0; + bcopy(elbuf + 16, data, 16); + return error; +} + +int +ch_move(ch, stat, chm, from, to, flags) + struct ch_softc *ch; + short *stat; + int chm, from, to, flags; +{ + struct scsi_move_medium scsi_cmd; + int error; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = 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; + error = scsi_scsi_cmd(ch->sc_link, (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), NULL, 0, CHRETRIES, 100000, NULL, flags); + if (error) + *stat = ch->lsterr; + else + *stat = 0; + return error; +} + +int +ch_position(ch, stat, chm, to, flags) + struct ch_softc *ch; + short *stat; + int chm, to, flags; +{ + struct scsi_position_to_element scsi_cmd; + int error; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = 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; + error = scsi_scsi_cmd(ch->sc_link, (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), NULL, 0, CHRETRIES, 100000, NULL, flags); + if (error) + *stat = ch->lsterr; + else + *stat = 0; + return error; +} + +/* + * Get the scsi driver to send a full inquiry to the + * device and use the results to fill out the global + * parameter structure. + */ +int +ch_mode_sense(ch, flags) + struct ch_softc *ch; + int 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; + int i, l; + int error; + struct scsi_link *sc_link = ch->sc_link; + + /* + * First check if we have it all loaded + */ + if (sc_link->flags & SDEV_MEDIA_LOADED) + return 0; + + /* + * First do a mode sense + */ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = MODE_SENSE; + scsi_cmd.byte2 = SMS_DBD; + scsi_cmd.page = 0x3f; /* All Pages */ + scsi_cmd.length = sizeof(scsi_sense); + + /* + * Read in the pages + */ + if (error = scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), (u_char *) &scsi_sense, sizeof(scsi_sense), + CHRETRIES, 5000, NULL, flags | SCSI_DATA_IN)) { + printf("%s: could not mode sense\n", ch->sc_dev.dv_xname); + return error; + } + + sc_link->flags |= SDEV_MEDIA_LOADED; + l = scsi_sense[0] - 3; + b = &scsi_sense[4]; + + /* + * To avoid alignment problems + */ +/* XXXX - 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_char pc = (*b++) & 0x3f; + u_char pl = *b++; + u_char *bb = b; + switch (pc) { + case 0x1d: + ch->chmo = p2copy(bb); + ch->chms = p2copy(bb); + ch->sloto = p2copy(bb); + ch->slots = p2copy(bb); + ch->imexo = p2copy(bb); + ch->imexs = p2copy(bb); + ch->driveo = p2copy(bb); + ch->drives = p2copy(bb); + break; + case 0x1e: + ch->rot = *b & 0x1; + break; + case 0x1f: + ch->stor = *b & 0xf; + bb += 2; + ch->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->chmo, ch->chms, ch->sloto, ch->slots, ch->imexo, ch->imexs, + ch->driveo, ch->drives, ch->rot ? "can" : "can't")); + return 0; +} diff --git a/sys/scsi/files.scsi b/sys/scsi/files.scsi new file mode 100644 index 00000000000..c1060f0c18a --- /dev/null +++ b/sys/scsi/files.scsi @@ -0,0 +1,26 @@ +# $NetBSD: files.scsi,v 1.1 1995/04/17 07:15:40 cgd Exp $ +# +# Config.new file and device description for machine-independent SCSI code. +# Included by ports that need it. Ports that usee it must provide +# their own "major" declarations for the appropriate devices. + +define scsi {} +file scsi/scsi_base.c scsi +file scsi/scsi_ioctl.c scsi +file scsi/scsiconf.c scsi + +device scsibus at scsi {target = -1, lun = -1} + +device cd at scsibus: disk +file scsi/cd.c cd needs-flag +device ch at scsibus: disk +file scsi/ch.c ch needs-flag +device sd at scsibus: disk +file scsi/sd.c sd needs-flag +device st at scsibus: tape +file scsi/st.c st needs-flag +device su at scsibus: disk +file scsi/su.c su needs-flag +device uk at scsibus: disk +file scsi/uk.c uk needs-flag + diff --git a/sys/scsi/scsi_all.h b/sys/scsi/scsi_all.h new file mode 100644 index 00000000000..646826c872b --- /dev/null +++ b/sys/scsi/scsi_all.h @@ -0,0 +1,316 @@ +/* $NetBSD: scsi_all.h,v 1.6 1994/12/28 19:42:54 mycroft Exp $ */ + +/* + * 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 + */ + +#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_int8_t opcode; + u_int8_t bytes[11]; +}; + +struct scsi_test_unit_ready { + u_int8_t opcode; + u_int8_t byte2; + u_int8_t unused[3]; + u_int8_t control; +}; + +struct scsi_send_diag { + u_int8_t opcode; + u_int8_t byte2; +#define SSD_UOL 0x01 +#define SSD_DOL 0x02 +#define SSD_SELFTEST 0x04 +#define SSD_PF 0x10 + u_int8_t unused[1]; + u_int8_t paramlen[2]; + u_int8_t control; +}; + +struct scsi_sense { + u_int8_t opcode; + u_int8_t byte2; + u_int8_t unused[2]; + u_int8_t length; + u_int8_t control; +}; + +struct scsi_inquiry { + u_int8_t opcode; + u_int8_t byte2; + u_int8_t unused[2]; + u_int8_t length; + u_int8_t control; +}; + +struct scsi_mode_sense { + u_int8_t opcode; + u_int8_t byte2; +#define SMS_DBD 0x08 + u_int8_t 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_int8_t unused; + u_int8_t length; + u_int8_t control; +}; + +struct scsi_mode_sense_big { + u_int8_t opcode; + u_int8_t byte2; /* same bits as small version */ + u_int8_t page; /* same bits as small version */ + u_int8_t unused[4]; + u_int8_t length[2]; + u_int8_t control; +}; + +struct scsi_mode_select { + u_int8_t opcode; + u_int8_t byte2; +#define SMS_SP 0x01 +#define SMS_PF 0x10 + u_int8_t unused[2]; + u_int8_t length; + u_int8_t control; +}; + +struct scsi_mode_select_big { + u_int8_t opcode; + u_int8_t byte2; /* same bits as small version */ + u_int8_t unused[5]; + u_int8_t length[2]; + u_int8_t control; +}; + +struct scsi_reserve { + u_int8_t opcode; + u_int8_t byte2; + u_int8_t unused[2]; + u_int8_t length; + u_int8_t control; +}; + +struct scsi_release { + u_int8_t opcode; + u_int8_t byte2; + u_int8_t unused[2]; + u_int8_t length; + u_int8_t control; +}; + +struct scsi_prevent { + u_int8_t opcode; + u_int8_t byte2; + u_int8_t unused[2]; + u_int8_t how; + u_int8_t control; +}; +#define PR_PREVENT 0x01 +#define PR_ALLOW 0x00 + +struct scsi_changedef { + u_int8_t opcode; + u_int8_t byte2; + u_int8_t unused1; + u_int8_t how; + u_int8_t unused[4]; + u_int8_t datalen; + u_int8_t 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_CDROM 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_int8_t 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_int8_t dev_qual2; +#define SID_QUAL2 0x7F +#define SID_REMOVABLE 0x80 + u_int8_t version; +#define SID_ANSII 0x07 +#define SID_ECMA 0x38 +#define SID_ISO 0xC0 + u_int8_t response_format; + u_int8_t additional_length; + u_int8_t unused[2]; + u_int8_t 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_int8_t extra[8]; +}; + +/* + * This looks bad, and it is. However it fixes padding problems + * caused by using unions. This *needs* to be an array, if this code + * is to work on any architecture. + */ +struct scsi_sense_data { +/* 1*/ u_int8_t error_code; /* same bits as new version */ +#define XXX_unextended_blockhi extended_segment +#define XXX_unextended_blockmed extended_flags +#define XXX_unextended_blocklow extended_info[0] +/* 2*/ u_int8_t extended_segment; +/* 3*/ u_int8_t extended_flags; /* same bits as new version */ +/* 7*/ u_int8_t extended_info[4]; +/* 8*/ u_int8_t extended_extra_len; + /* + * allocate enough room to hold new stuff + * (by increasing 16 to 24 below) + */ +/*32*/ u_int8_t extended_extra_bytes[24]; +}; /* total of 32 bytes */ + +struct scsi_sense_data_new { +/* 1*/ u_int8_t error_code; +#define SSD_ERRCODE 0x7F +#define SSD_ERRCODE_VALID 0x80 + union { + struct { /* this is deprecated, the standard says "DON'T"*/ +/* 2*/ u_int8_t blockhi; +/* 3*/ u_int8_t blockmed; +/* 4*/ u_int8_t blocklow; + } unextended; + struct { +/* 2*/ u_int8_t segment; +/* 3*/ u_int8_t flags; +#define SSD_KEY 0x0F +#define SSD_ILI 0x20 +#define SSD_EOM 0x40 +#define SSD_FILEMARK 0x80 +/* 7*/ u_int8_t info[4]; +/* 8*/ u_int8_t extra_len; +/*12*/ u_int8_t cmd_spec_info[4]; +/*13*/ u_int8_t add_sense_code; +/*14*/ u_int8_t add_sense_code_qual; +/*15*/ u_int8_t fru; +/*16*/ u_int8_t sense_key_spec_1; +#define SSD_SCS_VALID 0x80 +/*17*/ u_int8_t sense_key_spec_2; +/*18*/ u_int8_t sense_key_spec_3; +/*32*/ u_int8_t extra_bytes[14]; + } extended; + } ext; +}; /* total of 32 bytes */ + +struct scsi_blk_desc { + u_int8_t density; + u_int8_t nblocks[3]; + u_int8_t reserved; + u_int8_t blklen[3]; +}; + +struct scsi_mode_header { + u_int8_t data_length; /* Sense data length */ + u_int8_t medium_type; + u_int8_t dev_spec; + u_int8_t blk_desc_len; +}; + +struct scsi_mode_header_big { + u_int8_t data_length[2]; /* Sense data length */ + u_int8_t medium_type; + u_int8_t dev_spec; + u_int8_t unused[2]; + u_int8_t 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 00000000000..7a61c55237f --- /dev/null +++ b/sys/scsi/scsi_base.c @@ -0,0 +1,848 @@ +/* $NetBSD: scsi_base.c,v 1.30 1995/09/26 19:26:55 thorpej Exp $ */ + +/* + * Copyright (c) 1994, 1995 Charles Hannum. All rights reserved. + * + * 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 Charles Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Originally written by Julian Elischer (julian@dialix.oz.au) + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/buf.h> +#include <sys/uio.h> +#include <sys/malloc.h> +#include <sys/errno.h> +#include <sys/device.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_disk.h> +#include <scsi/scsiconf.h> + +void scsi_error __P((struct scsi_xfer *, int)); + +LIST_HEAD(xs_free_list, scsi_xfer) xs_free_list; + +/* + * 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 * +scsi_get_xs(sc_link, flags) + struct scsi_link *sc_link; /* who to charge the xs to */ + int flags; /* if this call can sleep */ +{ + struct scsi_xfer *xs; + int s; + + SC_DEBUG(sc_link, SDEV_DB3, ("scsi_get_xs\n")); + s = splbio(); + while (sc_link->openings <= 0) { + SC_DEBUG(sc_link, SDEV_DB3, ("sleeping\n")); + if ((flags & SCSI_NOSLEEP) != 0) { + splx(s); + return 0; + } + sc_link->flags |= SDEV_WAITING; + (void) tsleep(sc_link, PRIBIO, "getxs", 0); + } + sc_link->openings--; + if (xs = xs_free_list.lh_first) { + LIST_REMOVE(xs, free_list); + splx(s); + } else { + splx(s); + SC_DEBUG(sc_link, SDEV_DB3, ("making\n")); + xs = malloc(sizeof(*xs), M_DEVBUF, + ((flags & SCSI_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK)); + if (!xs) { + sc_print_addr(sc_link); + printf("cannot allocate scsi xs\n"); + return 0; + } + } + + SC_DEBUG(sc_link, SDEV_DB3, ("returning\n")); + xs->flags = INUSE | flags; + 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 +scsi_free_xs(xs, flags) + struct scsi_xfer *xs; + int flags; +{ + struct scsi_link *sc_link = xs->sc_link; + + xs->flags &= ~INUSE; + LIST_INSERT_HEAD(&xs_free_list, xs, free_list); + + SC_DEBUG(sc_link, SDEV_DB3, ("scsi_free_xs\n")); + /* if was 0 and someone waits, wake them up */ + sc_link->openings++; + if ((sc_link->flags & SDEV_WAITING) != 0) { + sc_link->flags &= ~SDEV_WAITING; + wakeup(sc_link); + } else { + if (sc_link->device->start) { + SC_DEBUG(sc_link, SDEV_DB2, ("calling private start()\n")); + (*(sc_link->device->start)) (sc_link->device_softc); + } + } +} + +/* + * Make a scsi_xfer, and return a pointer to it. + */ +static __inline struct scsi_xfer * +scsi_make_xs(sc_link, scsi_cmd, cmdlen, data_addr, datalen, + retries, timeout, bp, flags) + struct scsi_link *sc_link; + struct scsi_generic *scsi_cmd; + int cmdlen; + u_char *data_addr; + int datalen; + int retries; + int timeout; + struct buf *bp; + int flags; +{ + struct scsi_xfer *xs; + + if ((xs = scsi_get_xs(sc_link, flags)) == NULL) + return NULL; + + /* + * Fill out the scsi_xfer structure. We don't know whose context + * the cmd is in, so copy it. + */ + xs->sc_link = sc_link; + bcopy(scsi_cmd, &xs->cmdstore, cmdlen); + xs->cmd = &xs->cmdstore; + xs->cmdlen = cmdlen; + xs->data = data_addr; + xs->datalen = datalen; + xs->retries = retries; + xs->timeout = timeout; + xs->bp = bp; + + return xs; +} + +/* + * Find out from the device what its capacity is. + */ +u_long +scsi_size(sc_link, flags) + struct scsi_link *sc_link; + int flags; +{ + struct scsi_read_cap_data rdcap; + struct scsi_read_capacity scsi_cmd; + u_long size; + + /* + * make up a scsi command and ask the scsi driver to do + * it for you. + */ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = 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 + */ +int +scsi_test_unit_ready(sc_link, flags) + struct scsi_link *sc_link; + int flags; +{ + struct scsi_test_unit_ready scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = TEST_UNIT_READY; + + return scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), 0, 0, 2, 10000, NULL, flags); +} + +/* + * Do a scsi operation, asking a device to run as SCSI-II if it can. + */ +int +scsi_change_def(sc_link, flags) + struct scsi_link *sc_link; + int flags; +{ + struct scsi_changedef scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = 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. + */ +int +scsi_inquire(sc_link, inqbuf, flags) + struct scsi_link *sc_link; + struct scsi_inquiry_data *inqbuf; + int flags; +{ + struct scsi_inquiry scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = 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, 10000, NULL, + SCSI_DATA_IN | flags); +} + +/* + * Prevent or allow the user to remove the media + */ +int +scsi_prevent(sc_link, type, flags) + struct scsi_link *sc_link; + int type, flags; +{ + struct scsi_prevent scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = 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 + */ +int +scsi_start(sc_link, type, flags) + struct scsi_link *sc_link; + int type, flags; +{ + struct scsi_start_stop scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = START_STOP; + scsi_cmd.byte2 = 0x00; + scsi_cmd.how = type; + return scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), 0, 0, 2, + type == SSS_START ? 30000 : 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; + int error; + + SC_DEBUG(sc_link, SDEV_DB2, ("scsi_done\n")); +#ifdef SCSIDEBUG + if ((sc_link->flags & SDEV_DB1) != 0) + 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) != 0) { + 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 ")); + + scsi_free_xs(xs, 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")); + error = (*sc_link->device->done) (xs); + if (error == EJUSTRETURN) + goto done; + SC_DEBUG(sc_link, SDEV_DB3, ("continuing with generic done()\n")); + } + if (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(xs); + return; + } + /* + * Go and handle errors now. + * If it returns ERESTART then we should RETRY + */ +retry: + if (sc_err1(xs, 1) == ERESTART) { + switch ((*(sc_link->adapter->scsi_cmd)) (xs)) { + case SUCCESSFULLY_QUEUED: + return; + + case TRY_AGAIN_LATER: + xs->error = XS_BUSY; + case COMPLETE: + goto retry; + } + } +done: + scsi_free_xs(xs, SCSI_NOSLEEP); +} + +int +scsi_execute_xs(xs) + struct scsi_xfer *xs; +{ + int error; + int s; + + xs->flags &= ~ITSDONE; + xs->error = XS_NOERROR; + xs->resid = xs->datalen; + +retry: + /* + * 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 + */ + switch ((*(xs->sc_link->adapter->scsi_cmd)) (xs)) { + case SUCCESSFULLY_QUEUED: + if (xs->bp) + return EJUSTRETURN; + s = splbio(); + while ((xs->flags & ITSDONE) == 0) + tsleep(xs, PRIBIO + 1, "scsi_scsi_cmd", 0); + splx(s); + case COMPLETE: /* Polling command completed ok */ + if (xs->bp) + return EJUSTRETURN; + doit: + SC_DEBUG(xs->sc_link, SDEV_DB3, ("back in cmd()\n")); + if ((error = sc_err1(xs, 0)) != ERESTART) + return error; + goto retry; + + case TRY_AGAIN_LATER: /* adapter resource shortage */ + xs->error = XS_BUSY; + goto doit; + + default: + panic("scsi_execute_xs: invalid return code"); + } + +#ifdef DIAGNOSTIC + panic("scsi_execute_xs: impossible"); +#endif +} + +/* + * 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. + */ +int +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; + int cmdlen; + u_char *data_addr; + int datalen; + int retries; + int timeout; + struct buf *bp; + int flags; +{ + struct scsi_xfer *xs; + int error; + + SC_DEBUG(sc_link, SDEV_DB2, ("scsi_cmd\n")); + +#ifdef DIAGNOSTIC + if (bp != 0 && (flags & SCSI_NOSLEEP) == 0) + panic("scsi_scsi_cmd: buffer without nosleep"); +#endif + + if ((xs = scsi_make_xs(sc_link, scsi_cmd, cmdlen, data_addr, datalen, + retries, timeout, bp, flags)) == NULL) + return ENOMEM; + + if ((error = scsi_execute_xs(xs)) == EJUSTRETURN) + return 0; + + /* + * we have finished with the xfer stuct, free it and + * check if anyone else needs to be started up. + */ + scsi_free_xs(xs, flags); + return error; +} + +int +sc_err1(xs, async) + struct scsi_xfer *xs; + int async; +{ + int error; + + 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 (xs->error) { + case XS_NOERROR: /* nearly always hit this one */ + error = 0; + break; + + case XS_SENSE: + if ((error = scsi_interpret_sense(xs)) == ERESTART) + goto retry; + SC_DEBUG(xs->sc_link, SDEV_DB3, + ("scsi_interpret_sense returned %d\n", error)); + break; + + case XS_BUSY: + if (xs->retries) { + if ((xs->flags & SCSI_POLL) != 0) + delay(1000000); + else if ((xs->flags & SCSI_NOSLEEP) == 0) + tsleep(&lbolt, PRIBIO, "scbusy", 0); + else +#if 0 + timeout(scsi_requeue, xs, hz); +#else + goto lose; +#endif + } + case XS_TIMEOUT: + retry: + if (xs->retries--) { + xs->error = XS_NOERROR; + xs->flags &= ~ITSDONE; + return ERESTART; + } + case XS_DRIVER_STUFFUP: + lose: + error = EIO; + break; + + case XS_SELTIMEOUT: + /* XXX Disable device? */ + error = EIO; + break; + + default: + sc_print_addr(xs->sc_link); + printf("unknown error category from scsi driver\n"); + error = EIO; + break; + } + + scsi_error(xs, error); + return error; +} + +void +scsi_error(xs, error) + struct scsi_xfer *xs; + int error; +{ + struct buf *bp = xs->bp; + + if (bp) { + if (error) { + bp->b_error = error; + bp->b_flags |= B_ERROR; + bp->b_resid = bp->b_bcount; + } else { + bp->b_error = 0; + bp->b_resid = xs->resid; + } + biodone(bp); + } +} + +/* + * 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 + */ +int +scsi_interpret_sense(xs) + struct scsi_xfer *xs; +{ + struct scsi_sense_data *sense; + struct scsi_link *sc_link = xs->sc_link; + u_int8_t key; + u_int32_t info; + int error; + + 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" + }; + + sense = &xs->sense; +#ifdef SCSIDEBUG + if ((sc_link->flags & SDEV_DB1) != 0) { + int count; + 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->extended_segment, + sense->extended_flags & SSD_KEY, + sense->extended_flags & SSD_ILI ? 1 : 0, + sense->extended_flags & SSD_EOM ? 1 : 0, + sense->extended_flags & SSD_FILEMARK ? 1 : 0); + printf("info: %x %x %x %x followed by %d extra bytes\n", + sense->extended_info[0], + sense->extended_info[1], + sense->extended_info[2], + sense->extended_info[3], + sense->extended_extra_len); + printf("extra: "); + for (count = 0; count < sense->extended_extra_len; count++) + printf("%x ", sense->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")); + error = (*sc_link->device->err_handler) (xs); + if (error != -1) + return error; /* error >= 0 better ? */ + } + /* otherwise use the default */ + 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->extended_flags & SSD_KEY; + printf(" DELAYED ERROR, key = 0x%x\n", key); + case 0x70: + if ((sense->error_code & SSD_ERRCODE_VALID) != 0) { + bcopy(sense->extended_info, &info, sizeof info); + info = ntohl(info); + } else + info = 0; + key = sense->extended_flags & SSD_KEY; + + switch (key) { + case 0x0: /* NO SENSE */ + case 0x1: /* RECOVERED ERROR */ + if (xs->resid == xs->datalen) + xs->resid = 0; /* not short read */ + case 0xc: /* EQUAL */ + error = 0; + break; + case 0x2: /* NOT READY */ + if ((sc_link->flags & SDEV_REMOVABLE) != 0) + sc_link->flags &= ~SDEV_MEDIA_LOADED; + if ((xs->flags & SCSI_IGNORE_NOT_READY) != 0) + return 0; + if ((xs->flags & SCSI_SILENT) != 0) + return EIO; + error = EIO; + break; + case 0x5: /* ILLEGAL REQUEST */ + if ((xs->flags & SCSI_IGNORE_ILLEGAL_REQUEST) != 0) + return 0; + error = EINVAL; + break; + case 0x6: /* UNIT ATTENTION */ + if ((sc_link->flags & SDEV_REMOVABLE) != 0) + sc_link->flags &= ~SDEV_MEDIA_LOADED; + if ((xs->flags & SCSI_IGNORE_MEDIA_CHANGE) != 0 || + /* XXX Should reupload any transient state. */ + (sc_link->flags & SDEV_REMOVABLE) == 0) + return ERESTART; + if ((xs->flags & SCSI_SILENT) != 0) + return EIO; + error = EIO; + break; + case 0x7: /* DATA PROTECT */ + error = EACCES; + break; + case 0x8: /* BLANK CHECK */ + error = 0; + break; + case 0xd: /* VOLUME OVERFLOW */ + error = ENOSPC; + break; + default: + error = EIO; + break; + } + + if (key) { + sc_print_addr(sc_link); + printf("%s", error_mes[key - 1]); + if ((sense->error_code & SSD_ERRCODE_VALID) != 0) { + switch (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); + } + } + if (sense->extended_extra_len != 0) { + int n; + printf(", data ="); + for (n = 0; n < sense->extended_extra_len; n++) + printf(" %02x", sense->extended_extra_bytes[n]); + } + printf("\n"); + } + return error; + + /* + * Not code 70, just report it + */ + default: + sc_print_addr(sc_link); + printf("error code %d", + sense->error_code & SSD_ERRCODE); + if ((sense->error_code & SSD_ERRCODE_VALID) != 0) { + printf(" at block no. %d (decimal)", + (sense->XXX_unextended_blockhi << 16) + + (sense->XXX_unextended_blockmed << 8) + + (sense->XXX_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) + u_int32_t val; + u_int8_t *bytes; +{ + + *bytes++ = (val >> 16) & 0xff; + *bytes++ = (val >> 8) & 0xff; + *bytes = val & 0xff; +} + +/* + * The reverse of lto3b + */ +u_int32_t +_3btol(bytes) + u_int8_t *bytes; +{ + u_int32_t rc; + + rc = (*bytes++ << 16); + rc += (*bytes++ << 8); + rc += *bytes; + return (rc); +} + +/* + * Print out the scsi_link structure's address info. + */ +void +sc_print_addr(sc_link) + struct scsi_link *sc_link; +{ + + printf("%s(%s:%d:%d): ", + sc_link->device_softc ? + ((struct device *)sc_link->device_softc)->dv_xname : "probe", + ((struct device *)sc_link->adapter_softc)->dv_xname, + 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(xs) + 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) == 0) { + 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) + u_char *address; + int num; +{ + int x; + + printf("------------------------------"); + for (x = 0; x < num; x++) { + if ((x % 16) == 0) + printf("\n%03d: ", x); + 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 00000000000..bbd15676e98 --- /dev/null +++ b/sys/scsi/scsi_cd.h @@ -0,0 +1,212 @@ +/* $NetBSD: scsi_cd.h,v 1.5 1994/12/28 19:42:58 mycroft Exp $ */ + +/* + * 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 + */ +#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_int8_t opcode; + 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 opcode; + 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 opcode; + 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 opcode; + 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 opcode; + 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 opcode; + 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 opcode; + 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 opcode; + 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 opcode; + 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 opcode; + 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 opcode; + 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 scsi_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 00000000000..5161ce8e166 --- /dev/null +++ b/sys/scsi/scsi_changer.h @@ -0,0 +1,94 @@ +/* $NetBSD: scsi_changer.h,v 1.5 1994/12/28 19:42:59 mycroft Exp $ */ + +/* + * 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 + */ +#ifndef _SCSI_SCSI_CHANGER_H +#define _SCSI_SCSI_CHANGER_H 1 + +/* + * SCSI command format + */ +struct scsi_read_element_status { + u_char opcode; + 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 opcode; + 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 opcode; + 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 00000000000..ad2c3da29be --- /dev/null +++ b/sys/scsi/scsi_debug.h @@ -0,0 +1,42 @@ +/* $NetBSD: scsi_debug.h,v 1.5 1994/12/28 19:43:00 mycroft Exp $ */ + +/* + * Written by Julian Elischer (julian@tfs.com) + */ +#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 DEBUGTARGET -1 /* -1 = disable */ +#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)) { \ + sc_print_addr(sc_link); \ + printf Printstuff; \ + } +#define SC_DEBUGN(sc_link,Level,Printstuff) \ + if ((sc_link)->flags & (Level)) { \ + printf Printstuff; \ + } +#else +#define SC_DEBUG(A,B,C) +#define SC_DEBUGN(A,B,C) +#endif + +#endif /* _SCSI_SCSI_DEBUG_H */ diff --git a/sys/scsi/scsi_disk.h b/sys/scsi/scsi_disk.h new file mode 100644 index 00000000000..1039f58b88a --- /dev/null +++ b/sys/scsi/scsi_disk.h @@ -0,0 +1,208 @@ +/* $NetBSD: scsi_disk.h,v 1.7 1994/12/28 19:43:02 mycroft Exp $ */ + +/* + * 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 + */ + +/* + * SCSI command format + */ + +#ifndef _SCSI_SCSI_DISK_H +#define _SCSI_SCSI_DISK_H 1 + +struct scsi_reassign_blocks { + u_char opcode; + u_char byte2; + u_char unused[3]; + u_char control; +}; + +struct scsi_rw { + u_char opcode; + 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 opcode; + 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 opcode; + 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 opcode; + u_char byte2; + u_char unused[2]; + u_char how; +#define SSS_STOP 0x00 +#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_ioctl.c b/sys/scsi/scsi_ioctl.c new file mode 100644 index 00000000000..3bc77130a63 --- /dev/null +++ b/sys/scsi/scsi_ioctl.c @@ -0,0 +1,361 @@ +/* $NetBSD: scsi_ioctl.c,v 1.19 1995/09/26 19:26:58 thorpej Exp $ */ + +/* + * Copyright (c) 1994 Charles Hannum. All rights reserved. + * + * 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 Charles Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Contributed by HD Associates (hd@world.std.com). + * Copyright (c) 1992, 1993 HD Associates + * + * Berkeley style copyright. + */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/device.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <sys/scsiio.h> + +struct scsi_ioctl { + LIST_ENTRY(scsi_ioctl) si_list; + struct buf si_bp; + struct uio si_uio; + struct iovec si_iov; + scsireq_t si_screq; + struct scsi_link *si_sc_link; +}; + +LIST_HEAD(, scsi_ioctl) si_head; + +struct scsi_ioctl * +si_get() +{ + struct scsi_ioctl *si; + int s; + + si = malloc(sizeof(struct scsi_ioctl), M_TEMP, M_WAITOK); + bzero(si, sizeof(struct scsi_ioctl)); + s = splbio(); + LIST_INSERT_HEAD(&si_head, si, si_list); + splx(s); + return (si); +} + +void +si_free(si) + struct scsi_ioctl *si; +{ + int s; + + s = splbio(); + LIST_REMOVE(si, si_list); + splx(s); + free(si, M_TEMP); +} + +struct scsi_ioctl * +si_find(bp) + struct buf *bp; +{ + struct scsi_ioctl *si; + int s; + + s = splbio(); + for (si = si_head.lh_first; si != 0; si = si->si_list.le_next) + if (bp == &si->si_bp) + break; + splx(s); + return (si); +} + +/* + * 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. + */ +void +scsi_user_done(xs) + struct scsi_xfer *xs; +{ + struct buf *bp; + struct scsi_ioctl *si; + scsireq_t *screq; + struct scsi_link *sc_link; + + 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; + } + si = si_find(bp); + if (!si) { + sc_print_addr(xs->sc_link); + printf("User command with no ioctl\n"); + return; + } + screq = &si->si_screq; + sc_link = si->si_sc_link; + SC_DEBUG(xs->sc_link, SDEV_DB2, ("user-done\n")); + + screq->retsts = 0; + screq->status = xs->status; + switch (xs->error) { + case XS_NOERROR: + SC_DEBUG(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(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(sc_link); + printf("host adapter code inconsistency\n"); + screq->retsts = SCCMD_UNKNOWN; + break; + case XS_TIMEOUT: + SC_DEBUG(sc_link, SDEV_DB3, ("timeout\n")); + screq->retsts = SCCMD_TIMEOUT; + break; + case XS_BUSY: + SC_DEBUG(sc_link, SDEV_DB3, ("busy\n")); + screq->retsts = SCCMD_BUSY; + break; + default: + sc_print_addr(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() */ +} + + +/* 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(bp) + struct buf *bp; +{ + struct scsi_ioctl *si; + scsireq_t *screq; + struct scsi_link *sc_link; + int error; + int flags = 0; + int s; + + si = si_find(bp); + if (!si) { + printf("user_strat: No ioctl\n"); + error = EINVAL; + goto bad; + } + screq = &si->si_screq; + sc_link = si->si_sc_link; + SC_DEBUG(sc_link, SDEV_DB2, ("user_strategy\n")); + + /* + * 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"); + error = EIO; + goto bad; + } + + if (screq->timeout == 0) { + error = EINVAL; + goto bad; + } + + if (screq->cmdlen > sizeof(struct scsi_generic)) { + sc_print_addr(sc_link); + printf("cmdlen too big\n"); + error = EFAULT; + goto bad; + } + + 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; + + error = scsi_scsi_cmd(sc_link, (struct scsi_generic *)screq->cmd, + screq->cmdlen, (u_char *)bp->b_data, screq->datalen, + 0, /* user must do the retries *//* ignored */ + screq->timeout, bp, flags | SCSI_USER | SCSI_NOSLEEP); + + /* because there is a bp, scsi_scsi_cmd will return immediatly */ + if (error) + goto bad; + + SC_DEBUG(sc_link, SDEV_DB3, ("about to sleep\n")); + s = splbio(); + while ((bp->b_flags & B_DONE) == 0) + tsleep(bp, PRIBIO, "scistr", 0); + splx(s); + SC_DEBUG(sc_link, SDEV_DB3, ("back from sleep\n")); + + return; + +bad: + bp->b_flags |= B_ERROR; + bp->b_error = error; + biodone(bp); +} + +/* + * 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 + */ +int +scsi_do_ioctl(sc_link, dev, cmd, addr, flag, p) + struct scsi_link *sc_link; + dev_t dev; + u_long cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + int error; + + SC_DEBUG(sc_link, SDEV_DB2, ("scsi_do_ioctl(0x%lx)\n", cmd)); + + switch(cmd) { + case SCIOCCOMMAND: { + scsireq_t *screq = (scsireq_t *)addr; + struct scsi_ioctl *si; + int len; + + si = si_get(); + si->si_screq = *screq; + si->si_sc_link = sc_link; + len = screq->datalen; + if (len) { + si->si_iov.iov_base = screq->databuf; + si->si_iov.iov_len = len; + si->si_uio.uio_iov = &si->si_iov; + si->si_uio.uio_iovcnt = 1; + si->si_uio.uio_resid = len; + si->si_uio.uio_offset = 0; + si->si_uio.uio_segflg = UIO_USERSPACE; + si->si_uio.uio_rw = + (screq->flags & SCCMD_READ) ? UIO_READ : UIO_WRITE; + si->si_uio.uio_procp = p; + error = physio(scsistrategy, &si->si_bp, dev, + (screq->flags & SCCMD_READ) ? B_READ : B_WRITE, + sc_link->adapter->scsi_minphys, &si->si_uio); + } else { + /* if no data, no need to translate it.. */ + si->si_bp.b_flags = 0; + si->si_bp.b_data = 0; + si->si_bp.b_bcount = 0; + si->si_bp.b_dev = dev; + si->si_bp.b_proc = p; + scsistrategy(&si->si_bp); + error = si->si_bp.b_error; + } + *screq = si->si_screq; + si_free(si); + return error; + } + 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; + return 0; + } + case SCIOCREPROBE: { + struct scsi_addr *sca = (struct scsi_addr *)addr; + + return scsi_probe_busses(sca->scbus, sca->target, sca->lun); + } + case SCIOCRECONFIG: + case SCIOCDECONFIG: + return EINVAL; + 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; + return 0; + } + default: + return ENOTTY; + } + +#ifdef DIAGNOSTIC + panic("scsi_do_ioctl: impossible"); +#endif +} diff --git a/sys/scsi/scsi_message.h b/sys/scsi/scsi_message.h new file mode 100644 index 00000000000..331a2d3ddea --- /dev/null +++ b/sys/scsi/scsi_message.h @@ -0,0 +1,33 @@ +/* Messages (1 byte) */ /* I/T (M)andatory or (O)ptional */ +#define MSG_CMDCOMPLETE 0x00 /* M/M */ +#define MSG_EXTENDED 0x01 /* O/O */ +#define MSG_SAVEDATAPOINTER 0x02 /* O/O */ +#define MSG_RESTOREPOINTERS 0x03 /* O/O */ +#define MSG_DISCONNECT 0x04 /* O/O */ +#define MSG_INITIATOR_DET_ERR 0x05 /* M/M */ +#define MSG_ABORT 0x06 /* O/M */ +#define MSG_MESSAGE_REJECT 0x07 /* M/M */ +#define MSG_NOOP 0x08 /* M/M */ +#define MSG_PARITY_ERROR 0x09 /* M/M */ +#define MSG_LINK_CMD_COMPLETE 0x0a /* O/O */ +#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */ +#define MSG_BUS_DEV_RESET 0x0c /* O/M */ +#define MSG_ABORT_TAG 0x0d /* O/O */ +#define MSG_CLEAR_QUEUE 0x0e /* O/O */ +#define MSG_INIT_RECOVERY 0x0f /* O/O */ +#define MSG_REL_RECOVERY 0x10 /* O/O */ +#define MSG_TERM_IO_PROC 0x11 /* O/O */ + +/* Messages (2 byte) */ +#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */ +#define MSG_HEAD_OF_Q_TAG 0x21 /* O/O */ +#define MSG_ORDERED_Q_TAG 0x22 /* O/O */ +#define MSG_IGN_WIDE_RESIDUE 0x23 /* O/O */ + +/* Identify message */ +#define MSG_IDENTIFY(lun, disc) (((disc) ? 0xc0 : 0x80) | (lun)) +#define MSG_ISIDENTIFY(m) ((m) & 0x80) + +/* Extended messages (opcode) */ +#define MSG_EXT_SDTR 0x01 +#define MSG_EXT_WDTR 0x03 diff --git a/sys/scsi/scsi_tape.h b/sys/scsi/scsi_tape.h new file mode 100644 index 00000000000..4ff8042e75b --- /dev/null +++ b/sys/scsi/scsi_tape.h @@ -0,0 +1,209 @@ +/* $NetBSD: scsi_tape.h,v 1.6 1994/12/28 19:43:08 mycroft Exp $ */ + +/* + * Copyright (c) 1994 Charles Hannum. All rights reserved. + * + * 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 Charles Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Originally 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 + */ + +/* + * SCSI tape interface description + */ + +#ifndef _SCSI_TAPE_H_ +#define _SCSI_TAPE_H_ 1 + +/* + * SCSI command formats + */ + +#define READ 0x08 +#define WRITE 0x0a +struct scsi_rw_tape { + u_char opcode; + u_char byte2; +#define SRW_FIXED 0x01 + u_char len[3]; + u_char control; +}; + +#define SPACE 0x11 +struct scsi_space { + u_char opcode; + u_char byte2; +#define SS_CODE 0x03 +#define SP_BLKS 0x00 +#define SP_FILEMARKS 0x01 +#define SP_SEQ_FILEMARKS 0x02 +#define SP_EOM 0x03 + u_char number[3]; + u_char control; +}; + +#define WRITE_FILEMARKS 0x10 +struct scsi_write_filemarks { + u_char opcode; + u_char byte2; + u_char number[3]; + u_char control; +}; + +#define REWIND 0x01 +struct scsi_rewind { + u_char opcode; + u_char byte2; +#define SR_IMMED 0x01 + u_char unused[3]; + u_char control; +}; + +#define LOAD 0x1b +struct scsi_load { + u_char opcode; + u_char byte2; +#define SL_IMMED 0x01 + u_char unused[2]; + u_char how; +#define LD_UNLOAD 0x00 +#define LD_LOAD 0x01 +#define LD_RETENSION 0x02 + u_char control; +}; + +#define READ_BLOCK_LIMITS 0x05 +struct scsi_block_limits { + u_char opcode; + u_char byte2; + u_char unused[3]; + u_char control; +}; + +struct scsi_block_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 block_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 Reel-to-Reel + 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_TAPE_H_ */ diff --git a/sys/scsi/scsiconf.c b/sys/scsi/scsiconf.c new file mode 100644 index 00000000000..39e5fc060a0 --- /dev/null +++ b/sys/scsi/scsiconf.c @@ -0,0 +1,576 @@ +/* $NetBSD: scsiconf.c,v 1.43 1995/10/13 20:01:03 gwr Exp $ */ + +/* + * Copyright (c) 1994 Charles Hannum. All rights reserved. + * + * 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 Charles Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Originally 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 + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/device.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#if 0 +#if NCALS > 0 + { T_PROCESSOR, T_FIXED, 1, + 0, 0, 0 }, +#endif /* NCALS */ +#if NBLL > 0 + { T_PROCESSOR, T_FIXED, 1, + "AEG ", "READER ", "V1.0" }, +#endif /* NBLL */ +#if NKIL > 0 + { T_SCANNER, T_FIXED, 0, + "KODAK ", "IL Scanner 900 ", 0 }, +#endif /* NKIL */ +#endif + +/* + * Declarations + */ +void scsi_probedev __P((struct scsibus_softc *, int, int)); +int scsi_probe_bus __P((int bus, int target, int lun)); + +struct scsi_device probe_switch = { + NULL, + NULL, + NULL, + NULL, +}; + +int scsibusmatch __P((struct device *, void *, void *)); +void scsibusattach __P((struct device *, struct device *, void *)); + +struct cfdriver scsibuscd = { + NULL, "scsibus", scsibusmatch, scsibusattach, DV_DULL, + sizeof(struct scsibus_softc) +}; + +int +scsibusmatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + + return 1; +} + +/* + * The routine called by the adapter boards to get all their + * devices configured in. + */ +void +scsibusattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct scsibus_softc *sb = (struct scsibus_softc *)self; + struct scsi_link *sc_link_proto = aux; + + sc_link_proto->scsibus = sb->sc_dev.dv_unit; + sb->adapter_link = sc_link_proto; + printf("\n"); + +#if defined(SCSI_DELAY) && SCSI_DELAY > 2 + printf("%s: waiting for scsi devices to settle\n", + sb->sc_dev.dv_xname); +#else /* SCSI_DELAY > 2 */ +#undef SCSI_DELAY +#define SCSI_DELAY 2 +#endif /* SCSI_DELAY */ + delay(1000000 * SCSI_DELAY); + + scsi_probe_bus(sb->sc_dev.dv_unit, -1, -1); +} + +int +scsibussubmatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct cfdata *cf = match; + struct scsibus_attach_args *sa = aux; + struct scsi_link *sc_link = sa->sa_sc_link; + + if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != sc_link->target) + return 0; + if (cf->cf_loc[1] != -1 && cf->cf_loc[1] != sc_link->lun) + return 0; + return ((*cf->cf_driver->cd_match)(parent, match, aux)); +} + +/* + * Probe the requested scsi bus. It must be already set up. + * -1 requests all set up scsi busses. + * target and lun optionally narrow the search if not -1 + */ +int +scsi_probe_busses(bus, target, lun) + int bus, target, lun; +{ + + if (bus == -1) { + for (bus = 0; bus < scsibuscd.cd_ndevs; bus++) + if (scsibuscd.cd_devs[bus]) + scsi_probe_bus(bus, target, lun); + return 0; + } else { + return scsi_probe_bus(bus, target, lun); + } +} + +/* + * Probe the requested scsi bus. It must be already set up. + * target and lun optionally narrow the search if not -1 + */ +int +scsi_probe_bus(bus, target, lun) + int bus, target, lun; +{ + struct scsibus_softc *scsi; + int maxtarget, mintarget, maxlun, minlun; + u_int8_t scsi_addr; + + if (bus < 0 || bus >= scsibuscd.cd_ndevs) + return ENXIO; + scsi = scsibuscd.cd_devs[bus]; + if (!scsi) + return ENXIO; + + scsi_addr = scsi->adapter_link->adapter_target; + + if (target == -1) { + maxtarget = 7; + mintarget = 0; + } else { + if (target < 0 || target > 7) + return EINVAL; + maxtarget = mintarget = target; + } + + if (lun == -1) { + maxlun = 7; + minlun = 0; + } else { + if (lun < 0 || lun > 7) + return EINVAL; + maxlun = minlun = lun; + } + + for (target = mintarget; target <= maxtarget; target++) { + if (target == scsi_addr) + continue; + for (lun = minlun; lun <= maxlun; lun++) { + /* + * See if there's a device present, and configure it. + */ + scsi_probedev(scsi, target, lun); + if ((scsi->moreluns & (1 << target)) == 0) + break; + /* otherwise something says we should look further */ + } + } + return 0; +} + +void +scsi_strvis(dst, src, len) + u_char *dst, *src; + int len; +{ + + /* Trim leading and trailing blanks. */ + while (len > 0 && src[0] == ' ') + ++src, --len; + while (len > 0 && src[len-1] == ' ') + --len; + + while (len > 0) { + if (*src < 0x20 || *src >= 0x80) { + /* non-printable characters */ + *dst++ = '\\'; + *dst++ = ((*src & 0300) >> 6) + '0'; + *dst++ = ((*src & 0070) >> 3) + '0'; + *dst++ = ((*src & 0007) >> 0) + '0'; + } else if (*src == '\\') { + /* quote characters */ + *dst++ = '\\'; + *dst++ = '\\'; + } else { + /* normal characters */ + *dst++ = *src; + } + ++src, --len; + } + + *dst++ = 0; +} + +struct scsi_quirk_inquiry_pattern { + struct scsi_inquiry_pattern pattern; + u_int8_t quirks; +}; + +struct scsi_quirk_inquiry_pattern scsi_quirk_patterns[] = { + {T_CDROM, T_REMOV, + "CHINON ", "CD-ROM CDS-431 ", "", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "CHINON ", "CD-ROM CDS-535 ", "", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "DENON ", "DRD-25X ", "V", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "IMS ", "CDD521/10 ", "2.06", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "MEDIAVIS", "CDR-H93MV ", "1.31", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "NEC ", "CD-ROM DRIVE:55 ", "", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "NEC ", "CD-ROM DRIVE:83 ", "", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "NEC ", "CD-ROM DRIVE:84 ", "", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "NEC ", "CD-ROM DRIVE:841", "", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "SONY ", "CD-ROM CDU-541 ", "", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "SONY ", "CD-ROM CDU-55S ", "", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "SONY ", "CD-ROM CDU-8003A", "", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "SONY ", "CD-ROM CDU-8012 ", "", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "TEAC ", "CD-ROM ", "1.06", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "TEXEL ", "CD-ROM ", "1.06", SDEV_NOLUNS}, + {T_CDROM, T_REMOV, + "TEXEL ", "CD-ROM DM-XX24 K", "1.10", SDEV_NOLUNS}, + + {T_DIRECT, T_FIXED, + "EMULEX ", "MD21/S2 ESDI", "A00", SDEV_FORCELUNS}, + {T_DIRECT, T_FIXED, + "MAXTOR ", "XT-3280 ", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "MAXTOR ", "XT-4380S ", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "MAXTOR ", "MXT-1240S ", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "MAXTOR ", "XT-4170S ", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "MAXTOR ", "XT-8760S ", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "MAXTOR ", "LXT-213S ", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "MAXTOR ", "LXT-213S SUN0207", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "MST ", "SnapLink ", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "QUANTUM ", "LPS525S ", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "QUANTUM ", "P105S 910-10-94x", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "QUANTUM ", "PD1225S ", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "RODIME ", "RO3000S ", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "SEAGATE ", "ST157N ", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "SEAGATE ", "ST296 ", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "SEAGATE ", "ST296N ", "", SDEV_NOLUNS}, + {T_DIRECT, T_FIXED, + "TOSHIBA ", "MK538FB ", "6027", SDEV_NOLUNS}, + + /* XXX: QIC-36 tape behind Emulex adapter. Very broken. */ + {T_SEQUENTIAL, T_REMOV, + " ", " ", " ", SDEV_NOLUNS}, + {T_SEQUENTIAL, T_REMOV, + "EXABYTE ", "EXB-8200 ", "", SDEV_NOLUNS}, + {T_SEQUENTIAL, T_REMOV, + "SONY ", "SDT-2000 ", "2.09", SDEV_NOLUNS}, + {T_SEQUENTIAL, T_REMOV, + "SONY ", "SDT-5000 ", "3.17", SDEV_NOSYNCWIDE}, + {T_SEQUENTIAL, T_REMOV, + "TANDBERG", " TDC 3600 ", "", SDEV_NOLUNS}, + {T_SEQUENTIAL, T_REMOV, + "WANGTEK ", "5099ES SCSI", "", SDEV_NOLUNS}, + {T_SEQUENTIAL, T_REMOV, + "WANGTEK ", "5150ES SCSI", "", SDEV_NOLUNS}, + {T_SEQUENTIAL, T_REMOV, + "WangDAT ", "Model 1300 ", "02.4", SDEV_NOSYNCWIDE}, + {T_SEQUENTIAL, T_REMOV, + "WangDAT ", "Model 2600 ", "01.7", SDEV_NOSYNCWIDE}, + {T_SEQUENTIAL, T_REMOV, + "WangDAT ", "Model 3200 ", "02.2", SDEV_NOSYNCWIDE}, +}; + +/* + * given a target and lu, ask the device what + * it is, and find the correct driver table + * entry. + */ +void +scsi_probedev(scsi, target, lun) + struct scsibus_softc *scsi; + int target, lun; +{ + struct scsi_link *sc_link; + static struct scsi_inquiry_data inqbuf; + struct scsi_quirk_inquiry_pattern *finger; + int priority; + u_int8_t type; + boolean removable; + char *dtype, *qtype; + char vendor[33], product[65], revision[17]; + struct scsibus_attach_args sa; + struct cfdata *cf; + + /* Skip this slot if it is already attached. */ + if (scsi->sc_link[target][lun]) + return; + + sc_link = malloc(sizeof(*sc_link), M_DEVBUF, M_NOWAIT); + *sc_link = *scsi->adapter_link; + sc_link->target = target; + sc_link->lun = lun; + sc_link->device = &probe_switch; + + /* + * Ask the device what it is + */ +#ifdef SCSIDEBUG + if (target == DEBUGTARGET && lun == DEBUGLUN) + sc_link->flags |= DEBUGLEVEL; +#endif /* SCSIDEBUG */ + + (void) scsi_test_unit_ready(sc_link, + SCSI_AUTOCONF | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY | SCSI_IGNORE_MEDIA_CHANGE); + +#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_AUTOCONF | SCSI_SILENT); +#endif /* SCSI_2_DEF */ + + /* Now go ask the device all about itself. */ + bzero(&inqbuf, sizeof(inqbuf)); + if (scsi_inquire(sc_link, &inqbuf, SCSI_AUTOCONF) != 0) + goto bad; + + { + int len = inqbuf.additional_length; + while (len < 3) + inqbuf.unused[len++] = '\0'; + while (len < 3 + 28) + inqbuf.unused[len++] = ' '; + } + + finger = (struct scsi_quirk_inquiry_pattern *)scsi_inqmatch(&inqbuf, + (caddr_t)scsi_quirk_patterns, + sizeof(scsi_quirk_patterns)/sizeof(scsi_quirk_patterns[0]), + sizeof(scsi_quirk_patterns[0]), &priority); + if (priority != 0) + sc_link->quirks |= finger->quirks; + if ((inqbuf.version & SID_ANSII) == 0 && + (sc_link->quirks & SDEV_FORCELUNS) == 0) + sc_link->quirks |= SDEV_NOLUNS; + + if ((sc_link->quirks & SDEV_NOLUNS) == 0) + scsi->moreluns |= (1 << target); + + /* + * note what BASIC type of device it is + */ + type = inqbuf.device & SID_TYPE; + removable = inqbuf.dev_qual2 & SID_REMOVABLE ? 1 : 0; + + if (removable) + sc_link->flags |= SDEV_REMOVABLE; + + /* + * Any device qualifier that has the top bit set (qualifier&4 != 0) + * is vendor specific and won't match in this switch. + */ + dtype = 0; + switch (inqbuf.device & SID_QUAL) { + case SID_QUAL_LU_OK: + qtype = ""; + break; + + case SID_QUAL_LU_OFFLINE: + qtype = " offline"; + break; + + case SID_QUAL_RSVD: + case SID_QUAL_BAD_LU: + goto bad; + + default: + qtype = ""; + dtype = "vendor-unique"; + break; + } + if (dtype == 0) { + switch (type) { + case T_DIRECT: + dtype = "direct"; + break; + case T_SEQUENTIAL: + dtype = "sequential"; + break; + case T_PRINTER: + dtype = "printer"; + break; + case T_PROCESSOR: + dtype = "processor"; + break; + case T_CDROM: + dtype = "cdrom"; + 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: + goto bad; + default: + dtype = "unknown"; + break; + } + } + + scsi_strvis(vendor, inqbuf.vendor, 8); + scsi_strvis(product, inqbuf.product, 16); + scsi_strvis(revision, inqbuf.revision, 4); + + printf("%s targ %d lun %d: <%s, %s, %s> SCSI%d %d/%s %s%s\n", + ((struct device *)sc_link->adapter_softc)->dv_xname, + target, lun, vendor, product, revision, + inqbuf.version & SID_ANSII, type, dtype, + removable ? "removable" : "fixed", qtype); + + sa.sa_sc_link = sc_link; + sa.sa_inqbuf = &inqbuf; + + if ((cf = config_search(scsibussubmatch, (struct device *)scsi, &sa)) != 0) { + scsi->sc_link[target][lun] = sc_link; + config_attach((struct device *)scsi, cf, &sa, NULL); + } else + goto bad; + + return; + +bad: + free(sc_link, M_DEVBUF); + return; +} + +/* + * Return a priority based on how much of the inquiry data matches + * the patterns for the particular driver. + */ +caddr_t +scsi_inqmatch(inqbuf, base, nmatches, matchsize, bestpriority) + struct scsi_inquiry_data *inqbuf; + caddr_t base; + int nmatches, matchsize; + int *bestpriority; +{ + u_int8_t type; + boolean removable; + caddr_t bestmatch; + + /* Include the qualifier to catch vendor-unique types. */ + type = inqbuf->device; + removable = inqbuf->dev_qual2 & SID_REMOVABLE ? T_REMOV : T_FIXED; + + for (*bestpriority = 0, bestmatch = 0; nmatches--; base += matchsize) { + struct scsi_inquiry_pattern *match = (void *)base; + int priority, len; + + if (type != match->type) + continue; + if (removable != match->removable) + continue; + priority = 2; + len = strlen(match->vendor); + if (bcmp(inqbuf->vendor, match->vendor, len)) + continue; + priority += len; + len = strlen(match->product); + if (bcmp(inqbuf->product, match->product, len)) + continue; + priority += len; + len = strlen(match->revision); + if (bcmp(inqbuf->revision, match->revision, len)) + continue; + priority += len; + +#if 0 + printf("scsi_inqmatch: %d/%d/%d <%s, %s, %s>\n", + priority, match->type, match->removable, + match->vendor, match->product, match->revision); +#endif + if (priority > *bestpriority) { + *bestpriority = priority; + bestmatch = base; + } + } + + return (bestmatch); +} diff --git a/sys/scsi/scsiconf.h b/sys/scsi/scsiconf.h new file mode 100644 index 00000000000..b7258e3027c --- /dev/null +++ b/sys/scsi/scsiconf.h @@ -0,0 +1,281 @@ +/* $NetBSD: scsiconf.h,v 1.25 1995/08/12 20:31:44 mycroft Exp $ */ + +/* + * Copyright (c) 1993, 1994, 1995 Charles Hannum. All rights reserved. + * + * 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 Charles Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Originally 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 + */ + +#ifndef SCSI_SCSICONF_H +#define SCSI_SCSICONF_H 1 + +typedef int boolean; + +#include <sys/queue.h> +#include <machine/cpu.h> +#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 { + int (*scsi_cmd)(); + void (*scsi_minphys) __P((struct buf *)); + int (*open_target_lu)(); + int (*close_target_lu)(); +}; + +/* + * return values for scsi_cmd() + */ +#define SUCCESSFULLY_QUEUED 0 +#define TRY_AGAIN_LATER 1 +#define COMPLETE 2 +#define ESCAPE_NOT_SUPPORTED 3 + +/* + * 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 { + int (*err_handler)(); /* returns -1 to say err processing done */ + void (*start)(); + int (*async)(); + int (*done)(); /* returns -1 to say done processing done */ +}; + +/* + * 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 { + u_int8_t scsibus; /* the Nth scsibus */ + u_int8_t target; /* targ of this dev */ + u_int8_t lun; /* lun of this dev */ + u_int8_t adapter_target; /* what are we on the scsi bus */ + u_int8_t openings; /* available operations */ + u_int8_t active; /* operations in progress */ + u_int8_t flags; /* flags that all devices have */ +#define SDEV_REMOVABLE 0x01 /* media is removable */ +#define SDEV_MEDIA_LOADED 0x02 /* device figures are still valid */ +#define SDEV_WAITING 0x04 /* a process is waiting for this */ +#define SDEV_OPEN 0x08 /* at least 1 open session */ +#define SDEV_DBX 0xf0 /* debuging flags (scsi_debug.h) */ + u_int8_t quirks; /* per-device oddities */ +#define SDEV_AUTOSAVE 0x01 /* do implicit SAVEDATAPOINTER on disconnect */ +#define SDEV_NOSYNCWIDE 0x02 /* does not grok SDTR or WDTR */ +#define SDEV_NOLUNS 0x04 /* does not grok LUNs */ +#define SDEV_FORCELUNS 0x08 /* prehistoric drive/ctlr groks LUNs */ + struct scsi_device *device; /* device entry points etc. */ + void *device_softc; /* needed for call to foo_start */ + struct scsi_adapter *adapter; /* adapter entry points etc. */ + void *adapter_softc; /* needed for call to foo_scsi_cmd */ +}; + +/* + * This describes matching information for scsi_inqmatch(). The more things + * match, the higher the configuration priority. + */ +struct scsi_inquiry_pattern { + u_int8_t type; + boolean removable; + char *vendor; + char *product; + char *revision; +}; + +/* + * 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_softc { + struct device sc_dev; + struct scsi_link *adapter_link; /* prototype supplied by adapter */ + struct scsi_link *sc_link[8][8]; + u_int8_t moreluns; +}; + +/* + * This is used to pass information from the high-level configuration code + * to the device-specific drivers. + */ +struct scsibus_attach_args { + struct scsi_link *sa_sc_link; + struct scsi_inquiry_data *sa_inqbuf; +}; + +/* + * 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 { + LIST_ENTRY(scsi_xfer) free_list; + int flags; + struct scsi_link *sc_link; /* all about our device and adapter */ + int retries; /* the number of times to retry */ + int timeout; /* in milliseconds */ + struct scsi_generic *cmd; /* The scsi command to execute */ + int cmdlen; /* how long it is */ + u_char *data; /* dma address OR a uio address */ + int datalen; /* data len (blank if uio) */ + int resid; /* how much buffer was not touched */ + int error; /* an error value */ + struct buf *bp; /* If we need to associate with a buf */ + struct scsi_sense_data sense; /* 32 bytes*/ + /* + * Believe it or not, Some targets fall on the ground with + * anything but a certain sense length. + */ + int req_sense_length; /* Explicit request sense length */ + u_int8_t status; /* SCSI status */ + struct scsi_generic cmdstore; /* stash the command in here */ +}; + +/* + * Per-request Flag values + */ +#define SCSI_NOSLEEP 0x0001 /* don't sleep */ +#define SCSI_POLL 0x0002 /* poll for completion */ +#define SCSI_AUTOCONF 0x0003 /* shorthand for SCSI_POLL | SCSI_NOSLEEP */ +#define SCSI_USER 0x0004 /* Is a user cmd, call scsi_user_done */ +#define ITSDONE 0x0008 /* the transfer is as done as it gets */ +#define INUSE 0x0010 /* The scsi_xfer block is in use */ +#define SCSI_SILENT 0x0020 /* don't announce NOT READY or MEDIA CHANGE */ +#define SCSI_IGNORE_NOT_READY 0x0040 /* ignore NOT READY */ +#define SCSI_IGNORE_MEDIA_CHANGE 0x0080 /* ignore MEDIA CHANGE */ +#define SCSI_IGNORE_ILLEGAL_REQUEST 0x0100 /* ignore ILLEGAL REQUEST */ +#define SCSI_RESET 0x0200 /* Reset the device in question */ +#define SCSI_DATA_UIO 0x0400 /* The data address refers to a UIO */ +#define SCSI_DATA_IN 0x0800 /* expect data to come INTO memory */ +#define SCSI_DATA_OUT 0x1000 /* expect data to flow OUT of memory */ +#define SCSI_TARGET 0x2000 /* This defines a TARGET mode op. */ +#define SCSI_ESCAPE 0x4000 /* 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 0 /* there is no error, (sense is invalid) */ +#define XS_SENSE 1 /* Check the returned sense for the error */ +#define XS_DRIVER_STUFFUP 2 /* Driver failed to perform operation */ +#define XS_SELTIMEOUT 3 /* The device timed out.. turned off? */ +#define XS_TIMEOUT 4 /* The Timeout reported was caught by SW */ +#define XS_BUSY 5 /* The device busy, try again later? */ + +caddr_t scsi_inqmatch __P((struct scsi_inquiry_data *, caddr_t, int, int, int *)); + +struct scsi_xfer *scsi_get_xs __P((struct scsi_link *, int)); +void scsi_free_xs __P((struct scsi_xfer *, int)); +int scsi_execute_xs __P((struct scsi_xfer *)); +u_long scsi_size __P((struct scsi_link *, int)); +int scsi_test_unit_ready __P((struct scsi_link *, int)); +int scsi_change_def __P((struct scsi_link *, int)); +int scsi_inquire __P((struct scsi_link *, struct scsi_inquiry_data *, int)); +int scsi_prevent __P((struct scsi_link *, int, int)); +int scsi_start __P((struct scsi_link *, int, int)); +void scsi_done __P((struct scsi_xfer *)); +int scsi_scsi_cmd __P((struct scsi_link *, struct scsi_generic *, + int cmdlen, u_char *data_addr, + int datalen, int retries, + int timeout, struct buf *bp, + int flags)); +int scsi_do_ioctl __P((struct scsi_link *, dev_t, u_long, caddr_t, int, struct proc *)); +void sc_print_addr __P((struct scsi_link *)); + +void show_scsi_xs __P((struct scsi_xfer *)); +void show_scsi_cmd __P((struct scsi_xfer *)); +void show_mem __P((u_char *, int)); + +void lto3b __P((u_int32_t val, u_int8_t *bytes)); +u_int32_t _3btol __P((u_int8_t *bytes)); + +#endif /* SCSI_SCSICONF_H */ diff --git a/sys/scsi/sd.c b/sys/scsi/sd.c new file mode 100644 index 00000000000..2d8ef52df38 --- /dev/null +++ b/sys/scsi/sd.c @@ -0,0 +1,996 @@ +/* $NetBSD: sd.c,v 1.80 1995/10/10 02:53:01 mycroft Exp $ */ + +/* + * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved. + * + * 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 Charles M. Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Originally 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 + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.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/device.h> +#include <sys/disklabel.h> +#include <sys/disk.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_disk.h> +#include <scsi/scsiconf.h> + +#define SDOUTSTANDING 2 +#define SDRETRIES 4 + +#define SDUNIT(dev) DISKUNIT(dev) +#define SDPART(dev) DISKPART(dev) +#define MAKESDDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part) + +#define SDLABELDEV(dev) (MAKESDDEV(major(dev), SDUNIT(dev), RAW_PART)) + +struct sd_softc { + struct device sc_dev; + struct dkdevice sc_dk; + + int flags; +#define SDF_LOCKED 0x01 +#define SDF_WANTED 0x02 +#define SDF_WLABEL 0x04 /* label is writable */ +#define SDF_LABELLING 0x08 /* writing label */ + struct scsi_link *sc_link; /* contains our targ, lun, etc. */ + struct disk_parms { + u_char heads; /* number of heads */ + u_short cyls; /* number of cylinders */ + u_char sectors; /* number of sectors/track */ + int blksize; /* number of bytes/sector */ + u_long disksize; /* total number sectors */ + } params; + struct buf buf_queue; +}; + +int sdmatch __P((struct device *, void *, void *)); +void sdattach __P((struct device *, struct device *, void *)); + +struct cfdriver sdcd = { + NULL, "sd", sdmatch, sdattach, DV_DISK, sizeof(struct sd_softc) +}; + +void sdgetdisklabel __P((struct sd_softc *)); +int sd_get_parms __P((struct sd_softc *, int)); +void sdstrategy __P((struct buf *)); +void sdstart __P((struct sd_softc *)); + +struct dkdriver sddkdriver = { sdstrategy }; + +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 */ +}; + +struct scsi_inquiry_pattern sd_patterns[] = { + {T_DIRECT, T_FIXED, + "", "", ""}, + {T_DIRECT, T_REMOV, + "", "", ""}, + {T_OPTICAL, T_FIXED, + "", "", ""}, + {T_OPTICAL, T_REMOV, + "", "", ""}, +}; + +int +sdmatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct cfdata *cf = match; + struct scsibus_attach_args *sa = aux; + int priority; + + (void)scsi_inqmatch(sa->sa_inqbuf, + (caddr_t)sd_patterns, sizeof(sd_patterns)/sizeof(sd_patterns[0]), + sizeof(sd_patterns[0]), &priority); + return (priority); +} + +/* + * The routine called by the low level scsi routine when it discovers + * a device suitable for this driver. + */ +void +sdattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct sd_softc *sd = (void *)self; + struct disk_parms *dp = &sd->params; + struct scsibus_attach_args *sa = aux; + struct scsi_link *sc_link = sa->sa_sc_link; + + SC_DEBUG(sc_link, SDEV_DB2, ("sdattach: ")); + + /* + * Store information needed to contact our base driver + */ + sd->sc_link = sc_link; + sc_link->device = &sd_switch; + sc_link->device_softc = sd; + if (sc_link->openings > SDOUTSTANDING) + sc_link->openings = SDOUTSTANDING; + + sd->sc_dk.dk_driver = &sddkdriver; +#if !defined(i386) || defined(NEWCONFIG) + dk_establish(&sd->sc_dk, &sd->sc_dev); +#endif + + /* + * Use the subdriver to request information regarding + * the drive. We cannot use interrupts yet, so the + * request must specify this. + */ + if (scsi_start(sd->sc_link, SSS_START, + SCSI_AUTOCONF | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT) || + sd_get_parms(sd, SCSI_AUTOCONF) != 0) + printf(": drive offline\n"); + else + printf(": %dMB, %d cyl, %d head, %d sec, %d bytes/sec\n", + dp->disksize / (1048576 / dp->blksize), dp->cyls, + dp->heads, dp->sectors, dp->blksize); +} + +/* + * Wait interruptibly for an exclusive lock. + * + * XXX + * Several drivers do this; it should be abstracted and made MP-safe. + */ +int +sdlock(sd) + struct sd_softc *sd; +{ + int error; + + while ((sd->flags & SDF_LOCKED) != 0) { + sd->flags |= SDF_WANTED; + if ((error = tsleep(sd, PRIBIO | PCATCH, "sdlck", 0)) != 0) + return error; + } + sd->flags |= SDF_LOCKED; + return 0; +} + +/* + * Unlock and wake up any waiters. + */ +void +sdunlock(sd) + struct sd_softc *sd; +{ + + sd->flags &= ~SDF_LOCKED; + if ((sd->flags & SDF_WANTED) != 0) { + sd->flags &= ~SDF_WANTED; + wakeup(sd); + } +} + +/* + * open the device. Make sure the partition info is a up-to-date as can be. + */ +int +sdopen(dev, flag, fmt) + dev_t dev; + int flag, fmt; +{ + struct sd_softc *sd; + struct scsi_link *sc_link; + int unit, part; + int error; + + unit = SDUNIT(dev); + if (unit >= sdcd.cd_ndevs) + return ENXIO; + sd = sdcd.cd_devs[unit]; + if (!sd) + 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, + sdcd.cd_ndevs, part)); + + if (error = sdlock(sd)) + return error; + + if (sd->sc_dk.dk_openmask != 0) { + /* + * If any partition is open, but the disk has been invalidated, + * disallow further opens. + */ + if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) { + error = EIO; + goto bad3; + } + } else { + /* Check that it is still responding and ok. */ + if (error = scsi_test_unit_ready(sc_link, + SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_IGNORE_NOT_READY)) + goto bad3; + + /* Start the pack spinning if necessary. */ + if (error = scsi_start(sc_link, SSS_START, + SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT)) + goto bad3; + + sc_link->flags |= SDEV_OPEN; + + /* Lock the pack in. */ + if (error = scsi_prevent(sc_link, PR_PREVENT, + SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE)) + goto bad; + + if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) { + sc_link->flags |= SDEV_MEDIA_LOADED; + + /* Load the physical device parameters. */ + if (sd_get_parms(sd, 0) != 0) { + error = ENXIO; + goto bad2; + } + SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded ")); + + /* Load the partition info if not already loaded. */ + sdgetdisklabel(sd); + SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel loaded ")); + } + } + + part = SDPART(dev); + + /* Check that the partition exists. */ + if (part != RAW_PART && + (part >= sd->sc_dk.dk_label.d_npartitions || + sd->sc_dk.dk_label.d_partitions[part].p_fstype == FS_UNUSED)) { + error = ENXIO; + goto bad; + } + + /* Insure only one open at a time. */ + switch (fmt) { + case S_IFCHR: + sd->sc_dk.dk_copenmask |= (1 << part); + break; + case S_IFBLK: + sd->sc_dk.dk_bopenmask |= (1 << part); + break; + } + sd->sc_dk.dk_openmask = sd->sc_dk.dk_copenmask | sd->sc_dk.dk_bopenmask; + + SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n")); + sdunlock(sd); + return 0; + +bad2: + sc_link->flags &= ~SDEV_MEDIA_LOADED; + +bad: + if (sd->sc_dk.dk_openmask == 0) { + scsi_prevent(sc_link, PR_ALLOW, + SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE); + sc_link->flags &= ~SDEV_OPEN; + } + +bad3: + sdunlock(sd); + return error; +} + +/* + * close the device.. only called if we are the LAST occurence of an open + * device. Convenient now but usually a pain. + */ +int +sdclose(dev, flag, fmt) + dev_t dev; + int flag, fmt; +{ + struct sd_softc *sd = sdcd.cd_devs[SDUNIT(dev)]; + int part = SDPART(dev); + int error; + + if (error = sdlock(sd)) + return error; + + switch (fmt) { + case S_IFCHR: + sd->sc_dk.dk_copenmask &= ~(1 << part); + break; + case S_IFBLK: + sd->sc_dk.dk_bopenmask &= ~(1 << part); + break; + } + sd->sc_dk.dk_openmask = sd->sc_dk.dk_copenmask | sd->sc_dk.dk_bopenmask; + + if (sd->sc_dk.dk_openmask == 0) { + /* XXXX Must wait for I/O to complete! */ + + scsi_prevent(sd->sc_link, PR_ALLOW, + SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY); + sd->sc_link->flags &= ~SDEV_OPEN; + } + + sdunlock(sd); + return 0; +} + +/* + * 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 sd_softc *sd = sdcd.cd_devs[SDUNIT(bp->b_dev)]; + int s; + + 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)); + /* + * The transfer must be a whole number of blocks. + */ + if ((bp->b_bcount % sd->sc_dk.dk_label.d_secsize) != 0) { + bp->b_error = EINVAL; + goto bad; + } + /* + * If the device has been made invalid, error out + */ + if ((sd->sc_link->flags & SDEV_MEDIA_LOADED) == 0) { + bp->b_error = EIO; + goto bad; + } + /* + * If it's a null transfer, return immediatly + */ + if (bp->b_bcount == 0) + goto done; + + /* + * Do bounds checking, adjust transfer. if error, process. + * If end of partition, just return. + */ + if (SDPART(bp->b_dev) != RAW_PART && + bounds_check_with_label(bp, &sd->sc_dk.dk_label, + (sd->flags & (SDF_WLABEL|SDF_LABELLING)) != 0) <= 0) + goto done; + + s = splbio(); + + /* + * Place it in the queue of disk activities for this disk + */ + disksort(&sd->buf_queue, bp); + + /* + * Tell the device to get going on the transfer if it's + * not doing anything, otherwise just wait for completion + */ + sdstart(sd); + + splx(s); + 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); +} + +/* + * 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 splbio from sdstrategy and scsi_done + */ +void +sdstart(sd) + register struct sd_softc *sd; +{ + register struct scsi_link *sc_link = sd->sc_link; + struct buf *bp = 0; + struct buf *dp; + struct scsi_rw_big cmd; + int blkno, nblks; + struct partition *p; + + SC_DEBUG(sc_link, SDEV_DB2, ("sdstart ")); + /* + * Check if the device has room for another command + */ + while (sc_link->openings > 0) { + /* + * 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) { + sc_link->flags &= ~SDEV_WAITING; + wakeup((caddr_t)sc_link); + 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-opened + */ + if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) { + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + biodone(bp); + continue; + } + + /* + * 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. + */ + blkno = + bp->b_blkno / (sd->sc_dk.dk_label.d_secsize / DEV_BSIZE); + if (SDPART(bp->b_dev) != RAW_PART) { + p = &sd->sc_dk.dk_label.d_partitions[SDPART(bp->b_dev)]; + blkno += p->p_offset; + } + nblks = howmany(bp->b_bcount, sd->sc_dk.dk_label.d_secsize); + + /* + * Fill out the scsi command + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = (bp->b_flags & B_READ) ? READ_BIG : WRITE_BIG; + cmd.addr_3 = (blkno >> 24) & 0xff; + cmd.addr_2 = (blkno >> 16) & 0xff; + cmd.addr_1 = (blkno >> 8) & 0xff; + cmd.addr_0 = blkno & 0xff; + cmd.length2 = (nblks >> 8) & 0xff; + cmd.length1 = nblks & 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_data, bp->b_bcount, + SDRETRIES, 10000, bp, SCSI_NOSLEEP | + ((bp->b_flags & B_READ) ? SCSI_DATA_IN : SCSI_DATA_OUT))) + printf("%s: not queued", sd->sc_dev.dv_xname); + } +} + +int +sdread(dev, uio) + dev_t dev; + struct uio *uio; +{ + struct sd_softc *sd = sdcd.cd_devs[SDUNIT(dev)]; + + return (physio(sdstrategy, NULL, dev, B_READ, + sd->sc_link->adapter->scsi_minphys, uio)); +} + +int +sdwrite(dev, uio) + dev_t dev; + struct uio *uio; +{ + struct sd_softc *sd = sdcd.cd_devs[SDUNIT(dev)]; + + return (physio(sdstrategy, NULL, dev, B_WRITE, + sd->sc_link->adapter->scsi_minphys, uio)); +} + +/* + * Perform special action on behalf of the user + * Knows about the internals of this device + */ +int +sdioctl(dev, cmd, addr, flag, p) + dev_t dev; + u_long cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + struct sd_softc *sd = sdcd.cd_devs[SDUNIT(dev)]; + int error; + + SC_DEBUG(sd->sc_link, SDEV_DB2, ("sdioctl 0x%lx ", cmd)); + + /* + * If the device is not valid.. abandon ship + */ + if ((sd->sc_link->flags & SDEV_MEDIA_LOADED) == 0) + return EIO; + + switch (cmd) { + case DIOCGDINFO: + *(struct disklabel *)addr = sd->sc_dk.dk_label; + return 0; + + case DIOCGPART: + ((struct partinfo *)addr)->disklab = &sd->sc_dk.dk_label; + ((struct partinfo *)addr)->part = + &sd->sc_dk.dk_label.d_partitions[SDPART(dev)]; + return 0; + + case DIOCWDINFO: + case DIOCSDINFO: + if ((flag & FWRITE) == 0) + return EBADF; + + if (error = sdlock(sd)) + return error; + sd->flags |= SDF_LABELLING; + + error = setdisklabel(&sd->sc_dk.dk_label, + (struct disklabel *)addr, /*sd->sc_dk.dk_openmask : */0, + &sd->sc_dk.dk_cpulabel); + if (error == 0) { + if (cmd == DIOCWDINFO) + error = writedisklabel(SDLABELDEV(dev), + sdstrategy, &sd->sc_dk.dk_label, + &sd->sc_dk.dk_cpulabel); + } + + sd->flags &= ~SDF_LABELLING; + sdunlock(sd); + return error; + + case DIOCWLABEL: + if ((flag & FWRITE) == 0) + return EBADF; + if (*(int *)addr) + sd->flags |= SDF_WLABEL; + else + sd->flags &= ~SDF_WLABEL; + return 0; + + default: + if (SDPART(dev) != RAW_PART) + return ENOTTY; + return scsi_do_ioctl(sd->sc_link, dev, cmd, addr, flag, p); + } + +#ifdef DIAGNOSTIC + panic("sdioctl: impossible"); +#endif +} + +/* + * Load the label information on the named device + */ +void +sdgetdisklabel(sd) + struct sd_softc *sd; +{ + struct disklabel *lp = &sd->sc_dk.dk_label; + char *errstring; + + bzero(lp, sizeof(struct disklabel)); + bzero(&sd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel)); + + lp->d_secsize = sd->params.blksize; + lp->d_ntracks = sd->params.heads; + lp->d_nsectors = sd->params.sectors; + lp->d_ncylinders = sd->params.cyls; + lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; + if (lp->d_secpercyl == 0) { + lp->d_secpercyl = 100; + /* as long as it's not 0 - readdisklabel divides by it (?) */ + } + + strncpy(lp->d_typename, "SCSI disk", 16); + lp->d_type = DTYPE_SCSI; + strncpy(lp->d_packname, "fictitious", 16); + lp->d_secperunit = sd->params.disksize; + lp->d_rpm = 3600; + lp->d_interleave = 1; + lp->d_flags = 0; + + lp->d_partitions[RAW_PART].p_offset = 0; + lp->d_partitions[RAW_PART].p_size = + lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); + lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; + lp->d_npartitions = RAW_PART + 1; + + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_checksum = dkcksum(lp); + + /* + * Call the generic disklabel extraction routine + */ + if (errstring = readdisklabel(MAKESDDEV(0, sd->sc_dev.dv_unit, + RAW_PART), sdstrategy, lp, &sd->sc_dk.dk_cpulabel)) { + printf("%s: %s\n", sd->sc_dev.dv_xname, errstring); + return; + } +} + +/* + * Find out from the device what it's capacity is + */ +u_long +sd_size(sd, flags) + struct sd_softc *sd; + int flags; +{ + struct scsi_read_cap_data rdcap; + struct scsi_read_capacity scsi_cmd; + u_long size; + + /* + * make up a scsi command and ask the scsi driver to do + * it for you. + */ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = READ_CAPACITY; + + /* + * If the command works, interpret the result as a 4 byte + * number of blocks + */ + if (scsi_scsi_cmd(sd->sc_link, (struct scsi_generic *)&scsi_cmd, + sizeof(scsi_cmd), (u_char *)&rdcap, sizeof(rdcap), SDRETRIES, + 2000, NULL, flags | SCSI_DATA_IN) != 0) + return 0; + + size = (rdcap.addr_3 << 24) + (rdcap.addr_2 << 16) + + (rdcap.addr_1 << 8) + rdcap.addr_0 + 1; + + return size; +} + +/* + * Tell the device to map out a defective block + */ +int +sd_reassign_blocks(sd, block) + struct sd_softc *sd; + u_long 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.opcode = 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->sc_link, (struct scsi_generic *)&scsi_cmd, + sizeof(scsi_cmd), (u_char *)&rbdata, sizeof(rbdata), SDRETRIES, + 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. + */ +int +sd_get_parms(sd, flags) + struct sd_softc *sd; + int flags; +{ + struct disk_parms *dp = &sd->params; + struct scsi_mode_sense scsi_cmd; + struct scsi_mode_sense_data { + struct scsi_mode_header header; + struct scsi_blk_desc blk_desc; + union disk_pages pages; + } scsi_sense; + u_long sectors; + + /* + * do a "mode sense page 4" + */ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.opcode = 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), + SDRETRIES, 6000, NULL, flags | SCSI_DATA_IN) != 0) { + printf("%s: could not mode sense (4)", sd->sc_dev.dv_xname); + fake_it: + printf("; using fictitious geometry\n"); + /* + * use adaptec standard fictitious geometry + * this depends on which controller (e.g. 1542C is + * different. but we have to put SOMETHING here..) + */ + sectors = sd_size(sd, flags); + dp->heads = 64; + dp->sectors = 32; + dp->cyls = sectors / (64 * 32); + dp->blksize = 512; + dp->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 ! + */ + dp->heads = scsi_sense.pages.rigid_geometry.nheads; + dp->cyls = + _3btol(&scsi_sense.pages.rigid_geometry.ncyl_2); + dp->blksize = _3btol(scsi_sense.blk_desc.blklen); + + if (dp->heads == 0 || dp->cyls == 0) { + printf("%s: mode sense (4) returned nonsense", + sd->sc_dev.dv_xname); + goto fake_it; + } + + if (dp->blksize == 0) + dp->blksize = 512; + + sectors = sd_size(sd, flags); + dp->disksize = sectors; + sectors /= (dp->heads * dp->cyls); + dp->sectors = sectors; /* XXX dubious on SCSI */ + } + + return 0; +} + +int +sdsize(dev) + dev_t dev; +{ + struct sd_softc *sd; + int part; + int size; + + if (sdopen(dev, 0, S_IFBLK) != 0) + return -1; + sd = sdcd.cd_devs[SDUNIT(dev)]; + part = SDPART(dev); + if (sd->sc_dk.dk_label.d_partitions[part].p_fstype != FS_SWAP) + size = -1; + else + size = sd->sc_dk.dk_label.d_partitions[part].p_size; + if (sdclose(dev, 0, S_IFBLK) != 0) + return -1; + return size; +} + +#ifndef __BDEVSW_DUMP_OLD_TYPE +/* #define SD_DUMP_NOT_TRUSTED if you just want to watch */ +static struct scsi_xfer sx; +static int sddoingadump; + +/* + * dump all of physical memory into the partition specified, starting + * at offset 'dumplo' into the partition. + */ +int +sddump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ + struct sd_softc *sd; /* disk unit to do the I/O */ + struct disklabel *lp; /* disk's disklabel */ + int unit, part; + int sectorsize; /* size of a disk sector */ + int nsects; /* number of sectors in partition */ + int sectoff; /* sector offset of partition */ + int totwrt; /* total number of sectors left to write */ + int nwrt; /* current number of sectors to write */ + struct scsi_rw_big cmd; /* write command */ + struct scsi_xfer *xs; /* ... convenience */ + int retval; + + /* Check if recursive dump; if so, punt. */ + if (sddoingadump) + return EFAULT; + + /* Mark as active early. */ + sddoingadump = 1; + + unit = SDUNIT(dev); /* Decompose unit & partition. */ + part = SDPART(dev); + + /* Check for acceptable drive number. */ + if (unit >= sdcd.cd_ndevs || (sd = sdcd.cd_devs[unit]) == NULL) + return ENXIO; + + /* Make sure it was initialized. */ + if (sd->sc_link->flags & SDEV_MEDIA_LOADED != SDEV_MEDIA_LOADED) + return ENXIO; + + /* Convert to disk sectors. Request must be a multiple of size. */ + lp = &sd->sc_dk.dk_label; + sectorsize = lp->d_secsize; + if ((size % sectorsize) != 0) + return EFAULT; + totwrt = size / sectorsize; + blkno = dbtob(blkno) / sectorsize; /* blkno in DEV_BSIZE units */ + + nsects = lp->d_partitions[part].p_size; + sectoff = lp->d_partitions[part].p_offset; + + /* Check transfer bounds against partition size. */ + if ((blkno < 0) || ((blkno + totwrt) > nsects)) + return EINVAL; + + /* Offset block number to start of partition. */ + blkno += sectoff; + + xs = &sx; + + while (totwrt > 0) { + nwrt = totwrt; /* XXX */ +#ifndef SD_DUMP_NOT_TRUSTED + /* + * Fill out the scsi command + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = WRITE_BIG; + cmd.addr_3 = (blkno >> 24) & 0xff; + cmd.addr_2 = (blkno >> 16) & 0xff; + cmd.addr_1 = (blkno >> 8) & 0xff; + cmd.addr_0 = blkno & 0xff; + cmd.length2 = (nwrt >> 8) & 0xff; + cmd.length1 = nwrt & 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_AUTOCONF | INUSE | SCSI_DATA_OUT; + xs->sc_link = sd->sc_link; + xs->retries = SDRETRIES; + xs->timeout = 10000; /* 10000 millisecs for a disk ! */ + xs->cmd = (struct scsi_generic *)&cmd; + xs->cmdlen = sizeof(cmd); + xs->resid = nwrt * sectorsize; + xs->error = XS_NOERROR; + xs->bp = 0; + xs->data = va; + xs->datalen = nwrt * sectorsize; + + /* + * Pass all this info to the scsi driver. + */ + retval = (*(sd->sc_link->adapter->scsi_cmd)) (xs); + if (retval != COMPLETE) + return ENXIO; +#else /* SD_DUMP_NOT_TRUSTED */ + /* Let's just talk about this first... */ + printf("sd%d: dump addr 0x%x, blk %d\n", unit, va, blkno); + delay(500 * 1000); /* half a second */ +#endif /* SD_DUMP_NOT_TRUSTED */ + + /* update block count */ + totwrt -= nwrt; + blkno += nwrt; + va += sectorsize * nwrt; + } + sddoingadump = 0; + return 0; +} +#else /* __BDEVSW_DUMP_NEW_TYPE */ +int +sddump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ + + /* Not implemented. */ + return ENXIO; +} +#endif /* __BDEVSW_DUMP_NEW_TYPE */ diff --git a/sys/scsi/st.c b/sys/scsi/st.c new file mode 100644 index 00000000000..3bc47126faa --- /dev/null +++ b/sys/scsi/st.c @@ -0,0 +1,1786 @@ +/* $NetBSD: st.c,v 1.54 1995/10/13 20:01:08 gwr Exp $ */ + +/* + * Copyright (c) 1994 Charles Hannum. All rights reserved. + * + * 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 Charles Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Originally 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 + * major changes by Julian Elischer (julian@jules.dialix.oz.au) May 1993 + */ + +/* + * 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/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 <sys/device.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_tape.h> +#include <scsi/scsiconf.h> + +/* Defines for device specific stuff */ +#define DEF_FIXED_BSIZE 512 +#define ST_RETRIES 4 /* only on non IO commands */ + +#define STMODE(z) ( minor(z) & 0x03) +#define STDSTY(z) ((minor(z) >> 2) & 0x03) +#define STUNIT(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_int quirks; /* same definitions as in quirkdata */ + int blksize; + u_int8_t density; +}; + +struct quirkdata { + u_int quirks; +#define ST_Q_FORCE_BLKSIZE 0x0001 +#define ST_Q_SENSE_HELP 0x0002 /* must do READ for good MODE SENSE */ +#define ST_Q_IGNORE_LOADS 0x0004 +#define ST_Q_BLKSIZE 0x0008 /* variable-block media_blksize > 0 */ + u_int page_0_size; +#define MAX_PAGE_0_SIZE 64 + struct modes modes[4]; +}; + +struct st_quirk_inquiry_pattern { + struct scsi_inquiry_pattern pattern; + struct quirkdata quirkdata; +}; + +struct st_quirk_inquiry_pattern st_quirk_patterns[] = { + {T_SEQUENTIAL, T_REMOV, + " ", " ", " ", 0, 0, { + ST_Q_FORCE_BLKSIZE, 512, 0, /* minor 0-3 */ + ST_Q_FORCE_BLKSIZE, 512, QIC_24, /* minor 4-7 */ + ST_Q_FORCE_BLKSIZE, 0, HALFINCH_1600, /* minor 8-11 */ + ST_Q_FORCE_BLKSIZE, 0, HALFINCH_6250 /* minor 12-15 */ + }}, + {T_SEQUENTIAL, T_REMOV, + "TANDBERG", " TDC 3600 ", "", 0, 12, { + 0, 0, 0, /* minor 0-3 */ + ST_Q_FORCE_BLKSIZE, 0, QIC_525, /* minor 4-7 */ + 0, 0, QIC_150, /* minor 8-11 */ + 0, 0, QIC_120 /* minor 12-15 */ + }}, + {T_SEQUENTIAL, T_REMOV, + "TANDBERG", " TDC 3800 ", "", 0, 0, { + ST_Q_FORCE_BLKSIZE, 512, 0, /* minor 0-3 */ + 0, 0, QIC_525, /* minor 4-7 */ + 0, 0, QIC_150, /* minor 8-11 */ + 0, 0, QIC_120 /* minor 12-15 */ + }}, + /* + * At least -005 and -007 need this. I'll assume they all do unless I + * hear otherwise. - mycroft, 31MAR1994 + */ + {T_SEQUENTIAL, T_REMOV, + "ARCHIVE ", "VIPER 2525 25462", "", 0, 0, { + ST_Q_SENSE_HELP, 0, 0, /* minor 0-3 */ + ST_Q_SENSE_HELP, 0, QIC_525, /* minor 4-7 */ + 0, 0, QIC_150, /* minor 8-11 */ + 0, 0, QIC_120 /* minor 12-15 */ + }}, + /* + * One user reports that this works for his tape drive. It probably + * needs more work. - mycroft, 09APR1994 + */ + {T_SEQUENTIAL, T_REMOV, + "SANKYO ", "CP525 ", "", 0, 0, { + ST_Q_FORCE_BLKSIZE, 512, 0, /* minor 0-3 */ + ST_Q_FORCE_BLKSIZE, 512, QIC_525, /* minor 4-7 */ + 0, 0, QIC_150, /* minor 8-11 */ + 0, 0, QIC_120 /* minor 12-15 */ + }}, + {T_SEQUENTIAL, T_REMOV, + "ANRITSU ", "DMT780 ", "", 0, 0, { + ST_Q_FORCE_BLKSIZE, 512, 0, /* minor 0-3 */ + ST_Q_FORCE_BLKSIZE, 512, QIC_525, /* minor 4-7 */ + 0, 0, QIC_150, /* minor 8-11 */ + 0, 0, QIC_120 /* minor 12-15 */ + }}, + {T_SEQUENTIAL, T_REMOV, + "ARCHIVE ", "VIPER 150 21247", "", 0, 12, { + 0, 0, 0, /* minor 0-3 */ + 0, 0, QIC_150, /* minor 4-7 */ + 0, 0, QIC_120, /* minor 8-11 */ + 0, 0, QIC_24 /* minor 12-15 */ + }}, + {T_SEQUENTIAL, T_REMOV, + "WANGTEK ", "5099ES SCSI", "", 0, 0, { + ST_Q_FORCE_BLKSIZE, 512, 0, /* minor 0-3 */ + 0, 0, QIC_11, /* minor 4-7 */ + 0, 0, QIC_24, /* minor 8-11 */ + 0, 0, QIC_24, /* minor 12-15 */ + }}, + {T_SEQUENTIAL, T_REMOV, + "WANGTEK ", "5150ES SCSI", "", 0, 0, { + ST_Q_FORCE_BLKSIZE, 512, 0, /* minor 0-3 */ + 0, 0, QIC_24, /* minor 4-7 */ + 0, 0, QIC_120, /* minor 8-11 */ + 0, 0, QIC_150 /* minor 12-15 */ + }}, + {T_SEQUENTIAL, T_REMOV, + "WANGTEK ", "5525ES SCSI REV7", "", 0, 0, { + 0, 0, 0, /* minor 0-3 */ + ST_Q_BLKSIZE, 0, QIC_525, /* minor 4-7 */ + 0, 0, QIC_150, /* minor 8-11 */ + 0, 0, QIC_120 /* minor 12-15 */ + }}, + {T_SEQUENTIAL, T_REMOV, + "WangDAT ", "Model 1300 ", "", 0, 0, { + 0, 0, 0, /* minor 0-3 */ + ST_Q_FORCE_BLKSIZE, 512, DDS, /* minor 4-7 */ + ST_Q_FORCE_BLKSIZE, 1024, DDS, /* minor 8-11 */ + ST_Q_FORCE_BLKSIZE, 0, DDS /* minor 12-15 */ + }}, +#if 0 + {T_SEQUENTIAL, T_REMOV, + "EXABYTE ", "EXB-8200 ", "", 0, 12, { + 0, 0, 0, /* minor 0-3 */ + 0, 0, 0, /* minor 4-7 */ + 0, 0, 0, /* minor 8-11 */ + 0, 0, 0 /* minor 12-15 */ + }}, +#endif +}; + +#define NOEJECT 0 +#define EJECT 1 + +struct st_softc { + struct device sc_dev; +/*--------------------present operating parameters, flags etc.----------------*/ + int flags; /* see below */ + u_int quirks; /* quirks for the open mode */ + int blksize; /* blksize we are using */ + u_int8_t density; /* present density */ + u_int page_0_size; /* size of page 0 data */ + u_int last_dsty; /* last density opened */ +/*--------------------device/scsi parameters----------------------------------*/ + struct scsi_link *sc_link; /* our link to the adpter etc. */ +/*--------------------parameters reported by the device ----------------------*/ + int blkmin; /* min blk size */ + int blkmax; /* max blk size */ + struct quirkdata *quirkdata; /* if we have a rogue entry */ +/*--------------------parameters reported by the device for this media--------*/ + u_long numblks; /* nominal blocks capacity */ + int media_blksize; /* 0 if not ST_FIXEDBLOCKS */ + u_int8_t media_density; /* this is what it said when asked */ +/*--------------------quirks for the whole drive------------------------------*/ + u_int drive_quirks; /* quirks of this drive */ +/*--------------------How we should set up when opening each minor device----*/ + struct modes modes[4]; /* plus more for each mode */ + u_int8_t 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------------*/ + u_char sense_data[MAX_PAGE_0_SIZE]; /* + * additional sense data needed + * for mode sense/select. + */ + struct buf buf_queue; /* the queue of pending IO operations */ +}; + +int stmatch __P((struct device *, void *, void *)); +void stattach __P((struct device *, struct device *, void *)); + +struct cfdriver stcd = { + NULL, "st", stmatch, stattach, DV_TAPE, sizeof(struct st_softc) +}; + +int st_space __P((struct st_softc *, int number, u_int what, int flags)); +int st_rewind __P((struct st_softc *, u_int immediate, int flags)); +int st_mode_sense __P((struct st_softc *, int flags)); +int st_decide_mode __P((struct st_softc *, boolean first_read)); +int st_read_block_limits __P((struct st_softc *, int flags)); +int st_touch_tape __P((struct st_softc *)); +int st_write_filemarks __P((struct st_softc *, int number, int flags)); +int st_load __P((struct st_softc *, u_int type, int flags)); +int st_mode_select __P((struct st_softc *, int flags)); +void ststrategy(); +int st_check_eod(); +void ststart(); +void st_unmount(); +int st_mount_tape(); +void st_loadquirks(); +void st_identify_drive(); +int st_interpret_sense(); + +struct scsi_device st_switch = { + st_interpret_sense, + ststart, + NULL, + NULL, +}; + +#define ST_INFO_VALID 0x0001 +#define ST_BLOCK_SET 0x0002 /* block size, mode set by ioctl */ +#define ST_WRITTEN 0x0004 /* data have been written, EOD needed */ +#define ST_FIXEDBLOCKS 0x0008 +#define ST_AT_FILEMARK 0x0010 +#define ST_EIO_PENDING 0x0020 /* we couldn't report it then (had data) */ +#define ST_NEW_MOUNT 0x0040 /* still need to decide mode */ +#define ST_READONLY 0x0080 /* st_mode_sense says write protected */ +#define ST_FM_WRITTEN 0x0100 /* + * EOF file mark written -- used with + * ~ST_WRITTEN to indicate that multiple file + * marks have been written + */ +#define ST_BLANK_READ 0x0200 /* BLANK CHECK encountered already */ +#define ST_2FM_AT_EOD 0x0400 /* write 2 file marks at EOD */ +#define ST_MOUNTED 0x0800 /* 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) + +struct scsi_inquiry_pattern st_patterns[] = { + {T_SEQUENTIAL, T_REMOV, + "", "", ""}, +}; + +int +stmatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct cfdata *cf = match; + struct scsibus_attach_args *sa = aux; + int priority; + + (void)scsi_inqmatch(sa->sa_inqbuf, + (caddr_t)st_patterns, sizeof(st_patterns)/sizeof(st_patterns[0]), + sizeof(st_patterns[0]), &priority); + return (priority); +} + +/* + * The routine called by the low level scsi routine when it discovers + * A device suitable for this driver + */ +void +stattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct st_softc *st = (void *)self; + struct scsibus_attach_args *sa = aux; + struct scsi_link *sc_link = sa->sa_sc_link; + + SC_DEBUG(sc_link, SDEV_DB2, ("stattach: ")); + + /* + * Store information needed to contact our base driver + */ + st->sc_link = sc_link; + sc_link->device = &st_switch; + sc_link->device_softc = st; + sc_link->openings = 1; + + /* + * Check if the drive is a known criminal and take + * Any steps needed to bring it into line + */ + st_identify_drive(st, sa->sa_inqbuf); + + /* + * Use the subdriver to request information regarding + * the drive. We cannot use interrupts yet, so the + * request must specify this. + */ + printf(": %s", st->quirkdata ? "rogue, " : ""); + if (scsi_test_unit_ready(sc_link, + SCSI_AUTOCONF | SCSI_SILENT | SCSI_IGNORE_MEDIA_CHANGE) || + st_mode_sense(st, + SCSI_AUTOCONF | SCSI_SILENT | SCSI_IGNORE_MEDIA_CHANGE)) + printf("drive empty\n"); + else { + printf("density code 0x%x, ", st->media_density); + if (st->media_blksize > 0) + printf("%d-byte", st->media_blksize); + else + printf("variable"); + printf(" blocks, write-%s\n", + (st->flags & ST_READONLY) ? "protected" : "enabled"); + } + + /* + * Set up the buf queue for this device + */ + st->buf_queue.b_active = 0; + st->buf_queue.b_actf = 0; + st->buf_queue.b_actb = &st->buf_queue.b_actf; +} + +/* + * Use the inquiry routine in 'scsi_base' to get drive info so we can + * Further tailor our behaviour. + */ +void +st_identify_drive(st, inqbuf) + struct st_softc *st; + struct scsi_inquiry_data *inqbuf; +{ + struct st_quirk_inquiry_pattern *finger; + int priority; + + finger = (struct st_quirk_inquiry_pattern *)scsi_inqmatch(inqbuf, + (caddr_t)st_quirk_patterns, + sizeof(st_quirk_patterns)/sizeof(st_quirk_patterns[0]), + sizeof(st_quirk_patterns[0]), &priority); + if (priority != 0) { + st->quirkdata = &finger->quirkdata; + st->drive_quirks = finger->quirkdata.quirks; + st->quirks = finger->quirkdata.quirks; /* start value */ + st->page_0_size = finger->quirkdata.page_0_size; + st_loadquirks(st); + } +} + +/* + * 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_softc *st; +{ + int i; + struct modes *mode; + struct modes *mode2; + + mode = st->quirkdata->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->quirks | st->drive_quirks) & ST_Q_FORCE_BLKSIZE) { + mode2->blksize = mode->blksize; + 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. + */ +int +stopen(dev, flags) + dev_t dev; + int flags; +{ + int unit; + u_int mode, dsty; + int error = 0; + struct st_softc *st; + struct scsi_link *sc_link; + + unit = STUNIT(dev); + if (unit >= stcd.cd_ndevs) + return ENXIO; + st = stcd.cd_devs[unit]; + if (!st) + return ENXIO; + + mode = STMODE(dev); + dsty = STDSTY(dev); + sc_link = st->sc_link; + + SC_DEBUG(sc_link, SDEV_DB1, ("open: dev=0x%x (unit %d (of %d))\n", dev, + unit, stcd.cd_ndevs)); + + /* + * Only allow one at a time + */ + if (sc_link->flags & SDEV_OPEN) { + printf("%s: already open\n", st->sc_dev.dv_xname); + return EBUSY; + } + + /* + * Catch any unit attention errors. + */ + if (error = scsi_test_unit_ready(sc_link, + SCSI_IGNORE_MEDIA_CHANGE | (mode == CTLMODE ? SCSI_IGNORE_NOT_READY : 0))) + goto bad; + + 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 opened to set defaults + * This mode does NOT ALLOW I/O, only ioctls + */ + if (mode == CTLMODE) + return 0; + + /* + * 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(st, 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")); + return 0; + +bad: + st_unmount(st, NOEJECT); + sc_link->flags &= ~SDEV_OPEN; + return error; +} + +/* + * close the device.. only called if we are the LAST + * occurence of an open device + */ +int +stclose(dev) + dev_t dev; +{ + struct st_softc *st = stcd.cd_devs[STUNIT(dev)]; + + SC_DEBUG(st->sc_link, SDEV_DB1, ("closing\n")); + if ((st->flags & (ST_WRITTEN | ST_FM_WRITTEN)) == ST_WRITTEN) + st_write_filemarks(st, 1, 0); + switch (STMODE(dev)) { + case 0: + case 3: /* for now */ + st_unmount(st, NOEJECT); + break; + case 1: + /* leave mounted unless media seems to have been removed */ + if (!(st->sc_link->flags & SDEV_MEDIA_LOADED)) + st_unmount(st, NOEJECT); + break; + case 2: + st_unmount(st, EJECT); + break; + } + st->sc_link->flags &= ~SDEV_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. + */ +int +st_mount_tape(dev, flags) + dev_t dev; + int flags; +{ + int unit; + u_int mode, dsty; + struct st_softc *st; + struct scsi_link *sc_link; + int error = 0; + + unit = STUNIT(dev); + mode = STMODE(dev); + dsty = STDSTY(dev); + st = stcd.cd_devs[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 (error = st_load(st, LD_LOAD, 0)) + return error; + /* + * 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); /* XXX */ + + /* + * 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_SENSE_HELP) + if (error = st_touch_tape(st)) + return error; + /* + * Load the physical device parameters + * loads: blkmin, blkmax + */ + if (error = st_read_block_limits(st, 0)) + return error; + /* + * Load the media dependent parameters + * includes: media_blksize,media_density,numblks + * As we have a tape in, it should be reflected here. + * If not you may need the "quirk" above. + */ + if (error = st_mode_sense(st, 0)) + return error; + /* + * 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->blksize = st->modes[dsty].blksize; + if (st->blksize) + st->flags |= ST_FIXEDBLOCKS; + } else { + if (error = st_decide_mode(st, FALSE)) + return error; + } + if (error = st_mode_select(st, 0)) { + printf("%s: cannot set selected mode\n", st->sc_dev.dv_xname); + return error; + } + scsi_prevent(sc_link, PR_PREVENT, + SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY); + 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(st, eject) + struct st_softc *st; + boolean eject; +{ + struct scsi_link *sc_link = st->sc_link; + int nmarks; + + if (!(st->flags & ST_MOUNTED)) + return; + SC_DEBUG(sc_link, SDEV_DB1, ("unmounting\n")); + st_check_eod(st, FALSE, &nmarks, SCSI_IGNORE_NOT_READY); + st_rewind(st, 0, SCSI_IGNORE_NOT_READY); + scsi_prevent(sc_link, PR_ALLOW, + SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY); + if (eject) + st_load(st, LD_UNLOAD, SCSI_IGNORE_NOT_READY); + 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) + */ +int +st_decide_mode(st, first_read) + struct st_softc *st; + boolean first_read; +{ +#ifdef SCSIDEBUG + struct scsi_link *sc_link = st->sc_link; +#endif + + SC_DEBUG(sc_link, SDEV_DB2, ("starting block mode decision\n")); + + /* + * 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->blksize = 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 (st->density) { + case HALFINCH_800: + case HALFINCH_1600: + case HALFINCH_6250: + case DDS: + st->flags &= ~ST_FIXEDBLOCKS; + st->blksize = 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_blksize > 0) + st->blksize = st->media_blksize; + else + st->blksize = 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_BLKSIZE) || (st->media_blksize == 0) || + (st->media_blksize == DEF_FIXED_BSIZE) || + (st->media_blksize == 1024))) { + if (st->media_blksize > 0) + st->flags |= ST_FIXEDBLOCKS; + else + st->flags &= ~ST_FIXEDBLOCKS; + st->blksize = st->media_blksize; + SC_DEBUG(sc_link, SDEV_DB3, + ("Used media_blksize of %d\n", st->media_blksize)); + goto done; + } + /* + * We're getting no hints from any direction. Choose variable- + * length blocks arbitrarily. + */ + st->flags &= ~ST_FIXEDBLOCKS; + st->blksize = 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 (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; +} + +/* + * 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 st_softc *st = stcd.cd_devs[STUNIT(bp->b_dev)]; + struct buf *dp; + int s; + + SC_DEBUG(st->sc_link, SDEV_DB1, + ("ststrategy %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->blksize) { + printf("%s: bad request, must be multiple of %d\n", + st->sc_dev.dv_xname, st->blksize); + bp->b_error = EIO; + goto bad; + } + } + /* + * as are out-of-range requests on variable drives. + */ + else if (bp->b_bcount < st->blkmin || + (st->blkmax && bp->b_bcount > st->blkmax)) { + printf("%s: bad request, must be between %d and %d\n", + st->sc_dev.dv_xname, st->blkmin, st->blkmax); + bp->b_error = EIO; + goto bad; + } + s = splbio(); + + /* + * 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; + bp->b_actf = NULL; + bp->b_actb = dp->b_actb; + *dp->b_actb = bp; + dp->b_actb = &bp->b_actf; + + /* + * 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(st); + + splx(s); + 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(st) + struct st_softc *st; +{ + struct scsi_link *sc_link = st->sc_link; + register struct buf *bp, *dp; + struct scsi_rw_tape cmd; + int 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->openings > 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; + } + + bp = st->buf_queue.b_actf; + if (!bp) + return; /* no work to bother with */ + if (dp = bp->b_actf) + dp->b_actb = bp->b_actb; + else + st->buf_queue.b_actb = bp->b_actb; + *bp->b_actb = dp; + + /* + * 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; + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + biodone(bp); + continue; + } + /* + * 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(st, 0, SP_FILEMARKS, 0)) { + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + biodone(bp); + continue; + } + } 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.opcode = WRITE; + st->flags &= ~ST_FM_WRITTEN; + st->flags |= ST_WRITTEN; + flags = SCSI_DATA_OUT; + } else { + cmd.opcode = READ; + 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 |= SRW_FIXED; + lto3b(bp->b_bcount / st->blksize, 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_data, bp->b_bcount, 0, + 100000, bp, flags | SCSI_NOSLEEP)) + printf("%s: not queued\n", st->sc_dev.dv_xname); + } /* go back and see if we can cram more work in.. */ +} + +int +stread(dev, uio) + dev_t dev; + struct uio *uio; +{ + struct st_softc *st = stcd.cd_devs[STUNIT(dev)]; + + return (physio(ststrategy, NULL, dev, B_READ, + st->sc_link->adapter->scsi_minphys, uio)); +} + +int +stwrite(dev, uio) + dev_t dev; + struct uio *uio; +{ + struct st_softc *st = stcd.cd_devs[STUNIT(dev)]; + + return (physio(ststrategy, NULL, dev, B_WRITE, + st->sc_link->adapter->scsi_minphys, uio)); +} + +/* + * Perform special action on behalf of the user; + * knows about the internals of this device + */ +int +stioctl(dev, cmd, arg, flag, p) + dev_t dev; + u_long cmd; + caddr_t arg; + int flag; + struct proc *p; +{ + int error = 0; + int unit; + int number, nmarks, dsty; + int flags; + struct st_softc *st; + int hold_blksize; + u_int8_t hold_density; + 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 = STUNIT(dev); + dsty = STDSTY(dev); + st = stcd.cd_devs[unit]; + hold_blksize = st->blksize; + 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_blksiz = st->blksize; + g->mt_density = st->density; + g->mt_mblksiz[0] = st->modes[0].blksize; + g->mt_mblksiz[1] = st->modes[1].blksize; + g->mt_mblksiz[2] = st->modes[2].blksize; + g->mt_mblksiz[3] = st->modes[3].blksize; + g->mt_mdensity[0] = st->modes[0].density; + g->mt_mdensity[1] = st->modes[1].density; + g->mt_mdensity[2] = st->modes[2].density; + g->mt_mdensity[3] = st->modes[3].density; + 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 */ + error = st_write_filemarks(st, number, flags); + break; + case MTBSF: /* backward space file */ + number = -number; + case MTFSF: /* forward space file */ + error = st_check_eod(st, FALSE, &nmarks, flags); + if (!error) + error = st_space(st, number - nmarks, + SP_FILEMARKS, flags); + break; + case MTBSR: /* backward space record */ + number = -number; + case MTFSR: /* forward space record */ + error = st_check_eod(st, TRUE, &nmarks, flags); + if (!error) + error = st_space(st, number, SP_BLKS, flags); + break; + case MTREW: /* rewind */ + error = st_rewind(st, 0, flags); + break; + case MTOFFL: /* rewind and put the drive offline */ + st_unmount(st, EJECT); + break; + case MTNOP: /* no operation, sets status only */ + break; + case MTRETEN: /* retension the tape */ + error = st_load(st, LD_RETENSION, flags); + if (!error) + error = st_load(st, LD_LOAD, flags); + break; + case MTEOM: /* forward space to end of media */ + error = st_check_eod(st, FALSE, &nmarks, flags); + if (!error) + error = st_space(st, 1, SP_EOM, flags); + break; + 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"); + error = EINVAL; + break; + } +#endif + if (number == 0) { + st->flags &= ~ST_FIXEDBLOCKS; + } else { + if ((st->blkmin || st->blkmax) && + (number < st->blkmin || + number > st->blkmax)) { + error = EINVAL; + break; + } + st->flags |= ST_FIXEDBLOCKS; + } + st->blksize = 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) + error = EINVAL; + else + st->density = number; + goto try_new_value; + + default: + error = EINVAL; + } + break; + } + case MTIOCIEOT: + case MTIOCEEOT: + break; + default: + if (STMODE(dev) == CTLMODE) + error = scsi_do_ioctl(st->sc_link, dev, cmd, arg, flag, p); + else + error = ENOTTY; + break; + } + return error; +/*-----------------------------*/ +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 (error = st_mode_select(st, 0)) { /* put it back as it was */ + printf("%s: cannot set selected mode\n", st->sc_dev.dv_xname); + st->density = hold_density; + st->blksize = hold_blksize; + if (st->blksize) + st->flags |= ST_FIXEDBLOCKS; + else + st->flags &= ~ST_FIXEDBLOCKS; + return error; + } + /* + * 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 (STMODE(dev) == 0x03) { + /* special mode */ + /* XXX */ + switch ((short) (mt->mt_op)) { + case MTSETBSIZ: + st->modes[dsty].blksize = st->blksize; + 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. + */ +int +st_read(st, buf, size, flags) + struct st_softc *st; + int size; + int flags; + char *buf; +{ + struct scsi_rw_tape cmd; + + /* + * If it's a null transfer, return immediatly + */ + if (size == 0) + return 0; + bzero(&cmd, sizeof(cmd)); + cmd.opcode = READ; + if (st->flags & ST_FIXEDBLOCKS) { + cmd.byte2 |= SRW_FIXED; + lto3b(size / (st->blksize ? st->blksize : DEF_FIXED_BSIZE), + cmd.len); + } else + lto3b(size, cmd.len); + return scsi_scsi_cmd(st->sc_link, (struct scsi_generic *) &cmd, + sizeof(cmd), (u_char *) buf, size, 0, 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. + */ +int +st_read_block_limits(st, flags) + struct st_softc *st; + int flags; +{ + struct scsi_block_limits cmd; + struct scsi_block_limits_data block_limits; + struct scsi_link *sc_link = st->sc_link; + int error; + + /* + * First check if we have it all loaded + */ + if ((sc_link->flags & SDEV_MEDIA_LOADED)) + return 0; + + /* + * do a 'Read Block Limits' + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = READ_BLOCK_LIMITS; + + /* + * do the command, update the global values + */ + if (error = scsi_scsi_cmd(sc_link, (struct scsi_generic *) &cmd, + sizeof(cmd), (u_char *) &block_limits, sizeof(block_limits), + ST_RETRIES, 5000, NULL, flags | SCSI_DATA_IN)) + return error; + + st->blkmin = b2tol(block_limits.min_length); + st->blkmax = _3btol(&block_limits.max_length_2); + + SC_DEBUG(sc_link, SDEV_DB3, + ("(%d <= blksize <= %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) + */ +int +st_mode_sense(st, flags) + struct st_softc *st; + int flags; +{ + u_int scsi_sense_len; + int error; + struct scsi_mode_sense cmd; + struct scsi_sense { + struct scsi_mode_header header; + struct scsi_blk_desc blk_desc; + u_char sense_data[MAX_PAGE_0_SIZE]; + } scsi_sense; + struct scsi_link *sc_link = st->sc_link; + + scsi_sense_len = 12 + st->page_0_size; + + /* + * Set up a mode sense + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = MODE_SENSE; + 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 (error = scsi_scsi_cmd(sc_link, (struct scsi_generic *) &cmd, + sizeof(cmd), (u_char *) &scsi_sense, scsi_sense_len, + ST_RETRIES, 5000, NULL, flags | SCSI_DATA_IN)) + return error; + + st->numblks = _3btol(scsi_sense.blk_desc.nblocks); + st->media_blksize = _3btol(scsi_sense.blk_desc.blklen); + st->media_density = scsi_sense.blk_desc.density; + if (scsi_sense.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_blksize, + st->flags & ST_READONLY ? "protected" : "enabled")); + SC_DEBUG(sc_link, SDEV_DB3, + ("%sbuffered\n", + scsi_sense.header.dev_spec & SMH_DSP_BUFF_MODE ? "" : "un")); + if (st->page_0_size) + bcopy(scsi_sense.sense_data, st->sense_data, st->page_0_size); + 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. + */ +int +st_mode_select(st, flags) + struct st_softc *st; + int flags; +{ + u_int scsi_select_len; + int error; + struct scsi_mode_select cmd; + struct scsi_select { + struct scsi_mode_header header; + struct scsi_blk_desc blk_desc; + u_char sense_data[MAX_PAGE_0_SIZE]; + } scsi_select; + struct scsi_link *sc_link = st->sc_link; + + scsi_select_len = 12 + st->page_0_size; + + /* + * Set up for a mode select + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = MODE_SELECT; + cmd.length = scsi_select_len; + + bzero(&scsi_select, scsi_select_len); + scsi_select.header.blk_desc_len = sizeof(struct scsi_blk_desc); + scsi_select.header.dev_spec |= SMH_DSP_BUFF_MODE_ON; + scsi_select.blk_desc.density = st->density; + if (st->flags & ST_FIXEDBLOCKS) + lto3b(st->blksize, scsi_select.blk_desc.blklen); + if (st->page_0_size) + bcopy(st->sense_data, scsi_select.sense_data, st->page_0_size); + + /* + * do the command + */ + return scsi_scsi_cmd(sc_link, (struct scsi_generic *) &cmd, + sizeof(cmd), (u_char *) &scsi_select, scsi_select_len, + ST_RETRIES, 5000, NULL, flags | SCSI_DATA_OUT); +} + +/* + * skip N blocks/filemarks/seq filemarks/eom + */ +int +st_space(st, number, what, flags) + struct st_softc *st; + u_int what; + int flags; + int number; +{ + struct scsi_space cmd; + int error; + + switch (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(st, 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 discovered 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++; /* XXX dubious */ + } + break; + case SP_EOM: + if (st->flags & ST_EIO_PENDING) { + /* pretend we just discovered the error */ + st->flags &= ~ST_EIO_PENDING; + return EIO; + } + if (st->flags & ST_AT_FILEMARK) + st->flags &= ~ST_AT_FILEMARK; + break; + } + if (number == 0) + return 0; + + bzero(&cmd, sizeof(cmd)); + cmd.opcode = SPACE; + cmd.byte2 = what; + lto3b(number, cmd.number); + + return scsi_scsi_cmd(st->sc_link, (struct scsi_generic *) &cmd, + sizeof(cmd), 0, 0, 0, 900000, NULL, flags); +} + +/* + * write N filemarks + */ +int +st_write_filemarks(st, number, flags) + struct st_softc *st; + int flags; + int number; +{ + struct scsi_write_filemarks cmd; + + /* + * It's hard to write a negative number of file marks. + * Don't try. + */ + if (number < 0) + return EINVAL; + switch (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(&cmd, sizeof(cmd)); + cmd.opcode = WRITE_FILEMARKS; + lto3b(number, cmd.number); + + return scsi_scsi_cmd(st->sc_link, (struct scsi_generic *) &cmd, + sizeof(cmd), 0, 0, 0, 100000, 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. + */ +int +st_check_eod(st, position, nmarks, flags) + struct st_softc *st; + boolean position; + int *nmarks; + int flags; +{ + int error; + + switch (st->flags & (ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD)) { + default: + *nmarks = 0; + return 0; + 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(st, *nmarks, flags); + if (position && !error) + error = st_space(st, -*nmarks, SP_FILEMARKS, flags); + return error; +} + +/* + * load/unload/retension + */ +int +st_load(st, type, flags) + struct st_softc *st; + u_int type; + int flags; +{ + struct scsi_load cmd; + + if (type != LD_LOAD) { + int error; + int nmarks; + + error = st_check_eod(st, FALSE, &nmarks, flags); + if (error) + return error; + } + if (st->quirks & ST_Q_IGNORE_LOADS) + return 0; + + bzero(&cmd, sizeof(cmd)); + cmd.opcode = LOAD; + cmd.how = type; + + return scsi_scsi_cmd(st->sc_link, (struct scsi_generic *) &cmd, + sizeof(cmd), 0, 0, ST_RETRIES, 300000, NULL, flags); +} + +/* + * Rewind the device + */ +int +st_rewind(st, immediate, flags) + struct st_softc *st; + u_int immediate; + int flags; +{ + struct scsi_rewind cmd; + int error; + int nmarks; + + error = st_check_eod(st, FALSE, &nmarks, flags); + if (error) + return error; + st->flags &= ~ST_PER_ACTION; + + bzero(&cmd, sizeof(cmd)); + cmd.opcode = REWIND; + cmd.byte2 = immediate; + + return scsi_scsi_cmd(st->sc_link, (struct scsi_generic *) &cmd, + sizeof(cmd), 0, 0, ST_RETRIES, immediate ? 5000 : 300000, NULL, + flags); +} + +/* + * 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) + */ +int +st_interpret_sense(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *sc_link = xs->sc_link; + struct scsi_sense_data *sense = &xs->sense; + struct buf *bp = xs->bp; + struct st_softc *st = sc_link->device_softc; + u_int8_t key; + u_int32_t info; + + /* + * Get the sense fields and work out what code + */ + if (sense->error_code & SSD_ERRCODE_VALID) { + bcopy(sense->extended_info, &info, sizeof info); + info = ntohl(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->blksize; + if (sense->extended_flags & SSD_EOM) { + st->flags |= ST_EIO_PENDING; + if (bp) + bp->b_resid = xs->resid; + } + if (sense->extended_flags & SSD_FILEMARK) { + st->flags |= ST_AT_FILEMARK; + if (bp) + bp->b_resid = xs->resid; + } + if (sense->extended_flags & SSD_ILI) { + st->flags |= ST_EIO_PENDING; + if (bp) + bp->b_resid = xs->resid; + if (sense->error_code & SSD_ERRCODE_VALID && + (xs->flags & SCSI_SILENT) == 0) + printf("%s: block wrong size, %d blocks residual\n", + st->sc_dev.dv_xname, 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_SENSE_HELP) && + !(sc_link->flags & SDEV_MEDIA_LOADED)) + st->blksize -= 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; + return 0; + } + } + } else { /* must be variable mode */ + xs->resid = xs->datalen; /* to be sure */ + if (sense->extended_flags & SSD_EOM) + return EIO; + if (sense->extended_flags & SSD_FILEMARK) { + if (bp) + bp->b_resid = bp->b_bcount; + return 0; + } + if (sense->extended_flags & SSD_ILI) { + if (info < 0) { + /* + * the record was bigger than the read + */ + if ((xs->flags & SCSI_SILENT) == 0) + printf("%s: %d-byte record too big\n", + st->sc_dev.dv_xname, + xs->datalen - info); + return EIO; + } + xs->resid = info; + if (bp) + bp->b_resid = info; + } + } + key = sense->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_SENSE_HELP) && + !(sc_link->flags & SDEV_MEDIA_LOADED)) { + /* still starting */ + st->blksize -= 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 0; + } + } + 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. + */ +int +st_touch_tape(st) + struct st_softc *st; +{ + char *buf; + int readsize; + int error; + + buf = malloc(1024, M_TEMP, M_NOWAIT); + if (!buf) + return ENOMEM; + + if (error = st_mode_sense(st, 0)) + goto bad; + st->blksize = 1024; + do { + switch (st->blksize) { + case 512: + case 1024: + readsize = st->blksize; + st->flags |= ST_FIXEDBLOCKS; + break; + default: + readsize = 1; + st->flags &= ~ST_FIXEDBLOCKS; + } + if (error = st_mode_select(st, 0)) + goto bad; + st_read(st, buf, readsize, SCSI_SILENT); /* XXX */ + if (error = st_rewind(st, 0, 0)) { +bad: free(buf, M_TEMP); + return error; + } + } while (readsize != 1 && readsize > st->blksize); + + free(buf, M_TEMP); + return 0; +} + +int +stdump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ + + /* Not implemented. */ + return ENXIO; +} diff --git a/sys/scsi/su.c b/sys/scsi/su.c new file mode 100644 index 00000000000..dc2ec883202 --- /dev/null +++ b/sys/scsi/su.c @@ -0,0 +1,5 @@ +/* $NetBSD: su.c,v 1.2 1994/06/29 06:43:24 cgd Exp $ */ + +/* 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 00000000000..faa473189f7 --- /dev/null +++ b/sys/scsi/uk.c @@ -0,0 +1,174 @@ +/* $NetBSD: uk.c,v 1.13 1995/03/24 20:17:15 glass Exp $ */ + +/* + * Copyright (c) 1994 Charles Hannum. All rights reserved. + * + * 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 Charles Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Dummy driver for a device we can't identify. + * Originally by Julian Elischer (julian@tfs.com) + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/device.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#define UKUNIT(z) (minor(z)) + +struct uk_softc { + struct device sc_dev; + + struct scsi_link *sc_link; /* all the inter level info */ +}; + +int ukmatch __P((struct device *, void *, void *)); +void ukattach __P((struct device *, struct device *, void *)); + +struct cfdriver ukcd = { + NULL, "uk", ukmatch, ukattach, DV_DULL, sizeof(struct uk_softc) +}; + +/* + * This driver is so simple it uses all the default services + */ +struct scsi_device uk_switch = { + NULL, + NULL, + NULL, + NULL, +}; + +int +ukmatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + + return 1; +} + +/* + * The routine called by the low level scsi routine when it discovers + * a device suitable for this driver. + */ +void +ukattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct uk_softc *uk = (void *)self; + struct scsibus_attach_args *sa = aux; + struct scsi_link *sc_link = sa->sa_sc_link; + + SC_DEBUG(sc_link, SDEV_DB2, ("ukattach: ")); + + /* + * Store information needed to contact our base driver + */ + uk->sc_link = sc_link; + sc_link->device = &uk_switch; + sc_link->device_softc = uk; + sc_link->openings = 1; + + printf(": unknown device\n"); +} + +/* + * open the device. + */ +int +ukopen(dev) + dev_t dev; +{ + int unit; + struct uk_softc *uk; + struct scsi_link *sc_link; + + unit = UKUNIT(dev); + if (unit >= ukcd.cd_ndevs) + return ENXIO; + uk = ukcd.cd_devs[unit]; + if (!uk) + return ENXIO; + + sc_link = uk->sc_link; + + SC_DEBUG(sc_link, SDEV_DB1, + ("ukopen: dev=0x%x (unit %d (of %d))\n", dev, unit, ukcd.cd_ndevs)); + + /* + * Only allow one at a time + */ + if (sc_link->flags & SDEV_OPEN) { + printf("%s: already open\n", uk->sc_dev.dv_xname); + return EBUSY; + } + + sc_link->flags |= SDEV_OPEN; + + SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n")); + return 0; +} + +/* + * close the device.. only called if we are the LAST + * occurence of an open device + */ +int +ukclose(dev) + dev_t dev; +{ + struct uk_softc *uk = ukcd.cd_devs[UKUNIT(dev)]; + + SC_DEBUG(uk->sc_link, SDEV_DB1, ("closing\n")); + uk->sc_link->flags &= ~SDEV_OPEN; + + return 0; +} + +/* + * Perform special action on behalf of the user + * Only does generic scsi ioctls. + */ +int +ukioctl(dev, cmd, addr, flag, p) + dev_t dev; + u_long cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + register struct uk_softc *uk = ukcd.cd_devs[UKUNIT(dev)]; + + return scsi_do_ioctl(uk->sc_link, dev, cmd, addr, flag, p); +} |