File:  [Apple Darwin 0.x] / drvEIDE / EIDE.drvproj / EIDE.lksproj / IdeCntInit.m
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:41:00 2018 UTC (8 years, 1 month ago) by root
Branches: MAIN, Apple
CVS tags: HEAD, Darwin03
Darwin 0.3 EIDE device driver

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "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."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright 1997-1998 by Apple Computer, Inc., All rights reserved.
 * Copyright 1994-1997 NeXT Software, Inc., All rights reserved.
 *
 * IdeCntInit.m - ATA controller initialization module. 
 *
 * 1-Feb-1998	Joe Liu at Apple
 *	Added "new style" device overrides, and matching inspector.
 *  Report the transfer mode set for both drives.
 *	Set PIO transfer mode for ATAPI drives as well.
 *	getTransferModeFromCycleTime method now deals with Multiword DMA cases.
 *
 * 04-Mar-1997	 Scott Vail at NeXT
 *	Fixes made for modifications in ideDriveInfo_t struct.
 *
 * 04-Sep-1996	 Becky Divinski at NeXT
 *	Added code to ideControllerInit method handle Dual EIDE personality case.
 *
 * 3-Sept-1996	Becky Divinski at NeXT
 * 		Changed name of controllerPrersent method to 
 *		controllerPresent.
 *
 * 30-Jan-1996 	Rakesh Dubey at NeXT
 *      Added forced device detection to fix PIONEER bug. 
 *
 * 13-Jul-1995 	Rakesh Dubey at NeXT
 *      Improved device detection. 
 *
 * 11-Aug-1994 	Rakesh Dubey at NeXT
 *      Created. 
 */

#import "IdeCnt.h"
#import "IdeCntInit.h"
#import "IdeCntCmds.h"
#import "IdePIIX.h"
#import "AtapiCntCmds.h"
#import "IdeShared.h"
#import <driverkit/kernelDriver.h>
#import <driverkit/interruptMsg.h>
#if (IO_DRIVERKIT_VERSION != 330)
#import <machdep/machine/pmap.h>        // XXX get rid of this
#endif
#import <mach/mach_interface.h>
#import <driverkit/IODevice.h>
#import <driverkit/align.h>
#import <machkit/NXLock.h>
#import "IdeDDM.h"
#import <machdep/i386/io_inline.h>
#import "DualEide.h"
#import <driverkit/return.h>
#import <mach/port.h>
#import <stdlib.h>


//#define DEBUG

/*
 * The controller at base address 0x1f0-0x1f7 is always the first controller
 * no matter when it gets probed, so we start the counter at 1. 
 */
static unsigned char controllerNum = 1;

/*
 * There is no standard for channels 3 and 4 (channel 2 is almost there). We
 * should stick to the following base address/IRQ pairings: 1F0/14, 170/15,
 * 1E8/11, 168/10. 
 */
static __inline__
unsigned int
assignRegisterAddresses(ideRegsAddrs_t *ideRegsAddrs,
            unsigned int baseAddress1)
{
    unsigned int baseAddress2;
    unsigned char cntNum;
    
    switch (baseAddress1) {
      case 0x1f0:
	cntNum = 0;			/* primary, always controller 0 */
	break;
      default:
	cntNum = controllerNum++;
	break;
    }
    	
    baseAddress2 = baseAddress1 +  0x206;
    
    ideRegsAddrs->data = baseAddress1;
    ideRegsAddrs->error = baseAddress1 + 1;
    ideRegsAddrs->features = baseAddress1 + 1;
    ideRegsAddrs->sectCnt = baseAddress1 + 2;
    ideRegsAddrs->sectNum = baseAddress1 + 3;
    ideRegsAddrs->cylLow = baseAddress1 + 4;
    ideRegsAddrs->cylHigh = baseAddress1 + 5;
    ideRegsAddrs->drHead = baseAddress1 + 6;
    ideRegsAddrs->status = baseAddress1 + 7;
    ideRegsAddrs->command = baseAddress1 + 7;
    
    ideRegsAddrs->deviceControl = baseAddress2;
    ideRegsAddrs->altStatus = baseAddress2;

    return cntNum;
}

/*
 * Function: ata_mode_to_num
 *
 * Convert ata_mode_t into a value. i.e.
 *	0x01 ==> 0
 *	0x02 ==> 1
 */	 
extern unsigned char
ata_mode_to_num(ata_mode_t mode)
{
	unsigned char tmp = 0;
	if (mode == ATA_MODE_NONE) {
//		IOLog("bad argument to ata_mode_to_num()\n");
		return (0);
	}
	while (mode != 0x01) {
		mode >>= 1;
		tmp++;
	}
	return (tmp);
}

/*
 * Function: ata_mask_to_mode
 *
 * Return the most significant bit in a ata_mask_t mask. i.e.
 *	0x03 ==> 0x02
 *	0x07 ==> 0x04
 */
extern ata_mode_t
ata_mask_to_mode(ata_mask_t mask)
{
	int i;
	if (mask == 0)
		return ATA_MODE_NONE;
	for (i = 7; i >= 0; i--) {
		if ((1 << i) & mask)
			return (1 << i);
	}
	return ATA_MODE_NONE;
}

/*
 * Function: ata_mode_to_mask
 *
 * Convert from a mode to a mask such that all less significant bits,
 * including the mode bit is set.
 */
extern ata_mask_t
ata_mode_to_mask(ata_mode_t mode)
{
	return ((mode == ATA_MODE_NONE) ? mode : (mode | (mode - 1)));
}

@implementation IdeController(Initialize)


#define ATAPI_SIGNATURE_LOW		0x14
#define ATAPI_SIGNATURE_HIGH		0xeb

#define ATA_SIGNATURE_LOW		0x00
#define ATA_SIGNATURE_HIGH		0x00

/*
 * This is called just after resetting the drives connected to this
 * controller. We read the drive registers to figure out if it is an ATAPI
 * drive. If it is then we make a note of it and return YES. 
 */
