/* commands.c Revision 2.0 1/1/93 */ /* This software was written by NCR Microelectronics to develop and test new * products. NCR assumes no liability for its use. This software is released * to the public domain to illustrate certain programming techniques for the * 53c700 and 53C800 family. */ #include #include #include "types.h" #include "commands.h" #include "table.h" #include "tools.h" #include "siop.h" /* The following structure stores all of the information that we know about * the attached drives */ struct _info { int attached; /* 1 if this ID is attached and running */ uquad lastblock; /* The last valid address on the drive */ uword blocklength;/* The length of a block on the disk */ uword offperiod; /* The offset and period for sync transfers * stored in the same form and needed for * table indirect selects */ } info[7]; static ubyte identify_msg[] = { 0xc0 /* 0xc0 = allow disconnect, 0x80 = no * disconnect */ }; static ubyte test_unit_ready_cmd[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static ubyte inquiry_cmd[] = { 0x12, 0x00, 0x00, 0x00, 36, 0x00 /* request 36 byte of inquiry * information */ }; static ubyte read_capacity_cmd[] = { 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static ubyte sync_msg[] = { 0x80, 0x01, 0x03, 0x01, PERIOD, OFFSET }; static ubyte format_unit_cmd[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 }; static ubyte request_sense_cmd[] = { 0x03, 0x00, 0x00, 0x00, 0x07, 0x00 }; /* The following two byte contain the status byte and the message in byte * after a SCSI command. */ static ubyte status[1]; static ubyte msg_in[1]; static ubyte scntl3_val = 0x33; /***************************************************************** Sends a Test Unit Ready command to a SCSI device. *****************************************************************/ int test_unit_ready(int drive) { int error; buffer_table[DEVICE].count = ((uquad)drive << 16) | ((uquad)scntl3_val<<24); buffer_table[CMD_ADR].count = sizeof(test_unit_ready_cmd); buffer_table[CMD_ADR].address = getPhysAddr(test_unit_ready_cmd); buffer_table[SENDMSG].count = 1; buffer_table[SENDMSG].address = getPhysAddr(identify_msg); buffer_table[STATUS_ADR].count = 1; buffer_table[STATUS_ADR].address = getPhysAddr(status); buffer_table[RCVMSG].count = 1; buffer_table[RCVMSG].address = getPhysAddr(msg_in); error = start_script(1, &info[drive].offperiod); if (error == 0) { printf("Status = %02x, ", status[0]); printf("Msg_in = %02x\n", msg_in[0]); } return (error); } /************************************************************ Does an inquiry and synchronous negotiation. ************************************************************/ int drive_info(int drive, ubyte *buffer) { int error; buffer_table[DEVICE].count = ((uquad)drive << 16) | info[drive].offperiod | ((uquad)scntl3_val << 24); buffer_table[CMD_ADR].count = sizeof(inquiry_cmd); buffer_table[CMD_ADR].address = getPhysAddr(inquiry_cmd); buffer_table[DATA_ADR].count = 36; buffer_table[DATA_ADR].address = getPhysAddr(buffer); error = start_script(1, &info[drive].offperiod); /* If we get a phase mismatch the drive is most likely sending me less * data than I requested. Flush the buffers and finish up the transfer. */ if (error == 8) { write_reg(CTEST3, (read_reg(CTEST3) | 0x04)); /* Clear DMA and SCSI FIFOs */ error = start_script(2, &info[drive].offperiod); } if (error == 0) { /* If the drive says it supports sync and we are currently not using * sync, perform a synchronous negotation. */ if (info[drive].offperiod == 00 && (buffer[7] & 0x10)) { error = sync_neg(drive); } printf("Status = %02x, ", status[0]); printf("Msg_in = %02x\n", msg_in[0]); } return (error); } /********************************************************************* Scans the SCSI bus for any devices, and sends Test Unit Ready, Inquiry, and Read Capacity commands. *********************************************************************/ void get_drive_info() { int i, j; int error; ubyte cap_data[8]; ubyte buffer[40]; /* For each device id we test to see if it responds and if it looks ok we * request all important information and neg. for sync if necessary. */ for (i = 0; i < 7; i++) { info[i].offperiod = 0x00; /* Init the sync info */ info[i].attached = 0; printf("Sending Test Unit Ready Command to ID #%d... \n",i); error = test_unit_ready(i); if (error == 0) printf("Test Unit Ready Successful.\n"); else printf("SCSI ID #%d Not Responding.\n", i); if (error == 0) { /* Request drive info to make sure we are using sync if possible * and get any other info we need for the future. */ printf("Sending Inquiry Command...\n"); error = drive_info(i, buffer); } if (error == 0) { print_buffer(buffer, 36); info[i].attached = 1; /* Read the capacity of the drive and enter that in the info * table. */ buffer_table[DEVICE].count = ( (uquad)i << 16) | info[i].offperiod | ((uquad)scntl3_val << 24); buffer_table[CMD_ADR].count = sizeof(read_capacity_cmd); buffer_table[CMD_ADR].address = getPhysAddr(read_capacity_cmd); /* Init cap_data to zeros */ for (j = 0; j < 8; j++) { cap_data[j] = 0; } buffer_table[DATA_ADR].count = 8; buffer_table[DATA_ADR].address = getPhysAddr(cap_data); buffer_table[SENDMSG].count = 1; buffer_table[SENDMSG].address = getPhysAddr(identify_msg); printf("Sending Read Capacity Command...\n"); error = start_script(1, &info[i].offperiod); if (error == 0) { uquad blocklength; printf("Status = %02x, ", status[0]); printf("Msg_in = %02x\n", msg_in[0]); for (j = 0; j < 4; j++) { ((ubyte *) (&info[i].lastblock))[j] = cap_data[3 - j]; ((ubyte *) (&blocklength))[j] = cap_data[7 - j]; } info[i].blocklength = (uword) blocklength; printf("last block = %lu length = %u\n", info[i].lastblock, info[i].blocklength); } else { printf("error #%d reading capacity data.\n", error); } } } printf("\nHit any key to continue.\n"); getch(); } /* This function performs a read or a write from the indicated disk. The two * commands are virtually identical execpt for the direction data is flowing * so the same code is used. */ int d_rw(int read_cmd, /* 1 == read, 0 == write */ int drive, uquad block, uword number, ubyte *buffer) { ubyte cmd_buf[10]; int i; int long_cmd; /* Should the 10 byte read/write * command be used? */ int error; /* Do a sanity check on the data requested. */ if (info[drive].attached == 0) return (56); if (block + number - 1 > info[drive].lastblock || number == 0) return (57); /* clear cmd buffer */ for (i = 0; i < 10; i++) { cmd_buf[i] = 0; } /* Check to see if we need to use the long command format */ long_cmd = (block > 0x1fffffl || number > 256); if (long_cmd) { cmd_buf[0] = read_cmd ? 0x28 : 0x2a; /* Copy block number to correct place in command */ for (i = 0; i < 4; i++) { cmd_buf[5 - i] = ((ubyte *) &block)[i]; } /* Include number of blocks to be transferred */ cmd_buf[7] = number >> 8; cmd_buf[8] = (ubyte) number; } else { cmd_buf[0] = read_cmd ? 0x08 : 0x0a; for (i = 0; i < 3; i++) { cmd_buf[3 - i] = ((ubyte *) &block)[i]; } cmd_buf[4] = number; } buffer_table[DEVICE].count = ( (uquad)drive << 16) | info[drive].offperiod | ((uquad)scntl3_val << 24); buffer_table[CMD_ADR].count = long_cmd ? 10 : 6; buffer_table[CMD_ADR].address = getPhysAddr(cmd_buf); buffer_table[SENDMSG].count = 1; buffer_table[SENDMSG].address = getPhysAddr(identify_msg); buffer_table[STATUS_ADR].count = 1; buffer_table[STATUS_ADR].address = getPhysAddr(status); buffer_table[RCVMSG].count = 1; buffer_table[RCVMSG].address = getPhysAddr(msg_in); /* Store a buffer table entry for every block to be transferred, They do * not need to be contiguous or of the same size. */ for (i = 0; i < number; i++) { buffer_table[DATA_ADR + i].count = info[drive].blocklength; buffer_table[DATA_ADR + i].address = getPhysAddr(&buffer[i * info[drive].blocklength]); } error = start_script(1, &info[drive].offperiod); printf("msg_in = %x, status = %x\n", *msg_in, *status); return (error); } /******************************************************************* Perform a synchronous negotiation with a target. *******************************************************************/ sync_neg(int drive) { int error; buffer_table[DEVICE].count = (uquad)drive << 16 | ((uquad)scntl3_val << 24); buffer_table[CMD_ADR].count = sizeof(test_unit_ready_cmd); buffer_table[CMD_ADR].address = getPhysAddr(test_unit_ready_cmd); buffer_table[SENDMSG].count = sizeof(sync_msg); buffer_table[SENDMSG].address = getPhysAddr(sync_msg); buffer_table[STATUS_ADR].count = 1; buffer_table[STATUS_ADR].address = getPhysAddr(status); buffer_table[RCVMSG].count = 1; buffer_table[RCVMSG].address = getPhysAddr(msg_in); error = start_script(1, &info[drive].offperiod); if (error == 0) { printf("Status = %02x, ", status[0]); printf("Msg_in = %02x\n", msg_in[0]); } return (error); } /**************************************************************** Format a drive. ****************************************************************/ format_unit(int drive) { buffer_table[CMD_ADR].count = sizeof(format_unit_cmd); buffer_table[CMD_ADR].address = getPhysAddr(format_unit_cmd); buffer_table[DEVICE].count = ( (uquad)drive << 16) | info[drive].offperiod | ((uquad)scntl3_val << 24); buffer_table[SENDMSG].count = 1; buffer_table[SENDMSG].address = getPhysAddr(identify_msg); buffer_table[STATUS_ADR].count = 1; buffer_table[STATUS_ADR].address = getPhysAddr(status); buffer_table[RCVMSG].count = 1; buffer_table[RCVMSG].address = getPhysAddr(msg_in); return (start_script(1, &info[drive].offperiod)); } /************************************************************* Issue a mode sense command. *************************************************************/ int d_mode_sense(int drive, int page, int length, ubyte *buffer) { ubyte cmd_buf[10]; ubyte *c; int long_cmd; /* Should the 10 byte command be * used? */ int error; /* Do a sanity check on the data requested. */ if (info[drive].attached == 0) return (56); /* clear cmd buffer */ for (c = cmd_buf; c < &cmd_buf[10]; *c++ = 0); /* Check to see if we need to use the long command format */ long_cmd = (length > 256); cmd_buf[0] = long_cmd ? 0x5a : 0x1a; cmd_buf[2] = page; if (!long_cmd) { cmd_buf[4] = length; } else { cmd_buf[7] = length >> 8; cmd_buf[8] = (ubyte) length; } buffer_table[DEVICE].count = (uquad)drive << 16 | ((uquad)scntl3_val << 24); buffer_table[CMD_ADR].count = long_cmd ? 10 : 6; buffer_table[CMD_ADR].address = getPhysAddr(cmd_buf); buffer_table[SENDMSG].count = 1; buffer_table[SENDMSG].address = getPhysAddr(identify_msg); buffer_table[STATUS_ADR].count = 1; buffer_table[STATUS_ADR].address = getPhysAddr(status); buffer_table[RCVMSG].count = 1; buffer_table[RCVMSG].address = getPhysAddr(msg_in); buffer_table[DATA_ADR].count = length; buffer_table[DATA_ADR].address = getPhysAddr(buffer); error = start_script(1, &info[drive].offperiod); if (error == 8) { /* Phase mismatch */ write_reg(CTEST3, (read_reg(CTEST3) | 0x04)); /* Clear DMA and SCSI FIFOs */ error = start_script(2, &info[drive].offperiod); } return (error); } /**************************************************************** Issue a request sense command. ****************************************************************/ request_sense(int drive, ubyte *buffer) { buffer_table[CMD_ADR].count = sizeof(request_sense_cmd); buffer_table[CMD_ADR].address = getPhysAddr(request_sense_cmd); buffer_table[DEVICE].count = ( (uquad)drive << 16) | info[drive].offperiod | ((uquad)scntl3_val << 24); buffer_table[SENDMSG].count = 1; buffer_table[SENDMSG].address = getPhysAddr(identify_msg); buffer_table[STATUS_ADR].count = 1; buffer_table[STATUS_ADR].address = getPhysAddr(status); buffer_table[RCVMSG].count = 1; buffer_table[RCVMSG].address = getPhysAddr(msg_in); buffer_table[DATA_ADR].count = 7; buffer_table[DATA_ADR].address = getPhysAddr(buffer); return (start_script(1, &info[drive].offperiod)); }