1329 lines
27 KiB
C
1329 lines
27 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* $Id: as.c,v 1.3 1993/05/22 08:01:02 cgd Exp $
|
|
*/
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
}
|
|
|
|
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 ((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;
|
|
if (as->tape && as->bs == 1)
|
|
total = bp->b_bcount;
|
|
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 = 2; /* scatter cmd, return resid */
|
|
else
|
|
ccb->ccb_opcode = 0;
|
|
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");
|
|
}
|
|
|
|
/* this assignment mixed sizes of controller commands
|
|
and data to read/write.
|
|
bp->b_resid = (ccb->ccb_data_len_msb << 16)
|
|
| (ccb->ccb_data_len_mid << 8)
|
|
| ccb->ccb_data_len_lsb;
|
|
*/
|
|
bp->b_resid = 0;
|
|
|
|
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 */
|