- (BOOL)controllerPresent
{
    unsigned char dh;
    unsigned char unit;
    
    unit = 0;
    
    dh = _drives[unit].addressMode;
    
    dh |= (unit ? SEL_DRIVE1 : SEL_DRIVE0);
    outb(_ideRegsAddrs.drHead, dh);

    /*
     * Test with some data. 
     */
    
    if (unit == 0)	{
	outb(_ideRegsAddrs.cylLow, 0xaa);
	outb(_ideRegsAddrs.cylHigh, 0xbb);
    } else {
	outb(_ideRegsAddrs.cylLow, 0xba);
	outb(_ideRegsAddrs.cylHigh, 0xbe);
    }
    
    if (unit == 0) {
	if ((inb(_ideRegsAddrs.cylLow) != 0xaa) ||
	    (inb(_ideRegsAddrs.cylHigh) != 0xbb)) {
	    return NO;
	}
    } else {
	if ((inb(_ideRegsAddrs.cylLow) != 0xba) ||
	    (inb(_ideRegsAddrs.cylHigh) != 0xbe)) {
	    return NO;
	}
    }
    
    return YES;
}

/*
 * One-time only init. Returns NO on failure. 
 */
- (BOOL)ideControllerInit:(IODeviceDescription *)deviceDescription
{
    int     	unit;
    int     	drivesPresent = 0;
    const char  *params;
    const char  *addressingMode, *multisectorMode;
    IOEISADeviceDescription *eisaDeviceDescription =
		(IOEISADeviceDescription *)deviceDescription;
    IOConfigTable *configTable = [deviceDescription configTable];
    int		override[MAX_IDE_DRIVES];

#if 0
    _printWaitForNotBusy = NO;
#endif 0

    if (dualEide) {
    	if (instanceNum)	
            [super setDeviceDescription:deviceDescription];
    } 

    if ([self enableAllInterrupts]) {
		IOLog("ideControllerInit: failed enableAllInterrupts\n");
		return NO;
    }
    
    if ((_ideCmdLock = [NXLock new]) == NULL) {
		IOLog("ideControllerInit: failed _ideCmdLock\n");
		return NO;
    }
    
    /*
     * We don't want to execute any IDE commands from Disk until
     * initialization is complete. 
     */
    [self ideCntrlrLock];
    
    /*
     * Initialize the register address values.
     */
    _controllerNum = assignRegisterAddresses(&_ideRegsAddrs,
                            [eisaDeviceDescription portRangeList][0].start);

    for (unit = 0; unit < MAX_IDE_DRIVES; unit++) {
		_drives[unit].atapiDevice = NO;
    }
    
    /*
     * We have to test here if the controller is present or else we will
     * block looking for a drive (in ideReset). 
     */
    if ([self controllerPresent] == NO)	{
		IOLog("%s: no devices detected at port 0x%0x\n", [self name],
			_ideRegsAddrs.data);
		[self ideCntrlrUnLock];
		return NO;
    } else {
		IOLog("%s: device detected at port 0x%x irq %d\n", [self name], 
			_ideRegsAddrs.data, [deviceDescription interrupt]);
    }
    
    _ideInterruptPort = [self interruptPort];
    _ideDevicePort = [deviceDescription devicePort];

    [self setInterruptTimeOut:IDE_INTR_TIMEOUT];

	_multiSectorRequested = NO;
    multisectorMode = [configTable valueForStringKey:MULTIPLE_SECTORS_ENABLE];
	if (multisectorMode) {
		if ((*multisectorMode == 'Y') || (*multisectorMode == 'y'))
			_multiSectorRequested = YES;
		[configTable freeString:multisectorMode];
	}

    /*
     * Select mode for addressing drives. The driver will fall back to CHS
     * mode if LBA is unsupported by the drive.
     */
    addressingMode = [[deviceDescription configTable] 
    				valueForStringKey: ADDRESS_MODE];
    for (unit = 0; unit < MAX_IDE_DRIVES; unit++) {
        if ((addressingMode == NULL) || (strcmp(addressingMode, "LBA") != 0)){
			_drives[unit].addressMode = ADDRESS_MODE_CHS;
		} else {
			_drives[unit].addressMode = ADDRESS_MODE_LBA;
		}
	}
	if (addressingMode) [configTable freeString:addressingMode];

#ifdef DEBUG    
    if (_addressMode[0] == ADDRESS_MODE_LBA)	{
	IOLog("%s: Using Logical address mode..\n", [self name]);
    }
#endif DEBUG
    
    /*
     * Find out from config table if we are not using BIOS parameters.
     * Default is to use stored values in CMOS/BIOS.
     */
    params = [[deviceDescription configTable] 
    			valueForStringKey: DRIVE_PARAMETERS];
    for (unit = 0; unit < MAX_IDE_DRIVES; unit++) {
	if ((params == NULL) || ((*params != 'Y') && (*params != 'y')))
	    _drives[unit].biosGeometry = YES;
	else
	    _drives[unit].biosGeometry = NO;
    }
	if (params) [configTable freeString:params];

    /*
     * Same as DRIVE_PARAMETERS. See comment above.
     */
    params = [[deviceDescription configTable] 
    			valueForStringKey: USE_DISK_GEOMETRY];
    for (unit = 0; unit < MAX_IDE_DRIVES; unit++) {
		if (params && ((*params == 'Y') || (*params == 'y')))
	    	_drives[unit].biosGeometry = NO;
    }
	if (params) [configTable freeString:params];

    /*
     * Are we supporting ATAPI? 
     */
	_EIDESupport = YES;
    params = [[deviceDescription configTable] 
    			valueForStringKey: EIDE_SUPPORT];
	if (params) {
		if ((*params == 'N') || (*params == 'n'))
			_EIDESupport = NO;			
		[configTable freeString:params];
	}

	/*
	 * Obtain the transfer type/mode mask. A user may have disabled
	 * certain type of transfer modes.
	 */
	for (unit = 0; unit < MAX_IDE_DRIVES; unit++) {
		char *key;
		_drives[unit].driveMasks.modes = 0x000000ff;	// enable all PIO modes
		
		if (dualEide && instanceNum) {
			if (unit)
				key = MODES_MASK_SLAVE_SEC;
			else
				key = MODES_MASK_MASTER_SEC;
		}
		else {
			if (unit)
				key = MODES_MASK_SLAVE;
			else
				key = MODES_MASK_MASTER;
		}
		
    	params = [[deviceDescription configTable] 
    			valueForStringKey:key];		
		if (params) {
			// Read the mask set by the user, however we assume that
			// PIO modes 0 is always available.
			unsigned int mask = strtoul(params, NULL, 16);
			_drives[unit].driveMasks.modes = mask | 
				ata_mode_to_mask(ATA_MODE_0);
			[configTable freeString:params];
		}
	}

#if 0
    /*
     * Does the host support IOCHRDY? There is no way for the driver to find
     * this out. The user has to set this flag.
     */
    params = [[deviceDescription configTable] 
    			valueForStringKey: HOST_IORDY_SUPPORT];

    if ((params == NULL) || (strcmp(params, "Yes") != 0))
	_IOCHRDYSupport = NO;
    else
	_IOCHRDYSupport = YES;  
#endif /* 0 */

    _transferWidth = IDE_TRANSFER_16_BIT;

    /*
     * We acquire the disk geometry from either the BIOS or the disk itself.
     * If we are using bios we get the values from it. If we are not using
     * BIOS then we just determine presence of disk here. The actual geometry
     * will be read later. However if the user has chosen "disk geometry"
     * option and disk 0 is not detected then we fall back to BIOS for
     * geometry. This is intended to save the user from getting hosed in case
     * the disk does not support Identify drive command. 
     */

    /*
     * FIXME: This recovery strategy needs to thought over. 
     */
     
    _driveNum = unit;

    for (unit = 0; unit < MAX_IDE_DRIVES; unit++)
		override[unit] = DEVICE_AUTO;

    // Old-style overrides remain here for compatibility
    {
	const char *tmp;
	tmp = [configTable valueForStringKey:ATA_LOCATION];
        if (tmp) {
            if (strcmp(tmp, "Master")==0)
		override[0]=DEVICE_ATA;
            else if (strcmp(tmp, "Slave")==0)
		override[1]=DEVICE_ATA;
	    [configTable freeString:tmp];
        }
	tmp = [configTable valueForStringKey:ATAPI_LOCATION];
        if (tmp) {
            if (strcmp(tmp, "Master")==0)
		override[0]=DEVICE_ATAPI;
            else if (strcmp(tmp, "Slave")==0)
		override[1]=DEVICE_ATAPI;
	    [configTable freeString:tmp];
        }
    }
	
    // New-style overrides take precidence over Old-style overrides
    for (unit = 0; unit < MAX_IDE_DRIVES; unit++) {
        const char *tmp;
		
		if (dualEide && instanceNum)
			tmp = [configTable valueForStringKey:
				(unit ? IDE_SLAVE_KEY_SEC : IDE_MASTER_KEY_SEC)];
		else
        	tmp = [configTable valueForStringKey:
				(unit ? IDE_SLAVE_KEY : IDE_MASTER_KEY)];
		
		if (tmp == NULL)
			continue;

		if ( (strcmp(tmp, "")==0) ||
			(strcmp(tmp, overrideTable[DEVICE_AUTO])==0) )
			override[unit] = DEVICE_AUTO;	
		else if (strcmp(tmp, overrideTable[DEVICE_NONE])==0)
			override[unit] = DEVICE_NONE;
		else if ( (strcmp(tmp, overrideTable[DEVICE_ATA])==0) ||
			(strcmp(tmp, "IDE")==0) || (strcmp(tmp, "EIDE")==0) )
			override[unit] = DEVICE_ATA;
		else if ( (strcmp(tmp, overrideTable[DEVICE_ATAPI])==0) ||
			(strcmp(tmp, "CD")==0) || (strcmp(tmp, "CDROM")==0) )
			override[unit] = DEVICE_ATAPI;
		[configTable freeString:tmp];
    }

    for (unit = 0; unit < MAX_IDE_DRIVES; unit++) {
        switch (override[unit]) {
            case DEVICE_NONE:
				IOLog("%s: Drive %d scan will be skipped due to override.\n",
					[self name], unit);
				break;
            case DEVICE_ATA:
				IOLog("%s: Drive %d forced to ATA by override.\n",
					[self name], unit);
				break;
            case DEVICE_ATAPI:
				IOLog("%s: Drive %d forced to ATAPI by override.\n",
					[self name], unit);
				break;
            default :
				break;
	}
	bzero(&(_drives[unit].ideInfo), sizeof(ideDriveInfo_t));
	
	[self ideReset];
	
	/*
	 * We need to use the disk geometry (and override user selection) if
	 * the BIOS reports more than 16 heads. 
	 */
	if (_drives[unit].biosGeometry == YES)	{
	    _drives[unit].ideInfo = [self getIdeInfoFromBIOS:unit];
	    if ((_drives[unit].ideInfo.type != 0) &&
			(_drives[unit].ideInfo.heads > 16)) {
			_drives[unit].biosGeometry = NO;
			_drives[unit].ideInfo.type = 0;
			IOLog("%s: WARNING: using disk geometry for drive %d.\n", 
			    [self name], unit);
	    }
	}

	/*
	 * If IDE_IDENTIFY_DRIVE command fails then we will override user
	 * selection and use BIOS geometry if this is the boot disk (first
	 * disk on first controller). 
	 */
	if ((override[unit] == DEVICE_AUTO) || (override[unit] == DEVICE_ATA)) {
	    if (_drives[unit].biosGeometry == NO)	{
	        if ([self ideDetectDrive:unit override:override[unit]] == YES)	{
		    _drives[unit].ideInfo.type = 255;
	        } else if ((_controllerNum == 0) && (unit == 0))	{
		    _drives[unit].ideInfo = [self getIdeInfoFromBIOS:unit];
		    if (_drives[unit].ideInfo.type != 0)	{
		        _drives[unit].biosGeometry = YES;
		        IOLog("%s: WARNING: using BIOS geometry for drive %d.\n", 
				    [self name], unit);
		    }
	        }
	    }
	}

	/*
	 * If we didn't find ATA drive then look for ATAPI device. 
	 */
	if ((override[unit] == DEVICE_AUTO) || (override[unit] == DEVICE_ATAPI)) {
	    if ((_drives[unit].ideInfo.type == 0) && (_EIDESupport == YES) &&
	        ([self ideDetectATAPIDevice:unit override:override[unit]] == YES)){
	        _drives[unit].ideInfo.type = 127;
	    }
	}

	if (_drives[unit].ideInfo.type != 0)
	    drivesPresent += 1 ;
    }
    
    if (drivesPresent == 0)	{
		[self ideCntrlrUnLock];
		IOLog("ideControllerInit: failed drivesPresent\n");
		return NO;
    }

    /*
     * Initialization was successful. We should release the lock so that the
     * controller can now execute commands from disk objects. 
     */

    [self resetAndInit];

#ifdef DDM_DEBUG
    IOInitDDM(IDE_NUM_DDM_BUFS);
#endif DDM_DEBUG
    
    /*
     * Set drive power state to active.
     */
    for (unit = 0; unit < MAX_IDE_DRIVES; unit++) {
		if (_drives[unit].ideInfo.type != 0)
	    	[self setDrivePowerState:IDE_PM_ACTIVE];
    }
    
    _driveSleepRequest = NO;
    [self enableInterrupts];
    [self ideCntrlrUnLock];
    return YES;
}

