Source to driverkit/i386/autoconf_i386.m


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @[email protected]
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @[email protected]
 */

/*
 * Copyright (c) 1993 NeXT Computer, Inc.
 *
 * 486 Driverkit configuration.
 *
 * HISTORY
 *
 * 28 Jan 1993  Brian Pinkerton at NeXT
 *	Added support for new, MI autoconf logic, and new driverkit API.
 * 10 Nov 1992	Brian Pinkerton at NeXT
 *	Added indirect probe logic.
 * 20 Aug 1992	Joe Pasqua
 *	Init intrReq & intrAttached of kern_dev in configure_device_class().
 * 3 August 1992 ? at NeXT
 *	Created.
 */

#import <mach/mach_types.h>
#import <kern/mach_header.h>

#import <machkit/NXLock.h>
#import <driverkit/KernDeviceDescription.h>
#import <driverkit/KernStringList.h>
#import <driverkit/i386/IOEISADeviceDescription.h>
#import <driverkit/IODeviceDescriptionPrivate.h>
#import <driverkit/IODirectDevice.h>
#import <driverkit/IODeviceKernPrivate.h>
#import <driverkit/IODeviceParams.h>
#import <driverkit/Device_ddm.h>
#import <driverkit/configTableKern.h>
#import <driverkit/KernDevice.h>
#import <driverkit/IOConfigTable.h>
#import <driverkit/driverTypesPrivate.h>
#import <driverkit/autoconfCommon.h>
#import <driverkit/i386/EISAKernBus.h>
#import <driverkit/i386/PCMCIA.h>
#import <driverkit/i386/PCMCIAKernBus.h>
#import <driverkit/i386/PCIKernBus.h>

#import <machdep/i386/kernBootStruct.h>

boolean_t eisa_id(int slot, unsigned int *_id);

static void probe_indirect(const char **indirectNamep, id driverInstance);

static BOOL configureDriver(const char *configData);
const char *findBootConfigString(int n);
static void bootDriverInit(void);

id		defaultBus, defaultBusClass;
static BOOL	printedPCMCIAMessage;

/*
 * Native indirect driver classes.
 */
char *indirectDevList[] = {
	"SCSIDisk",
	"SCSIGeneric",
	"IODiskPartition",
	NULL
};

/*
 * Pseudo device list.
 */
char *pseudoDevList[] = {
	"EventDriver",
	"kmDevice",
	NULL
};

#define SERVER_NAME_KEY		"Server Name"
#define CLASS_NAMES_KEY 	"Class Names"
#define DRIVER_NAME_KEY 	"Driver Name"
#define BUS_TYPE_KEY		"Bus Type"
#define BUS_CLASS_KEY		"Bus Class"
#define FAMILY_KEY		"Family"
#define BUS_FAMILY		"Bus"
#define KERN_BUS_FORMAT		"%sKernBus"
#define DEVICE_DESCR_FORMAT	"IO%sDeviceDescription"
#define DEFAULT_BUS		"EISA"
#define NAME_BUF_LEN		128


BOOL is_ISA;

static BOOL
versionIsOK(const char *serverName)
{
    int version;
    
    /*
     * Right now, if serverName is NULL, that means the driver
     * is compiled into the kernel, so don't check the version.
     * This may change later, however.
     */
    
    if (serverName != NULL) {
	version = [IODevice
		driverKitVersionForDriverNamed:(char *)serverName];
	if (version == -1)
	    version = 310;
	/*
	 * TODO: revisit this policy decision if DriverKit
	 * compatibility changes in the future.
	 */
	if (version <= 310) {
	    IOLog("WARNING: driver %s uses incompatible DriverKit version %d\n", serverName, version);
	    IOLog("Driver %s could not be configured\n", 
		serverName);
	    return NO;
	}
    }
    return YES;
}

/*
 * Eventually, this routine will only initialize the boot device(s) per 
 * input from the booter via KERNBOOTSTRUCT. 
 * For now, configure all native devices enumerated in the various
 * tables in device_configuration.c.
 */
