/* * 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.17 1994/12/18 18:48:39 phk Exp $ */ #include #include #include #include #include #include #include #include "st.h" #include "sd.h" #include "ch.h" #include "cd.h" #include "uk.h" #include "su.h" #include "scbus.h" /* If we have any at all, we want at least 8 */ #if NSCBUS > 0 #if NSCBUS < 8 #undef NSCBUS #endif /* NSCBUS < 8 */ #endif /* NSCBUS > 0 */ #ifndef NSCBUS #define NSCBUS 8 #endif /* NSCBUS */ #include #include #ifdef TFS #include "bll.h" #include "cals.h" #include "kil.h" #include "scan.h" #else /* TFS */ #define NBLL 0 #define NCALS 0 #define NKIL 0 #define NSCAN 0 #endif /* TFS */ #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 */ #if NUK > 0 extern ukattach(); #endif /* NUK */ /* * One of these is allocated and filled in for each scsi bus. * it holds pointers to allow the scsi bus to get to the driver * That is running each LUN on the bus * it also has a template entry which is the prototype struct * supplied by the adapter driver, this is used to initialise * the others, before they have the rest of the fields filled in */ struct scsibus_data *scbus_data[NSCBUS]; /* * 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; errval(*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 */ struct scsidevs { u_int32 type; boolean removable; char *manufacturer; char *model; char *version; errval(*attach_rtn) (); char *devname; char flags; /* 1 show my comparisons during boot(debug) */ #ifdef NEW_SCSICONF u_int16 quirks; void *devmodes; #endif }; #define SC_SHOWME 0x01 #define SC_ONE_LU 0x00 #define SC_MORE_LUS 0x02 #if NUK > 0 static struct scsidevs unknowndev = { -1, 0, "*", "*", "*", ukattach, "uk", SC_MORE_LUS }; #endif /*NUK*/ #ifdef NEW_SCSICONF static st_modes mode_tandberg3600 = { {0, 0, 0}, /* minor 0,1,2,3 */ {0, ST_Q_FORCE_VAR_MODE, QIC_525}, /* minor 4,5,6,7 */ {0, 0, QIC_150}, /* minor 8,9,10,11 */ {0, 0, QIC_120} /* minor 12,13,14,15 */ }; static st_modes mode_archive2525 = { {0, ST_Q_SNS_HLP, 0}, /* minor 0,1,2,3 */ {0, ST_Q_SNS_HLP, QIC_525}, /* minor 4,5,6,7 */ {0, 0, QIC_150}, /* minor 8,9,10,11 */ {0, 0, QIC_120} /* minor 12,13,14,15 */ }; static st_modes mode_archive150 = { {0, 0, 0}, /* minor 0,1,2,3 */ {0, 0, QIC_150}, /* minor 4,5,6,7 */ {0, 0, QIC_120}, /* minor 8,9,10,11 */ {0, 0, QIC_24} /* minor 12,13,14,15 */ }; static st_modes mode_wangtek5525 = { {0, 0, 0}, /* minor 0,1,2,3 */ {0, ST_Q_BLKSIZ, QIC_525}, /* minor 4,5,6,7 */ {0, 0, QIC_150}, /* minor 8,9,10,11 */ {0, 0, QIC_120} /* minor 12,13,14,15 */ }; static st_modes mode_wangdat1300 = { {0, 0, 0}, /* minor 0,1,2,3 */ {512, ST_Q_FORCE_FIXED_MODE, DDS}, /* minor 4,5,6,7 */ {1024, ST_Q_FORCE_FIXED_MODE, DDS}, /* minor 8,9,10,11 */ {0, ST_Q_FORCE_VAR_MODE, DDS} /* minor 12,13,14,15 */ }; static st_modes mode_unktape = { {512, ST_Q_FORCE_FIXED_MODE, 0}, /* minor 0,1,2,3 */ {512, ST_Q_FORCE_FIXED_MODE, QIC_24}, /* minor 4,5,6,7 */ {0, ST_Q_FORCE_VAR_MODE, HALFINCH_1600}, /* minor 8,9,10,11 */ {0, ST_Q_FORCE_VAR_MODE, HALFINCH_6250} /* minor 12,13,14,15 */ }; #endif /* NEW_SCSICONF */ static struct scsidevs knowndevs[] = #ifdef NEW_SCSICONF { #if NSD > 0 { T_DIRECT, T_FIXED, "MAXTOR", "XT-4170S", "B5A", sdattach, "mx1", SC_ONE_LU }, { T_DIRECT, T_FIXED, "*", "*", "*", sdattach, "sd", SC_ONE_LU }, #endif /* NSD */ #if NST > 0 { T_SEQUENTIAL, T_REMOV, "TANDBERG", " TDC 3600", "*", stattach, "st", SC_ONE_LU, ST_Q_NEEDS_PAGE_0, mode_tandberg3600 }, { T_SEQUENTIAL, T_REMOV, "ARCHIVE", "VIPER 2525*", "-005", stattach, "st", SC_ONE_LU, 0, mode_archive2525 }, { T_SEQUENTIAL, T_REMOV, "ARCHIVE", "VIPER 150", "*", stattach, "st", SC_ONE_LU, ST_Q_NEEDS_PAGE_0, mode_archive150 }, { T_SEQUENTIAL, T_REMOV, "WANGTEK", "5525ES*", "*", stattach, "st", SC_ONE_LU, 0, mode_wangtek5525 }, { T_SEQUENTIAL, T_REMOV, "WangDAT", "Model 1300", "*", stattach, "st", SC_ONE_LU, 0, mode_wangdat1300 }, { T_SEQUENTIAL, T_REMOV, "*", "*", "*", stattach, "st", SC_ONE_LU, 0, mode_unktape }, #endif /* NST */ #if NCALS > 0 { T_PROCESSOR, T_FIXED, "*", "*", "*", calsattach, "cals", SC_MORE_LUS }, #endif /* NCALS */ #if NCH > 0 { T_CHANGER, T_REMOV, "*", "*", "*", chattach, "ch", SC_ONE_LU }, #endif /* NCH */ #if NCD > 0 #ifndef UKTEST /* make cdroms unrecognised to test the uk driver */ { 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", "*", cdattach, "cd", SC_MORE_LUS }, #endif #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", "*", kil_attach, "kil", SC_ONE_LU }, #endif /* NKIL */ { 0 } }; #else { #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 #ifndef UKTEST /* make cdroms unrecognised to test the uk driver */ { 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 #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 } }; #endif /* NEW_SCSICONF */ /* * Declarations */ struct predefined *scsi_get_predef(); struct scsidevs *scsi_probedev(); struct scsidevs *selectdev(); errval scsi_probe_bus __P((int bus, int targ, int lun)); struct scsi_device probe_switch = { NULL, NULL, NULL, NULL, "probe", 0, { 0, 0 } }; /* * controls debug level within the scsi subsystem - * see scsiconf.h for values */ int32 scsibus = 0x0; /* This is the Nth scsibus we've seen */ /* * XXX * This is BOGUS. * We do this because it was easier than adding the requisite information * to the scsi_link structure and modifying everything to use that. * Someday, we will do just that, and users will be able to nail down their * preferred SCSI ids. */ struct kern_devconf kdc_scbus0 = { 0, 0, 0, /* filled in by dev_attach */ "scbus", 0, MDDC_SCBUS, 0, 0, 0, 0, /* no external data */ 0, /* no parent */ 0, /* no parentdata */ DC_BUSY, /* busses are always busy */ "SCSI subsystem" }; /* * The routine called by the adapter boards to get all their * devices configured in. */ void scsi_attachdevs(sc_link_proto) struct scsi_link *sc_link_proto; { static int timesthru = 0; if(!timesthru++) { dev_attach(&kdc_scbus0); } if(scsibus >= NSCBUS) { printf("too many scsi busses, reconfigure the kernel\n"); return; } sc_link_proto->scsibus = scsibus; scbus_data[scsibus] = malloc(sizeof(struct scsibus_data), M_TEMP, M_NOWAIT); if(!scbus_data[scsibus]) { panic("scsi_attachdevs: malloc\n"); } bzero(scbus_data[scsibus], sizeof(struct scsibus_data)); scbus_data[scsibus]->adapter_link = sc_link_proto; #if defined(SCSI_DELAY) && SCSI_DELAY > 2 printf("%s%d waiting for scsi devices to settle\n", sc_link_proto->adapter->name, sc_link_proto->adapter_unit); #else /* SCSI_DELAY > 2 */ #undef SCSI_DELAY #define SCSI_DELAY 2 #endif /* SCSI_DELAY */ DELAY(1000000 * SCSI_DELAY); scsibus++; scsi_probe_bus(scsibus - 1,-1,-1); } /* * Probe the requested scsi bus. It must be already set up. * -1 requests all set up scsi busses. * targ and lun optionally narrow the search if not -1 */ errval scsi_probe_busses(int bus, int targ, int lun) { if (bus == -1) { for(bus = 0; bus < scsibus; bus++) { scsi_probe_bus(bus, targ, lun); } return 0; } else { return scsi_probe_bus(bus, targ, lun); } } /* * Probe the requested scsi bus. It must be already set up. * targ and lun optionally narrow the search if not -1 */ errval scsi_probe_bus(int bus, int targ, int lun) { struct scsibus_data *scsi ; int maxtarg,mintarg,maxlun,minlun; struct scsi_link *sc_link_proto; u_int8 scsi_addr ; struct scsidevs *bestmatch = NULL; struct predefined *predef = NULL; struct scsi_link *sc_link = NULL; boolean maybe_more; if ((bus < 0 ) || ( bus >= scsibus)) { return ENXIO; } scsi = scbus_data[bus]; if(!scsi) return ENXIO; sc_link_proto = scsi->adapter_link; scsi_addr = sc_link_proto->adapter_targ; if(targ == -1){ maxtarg = 7; mintarg = 0; } else { if((targ < 0 ) || (targ > 7)) return EINVAL; maxtarg = mintarg = targ; } if(lun == -1){ maxlun = 7; minlun = 0; } else { if((lun < 0 ) || (lun > 7)) return EINVAL; maxlun = minlun = lun; } for ( targ = mintarg;targ <= maxtarg; targ++) { maybe_more = 0; /* by default only check 1 lun */ if (targ == scsi_addr) { continue; } for ( lun = minlun; lun <= maxlun ;lun++) { /* * The spot appears to already have something * linked in, skip past it. Must be doing a 'reprobe' */ if(scsi->sc_link[targ][lun]) {/* don't do this one, but check other luns */ maybe_more = 1; continue; } /* * If we presently don't have a link block * then allocate one to use while probing */ if (!sc_link) { sc_link = malloc(sizeof(*sc_link), M_TEMP, M_NOWAIT); *sc_link = *sc_link_proto; /* struct copy */ sc_link->opennings = 1; sc_link->device = &probe_switch; } sc_link->target = targ; sc_link->lun = lun; sc_link->quirks = 0; predef = scsi_get_predef(sc_link, &maybe_more); bestmatch = scsi_probedev(sc_link, &maybe_more); #ifdef NEW_SCSICONF if (bestmatch) { sc_link->quirks = bestmatch->quirks; sc_link->devmodes = bestmatch->devmodes; } else { sc_link->quirks = 0; sc_link->devmodes = NULL; } #endif if ((bestmatch) && (predef)) { /* both exist */ if (bestmatch->attach_rtn != predef->attach_rtn) { printf("Clash in found/expected devices\n"); #if NUK > 0 if(bestmatch == &unknowndev) { printf("will link in PREDEFINED\n"); (*(predef->attach_rtn)) (sc_link); } else #endif /*NUK*/ { printf("will link in FOUND\n"); (*(bestmatch->attach_rtn)) (sc_link); } } else { (*(bestmatch->attach_rtn)) (sc_link); } } if ((bestmatch) && (!predef)) { /* just FOUND */ (*(bestmatch->attach_rtn)) (sc_link); } if ((!bestmatch) && (predef)) { /* just predef */ (*(predef->attach_rtn)) (sc_link); } if ((bestmatch) || (predef)) { /* one exists */ scsi->sc_link[targ][lun] = sc_link; sc_link = NULL; /* it's been used */ } if (!(maybe_more)) { /* nothing suggests we'll find more */ break; /* nothing here, skip to next targ */ } /* otherwise something says we should look further */ } } if (sc_link) { free(sc_link, M_TEMP); } return 0; } /* Return the scsi_link for this device, if any. */ struct scsi_link * scsi_link_get(bus, targ, lun) int bus; int targ; int lun; { struct scsibus_data *scsi = scbus_data[bus]; return (scsi) ? scsi->sc_link[targ][lun] : 0; } /* * given a target and lu, check if there is a predefined device for * that address */ struct predefined * scsi_get_predef(sc_link, maybe_more) struct scsi_link *sc_link; boolean *maybe_more; { u_int8 unit = sc_link->scsibus; u_int8 target = sc_link->target; u_int8 lu = sc_link->lun; struct scsi_adapter *scsi_adapter = sc_link->adapter; u_int32 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("%s%d targ %d lun %d: <%s> - PRECONFIGURED -\n" ,scsi_adapter->name ,unit ,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(sc_link, maybe_more) boolean *maybe_more; struct scsi_link *sc_link; { u_int8 unit = sc_link->adapter_unit; u_int8 target = sc_link->target; u_int8 lu = sc_link->lun; struct scsi_adapter *scsi_adapter = sc_link->adapter; struct scsidevs *bestmatch = (struct scsidevs *) 0; char *dtype = (char *) 0, *desc; char *qtype; struct scsi_inquiry_data *inqbuf; u_int32 len, qualifier, type; boolean remov; char manu[32]; char model[32]; char version[32]; int z; inqbuf = &sc_link->inqbuf; bzero(inqbuf, sizeof(*inqbuf)); /* * Ask the device what it is */ #ifdef SCSIDEBUG if ((target == DEBUGTARG) && (lu == DEBUGLUN)) sc_link->flags |= (DEBUGLEVEL); else sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2 | SDEV_DB3 | SDEV_DB4); #endif /* SCSIDEBUG */ /* catch unit attn */ scsi_test_unit_ready(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT); #ifdef DOUBTFULL switch (scsi_test_unit_ready(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) { case 0: /* said it WAS ready */ case EBUSY: /* replied 'NOT READY' but WAS present, continue */ case ENXIO: break; case EIO: /* device timed out */ case EINVAL: /* Lun not supported */ default: return (struct scsidevs *) 0; } #endif /*DOUBTFULL*/ #ifdef SCSI_2_DEF /* some devices need to be told to go to SCSI2 */ /* However some just explode if you tell them this.. leave it out */ scsi_change_def(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT); #endif /*SCSI_2_DEF */ /* Now go ask the device all about itself */ if (scsi_inquire(sc_link, inqbuf, SCSI_NOSLEEP | SCSI_NOMASK) != 0) { return (struct scsidevs *) 0; } /* * note what BASIC type of device it is */ 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. */ switch ((int)qualifier) { case SID_QUAL_LU_OK: qtype = ""; break; case SID_QUAL_LU_OFFLINE: qtype = ", Unit not Connected!"; break; case SID_QUAL_RSVD: qtype = ", Reserved Peripheral Qualifier!"; *maybe_more = 1; return (struct scsidevs *) 0; break; case SID_QUAL_BAD_LU: /* * Check for a non-existent unit. If the device is returning * this much, then we must set the flag that has * the searchers keep looking on other luns. */ qtype = ", The Target can't support this Unit!"; *maybe_more = 1; return (struct scsidevs *) 0; default: dtype = "vendor specific"; qtype = ""; *maybe_more = 1; break; } if (dtype == 0) { switch ((int)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; case T_NODEVICE: *maybe_more = 1; return (struct scsidevs *) 0; default: dtype = "unknown"; break; } } /* * Then if it's advanced enough, more detailed * information */ if ((inqbuf->version & SID_ANSII) > 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); strncpy(model, inqbuf->product, 16); strncpy(version, inqbuf->revision, 4); for(z = 0; z < 4; z++) { if (version[z]<' ') version[z]='?'; } } else /* * If not advanced enough, use default values */ { desc = "early protocol device"; strncpy(manu, "unknown", 8); strncpy(model, "unknown", 16); strncpy(version, "????", 4); } manu[8] = 0; model[16] = 0; version[4] = 0; printf("%s%d targ %d lun %d: type %ld(%s) %s SCSI%d\n" ,scsi_adapter->name ,unit ,target ,lu ,type ,dtype ,remov ? "removable" : "fixed" ,inqbuf->version & SID_ANSII ); printf("%s%d targ %d lun %d: <%s%s%s>\n" ,scsi_adapter->name ,unit ,target ,lu ,manu ,model ,version ); if (qtype[0]) { printf("%s%d targ %d lun %d: qualifier %ld(%s)\n" ,scsi_adapter->name ,unit ,target ,lu ,qualifier ,qtype ); } /* * Try make as good a match as possible with * available sub drivers */ bestmatch = (selectdev( qualifier, type, remov ? T_REMOV : T_FIXED, manu, model, version)); if ((bestmatch) && (bestmatch->flags & SC_MORE_LUS)) { *maybe_more = 1; } return (bestmatch); } /* Try to find the major number for a device during attach. */ dev_t scsi_dev_lookup(d_open) int (*d_open)(); { int i; dev_t d = NODEV; for (i = 0; i < nchrdev; i++) if (cdevsw[i].d_open == d_open) { d = makedev(i, 0); break; } return d; } #ifdef NEW_SCSICONF /* * Compare name with pattern, return 0 on match. * Short pattern matches trailing blanks in name, * wildcard '*' in pattern matches rest of name */ int match(pattern, name) char *pattern; char *name; { char c; while (c = *pattern++) { if (c == '*') return 0; if (c != *name++) return 1; } while (c = *name++) { if (c != ' ') return 1; } return 0; } #endif /* * Try make as good a match as possible with * available sub drivers */ struct scsidevs * selectdev(qualifier, type, remov, manu, model, rev) u_int32 qualifier, type; boolean remov; char *manu, *model, *rev; { #ifdef NEW_SCSICONF struct scsidevs *bestmatch = NULL; struct scsidevs *thisentry; type |= qualifier; /* why? */ for ( thisentry = knowndevs; thisentry->manufacturer; thisentry++ ) { if (type != thisentry->type) { continue; } if (remov != thisentry->removable) { continue; } if (thisentry->flags & SC_SHOWME) printf("\n%s-\n%s-", thisentry->manufacturer, manu); if (match(thisentry->manufacturer, manu)) { continue; } if (thisentry->flags & SC_SHOWME) printf("\n%s-\n%s-", thisentry->model, model); if (match(thisentry->model, model)) { continue; } if (thisentry->flags & SC_SHOWME) printf("\n%s-\n%s-", thisentry->version, rev); if (match(thisentry->version, rev)) { continue; } bestmatch = thisentry; break; } #else u_int32 numents = (sizeof(knowndevs) / sizeof(struct scsidevs)) - 1; u_int32 count = 0; u_int32 bestmatches = 0; struct scsidevs *bestmatch = (struct scsidevs *) 0; struct scsidevs *thisentry = knowndevs; type |= qualifier; /* why? */ 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; } } #endif /* NEW_SCSICONF */ if (bestmatch == (struct scsidevs *) 0) { #if NUK > 0 bestmatch = &unknowndev; #else printf("No explicit device driver match.\n"); #endif } return (bestmatch); } int scsi_externalize(struct scsi_link *sl, void *userp, size_t *lenp) { if(*lenp < sizeof *sl) return ENOMEM; *lenp -= sizeof *sl; return copyout(sl, userp, sizeof *sl); }