/*
 * Method: getControllerCapability
 *
 * Query the controller's transfer mode capability and record them
 * to _controllerModes.
 *
 * Note that _controllerModes fields contain the mask of all modes that it
 * supports. Not just the highest possible mode.
 */
- (void) getControllerCapability
{
	/*
	 * Set default values.
	 */
	_controllerModes.mode.pio   = ATA_MODE_0;
	_controllerModes.mode.swdma = ATA_MODE_NONE;
	_controllerModes.mode.mwdma = ATA_MODE_NONE;
	_controllerModes.mode.udma  = ATA_MODE_NONE;
	
	if (_controllerID != PCI_ID_NONE)
		[self getPCIControllerCapabilities:(txferModes_t *)&_controllerModes];
	else {
		// Assume all IDE controllers are capable of PIO Mode 4
		//
		_controllerModes.mode.pio   = ata_mode_to_mask(ATA_MODE_4);
	}
}

#define ATA_RESET_DELAY		1500		/* milliseconds */

/*
 * Determine the presence and type of drive (IDE/ATAPI) attached to this
 * controller. We use this only if we are not using the info from CMOS/BIOS. 
 *
 * Even though this is an optional command it is safe to use since all ATA 
 * disks support it. Vey old disks may not support it and they can be used 
 * by the "Use BIOS Geometry" option. 
 *
 * Note Well: Some drives bail out immediately and return with ERR bit set in
 * the status register. Some bail out after a minute and set ERR bit. However
 * some simply return zero in the status register. I am sure there are other
 * variants. Hence we check for DRQ (success) bit. Note that we would not get
 * an interrupt since -registerDevice has not been called yet. This routine
 * probably needs some more thinking. It is very important that this routine
 * never detect ATAPI drives as ATA. 
 */

