RamINIT.c

#define __DebugVersion  1
/*
**  Apple Macintosh Developer Technical Support
**
**  RAMInit.c: An INIT which installs DRVR for RamDisk
**
**  by Gordon Sheridan and Jim Luther
**  modified incessantly by Brian Bechtel
**  and even more so by Quinn "The Eskimo!"
**
**  File:       RamINIT.c
**
**  Copyright © 1992-1996 Apple Computer, Inc.
**  All rights reserved.
**
**  You may incorporate this sample code into your applications without
**  restriction, though the sample code has been provided "AS IS" and the
**  responsibility for its operation is 100% yours.  However, what you are
**  not permitted to do is to redistribute the source as "DTS Sample 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 Code, but that you've made changes.
**
**  Change History (most recent first):
**
**      <1.4>   19970207    Quinn   Reworked to use new file layout and bring generally
**                                  up-to-date.
**      <1.4>   19970207    Quinn   Rewrote to use TradDriverLoaderLib
**                                  Junked some unused prototypes
**                                  Renamed RemoveDrive to RemoveDriveFromQueue
**      <1.3>   08/23/96    BL¡B    Change dcePtr to dceHandle. Thanks to
**                                  Tim Fredenburg for pointing out the error.
**      <1.3>   06/20/96    BL¡B    Changes to new routine names
**      <1.2>   07/03/95    BL¡B    Many changes for Universal Header 2.0 
**                                  compliance.  Changed how we install driver.
**                                  Other bug fixes. See "Changes in 1.2" for
**                                  details.
**       <7>    06/10/94    BL¡B    Explicitly set zone to system zone before
**                                  getting the DRVR resource.  Symantec 7.0
**                                  doesn't set the System attribute on the DRVR
**                                  resource.  This can be hard to track down.
**       <6>    05/19/94    BL¡B    Modified debugger macros to avoid use of ANSI
**                                  routines, and weird c-string dependencies.
**       <5>    10/15/93    JML     Added code to resize unit table if needed.
**       <4>    10/14/93    JML     Implement AddDebuggerLabels routine for MacsBug.
**                                  Added Panic debug macros.
**       <3>     6/29/93    gs      Change AddDriveToQueue to start with drive number 5.
**       <2>     6/28/93    gs      Change _DebugVersion to __DebugVersion
**       <1>     6/13/93    gs      Implement AddDebuggerLabels routine for TMON.
**       <0>     4/17/92    gs      Clean up for Sample Code release.
**/
 
#ifdef __MWERKS__
#include <A4Stuff.h>
#endif
#ifdef THINK_C
#include <SetupA4.h>
#endif
 
#include <string.h>
#include <TextUtils.h>
#include <QuickDraw.h>
#include <Traps.h>
#include <Gestalt.h>
#include <Memory.h>
#include <Resources.h>
#include <DiskInit.h>
 
#include "RamDiskCommon.h"
#include "TradDriverLoaderLib.h"
#include "ShowInitIcon.h"
 
///////////////////////////////////////////////////////////////////////////
 
#define     kDriverName     "\p.RamDRVR"
 
enum {
    kMinUnitNum = 48        /* the lowest unit number we can use */
};
 
///////////////////////////////////////////////////////////////////////////
//
// Custom drive queue element.  Normally you might to extend this to have
// extra drive-specific variables, but in this case we have no extra
// variables.  Still, we define our own structure rather than use
// the one in <Files.h> because our structure has the magic "flags"
// field at the front.
 
struct MyDrvQEl
{
    long            flags;
    QElemPtr        qLink;
    short           qType;
    short           dQDrive;
    short           dQRefNum;
    short           dQFSID;
    unsigned short  dQDrvSz;
    unsigned short  dQDrvSz2;
};
typedef struct MyDrvQEl MyDrvQEl;
typedef MyDrvQEl *MyDrvQElPtr;
 
///////////////////////////////////////////////////////////////////////////
 
enum {
    // ICN#
    rLoadOKIcon     =   -4064,  /* same as cdev icon */
    rLoadBadIcon    =   -4033,
    rPhysicalIcon   =   -4034,
    rMediaIcon      =   -4035
};
 
///////////////////////////////////////////////////////////////////////////
 
