/* * sys/i386/stand/as.c * * Standalone driver for Adaptech 1542 SCSI * * Pace Willisson pace@blitz.com April 8, 1992 */ #include "param.h" #include "disklabel.h" #include "i386/isa/asreg.h" #include "saio.h" #ifdef ASDEBUG #define ASPRINT(x) { printf x; DELAY (10000); } #else #define ASPRINT(x) #endif #define NRETRIES 3 int as_port = 0x330; struct mailbox_entry mailbox[2]; int asopen(io) struct iob *io; { struct disklabel *dd; char cdb[6]; char data[12]; int val; int oval; int i; struct iob aio; if (io->i_unit < 0 || io->i_unit > 8 || io->i_part < 0 || io->i_part > 8 || io->i_ctlr < 0 || io->i_ctlr > 0) return (-1); /* dma setup: see page 5-31 in the Adaptech manual */ outb (0xd6, 0xc1); outb (0xd4, 0x01); ASPRINT (("resetting adaptech card... ")); outb (as_port + AS_CONTROL, AS_CONTROL_SRST); /* delay a little */ for (i = 0; i < 100; i++) inb (0x84); while (inb (as_port + AS_STATUS) != (AS_STATUS_INIT | AS_STATUS_IDLE)) ; ASPRINT (("reset ok ")); as_put_byte (AS_CMD_MAILBOX_INIT); as_put_byte (1); /* one mailbox out, one in */ as_put_byte ((int)mailbox >> 16); as_put_byte ((int)mailbox >> 8); as_put_byte ((int)mailbox); while (inb (as_port + AS_STATUS) & AS_STATUS_INIT) ; ASPRINT (("mailbox init ok ")); /* do mode select to set the logical block size */ bzero (cdb, 6); cdb[0] = 0x15; /* MODE SELECT */ cdb[4] = 12; /* parameter list length */ bzero (data, 12); data[3] = 8; /* block descriptor length */ data[9] = DEV_BSIZE >> 16; data[10] = DEV_BSIZE >> 8; data[11] = DEV_BSIZE; if (ascmd (io->i_unit, 0, cdb, 6, data, 12, 1) < 0) { printf ("as%d: error setting logical block size\n", io->i_unit); return (-1); } aio = *io; aio.i_bn = LABELSECTOR; aio.i_cc = DEV_BSIZE; /*io->i_ma = buf;*/ aio.i_boff = 0; #ifdef was if (asstrategy (&aio, F_READ) == DEV_BSIZE) { dd = (struct disklabel *)aio.i_ma; io->i_boff = dd->d_partitions[io->i_part].p_offset; ASPRINT (("partition offset %d ", io->i_boff)); } #else { extern struct disklabel disklabel; io->i_boff = disklabel.d_partitions[io->i_part].p_offset; ASPRINT (("partition offset %d ", io->i_boff)); } #endif ASPRINT (("asopen ok ")); return(0); } /* func is F_WRITE or F_READ * io->i_unit, io->i_part, io->i_bn is starting block * io->i_cc is byte count * io->i_ma is memory address * io->i_boff is block offset for this partition (set up in asopen) */ int asstrategy(io, func) struct iob *io; { char cdb[6]; int blkno; int retry; ASPRINT (("asstrategy(target=%d, block=%d+%d, count=%d) ", io->i_unit, io->i_bn, io->i_boff, io->i_cc)); if (func == F_WRITE) { printf ("as%d: write not supported\n", io->i_unit); return (0); } if (io->i_cc == 0) return (0); if (io->i_cc % DEV_BSIZE != 0) { printf ("as%d: transfer size not multiple of %d\n", io->i_unit, DEV_BSIZE); return (0); } /* retry in case we get a unit-attention error, which just * means the drive has been reset since the last command */ for (retry = 0; retry < NRETRIES; retry++) { blkno = io->i_bn + io->i_boff; cdb[0] = 8; /* scsi read opcode */ cdb[1] = (blkno >> 16) & 0x1f; cdb[2] = blkno >> 8; cdb[3] = blkno; cdb[4] = io->i_cc / DEV_BSIZE; cdb[5] = 0; /* control byte (used in linking) */ if (ascmd (io->i_unit, 1, cdb, 6, io->i_ma, io->i_cc, retry == NRETRIES - 1) >= 0) { ASPRINT (("asstrategy ok ")); return (io->i_cc); } } ASPRINT (("asstrategy failed ")); return (0); } int ascmd (target, readflag, cdb, cdblen, data, datalen, printerr) int target; int readflag; char *cdb; int cdblen; char *data; int datalen; int printerr; { struct ccb ccb; int physaddr; unsigned char *sp; int i; if (mailbox[0].cmd != 0) /* this can't happen, unless the card flakes */ _stop ("asstart: mailbox not available\n"); bzero (&ccb, sizeof ccb); ccb.ccb_opcode = 3; ccb.ccb_addr_and_control = target << 5; if (datalen != 0) ccb.ccb_addr_and_control |= readflag ? 8 : 0x10; else ccb.ccb_addr_and_control |= 0x18; ccb.ccb_data_len_msb = datalen >> 16; ccb.ccb_data_len_mid = datalen >> 8; ccb.ccb_data_len_lsb = datalen; ccb.ccb_requst_sense_allocation_len = MAXSENSE; physaddr = (int)data; ccb.ccb_data_ptr_msb = physaddr >> 16; ccb.ccb_data_ptr_mid = physaddr >> 8; ccb.ccb_data_ptr_lsb = physaddr; ccb.ccb_scsi_command_len = cdblen; bcopy (cdb, ccb.ccb_cdb, cdblen); #ifdef ASDEBUG printf ("ccb: "); for (i = 0; i < 48; i++) printf ("%x ", ((unsigned char *)&ccb)[i]); printf ("\n"); /*getchar ();*/ #endif physaddr = (int)&ccb; mailbox[0].msb = physaddr >> 16; mailbox[0].mid = physaddr >> 8; mailbox[0].lsb = physaddr; mailbox[0].cmd = 1; /* tell controller to look in its mailbox */ outb (as_port + AS_CONTROL, AS_CONTROL_IRST); as_put_byte (AS_CMD_START_SCSI_COMMAND); /* wait for status */ ASPRINT (("waiting for status...")); while (mailbox[1].cmd == 0) ; mailbox[1].cmd = 0; if (ccb.ccb_host_status != 0 || ccb.ccb_target_status != 0) { #ifdef ASDEBUG printerr = 1; #endif if (printerr) { printf ("as%d error: hst=%x tst=%x sense=", target, ccb.ccb_host_status, ccb.ccb_target_status); sp = ccb_sense (&ccb); for (i = 0; i < 8; i++) printf ("%x ", sp[i]); printf ("\n"); #ifdef ASDEBUG /*getchar ();*/ #endif } return (-1); } ASPRINT (("ascmd ok ")); return (0); } int as_put_byte (val) int val; { while (inb (as_port + AS_STATUS) & AS_STATUS_CDF) ; outb (as_port + AS_DATA_OUT, val); }