/*
 * In Micron machine, if there is one ATAPI CD-ROM, it returns 78 or 7a in the
 * status register. Completely bogus. 
 */
-(BOOL)ideDetectDrive:(unsigned int)unit override:(int)override
{
    unsigned char dh = _drives[unit].addressMode;
    int i;
    unsigned char status;
    BOOL found = NO;
    
    if ((override != DEVICE_AUTO) && (override != DEVICE_ATA))
	return NO;

    IOLog("%s: Checking for ATA drive %d...  ", [self name], unit);
#ifdef DEBUG
    IOLog("\n");
#endif DEBUG
    
    [self disableInterrupts];
    
    dh |= (unit ? SEL_DRIVE1 : SEL_DRIVE0);
    outb(_ideRegsAddrs.drHead, dh);
    
    outb(_ideRegsAddrs.command, IDE_IDENTIFY_DRIVE);

    for (i = 0; i < (ATA_RESET_DELAY * 100); i++) {
		IODelay(10);
		status = inb(_ideRegsAddrs.status);
		
		if (status & BUSY)
			continue;
		
		if (status & ERROR) {
			break;				/* certain failure */
		}
		if (unit && (status & WRITE_FAULT)) {
			break;				/* failure as well */
		}
		if (status & DREQUEST) {
			found = YES;		/* success */
			break;
		}    
    }
#ifdef DEBUG
    IOLog("%s: ideDetectDrive: status %x\n", [self name], status);
#endif DEBUG
    
    if (found)	{
        IOLog("Detected\n");
    	return YES;
    }
    
    /*
     * If we got here this means that the ATA device is not present or was
     * not detected. Check if the user has told us that the drive is present. 
     */
    
    if (override == DEVICE_ATA) {
	IOLog("Not detected but proceeding\n");
	return YES;
    }

    IOLog("\n");
    return NO;
}

/*
 * We send the device ATAPI soft reset command and check for signature. This
 * is the first command that ATAPI devices need. 
 */
-(BOOL)ideDetectATAPIDevice:(unsigned int)unit override:(int)override
{
    unsigned char low, high;
//  const char  *location;
    BOOL found = NO;
    
    if ((override != DEVICE_AUTO) && (override != DEVICE_ATAPI))
	return NO;

    IOLog("%s: Checking for ATAPI drive %d... ", [self name], unit);
#ifdef DEBUG
    IOLog("\n");
#endif DEBUG
	
    [self atapiSoftReset:unit];
	
    low = inb(_ideRegsAddrs.cylLow);
    high = inb(_ideRegsAddrs.cylHigh);
    
    if ((low == ATAPI_SIGNATURE_LOW) && (high == ATAPI_SIGNATURE_HIGH)) {
		_drives[unit].atapiDevice = YES;
#ifdef undef
	IOLog("%s: ATAPI drive %d detected.\n", [self name], unit);
#endif undef
	found = YES;
    } else if (_ide_debug) {
	IOLog("%s: No ATAPI drive %d, low = %x high = %x\n", 
		[self name], unit, low, high);
    }
    
    if (found)	{
        IOLog("Detected\n");
    	return YES;
    }
    
    /*
     * If we got here this means that the ATAPI device is not present or was
     * not detected. Check if the user has told us that the device is
     * present. This will probably fail later but succeeds in case of some
     * drives (like PIONEER DR-124X 1.02). It is definitely lame to work
     * around firmware bugs in the driver but there seems to be no
     * alternative.. 
     */
    if (override == DEVICE_ATAPI) {
	_drives[unit].atapiDevice = YES;
	IOLog("Not detected but proceeding\n");
	return YES;
    }
    
    IOLog("\n");
    return NO;
}


-(ideDriveInfo_t)getIdeDriveInfo:(unsigned int)unit
{
    return _drives[unit].ideInfo;
}

#define MAX_RESET_ATTEMPTS 		2