static OSErr InitializeGlobals (DrvrGlobals *driverGlobals)
    // Initialize the globals using data read from resources.
{
    OSErr           result = -1;
    Handle          physicalIconHandle;
    Handle          mediaIconHandle;
    VersRecHndl     versionHandle;
    ConfigRecHandle configHandle;
    long            length;
 
    driverGlobals->ramDisk = nil;   /* RAM disk memory isn't allocated yet */
 
    /* Get the user configuration, icons, and the "where" string */
    configHandle = (ConfigRecHandle)Get1Resource ('RDcf', rConfiguration);
    physicalIconHandle = Get1Resource('ICN#', rPhysicalIcon);
    mediaIconHandle = Get1Resource('ICN#', rMediaIcon);
    
    versionHandle = (VersRecHndl) Get1Resource('vers', 1);
    
    if (configHandle && physicalIconHandle && mediaIconHandle && versionHandle)
    {
        /* See if we are supposed to install */
        if ((**configHandle).install)
        {           
            /* Copy the user's preferred volume name to the driver globals. */
            length = (**configHandle).volumeName[0]+1;
            BlockMoveData((**configHandle).volumeName, driverGlobals->volumeName, length);
            
            /* Copy ICN# to driver globals so it has a physical location icon for volumes. */
            BlockMoveData( &(**physicalIconHandle), (Ptr)(driverGlobals->physicalIcon), kLargeIconSize);
            
            /* Copy ICN# to driver globals so it has a default media icon for volumes. */
            BlockMoveData( &(**mediaIconHandle), (Ptr)(driverGlobals->mediaIcon), kLargeIconSize);
            
            /* Put the drive location string into the driver globals. */
            GetIndString(driverGlobals->locationStr, rStringList, strLocationStr);
            if (ResError() != noErr)
                BlockMoveData("\pIn RAM", driverGlobals->locationStr, 7);
                        
            driverGlobals->ramSize = (**configHandle).size * 1024;
            
            driverGlobals->driverVersion = (**versionHandle).numericVersion;
            
            result = noErr;
        }
    }
    
    if (configHandle)
        ReleaseResource((Handle)configHandle);
    else
        Panic("\pYou forgot to turn settings on in control panel");
    if (physicalIconHandle)
        ReleaseResource(physicalIconHandle);
    if (mediaIconHandle)
        ReleaseResource(mediaIconHandle);
    
    return (result);
}
 
///////////////////////////////////////////////////////////////////////////
 
static OSErr AddDriveToQueue (long size, short drvrRef, short *driveNum)
    // Find the first unused drive number greater than 4, allocate and initialize
    // a drive queue element (including the drive flags), and add the drive queue
    // element to the drive queue.
{
    OSErr       result = noErr;
    QHdrPtr     driveQHdr;
    DrvQEl      *drivePtr;
    MyDrvQElPtr newDrivePtr;
    Boolean     driveNumFound = false;
 
    
    driveQHdr = GetDrvQHdr();
    
    /* find first free drive number */
    *driveNum = 5;                                          /* drive numbers 1-4 are reserved */
    while (! driveNumFound)
    {
        drivePtr = (DrvQEl *)driveQHdr->qHead;              /* get first drive */
        while (drivePtr && *driveNum != drivePtr->dQDrive)  /* order of tests important! */
            drivePtr = (DrvQEl *)drivePtr->qLink;           /* get next drive */
        
        if (drivePtr == nil)
            driveNumFound = true;
        else
            ++(*driveNum);
    }
 
    if (*driveNum > 0)  /* must be a positive short */
    {
        /* allocate new drive queue element */
        newDrivePtr = (MyDrvQElPtr)NewPtrSysClear(sizeof(MyDrvQEl));            
        if (newDrivePtr != nil)
        {
            newDrivePtr->flags      = 0x00080000;           /* non-ejectable, disk not locked */
            newDrivePtr->qType      = 1;                    /* see IM vol.4 p.181 */
            newDrivePtr->dQDrive    = *driveNum;            /* ¥¥ dQDrive and dQRefNum are filled */
            newDrivePtr->dQRefNum   = drvrRef;              /* ¥¥ in by AddDrive */
            newDrivePtr->dQFSID     = 0;                    /* HFS */
            newDrivePtr->dQDrvSz    = size & 0x0000FFFF;    /* dQDrvSz  = LoWord of size */
            newDrivePtr->dQDrvSz2   = size >> 16;           /* dQDrvSz2 = HiWord of size */
            
            AddDrive (drvrRef, *driveNum, (DrvQEl *)&newDrivePtr->qLink);
        }
        else
            result = memFullErr;
    }
    else
        /* more than 32768 drives!?! */
        result = nsDrvErr;
    
    return (result);
}
 
