RamDRVR.c

#define __DebugVersion  1
/*
**  Apple Macintosh Developer Technical Support
**
**  RamDRVR.c: Driver for RamDisk Sample
**
**  by Gordon Sheridan and Jim Luther
**  modified incessantly by Brian Bechtel
**  and even more so by Quinn "The Eskimo!"
**
**  File:       RamDRVR.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.3>   06/20/96    BL¡B    Added DriverGestalt, no OLDROUTINENAMES
**                                  detailed comment on driveInfoCC call.
**       <6>    06/10/94    BL¡B    Converted to work with Symantec as well.
**       <5>    05/19/94    BL¡B    Converted to work with Metrowerks.  Added 
**                                  conditionally compiled main routine for Metrowerks.
**       <4>    10/23/93    JML     Added check for valid globals before accepting
**                                  Prime, Control, or Status calls (except the control
**                                  and status routines that get and set the globals).
**       <3>    10/14/93    JML     Added support for the following Control and Status
**                                  calls: physicalIconCC, mediaIconCC, driveInfoCC,
**                                  formatListSC.  driveStatusSC now returns a copy
**                                  of our DrvQEl.  Prime, Control and Status now checks
**                                  that ioVRefNum = our drive number.  Prime now updates
**                                  dCtlPosition so I/O using fsFromMark will work. All
**                                  debugging code now hidden with "Panic" macros. Prime
**                                  now checks for block aligned I/O (since this is a
**                                  block device).  All calls now return appropriate error
**                                  results.
**      <2+>     7/26/93    gs      Set result = noErr for Status csCode = 8.
**       <2>     6/29/93    gs      Return Drive Stats, miscellaneous clean up.
**       <1>     6/13/93    gs      Allocate space for disk from driver. Clean up comments.
**       <0>     1/17/90    gs      11:58 PM, first version.
**/
 
#ifdef __MWERKS__
#include <A4Stuff.h>
#endif
#ifdef THINK_C
#include <SetupA4.h>
#endif
 
#include <DriverGestalt.h>
#include <Disks.h>
 
#include "RamDiskCommon.h"
 
///////////////////////////////////////////////////////////////////////////
 
// These should be in <DriverGestalt.h>.
 
enum {
    kdgFlush    = 'flus'                /* Determine if disk driver supports flush and if it needs a flush */
};
 
struct DriverGestaltFlushResponse {
    Boolean     canFlush;               /* Return true if driver supports the */
                                        /* kdcFlush Driver Configure _Control call */
    Boolean     needsFlush;             /* Return true if driver/device has data cached */
                                        /* and needs to be flushed when the disk volume */
                                        /* is flushed by the File Manager */
    UInt8       pad[2];
};
typedef struct DriverGestaltFlushResponse DriverGestaltFlushResponse;
 
enum {
    kdcFlush    = 'flus'                /* Tell a disk driver to flush its cache and any hardware caches */
};
 
#define GetDriverGestaltFlushResponse(p) ((DriverGestaltFlushResponse *)(&((p)->driverGestaltResponse)))
 
///////////////////////////////////////////////////////////////////////////
 
// These definitely should be a system header file somewhere!
 
enum {
    // Common control codes
    killIOCC            = 1,        /* kill I/O */
    verifyDiskCC        = 5,        /* verify disk */
    formatDiskCC        = 6,        /* format disk */
    ejectDiskCC         = 7,        /* eject disk (ejectable media only) */
    setTagBufferCC      = 8,        /* set tag buffer (.SONY) */
    trackCacheCC        = 9,        /* control track cache (.SONY) */
    physicalIconCC      = 21,       /* return physical location icon and where string */
    mediaIconCC         = 22,       /* return media icon */
    driveInfoCC         = 23,       /* return drive info */
    trackDumpCC         = 18244,    /* diagnostic raw track dump (.SONY) */
 
    // Common status codes
    returnFormatList    = 6,        /* return format list (.SONY) */
    driveStatusSC       = 8         /* drive status */
};
 
///////////////////////////////////////////////////////////////////////////
 