- (void)ideReset
{
    int count;
    int delay;
    unsigned char status;

#ifdef DEBUG_TRACE
    IOLog("%s: ideReset\n", [self name]);
#endif DEBUG_TRACE

    for (count = 0; count < MAX_RESET_ATTEMPTS; count++) {
    
	outb(_ideRegsAddrs.deviceControl, DISK_RESET_ENABLE);
	IODelay(100);	   		/* spec >= 25 us */
	outb(_ideRegsAddrs.deviceControl, 0x0);
	
	[self enableInterrupts];

    	delay = 31 * 1000 * 1000;	/* thirty one seconds */
	
	IOSleep(1000);			/* Enough time to assert busy */
	
	while (delay > 0) {
	
	    status = inb(_ideRegsAddrs.status);
	    if (!(status & BUSY)) {
		return;
	    }
	    
	    IOSleep(1);
	    delay -= 1000;
	}
	
	IOLog("%s: Reset failed, retrying..\n", [self name]);
	IOSleep(2000);
    }
    
    /* FIXME: I don't think we should panic */
    if (count == MAX_RESET_ATTEMPTS) {
	IOLog("%s: can not reset.\n", [self name]);
	IOPanic("IDE Reset");
	/* NOT REACHED */
    }
}

/*
 * Initialize the IDE interface. Find about drive capabilities (by
 * IDE_IDENTIFY_DRIVE command) and configure drive. We either use CMOS/BIOS
 * settings to program the drive or bypass CMOS/BIOS and use the values
 * returned by the drive depending upon biosGeometry flag. By the time we
 * enter this routine we know about all devices connected to this controller. 
 */
- (void)resetAndInit
{
    int i;
    unsigned char unit;
    ide_return_t rtn;
    BOOL ataDevicePresent;
	BOOL retry;
	
	/*
	 * Determine what transfer modes the controller is capable of.
	 */
	[self getControllerCapability];

	/* Qualify the default mask for each drive with the
	 * controller's mask.
	 */
	for (i = 0; i < MAX_IDE_DRIVES; i++) {
		_drives[i].driveMasks.modes &= _controllerModes.modes;
	}
	
	/*
	 * Loops through one cycle of the configuration process:
	 *
	 * 0. Get controller capability.
	 * 1. Revert controller back to compatibility timing.
	 * 2. Reset ATA/ATAPI.
	 * 3. Determine drive(s) capabilities.
	 * 4. Set drive(s) feature according to controller & drive capability.
	 * 5. Set controller timing and mode. (UDMA, PIO, etc...)
	 * 6. If DMA or Multisector mode, perform test.
	 * 7. If test failed, mask out the current mode, goto step 1.
	 */

	do {
	
	/*
	 * Reset the IDE controller to stop any transfer in progress, and
	 * revert back to the slowest timing settings.
	 */
	[self resetController];
	
	retry = NO;
    IOLog("%s: Resetting drives...\n", [self name]);

    /*
     * If there is at least one ATA drive connected to this controller then
     * is is necessary to (ATA style) reset them. Otherwise, We use the ATAPI 
     * reset instead (in setATAPIDriveCapabilities: method below).
     */
    ataDevicePresent = NO;
    for (i = 0; i < MAX_IDE_DRIVES; i++) {
		if ((_drives[i].ideInfo.type != 0) && ([self isAtapiDevice:i] == NO)){
			ataDevicePresent = YES;
			break;
		}
    }
    if (ataDevicePresent == YES) {
#ifdef DEBUG
        IOLog("%s: ATA device present on this controller\n", [self name]);
#endif DEBUG
		[self ideReset];
    }

    /*
     * Send the controller necessary commands to set capabilities and then
     * set drive parameters. It is necessary to set drive parameters before
     * any data I/O can be done from the disk. 
     */
    for (unit = 0; unit < MAX_IDE_DRIVES; unit++) {

		if (_drives[unit].ideInfo.type == 0)	{
			//IOLog("%s: Drive %d not present.\n", [self name], unit);
			continue;
		}

		/* Set default values.
		 */
		_drives[unit].multiSector  = 0;
		_drives[unit].transferType = IDE_TRANSFER_PIO;
		_drives[unit].transferMode = ATA_MODE_0;

		/*
		 * ATAPI devices.
		 */
		if ([self isAtapiDevice:unit] == YES) {
			rtn = [self setATAPIDriveCapabilities:unit];
			if (rtn != IDER_SUCCESS) {
				IOLog("%s: ATAPI drive %d is not present.\n",
					[self name], unit);
				_drives[unit].ideInfo.type = 0;
				_drives[unit].atapiDevice = NO;
			}
			continue;
		}
		
		/*
		 * ATA devices.
		 */
		if (_drives[unit].biosGeometry == YES)	{
			rtn = [self setATADriveCapabilities:unit withBIOSInfo:YES];
		} else {
			rtn = [self setATADriveCapabilities:unit withBIOSInfo:NO];
			if (rtn != IDER_SUCCESS) {
				IOLog("%s: ATA drive %d is not present.\n", [self name], unit);
				_drives[unit].ideInfo.type = 0;
				continue;
			}
		}
    }

    /*
     * Set IDE controller capabilities. This must be done after the disks are
     * configured. 
     */
	[self setControllerCapabilities];

	/*
	 * Report the transfer mode set for both drives.
	 */
	if (_controllerID != PCI_ID_NONE) {
		const char *mode_string[] =
			{"PIO", "Single-word DMA", "Multiword DMA", "Ultra DMA"};
		for (unit = 0; unit < MAX_IDE_DRIVES; unit++) {
			if (_drives[unit].ideInfo.type == 0)
				continue;
			if (_drives[unit].multiSector)
				IOLog("%s: Drive %d: %s Mode %d (%d sectors)\n",
					[self name], unit, mode_string[_drives[unit].transferType], 
					ata_mode_to_num(_drives[unit].transferMode),
					_drives[unit].multiSector);
			else				
				IOLog("%s: Drive %d: %s Mode %d\n",
					[self name], unit, mode_string[_drives[unit].transferType], 
					ata_mode_to_num(_drives[unit].transferMode));
		}
	}
	
	/*
	 * After the drives and the controller are setup, we perform a read
	 * and make sure everything is OK. We do not perform this test for
	 * ATAPI or removable ATA devices (if they become supported).
	 *
	 * FIXME: perhaps instead of reading a block from the disk, we should
	 * consider using DMA on non-media commands. This will be needed when
	 * DMA is supported on removable media drives. Do those commands exist?
	 * It seems that ATA-3 defined a IDENTIFY DEVICE DMA command, but they
	 * no longer exist in ATA-4. Did they get dropped?
	 */
	for (unit = 0; unit < MAX_IDE_DRIVES; unit++) {

		_driveNum = unit;
		
		// Don't test ATAPI devices.
		//
		if ([self isAtapiDevice:unit])
			continue;
		
		// Test DMA reads.
		//
		if ((_drives[unit].ideInfo.type != 0) &&	
			(_drives[unit].transferType != IDE_TRANSFER_PIO) &&
			([self performDMATest] != IDER_SUCCESS)) {
			IOLog("%s: Drive %d: DMA read test FAILED\n", [self name], unit);
			/* Failure, mask out the current mode */
			_drives[unit].driveMasks.array.mode[_drives[unit].transferType] &= 
				~_drives[unit].transferMode;
			retry = YES;
		}

		// Test PIO multisector reads.
		//
		// FIXME - Test Fast PIO modes as well?
		//
		if ((_drives[unit].ideInfo.type != 0) &&
			(_drives[unit].transferType == IDE_TRANSFER_PIO) &&
			(_drives[unit].multiSector)) {

			ideRegsVal_t ideRegs;
			char *buf;
			
			ideRegs = [self logToPhys:0 numOfBlocks:_drives[unit].multiSector];
			buf = IOMalloc(_drives[unit].multiSector * IDE_SECTOR_SIZE);
			if ([self ideReadMultiple:&ideRegs 
	    		client:(struct vm_map *)IOVmTaskSelf()
	    		addr:buf] != IDER_SUCCESS) {
	    		IOLog("%s: Drive %d: Read Multiple test FAILED\n",
					[self name], unit);
				_multiSectorRequested = NO;
				retry = YES;
	    	}
	    	IOFree(buf, _drives[unit].multiSector * IDE_SECTOR_SIZE);
		}
	}

	} while (retry);
}

