386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Tue, 14 Jul 1992 04:34:06 +0000 (20:34 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Tue, 14 Jul 1992 04:34:06 +0000 (20:34 -0800)
Work on file usr/src/sys.386bsd/i386/isa/as.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

usr/src/sys.386bsd/i386/isa/as.c [new file with mode: 0644]

diff --git a/usr/src/sys.386bsd/i386/isa/as.c b/usr/src/sys.386bsd/i386/isa/as.c
new file mode 100644 (file)
index 0000000..884854e
--- /dev/null
@@ -0,0 +1,1326 @@
+/*
+ * Adaptech 1542 SCSI driver for 386bsd
+ *
+ * Pace Willisson     pace@blitz.com    March 28, 1992
+ *
+ * Placed in the public domain with NO WARRANTIES, not even the
+ * implied warranties for MERCHANTABILITY or FITNESS FOR A 
+ * PARTICULAR PURPOSE.
+ *
+ *
+ * This is a very early version - use with care.
+ *
+ * Here is the config info:
+ *
+ * controller  as0     at isa? port 0x330 bio irq 11 drq 5 vector asintr
+ * disk                dk6     at as0 drive 0 
+ *
+ * Also, don't forget to update sys/i386/conf/files.i386.
+ *
+ * So far, used with:
+ *
+ *   CDC WREN 5 600 Megabyte magnetic disk
+ *   EXABYTE EXB-8200 8mm tape drive
+ *   SONY CDU-541 cdrom
+ *
+ * The the tape stuff still needs a lot of working concerning
+ * file marks, end of tape handling and rewinding, but I have
+ * extracted tar tapes to a file system mounted on the CDC disk.
+ *
+ * minor number bits:
+ *
+ *      7  6  5  4  3  2  1  0
+ *                     +-----+  partition number
+ *            +-----+           scsi target number
+ *      +--+                    unused (should be 0)
+ *
+ * For tape drives, set the partition number to 0 for regular,
+ * 1 for no rewind.
+ *
+ * Only supports LUN 0.
+ *
+ * To use with a read-write disk, first use diskpart to create
+ * a disktab entry, then use disklabel.  Since I don't have
+ * the boot programs done yet, I faked it with:
+ *
+ *             # cd /usr/mdec
+ *             # cp wdboot asboot
+ *             # cp bootwd bootas
+ *
+ * Now you can run disklabel, newfs, etc.
+ *
+ * Please send patches and names other perpherials that work to
+ * pace@blitz.com.  If you have trouble that you can't fix, please
+ * wait for the next release before contacting me.
+ */
+
+#include "as.h"
+#if NAS > 0
+
+#include "param.h"
+#include "dkbad.h"
+#include "systm.h"
+#include "conf.h"
+#include "file.h"
+#include "stat.h"
+#include "ioctl.h"
+#include "disklabel.h"
+#include "buf.h"
+#include "uio.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/icu.h"
+#include "syslog.h"
+#include "vm/vm.h"
+#include "kernel.h"
+
+#include "asreg.h"
+
+int asstrategy ();
+int asabort ();
+extern int hz;
+
+int asverbose = 0;
+
+
+/* target id 7 is the controller itself */
+#define NTARGETS 7
+
+struct mailbox_entry mailboxes[NTARGETS * 2] = {0};
+
+#define b_cylin        b_resid /* fake cylinder number for disksort */
+
+/* maximum scatter list size for Adaptech controller */
+#define NSCATTER 17
+
+/* this array must reside in contiguous physical memory */
+struct asinfo {
+       dev_t dev;
+       struct buf requests;
+       struct mailbox_entry *mailbox;
+       int active;
+       struct ccb ccb;
+       unsigned int ccb_phys;
+       char scatter_list[NSCATTER * 6];
+
+       struct disklabel label;
+       struct dos_partition dospart[NDOSPART];
+       int have_label;
+
+       int scsi_lock;
+       struct buf *scsi_bp;
+       int scsi_cdb_len;
+       unsigned char scsi_cdb[MAXCDB];
+
+       int tape;               /* sequential */
+       int disk;               /* nonsequential */
+       int read_only;          /* CDROM */
+       int removable;          /* unsupported and tested */
+       char vendor[9];
+       char model[17];
+       char revision[5];
+       int bs;                 /* device block size */
+
+       int open_lock;
+       int open;
+       int units_open;
+
+       int wlabel;
+
+       int retry_count;
+       int start_time;
+       int restart_pending;
+
+} asinfo[NTARGETS] = {0};
+
+#define dev_part(dev) (minor (dev) & 7)
+#define dev_target(dev) ((minor (dev) >> 3) & 7)
+#define dev_rewind(dev) ((minor (dev) & 1) == 0)
+
+#define makeasdev(major, target, part) \
+       makedev ((major), ((target) << 3) | (part))
+
+int as_port;
+
+int    asprobe(struct isa_device *), asattach(struct isa_device *),
+       asintr(dev_t);
+
+struct isa_driver asdriver = {
+       asprobe, asattach, "as",
+};
+
+int
+asprobe (struct isa_device *dvp)
+{
+       int val;
+
+       as_port = dvp->id_iobase;
+
+       outb (as_port + AS_CONTROL, AS_CONTROL_SRST);
+       DELAY (30000);
+       val = inb (as_port + AS_STATUS);
+
+       if (val == (AS_STATUS_INIT | AS_STATUS_IDLE))
+               return (1);
+       as_port = 0;
+       return (0);
+}
+
+asattach (struct isa_device *dvp)
+{
+       int i;
+       unsigned int physaddr;
+       int val;
+       int s;
+
+       for (i = 0; i < NTARGETS; i++) {
+               asinfo[i].mailbox = &mailboxes[i];
+               asinfo[i].ccb_phys = vtophys (&asinfo[i].ccb);
+       }
+
+       isa_dmacascade(dvp->id_drq);
+       
+       physaddr = vtophys (mailboxes);
+       
+       s = splbio ();
+       if (as_put_byte (AS_CMD_MAILBOX_INIT) < 0
+           || as_put_byte (NTARGETS) < 0
+           || as_put_byte (physaddr >> 16) < 0
+           || as_put_byte (physaddr >> 8) < 0
+           || as_put_byte (physaddr) < 0) {
+               splx (s);
+               return (EIO);
+       }
+       splx (s);
+       DELAY (300);
+       val = inb (as_port + AS_STATUS);
+
+       if (val & AS_STATUS_INIT)
+               printf ("as: mailbox init error: 0x%x\n", val);
+}
+
+int
+ascmd (as, bp, direction, count, retrycount)
+struct asinfo *as;
+struct buf *bp;
+int direction;
+int count;
+int retrycount;
+{
+       int err;
+
+       do {
+               if (asverbose)
+                       printf ("ascmd ");
+               bp->b_bcount = count;
+               bp->b_error = 0;
+               bp->b_flags &= ~(B_READ | B_ERROR | B_DONE);
+               if (direction == B_READ)
+                       bp->b_flags |= B_READ;
+
+               bp->b_dev = as->dev;
+               bp->b_blkno = 0;
+
+               as->scsi_bp = bp;
+               /* scsi_cdb, scsi_cdb_len set up by caller */
+
+               asstrategy (bp);
+               err = biowait (bp);
+               as->scsi_bp = NULL;
+               
+       } while (err && --retrycount);
+
+       return (err);
+}
+
+asstring (dest, src, size)
+char *dest;
+char *src;
+int size;
+{
+       size--;
+       bcopy (src, dest, size);
+       while (size > 0 && dest[size - 1] == ' ')
+               size--;
+       dest[size] = 0;
+}
+
+asopen (dev, flag)
+dev_t dev;
+int flag;
+{
+       struct asinfo *as;
+       unsigned int physaddr;
+       struct buf *bp = NULL;
+       int retry;
+       unsigned char *cdb;
+       char *p, *q;
+       int n;
+       int error;
+       char vendor[9];
+       char model[17];
+       int disksize;
+
+       if (as_port == 0 || dev_target (dev) >= NTARGETS)
+               return (ENXIO);
+
+       as = &asinfo[dev_target (dev)];
+       as->dev = dev;
+
+       while (as->open_lock)
+               if (error = tsleep ((caddr_t)as, PZERO|PCATCH, "scsiopen", 0))
+                       return (error);
+
+       if (as->open) {
+               if (as->tape)
+                       return (EBUSY);
+
+               if (as->have_label == 0 && dev_part (dev) != 3)
+                       return (ENXIO);
+               
+               as->units_open |= 1 << dev_part (dev);
+               return (0);
+       }
+
+       as->open_lock = 1;
+
+       /* it seems like we might have to block here in case someone
+        * opens the device just after someone else closes
+        */
+       while (as->scsi_lock)
+               if (error = tsleep ((caddr_t)as, PZERO|PCATCH, "scsicmd", 0))
+                       return (error);
+
+       as->scsi_lock = 1;
+
+       error = EIO;
+
+       as->have_label = 0;
+       as->tape = 0;
+       as->disk = 0;
+       as->read_only = 0;
+       as->removable = 0;
+       bcopy(as->vendor, vendor, sizeof(vendor));
+       bcopy(as->model, model, sizeof(model));
+       as->vendor[0] = 0;
+       as->model[0] = 0;
+       as->revision[0] = 0;
+
+       bp = geteblk (DEV_BSIZE);
+
+       if (asverbose) {
+               printf ("openbuf = 0x%x phys 0x%x\n",
+                       bp->b_un.b_addr, vtophys (bp->b_un.b_addr));
+               printf ("mailboxes = 0x%x\n", mailboxes);
+       }
+
+       /* first, find out if a device is present, and just what it is */
+       as->scsi_cdb_len = 6;
+       cdb = as->scsi_cdb;
+       bzero (cdb, 6);
+       cdb[0] = 0x12; /* INQUIRY */
+       cdb[4] = 255; /* allocation length */
+       if (error = ascmd (as, bp, B_READ, DEV_BSIZE, 2))
+               /* does not respond to inquiry, obviously not CCS, give up */
+               goto done;
+
+       
+       /* blather on console about it */
+       p = bp->b_un.b_addr;
+       if (asverbose) {
+               printf ("inquiry: ");
+               for (n = 0; n < 20; n++)
+                       printf ("%x ", p[n] & 0xff);
+               printf ("\n");
+               for (n = 0; n < 40; n++) {
+                       if (p[n] >= ' ' && p[n] < 0177)
+                               printf ("%c", p[n]);
+                       else
+                               printf (".");
+               }
+               printf ("\n");
+       }
+
+       switch (p[0]) {
+       case 0: /* normal disk */
+       case 4: /* write once disk */
+               as->disk = 1;
+               break;
+       case 5: /* read only disk */
+               as->read_only = 1;
+               as->disk = 1;
+               break;
+       case 1: /* tape */
+               as->tape = 1;
+               break;
+       case 0x7f:
+               printf ("logical unit not present\n");
+               goto done;
+       default:
+               printf ("unknown peripheral device type: 0x%x\n", p[0]);
+               goto done;
+       }
+
+       as->removable = (p[1] & 0x80) ? 1 : 0;
+
+       n = p[4] & 0xff;
+       if (n >= 31) {
+               asstring (as->vendor, p + 8, sizeof as->vendor);
+               asstring (as->model, p + 16, sizeof as->model);
+               asstring (as->revision, p + 32, sizeof as->revision);
+       }
+
+       if(bcmp(as->vendor,vendor, sizeof(vendor)) != 0 ||
+               bcmp(as->model,model, sizeof(model)) != 0) {
+               printf("as%d: attached tgt %d <%s %s %s> ",  0, dev_target(dev),
+                       as->vendor, as->model, as->revision);
+               if (as->read_only) printf("readonly ");
+               if (!as->removable) printf("winchester ");
+               if (as->tape) printf("tape ");
+               if (as->disk) printf("disk ");
+               printf("\n");
+       }
+
+       /* probe for desired block size */
+
+       /* assume default of 512, except if CDROM (2048) */
+       if (as->read_only)
+               as->bs = 2048;
+       else
+               as->bs = 512;
+
+       bzero(cdb, 6);
+       cdb[0] = 0x1A;  /* SCSI_MDSENSE */
+       cdb[4] = 255;
+       if (as->tape && ascmd (as, bp, B_READ, 12, 2) == 0)  {
+               int minblk, maxblk;
+
+#ifdef notdef
+               /* blather about device more */
+               if(bcmp(as->vendor,vendor, sizeof(vendor)) != 0 ||
+                       bcmp(as->model,model, sizeof(model)) != 0) {
+                       p = bp->b_un.b_addr;
+                       printf("as%d: data len %d medium %d speed/bufmode 0x%x desc len %d\n",
+                               dev_target(dev), p[0], p[1], p[2], p[3]);
+                       printf("as%d: density %d nblocks %d block len %d\n",
+                               dev_target(dev), p[4],
+                               (long)p[5]*65536+p[6]*256+p[7],
+                               (long)p[9]*65536+p[10]*256+p[11]);
+               }
+#endif
+               
+               /* obtain possible block sizes */
+               bzero(cdb, 6);
+               cdb[0] = 0x05; /* SCSI_RDLIMITS; */
+               if (ascmd (as, bp, B_READ, 12, 2) == 0) {
+                       p = bp->b_un.b_addr;
+                       minblk = p[4]*256+p[5];
+                       maxblk = p[1]*65536+p[2]*256+p[3];
+#ifdef notdef
+                       if(bcmp(as->vendor,vendor, sizeof(vendor)) != 0 ||
+                               bcmp(as->model,model, sizeof(model)) != 0) {
+                               printf("as%d: limits: min block len %ld  max block len %ld\n",
+                                       dev_target(dev), minblk, maxblk);
+                       }
+#endif
+                       if ( minblk == maxblk )
+                               as->bs = minblk;
+                       else if (as->tape)
+                               as->bs = 1;
+               }
+       }
+       
+       if (as->tape &&  dev_part(dev)) {
+               error = EIO;
+               goto done;
+       }
+
+       as->scsi_cdb_len = 10;
+       bzero(cdb, 10);
+       cdb[0] = 0x25;  /* SCSI_READCAPACITY */
+       disksize = 0;
+       if (as->disk && ascmd (as, bp, B_READ, 12, 2) == 0)  {
+               p = bp->b_un.b_addr;
+               disksize = ntohl(*(long *)p);
+               as->bs = ntohl(*(long *)(p+4));
+               
+       }
+
+if(asverbose)
+       printf("block size %d disksize %d ", as->bs, disksize);
+
+
+       /* for standard disk, negotiate block size */
+       if (as->read_only == 0 && as->disk) {
+               /* do mode select to set the logical block size */
+               as->scsi_cdb_len = 6;
+               cdb = as->scsi_cdb;
+               bzero (cdb, 6);
+               cdb[0] = 0x15; /* MODE SELECT */
+               cdb[4] = 12; /* parameter list length */
+       
+               p = bp->b_un.b_addr;
+               bzero (p, 12);
+               p[3] = 8; /* block descriptor length */
+               n = as->bs == 1 ? 0 : as->bs;
+               p[9] = n >> 16;
+               p[10] = n >> 8;
+               p[11] = n;
+       
+               (void) ascmd (as, bp, B_WRITE, 12, 2);
+       }
+
+       /* device online and ready? */
+       as->scsi_cdb_len = 6;
+       bzero(cdb, 6);
+       cdb[0] = 0x00;  /* SCSI_UNITRDY */
+       if (error = ascmd (as, bp, B_READ, 12, 2)) {
+               printf("as%d: drive not online\n", dev_target(dev));
+               goto done;
+       }
+
+       if (as->disk && as->read_only == 0) {
+               /* read disk label */
+               bzero ((caddr_t)&as->label, sizeof as->label);
+               as->label.d_secsize = as->bs;
+               as->label.d_secpercyl = 64*32;
+               as->label.d_type = DTYPE_SCSI;
+
+               
+               /* read label using "d" partition */
+               if ((p = readdisklabel (
+                       makeasdev (major (dev), dev_target (dev), 3),
+                       asstrategy, &as->label, as->dospart, 0, 0)) == NULL){
+                       as->have_label = 1;
+               } else {
+                       if (disksize) {
+                               as->label.d_subtype = DSTYPE_GEOMETRY;
+                               as->label.d_npartitions = 3;
+                               /* partition 0  holds bios, partition 1 ESDI */
+                               as->label.d_partitions[2].p_size = disksize;
+                               as->label.d_partitions[2].p_offset = 0;
+                       }
+                       if (asverbose || dev_part (dev) != 3)
+                               printf ("error reading label: %s\n", p);
+                       if (dev_part (dev) != 3) {
+                               error = EINVAL;
+                               goto done;
+                       }
+               }
+       }
+
+       /* may want to set logical block size here ? */
+       error = 0;
+
+ done:
+       if (bp) {
+               bp->b_flags |= B_INVAL | B_AGE;
+               brelse (bp);
+       }
+
+       if (error == 0)
+               as->open = 1;
+
+       as->open_lock = 0;
+       as->scsi_lock = 0;
+       wakeup (as);
+
+       return (error);
+}
+
+asclose (dev, flag)
+dev_t dev;
+{
+       struct asinfo *as;
+       int error = 0;
+       unsigned char *cdb;
+       struct buf *bp;
+       int n;
+
+       as = &asinfo[dev_target (dev)];
+
+       while (as->open_lock)
+               if (error = tsleep ((caddr_t)as, PZERO|PCATCH, "scsiclose", 0))
+                       return (error);
+
+       as->open_lock = 1;
+
+       if (as->tape) {
+               while (as->scsi_lock)
+                       if (error = tsleep ((caddr_t)as, PZERO|PCATCH,
+                                           "scsicmd", 0))
+                               return (error);
+
+               as->scsi_lock = 1;
+               
+               bp = geteblk (DEV_BSIZE);
+
+             if (0 && (flag & FWRITE) != 0) {
+                             /* presume user will use tape again */
+                     as->scsi_cdb_len = 6;
+                     cdb = as->scsi_cdb;
+                     bzero (cdb, 6);
+                     cdb[0] = 0x10; /* write filemarks */
+                     cdb[4] = 1; /* one of them */
+                     error = ascmd (as, bp, B_READ, 0, 1);
+             }
+             if (dev_rewind (dev) || error) {
+                     if ( error == 0 && (flag & FWRITE) != 0) {
+                                     /* presumption error correction */
+                             as->scsi_cdb_len = 6;
+                             cdb = as->scsi_cdb;
+                             bzero (cdb, 6);
+                             cdb[0] = 0x10; /* write filemarks */
+                             cdb[4] = 1; /* one of them */
+                             error |= ascmd (as, bp, B_READ, 0, 1);
+                     }
+                     as->scsi_cdb_len = 6;
+                     cdb = as->scsi_cdb;
+                     bzero (cdb, 6);
+                     cdb[0] = 0x1; /* rewind */
+                     cdb[1] = 1; /* don't wait until done */
+                     error |= ascmd (as, bp, B_READ, 0, 1);
+             }
+#ifdef notdef
+               } else {
+                       cdb[0] = 0x11; /* backspace */
+                       cdb[1] = 1; /* look at filemarks (instead of blocks) */
+                       n = -1;
+                       cdb[2] = n >> 16;
+                       cdb[3] = n >> 8;
+                       cdb[4] = n;
+                       error = ascmd (as, bp, B_READ, 0, 1);
+               } 
+#endif
+
+               bp->b_flags |= B_INVAL | B_AGE;
+               brelse (bp);
+
+               as->scsi_lock = 0;
+       }
+
+       as->units_open &= ~(1 << dev_part (dev));
+
+       if (as->units_open == 0)
+               as->open = 0;
+
+       as->open_lock = 0;
+
+       wakeup (as);
+
+       return (error);
+}
+
+int
+asioctl (dev, cmd, addr, flag)
+dev_t dev;
+int cmd;
+caddr_t addr;
+int flag;
+{
+       struct scsicmd *cmdp;
+       struct asinfo *as;
+       int ccblen;
+       struct buf *bp;
+       int error = 0;
+       int direction;
+       struct disklabel *dl;
+       int old_wlabel;
+
+       as = &asinfo[dev_target (dev)];
+
+       switch (cmd) {
+       case DIOCGDINFO:
+               *(struct disklabel *)addr = as->label;
+               break;
+
+        case DIOCSDINFO:
+                if ((flag & FWRITE) == 0) {
+                        error = EBADF;
+                       break;
+               }
+               dl = (struct disklabel *)addr;
+               if (error = setdisklabel(&as->label, dl, 0, as->dospart))
+                       break;
+               as->have_label = 1;
+                break;
+
+        case DIOCWLABEL:
+                if ((flag & FWRITE) == 0) {
+                        error = EBADF;
+                       break;
+               }
+               as->wlabel = *(int *)addr;
+                break;
+
+        case DIOCWDINFO:
+                if ((flag & FWRITE) == 0) {
+                        error = EBADF;
+                       break;
+               }
+
+               dl = (struct disklabel *)addr;
+
+               if (error = setdisklabel (&as->label, dl, 0, as->dospart))
+                       break;
+
+               as->have_label = 1;
+
+               old_wlabel = as->wlabel;
+               as->wlabel = 1;
+               error = writedisklabel(dev, asstrategy, &as->label,
+                               as->dospart);
+               as->wlabel = old_wlabel;
+                break;
+
+       case SCSICMD:
+               cmdp = (struct scsicmd *)addr;
+
+               /* limited by max sizeof of geteblk */
+               if (cmdp->datalen >= 8192 
+                   || cmdp->cdblen >= MAXCDB) {
+                       error = EINVAL;
+                       break;
+               }
+
+               ccblen = cmdp->ccblen;
+               if (ccblen > sizeof (struct ccb))
+                       ccblen = sizeof (struct ccb);
+
+               while (as->scsi_lock)
+                       if (error = tsleep ((caddr_t)as, PZERO|PCATCH,
+                                           "scsicmd", 0))
+                               break;
+
+               as->scsi_lock = 1;
+                       
+               bp = geteblk (cmdp->datalen);
+
+               as->scsi_cdb_len = cmdp->cdblen;
+               if (error = copyin (cmdp->cdb, as->scsi_cdb, cmdp->cdblen))
+                       goto done;
+               
+               direction = cmdp->readflag ? B_READ : B_WRITE;
+
+               if (direction == B_WRITE)
+                       if (error = copyin (cmdp->data,
+                                           bp->b_un.b_addr, cmdp->datalen))
+                               goto done;
+               
+               ascmd (as, bp, direction, cmdp->datalen, 1);
+
+               copyout (&as->ccb, cmdp->ccb, ccblen);
+               if (direction == B_READ)
+                       copyout (bp->b_un.b_addr, cmdp->data, cmdp->datalen);
+       done:
+               bp->b_flags |= B_INVAL | B_AGE;
+               brelse (bp);
+               as->scsi_lock = 0;
+               wakeup (as);
+               break;
+       default:
+               error = ENOTTY;
+               break;
+       }
+       return (error);
+}
+
+int
+asstrategy (bp)
+struct buf *bp;
+{
+       struct asinfo *as;
+       int s;
+
+       if (asverbose)
+               printf ("asstrategy %d %d ", bp->b_blkno, bp->b_bcount);
+       s = splbio ();
+
+       as = &asinfo[dev_target (bp->b_dev)];
+       
+       if (as->tape) {
+               bp->av_forw = NULL;
+               if (as->requests.b_actf)
+                       as->requests.b_actl->av_forw = bp;
+               else
+                       as->requests.b_actf = bp;
+               as->requests.b_actl = bp;
+       } else {
+               if (bp != as->scsi_bp
+                   && as->have_label == 0
+                   && dev_part (bp->b_dev) != 3)
+                       goto bad;
+
+               bp->b_cylin = bp->b_blkno;
+               disksort (&as->requests, bp);
+       }
+       
+       if (as->active == 0)
+               asstart (as);
+
+       splx (s);
+       return;
+
+ bad:
+       bp->b_flags |= B_ERROR;
+       biodone (bp);
+}
+
+asrestart (as)
+struct asinfo *as;
+{
+       int s;
+       s = splbio ();
+       as->restart_pending = 0;
+       as->retry_count++;
+       asstart (as);
+       splx (s);
+}
+
+asstart (as)
+struct asinfo *as;
+{
+       struct buf *bp;
+       int blknum;
+       unsigned int physaddr;
+       struct ccb *ccb;
+       unsigned char *cdb;
+       int target;
+       char *p;
+       int n;
+       char *sp;
+       int nscatter;
+       int thistime;
+       int nbytes;
+       struct partition *part;
+       int blkno;
+       int nblocks;
+       int total;
+       int bs = as->bs;
+
+
+       if (as->restart_pending) {
+               as->restart_pending = 0;
+               untimeout (asrestart, as);
+       }
+
+ again:
+       if ((bp = as->requests.b_actf) == NULL)
+               return;
+
+       bp->b_error = 0;
+
+       if (asverbose)
+               printf ("asstart %x ", bp);
+
+       if (as->mailbox->cmd != 0) {
+               /* this can't happen, unless the card flakes */
+               printf ("asstart: mailbox not available\n");
+               bp->b_error = EIO;
+               goto bad;
+       }
+
+       if (as->retry_count == 0) {
+               as->start_time = time.tv_sec;
+       } else {
+               if (time.tv_sec - as->start_time > 60) {
+                       printf ("as: command timed out\n");
+                       bp->b_error = EIO;
+                       goto done;
+               }
+       }
+
+       if (bp != as->scsi_bp) {
+               if (bp->b_bcount == 0)
+                       goto done;
+
+               if ((bp->b_bcount % bs) != 0) {
+                       printf("as: partial block read\n");
+                       bp->b_error = EIO;
+                       goto bad;
+               }
+       }
+
+       if (bp != as->scsi_bp) {
+
+               blkno = bp->b_blkno;
+               nblocks = bp->b_bcount / bs;
+
+               if (as->have_label && dev_part(bp->b_dev) != 3) {
+                       part = &as->label.d_partitions[dev_part (bp->b_dev)];
+                       
+                       if (blkno > part->p_size) {
+                               bp->b_error = EINVAL;
+                               goto bad;
+                       }
+                       if (blkno == part->p_size) {
+                               bp->b_resid = bp->b_bcount;
+                               goto done;
+                       }
+                       
+                       if (blkno + nblocks >= part->p_size)
+                               nblocks = part->p_size - blkno;
+                       
+                       blkno += part->p_offset;
+               } else
+                       blkno = (blkno * DEV_BSIZE)/bs;
+if(asverbose)
+       printf("trans %d ", blkno);
+               if (nblocks > 255)
+                       nblocks = 255;
+               total = nblocks * bs;
+if(asverbose)
+printf("total %d nblocks %d ", total, nblocks);
+               /*bp->b_bcount = total; /* XXX partial tape block read - wrong */
+       } else {
+#ifdef nomore
+               if (as->fixed == 0) {
+                       total = bp->b_bcount;
+               } else {
+                       total = bp->b_bcount;
+                       blkno = bp->b_blkno;
+                       nblocks = bp->b_bcount / as->fixed;
+               }
+#else
+               total = bp->b_bcount;
+#endif
+       }
+
+       p = bp->b_un.b_addr;
+       n = 0;
+       sp = as->scatter_list;
+       nscatter = 0;
+       while (n < total && nscatter < NSCATTER) {
+               thistime = page_size - ((vm_offset_t)p - trunc_page (p));
+
+               if (n + thistime > total)
+                       thistime = total - n;
+
+               physaddr = vtophys (p);
+               
+               if (asverbose)
+                       printf ("%d bytes to %x (%x)\n",
+                               thistime, p, physaddr);
+               sp[0] = thistime >> 16;
+               sp[1] = thistime >> 8;
+               sp[2] = thistime;
+               sp[3] = physaddr >> 16;
+               sp[4] = physaddr >> 8;
+               sp[5] = physaddr;
+               
+               p += thistime;
+               n += thistime;
+               sp += 6;
+               nscatter++;
+       }
+       
+       if (nscatter == NSCATTER) {
+               printf("out of range, cannot happen?");
+               bp->b_error = ENXIO;
+               goto bad;
+       }
+
+       ccb = &as->ccb;
+
+       /* this only needed to make debugging easier */
+       bzero ((caddr_t)ccb, sizeof *ccb); 
+
+       if (nscatter)
+               ccb->ccb_opcode = 4; /* scatter cmd, return resid */
+       else
+               ccb->ccb_opcode = 3;
+       target = dev_target (bp->b_dev);
+       ccb->ccb_addr_and_control = target << 5;
+       if (bp->b_bcount != 0)
+               ccb->ccb_addr_and_control |= (bp->b_flags & B_READ) ? 8 : 0x10;
+       else
+               ccb->ccb_addr_and_control |= 0x18;
+
+       nbytes = nscatter * 6;
+       ccb->ccb_data_len_msb = nbytes >> 16;
+       ccb->ccb_data_len_mid = nbytes >> 8;
+       ccb->ccb_data_len_lsb = nbytes;
+
+       ccb->ccb_requst_sense_allocation_len = MAXSENSE;
+
+       physaddr = vtophys (as->scatter_list);
+       ccb->ccb_data_ptr_msb = physaddr >> 16;
+       ccb->ccb_data_ptr_mid = physaddr >> 8;
+       ccb->ccb_data_ptr_lsb = physaddr;
+
+       ccb->ccb_link_msb = 0;
+       ccb->ccb_link_mid = 0;
+       ccb->ccb_link_lsb = 0;
+       ccb->ccb_link_id = 0;
+       ccb->ccb_host_status = 0;
+       ccb->ccb_target_status = 0;
+       ccb->ccb_zero1 = 0;
+       ccb->ccb_zero2 = 0;
+
+       cdb = ccb->ccb_cdb;
+       if (bp == as->scsi_bp) {
+               ccb->ccb_scsi_command_len = as->scsi_cdb_len;
+               bcopy (as->scsi_cdb, cdb, as->scsi_cdb_len);
+       } else if (as->tape) {
+               ccb->ccb_scsi_command_len = 6;
+               cdb[0] = (bp->b_flags & B_READ) ? 8 : 0xa;
+               if (as->bs == 1) {
+                       cdb[1] = 0; /* logical unit 0, variable block size */
+                       cdb[2] = bp->b_bcount >> 16;
+                       cdb[3] = bp->b_bcount >> 8;
+                       cdb[4] = bp->b_bcount;
+               } else {
+                       cdb[1] = 1;     /* fixed block size */
+                       cdb[2] = nblocks >> 16;
+                       cdb[3] = nblocks >> 8;
+                       cdb[4] = nblocks;
+               }
+               cdb[5] = 0; /* control byte (used in linking) */
+       } else {
+               ccb->ccb_scsi_command_len = 10;
+               cdb[0] = (bp->b_flags & B_READ) ? 0x28 : 0x2a;
+               cdb[1] = 0;
+               *(long *) (cdb+2) = htonl(blkno);
+               *(short *) (cdb+7) = htons(nblocks);
+               cdb[9] = 0; /* control byte (used in linking) */
+       }
+
+#ifdef notdef
+       if (asverbose) {
+               printf ("ccb: ");
+               for (n = 0; n < 48; n++)
+                       printf ("%02x ", ((unsigned char *)ccb)[n]);
+               printf ("\n");
+       }
+#endif
+
+       physaddr = vtophys (ccb);
+       as->mailbox->msb = physaddr >> 16;
+       as->mailbox->mid = physaddr >> 8;
+       as->mailbox->lsb = physaddr;
+       as->mailbox->cmd = 1;
+       
+       /* tell controller to look in its mailbox */
+       as_put_byte (AS_CMD_START_SCSI_COMMAND);
+       as->active = 1;
+       timeout (asabort, as, hz * 60 * 2);
+       return;
+
+ bad:
+       bp->b_flags |= B_ERROR;
+ done:
+       asdone (as, 0);
+       goto again;
+}
+
+asabort (as)
+struct asinfo *as;
+{
+       int s;
+       int physaddr;
+       struct buf *bp;
+
+       s = splbio ();
+       if (as->active) {
+               printf ("asabort %d\n", as - asinfo);
+               physaddr = vtophys (&as->ccb);
+               as->mailbox->msb = physaddr >> 16;
+               as->mailbox->mid = physaddr >> 8;
+               as->mailbox->lsb = physaddr;
+               as->mailbox->cmd = 2;
+               as_put_byte (AS_CMD_START_SCSI_COMMAND);
+
+               as->active = 0;
+               bp = as->requests.b_actf;
+               if (bp) {
+                       bp->b_flags |= B_ERROR;
+                       asdone (as, 1);
+               }
+       }
+       splx (s);
+}
+
+asintr (dev_t dev)
+{
+       int didwork;
+       int i, j;
+       struct mailbox_entry *mp;
+       unsigned int physaddr;
+       int val;
+
+       outb (as_port + AS_CONTROL, AS_CONTROL_IRST);
+#ifdef notdef
+       if (asverbose)
+               printf ("asintr %x ", cpl);
+#endif
+ again:
+       didwork = 0;
+       for (i = NTARGETS; i < NTARGETS * 2; i++) {
+               mp = &mailboxes[i];
+               
+               if ((val = mp->cmd) == 0)
+                       continue;
+               
+               didwork = 1;
+
+               physaddr = (mp->msb << 16)
+                       | (mp->mid << 8)
+                               | mp->lsb;
+               
+               for (j = 0; j < NTARGETS; j++) {
+                       if (asinfo[j].ccb_phys == physaddr) {
+                               mp->cmd = 0;
+                               asintr1 (&asinfo[j], val);
+                               break;
+                       }
+               }
+               if (j == NTARGETS) {
+                       printf ("as: unknown mailbox paddr 0x%x\n", physaddr);
+                       mp->cmd = 0;
+               }
+       }
+
+       if (didwork)
+               goto again;
+}
+
+asintr1 (as, val)
+struct asinfo *as;
+int val;
+{
+       struct buf *bp;
+       struct ccb *ccb;
+       int n;
+       int bad;
+       char *msg;
+       char msgbuf[100];
+       unsigned char *sp;
+       int i,key;
+
+       if (asverbose)
+               printf ("asintr1 %x ", val);
+       if (as->active == 0) {
+               printf ("as: stray intr 0x%x\n", as->dev);
+               return;
+       }
+
+       as->active = 0;
+       untimeout (asabort, as);
+       
+       bp = as->requests.b_actf;
+       ccb = &as->ccb;
+
+       if (bp == as->scsi_bp) {
+               /* no fancy error recovery in this case */
+               if (asverbose)
+                       printf ("asintr1:scsicmd ");
+#if 0
+               if (val != 1)
+                       bp->b_flags |= B_ERROR;
+               goto next;
+#endif
+       }
+
+       bad = 0;
+       msg = NULL;
+
+       if (val != 1 && val != 4) {
+               bad = 1;
+               sprintf (msgbuf, "funny mailbox message 0x%x\n", val);
+               msg = msgbuf;
+               goto wrapup;
+       }
+
+       if (ccb->ccb_host_status != 0) {
+               bad = 1;
+               sprintf (msgbuf, "controller error 0x%x",
+                        ccb->ccb_host_status);
+               msg = msgbuf;
+               goto wrapup;
+       }
+
+       if (ccb->ccb_target_status == 0)
+               /* good transfer */
+               goto wrapup;
+
+       if (ccb->ccb_target_status == 8) {
+               /* target rejected command because it is busy
+                * and wants us to try again later.  We'll wait 1 second
+                */
+               as->restart_pending = 1;
+               timeout (asrestart, as, hz);
+               return;
+       }
+
+       if (ccb->ccb_target_status != 2) {
+               bad = 1;
+               sprintf (msgbuf, "target error 0x%x",
+                        ccb->ccb_target_status);
+               msg = msgbuf;
+               goto wrapup;
+       }
+
+       /* normal path for errors */
+
+       sp = ccb_sense (ccb);
+       /* check for extended sense information */
+       if ((sp[0] & 0x7f) != 0x70) {
+               /* none */
+               bad = 1;
+               sprintf (msgbuf, "scsi error 0x%x", sp[0] & 0x7f);
+               msg = msgbuf;
+               goto wrapup;
+       }
+       
+       if (as->tape && (sp[2] & 0xf) == 0) {
+               if (sp[2] & 0xe0) {
+                       /* either we read a file mark, the early warning EOT,
+                        * or the block size did not match.  In any case, the
+                        * normal residue handling will work (I think)
+                        */
+                       goto wrapup;
+               }
+       }
+
+       bad = 1;
+
+       switch (key = sp[2] & 0xf) {
+       case 1: 
+               msg = "soft error";
+               bad = 0;
+               break;
+       case 2:
+               msg = "not ready";
+               break;
+       case 3:
+               msg = "hard error";
+               break;
+       case 4:
+               msg = "target hardware error";
+               break;
+       case 5:
+               msg = "illegal request";
+               break;
+       case 6:
+               msg = "unit attention error";
+               break;
+       case 7: 
+               msg = "write protect error";
+               break;
+       case 0xd:
+               msg = "volume overflow";
+               break;
+       default:
+               sprintf (msgbuf, "scsi extended error 0x%x", sp[2] & 0xf);
+               msg = msgbuf;
+               break;
+       }
+
+ wrapup:
+
+       if (bad && msg == NULL)
+               msg = "unknown error";
+
+       if (msg && key != 6) {
+               diskerr (bp, "as", msg,
+                        LOG_PRINTF,
+                        -1, /* number of successful blks */
+                        as->have_label ? &as->label : NULL);
+               printf ("\n");
+       }
+
+       if (bad && key != 6) {
+               bp->b_flags |= B_ERROR;
+               printf ("scsi sense: ");
+               sp = ccb_sense (ccb);
+               for (i = 0; i < 30; i++)
+                       printf ("%x ", sp[i] & 0xff);
+               printf ("\n");
+       }
+
+       bp->b_resid = (ccb->ccb_data_len_msb << 16)
+               | (ccb->ccb_data_len_mid << 8)
+                       | ccb->ccb_data_len_lsb;
+       if (bp != as->scsi_bp && bp->b_resid != 0)
+               printf ("scsi resid = %d\n", bp->b_resid);
+
+ next:
+       asdone (as, 1);
+}
+
+asdone (as, restart)
+struct asinfo *as;
+int restart;
+{
+       struct buf *bp;
+
+       bp = as->requests.b_actf;
+       as->requests.b_actf = bp->av_forw;
+       biodone (bp);
+       as->retry_count = 0;
+       if (restart && as->requests.b_actf)
+               asstart (as);
+}
+
+int
+assize (dev)
+dev_t dev;
+{
+       struct asinfo *as;
+       struct disklabel *lp;
+       int val;
+
+       if (as_port == 0 || dev_target (dev) >= NTARGETS)
+               return (ENXIO);
+
+       as = &asinfo[dev_target (dev)];
+
+       if (as->open == 0
+           && asopen (dev, FREAD, S_IFBLK, NULL) != 0)
+               return (0);
+
+       if (as->have_label == 0)
+               return (0);
+
+       lp = &as->label;
+       val = lp->d_partitions[dev_part (dev)].p_size
+               * lp->d_secsize / DEV_BSIZE;
+       (void) asclose(dev, FREAD, S_IFBLK, NULL);
+       return (val);
+}
+
+int
+as_put_byte (val)
+int val;
+{
+       int i;
+
+       for (i = 100; i > 0; i--) {
+               if ((inb (as_port + AS_STATUS) & AS_STATUS_CDF) == 0)
+                       break;
+               DELAY (100);
+       }
+       if (i == 0) {
+               printf ("as: put byte timed out\n");
+               return (-1);
+       }
+       outb (as_port + AS_DATA_OUT, val);
+       return (0);
+}
+               
+int
+as_get_byte (as)
+{
+       int i;
+
+       for (i = 100; i > 0; i--) {
+               if ((inb (as_port + AS_STATUS) & AS_STATUS_DF) != 0)
+                       break;
+               DELAY (100);
+       }
+       if (i == 0) {
+               printf ("as_get_byte timed out\n");
+               return (-1);
+       }
+       return (inb (as_port + AS_DATA_OUT) & 0xff);
+}
+#endif /* NAS */