struct DriverConfigParam {
    QElemPtr    qLink;
    short       qType;
    short       ioTrap;
    Ptr         ioCmdAddr;
    ProcPtr     ioCompletion;
    OSErr       ioResult;
    StringPtr   ioNamePtr;
    short       ioVRefNum;
    short       ioCRefNum;                  /* refNum for I/O operation */
    short       csCode;                     /* == kDriverConfigureCode */
    OSType      driverConfigureSelector;
    UInt32      driverConfigureParameter;
};
typedef struct DriverConfigParam DriverConfigParam;
 
///////////////////////////////////////////////////////////////////////////
 
// Prototypes for main driver entry points.
 
extern pascal OSErr DRVROpen(ParmBlkPtr paramBlock, DCtlPtr devCtlEnt);
extern pascal OSErr DRVRPrime(ParmBlkPtr paramBlock, DCtlPtr devCtlEnt);
extern pascal OSErr DRVRControl(ParmBlkPtr paramBlock, DCtlPtr devCtlEnt);
extern pascal OSErr DRVRStatus(ParmBlkPtr paramBlock, DCtlPtr devCtlEnt);
extern pascal OSErr DRVRClose(ParmBlkPtr paramBlock, DCtlPtr devCtlEnt);
 
///////////////////////////////////////////////////////////////////////////
 
pascal OSErr DRVROpen (ParmBlkPtr pb, DCtlPtr dce)
{
    DrvrGlobals    *globe;
    OSErr           result = noErr;
 
#pragma unused (pb)
 
    if (!dce->dCtlStorage)  /* is driver already open ? */
    {
        dce->dCtlStorage = (Handle)NewPtrSysClear(sizeof(DrvrGlobals));
        if (!(Ptr)dce->dCtlStorage)
        {
            result = MemError();
            Panic("\pDRVROpen:NewPtr returned nil");
        }
            
        globe = (DrvrGlobals *)dce->dCtlStorage;
        globe->driveNeedsFlush = false;
    }
    else
        Panic("\pDRVROpen:2nd open attempt");
    
    return (result);
}
 
/*****************************************************************************/
 
pascal OSErr DRVRPrime (ParmBlkPtr pb, DCtlPtr dce)
{
    DrvrGlobals     *globe;
    unsigned long   position;
    unsigned long   count;
    short           calltype;
    OSErr           result;
 
    if (dce->dCtlStorage)
    {
        globe = (DrvrGlobals *)dce->dCtlStorage;
        
        if (globe->driveNumber != 0) /* accept no calls, before the globals are initialized */
        {
            position = dce->dCtlPosition;
            count = pb->ioParam.ioReqCount;
            
            /* Preflight the request for block alignment, size, and range */
            if (((position % 512) == 0) &&
                ((count % 512) == 0) &&
                ((count + position) <= globe->ramSize))
            {
                calltype = 0x00FF & pb->ioParam.ioTrap;
                switch(calltype)
                {
                    case aRdCmd:
                        /* Read the data */
                        BlockMoveData(&globe->ramDisk[position], pb->ioParam.ioBuffer, count);
                        break;
                    case aWrCmd:
                        /* Write the data */
                        BlockMoveData(pb->ioParam.ioBuffer, &globe->ramDisk[position], count);
                        globe->driveNeedsFlush = true;
                        break;
                    default:        
                        Panic("\pDRVRPrime: Call wasn't _Read or _Write");
                        break;
                }
                
                dce->dCtlPosition += count;     /* Update the position */
                pb->ioParam.ioActCount = count; /* Return the actual count */
                
                result = noErr;
            }
            else
            {
                Panic("\pDRVRPrime: Invalid block request");
                result = paramErr;
            }
        }
        else
        {
            Panic("\pDRVRPrime: Globals aren't initialized");
            result = nsDrvErr;
        }
    }
    else
    {
        Panic("\pDRVRPrime: No dCtlStorage");
        result = notOpenErr;
    }
    
    return (result);
}
 
/*****************************************************************************/
 