/*
 * Method: resetController
 *
 * Reset the controller on the HOST side.
 */
- (void)resetController
{
	/*
	 * Return the controller to the compatible timing mode.
	 */
	[self resetPCIController];
}

/*
 * Method: getBestTransferMode
 *
 * Find the "best" mode given the txferModes_t structure for both the drive
 * and the controller. Recall that the txferModes_t type encodes all the
 * transfer modes that a device can attain. Certain modes may also be masked
 * so that they will never be used. This mask is stored in the driveMasks.
 */
-(void)getBestTransferMode:(ata_mode_t *)mode type:(ideTransferType_t *)type 
	forUnit:(unsigned int)unit
{
	txferModes_t m;		// store all the possible modes.

	m.modes = _drives[unit].driveModes.modes & _drives[unit].driveMasks.modes &
		_controllerModes.modes;

	/*
	 * Test in the order of "best" to "worst" modes.
	 */	
	if (m.mode.udma) {
		*type = IDE_TRANSFER_ULTRA_DMA;
		*mode = ata_mask_to_mode(m.mode.udma);
	}
	else if (m.mode.mwdma) {
		*type = IDE_TRANSFER_MW_DMA;
		*mode = ata_mask_to_mode(m.mode.mwdma);
	}
	else if (m.mode.swdma) {
		*type = IDE_TRANSFER_SW_DMA;
		*mode = ata_mask_to_mode(m.mode.swdma);
	}
	else if (m.mode.pio) {
		*type = IDE_TRANSFER_PIO;
		*mode = ata_mask_to_mode(m.mode.pio);
	}
	else {
		*type = IDE_TRANSFER_PIO;
		*mode = ATA_MODE_0;
	}
}

/*
 * Method: getTransferModes:fromInfo:
 *
 * Purpose:
 * Given an infoPtr which points to information from the IDE Identify Drive
 * command, determine the transfer modes that the drive is capable of.
 *
 */
- (void)getTransferModes:(txferModes_t *)modes
	fromInfo:(ideIdentifyInfo_t *)infoPtr 
{
	unsigned char n;
    ata_mode_t m = ATA_MODE_0;
	int i;

    /*
     * For PIO, check if we support the ATA-2 additions. 
     */
	if (infoPtr->fieldValidity & IDE_WORDS64_TO_70_SUPPORTED) {
		if (infoPtr->fcPioDataTransferCyleTimingMode &
			IDE_FC_PIO_MODE_5_SUPPORTED)
			m = ATA_MODE_5;
		else if (infoPtr->fcPioDataTransferCyleTimingMode &
			IDE_FC_PIO_MODE_4_SUPPORTED)
			m = ATA_MODE_4;
		else if (infoPtr->fcPioDataTransferCyleTimingMode &
			IDE_FC_PIO_MODE_3_SUPPORTED)
			m = ATA_MODE_3;
	}
	else {
		n = (infoPtr->pioDataTransferCyleTimingMode &
			IDE_PIO_TIMING_MODE_MASK) >> 8;
		if (n >= 2) n = 2;
		m = (1 << n);	// convert number to mode
	}
		
	/* For mode 2 and above, IORDY must be supported. */
	if ((m > ATA_MODE_2) &&
		!(infoPtr->capabilities & IDE_CAP_IORDY_SUPPORTED))
		m = ATA_MODE_2;
	
	/*
	 * A drive supporting a certain mode must also support slower modes
	 * of the same transfer type.
	 */
	modes->mode.pio = ata_mode_to_mask(m);
	
	/*
	 * Drive must indicate that it supports DMA in Word 49 before
	 * continuing.
	 */
	if (!(infoPtr->capabilities & IDE_CAP_DMA_SUPPORTED)) {
		modes->mode.swdma = ATA_MODE_NONE;
		modes->mode.mwdma = ATA_MODE_NONE;
		modes->mode.udma  = ATA_MODE_NONE;
		return;
	}
	
	/*
	 * Single word DMA. Read from Word 52.
	 */
	n = (infoPtr->dmaDataTransferCyleTimingMode &
		IDE_DMA_TIMING_MODE_MASK) >> 8;
	if (n >= 2)
		n = 2;
	m = (1 << n);
	modes->mode.swdma = ata_mode_to_mask(m);	
	
	/*
	 * Multiword DMA. Read Word 63.
	 */
	m = ATA_MODE_NONE;
	for (i = 2; i >= 0; i--) {
		if (infoPtr->mwDma & (1 << i)) {
			m = (1 << i);
			break;
		}
	}
	/* Can't be Multiword DMA mode 1 and above and NOT support
	 * Words 64 through 70.
	 */
	if ((m >= ATA_MODE_1) &&
		!(infoPtr->fieldValidity & IDE_WORDS64_TO_70_SUPPORTED)) {
		m = ATA_MODE_0;
    }
	modes->mode.mwdma = ata_mode_to_mask(m);	
    
	/*
	 * Ultra DMA. Read capability from Word 88.
	 */
	m = ATA_MODE_NONE;
	if (infoPtr->fieldValidity & IDE_WORD88_SUPPORTED) {
		for (i = 2; i >= 0; i--) {
			if (infoPtr->UDma & (1 << i)) {
				m = (1 << i);
				break;
			}
		}
	}
	modes->mode.udma = ata_mode_to_mask(m);

    return;
}

