/************** SCAM Test Program for NCR 53C80 (Revision 1.02) ************** Copyright 1993 NCR Corporation This program assigns SCSI IDs with the SCAM (SCSI Configured AutoMagically) protocol being developed by the X3T9.2 committee. The program is designed to work with an NCR 53C80 chip and was tested using an NCR ADP31A Host Adapter on a 33 MHz 80486 PC. This code should be adaptable to work with other SCSI protocol chips that support low-level control of the SCSI signals (e.g., NCR 53C400, 53C700, 53C710, 53C720, 53C810, etc.). NCR Corporation is making this program available as a tool to aid others in developing and testing their SCAM implementations. While this program attempts to conform with the SCAM revision 5 document (X3T9.2/93-109r5), NCR Corporation disclaims all warranties express or implied including the warranties of merchantability and fitness for a particular purpose with respect to this program or its operation. This program has been tested in a very limited environment and does not attempt to fully implement the SCSI protocol. It may or may not perform correctly in your environment. By using this program, you accept all risks associated with its use. You may copy this program, including the source code, provided this notice is included. If you distribute altered source and/or object code, you must identify the altered sections of source code and, at NCR's request, provide an electronic copy of the altered source code to NCR Corporation. Any alteration to the code by you which infringes another parties patent or copyright will be your sole responsibility. Your use or alteration of this program will signify your acceptace of all the terms of this notice. Please note: NCR does not have resources allocated to support users of this program. Nonetheless, we are interested in your comments, especially any suggestions for improvements or corrections. Please send such comments (email or BBS message is preferred) to: John Lohmeyer NCR Corporation 1635 Aeroplaza Dr. Colorado Springs, CO 80916 Fax: (719) 597-8225 Email: john.lohmeyer@ftcollinsco.ncr.com SCSI BBS: (719) 574-0424 *****************************************************************************/ /***************************************************************************** Revision History: 1.0 First fully functional (I hope) version. 1.01 Minor edits to disclaimer (to keep the legal folks happy). First public release. 1.02 Revised software deglitch function per Ed Gardner's recommendation in X3T9.2/93-173. You may need to adjust the #define for the DEGLITCH parameter if you have a very fast computer. *****************************************************************************/ /* Diagnostic Equates -- for use with Pause feature (-e) */ /* #define MASTER_SELECT */ /* #define SCAM_XFER */ /* Define default SCSI port */ #define SCSIPORT 0x330 /* Define SCSI phases [MSG,C/D,I/O] */ #define DATAOUT 0 #define DATAIN 1 #define COMMAND 2 #define STATUS 3 #define RSVDOUT 4 #define RSVDIN 5 #define MSGOUT 6 #define MSGIN 7 /* Define some SCSI and SCAM constants */ #define SEL_WO_ATN 0 #define SEL_W_ATN 1 #define ARBITRATION_DELAY 3 /* actually should be 2.4 us */ #define SHORT_SCAM_SEL_TO 1000 /* 1 ms */ #define LONG_SCAM_SEL_TO 250000 /* 250 ms */ #define SCAM_HOLD_TIME 250000 /* 250 ms */ #define SELECTION_ABORT_TIME 200 /* 200 us */ #define RST_HOLD_TIME 1000 /* actually should be 25 us */ #define RESET_TO_SEL_TIME 250000 /* 250 ms */ #define DEGLITCH 32 /* number of times to deglitch signal */ /* Define Message Codes and Status Codes and Operation Codes */ #define COMMAND_COMPLETE 0x00 #define MESSAGE_REJECT 0x07 #define GOOD 0x00 #define CHECK_CONDITION 0x02 #define INQUIRY 0x12 #define REQUEST_SENSE 0x03 #define TEST_UNIT_READY 0x00 /* Define Text Modes */ #define NORMAL 0 #define BOLD 1 #define REVERSE 2 #define BLINK 3 #define BOLDYELLOW 4 #define YELLOWBLINK 5 #define ESC 0x1b /* Define SCAM Values */ #define SYNC_PATTERN 0x1f #define ASSIGN_ID 0x00 #define SET_PRIORITY 0x01 #define DOMINANT_MASTER 0x0f /* Define parameters for functions */ #define DEFER 1 #define PARTICIPATE 0 /* library includes */ #include #include #include #include #include #include #include /* Global variables */ int scsi = -1; /* 53C80 port address (-1 => use default SCSIPORT) */ int test_mode = 0; /* 1 => don't talk to 53C80 chip */ int defer; /* flag to say we are are not outputting during isolation stage */ int rtype_byte1; /* received type bytes */ int rtype_byte2; char ts[256]; /* temporary string */ int status_byte, msg_in_byte; /* storage for status and message in bytes */ int pause_disabled = 1; /* flag to say pausing is disabled */ int I_am_the_master = 0; /* flag to we are the dominent master */ int locate_request = 0; /* flag to say user wants Locate function */ int another_device = 0; /* flag to say another device did the SCAM sel */ /* Misc stuff */ #define MAX_STR 40 /* maximum supported SCAM string */ #define MAX_INQ_DATA 36 /* maximum length INQUIRY DATA */ char received_string[MAX_STR]; /* string to store received info */ int inq_cdb[6] = {0x12,0,0,0,MAX_INQ_DATA,0}; /* INQUIRY CDB */ char inq_dat[MAX_INQ_DATA], *c; /* INQUIRY Data */ char inq0[MAX_INQ_DATA+16+1] = "No INQUIRY data for device 0", inq1[MAX_INQ_DATA+16+1] = "No INQUIRY data for device 1", inq2[MAX_INQ_DATA+16+1] = "No INQUIRY data for device 2", inq3[MAX_INQ_DATA+16+1] = "No INQUIRY data for device 3", inq4[MAX_INQ_DATA+16+1] = "No INQUIRY data for device 4", inq5[MAX_INQ_DATA+16+1] = "No INQUIRY data for device 5", inq6[MAX_INQ_DATA+16+1] = "No INQUIRY data for device 6", inq7[MAX_INQ_DATA+16+1] = "No INQUIRY data for device 7", *inq_data[] = {inq0,inq1,inq2,inq3,inq4,inq5,inq6,inq7}; char /* space to hold previously-seen SCAM strings */ scam0[MAX_STR] = " ", scam1[MAX_STR] = " ", scam2[MAX_STR] = " ", scam3[MAX_STR] = " ", scam4[MAX_STR] = " ", scam5[MAX_STR] = " ", scam6[MAX_STR] = " ", scam7[MAX_STR] = " ", *scam_str[] = {scam0,scam1,scam2,scam3,scam4,scam5,scam6,scam7}; /* INQUIRY Data for use in target/slave mode */ int no_lu_inq_data[] = { 0x7F, /* logical unit not supported */ 0x00, /* device type qualifier */ 0x00, /* doesn't comply to anything */ 0x02, /* response data format: SCSI-2 */ 31, /* 31 more bytes */ 0,0,0, /* reserved/features */ 0,0,0,0, 0,0,0,0, /* space for vendor ID */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* space for product identification */ 0,0,0,0};/* space for product revision level */ int targ_inq_data[] = { 0x03, /* processor device */ 0x00, /* device type qualifier */ 0x00, /* doesn't comply to anything */ 0x02, /* response data format: SCSI-2 */ 31, /* 31 more bytes */ 0,0,0, /* reserved/features */ 0,0,0,0, 0,0,0,0, /* space for vendor ID */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* space for product identification */ 0,0,0,0};/* space for product revision level */ /* stuff about us: */ int priority; /* our priority bit */ int preference=0x01; /* our preference code (level 2) */ int max_id_code = 0x02; /* maximum SCSI ID is 7 (for type byte1) */ int scam_dev_type_code = 0x07; /* scam master (obsolete in SCAM rev 5) */ int id_valid = 0x01; /* our default id is valid */ int serial_no_avail = 1; /* our serial number is available */ int default_id = 7; /* our default ID */ char vendor_id[] = "NCR "; /* Vendor ID */ char model[] = "SCAM Test Prog "; /* Model ID */ char serial_no[] = "12345"; /* Serial Number */ int want_scsi_id = 1; /* Flag to say we want an SCSI ID */ int my_scsi_id = 0x00; /* Our assigned SCSI ID bit */ int available_ids = 0xFF; /* available IDs to assign */ int hard_ids = 0; /* bit significant list of blocked + hard IDs */ int soft_ids = 0; /* bit significant list of soft IDs */ int disc_ids = 0; /* bit significant list of discovered hard IDs */ int use_id = 1; /* use an scsi id flag (reset by -a option) */ int reserved_ids = 0; /* blocked ids from -r option */ clock_t rst_time = 0; /* time that SCSI Bus reset issued */ /* * Prototypes: */ void ctrlchandler( void ); void init_ctrlc( void ); void ctrlchandler( void ); int process_args( int , char *[] ); int parsehex( char ** ); char *parse_str( char **, int ); void set_inq_data( int *); void dsp_blocked( void ); void dsp_hard_id( void ); void dsp_scam_string( int, char * ); void dsp_time( void ); void dsp( char *, int , int , int ); void clear_field( int, int, int ); void cls( void ); void pickcursor( int ); void pause( char * ); int check_keyboard( void ); int getkey( void ); void locate_fun( int, int ); void iterate( int, int, int, int ); int isolate( char *, int, int, int ); int byte_xfer(char *, char , int ); int bit_xfer(int *, int, int ); int assign_id( int *, int, int ); int previous_device( char *); int get_action_code( void ); int valid_code( int ); int convert_id( int ); int scan_old_devices( void ); int scan_scam_devices( int ); int perform_scsi_io( int ); int initial_message_out( void ); int get_cdb( int * ); int send_inq_data( int, int ); int send_sense_data( int, int ); int targ_read_byte( int ); int targ_send_byte( int, int ); int wait_for_ack_true( void ); int wait_for_ack_false( void ); int do_inquiry( int ); int wait_for_req( void ); void inquiry_data_to_ascii( char *, char * ); char bin2ascii( char ); void set_scsi_port( void ); void reset_scsi( void ); int arbitrate( int ); int select( int , int , long ); int init_byte_xfer( int, int ); void master_select( long ); int slave_select( int ); int scam_xfer( int ); void deglitch( int, int ); int monitor_scsi( int ); int check_cd( void ); void drop_out( void ); void delay( long ); int delay_test_true( int, int, long ); char welcome[] = "SCAM Reference Program v1.02\n\ Courtesy of NCR Corporation\n\ \n\ \n\ \n\ ID Type INQUIRY Data / SCAM String\n\ -- ------------ -------------------------------------------------------------\n\ 0 available\n\ \n\ 1 available\n\ \n\ 2 available\n\ \n\ 3 available\n\ \n\ 4 available\n\ \n\ 5 available\n\ \n\ 6 available\n\ \n\ 7 available\n\ \n\ \n\ l: Locate r: Reset SCSI ESC: exit"; /* define table position */ #define TABLE 8 #define TYPE_COL 5 #define INQ_COL 19 /**** Main: SCAM Test Program * */ void main( int argc, char *argv[] ) { int temp, rowpntr = 5, i, key, we_won, assigned_id; int t1, t2; /* type code bytes (except priority code) */ int quintet, result; clock_t tmp_t; /* * Wakeup */ init_ctrlc; /* invoke our Control-C handler */ if( process_args( argc, argv ) ) return; set_inq_data( no_lu_inq_data ); /* plug v_id & model into INQ Data */ set_inq_data( targ_inq_data ); /* plug v_id & model into INQ Data */ set_scsi_port(); /* set the SCSI port address */ drop_out(); /* make sure chip is reset */ cls(); /* clear the screen */ dsp( welcome, NORMAL, 1, 1 ); /* display initial screen */ dsp( "L", BOLDYELLOW, 25, 44); dsp( "R", BOLDYELLOW, 25, 55); dsp( "ESC", BOLDYELLOW, 25, 70); dsp_blocked(); /* display blocked ID info */ t1 = max_id_code<<4 | id_valid<<1 | serial_no_avail; /* type code byte 1 except priority code */ t2 = default_id; /* * Compete to be the Dominent SCAM Master (after power up) */ arbitrate( 0 ); /* arbitrate w/o an ID */ master_select( LONG_SCAM_SEL_TO ); /* get into scam mode */ scam_xfer( SYNC_PATTERN ); /* put out a sync pattern */ scam_xfer( DOMINANT_MASTER ); /* say this cycle is for dominance */ clear_field( TABLE-4, 1, 0); /* clear dominent master line */ if( isolate(received_string, PARTICIPATE, t1 | preference<<6 | I_am_the_master<<7, t2 ) > 0 ) { I_am_the_master = 1; dsp( "Dominent Master (this device):", NORMAL, TABLE-4, 1 ); sprintf( ts, "%.2x %.2x %s", (int)(*received_string)&0xFF, (int)(*(received_string+1))&0xFF, received_string+2 ); dsp( ts, REVERSE, TABLE-4, 32 ); } else { I_am_the_master = 0; clear_field( 25, 44, 9 ); /* remove Locate function */ dsp( "Dominent Master (another device):", NORMAL, TABLE-4, 1 ); sprintf( ts, "%.2x %.2x %s", (int)(*received_string)&0xFF, (int)(*(received_string+1))&0xFF, received_string+2 ); dsp( ts, REVERSE, TABLE-4, 35 ); drop_out(); goto monitor; /* nothing to do, but watch */ }; drop_out(); /* drop scam mode */ /* * Reset the SCSI Bus */ reset_scsi_bus: reset_scsi(); /* reset scsi bus */ scsi_reset_detected: /* someone else reset the bus */ cls(); /* clear the screen */ dsp( welcome, NORMAL, 1, 1 ); /* display initial screen */ dsp( "L", BOLDYELLOW, 25, 44); dsp( "R", BOLDYELLOW, 25, 55); dsp( "ESC", BOLDYELLOW, 25, 70); dsp_blocked(); /* display blocked ID info */ want_scsi_id = use_id; /* restore several values */ my_scsi_id = 0x00; available_ids = 0xFF; soft_ids = 0; disc_ids = 0; delay( RESET_TO_SEL_TIME ); /* give devices time to get sane */ /* * Compete to be the Dominent SCAM Master (after a bus reset) */ re_elect_scam_master: arbitrate( 0 ); /* arbitrate w/o an ID */ elect_scam_master: /* some device did SCAM Selection */ priority = 1; master_select( LONG_SCAM_SEL_TO ); /* get into scam mode */ scam_xfer( SYNC_PATTERN ); /* put out a sync pattern */ scam_xfer( DOMINANT_MASTER ); /* say this cycle is for dominance */ clear_field( TABLE-4, 1, 0); /* clear dominent master line */ if( isolate(received_string, PARTICIPATE, t1 | preference<<6 | I_am_the_master<<7, t2 ) > 0 ) { I_am_the_master = 1; dsp( "Dominent Master (this device):", NORMAL, TABLE-4, 1 ); sprintf( ts, "%.2x %.2x %s", (int)(*received_string)&0xFF, (int)(*(received_string+1))&0xFF, received_string+2 ); dsp( ts, REVERSE, TABLE-4, 32 ); } else { I_am_the_master = 0; clear_field( 25, 44, 9 ); /* remove Locate function */ dsp( "Dominent Master (another device):", NORMAL, TABLE-4, 1 ); sprintf( ts, "%.2x %.2x %s", (int)(*received_string)&0xFF, (int)(*(received_string+1))&0xFF, received_string+2 ); dsp( ts, REVERSE, TABLE-4, 35 ); drop_out(); goto monitor; /* nothing to do, but watch */ }; /* * Play with Locate function if we got here via 'L' key */ if( locate_request ) { locate_fun( t1, t2 ); locate_request = 0; goto reset_scsi_bus; }; /* * Scan all IDs for pre-SCAM devices */ if( !another_device ) { /* skip this stuff if we got here by a SCAM selection from another device */ if( (disc_ids=scan_old_devices() ) <0 ) goto elect_scam_master; dsp_hard_id(); /* display hard ID info */ hard_ids = reserved_ids | disc_ids; /* IDs not to be assigned */ available_ids = 0xFF ^ hard_ids; /* remove unavailable IDs */ }; another_device = 0; /* clear flag */ /* * Assign soft IDs to any SCAM devices with remaining IDs */ arbitrate( 0 ); master_select( LONG_SCAM_SEL_TO ); /* get into SCAM mode */ while( 1 ) { /* assign IDs until finished */ scam_xfer( SYNC_PATTERN ); /* put out a sync pattern */ if( (temp=scam_xfer(ASSIGN_ID))!=ASSIGN_ID ) { /* assign ID iteration */ sprintf( ts, "Function code corrupted: %.2x.", temp); dsp( ts, BLINK, 25, 1 ); getkey(); break; /* try another iteration */ }; if( isolate( received_string, !want_scsi_id, t1 | priority<<7, t2 ) > 0 ) /* we were isolated */ we_won = 1; /* say we won */ if( !strlen(received_string) ) break; /* no more contending devices */ if( (assigned_id=assign_id( &available_ids, (*received_string>>1)&0x03, (*(received_string+1))&0x07) ) < 0 ) { /* out of IDs or corrupted action/ID code */ drop_out; goto monitor; }; if( we_won ) { my_scsi_id = assigned_id; /* accept our ID */ want_scsi_id = 0; /* don't need another */ we_won = 0; /* clear flag */ }; dsp_scam_string( assigned_id, received_string ); /* display scam stuff on screen */ }; /* * Scan the soft ID devices for INQUIRY data and display */ drop_out(); if( scan_scam_devices( soft_ids )<0 ) goto elect_scam_master; /* * Monitor SCSI bus and keyboard for resets, SCAM selections, & keys */ monitor: another_device = 0; if( (temp = monitor_scsi(my_scsi_id)) > 0 ) goto scsi_reset_detected; if( temp == -1 && I_am_the_master ) { another_device++; /* say another device did the SCAM selection */ goto elect_scam_master; }; if( temp == -1 ) goto slave_mode; if( temp < -1 ) goto we_are_selected; if( (key = check_keyboard()) == ESC ) { drop_out(); cls(); return; }; if( key == 'r' || key == 'R' ) goto reset_scsi_bus; if( key == 'l' || key == 'L' ) { if( !I_am_the_master ) goto monitor; locate_request++; goto reset_scsi_bus; }; dsp_time(); goto monitor; /* * Slave Mode: Some other master is dominent */ slave_mode: priority = 1; if( !slave_select(1) ) goto re_elect_scam_master; /* no master found ?!? */ while( check_cd() ) { if( (quintet=scam_xfer(0)) == SYNC_PATTERN ) { if( (quintet=scam_xfer(0)) == ASSIGN_ID || quintet == SET_PRIORITY ) { if( quintet == SET_PRIORITY ) priority = 1; /* * We are in an ASSIGN ID isolation stage */ result = isolate( received_string, !want_scsi_id, t1 | priority<<7, t2 ); if( result < 0 ) break; /* master dropped C/D */ if( result ) { /* we were isolated */ temp = get_action_code(); if( temp == -1 || temp < -4 ) continue; if( temp == -2 ) priority = 0; if( temp == -3 ) dsp("L o c a t e", YELLOWBLINK, 1, 34 ); if( temp == -4 ) dsp(" ", NORMAL, 1, 34 ); if( temp > -1 ) { my_scsi_id = assigned_id = convert_id( temp ); dsp_scam_string( assigned_id, received_string ); /* display scam stuff on screen */ clear_field( TABLE+2*temp, INQ_COL, 0); /* clear line for our ID */ dsp( "(This device)", NORMAL, TABLE+2*temp, INQ_COL ); want_scsi_id = 0; /* don't need another id */ }; } else { /* some other device was isolated */ temp = get_action_code(); if( temp > -1 ) { dsp_scam_string( convert_id(temp), received_string ); clear_field( TABLE+2*temp, INQ_COL, 0); /* clear line for our ID */ dsp( "soft ID (Some other device)", NORMAL, TABLE+2*temp, TYPE_COL ); }; }; }; }; }; /* * Master(s) dropped C/D */ drop_out(); /* release all signals */ goto monitor; /* * We are selected (perform limited commands) */ we_are_selected: result = perform_scsi_io( my_scsi_id ); if( result ) goto scsi_reset_detected; goto monitor; } /***** end of main *****/ /**** Initialize Control-C Handler * */ void init_ctrlc( void ) { /* Modify CTRL+C behavior. */ if( signal( SIGINT, ctrlchandler ) == SIG_ERR ) { fprintf( stderr, "Couldn't set SIGINT\n" ); abort(); } } /***** end of init_ctrlc *****/ /**** Control-C Handler (ignores ^C) * */ void ctrlchandler() { /* On each ^C, the ^C interrupt must be re-vectored to our handler since by * default it is reset to the system handler. */ signal( SIGINT, ctrlchandler ); /* Tell system to call us next time */ } /***** end of ctrlchandler *****/ /**** Process command line arguments * */ int process_args( int argc, char *argv[] ) { char *s, *p; int error = 0; while( --argc > 0 && (*++argv)[0] == '-' ) for( s = argv[0]+1 ; *s != '\0' ; s++ ) switch( *s ) { case 'a': case 'A': want_scsi_id = 0; use_id = 0; break; case 'e': case 'E': pause_disabled = 0; break; case 'n': case 'N': test_mode = 1; break; case 'p': case 'P': scsi = parsehex(&s); break; case 'r': case 'R': reserved_ids = parsehex(&s); break; case 'v': case 'V': p = parse_str(&s, 8); if( p == NULL ) { error++; break; }; strcpy( vendor_id, p ); break; case 'm': case 'M': p = parse_str(&s, 16); if( p == NULL ) { error++; break; }; strcpy( model, p ); break; case 's': case 'S': p = parse_str(&s, 5); if( p == NULL ) { error++; break; }; strcpy( serial_no, p ); break; default: printf( "scam: illegal option %c\n", *s ); error++; break; }; if( argc > 0 || error > 0 ) { printf( "Usage: scam [-a][ -e][ -n][ -pxxx][ -rxx][ -v]\n\ [ -m][ -s]\n\n\ \t -a = anonymous (don't request an ID for ourself)\n\ \t -e = enable diagnostic pauses\n\ \t -n = no scsi adapter\n\ \t -pxxx = hex 53C80 port address [default = 330h]\n\ \t -rxx = hex reserved ID bits (e.g., -r40 reserves SCSI ID 6)\n\ \t -v = Vendor ID string [8 chars max] (e.g., -v\"NCR \")\n\ \t -m = Model string [16 chars max] (e.g., -m\"Model ABC\")\n\ \t -s = Serial Number string [5 chars max] (e.g., -s12345)\n\ \n\ \t Note: The double quote characters (\") are only necessary if\n\ \t blanks are included in the Vendor ID, Model, or Serial #." ); return -1; }; return 0; } /***** end of process_args *****/ /**** parsehex * * parse up to 4 characters of input string as a hex number and return int */ int parsehex( char **ps ) { int val = 0, digit, i; for( i=4; i; i--) { switch(*++(*ps)) { case '0': digit = 0; break; case '1': digit = 1; break; case '2': digit = 2; break; case '3': digit = 3; break; case '4': digit = 4; break; case '5': digit = 5; break; case '6': digit = 6; break; case '7': digit = 7; break; case '8': digit = 8; break; case '9': digit = 9; break; case 'a': case 'A': digit = 10; break; case 'b': case 'B': digit = 11; break; case 'c': case 'C': digit = 12; break; case 'd': case 'D': digit = 13; break; case 'e': case 'E': digit = 14; break; case 'f': case 'F': digit = 15; break; default: {*--(*ps);return val;}; }; val = val*16+digit; }; return val; } /***** end of parsehex *****/ char str[80]; /**** parse_str * * parse up to n characters of input string as a string and return *char */ char *parse_str( char **ps, int n ) { int i; char *p, c; p = str; for( i=0; i 25 ) row = 25; if( col < 1 ) col = 1; if( col >80 ) col = 80; if( num < 1 ) num = 81-col; if( num > 81-col ) num = 81-col; p = s; for( ; num; num--) *p++ = ' '; *p = '\0'; dsp( s, NORMAL, row, col ); } /***** end of clear_field *****/ /**** cls - clear the screen * */ void cls() { inregs.h.ah = 6; /* Initialize or Scroll Up Window */ inregs.h.al = 0; /* number of lines ( 0 => entire window) */ inregs.h.bh = 7; /* Attribute byte */ inregs.h.ch = 0; /* y (upper left corner) */ inregs.h.cl = 0; /* x (upper left corner) */ inregs.h.dh = 24; /* y (lower right corner) */ inregs.h.dl = 79; /* x (lower right corner) */ int86( 0x10, &inregs, &outregs ); set_cursor( 1, 1); pickcursor( 0 ); } /***** end of cls *****/ /**** pickcursor - pick a big or little cursor * * input: big = 0 ==> small (underscore) cursor * != 0 ==> large (block) cursor */ void pickcursor( int big ) { inregs.h.ah = 1; /* Set Cursor Type function */ inregs.h.ch = 0; /* Starting Scan Line */ inregs.h.cl = 7; /* Ending Scan Line */ if( big ) int86( 0x10, &inregs, &outregs ); else { inregs.h.ch = 6; /* Starting Scan Line */ int86( 0x10, &inregs, &outregs ); }; } /***** end of pickcursor *****/ /**** check_keyboard * * returns: 0 if no key has been hit * getkey function's key code if a key has been hit */ int check_keyboard( void ) { if( kbhit() ) return getkey(); else return 0; } /***** end of check_keyboard *****/ /**** get key * * returns: key code */ int getkey( void ) { int key; key = getch(); if( (key == 0) || (key == 0xe0) ) return (key<<8) | getch(); else return key; } /***** end of getkey *****/ /**** Locate function * * Invoke the Locate function for 5 seconds on each SCAM device, * one at a time */ void locate_fun( int t1, int t2 ) { int i; char *s; clock_t l_time, tmp_t; s = received_string; for( i = 8; i < 26; i++ ) clear_field( i, 1, 0 ); /* clear lines 6--24 */ dsp( "Locating each SCAM device for 5 seconds...", BOLD, 10, 1); dsp( "Locating:", NORMAL, 16, 1 ); arbitrate( 0 ); master_select( LONG_SCAM_SEL_TO ); /* get into SCAM mode */ while( 1 ) { iterate( t1, t2, 0x14, 0x12 ); /* turn on Locate */ if( !( (int)(*s)&0x80 ) ) { /* when a priority bit is found off... */ iterate( t1, t2, 0x14, 0x0B ); /* turn off Locate */ return; /* and return */ }; sprintf( ts, "%.2x %.2x %s", (int)(*s)&0xFF, (int)(*(s+1))&0xFF, s+2 ); dsp( ts, REVERSE, 16, 11 ); l_time = clock(); /* time locate displayed */ while( l_time ) { if(((tmp_t=clock()) < l_time) ) l_time = tmp_t; /* cure for another task changing the time */ if((tmp_t-l_time) < 5000) continue; /* not 5 seconds yet */ l_time = 0; /* say we've done */ }; clear_field( 16, 11, 0 ); iterate( t1, t2, 0x14, 0x0B ); /* turn off Locate */ iterate( t1, t2, 0x14, 0x18 ); /* turn off priority bit */ }; } /**** SCAM iteration * * inputs: t1 -- type code byte 1 * t2 -- type code byte 2 * a1 -- action/ID code quintet 1 * a2 -- action/ID code quintet 2 * */ void iterate( int t1, int t2, int a1, int a2 ) { int it_is_us = 0; scam_xfer( SYNC_PATTERN ); /* put out a sync pattern */ scam_xfer( ASSIGN_ID ); /* assign ID iteration */ if( isolate( received_string, PARTICIPATE, t1 | priority<<7, t2 ) > 0 ) it_is_us++; scam_xfer( a1 ); /* action/ID code 1 */ scam_xfer( a2 ); /* action/ID code 2 */ if( it_is_us ) if( a1 == 0x14 ) { if( a2 == 0x12 ) dsp("L o c a t e", YELLOWBLINK, 1, 34 ); if( a2 == 0x0B ) dsp(" ", NORMAL, 1, 34 ); if( a2 == 0x18 ) priority = 0; }; return; } /**** isolate - Do the isolation stage of a SCAM iteration * * returns: 1 - we were isolated (our values were biggest or * master asserted DB4) * 0 - someone else's values were bigger * -1 - C/D dropped (shouldn't happen if we are the dominent master) * inputs: rcvd_stuff - address of string to accept rcv'd bytes * action - 0 => output bytes (i.e., participate) * 1 => output nothing * t1 - type code byte 1 * t2 - type code byte 2 */ int isolate( char *rcvd_stuff, int action, int t1, int t2 ) { int done, string_length, rc; char *s, byte_bucket, type_byte; defer = 0; /* say we are not deferring...yet */ string_length = 0; /* * send type bytes */ if( rc=byte_xfer(rcvd_stuff++,(char)(t1&0xFF),action) ) goto done; string_length++; if( !check_cd() ) goto lost_cd; if( rc=byte_xfer(rcvd_stuff++,(char)(t2&0xFF),action) ) goto done; string_length++; if( !check_cd() ) goto lost_cd; /* * Send our ID stuff and capture received ID stuff */ s = vendor_id; /* point to vendor ID string */ while( *s ) { /* output vendor ID string */ if( rc=byte_xfer(rcvd_stuff++,*s++,action) ) goto done; if( !check_cd() ) goto lost_cd; string_length++; } s = model; /* point to model string */ while( *s ) { /* output model string */ if( rc=byte_xfer(rcvd_stuff++,*s++,action) ) goto done; if( !check_cd() ) goto lost_cd; string_length++; } s = serial_no; /* point to serial number string */ while( *s ) { /* output serial number string */ if( rc=byte_xfer(rcvd_stuff++,*s++,action) ) goto done; if( !check_cd() ) goto lost_cd; string_length++; } /* * Looking good. Output nothing: if nothing returned, we win */ rc=byte_xfer(rcvd_stuff++,'\0',1); done: /* defer or terminate condition */ if( rc == 2 ) return !action; /* terminate: say we were isolated if we were participating, else say someone else won */ /* defer: keep handshaking and saving string */ if( !check_cd() ) goto lost_cd; string_length++; while( string_length < MAX_STR ) { if( byte_xfer(rcvd_stuff++,'\0',1)==2 ) {rcvd_stuff = '\0'; return 0;}; string_length++; if( !check_cd() ) goto lost_cd; }; /* string too long for us -- trash the rest */ while( byte_xfer(&byte_bucket,'\0',1)!=2 ) if( !check_cd() ) goto lost_cd; rcvd_stuff = '\0'; return 0; lost_cd: rcvd_stuff = '\0'; return -1; } /***** end of isolate *****/ /**** SCAM byte transfer * * byte_xfer( &rbyte, byte, action ) * * returns: 0 - transfer okay (continue and/or our byte was none) * 1 - defer (someone else beat our value) * 2 - terminate (someone asserted DB4 or end of string) * * inputs: &rbyte - address to store returned byte * byte - byte to output * action - 0 => output byte * 1 => output nothing */ int byte_xfer(char *outbyte, char inbyte, int action) { int i, ret_code, rb; char tmp_byte_in, tmp_byte_out; tmp_byte_in = inbyte; tmp_byte_out = 0; ret_code = 0; for( i=8; i; i-- ) { switch( bit_xfer(&rb,(tmp_byte_in>>(i-1))&1,defer|action) ) { case 1: defer = 1; ret_code = 1; break; case 2: ret_code = 2; goto byte_terminate; case 0: break; }; tmp_byte_out = tmp_byte_out | (rb<<(i-1)); }; byte_terminate: *outbyte = tmp_byte_out; return ret_code; } /***** end of byte_xfer *****/ /**** SCAM bit transfer * * bit_xfer( &rbit, bit, action ) * * returns: 0 - transfer okay (continue and/or our bit was none) * 1 - defer (someone else beat our value) * 2 - terminate (someone else asserted DB4 or end of string) * * inputs: &rbit - address to store returned bit (right justified) * bit - bit to output: 0 => DB0 * 1 => DB1 * action - 0 => output bit * 1 => defer (don't output a bit) * -1 => terminate (output DB4) */ int bit_xfer(int *rbit, int bit, int action ) { int quintet, rcvd_bit; bit = bit & 1; switch(action) { case 0: quintet = bit ? 2 : 1; break; case 1: quintet = 0; break; case -1: quintet = 0x10; break; }; rcvd_bit = scam_xfer(quintet); /* do the scam transfer */ *rbit = (rcvd_bit & 0x02) >>1; /* get bit from DB1 */ if( rcvd_bit>0x0f ) return 2; /* DB4 true -- terminate */ switch(action) { case 0: if( !bit ) { /* we outputted an 0 (DB0) */ if( rcvd_bit & 2 ) /* if DB1 was true, defer */ return 1; }; return 0; /* otherwise, continue */ case 1: /* we outputted nothing */ if( rcvd_bit ) return 0; /* continue */ else return 2; /* terminate */ }; } /***** end of bit_xfer *****/ /**** assign SCSI ID * * returns: ID assigned * -1 if no more IDs available * * inputs: *aid - available IDs * id_valid - ID valid field from type code byte 1 * pid - ID field from type code byte 2 * * Global inputs/outputs: received_string, scam_str array * * side effect: soft_ids updated to bit-mapped list of soft IDs */ int assign_id( int *aid, int id_valid, int pid ) { int q1 = 0x18, q2, i; int preferred_id = 7, pid_bit, prev_id; if( id_valid == 1 || id_valid == 2 ) preferred_id = pid; if( (prev_id=previous_device(received_string+2) ) > -1 ) { /* * we've assigned this device before -- try to use same ID */ if( (1<> 3) == count ); } /***** end of valid_code *****/ /**** converts an SCSI address into an SCSI ID bit * * returns: ID bit (01h..80h) * * input: SCSI address (0..7) */ int convert_id( int id_code ) { return 1<>1 ) { if( arbitrate(0) ) /* arbitrate with no ID */ return -1; /* late SCAM device arrived */ for( i=1; i ; i++); if( select( id, SEL_WO_ATN, SHORT_SCAM_SEL_TO) ){ /* do SASI-style selection */ continue; /* selection timed out */ } else { /* found pre-SCAM device */ if(!scsi) continue; /* didn't really find one */ hard_ids_found |= id; /* add to our list */ for( i=3; i; i-- ) /* try up to 3 times to do... */ if(do_inquiry( id )==GOOD) break; /* INQUIRY command */ }; }; return hard_ids_found; /* return our list */ } /***** end of scan_old_devices *****/ /**** scan SCAM devices (to get INQUIRY data) * * returns: 0 - done * -1 - SCAM selection detected */ int scan_scam_devices( int soft_ids ) { int i, id; for( id=0x80, i=7; id; id=id>>1, i-- ) { if(!(id&soft_ids)) continue; /* skip non-soft ID devices */ if( id==my_scsi_id ) { /* don't select ourself */ clear_field( TABLE+2*i, INQ_COL, 0); dsp( "(INQUIRY not issued to ourself)", NORMAL, TABLE+2*i, INQ_COL ); continue; }; if( arbitrate(0) ) /* arbitrate with no ID */ return -1; /* late SCAM device arrived */ if(select(id,SEL_WO_ATN,LONG_SCAM_SEL_TO)) /* do SASI-style selection */ continue; /* selection timed out (probably host) */ else /* found SCAM device */ for( i=3; i; i-- ) /* try up to 3 times to do... */ if(do_inquiry( id )==GOOD) break; /* INQUIRY command */ }; return 0; /* done */ } /***** end of scan_scam_devices *****/ /**** do INQUIRY command (we already have the target selected w/o ATN) * * outputs selected fields from INQUIRY data to inq_data structure * _p_r_i_m_i_t_i_v_e_ INQUIRY function. * * returns: status byte received or * -1 for unexpected disconnect (BUS FREE) */ int do_inquiry( int id ) { int rc, i, *ip, adrs, ph, not_done; if(!scsi) return GOOD; /* don't try w/o a host adapter */ not_done = 1; while( not_done ) { ph=wait_for_req(); if( ph<0 ) return -1; /* unexpected BUS FREE */ switch( ph ) { case 0: /* DATAOUT */ dsp("DATA OUT phase on INQUIRY!", BLINK, 24,1 ); while( init_byte_xfer(DATAOUT,0)>=0 ); /* send it 0s 'till it chokes */ break; case 1: /* DATAIN */ strnset(inq_dat,'\0',MAX_INQ_DATA); /* clear out INQUIRY Data Buffer */ c = inq_dat; /* point at INQUIRY Data Buffer */ for( i=MAX_INQ_DATA; i; i-- ) { if((rc=init_byte_xfer(DATAIN,0))>=0) *c++=rc; /* store INQUIRY Data */ else break; }; while( init_byte_xfer(DATAIN,0)>=0 ); /* trash any extra INQUIRY data */ break; case 2: /* COMMAND */ ip = inq_cdb; /* point at INQUIRY CDB */ for( i=6; i; i-- ) if( (rc=init_byte_xfer(COMMAND,*ip++)<0) ) dsp("Premature end of COMMAND phase",BLINK, 24,1 ); break; case 3: /* STATUS */ status_byte=init_byte_xfer(STATUS,0); break; case 4: /* RSVDOUT */ dsp("RESERVED OUT phase on INQUIRY!", BLINK, 24,1 ); while( init_byte_xfer(DATAOUT,0)>=0 ); /* send it 0s 'till it chokes */ break; case 5: /* RSVDIN */ dsp("RESERVED IN phase on INQUIRY!", BLINK, 24,1 ); while( init_byte_xfer(DATAOUT,0)>=0 ); /* put it all in the bit bucket */ break; case 6: /* MSGOUT */ dsp("MESSAGE OUT phase on INQUIRY w/ no ATN signal!", BLINK, 24,1 ); while( init_byte_xfer(DATAOUT,0)>=0 ); /* send it 0s 'till it chokes */ break; case 7: /* MSGIN */ msg_in_byte=init_byte_xfer(MSGIN,0); if(msg_in_byte==COMMAND_COMPLETE) not_done = 0; break; default: sprintf(ts,"Illegal phase in INQUIRY: %x", ph); dsp(ts,BLINK, 24,1 ); }; }; drop_out(); /* drop all 53C80 signals */ for( adrs=-1; id ; id=id>>1,adrs++); /* convert ID bit to ID address */ inquiry_data_to_ascii(inq_data[adrs],inq_dat); clear_field( TABLE+2*adrs, INQ_COL, 0 ); dsp( inq_data[adrs], NORMAL, TABLE+2*adrs, INQ_COL ); return status_byte; } /***** end of do_inquiry *****/ /**** convert INQUIRY Data to ASCII string * */ void inquiry_data_to_ascii( char *d, char *s ) { int i; char c; for( i=8; i; i-- ) { *d++ = bin2ascii( *s>>4 ); *d++ = bin2ascii( *s++ ); *d++ = ' '; }; for( i=MAX_INQ_DATA-8; i; i-- ) *d++ = *s++; } /***** end of inquiry_data_to_ascii *****/ /**** Binary to ASCII * */ char bin2ascii( char b ) { char c; c = ( b & 0x0F ) + '0'; if( c > '9' ) c += 'A'-'9'-1; return c; } /***** end of bin2ascii *****/ /****************************************************************** * Low-level SCSI Functions * * Code below here may need changes for other SCSI protocol chips * ******************************************************************/ /**** set scsi port * * sets base address of 53C80 to: 0 if test mode (no chip) * SCSIPORT if no command line parameter * (see process_args for command line parsing) */ void set_scsi_port( void ) { if( test_mode ) { scsi = 0; return; }; if( scsi<0 ) scsi = SCSIPORT; } /***** end of set_scsi_port *****/ /**** reset SCSI bus * */ void reset_scsi( void ) { if(!scsi) return; /* no scsi: skip it */ outp( scsi+0, 0 ); /* clear out 53C80 registers */ outp( scsi+2, 0 ); outp( scsi+3, 0 ); outp( scsi+4, 0 ); outp( scsi+1, 0x80 ); /* assert RST/ */ delay( RST_HOLD_TIME ); /* hold RST/ for a while */ outp( scsi+1, 0 ); } /***** end of reset_scsi *****/ /**** arbitration function * * returns: 0 - arbitration won * 1 - SCAM selection detected * input: our scsi id, bit significant: * 0x00 - no ID (for SCAM) * 0x01 - ID 0 * 0x02 - ID 1 * 0x04 - ID 2 * 0x08 - ID 3 * 0x10 - ID 4 * 0x20 - ID 5 * 0x40 - ID 6 * 0x80 - ID 7 */ int arbitrate( int scsi_id ) { int temp; if(!scsi) return 0; /* no scsi: say we won arbitration */ while( 1 ) { outp( scsi+1, 0x00 ); /* clear initiator command register */ outp( scsi+2, 0x00 ); /* clear mode register */ outp( scsi+0, scsi_id ); /* scsi_id to Output Reg */ outp( scsi+2, 0x01 ); /* Arbitate for SCSI Bus */ while( !(inp(scsi+1)&0x40) ) /* while not in ARBITRATION phase */ if( (inp(scsi+4)&0x52)==0x12 ) return 1; /* check for SCAM selection */ delay( 3 * ARBITRATION_DELAY ); /* 3 arbitration delays */ if( inp(scsi+1)&0x20 ) continue; /* Arbitration lost (SEL true) */ if( (inp(scsi+0)^scsi_id)>scsi_id ) continue; /* someone has a higher ID */ if( inp(scsi+1)&0x20 ) continue; /* Arbitration lost (SEL true) */ outp( scsi+1, 0x04 ); /* Assert SEL */ if( inp(scsi+1)&0x20 ) continue; /* Arbitration lost (SEL true) */ delay( 2 ); /* Bus Clear+Bus Settle delay = 1.2 us */ return 0; } } /***** end of arbitrate *****/ /**** selection function * * returns: 0 - selection successful * 1 - selection timeout occurred * * inputs: scsi_ids - our_id | sel_id * * set_atn - 0 => select w/o ATN, 1 => select with ATN * * sel_time_out - selection time out in microseconds */ int select( int scsi_ids, int set_atn, long sel_time_out ) { long sel_abort_time; int temp; char tempstr[80]; if(!scsi) return 0; /* no scsi: say selection sucessful */ outp( scsi+0, scsi_ids ); /* scsi_ids to Output Reg */ outp( scsi+1, 0x0d|(set_atn<<1) );/* assert BSY, SEL, ADB, & possibly ATN */ outp( scsi+2, 0x00 ); /* reset ARB bit */ outp( scsi+3, 0x00 ); /* reset target command register */ outp( scsi+4, 0x00 ); /* reset selection enable register */ outp( scsi+1, 0x05|(set_atn<<1));/* release BSY (and continue other bits) */ if ( delay_test_true( scsi+4, 0x40, sel_time_out ) ) /* is BSY true? */ { outp( scsi+1, 0x00 ); /* yes; drop SEL, ADB, & possibly ATN */ return 0; /* return success */ } /* * Selection timeout procedure */ outp( scsi+1, 0x04|(set_atn<<1) ); /* drop ADB bit; retain SEL */ outp( scsi+0, 0x00 ); /* reset SCSI IDs */ if ( delay_test_true( scsi+4, 0x40, SELECTION_ABORT_TIME ) ) /* is BSY true? */ { outp( scsi+1, 0x00 ); /* better late than never; drop SEL */ return 0; /* return success */ } outp( scsi+1, 0x00 ); /* timed out -- drop BSY */ return 1; } /***** end of select *****/ /**** wait for REQ signal to be asserted * * returns: current SCSI bus phase or * -1 for unexpected disconnect */ int wait_for_req( void ) { int scsi_stat; if(!scsi) return -1; /* if no SCSI Host Adapter, say unexpected BUS FREE occurred */ while( !((scsi_stat=inp(scsi+4))&0x20) ) /* while REQ is false... */ if( !(scsi_stat&0x40) ) return -1; /* if BSY goes false, say unexpected BUS FREE occurred */ return (scsi_stat>>2)&0x07; /* return MSG,C/D,I/O */ } /***** end of wait_for_req *****/ /**** initiator byte transfer * * returns: byte received (if INFO XFER IN) or byte sent * -1 -- -8 if wrong phase error (add 8 for new phase) * -9 if unexpected disconnect * * inputs: phase - expected INFORMATION TRANSFER phase number * (see #defines for the coding) * byte to send (ignored on INFO XFER IN phases) */ int init_byte_xfer( int phase, int out_byte ) { int scsi_stat, actual_phase, in_byte; if(!scsi) return 0; /* no scsi: say sucessful; return 0 */ scsi_stat = inp(scsi+4); /* get Current SCSI Bus Status */ if( !(scsi_stat&0x40) ) return -9; /* unexpected disconnect */ actual_phase=(scsi_stat>>2)&0x07; /* get current phase */ if( actual_phase!=phase ) return -8+actual_phase; /* say phase error */ outp( scsi+3, actual_phase ); /* set phase control value */ if( !(actual_phase&1) ) /* if INFO OUT phase */ outp( scsi+0, out_byte ); /* write byte to output register */ while( 1 ) { /** spin waiting for REQ true **/ scsi_stat = inp(scsi+4); /* get Current SCSI Bus Status */ if( !(scsi_stat&0x40) ) return -9; /* unexpected disconnect */ if( scsi_stat&0x20 ) break; /* target asserted REQ */ } if( actual_phase&1 ) { /* if INFO IN phase */ in_byte = inp( scsi+0 ); /* read byte from SCSI bus */ outp( scsi+1, 0x10 ); /* assert ACK */ } else { /* else INFO OUT phase */ outp( scsi+1, 0x01 ); /* assert data bus (ADB) */ outp( scsi+1, 0x11 ); /* assert ACK + ADB */ }; while( 1 ) { /** spin waiting for REQ false **/ scsi_stat = inp(scsi+4); /* get Current SCSI Bus Status */ if( !(scsi_stat&0x40) ) return -9; /* unexpected disconnect */ if( !(scsi_stat&0x20) ) break; /* target released REQ */ } if( actual_phase&1 ) { /* if INFO IN phase */ outp( scsi+1, 0x00 ); /* release ACK */ return in_byte; /* return byte received */ } else { /* else INFO OUT phase */ outp( scsi+1, 0x01 ); /* release ACK; maintain ADB */ return out_byte; /* return byte sent */ }; } /***** end of init_byte_xfer *****/ /* Some global variables for target mode operations */ int lun; int parity_error = 0; int cdb[16]; /**** perform selected SCSI commands * * _P_r_i_m_i_t_i_v_e_ target command support. Only supports minimal * commands and features. Reports CHECK CONDITION status on all commands * except INQUIRY, TEST UNIT READY, and REQUEST SENSE. Disconnects are * not supported. Doesn't check reserved bits. Etc. * * This function should only be invoked if a selection is in progress to * my_id. * * returns: 0 -- normal completion * -1 -- SCSI reset occurred * * input: my_id: our scsi ID bit */ int perform_scsi_io( int my_id ) { int selection_ids, atn, temp, status; outp( scsi+2, 0x60 ); /* target mode and enable parity checking */ selection_ids = inp( scsi+0 ); /* get selection IDs */ parity_error = inp( scsi+5 ) & 0x20; /* check parity */ if( !(my_id & selection_ids) || /* if selection went away or */ parity_error ) { /* there is a parity error... */ drop_out(); /* release all signals and */ return 0; /* ignore selection */ }; outp( scsi+1, 0x08 ); /* assert BSY */ atn = inp( scsi+5 ) & 0x02; /* read ATN signal */ deglitch( scsi+4, 0x02 ); /* spin until SEL is false */ /* * Connected as a target -- ready for information transfer phases */ lun = -1; /* say LUN is not known yet */ if( atn ) { lun = initial_message_out(); /* LUN is all we need */ if ( lun < 0 ) { /* simplistic error handling */ drop_out(); return ( lun==-1 ? -1 : 0 ); }; }; if( temp=get_cdb( cdb ) ) goto reset; /* * Received CDB, parse it */ if( lun == -1 ) lun = cdb[1] >> 5; switch( cdb[0] ) { case INQUIRY: if( temp=send_inq_data( lun, cdb[4] ) ) goto reset; if( temp=targ_send_byte( STATUS, GOOD ) ) goto reset; if( temp=targ_send_byte( MSGIN, COMMAND_COMPLETE ) ) goto reset; break; case REQUEST_SENSE: if( temp=send_sense_data( lun, cdb[4] ) ) goto reset; if( temp=targ_send_byte( STATUS, GOOD ) ) goto reset; if( temp=targ_send_byte( MSGIN, COMMAND_COMPLETE ) ) goto reset; break; case TEST_UNIT_READY: status = lun ? CHECK_CONDITION : GOOD; /* only support LUN 0 */ if( temp=targ_send_byte( STATUS, status ) ) goto reset; if( temp=targ_send_byte( MSGIN, COMMAND_COMPLETE ) ) goto reset; break; default: /* any other command results in CHECK CONDITION status */ if( temp=targ_send_byte( STATUS, CHECK_CONDITION ) ) goto reset; if( temp=targ_send_byte( MSGIN, COMMAND_COMPLETE ) ) goto reset; break; }; drop_out(); return 0; reset: drop_out(); return ( temp==-1 ? -1 : 0 ); } /***** end of perform_scsi_io *****/ /**** perform initial MESSAGE OUT phase * * returns: 0-7 -- selected LUN * -1 -- SCSI reset occurred * -2 -- command aborted * -3 -- parity error detected */ int initial_message_out( void ) { int msg_byte, lu; msg_byte = targ_read_byte( MSGOUT ); /* read message byte */ if( msg_byte < 0 ) return msg_byte; if( msg_byte < 0x80 ) return -2; /* treat ABORT,BDR,etc. as ABORT*/ lu = msg_byte & 0x1F; /* get SCSI-3 LUN field from IDENTIFY message */ while( inp( scsi+5 ) & 0x02 ) { /* while ATN signal is true... */ if( (msg_byte=targ_read_byte(MSGOUT)) /* reject stuff until */ < 0 ) return msg_byte; /* initiator gives up */ if( targ_send_byte( MSGIN, MESSAGE_REJECT ) ) return -1; }; return lu; } /***** end of initial_message_out *****/ int cdb_length[8] = { 6, 10, 10, 0, 0, 12, 0 , 0 }; /**** get CDB * * returns: 0 -- got CDB successfully * -1 -- SCSI reset occurred * -3 -- parity error detected * -4 -- command group not supported */ int get_cdb( int *cdb ) { int cdb_byte, *p, i; p = cdb; cdb_byte = targ_read_byte( COMMAND ); /* read 1st CDB byte */ if( cdb_byte < 0 ) return cdb_byte; /* return errors */ *p++ = cdb_byte; /* store first CDB byte */ i = cdb_length[ cdb_byte>>5 ] - 1; /* calc remaining length */ if ( i < 0 ) return -4; /* unsupported group */ for( ; i; i-- ) { cdb_byte = targ_read_byte( COMMAND ); if( cdb_byte < 0 ) return cdb_byte; /* return errors */ *p++ = cdb_byte; /* store CDB bytes */ }; return 0; } /***** end of get_cdb *****/ /**** send INQUIRY Data * * returns: 0 -- sent data successfully * -1 -- SCSI reset occurred * * inputs: lu -- if nonzero, say logical unit not supported * al -- allocation length (max data to return); */ int send_inq_data( int lu, int al ) { int i, *p; if( al > 36 ) al = 36; /* send no more than 36 bytes */ p = lu ? no_lu_inq_data : targ_inq_data; /* select correct INQUIRY Data */ for( i = al; i; i-- ) if( targ_send_byte( DATAIN, *p++ ) ) return -1; return 0; } /***** end of send_inq_data *****/ int no_lu[18] = { 0x70, /* error code 70h */ 0x00, /* segement */ 0x05, /* Sense Key = ILLEGAL REQUEST */ 0,0,0,0, /* Information Bytes */ 11, /* Additional Sense Length */ 0,0,0,0, /* Command-specific information */ 0x25, /* ASC = LOGICAL UNIT NOT SUPPORTED */ 0x00, /* ASCQ */ 0, /* FRU */ 0,0,0 }; /* Sense Key Specific */ int lu_ok[18] = { 0x70, /* error code 70h */ 0x00, /* segement */ 0x05, /* Sense Key = ILLEGAL REQUEST */ 0,0,0,0, /* Information Bytes */ 11, /* Additional Sense Length */ 0,0,0,0, /* Command-specific information */ 0x20, /* ASC = INVALID COMMAND OPERATION CODE */ 0x00, /* ASCQ */ 0, /* FRU */ 0,0,0 }; /* Sense Key Specific */ /**** send REQUEST SENSE Data * * returns: 0 -- sent data successfully * -1 -- SCSI reset occurred * * inputs: lu -- if nonzero, say logical unit not supported * al -- allocation length (max data to return); */ int send_sense_data( int lu, int al ) { int i, *p; if( al > 18 ) al = 18; /* send no more than 18 bytes */ p = lu ? no_lu : lu_ok; /* select correct Sense Data */ for( i =al ; i; i-- ) if( targ_send_byte( DATAIN, *p++ ) ) return -1; return 0; } /***** end of send_sense_data *****/ /**** target receive byte * * returns: 00h - FFh -- SCSI byte received * -1 -- SCSI bus reset occurred * -3 -- parity error detected * * input: phase */ int targ_read_byte( int phase ) { int byte; outp( scsi+3, phase ); /* set phase lines */ outp( scsi+3, 0x08 | phase ); /* Assert REQ */ if( wait_for_ack_true() ) return -1; /* if reset occurs, return -1 */ byte = inp( scsi+0 ); /* read message byte */ parity_error = inp( scsi+5 ) & 0x20; /* check parity */ if( parity_error ) return -3; outp( scsi+3, phase ); /* drop REQ, maintain phase */ if( wait_for_ack_false() ) return -1; /* if reset occurs, return -1 */ return byte; } /***** end of targ_read_byte *****/ /**** target send byte * * returns: 0 -- byte transferred * -1 -- SCSI bus reset occurred * * input: phase, byte */ int targ_send_byte( int phase, int byte ) { outp( scsi+3, phase ); /* set phase lines */ outp( scsi+0, byte ); /* put byte in output data reg. */ outp( scsi+1, 0x09 ); /* assert data bus */ outp( scsi+3, 0x08 | phase ); /* Assert REQ */ if( wait_for_ack_true() ) return -1; /* if reset occurs, return -1 */ outp( scsi+3, phase ); /* drop REQ, maintain phase */ if( wait_for_ack_false() ) return -1; /* if reset occurs, return -1 */ return 0; } /***** end of targ_send_byte *****/ /**** wait for ACK to become true (or bus reset) * * returns: 0 -- ACK is true * -1 -- SCSI reset detected */ int wait_for_ack_true( void ) { while( !(inp(scsi+4) & 0x80) ) if( inp(scsi+5) & 0x01 ) return 0; return -1; } /**** wait for ACK to become false (or bus reset) * * returns: 0 -- ACK is false * -1 -- SCSI reset detected */ int wait_for_ack_false( void ) { while( !(inp(scsi+4) & 0x80) ) if( !(inp(scsi+5) & 0x01) ) return 0; return -1; } /**** SCAM master selection function * * returns: void * * input: scam_sel_time - SCAM selection time in microseconds * */ void master_select( long scam_sel_time ) { if(!scsi) return; /* no scsi: we are only device */ #if defined MASTER_SELECT pause("arbitration won -- BSY & SEL set"); #endif outp( scsi+2, (inp(scsi+2)&1)|0x40 ); /* maintain ARB bit (if set) and set target mode */ outp( scsi+0, 0x00 ); /* no ID bits to distinguish from normal selection */ outp( scsi+3, 0x04 ); /* assert MSG */ #if defined MASTER_SELECT pause("MSG set"); #endif outp( scsi+1, 0x0d ); /* assert BSY, SEL, ADB */ outp( scsi+2, 0x40 ); /* reset ARB bit & maintain target mode */ outp( scsi+4, 0x00 ); /* reset selection enable register */ outp( scsi+1, 0x05 ); /* release BSY, maintain SEL & ADB */ #if defined MASTER_SELECT pause("BSY released"); #endif delay( scam_sel_time ); /* give other devices time to see SCAM sel */ outp( scsi+3, 0x00 ); /* release MSG */ outp( scsi+2, 0x00 ); /* turn off Targ mode */ deglitch( scsi+4, 0x10 ); /* spin until MSG is false */ #if defined MASTER_SELECT pause("MSG released"); #endif outp( scsi+1, 0x0d ); /* assert BSY and maintain SEL & ADB */ #if defined MASTER_SELECT pause("BSY set"); #endif outp( scsi+2, 0x40 ); /* set Targ mode */ outp( scsi+3, 0x03 ); /* turn on C/D & I/O */ outp( scsi+0, 0xC0 ); /* turn on DB6 and DB7 */ #if defined MASTER_SELECT pause("C/D, I/O, DB6, & DB7 set"); #endif outp( scsi+1, 0x09 ); /* release SEL, maintain BSY & ADB */ deglitch( scsi+4, 0x02 ); /* spin until SEL is false */ #if defined MASTER_SELECT pause("SEL released"); #endif outp( scsi+0, 0x80 ); /* maintain DB7 and release DB6 */ deglitch( scsi+0, 0x40 ); /* spin until DB6 is false */ #if defined MASTER_SELECT pause("DB6 released"); #endif outp( scsi+1, 0x0d ); /* assert SEL and maintain BSY & ADB */ return; } /***** end of master_select *****/ /**** SCAM slave selection function * * returns: 0 - no masters responded * 1 - one or more masters responded * * input: scam_sel_time - SCAM selection time in microseconds */ int slave_select( int scam_sel_time ) { int master_present; if(!scsi) return 0; /* no scsi: say no masters responded */ outp( scsi+1, 0x0c ); /* assert BSY and SEL */ outp( scsi+2, 0x40 ); /* set target mode */ outp( scsi+3, 0x04 ); /* assert MSG */ outp( scsi+4, 0x00 ); /* reset selection enable register */ outp( scsi+1, 0x04 ); /* release BSY and maintain SEL */ delay( scam_sel_time ); /* give other devices time to see SCAM sel */ outp( scsi+3, 0x00 ); /* release MSG */ deglitch( scsi+4, 0x10 ); /* spin until MSG is false */ outp( scsi+1, 0x0c ); /* assert BSY and maintain SEL */ outp( scsi+3, 0x01 ); /* assert I/O */ outp( scsi+0, 0xC0 ); /* set DB6 and DB7 */ outp( scsi+1, 0x09 ); /* assert ADB, maintain BSY, & release SEL */ deglitch( scsi+4, 0x02 ); /* spin until SEL is false */ outp( scsi+0, 0x80 ); /* release DB6 and maintain DB7 */ master_present = inp(scsi+4)&0x08; /* C/D true if master present */ deglitch( scsi+0, 0x40 ); /* spin until DB6 is false */ if( master_present ) { outp( scsi+1, 0x0d ); /* assert SEL and maintain BSY & ADB */ return 1; }; outp( scsi+3, 0x00 ); /* release I/O */ outp( scsi+1, 0x00 ); /* release BSY and ADB */ outp( scsi+0, 0x00 ); /* clean up */ return 0; /* say no master found */ } /***** end of slave_select *****/ /**** SCAM transfer cycle * * returns: value received on DB4-0 * * input: value to output on DB4-0 (wire-OR'd with values from all * participating devices) */ int scam_xfer( int value ) { int read_value; value = value&0x1f; if(!scsi) return value; /* no scsi: return our value */ outp( scsi+0, value|0xa0 ); /* output value to DB4-0; assert DB5, DB7 */ #if defined SCAM_XFER pause("Value to DB4-0; DB5 & DB7 set"); #endif outp( scsi+0, value|0x20 ); /* release DB7 */ deglitch( scsi+0, 0x80 ); /* spin until DB7 is false */ #if defined SCAM_XFER pause("DB7 released"); #endif read_value = inp(scsi+0)&0x1f; /* read value from DB4-0 */ outp( scsi+0, value|0x60 ); /* assert DB5, DB6 */ #if defined SCAM_XFER pause("DB6 set"); #endif outp( scsi+0, value|0x40 ); /* release DB5 */ deglitch( scsi+0, 0x20 ); /* spin until DB5 is false */ #if defined SCAM_XFER pause("DB5 released"); #endif outp( scsi+0, 0xc0 ); /* assert DB6, DB7; release all others */ #if defined SCAM_XFER pause("DB7 set"); #endif outp( scsi+0, 0x80 ); /* release DB6 */ deglitch( scsi+0, 0x40 ); /* spin until DB6 is false */ #if defined SCAM_XFER pause("DB6 released"); #endif return read_value; } /***** end of scam_xfer *****/ /**** de-glitch signal line * * inputs: io - I/O port to test * mask - mask to use on returned value * * Returns when the masked bit is false for at least DEGLITCH samples. * This is necessary to not respond to wire-OR glitches. DEGLITCH is * selected per guidelines in X3T9.2/93-173. I used 32 because I expect * that the loop time exceeds a Bus Settle Delay (400 ns.). */ void deglitch( int io, int mask ) { int i; for( i = DEGLITCH; i ; ) if( inp( io ) & mask ) /* if bit is true, start over */ i = DEGLITCH; else /* else, decrement counter */ i--; } /***** end of deglitch *****/ clock_t status_line_used = 0; /* flag to say status line is non-blank */ /**** monitor_scsi * * returns: 0 => nothing interesting happened * 1 => SCSI bus reset detected * -1 => SCAM Selection by someone else detected * -2 => we are being selected * * input: our_id is our assigned SCSI ID bit */ int monitor_scsi( int our_id ) { int temp; clock_t tmp_t; if(!scsi) return 0; /* no scsi: say nothing interesting */ temp = inp( scsi+4 ); /* get current scsi bus status */ if( temp & 0x80 ) { /* RST true */ dsp( "SCSI reset detected", BOLD, 25, 1 ); status_line_used = clock(); /* say when status line was used */ while( inp( scsi+4 ) & 0x80 ); /* wait for RST to go false */ return 1; }; if( (temp & 0x52) == 0x12 ) { /* SCAM selection detected */ dsp( "SCAM selection detected", BOLD, 25, 1 ); status_line_used = clock(); /* say when status line was used */ return -1; }; if( (temp & 0x52) == 0x02 ) { /* normal selection detected */ sprintf( ts, "Normal selection (%.2x) ", inp( scsi+0 ) ); dsp( ts, BOLD, 25, 1 ); status_line_used = clock(); /* say when status line was used */ if( inp( scsi+0 ) & our_id ) { /* we are being selected */ return -2; }; }; if ( status_line_used ) { if(((tmp_t=clock()) < status_line_used)) status_line_used = tmp_t-1000; /* cure for another task changing the time */ if((tmp_t-status_line_used) < 4000) return 0; /* return if it hasn't been 4 seconds */ clear_field( 25, 1, 24); status_line_used = 0; }; return 0; } /***** end of monitor_scsi *****/ /**** Check C/D signal * * returns: 1 if C/D signal is true * 0 if C/D signal is false (and releases all signals) * */ int check_cd( void ) { if(!scsi) return 1; /* no scsi: lie */ if( inp(scsi+4)&0x08 ) return 1; /* return 1 if C/D is true */ outp( scsi+3, 0x00 ); /* clear phase lines */ outp( scsi+1, 0x00 ); /* clear everything else */ return 0; } /***** end of check_cd *****/ /**** drop out * * Drop out of SCAM protocol by releasing all signals */ void drop_out( void ) { if(!scsi) return; /* no scsi */ outp( scsi+0, 0 ); /* clear out 53C80 registers */ outp( scsi+2, 0 ); outp( scsi+3, 0 ); outp( scsi+4, 0 ); outp( scsi+1, 0 ); return; } /***** end of drop_out *****/ /**** Pause --- debug aid * */ void pause( char *s ) { int s0, s4, s5; if(pause_disabled) return; sprintf( ts, "Paused (%s)", s ); dsp( ts, BLINK, 25,1); s0 = inp( scsi+0 ); s4 = inp( scsi+4 ); s5 = inp( scsi+5 ); sprintf(ts,"BSY:%d SEL:%d MSG:%d C/D:%d I/O:%d REQ:%d ACK:%d RST:%d ATN:%d Data:%.2x P%d", (s4&0x40)>>6, (s4&0x02)>>1, (s4&0x10)>>4, (s4&0x08)>>3, (s4&0x04)>>2, (s4&0x20)>>5, s5&0x01, (s4&0x80)>>7, (s5&0x02)>>1, s0, s4&0x01 ); dsp( ts, BOLD, 24, 1); getkey(); clear_field( 24, 1, 0 ); clear_field( 25, 1, 43 ); return; } /***** end of pause *****/ /******************* * Delay Functions * *******************/ #define SYS_PORT_B 0x61 #define NS_PER_TICK 15086 void delay( long us ) { int ticks; long ns; /* * Stall execution for approximately us microseconds. Watch for the DRAM * refresh bit to toggle -- which should not be affected by the * CPU speed. * * Modified 9/3/93 by John Lohmeyer to time variable delays -- was fixed * at 250 ms. Timing resolution is around 30 microseconds. */ ns = us * 1000 + NS_PER_TICK; /* delay time in nanoseconds (rounded up one tick) */ ticks = (int)( ns / NS_PER_TICK ); /* number of ticks to count */ _asm mov dx, SYS_PORT_B; _asm mov cx, ticks; _asm in al, dx; _asm mov ah, al; wait_x_us: _asm in al, dx; _asm and al, 10h; _asm cmp al, ah; _asm jz wait_x_us; _asm mov ah, al; _asm loop wait_x_us; } /***** end of delay *****/ int delay_test_true( int io, int mask, long us ) { int ticks; long ns; /* * Stall execution for approximately us microseconds or until the * mask value is present on the io port. Return 0 if the timer * expires and 1 if the mask value is found. * * Created 9/3/93 by John Lohmeyer from the fixed time delay function. */ ns = us * 1000 + NS_PER_TICK; /* delay time in nanoseconds (rounded up one tick) */ ticks = (int)( ns / NS_PER_TICK ); /* number of ticks to count */ _asm mov dx, SYS_PORT_B; _asm mov bl, mask; _asm mov cx, ticks; _asm in al, dx; _asm mov ah, al; _asm jmp wait_x_us; found_mask: return 1; wait_x_us: _asm in al, dx; _asm and al, 10h; _asm cmp al, ah; _asm jz wait_x_us; _asm mov ah, al; _asm mov dx, io; _asm in al, dx; _asm and al, bl; _asm cmp al, bl; _asm je found_mask; _asm mov dx, SYS_PORT_B; _asm loop wait_x_us; return 0; } /***** end of delay_test_true *****/