Imported Debian patch 2.5.0-1
[debian/amanda] / contrib / gsc / gscdd.c
diff --git a/contrib/gsc/gscdd.c b/contrib/gsc/gscdd.c
new file mode 100644 (file)
index 0000000..5e39a3a
--- /dev/null
@@ -0,0 +1,822 @@
+/*
+ * $Id: gscdd.c,v 1.1 2001/04/15 11:12:37 ant Exp $
+ * Copyright (c) 1996, 1997 by Matthew Jacob
+ *
+ *     This software is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Library General Public
+ *     License as published by the Free Software Foundation; version 2.
+ *
+ *     This software is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *     Library General Public License for more details.
+ *
+ *     You should have received a copy of the GNU Library General Public
+ *     License along with this software; if not, write to the Free
+ *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *     The author may be reached via electronic communications at
+ *
+ *             mjacob@feral.com
+ *
+ *     or, via United States Postal Address
+ *
+ *             Matthew Jacob
+ *             1831 Castro Street
+ *             San Francisco, CA, 94131
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/sysmacros.h>
+#include <sys/syspest.h>
+#include <sys/ioctl.h>
+#include <sys/i_machine.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/devinfo.h>
+#include <sys/lockl.h>
+#include <sys/device.h>
+#include <sys/uio.h>
+#include <sys/watchdog.h>
+#include <sys/errids.h>
+#include <sys/trchkid.h>
+#include <sys/priv.h>
+#include <sys/iostat.h>
+#include <sys/bootrecord.h>
+#include <sys/scsi.h>
+#include <sys/malloc.h>
+#include <sys/sleep.h>
+#include <sys/fp_io.h>
+#include <sys/pin.h>
+#include <sys/lock_alloc.h>
+#define        DD_LOCK 37
+#include "gscdds.h"
+
+static int strlen(char *s) { char *p = s; while (*p) p++; return p - s; }
+static void memset(void *x, int val, size_t amt)
+{ char *p = (char *)x; while (--amt) *p++ = (char) val; }
+static void memcpy(void *dst, void *src, size_t amt)
+ { char *dest = dst, *source = src; while (--amt) *dest++ = *source++; }
+#define        bcopy(src, dst, nbytes) memcpy(dst, src, nbytes)
+
+/*
+ * Local Definitions
+ */
+
+#define        HKWD_GSC_DD     0x66600000
+
+#define        _COM_TRACE(b, var)      \
+       var = strlen(b); trcgenk(0, HKWD_GSC_DD, var, var, b)
+
+#define        Trace0(val, str)        \
+       if (scudebug >= val) { \
+               int icxq; \
+               _COM_TRACE(str, icxq); \
+       }
+#define        Trace1(val, fmt, arg1)  \
+       if (scudebug >= val) { \
+               int icxq; char buf[256]; \
+               (void) sprintf(buf, fmt, arg1); \
+               _COM_TRACE(buf, icxq); \
+       }
+#define        Trace2(val, fmt, arg1, arg2)    \
+       if (scudebug >= val) { \
+               int icxq; char buf[256]; \
+               (void) sprintf(buf, fmt, arg1, arg2); \
+               _COM_TRACE(buf, icxq); \
+       }
+#define        Trace3(val, fmt, arg1, arg2, arg3)      \
+       if (scudebug >= val) { \
+               int icxq; char buf[256]; \
+               (void) sprintf(buf, fmt, arg1, arg2, arg3); \
+               _COM_TRACE(buf, icxq); \
+       }
+#define        Trace4(val, fmt, arg1, arg2, arg3, arg4)        \
+       if (scudebug >= val) { \
+               int icxq; char buf[256]; \
+               (void) sprintf(buf, fmt, arg1, arg2, arg3, arg4); \
+               _COM_TRACE(buf, icxq); \
+       }
+
+#define        Trace5(val, fmt, arg1, arg2, arg3, arg4, arg5)  \
+       if (scudebug >= val) { \
+               int icxq; char buf[256]; \
+               (void) sprintf(buf, fmt, arg1, arg2, arg3, arg4, arg5); \
+               _COM_TRACE(buf, icxq); \
+       }
+
+#define        Trace6(val, fmt, arg1, arg2, arg3, arg4, arg5, arg6)    \
+       if (scudebug >= val) { \
+               int icxq; char buf[256]; \
+               (void) sprintf(buf, fmt, arg1, arg2, arg3, arg4, arg5, arg6); \
+               _COM_TRACE(buf, icxq); \
+       }
+
+#define        MJ_RTN(VAL)     simple_unlock(&sp->dd_lock); return (VAL)
+
+typedef struct {
+    struct sc_buf scsibuf;
+    uint index;
+} gsc_buf_t;
+
+typedef struct {
+    Simple_lock dd_lock;
+    Simple_lock buf_lock;
+    struct file *fp;           /* file pointer */
+    gsc_buf_t cbuf;
+#define        cmdbuf cbuf.scsibuf     /* buffer for command */
+    gsc_buf_t rbuf;
+#define rqsbuf rbuf.scsibuf    /* buffer for request sense */
+    dev_t dev;                 /* Adapter dev */
+    u_char tgt;                        /* target ID */
+    u_char lun;                        /* logical unit */
+    u_char isopen;             /* device is open */
+    u_char iscfg;              /* non-zero to show this as configured */
+    u_char unstart;            /* stop device on unconfigure */
+    u_char needresume;         /* needs an SC_RESUME with next command */
+} gsc_softc_t;
+
+/*
+ * External References
+ */
+extern int copyin(void *, void *, int);
+extern int copyout(void *, void *, int);
+extern int devstrat(struct buf *);
+extern int nodev(void);
+extern void setuerror(int);
+
+
+/*
+ * Device Driver Entry Points
+ */
+int gsc_config(dev_t, int, struct uio *);
+static int gsc_open(dev_t);
+static int gsc_close(dev_t);
+static int gsc_ioctl(dev_t, int, void *, ulong);
+static void gscdd_intr(struct buf *);
+
+/*
+ * Static Data
+ */
+static int scudebug = 10;
+static int nunits = 0;
+static gsc_softc_t softinfo[MAX_UNITS] = { 0 };
+lock_t config_lock = { LOCK_AVAIL };
+
+
+/*
+ * Local Function Prototypes
+ */
+
+static int gsopen(gsc_softc_t *);
+static void gsclose(gsc_softc_t *, dev_t);
+static int gsccmd(dev_t, scmd_t *, ulong);
+static int make_rqs(gsc_softc_t *, char, char *, int, int);
+
+/*
+ * Configuration Routines
+ */
+
+int
+gsc_config(dev_t devno, int cmd, struct uio * uiop)
+{
+    struct gsc_ddsinfo ddsinfo;
+    gsc_softc_t *sp;
+    int result, i, unit;
+    extern int nodev();
+    static struct devsw gsc_dsw = {
+       gsc_open,       /* entry point for open routine */
+       gsc_close,      /* entry point for close routine */
+       nodev,          /* entry point for read routine */
+       nodev,          /* entry point for write routine */
+       gsc_ioctl,      /* entry point for ioctl routine */
+       nodev,          /* entry point for strategy routine */
+       0,              /* pointer to tty device structure */
+       nodev,          /* entry point for select routine */
+       gsc_config,     /* entry point for config routine */
+       nodev,          /* entry point for print routine */
+       nodev,          /* entry point for dump routine */
+       nodev,          /* entry point for mpx routine */
+       nodev,          /* entry point for revoke routine */
+       NULL,           /* pointer to device specific data */
+       NULL,           /* select pointer */
+       DEV_MPSAFE
+    };
+
+    if (lockl(&config_lock, LOCK_SHORT) != LOCK_SUCC) {
+       return (EINVAL);
+    }
+    unit = minor(devno);
+    if (unit < 0 || unit >= MAX_UNITS) {
+       Trace2(0, "%d: bad unit %d", __LINE__, unit);
+       result = EINVAL;
+       unlockl(&config_lock);
+       return (result);
+    }
+
+    switch (cmd) {
+    case CFG_INIT:
+       Trace2(2, "CFG_INIT: unit %d nunit %d\n", unit, nunits);
+       /*
+        * Initialize softinfo, first time around.
+        */
+       if (nunits == 0) {
+           memset(softinfo, 0, sizeof (softinfo));
+       }
+       /*
+        * Copy in DDS information
+        */
+       uiomove((caddr_t) &ddsinfo, sizeof ddsinfo, UIO_WRITE, uiop);
+       sp = &softinfo[unit];
+       if (sp->iscfg) {
+           Trace1(0, "CFG_INIT: unit %d already configd", unit);
+           result = EBUSY;
+           break;
+       }
+       lock_alloc(&sp->dd_lock, LOCK_ALLOC_PIN, DD_LOCK, -1);
+       lock_alloc(&sp->buf_lock, LOCK_ALLOC_PIN, DD_LOCK, -1);
+       simple_lock_init(&sp->dd_lock);
+       sp->dev = ddsinfo.busid;
+       sp->tgt = ddsinfo.target;
+       sp->lun = ddsinfo.lun;
+       sp->cbuf.index = sp->rbuf.index = unit;
+       /*
+        * If this is the first time through:
+        *   Add entry to the device switch table to call this driver
+        *   Pin driver code.
+        */
+       if (nunits == 0) {
+           result = devswadd(devno, &gsc_dsw);
+           if (result != 0) {
+               Trace1(0, "CFG_INIT: devswadd result: %d", result);
+               break;
+           }
+           result = pincode((int (*) ()) gscdd_intr);
+           if (result) {
+               Trace1(0, "CFG_INIT: pincode result: %d", result);
+               devswdel(devno);
+               break;
+           }
+       }
+       sp->iscfg = 1;
+       result = gsopen(sp);
+       if (result) {
+           Trace2(0, "CFG_INIT: gsopen returns %d for unit %d", result, unit);
+           sp->iscfg = 0;
+           gsclose(sp, devno);
+           break;
+       }
+       if (nunits <= unit)
+           nunits = unit + 1;
+       sp->iscfg = 1;
+       break;
+
+    case CFG_TERM:
+       Trace1(2, "CFG_TERM unit %d", unit);
+       result = 0;
+       sp = &softinfo[unit];
+       if (sp->iscfg == 0) {
+           Trace1(0, "CFG_TERM: unit %d not already configd", unit);
+           result = ENXIO;
+           break;
+       } else if (sp->isopen) {
+           Trace1(0, "CFG_TERM: unit %d open", unit);
+           result = EBUSY;
+           break;
+       }
+       sp->iscfg = 0;  /* block further actions */
+       gsclose(sp, devno);
+       break;
+
+    default:
+       result = EINVAL;
+       break;
+    }
+    unlockl(&config_lock);
+    return (result);
+}
+
+/*
+ * Validate that devno is indeed for a SCSI adapter, and set up stuff for it.
+ */
+static int
+gsopen(gsc_softc_t * sp)
+{
+    struct file *fp;
+    int r;
+    struct devinfo di;
+
+    Trace2(2, "gsopen: %d.%d", major(sp->dev), minor(sp->dev));
+    sp->fp = NULL;
+    r = fp_opendev(sp->dev, DREAD|DWRITE|DKERNEL, NULL, 0, &fp);
+    if (r) {
+       Trace3(0, "%d: fp_opendev unit %d=%d", __LINE__, sp->cbuf.index, r);
+       return (r);
+    }
+    r = fp_ioctl(fp, IOCINFO, (caddr_t) &di, NULL);
+    if (r) {
+       Trace3(0, "%d: fp_ioctl unit %d=%d", __LINE__, sp->cbuf.index, r);
+       (void) fp_close(fp);
+       return (r);
+    }
+    if (di.devtype != DD_BUS || di.devsubtype != DS_SCSI) {
+       Trace2(0, "%d: not SCSI bus on unit %d", __LINE__,  sp->cbuf.index);
+       (void) fp_close(fp);
+       return (r);
+    }
+    sp->fp = fp;
+    sp->unstart = 1;
+    if (fp_ioctl(sp->fp, SCIOSTART, (caddr_t) IDLUN(sp->tgt, sp->lun), NULL)) {
+       sp->unstart = 0;
+    }
+    return (0);
+}
+
+/*
+ * Shut down a device
+ */
+static void
+gsclose(gsc_softc_t *sp, dev_t devno)
+{
+    int i;
+    if (sp->fp != NULL && sp->unstart) {
+       (void) fp_ioctl(sp->fp, SCIOSTOP, (caddr_t) IDLUN(sp->tgt, sp->lun), NULL);
+       sp->unstart = 0;
+    }
+    if (sp->fp) {
+       (void) fp_close(sp->fp);
+       sp->fp = NULL;
+    }
+    for (i = 0; i < MAX_UNITS; i++) {
+       if (softinfo[i].iscfg) {
+           Trace1(0, "gsclose: unit %d still confd", i);
+           break;
+       }
+    }
+    if (i == MAX_UNITS) {
+       Trace0(0, "gsclose: All unconfigured now");
+       (void) devswdel(devno);
+       unpincode((int (*) ()) gscdd_intr);
+    }
+}
+
+/*
+ * VFS entry points
+ */
+
+static int
+gsc_open(dev_t devno)
+{
+    gsc_softc_t *sp;
+    int unit = minor(devno);
+
+    Trace1(2, "gsc_open: open unit %d", unit);
+    if (unit < 0 || unit >= MAX_UNITS) {
+       return (ENODEV);
+    }
+    sp = &softinfo[unit];
+    if (sp->iscfg == 0 || sp->fp == NULL) {
+       Trace2(0, "%d: bad unit (%d)", __LINE__, unit);
+       return (ENODEV);
+    }
+    simple_lock(&sp->dd_lock);
+    if (sp->isopen) {
+       simple_unlock(&sp->dd_lock);
+       return (EBUSY);
+    }
+    sp->isopen = 1;
+    simple_unlock(&sp->dd_lock);
+    return (0);
+}
+
+static int
+gsc_close(dev_t dev)
+{
+    gsc_softc_t *sp;
+    int unit = minor(dev);
+
+    Trace1(2, "gsc_close: close unit %d", unit);
+    if (unit < 0 || unit >= MAX_UNITS) {
+       return (ENODEV);
+    }
+    sp = &softinfo[unit];
+    if (sp->iscfg == 0) {
+       return (ENODEV);
+    }
+    simple_lock(&sp->dd_lock);
+    sp->isopen = 0;
+    simple_unlock(&sp->dd_lock);
+    return (0);
+}
+
+static int
+gsc_ioctl(dev_t dev, int cmd, void *arg, ulong dflag)
+{
+    switch (cmd) {
+    case GSC_CMD:
+       return (gsccmd(dev, arg, dflag));
+
+    case GSC_SETDBG:
+    {
+       int i;
+       cmd = copyin(arg, (caddr_t) &i, sizeof (int));
+       if (cmd != 0) {
+           return (cmd);
+       }
+       cmd = scudebug;
+       scudebug = i;
+       return (copyout((caddr_t) &cmd, arg, sizeof (int)));
+    }
+    default:
+       return (ENOTTY);
+    }
+}
+
+
+/****************************************************************************/
+
+static int
+gsccmd(dev_t dev, scmd_t *argcmd, ulong dflag)
+{
+    gsc_softc_t *sp;
+    scmd_t local, *l;
+    char sbyte, albits;
+    struct sc_buf *usc;
+    struct buf *Ubp;
+    int r, r2, ival, upin, unit, rqvalid, once;
+
+    unit = minor(dev);
+    Trace2(1, "%d: cmd for unit %d", __LINE__, minor(dev));
+    if (unit < 0 || unit >= MAX_UNITS) {
+       setuerror(ENXIO);
+       return (ENXIO);
+    }
+    sp = &softinfo[unit];
+    if (sp->iscfg == 0 || sp->fp == NULL) {
+       Trace2(0, "gsccmd: bad unit %d (cfg=%d)", unit, sp->iscfg);
+       r = ENODEV;
+       setuerror(r);
+       return (r);
+    }
+    simple_lock(&sp->dd_lock);
+    l = &local;
+    if (dflag & DKERNEL) {
+       l = argcmd;
+    } else {
+       r = copyin((caddr_t) argcmd, (caddr_t) l, sizeof (scmd_t));
+       if (r != 0) {
+           Trace2(0, "%d: copyin=%d", __LINE__, r);
+           setuerror(r);
+           MJ_RTN (r);
+       }
+    }
+    Trace6(1, "%d: cdblen%d datalen%d snslen%d rw=%d tv=%d", __LINE__,
+          l->cdblen, l->datalen, l->senselen, l->rw, l->timeval);
+    sbyte = 0;
+    rqvalid = upin = r = r2 = 0;
+    usc = &sp->cmdbuf;
+    Ubp = &usc->bufstruct;
+    memset(usc, 0, sizeof (struct sc_buf));
+
+    /*
+     * Check some parameters...
+     */
+
+    if (l->cdblen > sizeof (struct sc_cmd)) {
+       r = EINVAL;
+       goto out;
+    }
+
+    /*
+     * Setup sc_buf structure
+     */
+    Ubp->b_iodone = gscdd_intr;
+    Ubp->b_dev = sp->dev;
+    Ubp->b_flags = B_BUSY | B_MPSAFE;
+    Ubp->b_resid = Ubp->b_bcount = l->datalen;
+    Ubp->b_xmemd.aspace_id = XMEM_INVAL;
+    Ubp->b_event = EVENT_NULL;
+
+    if (l->datalen) {
+       Ubp->b_un.b_addr = l->data_buf;
+       if (l->rw) {
+           Ubp->b_flags |= B_READ;
+       }
+       if (dflag & DKERNEL) {
+           r = pinu(l->data_buf, l->datalen, UIO_SYSSPACE);
+       } else {
+           r = pinu(l->data_buf, l->datalen, UIO_USERSPACE);
+       }
+       if (r) {
+           Trace2(0, "%d: pinu buf %d", __LINE__, r);
+           goto out;
+       }
+       upin++;
+       if (dflag & DKERNEL) {
+           r = xmattach(l->data_buf, l->datalen, &Ubp->b_xmemd, SYS_ADSPACE);
+       } else {
+           r = xmattach(l->data_buf, l->datalen, &Ubp->b_xmemd, USER_ADSPACE);
+       }
+       if (r != XMEM_SUCC) {
+           Trace2(0, "%d: xmattach %d", __LINE__, r);
+           r = EFAULT;
+           goto out;
+       }
+       upin++;
+       r = xmemdma(&Ubp->b_xmemd, l->data_buf, XMEM_UNHIDE);
+       if (r == XMEM_FAIL) {
+           Trace2(0, "%d: xmemdma %d", __LINE__, r);
+           r = EFAULT;
+           goto out;
+       }
+       r = 0;
+    }
+    usc->scsi_command.scsi_id = sp->tgt;
+    usc->scsi_command.scsi_length = l->cdblen;
+    if (dflag & DKERNEL) {
+       bcopy(l->cdb, (caddr_t)&usc->scsi_command.scsi_cmd, l->cdblen);
+    } else {
+       r = copyin(l->cdb, (caddr_t) & usc->scsi_command.scsi_cmd, l->cdblen);
+       if (r != 0) {
+           goto out;
+       }
+    }
+    /* Setting lun in SCSI CDB as well as sc_buf structure */
+    usc->lun = sp->lun;
+    usc->scsi_command.scsi_cmd.lun &= 0x1F;
+    usc->scsi_command.scsi_cmd.lun |= (sp->lun << 5) & 0xE0;
+    albits = usc->scsi_command.scsi_cmd.lun;
+    usc->timeout_value = l->timeval;
+    if (sp->needresume) {
+       usc->flags |= SC_RESUME;
+       sp->needresume = 0;
+    }
+
+    if (scudebug > 1) {
+       char *c = (char *) &usc->scsi_command.scsi_cmd;
+       char cdbuf[64];
+       (void) sprintf(cdbuf,
+                      "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x "
+                      "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x",
+                      c[0], c[1], c[2], c[3], c[4], c[5],
+                      c[6], c[7], c[8], c[9], c[10], c[11]);
+       Trace2(0, "%d: cdb=%s", __LINE__, cdbuf);
+    }
+
+    once = 0;
+again:
+    Ubp->b_flags &= ~B_DONE;
+    r = devstrat(Ubp);
+    if (r == 0) {
+       ival = disable_lock(INTCLASS1, &sp->buf_lock);
+       while ((Ubp->b_flags & B_DONE) == 0) {
+           e_sleep_thread(&Ubp->b_event, &sp->buf_lock, LOCK_HANDLER);
+       }
+       unlock_enable(ival, &sp->buf_lock);
+    } else {
+       /*
+        * If ENXIO,  We never actually got started.
+        */
+       if (r == ENXIO && once == 0) {
+           once++;
+           usc->flags |= SC_RESUME|SC_DELAY_CMD;
+           goto again;
+       }
+       sp->needresume = 1;
+       Trace2(1, "%d: devstrat=%d", __LINE__, r);
+       goto out;
+    }
+
+    Trace4(1, "%d: b_flags %x b_error %d b_resid %d", __LINE__,
+          Ubp->b_flags, Ubp->b_error, Ubp->b_resid);
+    Trace5(1, "%d: sv %x st %x gc %x as %x", __LINE__,
+          usc->status_validity, usc->scsi_status,
+          usc->general_card_status, usc->adap_q_status);
+
+    if (Ubp->b_flags & B_ERROR) {
+       r = Ubp->b_error;
+       sp->needresume = 1;
+    }
+
+    if (usc->status_validity & SC_SCSI_ERROR) {
+       sbyte = (usc->scsi_status & SCSI_STATUS_MASK);
+       sp->needresume = 1;
+       if (sbyte == SC_CHECK_CONDITION && l->senselen) {
+           struct sc_buf *usl;
+           struct buf *Sbp;
+
+           r = make_rqs(sp, albits, l->sense_buf, l->senselen,
+                    (dflag & DKERNEL) != 0);
+           if (r) {
+               Trace2(0, "%d: make_rqs=%d", __LINE__, r);
+               goto out;
+           }
+           usl = &sp->rqsbuf;
+           Sbp = &usl->bufstruct;
+           r = devstrat(Sbp);
+           if (r == 0) {
+               ival = disable_lock(INTCLASS1, &sp->buf_lock);
+               while ((Sbp->b_flags & B_DONE) == 0) {
+                   e_sleep_thread(&Sbp->b_event, &sp->buf_lock, LOCK_HANDLER);
+               }
+               unlock_enable(ival, &sp->buf_lock);
+           } else {
+               Trace2(0, "%d:ds=%d for rqs", __LINE__, r);
+               goto out;
+           }
+           xmdetach(&Sbp->b_xmemd);
+           if (dflag & DKERNEL) {
+               (void) unpinu(l->sense_buf, l->senselen, UIO_SYSSPACE);
+           } else {
+               (void) unpinu(l->sense_buf, l->senselen, UIO_USERSPACE);
+           }
+           Trace4(1, "%d SENSE: b_flags %x b_error %d b_resid %d",
+                  __LINE__, Sbp->b_flags, Sbp->b_error,
+                  Sbp->b_resid);
+           Trace5(1, "%d: sv %x st %x gc %x as %x", __LINE__,
+                  usl->status_validity, usl->scsi_status,
+                  usl->general_card_status, usl->adap_q_status);
+           if (usl->scsi_status || usl->general_card_status) {
+               r = EIO;
+           } else {
+               rqvalid = 1;
+           }
+       }
+    }
+
+    if (usc->status_validity & SC_ADAPTER_ERROR) {
+       sp->needresume = 1;
+       Trace2(0, "%d: adapter error 0x%x", __LINE__,
+              usc->general_card_status);
+       Ubp->b_flags |= B_ERROR;
+       switch (usc->general_card_status) {
+       case SC_NO_DEVICE_RESPONSE:
+       case SC_HOST_IO_BUS_ERR:
+       case SC_SCSI_BUS_FAULT:
+       case SC_CMD_TIMEOUT:
+       case SC_ADAPTER_HDW_FAILURE:
+       case SC_ADAPTER_SFW_FAILURE:
+       case SC_FUSE_OR_TERMINAL_PWR:
+       case SC_SCSI_BUS_RESET:
+       default:
+           r = EIO;
+           break;
+       }
+    }
+
+    /*
+     * Log errors through errsave function
+     */
+    if (usc->status_validity & (SC_SCSI_ERROR|SC_ADAPTER_ERROR)) {
+       struct sc_error_log_df log;
+
+       memset(&log, 0, sizeof (log));
+       /*
+        * All errors are 'temporary unknown driver error'
+        */
+       log.error_id = ERRID_SCSI_ERR6;
+       (void) sprintf(log.resource_name, "gsc%d", unit);
+       memcpy(&log.scsi_command, &usc->scsi_command, sizeof (struct scsi));
+       log.status_validity = usc->status_validity;
+       log.scsi_status = usc->scsi_status;
+       log.general_card_status = usc->general_card_status;
+       if (rqvalid) {
+           int amt;
+           if (l->senselen > 128)
+               amt = 128;
+           else
+               amt = l->senselen;
+           (void) copyin(l->sense_buf, log.req_sense_data, amt);
+       }
+       errsave(&log, sizeof (struct sc_error_log_df));
+    }
+
+    if (dflag & DKERNEL) {
+       *l->statusp = sbyte;
+    } else {
+       r2 = copyout(&sbyte, l->statusp, 1);
+       if (r2 != 0) {
+           if (r == 0)
+               r = r2;
+           goto out;
+       }
+    }
+out:
+    if (l->datalen) {
+       if (upin > 1) {
+           xmdetach(&Ubp->b_xmemd);
+           upin--;
+       }
+       if (upin > 0) {
+           if (dflag & DKERNEL) {
+               (void) unpinu(l->data_buf, l->datalen, UIO_SYSSPACE);
+           } else {
+               (void) unpinu(l->data_buf, l->datalen, UIO_USERSPACE);
+           }
+           upin--;
+       }
+    }
+    Trace2(1, "%d: returning %d", __LINE__, r);
+    if (r)
+       setuerror(r);
+    MJ_RTN (r);
+}
+
+static int
+make_rqs(gsc_softc_t * sp, char albits, char *uaddr, int ulen, int isk)
+{
+    struct sc_buf *usl;
+    struct buf *Sbp;
+    int err, upin;
+
+    if (ulen > 255)
+       ulen = 255;
+    upin = err = 0;
+    usl = &sp->rqsbuf;
+    Sbp = &usl->bufstruct;
+    memset(usl, 0, sizeof (struct sc_buf));
+
+    Sbp->b_un.b_addr = uaddr;
+    Sbp->b_resid = Sbp->b_bcount = ulen;
+    Sbp->b_iodone = gscdd_intr;
+    Sbp->b_dev = sp->dev;
+    Sbp->b_flags = B_BUSY | B_READ | B_MPSAFE;
+    Sbp->b_event = EVENT_NULL;
+    Sbp->b_xmemd.aspace_id = XMEM_INVAL;
+
+    if (isk)
+       err = pinu(uaddr, ulen, UIO_SYSSPACE);
+    else
+       err = pinu(uaddr, ulen, UIO_USERSPACE);
+    if (err)
+       goto out;
+    upin++;
+    if (isk)
+       err = xmattach(uaddr, ulen, &Sbp->b_xmemd, SYS_ADSPACE);
+    else
+       err = xmattach(uaddr, ulen, &Sbp->b_xmemd, USER_ADSPACE);
+    if (err != XMEM_SUCC) {
+       err = EFAULT;
+       goto out;
+    }
+    upin++;
+    err = xmemdma(&Sbp->b_xmemd, Sbp->b_un.b_addr, XMEM_UNHIDE);
+    if (err == XMEM_FAIL) {
+       err = EFAULT;
+       (void) xmdetach(&Sbp->b_xmemd);
+       goto out;
+    }
+    err = 0;
+
+    usl->lun = sp->lun; /* Setting lun in sc_buf structure */
+    usl->scsi_command.scsi_id = sp->tgt;
+    usl->scsi_command.scsi_length = 6;
+    usl->scsi_command.scsi_cmd.scsi_op_code = 0x3;
+    usl->scsi_command.scsi_cmd.lun = albits & 0xE0; /*ONLY copy the lun bits*/
+    usl->scsi_command.scsi_cmd.scsi_bytes[2] = ulen;
+    usl->timeout_value = 2;
+    usl->flags = SC_RESUME;
+    sp->needresume = 0;
+
+ out:
+    if (err) {
+       if (upin > 1) {
+           xmdetach(&Sbp->b_xmemd);
+           upin--;
+       }
+       if (upin > 0) {
+           if (isk)
+               (void) unpinu(uaddr, ulen, UIO_SYSSPACE);
+           else
+               (void) unpinu(uaddr, ulen, UIO_USERSPACE);
+           upin--;
+       }
+    }
+    return (err);
+}
+
+void
+gscdd_intr(struct buf * bp)
+{
+    int lv;
+    gsc_softc_t *sp = &softinfo[((gsc_buf_t *)bp)->index];
+
+    lv = disable_lock(INTIODONE, &sp->buf_lock);
+    bp->b_flags |= B_DONE;
+    unlock_enable(lv, &sp->buf_lock);
+    e_wakeup(&bp->b_event);
+}
+/*
+ * mode: c
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * End:
+ */