/*
 * We do host dependent hardware initialization here. 
 */
- (BOOL) setControllerCapabilities
{
	if (_controllerID != PCI_ID_NONE) {
		if ([self setPCIControllerCapabilitiesForDrives:_drives] == NO)
			return NO;
		_transferWidth = [self getPIOTransferWidth];
		return YES;
	}
	return YES;
}

/*
 * Method: setATADriveCapabilities
 *
 * Send the Set Parameters command to the drive and set multi-sector mode
 * etc. if the drive supports them. If biosInfo is YES, then we are using
 * drive geometry information from the BIOS else we use Identify command to
 * get this. 
 */
- (ide_return_t)setATADriveCapabilities:(unsigned int)unit	
		withBIOSInfo:(BOOL)biosInfo
{
    ideRegsVal_t ideRegs;
    unsigned char nSectors;
	ide_return_t rtn;
    ideIdentifyInfo_t *infoPtr = _drives[unit].ideIdentifyInfo;

    _drives[unit].ideIdentifyInfoSupported = YES;
    bzero(infoPtr, sizeof(ideIdentifyInfo_t));

    _driveNum = unit;

    /*
     * Remember this command is optional. Failure means that either this
     * command is not supported or the drive is not present.
     */
    if ([self ideReadGetInfoCommon:&ideRegs 
    		client:(struct vm_map *)IOVmTaskSelf() 
    		addr:(unsigned char *)infoPtr
			command:IDE_IDENTIFY_DRIVE] != IDER_SUCCESS) {
		_drives[unit].ideIdentifyInfoSupported = NO;
		if (_ide_debug)
	    	IOLog("ATA: ideReadGetInfoCommon failed.\n");
		/*
		 * FIXME: If this is a slave device and we perform a reset. The
		 * master device will lose its configuration state.
		 */
		[self ideReset];	/* necessary */
        return IDER_CMD_ERROR;
    }

    /*
     * Fill in this struct similar to which we would have gotten from CMOS. 
     */
    if (biosInfo == NO)	{
		ideDriveInfo_t *ip = &(_drives[unit].ideInfo);
	
		ip->cylinders = _drives[unit].ideIdentifyInfo->cylinders;
		ip->heads = _drives[unit].ideIdentifyInfo->heads;
		ip->control_byte = 0;
		ip->landing_zone = 0;
		ip->sectors_per_trk = _drives[unit].ideIdentifyInfo->sectorsPerTrack;
		ip->bytes_per_sector = IDE_SECTOR_SIZE;
		if ([IODevice driverKitVersion] > 410) { 
			ip->total_sectors = ip->sectors_per_trk *
								ip->heads *
								ip->cylinders;
			
			/* If disk supports LBA, and Words (61:60) indicates that the
			 * user-addressable logical sectors is larger than the number
			 * of sectors computed through CHS translation, then use the
			 * larger value. This will be needed for disks with more than
			 * 16,514,064 sectors. (16383 C x 16 H x 63 S)
			 */
			if ((infoPtr->capabilities & IDE_CAP_LBA_SUPPORTED) &&
				(infoPtr->userAddressableSectors > ip->total_sectors))
				ip->total_sectors = infoPtr->userAddressableSectors;
		}
		else {
    		// fake capacity for backward compatibility.
			ip->total_sectors = ip->sectors_per_trk * ip->heads *
		    	ip->cylinders * ip->bytes_per_sector;
		}
    }

    /*
     * Set address mode. The only case in which we need to override user
     * selection if the user chooses LBA and the drive supports only CHS. 
     */
    if (((infoPtr->capabilities & IDE_CAP_LBA_SUPPORTED) == 0x0) &&
		(_drives[unit].addressMode == ADDRESS_MODE_LBA)) {
#ifdef DEBUG
		IOLog("%s: WARNING: LBA mode is not supported by drive %d.\n",
		    [self name], unit);
#endif DEBUG
		_drives[unit].addressMode = ADDRESS_MODE_CHS;
	}

    /*
     * Set disk parameters (Initialize Device Parameters command). 
     */
    [self ideSetParams:_drives[unit].ideInfo.sectors_per_trk
		numHeads:_drives[unit].ideInfo.heads ForDrive:unit];
	
	/*
	 * Determine the best transfer mode.
	 */
	[self getTransferModes:&_drives[unit].driveModes fromInfo:infoPtr];
	[self getBestTransferMode:&_drives[unit].transferMode
		type:&_drives[unit].transferType
		forUnit:unit];

	/*
	 * Determine the number of sectors for each Multiple Read/Write.
	 */
	nSectors = infoPtr->multipleSectors & IDE_MULTI_SECTOR_MASK;
	if ((_drives[unit].transferType == IDE_TRANSFER_PIO) &&
		(nSectors) && (_multiSectorRequested == YES)) {
		if ([self ideSetMultiSectorMode:&ideRegs numSectors:nSectors] ==
			IDER_SUCCESS) {
			_drives[unit].multiSector = nSectors;
		}
	}

#if 0
	IOLog("Drive      modes: %08x\n", _driveModes[unit].modes);
	IOLog("Controller modes: %08x\n", _controllerModes.modes);
	IOLog("drive %d: MODE:0x%02x TYPE:%d\n", unit, _transferMode[unit],
		_transferType[unit]);
#endif

	/*
	 * Set drive feature.
	 */
	rtn = [self ideSetDriveFeature:FEATURE_SET_TRANSFER_MODE
			value:_drives[unit].transferMode
			transferType:_drives[unit].transferType];

	if (rtn != IDER_SUCCESS) {
		IOLog("%s: Drive %d: set transfer mode failed\n", [self name], unit);
		_drives[unit].transferType = IDE_TRANSFER_PIO;
		_drives[unit].transferMode = ATA_MODE_0;
	}

    return IDER_SUCCESS;
}