pascal OSErr DRVRControl (ParmBlkPtr pb, DCtlPtr dce)
{
    DrvrGlobals    *globe;
    OSErr           result = controlErr;
    long            size;
    long            i;
    
    if (dce->dCtlStorage)
    {
        globe = (DrvrGlobals *)dce->dCtlStorage;
        
        /* Accept only setGlobalsCC call, before the globals are initialized */
        if (pb->cntrlParam.csCode == setGlobalsCC)
        {
            /* Initialize DrvrGlobals */
            BlockMoveData( *(Ptr *)pb->cntrlParam.csParam,(Ptr)globe, sizeof(DrvrGlobals));
            result = noErr;
        }
        else if (globe->driveNumber != 0)
        {
            switch(pb->cntrlParam.csCode)
            {
                case killIOCC:
                    /* What's there to kill?  A BlockMove? sure...  */
                    /* We call Panic in this example; a real driver */
                    /* would do something more reasonable.          */
                    Panic("\pDRVRControl: KillIO on Ram Disk?");
                    break;
                
                case verifyDiskCC:
                    result = noErr;
                    break;
                
                case formatDiskCC:
                    /* zero out ram disk memory */
                    size = globe->ramSize / 4;
                    for (i = 0 ; i < size; i++)
                        ((long *)globe->ramDisk)[i] = 0;
                    result = noErr;
                    break;
                        
                case ejectDiskCC:
                    // From Martin Minow's SCSI disk sample:
                    // Certain old SFGetFile calls (System 4.1?) will eject
                    // disks which are marked as non-ejectable (such as this one)
                    // If that happens, we need to issue a disk insert event
                    // to remount the disk
                    PostEvent(diskEvt, globe->driveNumber);
                    break;
                            
                case physicalIconCC:
                    /* return pointer to icon and where string */
                    *(Ptr *)pb->cntrlParam.csParam = (Ptr)globe->physicalIcon;
                    result = noErr;
                    break;
                
                case mediaIconCC:
                    /* return pointer to icon and where string */
                    *(Ptr *)pb->cntrlParam.csParam = (Ptr)globe->mediaIcon;
                    result = noErr;
                    break;
    
// When a HFS volume is mounted, the File Manager calls the disk driver
// with a "Return Drive Info" _Control call (csCode=23). Then if there
// are no errors, it looks at the low-byte (bits 0-7) of csParam to see
// if the drive type is ramDiskType (16, $10) or romDiskType (17, $11)
// and if so, vcbAtDontCache is set in vcbAtrb.
// 
// You shouldn't normally have to mess with the vcbAtDontCache bit in the
// vcbAtrb. If you've written a RAM or ROM disk and you want the cache to
// be bypassed, you only need to support _Control csCode 23 and say
// you're a RAM or ROM disk. Other disk drivers probably should not mess
// with the vcbAtDontCache bit because any improvements we make to the
// File Manager cache will be lost on those drives (and we'll have to say
// so when customers ask why our improvements didn't help their drives).
//
// See the Inside Macintosh:Files Errata technote for a discussion of this.
                case driveInfoCC:
                    /*  high word (bytes 2 & 3) clear  */
                    /*  byte 1 = primary + fixed media + internal  */
                    /*  byte 0 = drive type (0x10 = RAM disk)  */
                    *(unsigned long *)pb->cntrlParam.csParam = 0x00000410;
                    result = noErr;
                    break;
                
                case 24:    /* ¥¥¥ Return SCSI csCode Partition Size */
                    *(unsigned long *)pb->cntrlParam.csParam = globe->ramSize >> 9;
                    result = noErr;
                    break;
    
                case accRun:
                    result = noErr;
                    break;
                
                case kDriverConfigureCode:
                    switch ( ((DriverConfigParam *)pb)->driverConfigureSelector )
                    {
                        case kdcFlush:
                            globe->driveNeedsFlush = false;
                            result = noErr;
                            break;
                        
                        default:
                            break;
                    }
                    break;
                
                default:
                    Panic("\pUnrecognized control call");
                    break;
            }
        }
        else
        {
            Panic("\pDRVRControl: Globals not initialized");
        }
    }
    else
    {
        Panic("\pDRVRControl: No dCtlStorage");
        result = notOpenErr;
    }
        
    return (result);
}
 
/*****************************************************************************/
 