void
probeNativeDevices(void)
{
	int		slot;
	unsigned int	_id;
	char		**configData;
	int		i;
	const char	*config;
	IOConfigTable	*configTable = nil;
	const char	*family, *busName;
	char		*nameBuf;
	id		busClass;
	
	//
	// Initialize drivers and modules loaded by booter.
	
	bootDriverInit();

	nameBuf = (char *)IOMalloc(NAME_BUF_LEN);

	//
	// Find all bus drivers.
	//
	for(i=1; ; i++) {
		config = findBootConfigString(i);
		if(config == NULL) {
			break;
		}
		configTable = [IOConfigTable newForConfigData:config];
		family = [configTable valueForStringKey:FAMILY_KEY];
		if (family && strcmp(family, BUS_FAMILY) == 0) {
		    busName = [configTable valueForStringKey:BUS_CLASS_KEY];
		    busClass = objc_getClass(busName);
		    (void)[busClass probeBus:configTable];
		}
		if (family)
		    [configTable freeString:family];
	}
	
	defaultBusClass = [KernBus lookupBusClassWithName:DEFAULT_BUS];	// XXX
	if (defaultBusClass == nil) {
		sprintf(nameBuf, "Missing %s kernel bus class", DEFAULT_BUS);
		panic(nameBuf);
	}
	defaultBus = [KernBus lookupBusInstanceWithName:DEFAULT_BUS busId:0];
	
	/*
	 * For now this is the only
	 * concession to EISA bus.
	 */
	if (eisa_id(0, &_id)) {
		printf("CPU:\tEISA id %08x\n", _id);
		for (slot = 1; slot <= 0xf; slot++) {
		   	if (eisa_id(slot, &_id))
				printf("slot %x:\tEISA id %08x\n", slot, _id);
		}
		led_msg("NeXT");
		is_ISA = NO;
	}
	else {
	   	printf("ISA bus\n");
   		is_ISA = YES;
 	}


	printf("DriverKit version %d\n",[IODevice driverKitVersion]);

	/*
	 * Configure all devices for which the booter has given us config info.
	 * The zeroth config string is the system config table.
	 */
	for(i=1; ; i++) {
		config = findBootConfigString(i);
		if(config == NULL) {
			break;
		}
	   	configureDriver(config);
	}
	IOFree(nameBuf, NAME_BUF_LEN);
}


@protocol OldProbeMethods
+ probe;
+ probe:(int)a deviceMaster:(port_t) b;
@end