/*
 * Method: setATAPIDriveCapabilities
 *
 * Identify the ATAPI device and set its transfer type/mode.
 */
- (ide_return_t)setATAPIDriveCapabilities:(unsigned int)unit	
{
    atapi_return_t 	rtn;

	_driveNum = unit;
		
	[self atapiSoftReset:unit];

	/*
	 * We have to do this test because of "task file shadow" bug
	 * present in many ATAPI CD-ROMs. 
	 */
	rtn = [self atapiIdentifyDevice:(struct vm_map *)IOVmTaskSelf()
			addr:(unsigned char *)_drives[unit].ideIdentifyInfo unit:unit];
		
	/* Too bad.. */
	if (rtn != IDER_SUCCESS)
		return rtn;

	[self printAtapiInfo:_drives[unit].ideIdentifyInfo Device:unit];
	[self atapiInitParameters:_drives[unit].ideIdentifyInfo Device:unit];

	_drives[unit].addressMode = ADDRESS_MODE_LBA;
	
	[self getTransferModes:&_drives[unit].driveModes
		fromInfo:_drives[unit].ideIdentifyInfo];
	[self getBestTransferMode:&_drives[unit].transferMode
		type:&_drives[unit].transferType forUnit:unit];

	rtn = [self ideSetDriveFeature:FEATURE_SET_TRANSFER_MODE
			value:_drives[unit].transferMode
			transferType:_drives[unit].transferType];
	if (rtn != IDER_SUCCESS) {
		IOLog("%s: Drive %d: set transfer mode failed\n",
			[self name], unit);
		_drives[unit].transferMode = ATA_MODE_0;
		_drives[unit].transferType = IDE_TRANSFER_PIO;
	}

	return IDER_SUCCESS;
}

-(ideIdentifyInfo_t *)getIdeIdentifyInfo:(unsigned int)unit
{
    return _drives[unit].ideIdentifyInfoSupported ?
		_drives[unit].ideIdentifyInfo : NULL;
}

-(BOOL)isDiskGeometry:(unsigned int)unit
{
    return (_drives[unit].biosGeometry == YES) ? NO : YES;
}


- (ideDriveInfo_t)getIdeInfoFromBIOS:(unsigned)unit 
{
    caddr_t hdtbl;
    ideDriveInfo_t hdInfo;

    hdInfo.type = [self getIdeTypeFromCMOS:unit];
    if ((hdInfo.type == 0) || (hdInfo.type < MIN_CMOS_IDETYPE) ||
	(hdInfo.type > MAX_CMOS_IDETYPE)) {
	hdInfo.type = 0;
	return (hdInfo);
    }

    /*
     * Drive info (the drive characteristic table) for drive 0 is located at
     * INT41h and for drive 1 it is located at INT46h. 
     */
    if (unit == 0) {
	hdtbl = (caddr_t) (*(unsigned short *)(0x41 << 2) +
			(*(unsigned short *)((0x41 << 2) + 2) << 4));
    } else if (unit == 1) {
	hdtbl = (caddr_t) (*(unsigned short *)(0x46 << 2) +
			(*(unsigned short *)((0x46 << 2) + 2) << 4));
    } else {
	hdInfo.type = 0;
	return (hdInfo);
    }

    if (hdtbl == 0) {
	IOLog("%s: drive %d, type %d, using geometry from CMOS.\n",
		[self name], unit, hdInfo.type);
    } else {
	IOLog("%s: drive %d, type %d, using geometry from INT table.\n",
		[self name], unit, hdInfo.type);
    }
    
    if (hdtbl == 0) {
	hdtbl = (caddr_t) pmap_phys_to_kern(CMOS_IDE_TABLE);
	hdtbl += ((hdInfo.type - 1) * SIZE_OF_IDE_TABLE);
    }
    
    hdInfo.cylinders = *(unsigned short *)hdtbl;
    hdInfo.heads = *(hdtbl + 2);
    hdInfo.precomp = *(unsigned short *)(hdtbl + 5);
    hdInfo.control_byte = *(hdtbl + 8);
    hdInfo.landing_zone = *(unsigned short *)(hdtbl + 12);
    hdInfo.sectors_per_trk = *(hdtbl + 14);
    hdInfo.bytes_per_sector = IDE_SECTOR_SIZE;
    if([IODevice driverKitVersion] > 410) { 
	hdInfo.total_sectors = hdInfo.sectors_per_trk *
	    hdInfo.heads * hdInfo.cylinders;
    }
    else {
    	// fake capacity for backward compatibility.
	hdInfo.total_sectors = hdInfo.sectors_per_trk *
	    hdInfo.heads * hdInfo.cylinders * hdInfo.bytes_per_sector;
    }

#ifdef DEBUG
    /*
     * FIXME: This is for testing only. Remove later. 
     */
    IOLog("%s: drive %d, %d cylinders, %d heads, %d sectors (BIOS).\n", 
	[self name], unit, hdInfo.cylinders, hdInfo.heads, 
	hdInfo.sectors_per_trk);
#endif DEBUG

    return hdInfo;
}

- (unsigned)getIdeTypeFromCMOS:(int)unit
{
    unsigned int hdtype;

    outb(CMOSADDR, HDTYPE);
    IODelay(10);
    hdtype = inb(CMOSDATA);

    if (unit == 0) {
	hdtype = hdtype >> 4;
	if (hdtype != 0xf) {
	    return hdtype;
	} else {
	    outb(CMOSADDR, HD0_EXTTYPE);
	    IODelay(10);
	    hdtype = inb(CMOSDATA);
	}
    } else {
	hdtype = hdtype & 0x0f;
	if (hdtype != 0xf) {
	    return hdtype;
	} else {
	    outb(CMOSADDR, HD1_EXTTYPE);
	    IODelay(10);
	    hdtype = inb(CMOSDATA);
	}
    }


    return hdtype;
}

@end


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.