home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Garbo
/
Garbo.cdr
/
mac
/
source
/
drvrnstl.sit
/
driver.c
next >
Wrap
Text File
|
1991-01-28
|
15KB
|
506 lines
/* driver.c */
/*
* The following code is to install and remove RAM drivers in the system
* heap. Written by Pete Resnick with the help of Joe Holt, Phil Shapiro,
* Tom Johnson, Michael A. Libes, Matthias Urlichs, John Norstad, and
* Charles Martin.
*
* Change Log
* ----------
* Date: Change: Who:
* ----- ------- ----
* 22 Jan 91 Changed "if (UTableSize < UnitNtryCnt) {" pr
* to "if (UTableSize > UnitNtryCnt) {" in
* DriverAvail.
*
* 28 Jan 91 Make sure resource map is updated to pr
* change back the resource ID's in
* InstallRAMDriver by adding code to
* SetResFileAttrs and UpdateResFile.
*/
#include <MacTypes.h>
#include <DeviceMgr.h>
#include <FileMgr.h>
#include <MemoryMgr.h>
#include <ResourceMgr.h>
#include "driver.h"
#define LOW_UNIT 48 /* First Unit Table Entry to use */
#define NEW_UNIT 64 /* Size of a "normal" Unit Table */
#define MAX_UNIT 128 /* Maximum size of a Unit Table */
#define UP_UNIT 4 /* Size to bounce up Unit Table */
#define nil 0L /* Just nil */
#define SCCLockOut 0x2600 /* Disable interrupts flag */
pascal OSErr DrvrInstall(Handle drvrHandle, short refNum)
{
asm {
MOVE.W refNum, D0 ; driver reference number
MOVEA.L drvrHandle, A0 ; handle to driver
MOVEA.L (A0), A0 ; pointer to driver
DC.W 0xA03D ; _DrvrInstall
; error is in DO (and is pushed on stack)
}
}
pascal OSErr DrvrRemove(short refNum)
{
asm {
MOVE.W refNum, D0 ; driver reference number
DC.W 0xA03E ; _DrvrRemove
; error is in DO (and is pushed on stack)
}
}
/*
* InstallRAMDriver will install the named driver into the system heap
* locked and return the driver reference number in refNum. Make sure
* that the DRVR resources are numbered between 0 and 47 and that the
* resources owned by the driver are also numbered appropriately. Using
* resource ID's of 48 and higher may cause conflicts if you are using
* the THINK C global data DATA resource or the THINK C multi-segment
* DCOD resource, which get temporarily renumbered by this routine.
* One major kludge: the ioMisc field of the ioParam block is not used
* for non-file operations, so it is used to store a pointer to a block
* containing the addresses of the DCOD resources (if they are being
* used) so that they can be removed by RemoveRAMDriver. If Apple ever
* uses that field, that mechanism will be gone. See driver.h for the
* drvrInstFlags.
*/
OSErr InstallRAMDriver(Str255 drvrName, short *refNum, Byte drvrInstFlags)
{
OSErr errCode;
short index, rsrcID, dcodRsrcID, unitNum, dcodSegments, dcodIndex;
ResType rsrcType;
Str255 rsrcName;
Handle dataHandle, drvrHandle, *dcodHList = nil, tempHandle;
ioParam openBlock;
register DriverPtr drvrPtr;
register DCtlPtr ctlEntryPtr;
/* Get the unit number for the driver */
errCode = DriverAvail(&unitNum);
if(errCode != noErr)
return(errCode);
/* The driver must load into the system heap locked. */
SetResLoad(false);
drvrHandle = Get1NamedResource('DRVR', drvrName);
SetResLoad(true);
if(drvrHandle == nil)
return(dInstErr);
SetResAttrs(drvrHandle, resSysHeap + resLocked);
if(ResErr != noErr) {
errCode = ResErr;
ReleaseResource(drvrHandle);
return(errCode);
}
/* Save the resource info for later use */
GetResInfo(drvrHandle, &rsrcID, &rsrcType, &rsrcName);
if(ResErr != noErr) {
errCode = ResErr;
ReleaseResource(drvrHandle);
return(errCode);
}
/* Now load it and detach it */
LoadResource(drvrHandle);
if(ResErr != noErr) {
errCode = ResErr;
ReleaseResource(drvrHandle);
return(errCode);
}
DetachResource(drvrHandle);
if(drvrInstFlags & thinkDATA) {
/*
* Make sure the DATA resource will load into the system heap,
* locked, and is owned by our driver, where the ID is the
* unitNum in bits 5 through 10 (or 11 if needed), zeros in
* bits 11 (if there is room) 12 and 13 (which means DRVR), zeros
* in bits 0 through 4 (since there is only 1 DATA resource), and
* ones in bits 14 and 15. If any errors occur after the resource
* ID is changed, it must be changed back!
*/
SetResLoad(false);
dataHandle = Get1Resource('DATA', (rsrcID << 5) | 0xC000);
SetResLoad(true);
if(dataHandle == nil) {
errCode = ResErr;
DisposHandle(drvrHandle);
return(errCode != noErr ? errCode : resNotFound);
}
SetResAttrs(dataHandle, resSysHeap + resLocked);
if(ResErr != noErr) {
errCode = ResErr;
DisposHandle(drvrHandle);
return(errCode);
}
SetResInfo(dataHandle, (unitNum << 5) | 0xC000, 0);
if(ResErr != noErr) {
errCode = ResErr;
DisposHandle(drvrHandle);
return(errCode);
}
}
if(drvrInstFlags & thinkMultSeg) {
/*
* Make sure the DCOD resources will load into the system heap,
* locked, and are owned by our driver, where the ID is the
* unitNum in bits 5 through 10 (or 11 if needed), zeros in
* bits 11 (if there is room) 12 and 13 (which means DRVR),
* the same ID in bits 0 through 4, and ones in bits 14 and 15.
* Keep a block containing the handles to those segments so that
* they can be thrown away later if needed. If any errors
* occur after the resource ID's are changed, they are changed
* back in ReleaseDrvrSegments.
*/
dcodSegments = 0;
errCode = noErr;
SetResLoad(false);
/* Count how many segments there are */
for(index = 1;
(index <= Count1Resources('DCOD')) && (errCode == noErr);
++index) {
tempHandle = Get1IndResource('DCOD', index);
if(tempHandle == nil)
errCode = (ResErr != noErr ? ResErr : resNotFound);
GetResInfo(tempHandle, &dcodRsrcID, &rsrcType, &rsrcName);
ReleaseResource(tempHandle);
if((dcodRsrcID & ~0xF01F) >> 5 == rsrcID)
++dcodSegments;
}
SetResLoad(true);
if(errCode != noErr) {
if(drvrInstFlags & thinkDATA) {
SetResInfo(dataHandle, (rsrcID << 5) | 0xC000, 0);
SetResFileAttrs(CurResFile(),
GetResFileAttrs(index) | mapChanged);
UpdateResFile(CurResFile());
ReleaseResource(dataHandle);
}
DisposHandle(drvrHandle);
return(errCode);
}
/* Get a block of memory to hold the handles */
dcodHList = (Handle *)NewPtrSysClear(sizeof(Handle) * dcodSegments);
if(dcodHList == nil) {
if(drvrInstFlags & thinkDATA) {
SetResInfo(dataHandle, (rsrcID << 5) | 0xC000, 0);
SetResFileAttrs(CurResFile(),
GetResFileAttrs(index) | mapChanged);
UpdateResFile(CurResFile());
ReleaseResource(dataHandle);
}
DisposHandle(drvrHandle);
return(memFullErr);
}
/* Get the resources and change the attributes and ID's */
dcodIndex = 0;
SetResLoad(false);
for(index = 1;
(index <= Count1Resources('DCOD')) && (errCode == noErr);
++index) {
tempHandle = Get1IndResource('DCOD', index);
if(tempHandle == nil) {
errCode = (ResErr != noErr ? ResErr : resNotFound);
} else {
GetResInfo(tempHandle, &dcodRsrcID, &rsrcType, &rsrcName);
if((dcodRsrcID & ~0xF01F) >> 5 == rsrcID) {
dcodHList[dcodIndex] = tempHandle;
SetResAttrs(dcodHList[dcodIndex],
resSysHeap + resLocked);
if(ResErr != noErr) {
errCode = ResErr;
} else {
SetResInfo(dcodHList[dcodIndex],
(dcodRsrcID & 0xF01F) + (unitNum << 5),
0);
errCode = ResErr;
}
++dcodIndex;
}
}
}
SetResLoad(true);
if(errCode != noErr) {
if(drvrInstFlags & thinkDATA) {
SetResInfo(dataHandle, (rsrcID << 5) | 0xC000, 0);
ReleaseResource(dataHandle);
}
ReleaseDrvrSegments(dcodHList, rsrcID, false);
DisposHandle(drvrHandle);
return(dInstErr);
}
}
/* Install with the refNum. The refNum is the -(unitNum + 1) */
errCode = DrvrInstall(drvrHandle, ~unitNum);
if(errCode != noErr) {
if(drvrInstFlags & thinkDATA) {
SetResInfo(dataHandle, (rsrcID << 5) | 0xC000, 0);
SetResFileAttrs(CurResFile(),
GetResFileAttrs(index) | mapChanged);
UpdateResFile(CurResFile());
ReleaseResource(dataHandle);
}
if(drvrInstFlags & thinkMultSeg)
ReleaseDrvrSegments(dcodHList, rsrcID, false);
DisposHandle(drvrHandle);
return(dInstErr);
}
/* Move the important information to the driver entry */
ctlEntryPtr = *UTableBase[unitNum];
drvrPtr = *(DriverHandle)drvrHandle;
ctlEntryPtr->dCtlDriver = (Ptr)drvrHandle;
ctlEntryPtr->dCtlFlags = drvrPtr->drvrFlags;
ctlEntryPtr->dCtlDelay = drvrPtr->drvrDelay;
ctlEntryPtr->dCtlEMask = drvrPtr->drvrEMask;
ctlEntryPtr->dCtlMenu = drvrPtr->drvrMenu;
ctlEntryPtr->dCtlFlags |= dRAMBased;
/* Hang onto the refNum just in case open changes it. */
index = CurResFile();
/*
* The open routine better load all the DATA and DCOD resources. If
* the driver is going to want to be closed, it should store what
* is passed to it in ioMisc and pass it back when close is called.
*/
if(drvrInstFlags & open) {
openBlock.ioCompletion = nil;
openBlock.ioNamePtr = drvrName;
openBlock.ioPermssn = fsCurPerm;
openBlock.ioMisc = (Ptr)dcodHList;
errCode = PBOpen(&openBlock, false);
}
/* Change CurResFile back to our original one. */
UseResFile(index);
if(drvrInstFlags & thinkDATA) {
/*
* If the open was successful, the dataHandle will be detached.
* If the open failed, dataHandle may or may not be an attached
* resource, but probably isn't attached. Therefore, the resource
* must be retrieved to change the resource ID back, just in case
* the open routine changed the file's resource map. After that,
* release it. Errors here will be horrific because the file will
* basically be corrupted if the ID can't be changed back, so
* don't bother checking. At this point in the game, errors
* shouldn't occur anyway.
*/
SetResLoad(false);
dataHandle = Get1Resource('DATA', (unitNum << 5) | 0xC000);
SetResLoad(true);
if(dataHandle != nil) {
SetResInfo(dataHandle, (rsrcID << 5) | 0xC000, 0);
SetResFileAttrs(CurResFile(),
GetResFileAttrs(index) | mapChanged);
UpdateResFile(CurResFile());
ReleaseResource(dataHandle);
}
}
/* If an error occurred during the open */
if(errCode != noErr) {
RemoveRAMDriver(~unitNum, false);
if(drvrInstFlags & thinkMultSeg)
ReleaseDrvrSegments(dcodHList, rsrcID, false);
} else {
ReleaseDrvrSegments(dcodHList, rsrcID, true);
*refNum = ~unitNum;
}
return(errCode);
}
/*
* Removes the driver installed in the system heap by InstallRAMDriver.
* See the warning on InstallRAMDriver about the ioMisc field.
*/
OSErr RemoveRAMDriver(short refNum, Boolean dcodRemove)
{
OSErr errCode = noErr, dcodHandles, index;
Handle dataHandle = nil, *dcodHList = nil, drvrHandle;
ioParam closeBlock;
/* If the driver is open, close it */
if((**UTableBase[~refNum]).dCtlFlags & dOpened) {
closeBlock.ioCompletion = nil;
closeBlock.ioRefNum = refNum;
errCode = PBClose(&closeBlock, false);
if(errCode != noErr)
return(errCode);
dcodHList = (Handle *)closeBlock.ioMisc;
dataHandle = (Handle)(**UTableBase[~refNum]).dCtlStorage;
}
/*
* Since the driver has been detached, it will have to be disposed of
* later since DrvrRemove just does a ReleaseResource on the handle.
*/
drvrHandle = (Handle)(**UTableBase[~refNum]).dCtlDriver;
errCode = DrvrRemove(refNum);
if(errCode != noErr)
return(errCode);
if(drvrHandle != nil) {
DisposHandle(drvrHandle);
if(dcodRemove && (dcodHList != nil)) {
/* The driver has passed back the handles to its segments so
* they can be disposed of.
*/
dcodHandles = GetPtrSize(&dcodHList) / sizeof(Handle);
for(index = 0; index < dcodHandles; ++index)
DisposHandle(dcodHList[index]);
DisposPtr(dcodHList);
}
}
return(noErr);
}
short GetDrvrRefNum(Str255 drvrName)
{
short unitNum;
DCtlHandle curDCtlEntry;
DriverPtr curDrvrPtr;
/* Walk through the Unit Table */
for(unitNum = 0; unitNum < UnitNtryCnt; ++unitNum) {
curDCtlEntry = GetDCtlEntry(~unitNum);
if(curDCtlEntry != nil) {
/* If this is a RAM driver, it's a handle. ROM is a pointer */
if((**curDCtlEntry).dCtlFlags & dRAMBased)
curDrvrPtr = *(DriverHandle)(**curDCtlEntry).dCtlDriver;
else
curDrvrPtr = (DriverPtr)(**curDCtlEntry).dCtlDriver;
/* Does the driver name match? */
if(curDrvrPtr != nil)
if(EqualString(drvrName, curDrvrPtr->drvrName,
false, false))
return(~unitNum);
}
}
return(0);
}
OSErr GrowUTable(short newEntries)
{
DCtlHandle *newUTableBase;
/* Make room for the new Unit Table */
newUTableBase = (DCtlHandle *)NewPtrSysClear((UnitNtryCnt + newEntries)
* sizeof(DCtlHandle));
if(MemError() != noErr)
return(MemError());
asm {
MOVE SR,-(SP) ; Save status register
MOVE #SCCLockOut,SR ; Disable interrupts
}
/* Move the old Unit Table to the new Unit Table */
BlockMove(UTableBase, newUTableBase, UnitNtryCnt * sizeof(DCtlHandle));
DisposPtr(UTableBase);
UTableBase = newUTableBase;
UnitNtryCnt += newEntries;
asm {
MOVE (SP)+,SR ; Restore status register
}
return(noErr);
}
OSErr DriverAvail(short *unitNum)
{
short unitIndex;
Size UTableSize;
OSErr errCode = noErr;
*unitNum = 0;
/* Increase Unit Table size for Mac Plus */
if(UnitNtryCnt <= LOW_UNIT)
errCode = GrowUTable(NEW_UNIT - UnitNtryCnt);
if(errCode != noErr)
return(errCode);
/* Look for an empty slot in what's already there */
for(unitIndex = LOW_UNIT;
(unitIndex < UnitNtryCnt) && (*unitNum == 0);
++unitIndex)
if(UTableBase[unitIndex] == nil)
*unitNum = unitIndex;
/* Unit Table full up to UnitNtryCnt, so increase it */
if(*unitNum == 0) {
UTableSize = GetPtrSize(UTableBase) / sizeof(DCtlHandle);
/* If there is space in the Unit Table, just up the count */
if(UTableSize > UnitNtryCnt) {
*unitNum = UnitNtryCnt;
UnitNtryCnt += (UTableSize - UnitNtryCnt < UP_UNIT
? UTableSize - UnitNtryCnt
: UP_UNIT);
/* If there isn't enough space, try to increase it */
} else {
if(MAX_UNIT - UnitNtryCnt != 0) {
unitIndex = UnitNtryCnt;
errCode = GrowUTable(MAX_UNIT - UnitNtryCnt < UP_UNIT
? MAX_UNIT - UnitNtryCnt
: UP_UNIT);
if(errCode != noErr)
return(errCode);
*unitNum = unitIndex;
}
}
}
if(*unitNum == 0)
return(unitTblFullErr);
else
return(noErr);
}
void ReleaseDrvrSegments(Handle *dcodHList, short rsrcID, Boolean detach)
{
short index, dcodHandles, dcodRsrcID;
long rsrcType;
Str255 rsrcName;
dcodHandles = GetPtrSize(&dcodHList) / sizeof(Handle);
if(rsrcID != 0) {
for(index = 0; index < dcodHandles; ++index)
if(dcodHList[index]) {
GetResInfo(dcodHList[index], &dcodRsrcID,
&rsrcType, &rsrcName);
SetResInfo(dcodHList[index],
(dcodRsrcID & 0xF01F) + (rsrcID << 5),
0);
}
SetResFileAttrs(CurResFile(), GetResFileAttrs(index) | mapChanged);
UpdateResFile(CurResFile());
}
for(index = 0; index < dcodHandles; ++index)
if(dcodHList[index]) {
if(detach)
DetachResource(dcodHList[index]);
else
ReleaseResource(dcodHList[index]);
}
if(!detach)
DisposPtr(dcodHList);
}