/* 
 * Called from MD probeNativeDevices() and from configureThread(), the
 * IOTask version of IOProbeDriver().
 *
 * Given a class name and an ASCII representation of a config file,
 * create a kern_dev and an IODeviceDescription and probe the class. 
 * This is used both at boot time - for the boot device and (temporarily) 
 * any native drivers with static config tables - and by IOProbeDriver().
 * 
 * Returns YES if driver was started successfully, else returns NO.
*/
static BOOL
configureDriver(
    const char *configData
)
{
	const char	*className, *classNames;
	KernStringList  *classNameList = nil;
	const char	*serverName = NULL;
	IOConfigTable	*configTable = nil;
	Class		theClass;
	id		deviceDescription = nil;
	id		device = nil;
	id		ioDeviceDescription = nil;
	IOReturn	drtn;
	int		index, classesLoaded, version;
	BOOL		versionChecked = NO;
	const char	*busType, *busTypeString;
	char		*nameBuf;
	id		busClass, busDescriptionClass;

	nameBuf = (char *)IOMalloc(NAME_BUF_LEN);
	
	configTable = [IOConfigTable newForConfigData:configData];

	serverName = [configTable valueForStringKey:SERVER_NAME_KEY];
	
	classNames = (char *)[configTable valueForStringKey:CLASS_NAMES_KEY];
	if (classNames == NULL)
	    classNames = (char *)[configTable 
				     valueForStringKey:DRIVER_NAME_KEY];
	classNameList = [[KernStringList alloc]
			    initWithWhitespaceDelimitedString:classNames];
	IOFree(classNames, strlen(classNames) + 1);
	busTypeString = [configTable valueForStringKey:BUS_TYPE_KEY];
	if (busTypeString == NULL || *busTypeString == '\0')
	    busType = DEFAULT_BUS;
	else
	    busType = busTypeString;

	sprintf(nameBuf, KERN_BUS_FORMAT, busType);
	busClass = objc_getClass((const char *)nameBuf);
	if (busClass == nil)	{
	    busClass = defaultBusClass;
	    busType = DEFAULT_BUS;
	}

	classesLoaded = 0;
	for (index = 0; index < [classNameList count]; index++) {
	    className = [classNameList stringAt:index];
	    theClass = objc_getClass(className);

	    if (theClass == nil) {
		    IOLog("configureDriver: "
		          "driver class '%s' was not loaded\n", className);
		    IOLog("Driver %s could not be configured\n", serverName);
		    goto abort;
	    }
	    
	    if (versionChecked == NO) {
		/*
		 * Right now, if serverName is NULL, that means the driver
		 * is compiled into the kernel, so don't check the version.
		 * This may change later, however.
		 */
    
		if (serverName != NULL) {
		    if (versionIsOK(serverName) == NO)
			goto abort;
		}
		versionChecked = YES;
	    }

	    /*
	     * Check to see if bus wants to configure driver itself.
	     */
	    if ([busClass configureDriverWithTable:configTable]) {
		classesLoaded = 1;
		/* Return success. */
		break;
	    }
	    
	    /*
	     *  Depending on the deviceStyle, we either make
	     *  an DeviceDescription or the bus type device description.
	     */
	    switch ([theClass deviceStyle]) {
		case IO_DirectDevice:
		    deviceDescription = [busClass
			deviceDescriptionFromConfigTable:configTable];	
    
		    if (deviceDescription == nil) {
			    IOLog("configureDriver: initFromConfigTable "
					"failed for class %s\n", className);
				goto abort;
		    }
		    
		    if ([deviceDescription bus] == nil) {
			[deviceDescription setBus:defaultBus];
		    }
		    
		    if ([[deviceDescription bus] allocateResourcesForDeviceDescription:
				    deviceDescription] == nil) {
			    IOLog("configureDriver: could not allocate "
				  "resources for class %s\n", className);
				goto abort;
		    }

		    device = [[KernDevice alloc]
		    	initWithDeviceDescription:deviceDescription];
    
		    if (device == nil) {
			    IOLog("configureDriver: initFromDeviceDescription "
					"failed for class %s\n", className);
				goto abort;
		    }
		    
		    [deviceDescription setDevice:device];
		    
		    sprintf(nameBuf, "IO%sDeviceDescription", busType);
		    busDescriptionClass = objc_getClass((const char *)nameBuf);

		    ioDeviceDescription = [[busDescriptionClass alloc]
			_initWithDelegate:deviceDescription];
			
		    if (ioDeviceDescription == nil) {
		    		goto abort;
		    }
				
		    [ioDeviceDescription 
		    		setDevicePort:create_dev_port(device)];
		    break;
    
		case IO_IndirectDevice:
		case IO_PseudoDevice:
		    deviceDescription = [[KernDeviceDescription alloc]
					    initFromConfigTable:configTable];
		    if (deviceDescription == nil) {
			    goto abort;
		    }
		    
		    ioDeviceDescription = [[IODeviceDescription alloc]
		    	_initWithDelegate:deviceDescription];
			
		    if (ioDeviceDescription == nil)
		    		goto abort;

		    break;

		default:
		    IOLog("Invalid style for class %s", className);
		    goto abort;
	    }
    
	    /*
	     *  Now that we have a valid device description for the device,
	     *  probe the sucker.
	     */
	    if (![theClass respondsTo:@selector(probe:)]) {
		    IOLog("configureDriver: Class %s does not respond "
		          "to probe:\n", className);
		    continue;
	    }
    
	    if (
		[IODevice addLoadedClass:theClass 
			    description:ioDeviceDescription] == IO_R_SUCCESS) {
		    classesLoaded++;
	    }
	}

	/*
	 * Only free resources if there was a fatal error before
	 * addLoadedClass: was called, because the class may cache
	 * the device description and config table for later use.
	 */
	IOFree(nameBuf, NAME_BUF_LEN);
	[classNameList free];
	if (serverName)
	    [configTable freeString:serverName];
	if (busTypeString)
	    [configTable freeString:busTypeString];
	if (classesLoaded)
	    return YES;
	else {
	    [configTable free];
	    [ioDeviceDescription free];
	    [deviceDescription free];
	    [device free];
	    return NO;
	}

abort:
	if (serverName)
		[configTable freeString:serverName];
	if (busTypeString)
		[configTable freeString:busTypeString];
	if (configTable)
		[configTable free];

	if (ioDeviceDescription) {
		[ioDeviceDescription free];
	}
	if (deviceDescription) {
		[deviceDescription free];
	}
	if (device) {
		[device free];
	}

	return NO;
}


