diff options
author | Mats O Jansson <maja@cvs.openbsd.org> | 1997-01-15 23:25:34 +0000 |
---|---|---|
committer | Mats O Jansson <maja@cvs.openbsd.org> | 1997-01-15 23:25:34 +0000 |
commit | bf6413046b73005ecca3f5352c8711d00a6d00b9 (patch) | |
tree | fcd2c64f80b1be68d962f84a1e34da0a02d5385f /sys/arch/vax/boot/scsi_hi.c | |
parent | 957078809571dbe61aac81705145fa4cee6bfda8 (diff) |
sync with NetBSD 970112 -moj
Diffstat (limited to 'sys/arch/vax/boot/scsi_hi.c')
-rw-r--r-- | sys/arch/vax/boot/scsi_hi.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/sys/arch/vax/boot/scsi_hi.c b/sys/arch/vax/boot/scsi_hi.c new file mode 100644 index 00000000000..0102123cc32 --- /dev/null +++ b/sys/arch/vax/boot/scsi_hi.c @@ -0,0 +1,297 @@ +/* $NetBSD: scsi_hi.c,v 1.1 1996/08/02 11:22:31 ragge Exp $ */ + +/**************************************************************************** + * NS32K Monitor SCSI high-level driver + * Bruce Culbertson + * 8 March 1990 + * (This source is public domain source) + * + * There are three monitor SCSI commands. "Read" and "write" I think are + * fairly self explanatory once you read the help messages. They, in fact, + * execute the "extended read", "extended write", and "request sense" + * commands from the SCSI standard. + * + * "Raw" lets you execute any SCSI command but you need a SCSI reference to + * know what the commands are and what their formats are. The SCSI + * standard specifies that there are six buffers which, for example, hold a + * SCSI command or are the source or destination for data. You provide + * "raw" with an array of pointers to the six buffers. Using "edit", you + * can enter a SCSI command somewhere in memory and you can create the + * array of pointers. The array must actually be eight entries long; two + * entries are not used. By typing "raw <array address>", the SCSI command + * is executed. + * + * By the way, "read", "write", and "raw" talk only to the DP8490 SCSI + * controller. I have not had time to read the Adaptec data sheet and + * write a driver for it. + ****************************************************************************/ +#include "so.h" + +#define OK 0 +#define NOT_OK OK+1 +#define PRIVATE +#define PUBLIC +#define U8 unsigned char + +long scsiAdr = DEFAULT_SCSI_ADR, /* default SCSI address */ + scsiLun = DEFAULT_SCSI_LUN; + +struct cmd_desc { /* SCSI command description */ + const U8 *cmd; /* command string */ + const U8 *odata; /* data to output, if any */ + const struct cmd_desc *chain; /* next command */ +}; + +struct drive { /* SCSI drive description */ + U8 adr, lun; /* SCSI address and LUN */ + U8 flags; /* drive characteristics */ + U8 stat; /* drive state */ + const struct cmd_desc *init; /* list of initialize commands */ +}; +/* for drive.flags */ +#define EXTENDED_RDWR 1 /* device does extended read, write */ +#define EXTENDED_SENSE 2 /* device does extended sense */ +/* for drive.stat */ +#define INITIALIZED 1 /* device is initialized */ + +PRIVATE struct drive drive_tbl[] = { +#if 1 + {0, 0, 0, 1, 0}, + {1, 0, 0, 1, 0}, + {2, 0, 0, 1, 0}, + {3, 0, 0, 1, 0}, + {4, 0, 0, 1, 0}, + {5, 0, 0, 1, 0}, + {6, 0, 0, 1, 0}, + {7, 0, 0, 1, 0}, +#else + {0, 0, EXTENDED_RDWR | EXTENDED_SENSE, 1, 0}, + {1, 0, EXTENDED_RDWR | EXTENDED_SENSE, 1, 0}, + {2, 0, EXTENDED_RDWR | EXTENDED_SENSE, 1, 0}, + {3, 0, EXTENDED_RDWR | EXTENDED_SENSE, 1, 0}, + {4, 0, EXTENDED_RDWR | EXTENDED_SENSE, 1, 0}, + {5, 0, EXTENDED_RDWR | EXTENDED_SENSE, 1, 0}, + {6, 0, EXTENDED_RDWR | EXTENDED_SENSE, 1, 0}, + {7, 0, EXTENDED_RDWR | EXTENDED_SENSE, 1, 0}, +#endif +}; +#define DRV_TBL_SZ (sizeof (drive_tbl) / sizeof (struct drive)) + +/* Round up to multiple of four since SCSI transfers are always multiples + * of four bytes. + */ +#define CMD_LEN 12 /* longest SCSI command */ +#define SENSE_LEN 24 /* extended sense length */ +#define MSG_LEN 4 +#define STAT_LEN 4 + +#define MAX_SCSI_RETRIES 6 +#define CMD_IX 2 +#define CMD_SENSE 0x03 +#define CMD_READ 0x08 +#define CMD_WRITE 0x0a +#define CMD_XREAD 0x28 +#define CMD_XWRITE 0x2a +PRIVATE U8 cmd_buf[CMD_LEN]; + +#define SENSE_KEY 2 +#define NO_SENSE 0 +#define RECOVERY_ERR 1 +#define UNIT_ATTN 6 +#define ADD_SENSE_CODE 12 +#define SENSE_RST 0x29 +PRIVATE U8 sense_buf[SENSE_LEN]; + +#define CHECK_CONDITION 2 +#define STAT_IX 3 +#define STAT_MASK 0x1f +PRIVATE U8 stat_buf[STAT_LEN]; +#define IMSG_IX 7 +PRIVATE U8 msg_buf[MSG_LEN]; + +#define ODATA_IX 0 +#define IDATA_IX 1 +PRIVATE struct scsi_args scsi_args; + +/*===========================================================================* + * sc_rdwt * + *===========================================================================*/ +/* Carry out a read or write request for the SCSI disk. */ +PRIVATE int +sc_rdwt(op, block, ram_adr, len, sc_adr, lun) +long block, ram_adr, len, sc_adr, lun; +{ + int retries, ret; + U8 *p; + struct drive *dp; + + printf ("sc_rdwt: op %x, block %d, ram %x, len %d, sc_adr %d, lun %d\n", + op, block, ram_adr, len, sc_adr, lun); + + /* get drive characteristics */ + for (dp = drive_tbl; dp < drive_tbl + DRV_TBL_SZ - 1; ++dp) + if (dp->adr == sc_adr && dp->lun == lun) break; + if (dp == drive_tbl + DRV_TBL_SZ - 1) { + dp->adr = sc_adr; /* have default, set adr, lun */ + dp->lun = lun; + } + for (retries = 0; retries < MAX_SCSI_RETRIES; ++retries) { + if (dp->init && !(dp->stat & INITIALIZED)) + if (OK != sc_initialize (dp)) { + printf("SCSI cannot initialize device\n"); + return NOT_OK; + } + p = cmd_buf; /* build SCSI command */ + if (dp->flags & EXTENDED_RDWR) { /* use extended commands */ + *p++ = (op == DISK_READ)? CMD_XREAD: CMD_XWRITE; + *p++ = lun << 5; + *p++ = (block >> 24) & 0xff; + *p++ = (block >> 16) & 0xff; + *p++ = (block >> 8) & 0xff; + *p++ = (block >> 0) & 0xff; + *p++ = 0; + *p++ = (len >> 8) & 0xff; + *p++ = (len >> 0) & 0xff; + *p = 0; + } else { /* use short (SASI) commands */ + *p++ = (op == DISK_READ)? CMD_READ: CMD_WRITE; + *p++ = (lun << 5) | ((block >> 16) & 0x1f); + *p++ = (block >> 8) & 0xff; + *p++ = (block >> 0) & 0xff; + *p++ = len; + *p = 0; + } + if (op == DISK_READ) + ret = exec_scsi_hi (cmd_buf, (U8 *)ram_adr, (U8 *)0, dp); + else + ret = exec_scsi_hi (cmd_buf, (U8 *)0, (U8 *)ram_adr, dp); + if (OK == ret) return OK; + dp->stat &= ~INITIALIZED; + } + printf("SCSI %s, block %d failed even after retries\n", + op == DISK_READ? "READ": "WRITE", block); + return NOT_OK; +} + +/*===========================================================================* + * sc_initialize * + *===========================================================================*/ +/* Execute the list of initialization commands for the given drive. + */ +int +sc_initialize (dp) +struct drive *dp; +{ + const struct cmd_desc *cp; + + for (cp = dp->init; cp != 0; cp = cp->chain) + if (OK != exec_scsi_hi (cp->cmd, 0, cp->odata, dp)) { + dp->stat &= ~INITIALIZED; + return NOT_OK; + } + dp->stat |= INITIALIZED; + return OK; +} + +/*===========================================================================* + * exec_scsi_hi * + *===========================================================================*/ +/* Execute a "high-level" SCSI command. This means execute a low level + * command and, if it fails, execute a request sense to find out why. + */ +PRIVATE int +exec_scsi_hi(cmd, data_in, data_out, dp) +U8 *cmd, *data_out, *data_in; +struct drive *dp; +{ + scsi_args.ptr[CMD_IX] = (long)cmd; + scsi_args.ptr[STAT_IX] = (long)stat_buf; + scsi_args.ptr[IMSG_IX] = (long)msg_buf; + scsi_args.ptr[IDATA_IX] = (long)data_in; + scsi_args.ptr[ODATA_IX] = (long)data_out; + if (OK != exec_scsi_low (&scsi_args, dp->adr)) + return NOT_OK; + *stat_buf &= STAT_MASK; /* strip off lun */ + if (*stat_buf == 0) + /* Success -- this should be the usual case */ + return OK; + if (*stat_buf != CHECK_CONDITION) { + /* do not know how to handle this so return error */ + printf("SCSI device returned unknown status: %d\n", *stat_buf); + return NOT_OK; + } + /* Something funny happened, need to execute request-sense command + * to learn more. + */ + if (OK == get_sense(dp)) + /* Something funny happened, but the device recovered from it and + * the command succeeded. + */ + return OK; + return NOT_OK; +} + +/*===========================================================================* + * get_sense * + *===========================================================================*/ +/* Execute a "request sense" SCSI command and check results. When a SCSI + * command returns CHECK_CONDITION, a request-sense command must be executed. + * A request-sense command provides information about the original command. + * The original command might have succeeded, in which case it does not + * need to be retried and OK is returned. Examples: read error corrected + * with error correction code, or error corrected by retries performed by + * the SCSI device. The original command also could have failed, in + * which case NOT_OK is returned. + */ +#define XLOGICAL_ADR \ + (sense_buf[3]<<24 | sense_buf[4]<<16 | sense_buf[5]<<8 | sense_buf[6]) +#define LOGICAL_ADR \ + (sense_buf[1]<<16 | sense_buf[2]<<8 | sense_buf[3]) + +PRIVATE int +get_sense (dp) +struct drive *dp; +{ + U8 *p; + + p = cmd_buf; /* build SCSI command */ + *p++ = CMD_SENSE; + *p++ = dp->lun << 5; + *p++ = 0; + *p++ = 0; + *p++ = (dp->flags & EXTENDED_SENSE)? SENSE_LEN: 0; + *p = 0; + scsi_args.ptr[IDATA_IX] = (long)sense_buf; + scsi_args.ptr[ODATA_IX] = 0; + scsi_args.ptr[CMD_IX] = (long)cmd_buf; + scsi_args.ptr[STAT_IX] = (long)stat_buf; + scsi_args.ptr[IMSG_IX] = (long)msg_buf; + if (OK != exec_scsi_low (&scsi_args, dp->adr)) { + printf("SCSI SENSE command failed\n"); + return NOT_OK; + } + if ((*stat_buf & STAT_MASK) != 0) { + printf("SCSI SENSE returned wrong status %d\n", *stat_buf); + return NOT_OK; + } + if (0 == (dp->flags & EXTENDED_SENSE)) { + printf("SCSI request sense, code 0x%x, log_adr 0x%x\n", + sense_buf[0], LOGICAL_ADR); + return NOT_OK; + } + switch (sense_buf[SENSE_KEY] & 0xf) { + case NO_SENSE: + case UNIT_ATTN: /* reset */ + return NOT_OK; /* must retry command */ + case RECOVERY_ERR: + /* eventually, we probably do not want to hear about these. */ + printf("SCSI ok with recovery, code 0x%x, logical address 0x%x\n", + sense_buf[ADD_SENSE_CODE], XLOGICAL_ADR); + return OK; /* orig command was ok with recovery */ + default: + printf("SCSI failure: key 0x%x code 0x%x log adr 0x%x sense buf 0x%x\n", + sense_buf[SENSE_KEY], sense_buf[ADD_SENSE_CODE], + XLOGICAL_ADR, sense_buf); + return NOT_OK; /* orig command failed */ + } +} |