diff options
Diffstat (limited to 'sys/scsi/scsiconf.c')
-rw-r--r-- | sys/scsi/scsiconf.c | 761 |
1 files changed, 761 insertions, 0 deletions
diff --git a/sys/scsi/scsiconf.c b/sys/scsi/scsiconf.c new file mode 100644 index 0000000..fc1b5ca --- /dev/null +++ b/sys/scsi/scsiconf.c @@ -0,0 +1,761 @@ +/* + * Written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems for use under the MACH(2.5) operating system. + * + * TRW Financial Systems, in accordance with their agreement with Carnegie + * Mellon University, makes this software available to CMU to distribute + * or use in any manner that they see fit as long as this message is kept with + * the software. For this reason TFS also grants any other persons or + * organisations permission to use or modify this software. + * + * TFS supplies this software to be publicly redistributed + * on the understanding that TFS is not responsible for the correct + * functioning of this software in any circumstances. + * + * + * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE + * -------------------- ----- ---------------------- + * CURRENT PATCH LEVEL: 2 00149 + * -------------------- ----- ---------------------- + * + * 16 Feb 93 Julian Elischer ADDED for SCSI system + * 23 May 93 Rodney W. Grimes ADDED Pioneer DRM-600 cd changer + */ + +/* + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + */ + +/* +$Log: +* +*/ +#include <sys/types.h> +#include "st.h" +#include "sd.h" +#include "ch.h" +#include "cd.h" + +#ifdef MACH +#include <i386/machparam.h> +#endif MACH +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#if !defined(OSF) && !defined(__386BSD__) +#include "bll.h" +#include "cals.h" +#include "kil.h" +#else +#define NBLL 0 +#define NCALS 0 +#define NKIL 0 +#endif /* !defined(OSF) && !defined(__386BSD__) */ + +#if NSD > 0 +extern sdattach(); +#endif NSD +#if NST > 0 +extern stattach(); +#endif NST +#if NCH > 0 +extern chattach(); +#endif NCH +#if NCD > 0 +extern cdattach(); +#endif NCD +#if NBLL > 0 +extern bllattach(); +#endif NBLL +#if NCALS > 0 +extern calsattach(); +#endif NCALS +#if NKIL > 0 +extern kil_attach(); +#endif NKIL + +/***************************************************************\ +* The structure of pre-configured devices that might be turned * +* off and therefore may not show up * +\***************************************************************/ +struct predefined +{ + u_char scsibus; + u_char dev; + u_char lu; + int (*attach_rtn)(); + char *devname; + char flags; +} +pd[] = +{ +#ifdef EXAMPLE_PREDEFINE +#if NSD > 0 + {0,0,0,sdattach,"sd",0},/* define a disk at scsibus=0 dev=0 lu=0 */ +#endif NSD +#endif EXAMPLE_PREDEFINE + {0,9,9} /*illegal dummy end entry */ +}; + + +/***************************************************************\ +* The structure of known drivers for autoconfiguration * +\***************************************************************/ +static struct scsidevs +{ + int type; + int removable; + char *manufacturer; + char *model; + char *version; + int (*attach_rtn)(); + char *devname; + char flags; /* 1 show my comparisons during boot(debug) */ +} +#define SC_SHOWME 0x01 +#define SC_ONE_LU 0x00 +#define SC_MORE_LUS 0x02 +knowndevs[] = { +#if NSD > 0 + { T_DIRECT,T_FIXED,"standard","any" + ,"any",sdattach,"sd",SC_ONE_LU }, + { T_DIRECT,T_FIXED,"MAXTOR ","XT-4170S " + ,"B5A ",sdattach,"mx1",SC_ONE_LU }, +#endif NSD +#if NST > 0 + { T_SEQUENTIAL,T_REMOV,"standard","any" + ,"any",stattach,"st",SC_ONE_LU }, +#endif NST +#if NCALS > 0 + { T_PROCESSOR,T_FIXED,"standard","any" + ,"any",calsattach,"cals",SC_MORE_LUS }, +#endif NCALS +#if NCH > 0 + { T_CHANGER,T_REMOV,"standard","any" + ,"any",chattach,"ch",SC_ONE_LU }, +#endif NCH +#if NCD > 0 + { T_READONLY,T_REMOV,"SONY ","CD-ROM CDU-8012 " + ,"3.1a",cdattach,"cd",SC_ONE_LU }, + { T_READONLY,T_REMOV,"PIONEER ","CD-ROM DRM-600 " + ,"any",cdattach,"cd",SC_MORE_LUS }, +#endif NCD +#if NBLL > 0 + { T_PROCESSOR,T_FIXED,"AEG ","READER " + ,"V1.0",bllattach,"bll",SC_MORE_LUS }, +#endif NBLL +#if NKIL > 0 + { T_SCANNER,T_FIXED,"KODAK ","IL Scanner 900 " + ,"any",kil_attach,"kil",SC_ONE_LU }, +#endif NKIL + +{0} +}; +/***************************************************************\ +* Declarations * +\***************************************************************/ +struct predefined *scsi_get_predef(); +struct scsidevs *scsi_probedev(); +struct scsidevs *selectdev(); + +/* controls debug level within the scsi subsystem */ +/* see scsiconf.h for values */ +int scsi_debug = 0x0; +int scsibus = 0x0; /* This is the Nth scsibus */ + +/***************************************************************\ +* The routine called by the adapter boards to get all their * +* devices configured in. * +\***************************************************************/ +scsi_attachdevs( unit, scsi_addr, scsi_switch) +int unit,scsi_addr; +struct scsi_switch *scsi_switch; +{ + int targ,lun; + struct scsidevs *bestmatch = (struct scsidevs *)0; + struct predefined *predef; + int maybe_more; + +#ifdef SCSI_DELAY +#if SCSI_DELAY > 2 + printf("waiting for scsi devices to settle\n"); +#else SCSI_DELAY > 2 +#define SCSI_DELAY 15 +#endif SCSI_DELAY > 2 +#else +#define SCSI_DELAY 2 +#endif SCSI_DELAY + spinwait(1000 * SCSI_DELAY); + targ = 0; + while(targ < 8) + { + maybe_more = 0; /* by default only check 1 lun */ + if (targ == scsi_addr) + { + targ++; + continue; + } + lun = 0; + while(lun < 8) + { + predef = scsi_get_predef(scsibus + ,targ + ,lun + ,&maybe_more); + bestmatch = scsi_probedev(unit + ,targ + ,lun + ,scsi_switch + ,&maybe_more); + if((bestmatch) && (predef)) /* both exist */ + { + if(bestmatch->attach_rtn + != predef->attach_rtn) + { + printf("Clash in found/expected devices\n"); + printf("will link in FOUND\n"); + } + (*(bestmatch->attach_rtn))(unit, + targ, + lun, + scsi_switch); + } + if((bestmatch) && (!predef)) /* just FOUND */ + { + (*(bestmatch->attach_rtn))(unit, + targ, + lun, + scsi_switch); + } + if((!bestmatch) && (predef)) /* just predef */ + { + (*(predef->attach_rtn))(unit, + targ, + lun, + scsi_switch); + } + if(!(maybe_more)) /* nothing suggests we'll find more */ + { + break; /* nothing here, skip to next targ */ + } + /* otherwise something says we should look further*/ + lun++; + } + targ++; + } +#if NGENSCSI > 0 + /***************************************************************\ + * If available hook up the generic scsi driver, letting it * + * know which target is US. (i.e. illegal or at least special) * + \***************************************************************/ + genscsi_attach(unit,scsi_addr,0,scsi_switch); +#endif + scsibus++; /* next time we are on the NEXT scsi bus */ +} + +/***********************************************\ +* given a target and lu, check if there is a * +* predefined device for that address * +\***********************************************/ +struct predefined *scsi_get_predef(unit,target,lu,maybe_more) +int unit,target,lu,*maybe_more; +{ + int upto,numents; + + numents = (sizeof(pd)/sizeof(struct predefined)) - 1; + + for(upto = 0;upto < numents;upto++) + { + if(pd[upto].scsibus != unit) + continue; + if(pd[upto].dev != target) + continue; + if(pd[upto].lu != lu) + continue; + + printf(" dev%d,lu%d: %s - PRECONFIGURED -\n" + ,target + ,lu + ,pd[upto].devname); + *maybe_more = pd[upto].flags & SC_MORE_LUS; + return(&(pd[upto])); + } + return((struct predefined *)0); +} + +/***********************************************\ +* given a target and lu, ask the device what * +* it is, and find the correct driver table * +* entry. * +\***********************************************/ +struct scsidevs *scsi_probedev(unit,target,lu,scsi_switch, maybe_more) + +struct scsi_switch *scsi_switch; +int unit,target,lu; +int *maybe_more; +{ + struct scsidevs *bestmatch = (struct scsidevs *)0; + char *dtype=(char *)0,*desc; + char *qtype; + static struct scsi_inquiry_data inqbuf; + int len,qualifier,type,remov; + char manu[32]; + char model[32]; + char version[32]; + + + bzero(&inqbuf,sizeof(inqbuf)); + /***********************************************\ + * Ask the device what it is * + \***********************************************/ +#ifdef DEBUG + if((target == 0) && (lu == 0)) + scsi_debug = 0xfff; + else + scsi_debug = 0; +#endif DEBUG + if(scsi_ready( unit, + target, + lu, + scsi_switch, + SCSI_NOSLEEP | SCSI_NOMASK) != COMPLETE) + { + return(struct scsidevs *)0; + } + if(scsi_inquire(unit, + target, + lu, + scsi_switch, + &inqbuf, + SCSI_NOSLEEP | SCSI_NOMASK) != COMPLETE) + { + return(struct scsidevs *)0; + } + + /***********************************************\ + * note what BASIC type of device it is * + \***********************************************/ + if(scsi_debug & SHOWINQUIRY) + { + desc=(char *)&inqbuf; + printf("inq: %x %x %x %x %x %x %x %x %x %x %x %x %x\n", + desc[0], desc[1], desc[2], desc[3], + desc[4], desc[5], desc[6], desc[7], + desc[8], desc[9], desc[10], desc[11], + desc[12]); + } + + type = inqbuf.device_type; + qualifier = inqbuf.device_qualifier; + remov = inqbuf.removable; + + /* Check for a non-existent unit. If the device is returning + * this much, then we must set the flag that has + * the searcher keep looking on other luns. + */ + if (qualifier == 3 && type == T_NODEVICE) + { + *maybe_more = 1; + return (struct scsidevs *)0; + } + + /* Any device qualifier that has + * the top bit set (qualifier&4 != 0) is vendor specific and + * won't match in this switch. + */ + + switch(qualifier) + { + case 0: + qtype=""; + break; + case 1: + qtype=", Unit not Connected!"; + break; + case 2: + qtype=", Reserved Peripheral Qualifier!"; + break; + case 3: + qtype=", The Target can't support this Unit!"; + break; + + default: + dtype="vendor specific"; + qtype=""; + *maybe_more = 1; + break; + } + + if (dtype == 0) + switch(type) + { + case T_DIRECT: + dtype="direct"; + break; + case T_SEQUENTIAL: + dtype="sequential"; + break; + case T_PRINTER: + dtype="printer"; + break; + case T_PROCESSOR: + dtype="processor"; + break; + case T_READONLY: + dtype="readonly"; + break; + case T_WORM: + dtype="worm"; + break; + case T_SCANNER: + dtype="scanner"; + break; + case T_OPTICAL: + dtype="optical"; + break; + case T_CHANGER: + dtype="changer"; + break; + case T_COMM: + dtype="communication"; + break; + default: + dtype="unknown"; + break; + } + + /***********************************************\ + * Then if it's advanced enough, more detailed * + * information * + \***********************************************/ + if(inqbuf.ansii_version > 0) + { + if ((len = inqbuf.additional_length + + ( (char *)inqbuf.unused + - (char *)&inqbuf)) + > (sizeof(struct scsi_inquiry_data) - 1)) + len = sizeof(struct scsi_inquiry_data) - 1; + desc=inqbuf.vendor; + desc[len-(desc - (char *)&inqbuf)] = 0; + strncpy(manu,inqbuf.vendor,8);manu[8]=0; + strncpy(model,inqbuf.product,16);model[16]=0; + strncpy(version,inqbuf.revision,4);version[4]=0; + } + else + /***********************************************\ + * If not advanced enough, use default values * + \***********************************************/ + { + desc="early protocol device"; + strncpy(manu,"unknown",8); + strncpy(model,"unknown",16); + strncpy(version,"????",4); + } + printf(" dev%d,lu%d: type %d:%d(%s%s),%s '%s%s%s' scsi%d\n" + ,target + ,lu + ,qualifier,type + ,dtype,qtype + ,remov?"removable":"fixed" + ,manu + ,model + ,version + ,inqbuf.ansii_version + ); + /***********************************************\ + * Try make as good a match as possible with * + * available sub drivers * + \***********************************************/ + bestmatch = (selectdev(unit,target,lu,&scsi_switch, + qualifier,type,remov,manu,model,version)); + if((bestmatch) && (bestmatch->flags & SC_MORE_LUS)) + { + *maybe_more = 1; + } + return(bestmatch); +} + +/***********************************************\ +* Try make as good a match as possible with * +* available sub drivers * +\***********************************************/ +struct scsidevs +*selectdev(unit,target,lu,dvr_switch,qualifier,type,remov,manu,model,rev) +int unit,target,lu; +struct scsi_switch *dvr_switch; +int qualifier,type,remov; +char *manu,*model,*rev; +{ + int numents = (sizeof(knowndevs)/sizeof(struct scsidevs)) - 1; + int count = 0; + int bestmatches = 0; + struct scsidevs *bestmatch = (struct scsidevs *)0; + struct scsidevs *thisentry = knowndevs; + + type |= (qualifier << 5); + + thisentry--; + while( count++ < numents) + { + thisentry++; + if(type != thisentry->type) + { + continue; + } + if(bestmatches < 1) + { + bestmatches = 1; + bestmatch = thisentry; + } + if(remov != thisentry->removable) + { + continue; + } + if(bestmatches < 2) + { + bestmatches = 2; + bestmatch = thisentry; + } + if(thisentry->flags & SC_SHOWME) + printf("\n%s-\n%s-",thisentry->manufacturer, manu); + if(strcmp(thisentry->manufacturer, manu)) + { + continue; + } + if(bestmatches < 3) + { + bestmatches = 3; + bestmatch = thisentry; + } + if(thisentry->flags & SC_SHOWME) + printf("\n%s-\n%s-",thisentry->model, model); + if(strcmp(thisentry->model, model)) + { + continue; + } + if(bestmatches < 4) + { + bestmatches = 4; + bestmatch = thisentry; + } + if(thisentry->flags & SC_SHOWME) + printf("\n%s-\n%s-",thisentry->version, rev); + if(strcmp(thisentry->version, rev)) + { + continue; + } + if(bestmatches < 5) + { + bestmatches = 5; + bestmatch = thisentry; + break; + } + } + + if (bestmatch == (struct scsidevs *)0) + printf(" No explicit device driver match for \"%s %s\".\n", + manu, model); + + return(bestmatch); +} + +static int recurse = 0; +/***********************************************\ +* Do a scsi operation asking a device if it is * +* ready. Use the scsi_cmd routine in the switch * +* table. * +\***********************************************/ +scsi_ready(unit,target,lu,scsi_switch, flags) +struct scsi_switch *scsi_switch; +{ + struct scsi_test_unit_ready scsi_cmd; + struct scsi_xfer scsi_xfer; + volatile int rval; + int key; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + bzero(&scsi_xfer, sizeof(scsi_xfer)); + scsi_cmd.op_code = TEST_UNIT_READY; + + scsi_xfer.flags=flags | INUSE; + scsi_xfer.adapter=unit; + scsi_xfer.targ=target; + scsi_xfer.lu=lu; + scsi_xfer.cmd=(struct scsi_generic *)&scsi_cmd; + scsi_xfer.retries=8; + scsi_xfer.timeout=10000; + scsi_xfer.cmdlen=sizeof(scsi_cmd); + scsi_xfer.data=0; + scsi_xfer.datalen=0; + scsi_xfer.resid=0; + scsi_xfer.when_done=0; + scsi_xfer.done_arg=0; +retry: scsi_xfer.error=0; + /*******************************************************\ + * do not use interrupts * + \*******************************************************/ + rval = (*(scsi_switch->scsi_cmd))(&scsi_xfer); + if (rval != COMPLETE) + { + if(scsi_debug) + { + printf("scsi error, rval = 0x%x\n",rval); + printf("code from driver: 0x%x\n",scsi_xfer.error); + } + switch(scsi_xfer.error) + { + case XS_SENSE: + /*******************************************************\ + * Any sense value is illegal except UNIT ATTENTION * + * In which case we need to check again to get the * + * correct response. * + *( especially exabytes) * + \*******************************************************/ + if(scsi_xfer.sense.error_class == 7 ) + { + key = scsi_xfer.sense.ext.extended.sense_key ; + switch(key) + { + case 2: /* not ready BUT PRESENT! */ + return(COMPLETE); + case 6: + spinwait(1000); + if(scsi_xfer.retries--) + { + scsi_xfer.flags &= ~ITSDONE; + goto retry; + } + return(COMPLETE); + default: + if(scsi_debug) + printf("%d:%d,key=%x.", + target,lu,key); + } + } + return(HAD_ERROR); + case XS_BUSY: + spinwait(1000); + if(scsi_xfer.retries--) + { + scsi_xfer.flags &= ~ITSDONE; + goto retry; + } + return(COMPLETE); /* it's busy so it's there */ + case XS_TIMEOUT: + default: + return(HAD_ERROR); + } + } + return(COMPLETE); +} +/***********************************************\ +* Do a scsi operation asking a device what it is* +* Use the scsi_cmd routine in the switch table. * +\***********************************************/ +scsi_inquire(unit,target,lu,scsi_switch,inqbuf, flags) +struct scsi_switch *scsi_switch; +u_char *inqbuf; +{ + struct scsi_inquiry scsi_cmd; + struct scsi_xfer scsi_xfer; + volatile int rval; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + bzero(&scsi_xfer, sizeof(scsi_xfer)); + scsi_cmd.op_code = INQUIRY; + scsi_cmd.length = sizeof(struct scsi_inquiry_data); + + scsi_xfer.flags=flags | SCSI_DATA_IN | INUSE; + scsi_xfer.adapter=unit; + scsi_xfer.targ=target; + scsi_xfer.lu=lu; + scsi_xfer.retries=8; + scsi_xfer.timeout=10000; + scsi_xfer.cmd=(struct scsi_generic *)&scsi_cmd; + scsi_xfer.cmdlen= sizeof(struct scsi_inquiry); + scsi_xfer.data=inqbuf; + scsi_xfer.datalen=sizeof(struct scsi_inquiry_data); + scsi_xfer.resid=sizeof(struct scsi_inquiry_data); + scsi_xfer.when_done=0; + scsi_xfer.done_arg=0; +retry: scsi_xfer.error=0; + /*******************************************************\ + * do not use interrupts * + \*******************************************************/ + if ((*(scsi_switch->scsi_cmd))(&scsi_xfer) != COMPLETE) + { + if(scsi_debug) printf("inquiry had error(0x%x) ",scsi_xfer.error); + switch(scsi_xfer.error) + { + case XS_NOERROR: + break; + case XS_SENSE: + /*******************************************************\ + * Any sense value is illegal except UNIT ATTENTION * + * In which case we need to check again to get the * + * correct response. * + *( especially exabytes) * + \*******************************************************/ + if((scsi_xfer.sense.error_class == 7 ) + && (scsi_xfer.sense.ext.extended.sense_key == 6)) + { /* it's changed so it's there */ + spinwait(1000); + { + if(scsi_xfer.retries--) + { + scsi_xfer.flags &= ~ITSDONE; + goto retry; + } + } + return( COMPLETE); + } + return(HAD_ERROR); + case XS_BUSY: + spinwait(1000); + if(scsi_xfer.retries--) + { + scsi_xfer.flags &= ~ITSDONE; + goto retry; + } + case XS_TIMEOUT: + default: + return(HAD_ERROR); + } + } + return(COMPLETE); +} + + + + +/***********************************************\ +* Utility routines often used in SCSI stuff * +\***********************************************/ + +/***********************************************\ +* convert a physical address to 3 bytes, * +* MSB at the lowest address, * +* LSB at the highest. * +\***********************************************/ + +lto3b(val, bytes) +u_char *bytes; +{ + *bytes++ = (val&0xff0000)>>16; + *bytes++ = (val&0xff00)>>8; + *bytes = val&0xff; +} + +/***********************************************\ +* The reverse of lto3b * +\***********************************************/ +_3btol(bytes) +u_char *bytes; +{ + int rc; + rc = (*bytes++ << 16); + rc += (*bytes++ << 8); + rc += *bytes; + return(rc); +} + |