static boolean_t	isEISA=FALSE;

boolean_t
eisa_present(
    void
)
{
    static boolean_t	checked;

    if (!checked) {
	if (strncmp((char *)0xfffd9, "EISA", 4) == 0)
	    isEISA = TRUE;
	    
	checked = TRUE;
    }
    
    return (isEISA);
}

boolean_t
eisa_id(
    int			slot,
    unsigned int	*_id
)
{
    unsigned char	*ids = (unsigned char *)_id;
    IOEISAPortAddress		port;
    
    if (!eisa_present())
	return (FALSE);
    
    port = (slot << 12) | 0x0C80;
    
    ids[3] = inb(port);
    ids[2] = inb(++port);
    ids[1] = inb(++port);
    ids[0] = inb(++port);
    
    if (*_id == 0xffffffff)
    	return (FALSE);
	
    return (TRUE);
}


/*
 * probe all indirect drivers associated with specified driver.
 * As of mk-149.10, this is only used for IdeDisk.
 */
static void
probe_indirect(const char **indirectNamep, id driverInstance)
{
	id driverClass;
	
	if(indirectNamep == NULL) {
		return;
	}
	for(;  *indirectNamep; indirectNamep++) {
		driverClass = objc_getClass(*indirectNamep);
		if(driverClass == nil) {
			printf("probe_indirect: Class %s does not exist\n",
				*indirectNamep);
			continue;
		}
		if(![driverClass respondsTo:@selector(probe:)]) {
			printf("probe_indirect: Class %s does not respond to"
				" probe:\n", *indirectNamep);
			continue;
		}

		[driverClass probe:driverInstance]; 
	}
}

/*
 *  Configure a driver based on some config information.  This is really just
 *  a wrapper around configureDriver that runs in its own thread, and needs to
 *  be called in the IOTask context.
 */
void
configureThread(struct probeDriverArgs *args)
{
	args->rtn = configureDriver(args->configData);
	
	/*
	 * Notify parent that we're finished, then terminate.
	 */
	[args->waitLock lock];
	[args->waitLock unlockWith:YES];
	IOExitThread();
}

/*
 * Perform machine dependent hardware probe/config.
 */
void probeHardware(void)
{
}

/*
 * Start up all non-native direct drivers. Called after probeHardware(). 
 */
void probeDirectDevices(void)
{
	/* nope */
}

/*
 * Obtain n'th string in KERNBOOSTRUCT.config[]. Returns pointer to desired
 * string if found, else returns NULL.
 * KERNBOOTSTRUCT.config is a set of contiguous strings, NULL separated. 
 * The end of the list is delineated by a double NULL.
 */
const char *findBootConfigString(int n)
{
	KERNBOOTSTRUCT *kernBootStruct = KERNSTRUCT_ADDR;
	int stringsFound;
	const char *cp = kernBootStruct->config;
	int currentIndex = 0;
	int length;
	
	if(*cp == '\0') {
		IOLog("WARNING: No config table in KERNBOOTSTRUCT!\n");
		return NULL;
	}
	for(stringsFound=0; stringsFound<n; stringsFound++) {
		length = strlen(cp);
		currentIndex += (length + 1);
		cp += (length + 1);
		if( (length == 0) || 
		    (currentIndex > CONFIG_SIZE) ||
		    (*cp == '\0')) {
			/*
			 * Explicit end of list or beyond boundary.
			 */
			return NULL;
		}
	}
	return cp;
}


static void