* 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.
* Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
* $Id: scsiconf.c,v 1.5 1993/08/28 03:08:53 rgrimes Exp $
#include <i386/machparam.h>
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#if !defined(OSF) && !defined(__386BSD__)
#endif /* !defined(OSF) && !defined(__386BSD__) */
/***************************************************************\
* The structure of pre-configured devices that might be turned *
* off and therefore may not show up *
\***************************************************************/
{0,0,0,sdattach
,"sd",0},/* define a disk at scsibus=0 dev=0 lu=0 */
{0,9,9} /*illegal dummy end entry */
/***************************************************************\
* The structure of known drivers for autoconfiguration *
\***************************************************************/
char flags
; /* 1 show my comparisons during boot(debug) */
{ 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
},
{ T_SEQUENTIAL
,T_REMOV
,"standard","any"
,"any",stattach
,"st",SC_ONE_LU
},
{ T_PROCESSOR
,T_FIXED
,"standard","any"
,"any",calsattach
,"cals",SC_MORE_LUS
},
{ T_CHANGER
,T_REMOV
,"standard","any"
,"any",chattach
,"ch",SC_ONE_LU
},
{ 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
},
{ T_PROCESSOR
,T_FIXED
,"AEG ","READER "
,"V1.0",bllattach
,"bll",SC_MORE_LUS
},
{ T_SCANNER
,T_FIXED
,"KODAK ","IL Scanner 900 "
,"any",kil_attach
,"kil",SC_ONE_LU
},
/***************************************************************\
\***************************************************************/
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 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
)
struct scsi_switch
*scsi_switch
;
struct scsidevs
*bestmatch
= (struct scsidevs
*)0;
struct predefined
*predef
;
printf("%s%d waiting for scsi devices to settle\n",
scsi_switch
->name
, unit
);
spinwait(1000 * SCSI_DELAY
);
maybe_more
= 0; /* by default only check 1 lun */
predef
= scsi_get_predef(scsibus
bestmatch
= scsi_probedev(unit
if((bestmatch
) && (predef
)) /* both exist */
printf("Clash in found/expected devices\n");
printf("will link in FOUND\n");
(*(bestmatch
->attach_rtn
))(unit
,
if((bestmatch
) && (!predef
)) /* just FOUND */
(*(bestmatch
->attach_rtn
))(unit
,
if((!bestmatch
) && (predef
)) /* just predef */
(*(predef
->attach_rtn
))(unit
,
if(!(maybe_more
)) /* nothing suggests we'll find more */
break; /* nothing here, skip to next targ */
/* otherwise something says we should look further*/
/***************************************************************\
* If available hook up the generic scsi driver, letting it *
* know which target is US. (i.e. illegal or at least special) *
\***************************************************************/
sg_attach(unit
,scsi_addr
,scsi_switch
);
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
,scsi_switch
,maybe_more
)
int unit
,target
,lu
,*maybe_more
;
struct scsi_switch
*scsi_switch
;
numents
= (sizeof(pd
)/sizeof(struct predefined
)) - 1;
for(upto
= 0;upto
< numents
;upto
++)
if(pd
[upto
].scsibus
!= unit
)
if(pd
[upto
].dev
!= target
)
printf("%s%d targ %d lun %d: <%s> - PRECONFIGURED -\n"
*maybe_more
= pd
[upto
].flags
& SC_MORE_LUS
;
return((struct predefined
*)0);
/***********************************************\
* given a target and lu, ask the device what *
* it is, and find the correct driver table *
\***********************************************/
struct scsidevs
*scsi_probedev(unit
,target
,lu
,scsi_switch
, maybe_more
)
struct scsi_switch
*scsi_switch
;
struct scsidevs
*bestmatch
= (struct scsidevs
*)0;
char *dtype
=(char *)0,*desc
;
static struct scsi_inquiry_data inqbuf
;
int len
,qualifier
,type
,remov
;
bzero(&inqbuf
,sizeof(inqbuf
));
/***********************************************\
* Ask the device what it is *
\***********************************************/
if((target
== 0) && (lu
== 0))
SCSI_NOSLEEP
| SCSI_NOMASK
) != COMPLETE
)
return(struct scsidevs
*)0;
SCSI_NOSLEEP
| SCSI_NOMASK
) != COMPLETE
)
return(struct scsidevs
*)0;
/***********************************************\
* note what BASIC type of device it is *
\***********************************************/
if(scsi_debug
& SHOWINQUIRY
)
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],
type
= inqbuf
.device
& SID_TYPE
;
qualifier
= inqbuf
.device
& SID_QUAL
;
remov
= inqbuf
.dev_qual2
& SID_REMOVABLE
;
/* Any device qualifier that has
* the top bit set (qualifier&4 != 0) is vendor specific and
* won't match in this switch.
case SID_QUAL_LU_OFFLINE
:
qtype
=", Unit not Connected!";
qtype
=", Reserved Peripheral Qualifier!";
return (struct scsidevs
*)0;
* 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.
qtype
=", The Target can't support this Unit!";
return (struct scsidevs
*)0;
return (struct scsidevs
*)0;
/***********************************************\
* Then if it's advanced enough, more detailed *
\***********************************************/
if((inqbuf
.version
& SID_ANSII
) > 0)
if ((len
= inqbuf
.additional_length
+ ( (char *)inqbuf
.unused
> (sizeof(struct scsi_inquiry_data
) - 1))
len
= sizeof(struct scsi_inquiry_data
) - 1;
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;
/***********************************************\
* If not advanced enough, use default values *
\***********************************************/
desc
="early protocol device";
strncpy(manu
,"unknown",8);
strncpy(model
,"unknown",16);
strncpy(version
,"????",4);
printf("%s%d targ %d lun %d: type %d(%s) %s SCSI%d\n"
,remov
?"removable":"fixed"
,inqbuf
.version
& SID_ANSII
printf("%s%d targ %d lun %d: <%s%s%s>\n"
printf("%s%d targ %d lun %d: qualifier %d(%s)\n"
/***********************************************\
* Try make as good a match as possible with *
* available sub drivers *
\***********************************************/
bestmatch
= (selectdev(unit
,target
,lu
,&scsi_switch
,
qualifier
,type
,remov
?T_REMOV
:T_FIXED
,manu
,model
,version
));
if((bestmatch
) && (bestmatch
->flags
& SC_MORE_LUS
))
/***********************************************\
* Try make as good a match as possible with *
* available sub drivers *
\***********************************************/
*selectdev(unit
,target
,lu
,dvr_switch
,qualifier
,type
,remov
,manu
,model
,rev
)
struct scsi_switch
*dvr_switch
;
int qualifier
,type
,remov
;
int numents
= (sizeof(knowndevs
)/sizeof(struct scsidevs
)) - 1;
struct scsidevs
*bestmatch
= (struct scsidevs
*)0;
struct scsidevs
*thisentry
= knowndevs
;
type
|= qualifier
; /* why? */
while( count
++ < numents
)
if(type
!= thisentry
->type
)
if(remov
!= thisentry
->removable
)
if(thisentry
->flags
& SC_SHOWME
)
printf("\n%s-\n%s-",thisentry
->manufacturer
, manu
);
if(strcmp(thisentry
->manufacturer
, manu
))
if(thisentry
->flags
& SC_SHOWME
)
printf("\n%s-\n%s-",thisentry
->model
, model
);
if(strcmp(thisentry
->model
, model
))
if(thisentry
->flags
& SC_SHOWME
)
printf("\n%s-\n%s-",thisentry
->version
, rev
);
if(strcmp(thisentry
->version
, rev
))
if (bestmatch
== (struct scsidevs
*)0)
printf(" No explicit device driver match for \"%s %s\".\n",
/***********************************************\
* Do a scsi operation asking a device if it is *
* ready. Use the scsi_cmd routine in the switch *
\***********************************************/
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
;
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
.cmd
=(struct scsi_generic
*)&scsi_cmd
;
scsi_xfer
.cmdlen
=sizeof(scsi_cmd
);
retry
: scsi_xfer
.error
=0;
/*******************************************************\
* do not use interrupts *
\*******************************************************/
rval
= (*(scsi_switch
->scsi_cmd
))(&scsi_xfer
);
printf("scsi error, rval = 0x%x\n",rval
);
printf("code from driver: 0x%x\n",scsi_xfer
.error
);
/*******************************************************\
* Any sense value is illegal except UNIT ATTENTION *
* In which case we need to check again to get the *
*( especially exabytes) *
\*******************************************************/
if(((scsi_xfer
.sense
.error_code
& SSD_ERRCODE
) == 0x70 )
||((scsi_xfer
.sense
.error_code
& SSD_ERRCODE
) == 0x71 ))
key
= scsi_xfer
.sense
.ext
.extended
.flags
& SSD_KEY
;
case 2: /* not ready BUT PRESENT! */
scsi_xfer
.flags
&= ~ITSDONE
;
scsi_xfer
.flags
&= ~ITSDONE
;
return(COMPLETE
); /* it's busy so it's there */
/***********************************************\
* 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
;
struct scsi_inquiry scsi_cmd
;
struct scsi_xfer scsi_xfer
;
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
.cmd
=(struct scsi_generic
*)&scsi_cmd
;
scsi_xfer
.cmdlen
= sizeof(struct scsi_inquiry
);
scsi_xfer
.datalen
=sizeof(struct scsi_inquiry_data
);
scsi_xfer
.resid
=sizeof(struct scsi_inquiry_data
);
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
);
/*******************************************************\
* Any sense value is illegal except UNIT ATTENTION *
* In which case we need to check again to get the *
*( especially exabytes) *
\*******************************************************/
if(((scsi_xfer
.sense
.error_code
& SSD_ERRCODE
) == 0x70 )
&& ((scsi_xfer
.sense
.ext
.extended
.flags
& SSD_KEY
) == 6))
{ /* it's changed so it's there */
scsi_xfer
.flags
&= ~ITSDONE
;
scsi_xfer
.flags
&= ~ITSDONE
;
/***********************************************\
* Utility routines often used in SCSI stuff *
\***********************************************/
/***********************************************\
* convert a physical address to 3 bytes, *
* MSB at the lowest address, *
\***********************************************/
*bytes
++ = (val
&0xff0000)>>16;
*bytes
++ = (val
&0xff00)>>8;
/***********************************************\
\***********************************************/