* 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
* 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.
* CDC WREN 5 600 Megabyte magnetic disk
* EXABYTE EXB-8200 8mm tape drive
* 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.
* +-----+ partition number
* +-----+ scsi target number
* +--+ unused (should be 0)
* For tape drives, set the partition number to 0 for regular,
* 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:
* 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 "i386/isa/isa_device.h"
#include "i386/isa/icu.h"
/* target id 7 is the controller itself */
struct mailbox_entry mailboxes
[NTARGETS
* 2] = {0};
#define b_cylin b_resid /* fake cylinder number for disksort */
/* maximum scatter list size for Adaptech controller */
/* this array must reside in contiguous physical memory */
struct mailbox_entry
*mailbox
;
char scatter_list
[NSCATTER
* 6];
struct dos_partition dospart
[NDOSPART
];
unsigned char scsi_cdb
[MAXCDB
];
int tape
; /* sequential */
int disk
; /* nonsequential */
int read_only
; /* CDROM */
int removable
; /* unsupported and tested */
int bs
; /* device block size */
} 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 asprobe(struct isa_device
*), asattach(struct isa_device
*),
struct isa_driver asdriver
= {
asprobe (struct isa_device
*dvp
)
as_port
= dvp
->id_iobase
;
outb (as_port
+ AS_CONTROL
, AS_CONTROL_SRST
);
val
= inb (as_port
+ AS_STATUS
);
if (val
== (AS_STATUS_INIT
| AS_STATUS_IDLE
))
asattach (struct isa_device
*dvp
)
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
);
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) {
val
= inb (as_port
+ AS_STATUS
);
if (val
& AS_STATUS_INIT
)
printf ("as: mailbox init error: 0x%x\n", val
);
ascmd (as
, bp
, direction
, count
, retrycount
)
bp
->b_flags
&= ~(B_READ
| B_ERROR
| B_DONE
);
/* scsi_cdb, scsi_cdb_len set up by caller */
} while (err
&& --retrycount
);
asstring (dest
, src
, size
)
while (size
> 0 && dest
[size
- 1] == ' ')
if (as_port
== 0 || dev_target (dev
) >= NTARGETS
)
as
= &asinfo
[dev_target (dev
)];
if (error
= tsleep ((caddr_t
)as
, PZERO
|PCATCH
, "scsiopen", 0))
if (as
->have_label
== 0 && dev_part (dev
) != 3)
as
->units_open
|= 1 << dev_part (dev
);
/* it seems like we might have to block here in case someone
* opens the device just after someone else closes
if (error
= tsleep ((caddr_t
)as
, PZERO
|PCATCH
, "scsicmd", 0))
bcopy(as
->vendor
, vendor
, sizeof(vendor
));
bcopy(as
->model
, model
, sizeof(model
));
bp
= geteblk (DEV_BSIZE
);
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 */
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 */
/* blather on console about it */
printf ("%x ", p
[n
] & 0xff);
for (n
= 0; n
< 40; n
++) {
if (p
[n
] >= ' ' && p
[n
] < 0177)
case 0: /* normal disk */
case 4: /* write once disk */
case 5: /* read only disk */
printf ("logical unit not present\n");
printf ("unknown peripheral device type: 0x%x\n", p
[0]);
as
->removable
= (p
[1] & 0x80) ? 1 : 0;
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 ");
/* probe for desired block size */
/* assume default of 512, except if CDROM (2048) */
cdb
[0] = 0x1A; /* SCSI_MDSENSE */
if (as
->tape
&& ascmd (as
, bp
, B_READ
, 12, 2) == 0) {
/* blather about device more */
if(bcmp(as
->vendor
,vendor
, sizeof(vendor
)) != 0 ||
bcmp(as
->model
,model
, sizeof(model
)) != 0) {
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",
(long)p
[5]*65536+p
[6]*256+p
[7],
(long)p
[9]*65536+p
[10]*256+p
[11]);
/* obtain possible block sizes */
cdb
[0] = 0x05; /* SCSI_RDLIMITS; */
if (ascmd (as
, bp
, B_READ
, 12, 2) == 0) {
maxblk
= p
[1]*65536+p
[2]*256+p
[3];
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
);
if (as
->tape
&& dev_part(dev
)) {
cdb
[0] = 0x25; /* SCSI_READCAPACITY */
if (as
->disk
&& ascmd (as
, bp
, B_READ
, 12, 2) == 0) {
disksize
= ntohl(*(long *)p
);
as
->bs
= ntohl(*(long *)(p
+4));
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 */
cdb
[0] = 0x15; /* MODE SELECT */
cdb
[4] = 12; /* parameter list length */
p
[3] = 8; /* block descriptor length */
n
= as
->bs
== 1 ? 0 : as
->bs
;
(void) ascmd (as
, bp
, B_WRITE
, 12, 2);
/* device online and ready? */
cdb
[0] = 0x00; /* SCSI_UNITRDY */
if (error
= ascmd (as
, bp
, B_READ
, 12, 2)) {
printf("as%d: drive not online\n", dev_target(dev
));
if (as
->disk
&& as
->read_only
== 0) {
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 */
makeasdev (major (dev
), dev_target (dev
), 3),
asstrategy
, &as
->label
, as
->dospart
, 0, 0)) == NULL
){
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) {
/* may want to set logical block size here ? */
bp
->b_flags
|= B_INVAL
| B_AGE
;
as
= &asinfo
[dev_target (dev
)];
if (error
= tsleep ((caddr_t
)as
, PZERO
|PCATCH
, "scsiclose", 0))
if (error
= tsleep ((caddr_t
)as
, PZERO
|PCATCH
,
bp
= geteblk (DEV_BSIZE
);
if (0 && (flag
& FWRITE
) != 0) {
/* presume user will use tape again */
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 */
cdb
[0] = 0x10; /* write filemarks */
cdb
[4] = 1; /* one of them */
error
|= ascmd (as
, bp
, B_READ
, 0, 1);
cdb
[0] = 0x1; /* rewind */
cdb
[1] = 1; /* don't wait until done */
error
|= ascmd (as
, bp
, B_READ
, 0, 1);
cdb
[0] = 0x11; /* backspace */
cdb
[1] = 1; /* look at filemarks (instead of blocks) */
error
= ascmd (as
, bp
, B_READ
, 0, 1);
bp
->b_flags
|= B_INVAL
| B_AGE
;
as
->units_open
&= ~(1 << dev_part (dev
));
asioctl (dev
, cmd
, addr
, flag
)
as
= &asinfo
[dev_target (dev
)];
*(struct disklabel
*)addr
= as
->label
;
if ((flag
& FWRITE
) == 0) {
dl
= (struct disklabel
*)addr
;
if (error
= setdisklabel(&as
->label
, dl
, 0, as
->dospart
))
if ((flag
& FWRITE
) == 0) {
as
->wlabel
= *(int *)addr
;
if ((flag
& FWRITE
) == 0) {
dl
= (struct disklabel
*)addr
;
if (error
= setdisklabel (&as
->label
, dl
, 0, as
->dospart
))
error
= writedisklabel(dev
, asstrategy
, &as
->label
,
cmdp
= (struct scsicmd
*)addr
;
/* limited by max sizeof of geteblk */
if (cmdp
->datalen
>= 8192
|| cmdp
->cdblen
>= MAXCDB
) {
if (ccblen
> sizeof (struct ccb
))
ccblen
= sizeof (struct ccb
);
if (error
= tsleep ((caddr_t
)as
, PZERO
|PCATCH
,
bp
= geteblk (cmdp
->datalen
);
as
->scsi_cdb_len
= cmdp
->cdblen
;
if (error
= copyin (cmdp
->cdb
, as
->scsi_cdb
, cmdp
->cdblen
))
direction
= cmdp
->readflag
? B_READ
: B_WRITE
;
if (direction
== B_WRITE
)
if (error
= copyin (cmdp
->data
,
bp
->b_un
.b_addr
, cmdp
->datalen
))
ascmd (as
, bp
, direction
, cmdp
->datalen
, 1);
copyout (&as
->ccb
, cmdp
->ccb
, ccblen
);
copyout (bp
->b_un
.b_addr
, cmdp
->data
, cmdp
->datalen
);
bp
->b_flags
|= B_INVAL
| B_AGE
;
printf ("asstrategy %d %d ", bp
->b_blkno
, bp
->b_bcount
);
as
= &asinfo
[dev_target (bp
->b_dev
)];
as
->requests
.b_actl
->av_forw
= bp
;
as
->requests
.b_actf
= bp
;
as
->requests
.b_actl
= bp
;
&& dev_part (bp
->b_dev
) != 3)
bp
->b_cylin
= bp
->b_blkno
;
disksort (&as
->requests
, bp
);
if (as
->restart_pending
) {
untimeout (asrestart
, as
);
if ((bp
= as
->requests
.b_actf
) == NULL
)
printf ("asstart %x ", bp
);
if (as
->mailbox
->cmd
!= 0) {
/* this can't happen, unless the card flakes */
printf ("asstart: mailbox not available\n");
if (as
->retry_count
== 0) {
as
->start_time
= time
.tv_sec
;
if (time
.tv_sec
- as
->start_time
> 60) {
printf ("as: command timed out\n");
if ((bp
->b_bcount
% bs
) != 0) {
printf("as: partial block read\n");
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
) {
if (blkno
== part
->p_size
) {
bp
->b_resid
= bp
->b_bcount
;
if (blkno
+ nblocks
>= part
->p_size
)
nblocks
= part
->p_size
- blkno
;
blkno
= (blkno
* DEV_BSIZE
)/bs
;
printf("trans %d ", blkno
);
printf("total %d nblocks %d ", total
, nblocks
);
/*bp->b_bcount = total; /* XXX partial tape block read - wrong */
nblocks
= bp
->b_bcount
/ as
->fixed
;
while (n
< total
&& nscatter
< NSCATTER
) {
thistime
= page_size
- ((vm_offset_t
)p
- trunc_page (p
));
if (n
+ thistime
> total
)
printf ("%d bytes to %x (%x)\n",
if (nscatter
== NSCATTER
) {
printf("out of range, cannot happen?");
/* this only needed to make debugging easier */
bzero ((caddr_t
)ccb
, sizeof *ccb
);
ccb
->ccb_opcode
= 4; /* scatter cmd, return resid */
target
= dev_target (bp
->b_dev
);
ccb
->ccb_addr_and_control
= target
<< 5;
ccb
->ccb_addr_and_control
|= (bp
->b_flags
& B_READ
) ? 8 : 0x10;
ccb
->ccb_addr_and_control
|= 0x18;
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_host_status
= 0;
ccb
->ccb_target_status
= 0;
ccb
->ccb_scsi_command_len
= as
->scsi_cdb_len
;
bcopy (as
->scsi_cdb
, cdb
, as
->scsi_cdb_len
);
ccb
->ccb_scsi_command_len
= 6;
cdb
[0] = (bp
->b_flags
& B_READ
) ? 8 : 0xa;
cdb
[1] = 0; /* logical unit 0, variable block size */
cdb
[2] = bp
->b_bcount
>> 16;
cdb
[3] = bp
->b_bcount
>> 8;
cdb
[1] = 1; /* fixed block size */
cdb
[5] = 0; /* control byte (used in linking) */
ccb
->ccb_scsi_command_len
= 10;
cdb
[0] = (bp
->b_flags
& B_READ
) ? 0x28 : 0x2a;
*(long *) (cdb
+2) = htonl(blkno
);
*(short *) (cdb
+7) = htons(nblocks
);
cdb
[9] = 0; /* control byte (used in linking) */
printf ("%02x ", ((unsigned char *)ccb
)[n
]);
physaddr
= vtophys (ccb
);
as
->mailbox
->msb
= physaddr
>> 16;
as
->mailbox
->mid
= physaddr
>> 8;
as
->mailbox
->lsb
= physaddr
;
/* tell controller to look in its mailbox */
as_put_byte (AS_CMD_START_SCSI_COMMAND
);
timeout (asabort
, as
, hz
* 60 * 2);
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_put_byte (AS_CMD_START_SCSI_COMMAND
);
bp
= as
->requests
.b_actf
;
struct mailbox_entry
*mp
;
outb (as_port
+ AS_CONTROL
, AS_CONTROL_IRST
);
printf ("asintr %x ", cpl
);
for (i
= NTARGETS
; i
< NTARGETS
* 2; i
++) {
if ((val
= mp
->cmd
) == 0)
physaddr
= (mp
->msb
<< 16)
for (j
= 0; j
< NTARGETS
; j
++) {
if (asinfo
[j
].ccb_phys
== physaddr
) {
asintr1 (&asinfo
[j
], val
);
printf ("as: unknown mailbox paddr 0x%x\n", physaddr
);
printf ("asintr1 %x ", val
);
printf ("as: stray intr 0x%x\n", as
->dev
);
bp
= as
->requests
.b_actf
;
/* no fancy error recovery in this case */
printf ("asintr1:scsicmd ");
if (val
!= 1 && val
!= 4) {
sprintf (msgbuf
, "funny mailbox message 0x%x\n", val
);
if (ccb
->ccb_host_status
!= 0) {
sprintf (msgbuf
, "controller error 0x%x",
if (ccb
->ccb_target_status
== 0)
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
timeout (asrestart
, as
, hz
);
if (ccb
->ccb_target_status
!= 2) {
sprintf (msgbuf
, "target error 0x%x",
/* normal path for errors */
/* check for extended sense information */
if ((sp
[0] & 0x7f) != 0x70) {
sprintf (msgbuf
, "scsi error 0x%x", sp
[0] & 0x7f);
if (as
->tape
&& (sp
[2] & 0xf) == 0) {
/* 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)
switch (key
= sp
[2] & 0xf) {
msg
= "target hardware error";
msg
= "unit attention error";
msg
= "write protect error";
sprintf (msgbuf
, "scsi extended error 0x%x", sp
[2] & 0xf);
-1, /* number of successful blks */
as
->have_label
? &as
->label
: NULL
);
printf ("%x ", sp
[i
] & 0xff);
bp
->b_resid
= (ccb
->ccb_data_len_msb
<< 16)
| (ccb
->ccb_data_len_mid
<< 8)
if (bp
!= as
->scsi_bp
&& bp
->b_resid
!= 0)
printf ("scsi resid = %d\n", bp
->b_resid
);
bp
= as
->requests
.b_actf
;
as
->requests
.b_actf
= bp
->av_forw
;
if (restart
&& as
->requests
.b_actf
)
if (as_port
== 0 || dev_target (dev
) >= NTARGETS
)
as
= &asinfo
[dev_target (dev
)];
&& asopen (dev
, FREAD
, S_IFBLK
, NULL
) != 0)
val
= lp
->d_partitions
[dev_part (dev
)].p_size
* lp
->d_secsize
/ DEV_BSIZE
;
(void) asclose(dev
, FREAD
, S_IFBLK
, NULL
);
for (i
= 100; i
> 0; i
--) {
if ((inb (as_port
+ AS_STATUS
) & AS_STATUS_CDF
) == 0)
printf ("as: put byte timed out\n");
outb (as_port
+ AS_DATA_OUT
, val
);
for (i
= 100; i
> 0; i
--) {
if ((inb (as_port
+ AS_STATUS
) & AS_STATUS_DF
) != 0)
printf ("as_get_byte timed out\n");
return (inb (as_port
+ AS_DATA_OUT
) & 0xff);