From: William F. Jolitz Date: Tue, 14 Jul 1992 04:34:06 +0000 (-0800) Subject: 386BSD 0.1 development X-Git-Tag: 386BSD-0.1~17 X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/commitdiff_plain/2a329533925e4f37bbb7be8a6b2e1e44ee21f136 386BSD 0.1 development Work on file usr/src/sys.386bsd/i386/isa/as.c Co-Authored-By: Lynne Greer Jolitz Synthesized-from: 386BSD-0.1 --- diff --git a/usr/src/sys.386bsd/i386/isa/as.c b/usr/src/sys.386bsd/i386/isa/as.c new file mode 100644 index 0000000000..884854e2a3 --- /dev/null +++ b/usr/src/sys.386bsd/i386/isa/as.c @@ -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 */