--- /dev/null
+/*
+ * $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:
+ */