RAMDiskDriverMain.c

/*
    File:       RAMDiskDriverMain.c
 
    Contains:   Driver glue modified for use with CodeWarrior custom header.
 
    Written by: Quinn "The Eskimo!"
 
    Copyright:  © 1996 by Apple Computer, Inc., all rights reserved.
 
    Change History (most recent first):
 
    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 "DSC 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):
 
         <5>    970307  Quinn       Tidied up CodeWarrior driver header,
                                    as per Chad Magendanz's suggestions.
         <4>    970206  Quinn       Ported to CodeWarrior, which required
                                    syntactic changes everywhere and some
                                    changes to how the entry point offsets
                                    are calculated.  Also added A4 save, setup
                                    and restore.
         <3>    941027  BL¡B        Incomplete async routines should return noErr .
         <2>    960803  BL¡B        Moved to Universal Headers (post ETO 18).
         <1>     10/94  BL¡B        Corrected asynch not-complete return case
                                    to put 0 in D0.
         <0>     10/93  gs and JML  Clean up for Sample Code release.
 
*/
 
#include <A4Stuff.h>
#include <Devices.h>
#include <DriverGestalt.h>
 
/* This file is necessary because the standard Metrowerks driver header
    has a number of problems:
        
    o   It assumes that the driver entry point is using C calling
        conventions.  As you can't declare C calling convention routines
        in Pascal, I'm stuck with doing some sort of C glue.  Given
        the other problems, I thought might as well do the whole enchilada.
        
    o It does not handle _KillIO processing.
    
    o It doesn't set ioResult for immediate calls.
        
   This code is based on the "Driver.a" used for the long standing
    DTS RAMDisk sample, modified for operation in the CodeWarrior
    environment.
*/
 
// External declarations for the Pascal routines.
 
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);
 
// The Universal headers no longer define simple constants for low-memory
//  globals, so we have to define our own.
 
enum {
    JIODone = 0x8FC
};
 
// Constants for VMImmune bit in dCtlFlags.
 
enum {
    dVMImmuneBit = 0,
    dVMImmuneMask = 0x0001
};
        
/*  If dVMImmuneBit is set to true the VM system does not hold down
    parameter blocks (or the data that read/write parameter blocks point to)
    passed to this driver.
*/
 
/*  Driver Header
 
    The code is based on "Driver.a" from the RAMDisk sample.
    Unfortunately I had to make a number of changes.  Some of them are purely
    syntactic, using 0x instead of $ for example, but others are more substantial.
    CodeWarrior's assembler doesn't let you go "dc.b Label1 - Label2", so
    the entry points in the driver header are now calculated using the label
    and a static offset.
 
    My second change was to save, setup and restore A4 in the code.
*/
 
asm void __Startup__(void);
asm void __Startup__(void)
{
DHeader:
 
DFlags:         dc.w    dReadEnableMask     \
                        + dWritEnableMask   \
                        + dCtlEnableMask    \
                        + dStatEnableMask   \
                        + dNeedLockMask     \
                        + dVMImmuneMask     \
                        + kmDriverGestaltEnableMask // <5> Driver flags
 
DDelay:         dc.w    0                           // none
DEMask:         dc.w    0                           // DA event mask
DMenu:          dc.w    0                           // no menu
 
                dc.w    DOpen + 8                   // <4> <5> offset to Open
                dc.w    DPrime + 10                 // <4> <5> offset to Prime
                dc.w    DControl + 12               // <4> <5> offset to Control
                dc.w    DStatus + 14                // <4> <5> offset to Status
                dc.w    DClose + 16                 // <4> <5> offset to Close
 
Name:           dc.b    "\p.RamDRVR"                // <4> <5> name of driver
                
DOpen:          pea     DRVROpen
                bra.s   DRVRDispatch
                                
DPrime:         pea     DRVRPrime
                bra.s   DRVRDispatch
                                
DControl:       pea     DRVRControl
                bra.s   DRVRDispatch
 
DStatus:        pea     DRVRStatus
                bra.s   DRVRDispatch
 
DClose:         pea     DRVRClose                   // and fall thru to DRVRDispatch
 
DRVRDispatch:
                movem.l a0/a1/a4, -(sp)             // <4> save registers (for IODone)
                                
        // Push parameters for driver routines with Pascal calling conventions.
 
                clr.w   -(sp)                       // save room for result
                move.l  a0, -(sp)                   // push paramblock ptr on stack
                move.l  a1, -(sp)                   // push dce ptr on stack
 
        // Now setup A4 -- have to do this after above because it messes up a0.
        
                jsr     SetCurrentA4                // <4> establish CodeWarrior globals
        
        // Now call the driver routine.
 
                movea.l 0x16(sp), a0                // load address of driver routine into a0
                jsr     (a0)                        // go to it!
                                
                move.w  (sp)+, d0                   // put result into d0                               
                movem.l (sp)+, a0/a1/a4             // <4> restore registers (for IODone)
                addq.l  #4, a7                      // clear driver routine address off stack
 
// Check for special case exits (Open, Close, KillIO, Immediate, incomplete Async)
// Open, Close and KillIO always RTS to the Device Manager with the driver result
// in 
 
                move.w  struct(IOParam.ioTrap)(a0), d1  // copy low byte of trap word to d1
 
        // _Open check
                tst.b       d1                      // _Open == A000
                beq.s       Done                    // rts if _Open
 
        // _Close check
                cmpi.b  #1, d1                      // _Close == A001
                beq.s       Done                    // rts if _Close
 
        // _KillIO check
                cmpi.b  #6, d1                      // _KillIO == A006
                beq.s       Done                    // rts if _KillIO
 
        // It must be _Read, _Write, _Control, or _Status
        
        // Immediate check
                btst        #noQueueBit, d1         // test immediate bit
                beq.s       NotImmediate            // branch if not immediate
 
Immediate:
                move.w  d0, struct(IOParam.ioResult)(a0)
                                                    // The Device Manager doesn't set ioResult,
                                                    // so we do just in case the caller checks
                                                    // ioResult instead of the function result.
                bra.s       Done                    // rts if immediate
                                
NotImmediate:
 
// <3> BL¡B
// If the call is asynchronous and not complete, it should return noErr (0)
// in D0 instead of 1 -- otherwise, the Device Manager sees the 1 and
// returns 1 as an error to the caller. If the File Manager is the caller,
// it converts the 1 to ioErr and returns it to the program that called
// it.   See develop issue 13 page 10.
//
        // Incomplete Async check
                cmp.w       #1, d0                  // We're using the convention that if the
                                                    // driver result = 1 then the device driver
                                                    // hasn't completed the non-immediate
                                                    // operation.
                bne.s       Complete
                moveq       #0, d0                  // clear D0 and...
                bra.s       Done                    // rts if the operation is incomplete
 
Complete:       move.l  JIODone, -(sp)              // push jIODone onto stack
Done:           rts
 
}