Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
MoreDevices/TradDriverLoaderLib/TradDriverLoaderLib.c
/* |
File: TradDriverLoaderLib.c |
Contains: Implementation for the pseudo-DriverLoaderLib for 'DRVR's. |
Written by: Quinn |
Copyright: Copyright © 1996-1999 by Apple Computer, Inc., all rights reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
<2> 5/6/00 Quinn Missed a check for MemError in TradInstallDriverFromHandle. |
Thanks to Jim Luther for spotting this. |
<1> 25/2/99 Quinn First checked in. |
*/ |
///////////////////////////////////////////////////////////////// |
// MoreIsBetter Setup |
#include "MoreSetup.h" |
///////////////////////////////////////////////////////////////// |
// Mac OS Interfaces |
#include <LowMem.h> |
#include <DriverGestalt.h> |
#include <TextUtils.h> |
// Switched from using: |
// |
// #include <PLStringFuncs.h> |
// |
// to using BlockMoveData because it's so hard to get PLstrcpy working |
// across a zillion different compilers. *sigh* |
///////////////////////////////////////////////////////////////// |
// MIB Prototypes |
#include "MoreInterfaceLib.h" |
///////////////////////////////////////////////////////////////// |
// Our Prototypes |
#include "TradDriverLoaderLib.h" |
/////////////////////////////////////////////////////////////////////////// |
extern pascal SInt16 TradHigherDriverVersion(NumVersion *dv1, NumVersion *dv2) |
{ |
UInt16 nonRelRev1, nonRelRev2; |
if (dv1->majorRev > dv2->majorRev) return 1; |
if (dv1->majorRev < dv2->majorRev) return -1; |
if (dv1->minorAndBugRev > dv2->minorAndBugRev) return 1; |
if (dv1->minorAndBugRev < dv2->minorAndBugRev) return -1; |
if (dv1->stage > dv2->stage) return 1; |
if (dv1->stage < dv2->stage) return -1; |
nonRelRev1 = dv1->nonRelRev; |
nonRelRev2 = dv2->nonRelRev; |
if (dv1->stage == finalStage) { |
if (dv1->nonRelRev == 0) nonRelRev1 = 0xFFFF; |
if (dv2->nonRelRev == 0) nonRelRev2 = 0xFFFF; |
} |
if (nonRelRev1 > nonRelRev2) return 1; |
if (nonRelRev1 < nonRelRev2) return -1; |
return 0; |
} |
/////////////////////////////////////////////////////////////////////////// |
extern pascal UnitNumber TradHighestUnitNumber(void) |
// See comment in header file. |
{ |
return (MoreLMGetUnitTableEntryCount() - 1); |
} |
/////////////////////////////////////////////////////////////////////////// |
extern pascal Boolean TradDriverGestaltIsOn(DriverFlags flags) |
// See comment in header file. |
{ |
return ( (flags & kmDriverGestaltEnableMask) != 0 ); |
} |
/////////////////////////////////////////////////////////////////////////// |
static OSErr DriverGestaltOnOff(DriverRefNum refNum, Boolean setIt) |
// This routine is called by TradDriverGestaltOn and |
// TradDriverGestaltOff to either set or clear the |
// kmDriverGestaltEnableMask bit in the DCE flags. |
{ |
OSErr err; |
AuxDCEHandle thisDCE; |
// First called TradGetDriverInformation to validate the refNum |
// and verify that the driver exists. |
err = TradGetDriverInformation(refNum, nil, nil, nil, nil); |
if (err == noErr) { |
thisDCE = (AuxDCEHandle) GetDCtlEntry(refNum); |
if (setIt) { |
(**thisDCE).dCtlFlags |= kmDriverGestaltEnableMask; |
} else { |
(**thisDCE).dCtlFlags &= ~kmDriverGestaltEnableMask; |
} |
} |
return err; |
} |
/////////////////////////////////////////////////////////////////////////// |
extern pascal OSErr TradDriverGestaltOn(DriverRefNum refNum) |
// See comment in header file. |
{ |
return DriverGestaltOnOff(refNum, true); |
} |
/////////////////////////////////////////////////////////////////////////// |
extern pascal OSErr TradDriverGestaltOff(DriverRefNum refNum) |
// See comment in header file. |
{ |
return DriverGestaltOnOff(refNum, false); |
} |
/////////////////////////////////////////////////////////////////////////// |
extern pascal OSErr TradOpenInstalledDriver(DriverRefNum refNum, SInt8 ioPermission) |
// See comment in header file. |
{ |
OSErr err; |
Str255 driverName; |
DriverRefNum realRefNum; |
// Check parameters. |
err = noErr; |
if (ioPermission != fsRdWrPerm) { |
err = paramErr; |
} |
// Get the name of the driver, then simply open it. |
if (err == noErr) { |
err = TradGetDriverInformation(refNum, nil, nil, driverName, nil); |
} |
if (err == noErr) { |
if ( driverName[0] == 0 ) { |
err = paramErr; |
} |
} |
if (err == noErr) { |
err = OpenDriver(driverName, &realRefNum); |
} |
if (err == noErr) { |
if (realRefNum != refNum) { |
err = paramErr; // My favourite error code -- at some intrinsic level, every error is a paramErr (-; |
} |
} |
return err; |
} |
/////////////////////////////////////////////////////////////////////////// |
extern pascal OSErr TradLookupDrivers(UnitNumber beginningUnit, |
UnitNumber endingUnit, |
Boolean emptyUnits, |
ItemCount *returnedRefNums, |
DriverRefNum *refNums) |
// See comment in header file. |
{ |
OSErr err; |
AuxDCEHandle *unitTable; |
ItemCount maxRefNums; |
UnitNumber currentUnit; |
// Sanity check the parameters. |
if ( endingUnit > TradHighestUnitNumber() ) { |
endingUnit = TradHighestUnitNumber(); |
} |
err = noErr; |
if ( beginningUnit > TradHighestUnitNumber() ) { |
err = badUnitErr; |
} |
if (err == noErr) { |
if (beginningUnit > endingUnit ) { |
err = paramErr; |
} |
} |
// Now do the real work... |
if (err == noErr) { |
unitTable = (AuxDCEHandle *) LMGetUTableBase(); |
maxRefNums = *returnedRefNums; |
// Loop through each unit table entry from beginningUnit to endingUnit inclusive. |
*returnedRefNums = 0; |
currentUnit = beginningUnit; |
while ( currentUnit <= endingUnit ) { |
// If we've still got space to return a unit... |
if ( *returnedRefNums < maxRefNums ) { |
// and we're interested in this unit... |
if ( (emptyUnits && unitTable[currentUnit] == nil) || |
(!emptyUnits && unitTable[currentUnit] != nil) ) { |
// then copy the unit out to the caller's array |
refNums[*returnedRefNums] = ~currentUnit; |
*returnedRefNums += 1; |
} |
} |
currentUnit += 1; |
} |
} |
return err; |
} |
/////////////////////////////////////////////////////////////////////////// |
enum { |
kNoUnitNumber = 0xFFFF |
}; |
static UnitNumber IsDriverInstalled(ConstStr255Param name, UnitNumber skipThisUnit) |
// Look through the unit table to see if there is a driver with this name |
// already installed. Note that you might consider calling OpenDriver |
// here, but that would be wrong. OpenDriver has similar semantics, but |
// if it fails to find a driver in the unit table it will search the |
// current resource chain looking for a DRVR resource to install. |
// Given that it's likely our client has a DRVR resource in their |
// resource chain ('cause they're messing around trying to install |
// drivers), and that OpenDriver will install it without detaching |
// it from the client's resource file, and that the client's |
// resource file may go away (ie they're a DropMounter-like application |
// or some INIT running at system startup), this would be bad. |
{ |
UnitNumber endingUnit; |
UnitNumber unit; |
Str255 unitName; |
endingUnit = TradHighestUnitNumber(); |
for (unit = 0; unit <= endingUnit; unit++) { |
if ( TradGetDriverInformation(~unit, nil, nil, unitName, nil) == noErr) { |
if ( unit != skipThisUnit && EqualString(name, unitName, false, true) ) { |
return unit; |
} |
} |
} |
return kNoUnitNumber; |
} |
/////////////////////////////////////////////////////////////////////////// |
enum { |
kMaximumNumberOfUnitTableEntries = 1024, |
// kMaximumNumberOfUnitTableEntries = 8000, |
// kMaximumNumberOfUnitTableEntries is documented in Technote |
// DV 23 "Driver Education" <http://devworld.apple.com/dev/technotes/dv/dv_23.html> |
// as being the maximum size that the classic Device Manager |
// would grow the unit table. In theory, this limits the system |
// to 128 unit table entries. |
// However the traditional Mac OS is capable of dealing with much more |
// than 128 units. In fact, some multi-port serial card vendors |
// regularly install more. |
// Prior to Mac OS 8, the PCI DriverLoaderLib enforced the 128 limit. |
// I filed a bug against the PCI DriverLoaderLib to get this limit |
// raised, and the new limit under Mac OS 8 is 1024. |
// Given that official sanction, I have now raised the standard |
// limit enforced by this library to 1024. I supply an alternative |
// maximum (8000), designed to keep the unit table smaller than 32K. |
// This is important because many people use 68K word indexing |
// (ie x(a0,d0.w) to to access the entries. |
// I have tested TradDriverLoaderLib installing up to 500 device |
// drivers. |
kNumberOfEntriesToGrowUnitTable = 4 |
// Technote DV 23 "Driver Education" |
// <http://devworld.apple.com/dev/technotes/dv/dv_23.html> |
// documents that the system grows the unit table by 4 entries |
// at a time. |
}; |
/////////////////////////////////////////////////////////////////////////// |
static OSErr GrowUnitTable() |
// This routine grows the unit table by kNumberOfEntriesToGrowUnitTable, |
// up to a maximum of kMaximumNumberOfUnitTableEntries. The routine |
// is guaranteed to grow the table by at least one entry, or fail |
// with an error. |
{ |
OSErr err; |
Ptr oldTable; |
Ptr newTable; |
UInt32 oldCount; |
UInt32 newCount; |
// Get the info about the old table, and calculate the new table size. |
oldTable = LMGetUTableBase(); |
oldCount = MoreLMGetUnitTableEntryCount(); |
newCount = oldCount + kNumberOfEntriesToGrowUnitTable; |
// Guard against growing the table too big. |
err = noErr; |
if (newCount > kMaximumNumberOfUnitTableEntries) { |
err = unitTblFullErr; |
} |
// Allocate the new unit table in the system heap. Note that we |
// clear the newly allocated memory, so that later on, when we |
// use this memory as the new unit table, the newly allocated |
// entries will be empty. |
if (err == noErr) { |
newTable = NewPtrSysClear( newCount * sizeof(AuxDCEHandle)); |
err = MemError(); |
} |
// Copy the unit table entries over to the new table and then switch |
// to that table. Note that this sequence doesn't disable interrupts, |
// instead it relies on the fact that programs can't modify the |
// unit table at interrupt time, and thus we, running at non-interrupt |
// time, have exclusive write access to the table. |
// Note that the sequence of these next few lines is *very* important. |
// If we did this in any other order, you could get to a situation |
// where interrupt code might be looking at an inconsistent |
// unit table, which would be bad. |
// The sequence is: |
// 1. copy the old unit table entries to the new table |
// 2. change the unit table base pointer, so that interrupts |
// start using the new unit table |
// 3. then change the unit table count, so that we have |
// more entries available |
if (err == noErr) { |
BlockMoveData(oldTable, newTable, oldCount * sizeof(AuxDCEHandle)); // 1. |
LMSetUTableBase(newTable); // 2. |
MoreLMSetUnitTableEntryCount(newCount); // 3. |
// Now its safe to dispose of the old unit table. |
DisposePtr(oldTable); |
} |
return err; |
} |
/////////////////////////////////////////////////////////////////////////// |
static OSErr FindFreeUnitNumber(UnitNumber beginningUnit, |
UnitNumber endingUnit, |
UnitNumber *foundUnit) |
// This routine walks the unit table looking for a free |
// slot. The slot must be between beginningUnit |
// and endingUnit. If endingUnit is greater than |
// TradHighestUnitNumber(), then we're allowed |
// to grow the unit table to meet our needs. |
{ |
OSErr err; |
Boolean found; |
UnitNumber currentUnit; |
UnitNumber trueEndingUnit; |
AuxDCEHandle *unitTable; |
unitTable = (AuxDCEHandle *) LMGetUTableBase(); |
// Find trueEndingUnit, which is the minimum |
// of endingUnit and the highest unit number. |
trueEndingUnit = endingUnit; |
if ( trueEndingUnit > TradHighestUnitNumber() ) { |
trueEndingUnit = TradHighestUnitNumber(); |
} |
// Scan through the unit table, starting at beginningUnit |
// and ending at trueEndingUnit, looking for an |
// empty slot. |
currentUnit = beginningUnit; |
found = false; |
while (currentUnit <= trueEndingUnit && !found) { |
found = (unitTable[currentUnit] == nil); |
if (!found) { |
currentUnit += 1; |
} |
} |
// Finish up. |
if (found) { |
// We found an empty slot, return it. |
*foundUnit = currentUnit; |
err = noErr; |
} else { |
// We didn't find an empty slot. If we're |
// allowed to, grow the unit table, otherwise |
// just return an error. |
if (endingUnit > trueEndingUnit) { |
err = GrowUnitTable(); |
if (err == noErr) { |
*foundUnit = trueEndingUnit + 1; |
} |
} else { |
err = unitTblFullErr; |
} |
} |
return err; |
} |
/////////////////////////////////////////////////////////////////////////// |
extern pascal OSErr TradInstallDriverFromPtr(DRVRHeaderPtr driver, |
UnitNumber beginningUnit, |
UnitNumber endingUnit, |
DriverRefNum *refNum) |
// See comment in header file. |
{ |
OSErr err; |
UnitNumber foundUnit; |
AuxDCEHandle theDCE; |
// Sanity check parameters. |
err = noErr; |
if ( driver == nil ) { |
err = paramErr; |
} |
if ( beginningUnit > TradHighestUnitNumber() ) { |
err = badUnitErr; |
} |
if ( err == noErr && beginningUnit > endingUnit ) { |
err = paramErr; |
} |
// Check whether this driver is already installed. |
if ( err == noErr ) { |
// Check whether it's already installed. |
foundUnit = IsDriverInstalled(&driver->drvrName[0], kNoUnitNumber); |
if (foundUnit != kNoUnitNumber) { |
// Return the refnum of the existing driver to the caller. |
*refNum = ~foundUnit; |
err = dupFNErr; |
} |
} |
// Now walk the unit table looking for a free slot. |
if (err == noErr) { |
err = FindFreeUnitNumber(beginningUnit, endingUnit, &foundUnit); |
} |
// We've got a free slot, so let's install the device driver. |
// Note that we use DriverInstallReserveMem, rather than the standard |
// DriverInstall, so that the DCE is allocated low in the system |
// heap. DriverInstallReserveMem was introduced with the 128K ROM. |
if (err == noErr) { |
err = MoreDriverInstallReserveMem(driver, ~foundUnit); |
} |
// Now do some important tidying up. |
if (err == noErr) { |
// Return the refNum to the caller. |
*refNum = ~foundUnit; |
theDCE = (AuxDCEHandle) GetDCtlEntry(*refNum); |
// Now setup the DCE properly. There's a whole pile of things we |
// have to do, mainly because DriverInstall is such a brain-dead |
// routine. |
// First up, DriverInstall seems to ignore the first parameter |
// passed to it, so we have to blat the pointer to the driver code in |
// yourself afterwards. |
(**theDCE).dCtlDriver = (Ptr) driver; |
// Then we have to set up the flags. We do this by copying the flags |
// out of the first word of the driver code. We make sure to clear |
// the dRAMBased bit because we're actually a pointer-based driver |
// and DriverInstallReserveMem sets it to provisionally indicate that |
// we're a handle based driver. We also set dNeedLock because |
// we want the the Device Manager to lock down the DCE. |
(**theDCE).dCtlFlags = (driver->drvrFlags & ~dRAMBasedMask) | dNeedLockMask; |
// There's also a bunch of fields we copy straight across without |
// any modification. You might expect DriverInstall to copy |
// across these fields from the driver header to the DCE, but it doesn't |
// do that, so we do it ourselves. |
(**theDCE).dCtlDelay = driver->drvrDelay; |
(**theDCE).dCtlEMask = driver->drvrEMask; |
(**theDCE).dCtlMenu = driver->drvrMenu; |
// Finally, we lock the DCE. |
// Note that strictly speaking we don't need to HLock the DCE |
// because the Device Manager will do it when it you open a driver |
// that has dNeedLock set. However, we want to |
// lock it now because DriverInstallReserveMem has just made sure |
// that the DCE was created low in the system heap, so we might as |
// well lock it down low rather than let it float. |
HLock( (Handle) theDCE ); |
} |
return err; |
} |
/////////////////////////////////////////////////////////////////////////// |
extern pascal OSErr TradInstallDriverFromHandle(DRVRHeaderHandle driver, |
UnitNumber beginningUnit, |
UnitNumber endingUnit, |
DriverRefNum *refNum) |
// See comment in header file. |
{ |
OSErr err; |
Size driverSize; |
DRVRHeaderPtr driverPtr; |
driverPtr = nil; |
err = noErr; |
if (driver == nil || *driver == nil) { |
err = paramErr; |
} |
if (err == noErr) { |
driverSize = GetHandleSize( (Handle) driver ); |
err = MemError(); |
} |
if (err == noErr) { |
driverPtr = (DRVRHeaderPtr) NewPtrSys( driverSize ); |
err = MemError(); |
} |
if (err == noErr) { |
// This is *not* a BlockMoveData call. This time, we really are moving code! |
// I could have put cache flushing code in here, but then I would have |
// had to check whether it was available or not. |
BlockMove( *driver, driverPtr, driverSize ); |
err = TradInstallDriverFromPtr(driverPtr, beginningUnit, endingUnit, refNum); |
} |
// Clean up. |
if (err != noErr) { |
// We're returning an error. The API says we should leave the handle untouched, |
// but we should definitely clean up our new copy of the driver code. |
if (driverPtr != nil) { |
DisposePtr( (Ptr) driverPtr ); |
} |
} |
return err; |
} |
/////////////////////////////////////////////////////////////////////////// |
extern pascal OSErr TradInstallDriverFromResource(SInt16 rsrcID, StringPtr rsrcName, |
UnitNumber beginningUnit, |
UnitNumber endingUnit, |
DriverRefNum *refNum) |
// See comment in header file. |
{ |
OSStatus err; |
Handle driverHandle; |
// Note: We don't care which zone the resource gets loaded, because |
// TradInstallDriverFromHandle makes a copy of it anyway. |
// Get the resource, using either rsrcID or rsrcName. |
if (rsrcName == nil) { |
driverHandle = Get1Resource('DRVR', rsrcID); |
} else { |
driverHandle = Get1NamedResource('DRVR', rsrcName); |
} |
// Set err if we couldn't get the resource. |
if (driverHandle == nil) { |
err = ResError(); |
if (err == noErr) { |
err = resNotFound; |
} |
} else { |
// Make sure we're not killed by some clown making the 'DRVR' purgeable. |
HNoPurge(driverHandle); |
err = MemError(); |
} |
// Now install the driver as if we'd got it from a memory handle. |
if (err == noErr) { |
err = TradInstallDriverFromHandle( (DRVRHeaderHandle) driverHandle, beginningUnit, endingUnit, refNum); |
ReleaseResource(driverHandle); |
if (err == noErr) { |
err = ResError(); |
} |
} |
return err; |
} |
/////////////////////////////////////////////////////////////////////////// |
extern pascal OSErr TradGetDriverInformation(DriverRefNum refNum, |
UnitNumber *thisUnit, |
DriverFlags *flags, |
StringPtr name, |
DRVRHeaderPtr *driverHeader |
) |
// See comment in header file. |
{ |
OSErr err; |
UnitNumber tmpUnit; |
AuxDCEHandle tmpDCE; |
DRVRHeaderPtr tmpHeader; |
DRVRHeaderHandle tmpDriverHandle; |
// Get some initial information. |
tmpUnit = ~refNum; |
// Sanity check the refNum parameter. |
err = noErr; |
if (tmpUnit > TradHighestUnitNumber()) { |
err = badUnitErr; |
} |
if (err == noErr) { |
tmpDCE = (AuxDCEHandle) GetDCtlEntry(refNum); |
if ( tmpDCE == nil ) { |
err = unitEmptyErr; |
} |
} |
if (err == noErr) { |
if ( (*tmpDCE == nil) || (GetHandleSize( (Handle) tmpDCE) < sizeof(DCtlEntry)) ) { |
err = dceExtErr; |
} |
} |
// Get the information from the DCE. |
if (err == noErr) { |
// From the DCE, find the DRVR header. This can fail for a number of reasons: |
// 1. dCtlDriver is nil |
// 2. the driver is handle based, and the handle's master point is nil |
// 3. the driver is handle based, and the driver's handle is too small |
// In all of these cases, we set tmpHeader to nil, returning nil to our |
// client. |
tmpHeader = (DRVRHeaderPtr) (**tmpDCE).dCtlDriver; |
if ( tmpHeader != nil ) { |
if ( ((**tmpDCE).dCtlFlags & dRAMBasedMask) != 0 ) { |
tmpDriverHandle = (DRVRHeaderHandle) tmpHeader; |
if ( (*tmpDriverHandle != nil) && |
(GetHandleSize( (Handle) tmpDriverHandle) >= sizeof(DRVRHeader)) ) { |
tmpHeader = *tmpDriverHandle; |
} |
} |
} |
// Now copy out the various requested parameters |
if (thisUnit != nil) { |
*thisUnit = tmpUnit; |
} |
if (flags != nil) { |
*flags = (**tmpDCE).dCtlFlags; |
} |
if (name != nil) { |
if ( tmpHeader == nil ) { |
name[0] = 0; |
} else { |
BlockMoveData(&tmpHeader->drvrName[0], name, tmpHeader->drvrName[0] + 1); |
} |
} |
if (driverHeader != nil) { |
*driverHeader = tmpHeader; |
} |
} |
return err; |
} |
/////////////////////////////////////////////////////////////////////////// |
// Some extra dCtlFlags bits, as documented in: |
// <http://developer.apple.com/qa/dv/dv27.html> |
enum { |
dNativeDriverMask = 0x0008 |
}; |
/////////////////////////////////////////////////////////////////////////// |
extern pascal OSErr TradRemoveDriver(DriverRefNum refNum, Boolean immediate) |
// See comment in header file. |
{ |
OSErr err; |
DriverFlags flags; |
DRVRHeaderPtr driverHeader; |
// Check parameters. |
err = noErr; |
if (immediate) { |
err = paramErr; |
} |
// Get information about the driver we're closing. |
if (err == noErr) { |
err = TradGetDriverInformation(refNum, nil, &flags, nil, &driverHeader); |
} |
if (err == noErr) { |
if ( driverHeader == nil || ((flags & dNativeDriverMask) != 0)) { |
err = paramErr; |
} |
} |
// If the driver is open, close it. |
if (err == noErr) { |
if ( (flags & dOpenedMask) != 0 ) { |
err = CloseDriver(refNum); |
} |
} |
// Now call the system to remove the driver from the unit table. Note that this |
// works because of a subtlety in DriverRemove. If the driver being removed |
// is a RAM-based driver (which our drivers aren't), DriverRemove will call |
// ReleaseResource on the dCtlDriver. We don't want this, so we make our drivers |
// not RAM-based. |
if (err == noErr) { |
err = DriverRemove(refNum); |
} |
if (err == noErr) { |
// All is cool, so let's dispose of the code. |
DisposePtr( (Ptr) driverHeader); |
} |
return err; |
} |
/////////////////////////////////////////////////////////////////////////// |
extern pascal OSErr TradRenameDriver(DriverRefNum refNum, ConstStr255Param newDriverName) |
// See *important* comment in header file. |
{ |
OSErr err; |
Str255 driverName; |
DRVRHeaderPtr driverHeader; |
DriverFlags flags; |
err = noErr; |
if ( newDriverName[0] == 0 ) { |
err = paramErr; |
} |
if (err == noErr) { |
// Get information about the driver we're renaming. |
err = TradGetDriverInformation(refNum, nil, &flags, driverName, &driverHeader); |
} |
if (err == noErr) { |
if ( driverHeader == nil || ((flags & dNativeDriverMask) != 0)) { |
err = paramErr; |
} |
} |
// Now check the name lengths. See comment in implementation for details. |
if (err == noErr) { |
if ( newDriverName[0] > driverName[0] ) { |
err = paramErr; |
} |
} |
// Now check whether the new name is already present in the unit table. |
if (err == noErr) { |
if ( IsDriverInstalled(newDriverName, ~refNum) != kNoUnitNumber ) { |
err = dupFNErr; |
} |
} |
// Now copy in the new driver name. |
if (err == noErr) { |
BlockMoveData( newDriverName, &driverHeader->drvrName[0], newDriverName[0] + 1 ); |
} |
return err; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14