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