Home | History | Annotate | Line # | Download | only in scsipi
scsiconf.c revision 1.7
      1 /*
      2  * Written by Julian Elischer (julian (at) tfs.com)
      3  * for TRW Financial Systems for use under the MACH(2.5) operating system.
      4  * Hacked by Theo de Raadt <deraadt (at) fsa.ca>
      5  *
      6  * TRW Financial Systems, in accordance with their agreement with Carnegie
      7  * Mellon University, makes this software available to CMU to distribute
      8  * or use in any manner that they see fit as long as this message is kept with
      9  * the software. For this reason TFS also grants any other persons or
     10  * organisations permission to use or modify this software.
     11  *
     12  * TFS supplies this software to be publicly redistributed
     13  * on the understanding that TFS is not responsible for the correct
     14  * functioning of this software in any circumstances.
     15  *
     16  *	$Id: scsiconf.c,v 1.7 1993/05/20 03:46:36 cgd Exp $
     17  */
     18 
     19 #include "sys/types.h"
     20 #include "sys/param.h"
     21 #include "sys/systm.h"
     22 #include "sys/errno.h"
     23 #include "sys/ioctl.h"
     24 #include "sys/buf.h"
     25 #include "sys/proc.h"
     26 #include "sys/user.h"
     27 #include "sys/dkbad.h"
     28 #include "sys/disklabel.h"
     29 #include "scsi/scsi_all.h"
     30 #include "scsi/scsiconf.h"
     31 
     32 #include "st.h"
     33 #include "sd.h"
     34 #include "ch.h"
     35 #include "cd.h"
     36 #define	NBLL 0
     37 #define	NCALS 0
     38 #define	NKIL 0
     39 
     40 #if NSD > 0
     41 extern int sdattach();
     42 #endif NSD
     43 #if NST > 0
     44 extern int stattach();
     45 #endif NST
     46 #if NCH > 0
     47 extern int chattach();
     48 #endif NCH
     49 #if NCD > 0
     50 extern int cdattach();
     51 #endif NCD
     52 #if NBLL > 0
     53 extern int bllattach();
     54 #endif NBLL
     55 #if NCALS > 0
     56 extern int calsattach();
     57 #endif NCALS
     58 #if NKIL > 0
     59 extern int kil_attach();
     60 #endif NKIL
     61 
     62 struct scsidevs knowndevs[] = {
     63 #if NSD > 0
     64 	{
     65 		SC_TSD, T_DIRECT, T_FIXED, "standard", "any" ,"any",
     66 		sdattach, "sd" ,SC_ONE_LU
     67 	}, {
     68 		SC_TSD, T_DIRECT, T_FIXED, "MAXTOR  ", "XT-4170S	", "B5A ",
     69 		sdattach, "mx1", SC_ONE_LU
     70 	},
     71 #endif NSD
     72 #if NST > 0
     73 	{
     74 		SC_TST, T_SEQUENTIAL, T_REMOV, "standard", "any", "any",
     75 		stattach, "st" ,SC_ONE_LU
     76 	},
     77 #endif NST
     78 #if NCD > 0
     79 	{
     80 		SC_TCD, T_READONLY, T_REMOV, "SONY    ", "CD-ROM CDU-8012 ", "3.1a",
     81 		cdattach, "cd", SC_ONE_LU
     82 	},
     83 #endif NCD
     84 #if NCALS > 0
     85 	{
     86 		-1, T_PROCESSOR, T_FIXED, "standard" , "any" ,"any",
     87 		calsattach, "cals", SC_MORE_LUS
     88 	},
     89 #endif NCALS
     90 #if NCH > 0
     91 	{
     92 		-1, T_CHANGER, T_REMOV, "standard", "any", "any",
     93 		chattach, "ch", SC_ONE_LU
     94 	},
     95 #endif NCH
     96 #if NBLL > 0
     97 	{
     98 		-1, T_PROCESSOR, T_FIXED, "AEG     ", "READER	  ", "V1.0",
     99 		bllattach, "bll", SC_MORE_LUS
    100 	},
    101 #endif NBLL
    102 #if NKIL > 0
    103 	{
    104 		-1, T_SCANNER, T_FIXED, "KODAK   ", "IL Scanner 900  ", "any",
    105 		kil_attach, "kil", SC_ONE_LU
    106 	},
    107 #endif NKIL
    108 };
    109 
    110 /* controls debug level within the scsi subsystem: see scsiconf.h */
    111 int scsi_debug	= 0;
    112 
    113 struct scsidevs *
    114 scsi_probe(int masunit, struct scsi_switch *sw, int physid, int type, int want)
    115 {
    116 	static struct scsi_inquiry_data inqbuf;
    117 	struct scsidevs *ret = (struct scsidevs *)0;
    118 	int targ = physid >> 3;
    119 	int lun = physid & 7;
    120 	char *qtype=NULL, *dtype=NULL, *desc;
    121 	char manu[9], model[17], revision[5];
    122 	int len;
    123 
    124 	bzero(&inqbuf, sizeof inqbuf);
    125 
    126 	/*printf("probe: %s%d targ %d lun %d\n",
    127 		sw->name, masunit, targ, lun);*/
    128 
    129 	if( scsi_ready(masunit, targ, lun, sw,
    130 	    SCSI_NOSLEEP | SCSI_NOMASK) != COMPLETE)
    131 		return (struct scsidevs *)0;
    132 
    133 	if( scsi_inquire(masunit, targ, lun, sw, (u_char *)&inqbuf,
    134 	    SCSI_NOSLEEP | SCSI_NOMASK) != COMPLETE)
    135 		return (struct scsidevs *)0;
    136 
    137 	if( inqbuf.device_qualifier==3 && inqbuf.device_type==T_NODEVICE)
    138 		return (struct scsidevs *)0;
    139 
    140 	switch(inqbuf.device_qualifier) {
    141 	case 0:
    142 		qtype = "";
    143 		break;
    144 	case 1:
    145 		qtype = "Unit not Connected!";
    146 		break;
    147 	case 2:
    148 		qtype =", Reserved Peripheral Qualifier!";
    149 		break;
    150 	case 3:
    151 		qtype = ", The Target can't support this Unit!";
    152 		break;
    153 	default:
    154 		dtype = "vendor specific";
    155 		qtype = "";
    156 		break;
    157 	}
    158 
    159 	if (dtype == NULL) {
    160 		switch(inqbuf.device_type) {
    161 		case T_DIRECT:
    162 			dtype = "direct";
    163 			break;
    164 		case T_SEQUENTIAL:
    165 			dtype = "seq";
    166 			break;
    167 		case T_PRINTER:
    168 			dtype = "pr";
    169 			break;
    170 		case T_PROCESSOR:
    171 			dtype = "cpu";
    172 			break;
    173 		case T_READONLY:
    174 			dtype = "ro";
    175 			break;
    176 		case T_WORM:
    177 			dtype = "worm";
    178 			break;
    179 		case T_SCANNER:
    180 			dtype = "scan";
    181 			break;
    182 		case T_OPTICAL:
    183 			dtype = "optic";
    184 			break;
    185 		case T_CHANGER:
    186 			dtype = "changer";
    187 			break;
    188 		case T_COMM:
    189 			dtype = "comm";
    190 			break;
    191 		default:
    192 			dtype = "???";
    193 			break;
    194 		}
    195 	}
    196 
    197 	if(inqbuf.ansii_version > 0) {
    198 		len = inqbuf.additional_length +
    199 			((char *)inqbuf.unused - (char *)&inqbuf);
    200 		if( len > sizeof(struct scsi_inquiry_data) - 1)
    201 			len = sizeof(struct scsi_inquiry_data) - 1;
    202 		desc = inqbuf.vendor;
    203 		desc[len-(desc-(char *)&inqbuf)] = 0;
    204 		strncpy(manu, inqbuf.vendor, sizeof inqbuf.vendor);
    205 		manu[sizeof inqbuf.vendor] = '\0';
    206 		strncpy(model, inqbuf.product, sizeof inqbuf.product);
    207 		model[sizeof inqbuf.product] = '\0';
    208 		strncpy(revision, inqbuf.revision, sizeof inqbuf.revision);
    209 		revision[sizeof inqbuf.revision] = '\0';
    210 	} else {
    211 		desc = "early protocol device";
    212 		strcpy(manu, "????");
    213 		strcpy(model, "");
    214 		strcpy(revision, "");
    215 	}
    216 
    217 	if(want)
    218 		goto print;
    219 
    220 	ret = selectdev(masunit, targ, lun, sw, inqbuf.device_qualifier,
    221 		inqbuf.device_type, inqbuf.removable, manu, model, revision, type);
    222 	if(sw->printed[targ] & (1<<lun))
    223 		return ret;
    224 
    225 print:
    226 	printf("%s%d targ %d lun %d: type %d(%s) %s <%s%s%s> SCSI%d\n",
    227 		sw->name, masunit, targ, lun,
    228 		inqbuf.device_type, dtype,
    229 		inqbuf.removable ? "removable" : "fixed",
    230 		manu, model, revision, inqbuf.ansii_version);
    231 	if(qtype[0])
    232 		printf("%s%d targ %d lun %d: qualifier %d(%s)\n",
    233 			sw->name, masunit, targ, lun,
    234 			inqbuf.device_qualifier, qtype);
    235 	return ret;
    236 }
    237 
    238 void
    239 scsi_warn(int masunit, int mytarg, struct scsi_switch *sw)
    240 {
    241 	struct scsidevs *match = (struct scsidevs *)0;
    242 	int physid;
    243 	int targ, lun;
    244 
    245 	for(targ=0; targ<8; targ++) {
    246 		if(targ==mytarg)
    247 			continue;
    248 		for(lun=0; lun<8; lun++) {
    249 			/* check if device already used, or empty */
    250 			if( sw->empty[targ] & (1<<lun) )
    251 				continue;
    252 			if( sw->used[targ] & (1<<lun) )
    253 				continue;
    254 
    255 			physid = targ*8 + lun;
    256 			match = scsi_probe(masunit, sw, physid, 0, 0);
    257 
    258 			if(match == (struct scsidevs *)-1) {
    259 				if(lun==0)
    260 					sw->empty[targ] = 0xff;
    261 				else
    262 					sw->empty[targ] = 0xff;
    263 				continue;
    264 			}
    265 			if(match) {
    266 				targ = physid >> 3;
    267 				lun = physid & 7;
    268 				if(match->flags & SC_MORE_LUS)
    269 					sw->empty[targ] |= (1<<lun);
    270 				else
    271 					sw->empty[targ] = 0xff;
    272 			}
    273 		}
    274 	}
    275 }
    276 
    277 /*
    278  * not quite perfect. If we have two "drive ?" entries, this will
    279  * probe through all the devices twice. It should have realized that
    280  * any device that is not found the first time won't exist later on.
    281  */
    282 int
    283 scsi_attach(int masunit, int mytarg, struct scsi_switch *sw,
    284 	int *physid, int *unit, int type)
    285 {
    286 	struct scsidevs *match = (struct scsidevs *)0;
    287 	int targ, lun;
    288 	int ret=0;
    289 
    290 	/*printf("%s%d probing at targ %d lun %d..\n",
    291 		sw->name, masunit, *physid >> 3, *physid & 7);*/
    292 
    293 	if( *physid!=-1 ) {
    294 		targ = *physid >> 3;
    295 		lun = *physid & 7;
    296 
    297 		if( (sw->empty[targ] & (1<<lun)) || (sw->used[targ] & (1<<lun)) )
    298 			return 0;
    299 
    300 		match = scsi_probe(masunit, sw, *physid, type, 0);
    301 		if(match == (struct scsidevs *)-1) {
    302 			match = (struct scsidevs *)0;
    303 			if(lun==0)
    304 				sw->empty[targ] = 0xff;
    305 			else
    306 				sw->empty[targ] |= (1<<lun);
    307 			return 0;
    308 		}
    309 
    310 		sw->printed[targ] |= (1<<lun);
    311 		if(!match)
    312 			return 0;
    313 
    314 		ret = (*(match->attach_rtn))(masunit, sw, *physid, unit);
    315 		goto success;
    316 	}
    317 
    318 	for(targ=0; targ<8; targ++) {
    319 		if(targ==mytarg)
    320 			continue;
    321 		for(lun=0; lun<8; lun++) {
    322 			if( (sw->empty[targ] & (1<<lun)) || (sw->used[targ] & (1<<lun)) )
    323 				continue;
    324 
    325 			*physid = targ*8 + lun;
    326 			match = scsi_probe(masunit, sw, *physid, type, 0);
    327 			if( match==(struct scsidevs *)-1) {
    328 				if(lun==0)
    329 					sw->empty[targ] = 0xff;
    330 				else
    331 					sw->empty[targ] |= (1<<lun);
    332 				match = (struct scsidevs *)0;
    333 				continue;
    334 			}
    335 			if(!match) {
    336 				sw->printed[targ] |= (1<<lun);
    337 				break;
    338 			}
    339 			ret = (*(match->attach_rtn))(masunit, sw, *physid, unit);
    340 			if(ret)
    341 				goto success;
    342 			return 0;
    343 		}
    344 	}
    345 	*physid = -1;	/* failed... */
    346 	return 0;
    347 
    348 success:
    349 	targ = *physid >> 3;
    350 	lun = *physid & 7;
    351 	if(match->flags & SC_MORE_LUS) {
    352 		sw->used[targ] |= (1<<lun);
    353 		sw->printed[targ] |= (1<<lun);
    354 	} else {
    355 		sw->used[targ] = 0xff;
    356 		sw->printed[targ] = 0xff;
    357 	}
    358 	return ret;
    359 }
    360 
    361 /*
    362  * Try make as good a match as possible with
    363  * available sub drivers
    364  */
    365 struct scsidevs *
    366 selectdev(int unit, int target, int lu, struct scsi_switch *sw, int qual,
    367 	int dtype, int remov, char *manu, char *model, char *rev, int type)
    368 {
    369 	struct scsidevs *sdbest = (struct scsidevs *)0;
    370 	struct scsidevs *sdent = knowndevs;
    371 	int numents = sizeof(knowndevs)/sizeof(struct scsidevs);
    372 	int count = 0, sdbestes = 0;
    373 
    374 	dtype |= (qual << 5);
    375 
    376 	sdent--;
    377 	while( count++ < numents) {
    378 		sdent++;
    379 		if(dtype != sdent->dtype)
    380 			continue;
    381 		if(type != sdent->type)
    382 			continue;
    383 		if(sdbestes < 1) {
    384 			sdbestes = 1;
    385 			sdbest = sdent;
    386 		}
    387 		if(remov != sdent->removable)
    388 			continue;
    389 		if(sdbestes < 2) {
    390 			sdbestes = 2;
    391 			sdbest = sdent;
    392 		}
    393 		if(sdent->flags & SC_SHOWME)
    394 			printf("\n%s-\n%s-", sdent->manufacturer, manu);
    395 		if(strcmp(sdent->manufacturer, manu))
    396 			continue;
    397 		if(sdbestes < 3) {
    398 			sdbestes = 3;
    399 			sdbest = sdent;
    400 		}
    401 		if(sdent->flags & SC_SHOWME)
    402 			printf("\n%s-\n%s-",sdent->model, model);
    403 		if(strcmp(sdent->model, model))
    404 			continue;
    405 		if(sdbestes < 4) {
    406 			sdbestes = 4;
    407 			sdbest = sdent;
    408 		}
    409 		if(sdent->flags & SC_SHOWME)
    410 			printf("\n%s-\n%s-",sdent->version, rev);
    411 		if(strcmp(sdent->version, rev))
    412 			continue;
    413 		if(sdbestes < 5) {
    414 			sdbestes = 5;
    415 			sdbest = sdent;
    416 			break;
    417 		}
    418 	}
    419 	return sdbest;
    420 }
    421 
    422 /*
    423  * Do a scsi operation asking a device if it is
    424  * ready. Use the scsi_cmd routine in the switch
    425  * table.
    426  */
    427 int
    428 scsi_ready(int unit, int target, int lu,
    429 	struct scsi_switch *sw, int flags)
    430 {
    431 	struct scsi_test_unit_ready scsi_cmd;
    432 	struct scsi_xfer scsi_xfer;
    433 	volatile int rval;
    434 	int key;
    435 
    436 	bzero(&scsi_cmd, sizeof(scsi_cmd));
    437 	bzero(&scsi_xfer, sizeof(scsi_xfer));
    438 	scsi_cmd.op_code = TEST_UNIT_READY;
    439 
    440 	scsi_xfer.flags = flags | INUSE;
    441 	scsi_xfer.adapter = unit;
    442 	scsi_xfer.targ = target;
    443 	scsi_xfer.lu = lu;
    444 	scsi_xfer.cmd = (struct scsi_generic *)&scsi_cmd;
    445 	scsi_xfer.retries = 8;
    446 	scsi_xfer.timeout = 10000;
    447 	scsi_xfer.cmdlen = sizeof(scsi_cmd);
    448 	scsi_xfer.data = 0;
    449 	scsi_xfer.datalen = 0;
    450 	scsi_xfer.resid = 0;
    451 	scsi_xfer.when_done = 0;
    452 	scsi_xfer.done_arg = 0;
    453 retry:	scsi_xfer.error = 0;
    454 
    455 	/* don't use interrupts! */
    456 
    457 	rval = (*(sw->scsi_cmd))(&scsi_xfer);
    458 	if (rval != COMPLETE) {
    459 		if(scsi_debug) {
    460 			printf("scsi error, rval = 0x%x\n", rval);
    461 			printf("code from driver: 0x%x\n", scsi_xfer.error);
    462 		}
    463 		switch(scsi_xfer.error) {
    464 		case XS_SENSE:
    465 			/*
    466 			 * Any sense value is illegal except UNIT ATTENTION
    467 			 * In which case we need to check again to get the
    468 			 * correct response. (especially exabytes)
    469 			 */
    470 			if(scsi_xfer.sense.error_class == 7 ) {
    471 				key = scsi_xfer.sense.ext.extended.sense_key ;
    472 				switch(key) {
    473 				case 2:	/* not ready BUT PRESENT! */
    474 					return(COMPLETE);
    475 				case 6:
    476 					spinwait(1000);
    477 					if(scsi_xfer.retries--) {
    478 						scsi_xfer.flags &= ~ITSDONE;
    479 						goto retry;
    480 					}
    481 					return(COMPLETE);
    482 				default:
    483 					if(scsi_debug)
    484 						printf("%d:%d,key=%x.", target,
    485 							lu, key);
    486 				}
    487 			}
    488 			return(HAD_ERROR);
    489 		case XS_BUSY:
    490 			spinwait(1000);
    491 			if(scsi_xfer.retries--) {
    492 				scsi_xfer.flags &= ~ITSDONE;
    493 				goto retry;
    494 			}
    495 			return COMPLETE;	/* it's busy so it's there */
    496 		case XS_TIMEOUT:
    497 		default:
    498 			return HAD_ERROR;
    499 		}
    500 	}
    501 	return COMPLETE;
    502 }
    503 
    504 /*
    505  * Do a scsi operation asking a device what it is
    506  * Use the scsi_cmd routine in the switch table.
    507  */
    508 int
    509 scsi_inquire(int unit, int target, int lu, struct scsi_switch *sw,
    510 	u_char *inqbuf, int flags)
    511 {
    512 	struct scsi_inquiry scsi_cmd;
    513 	struct scsi_xfer scsi_xfer;
    514 
    515 	bzero(&scsi_cmd, sizeof(scsi_cmd));
    516 	bzero(&scsi_xfer, sizeof(scsi_xfer));
    517 	scsi_cmd.op_code = INQUIRY;
    518 	scsi_cmd.length = sizeof(struct scsi_inquiry_data);
    519 
    520 	scsi_xfer.flags = flags | SCSI_DATA_IN | INUSE;
    521 	scsi_xfer.adapter = unit;
    522 	scsi_xfer.targ = target;
    523 	scsi_xfer.lu = lu;
    524 	scsi_xfer.retries = 8;
    525 	scsi_xfer.timeout = 10000;
    526 	scsi_xfer.cmd = (struct scsi_generic *)&scsi_cmd;
    527 	scsi_xfer.cmdlen =  sizeof(struct scsi_inquiry);
    528 	scsi_xfer.data = inqbuf;
    529 	scsi_xfer.datalen = sizeof(struct scsi_inquiry_data);
    530 	scsi_xfer.resid = sizeof(struct scsi_inquiry_data);
    531 	scsi_xfer.when_done = 0;
    532 	scsi_xfer.done_arg = 0;
    533 
    534 retry:
    535 	scsi_xfer.error=0;
    536 	/* don't use interrupts! */
    537 
    538 	if ((*(sw->scsi_cmd))(&scsi_xfer) != COMPLETE) {
    539 		if(scsi_debug)
    540 			printf("inquiry had error(0x%x) ",scsi_xfer.error);
    541 		switch(scsi_xfer.error) {
    542 		case XS_NOERROR:
    543 			break;
    544 		case XS_SENSE:
    545 			/*
    546 			 * Any sense value is illegal except UNIT ATTENTION
    547 			 * In which case we need to check again to get the
    548 			 * correct response. (especially exabytes)
    549 			 */
    550 			if( scsi_xfer.sense.error_class==7 &&
    551 			    scsi_xfer.sense.ext.extended.sense_key==6) {
    552 				/* it's changed so it's there */
    553 				spinwait(1000);
    554 				if(scsi_xfer.retries--) {
    555 					scsi_xfer.flags &= ~ITSDONE;
    556 					goto retry;
    557 				}
    558 				return COMPLETE;
    559 			}
    560 			return HAD_ERROR;
    561 		case XS_BUSY:
    562 			spinwait(1000);
    563 			if(scsi_xfer.retries--) {
    564 				scsi_xfer.flags &= ~ITSDONE;
    565 				goto retry;
    566 			}
    567 		case XS_TIMEOUT:
    568 		default:
    569 			return(HAD_ERROR);
    570 		}
    571 	}
    572 	return COMPLETE;
    573 }
    574 
    575 /*
    576  * convert a physical address to 3 bytes,
    577  * MSB at the lowest address,
    578  * LSB at the highest.
    579  */
    580 void
    581 lto3b(u_long val, u_char *bytes)
    582 {
    583 	*bytes++ = (val&0xff0000)>>16;
    584 	*bytes++ = (val&0xff00)>>8;
    585 	*bytes = val&0xff;
    586 }
    587 
    588 /*
    589  * The reverse of lto3b
    590  */
    591 u_long
    592 _3btol(u_char *bytes)
    593 {
    594 	u_long rc;
    595 
    596 	rc = (*bytes++ << 16);
    597 	rc += (*bytes++ << 8);
    598 	rc += *bytes;
    599 	return rc;
    600 }
    601 
    602