///////////////////////////////////////////////////////////////////////////
 
static OSErr RemoveDriveFromQueue (short driveNum, DrvQElPtr *drivePtr)
    // Find the drive queue element for driveNum in the drive queue and Dequeue it.
    // Return pointer to the drive queue element removed in *drivePtr.
{
    QHdrPtr     driveQHdr;
 
    driveQHdr = GetDrvQHdr();
    *drivePtr = (DrvQEl *)driveQHdr->qHead;                 /* get first drive */
    while (*drivePtr && (driveNum != (*drivePtr)->dQDrive)) /* order of tests important! */
        *drivePtr = (DrvQEl *)(*drivePtr)->qLink;           /* get next drive */
    
    if (*drivePtr != nil)
        return (Dequeue((QElemPtr)*drivePtr, driveQHdr));
    else
        return (nsDrvErr);
}
 
///////////////////////////////////////////////////////////////////////////
 
static short NumToolboxTraps (void)
{
    if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap) )
        return (0x200);
    else
        return (0x400);
}
 
///////////////////////////////////////////////////////////////////////////
 
static TrapType GetTrapType (short theTrap)
{
    if (theTrap & 0x0800)
        return (ToolTrap);
    else
        return (OSTrap);
}
 
///////////////////////////////////////////////////////////////////////////
 
static Boolean TrapAvailable (short theTrap)
{
    TrapType    tType;
    
    tType = GetTrapType(theTrap);
    if (tType == ToolTrap)
    {
        theTrap = theTrap & 0x7FF;
        if (theTrap >= NumToolboxTraps() )
            theTrap = _Unimplemented;
    }
    
    return (NGetTrapAddress(theTrap, tType) !=
            NGetTrapAddress(_Unimplemented, ToolTrap) );
}
 
///////////////////////////////////////////////////////////////////////////
 
#define kMacsbugMacroStr    "\p;MC RamDisk 'DM #"
#define kMacsbugMacroStr2   "\p';g"
 
#define kTMONMacroStr       "\pªAddLabel RamDisk,."
#define kTMONMacroStr2      "\p,."
 
#define AddString(src, dst) {   BlockMoveData(&src[1], dst+i, src[0]); i += src[0]; }
 
static void AddDebuggerLabels (DrvrGlobals driverGlobals)
    // Add TMON or MacsBug debugger macros for easy viewing of RamDisk
{
    Str255      strbuf = "\p";
    Str31       numstr;
    long        tmonVal;
    short       err;
    Boolean     done = false;
    SignedByte  debugFlags;
    short       i;
 
    if (TrapAvailable(_Gestalt))    /* check for TMON */
    {
        err = Gestalt('TMON',&tmonVal);
        if (err == 0)
        {
            /* Add TMON label */
            i = 1;
            AddString(kTMONMacroStr, strbuf);
            NumToString((long)driverGlobals.ramDisk,numstr);
            AddString(numstr, strbuf);
            AddString(kTMONMacroStr2, strbuf);
            NumToString(driverGlobals.ramSize,numstr);
            AddString(numstr, strbuf);
            strbuf[0] = i - 1;
 
            DebugStr(strbuf);
            
            done = true;
        }
    }
    if (!done)  /* If TMON isn't installed, define a macro for MacsBug */
    {
        debugFlags = *(SignedByte *) 0x0BFF;        // MacJmpFlag
        if (debugFlags == -1)
            debugFlags = *(SignedByte *) 0x0120;    // MacJmp
        
        if (debugFlags & 0x20)
        {
            /* Define MacsBug macro */
            i = 1;
            
            AddString(kMacsbugMacroStr, strbuf);
            NumToString((long)driverGlobals.ramDisk, numstr);
            AddString(numstr, strbuf);
            AddString(kMacsbugMacroStr2, strbuf);
            strbuf[0] = i - 1;
            
            DebugStr(strbuf);
        }
    }
}
 
///////////////////////////////////////////////////////////////////////////
 