pascal OSErr DRVRStatus (ParmBlkPtr pb, DCtlPtr dce)
{
    DrvrGlobals         *globe;
    OSErr               result = statusErr;
    DrvSts              *driveStats;
    DrvQElPtr           driveQEl;
    DriverGestaltSyncResponse syncResponse;
    NumVersion          versResponse;
    
    if (dce->dCtlStorage)
    {
        globe = (DrvrGlobals *)dce->dCtlStorage;
    
        /* Accept only getGlobalsSC call, before the globals are initialized */
        if (pb->cntrlParam.csCode == getGlobalsSC)
        {
            *(long *)pb->cntrlParam.csParam = globe->ramSize;
            if (globe->ramSize == 0)
            {
                Panic("\pDRVRStatus: ramSize is zero");
            }
            result = noErr;
        }
        else if (globe->driveNumber != 0)
        {
            switch(pb->cntrlParam.csCode)
            {
                case driveStatusSC:
                    if (pb->cntrlParam.ioVRefNum != globe->driveNumber)
                        break;
                    
                    /* Drive Stats... */
                    driveStats = (DrvSts *)pb->cntrlParam.csParam;
                    driveStats->track       = 0;    /* not applicable */
                    driveStats->writeProt   = 0;    /* write enabled */
                    driveStats->diskInPlace = 0x08; /* non-ejectable */
                    driveStats->installed   = 1;    /* drive installed */
                    driveStats->sides       = 0;    /* not applicable */
                    driveStats->twoSideFmt  = 0;    /* not applicable */
                    driveStats->needsFlush  = 0;    /* not applicable */
                    driveStats->diskErrs    = 0;    /* not applicable */
                    
                    /* Copy qLink through dQFSID from our DrvQEl */
                    driveQEl = (DrvQElPtr)(GetDrvQHdr()->qHead);
                    while(driveQEl != nil)
                    {
                        if (driveQEl->dQDrive == globe->driveNumber)
                        {
                            driveStats->qLink = driveQEl->qLink;
                            driveStats->qType = driveQEl->qType;
                            driveStats->dQDrive = driveQEl->dQDrive;
                            driveStats->dQRefNum = driveQEl->dQRefNum;
                            driveStats->dQFSID = driveQEl->dQFSID;
    
                            break; /* while(driveQEl != nil) */
                        }
                        driveQEl = (DrvQElPtr)(driveQEl->qLink);
                    }
                    
                    result = noErr;
                    break;
                
                case kDriverGestaltCode:
                    /*  We only support some information calls.  A real driver */
                    /*  would support more of these calls, as defined in the */
                    /*  Designing PCI Cards & Drivers document */
                    switch ( ((DriverGestaltParam *)pb)->driverGestaltSelector )
                    {
                        case kdgSync:
                            syncResponse.behavesSynchronously = true;   
                            ((DriverGestaltParam *)pb)->driverGestaltResponse = 
                                        *(unsigned long *)&syncResponse;
                            result = noErr;
                            break;
                                                    
                        case kdgFlush:
                            {
                                GetDriverGestaltFlushResponse((DriverGestaltParam *)pb)->canFlush = true;
                                GetDriverGestaltFlushResponse((DriverGestaltParam *)pb)->needsFlush = globe->driveNeedsFlush;   
                                result = noErr;
                            }
                            break;
                                                    
                        case kdgVersion:
                            versResponse = globe->driverVersion;
                            
                            ((DriverGestaltParam *)pb)->driverGestaltResponse = 
                                        *(unsigned long *)&versResponse;
                            result = noErr;
                            break;
                        default:
                            break;
                        }
                    break;
                        
                default:
                    break;
            }
        }
        else
        {
            Panic("\pDRVRStatus: Globals not initialized");
        }
    }
    else
    {
        Panic("\pDRVRStatus: No dCtlStorage");
        result = notOpenErr;
    }
 
    return (result);
}
 
/*****************************************************************************/
 
pascal OSErr DRVRClose (ParmBlkPtr pb, DCtlPtr dce)
{
    DrvrGlobals *globe;
 
#pragma unused (pb)
        
    if (dce->dCtlStorage)
    {
        globe = (DrvrGlobals *)dce->dCtlStorage;
        
        if (globe->ramDisk)
            DisposePtr(globe->ramDisk);
        
        DisposePtr((Ptr)dce->dCtlStorage);
    }
    
    return (noErr);
}