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/scsi_ioctl.c |
initial import of NetBSD tree
Diffstat (limited to 'sys/scsi/scsi_ioctl.c')
-rw-r--r-- | sys/scsi/scsi_ioctl.c | 361 |
1 files changed, 361 insertions, 0 deletions
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 +} |