void    main (void)
{
    DrvrGlobals driverGlobals;
    short       csParam[11];
    short       drvrRefNum = 0;
    short       driveNum = 0;
    DrvQElPtr   driveQElPtr;
    OSErr       result;
    DCtlHandle  dceHandle;
    Boolean     driverInstalled = false;
 
    #ifdef THINK_C
        RememberA0();
        SetUpA4();
    #endif
    #ifdef __MWERKS__
        long oldA4 = SetCurrentA4();
    #endif
 
    Panic("\pStarting RAMDisk");
    ShowInitIcon(rLoadOKIcon,false);    /* show OK icon to indicate we're executing */
    
    result = InitializeGlobals(&driverGlobals);
    if (result != noErr)
    {
        Panic("\pCould not initialize globals");
        goto Done;
    }
 
    driverGlobals.ramDisk = NewPtrSysClear(driverGlobals.ramSize);
    if (driverGlobals.ramDisk == nil)
    {
        Panic("\pNewSysPtr = nil");
        result = memFullErr;
        goto Done;
    }
 
    #if     __DebugVersion
        AddDebuggerLabels(driverGlobals);
    #endif  __DebugVersion
    
    // <1.4>
    result = TradInstallDriverFromResource(0, kDriverName,
            kMinUnitNum,
            TradHighestUnitNumber() + 1,
            &drvrRefNum);
    if (result == noErr || result == dupFNErr) {
        driverInstalled = (result == noErr);
        result = TradOpenInstalledDriver(drvrRefNum, fsRdWrPerm);
    } else {
        Panic("\pTradInstallDriverFromResource failed!");
        goto Done;
    }
 
    if (result != noErr)
    {
        Panic("\pTradOpenInstalledDriver failed");
        goto Done;
    }
    
    /* put in drive queue */
    result = AddDriveToQueue (driverGlobals.ramSize / 512, drvrRefNum, &driveNum);
    if (result != noErr)
    {
        Panic("\pAddMyDrive returned negative driveNum!");
        goto Done;
    }
    /* driveNum now = drive number */
    
    /*  Save driveNum in our globals so driver code can verify drive number in
        Prime, Status and Control calls (if need be). The driver also checks 
        driveNum in the driver globals for a non-zero value before accepting
        regular prime, control and status calls.
    */
    driverGlobals.driveNumber = driveNum;
 
    /* set drivers globals */
    *(Ptr *)csParam = (Ptr)&driverGlobals;
    result = Control(drvrRefNum, setGlobalsCC, (Ptr)csParam);
    if (result != noErr)
    {
        Panic("\pControl returned err");
        goto Done;
    }
    
    /* check to see if it got them */
    result = Status(drvrRefNum, getGlobalsSC, (Ptr)csParam);
    if ((result != noErr) || (driverGlobals.ramSize != *(long *)csParam))
    {
        Panic("\pStatus returned err");
        goto Done;
    }
            
    /* zero & mount */
    result = DIZero(driveNum, driverGlobals.volumeName);
    if (result != noErr)
    {
        Panic("\pDIZero returned err");
        goto Done;
    }
    
Done:
    if (result == noErr)
        ShowInitIcon(rLoadOKIcon,true); /* draw OK icon and move pen */
    else
    {
        dceHandle = GetDCtlEntry(drvrRefNum);
        if (dceHandle != nil)
            if ((**dceHandle).dCtlFlags & dOpenedMask)
            {
                /* Driver is open - close it */
                result = CloseDriver(drvrRefNum);
                if (result == noErr)
                    /* If driver was closed, ramDisk memory was released! */
                    driverGlobals.ramDisk = nil;
            }
        
        if (driveNum != 0)
        {
            result = RemoveDriveFromQueue(driveNum, &driveQElPtr);  /* ignore errors */
            if (result == noErr)
                /* Dispose of the DrvQEl. Since it was allocated as a
                    MyDrvQEl record, we have to subract 4 from the address */
                DisposePtr((Ptr)((Ptr)(driveQElPtr) - 4));
        }
        
        // <1.4>
        if (driverInstalled) {
            (void) TradRemoveDriver(drvrRefNum, false);
        }
        
        if (driverGlobals.ramDisk != nil)
            DisposePtr(driverGlobals.ramDisk);
            
        #if     __DebugVersion
            /* ¥¥¥ Need to add routine to remove TMON/MacsBug debugging macros */
        #endif  __DebugVersion
            
        ShowInitIcon(rLoadBadIcon,true);            /* draw bad load icon and move pen */
    }
    #ifdef THINK_C
        RestoreA4();
    #endif
    #ifdef __MWERKS__
        SetA4(oldA4);
    #endif
}